2 * Intel Reference Systems cplds
4 * Copyright (C) 2014 Robert Jarzmik
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * Cplds motherboard driver, supporting lubbock and mainstone SoC board.
14 #include <linux/bitops.h>
15 #include <linux/gpio.h>
16 #include <linux/gpio/consumer.h>
17 #include <linux/interrupt.h>
19 #include <linux/irq.h>
20 #include <linux/irqdomain.h>
21 #include <linux/mfd/core.h>
22 #include <linux/module.h>
23 #include <linux/of_platform.h>
25 #define FPGA_IRQ_MASK_EN 0x0
26 #define FPGA_IRQ_SET_CLR 0x10
28 #define CPLDS_NB_IRQ 32
33 unsigned int irq_mask
;
34 struct gpio_desc
*gpio0
;
35 struct irq_domain
*irqdomain
;
38 static irqreturn_t
cplds_irq_handler(int in_irq
, void *d
)
40 struct cplds
*fpga
= d
;
41 unsigned long pending
;
44 pending
= readl(fpga
->base
+ FPGA_IRQ_SET_CLR
) & fpga
->irq_mask
;
45 for_each_set_bit(bit
, &pending
, CPLDS_NB_IRQ
)
46 generic_handle_irq(irq_find_mapping(fpga
->irqdomain
, bit
));
51 static void cplds_irq_mask_ack(struct irq_data
*d
)
53 struct cplds
*fpga
= irq_data_get_irq_chip_data(d
);
54 unsigned int cplds_irq
= irqd_to_hwirq(d
);
55 unsigned int set
, bit
= BIT(cplds_irq
);
57 fpga
->irq_mask
&= ~bit
;
58 writel(fpga
->irq_mask
, fpga
->base
+ FPGA_IRQ_MASK_EN
);
59 set
= readl(fpga
->base
+ FPGA_IRQ_SET_CLR
);
60 writel(set
& ~bit
, fpga
->base
+ FPGA_IRQ_SET_CLR
);
63 static void cplds_irq_unmask(struct irq_data
*d
)
65 struct cplds
*fpga
= irq_data_get_irq_chip_data(d
);
66 unsigned int cplds_irq
= irqd_to_hwirq(d
);
67 unsigned int bit
= BIT(cplds_irq
);
69 fpga
->irq_mask
|= bit
;
70 writel(fpga
->irq_mask
, fpga
->base
+ FPGA_IRQ_MASK_EN
);
73 static struct irq_chip cplds_irq_chip
= {
75 .irq_mask_ack
= cplds_irq_mask_ack
,
76 .irq_unmask
= cplds_irq_unmask
,
77 .flags
= IRQCHIP_MASK_ON_SUSPEND
| IRQCHIP_SKIP_SET_WAKE
,
80 static int cplds_irq_domain_map(struct irq_domain
*d
, unsigned int irq
,
81 irq_hw_number_t hwirq
)
83 struct cplds
*fpga
= d
->host_data
;
85 irq_set_chip_and_handler(irq
, &cplds_irq_chip
, handle_level_irq
);
86 irq_set_chip_data(irq
, fpga
);
91 static const struct irq_domain_ops cplds_irq_domain_ops
= {
92 .xlate
= irq_domain_xlate_twocell
,
93 .map
= cplds_irq_domain_map
,
96 static int cplds_resume(struct platform_device
*pdev
)
98 struct cplds
*fpga
= platform_get_drvdata(pdev
);
100 writel(fpga
->irq_mask
, fpga
->base
+ FPGA_IRQ_MASK_EN
);
105 static int cplds_probe(struct platform_device
*pdev
)
107 struct resource
*res
;
111 unsigned long irqflags
= 0;
113 fpga
= devm_kzalloc(&pdev
->dev
, sizeof(*fpga
), GFP_KERNEL
);
117 res
= platform_get_resource(pdev
, IORESOURCE_IRQ
, 0);
119 fpga
->irq
= (unsigned int)res
->start
;
120 irqflags
= res
->flags
;
125 base_irq
= platform_get_irq(pdev
, 1);
129 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
130 fpga
->base
= devm_ioremap_resource(&pdev
->dev
, res
);
131 if (IS_ERR(fpga
->base
))
132 return PTR_ERR(fpga
->base
);
134 platform_set_drvdata(pdev
, fpga
);
136 writel(fpga
->irq_mask
, fpga
->base
+ FPGA_IRQ_MASK_EN
);
137 writel(0, fpga
->base
+ FPGA_IRQ_SET_CLR
);
139 ret
= devm_request_irq(&pdev
->dev
, fpga
->irq
, cplds_irq_handler
,
140 irqflags
, dev_name(&pdev
->dev
), fpga
);
142 return -EPROBE_DEFER
;
145 dev_err(&pdev
->dev
, "couldn't request main irq%d: %d\n",
150 irq_set_irq_wake(fpga
->irq
, 1);
151 fpga
->irqdomain
= irq_domain_add_linear(pdev
->dev
.of_node
,
153 &cplds_irq_domain_ops
, fpga
);
154 if (!fpga
->irqdomain
)
158 ret
= irq_create_strict_mappings(fpga
->irqdomain
, base_irq
, 0,
161 dev_err(&pdev
->dev
, "couldn't create the irq mapping %d..%d\n",
162 base_irq
, base_irq
+ CPLDS_NB_IRQ
);
170 static int cplds_remove(struct platform_device
*pdev
)
172 struct cplds
*fpga
= platform_get_drvdata(pdev
);
174 irq_set_chip_and_handler(fpga
->irq
, NULL
, NULL
);
179 static const struct of_device_id cplds_id_table
[] = {
180 { .compatible
= "intel,lubbock-cplds-irqs", },
181 { .compatible
= "intel,mainstone-cplds-irqs", },
184 MODULE_DEVICE_TABLE(of
, cplds_id_table
);
186 static struct platform_driver cplds_driver
= {
188 .name
= "pxa_cplds_irqs",
189 .of_match_table
= of_match_ptr(cplds_id_table
),
191 .probe
= cplds_probe
,
192 .remove
= cplds_remove
,
193 .resume
= cplds_resume
,
196 module_platform_driver(cplds_driver
);
198 MODULE_DESCRIPTION("PXA Cplds interrupts driver");
199 MODULE_AUTHOR("Robert Jarzmik <robert.jarzmik@free.fr>");
200 MODULE_LICENSE("GPL");