diff options
Diffstat (limited to 'drivers/irqchip')
-rw-r--r-- | drivers/irqchip/Kconfig | 8 | ||||
-rw-r--r-- | drivers/irqchip/Makefile | 2 | ||||
-rw-r--r-- | drivers/irqchip/irq-renesas-intc-irqpin.c | 547 | ||||
-rw-r--r-- | drivers/irqchip/irq-renesas-irqc.c | 307 |
4 files changed, 864 insertions, 0 deletions
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index a350969e5efe..4a33351c25dc 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig | |||
@@ -25,6 +25,14 @@ config ARM_VIC_NR | |||
25 | The maximum number of VICs available in the system, for | 25 | The maximum number of VICs available in the system, for |
26 | power management. | 26 | power management. |
27 | 27 | ||
28 | config RENESAS_INTC_IRQPIN | ||
29 | bool | ||
30 | select IRQ_DOMAIN | ||
31 | |||
32 | config RENESAS_IRQC | ||
33 | bool | ||
34 | select IRQ_DOMAIN | ||
35 | |||
28 | config VERSATILE_FPGA_IRQ | 36 | config VERSATILE_FPGA_IRQ |
29 | bool | 37 | bool |
30 | select IRQ_DOMAIN | 38 | select IRQ_DOMAIN |
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 98e3b87bdf1b..e41ceb9bec22 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile | |||
@@ -8,4 +8,6 @@ obj-$(CONFIG_ARCH_SUNXI) += irq-sunxi.o | |||
8 | obj-$(CONFIG_ARCH_SPEAR3XX) += spear-shirq.o | 8 | obj-$(CONFIG_ARCH_SPEAR3XX) += spear-shirq.o |
9 | obj-$(CONFIG_ARM_GIC) += irq-gic.o | 9 | obj-$(CONFIG_ARM_GIC) += irq-gic.o |
10 | obj-$(CONFIG_ARM_VIC) += irq-vic.o | 10 | obj-$(CONFIG_ARM_VIC) += irq-vic.o |
11 | obj-$(CONFIG_RENESAS_INTC_IRQPIN) += irq-renesas-intc-irqpin.o | ||
12 | obj-$(CONFIG_RENESAS_IRQC) += irq-renesas-irqc.o | ||
11 | obj-$(CONFIG_VERSATILE_FPGA_IRQ) += irq-versatile-fpga.o | 13 | obj-$(CONFIG_VERSATILE_FPGA_IRQ) += irq-versatile-fpga.o |
diff --git a/drivers/irqchip/irq-renesas-intc-irqpin.c b/drivers/irqchip/irq-renesas-intc-irqpin.c new file mode 100644 index 000000000000..5a68e5accec1 --- /dev/null +++ b/drivers/irqchip/irq-renesas-intc-irqpin.c | |||
@@ -0,0 +1,547 @@ | |||
1 | /* | ||
2 | * Renesas INTC External IRQ Pin Driver | ||
3 | * | ||
4 | * Copyright (C) 2013 Magnus Damm | ||
5 | * | ||
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 | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
18 | */ | ||
19 | |||
20 | #include <linux/init.h> | ||
21 | #include <linux/platform_device.h> | ||
22 | #include <linux/spinlock.h> | ||
23 | #include <linux/interrupt.h> | ||
24 | #include <linux/ioport.h> | ||
25 | #include <linux/io.h> | ||
26 | #include <linux/irq.h> | ||
27 | #include <linux/irqdomain.h> | ||
28 | #include <linux/err.h> | ||
29 | #include <linux/slab.h> | ||
30 | #include <linux/module.h> | ||
31 | #include <linux/platform_data/irq-renesas-intc-irqpin.h> | ||
32 | |||
33 | #define INTC_IRQPIN_MAX 8 /* maximum 8 interrupts per driver instance */ | ||
34 | |||
35 | #define INTC_IRQPIN_REG_SENSE 0 /* ICRn */ | ||
36 | #define INTC_IRQPIN_REG_PRIO 1 /* INTPRInn */ | ||
37 | #define INTC_IRQPIN_REG_SOURCE 2 /* INTREQnn */ | ||
38 | #define INTC_IRQPIN_REG_MASK 3 /* INTMSKnn */ | ||
39 | #define INTC_IRQPIN_REG_CLEAR 4 /* INTMSKCLRnn */ | ||
40 | #define INTC_IRQPIN_REG_NR 5 | ||
41 | |||
42 | /* INTC external IRQ PIN hardware register access: | ||
43 | * | ||
44 | * SENSE is read-write 32-bit with 2-bits or 4-bits per IRQ (*) | ||
45 | * PRIO is read-write 32-bit with 4-bits per IRQ (**) | ||
46 | * SOURCE is read-only 32-bit or 8-bit with 1-bit per IRQ (***) | ||
47 | * MASK is write-only 32-bit or 8-bit with 1-bit per IRQ (***) | ||
48 | * CLEAR is write-only 32-bit or 8-bit with 1-bit per IRQ (***) | ||
49 | * | ||
50 | * (*) May be accessed by more than one driver instance - lock needed | ||
51 | * (**) Read-modify-write access by one driver instance - lock needed | ||
52 | * (***) Accessed by one driver instance only - no locking needed | ||
53 | */ | ||
54 | |||
55 | struct intc_irqpin_iomem { | ||
56 | void __iomem *iomem; | ||
57 | unsigned long (*read)(void __iomem *iomem); | ||
58 | void (*write)(void __iomem *iomem, unsigned long data); | ||
59 | int width; | ||
60 | }; | ||
61 | |||
62 | struct intc_irqpin_irq { | ||
63 | int hw_irq; | ||
64 | int requested_irq; | ||
65 | int domain_irq; | ||
66 | struct intc_irqpin_priv *p; | ||
67 | }; | ||
68 | |||
69 | struct intc_irqpin_priv { | ||
70 | struct intc_irqpin_iomem iomem[INTC_IRQPIN_REG_NR]; | ||
71 | struct intc_irqpin_irq irq[INTC_IRQPIN_MAX]; | ||
72 | struct renesas_intc_irqpin_config config; | ||
73 | unsigned int number_of_irqs; | ||
74 | struct platform_device *pdev; | ||
75 | struct irq_chip irq_chip; | ||
76 | struct irq_domain *irq_domain; | ||
77 | bool shared_irqs; | ||
78 | u8 shared_irq_mask; | ||
79 | }; | ||
80 | |||
81 | static unsigned long intc_irqpin_read32(void __iomem *iomem) | ||
82 | { | ||
83 | return ioread32(iomem); | ||
84 | } | ||
85 | |||
86 | static unsigned long intc_irqpin_read8(void __iomem *iomem) | ||
87 | { | ||
88 | return ioread8(iomem); | ||
89 | } | ||
90 | |||
91 | static void intc_irqpin_write32(void __iomem *iomem, unsigned long data) | ||
92 | { | ||
93 | iowrite32(data, iomem); | ||
94 | } | ||
95 | |||
96 | static void intc_irqpin_write8(void __iomem *iomem, unsigned long data) | ||
97 | { | ||
98 | iowrite8(data, iomem); | ||
99 | } | ||
100 | |||
101 | static inline unsigned long intc_irqpin_read(struct intc_irqpin_priv *p, | ||
102 | int reg) | ||
103 | { | ||
104 | struct intc_irqpin_iomem *i = &p->iomem[reg]; | ||
105 | |||
106 | return i->read(i->iomem); | ||
107 | } | ||
108 | |||
109 | static inline void intc_irqpin_write(struct intc_irqpin_priv *p, | ||
110 | int reg, unsigned long data) | ||
111 | { | ||
112 | struct intc_irqpin_iomem *i = &p->iomem[reg]; | ||
113 | |||
114 | i->write(i->iomem, data); | ||
115 | } | ||
116 | |||
117 | static inline unsigned long intc_irqpin_hwirq_mask(struct intc_irqpin_priv *p, | ||
118 | int reg, int hw_irq) | ||
119 | { | ||
120 | return BIT((p->iomem[reg].width - 1) - hw_irq); | ||
121 | } | ||
122 | |||
123 | static inline void intc_irqpin_irq_write_hwirq(struct intc_irqpin_priv *p, | ||
124 | int reg, int hw_irq) | ||
125 | { | ||
126 | intc_irqpin_write(p, reg, intc_irqpin_hwirq_mask(p, reg, hw_irq)); | ||
127 | } | ||
128 | |||
129 | static DEFINE_RAW_SPINLOCK(intc_irqpin_lock); /* only used by slow path */ | ||
130 | |||
131 | static void intc_irqpin_read_modify_write(struct intc_irqpin_priv *p, | ||
132 | int reg, int shift, | ||
133 | int width, int value) | ||
134 | { | ||
135 | unsigned long flags; | ||
136 | unsigned long tmp; | ||
137 | |||
138 | raw_spin_lock_irqsave(&intc_irqpin_lock, flags); | ||
139 | |||
140 | tmp = intc_irqpin_read(p, reg); | ||
141 | tmp &= ~(((1 << width) - 1) << shift); | ||
142 | tmp |= value << shift; | ||
143 | intc_irqpin_write(p, reg, tmp); | ||
144 | |||
145 | raw_spin_unlock_irqrestore(&intc_irqpin_lock, flags); | ||
146 | } | ||
147 | |||
148 | static void intc_irqpin_mask_unmask_prio(struct intc_irqpin_priv *p, | ||
149 | int irq, int do_mask) | ||
150 | { | ||
151 | int bitfield_width = 4; /* PRIO assumed to have fixed bitfield width */ | ||
152 | int shift = (7 - irq) * bitfield_width; /* PRIO assumed to be 32-bit */ | ||
153 | |||
154 | intc_irqpin_read_modify_write(p, INTC_IRQPIN_REG_PRIO, | ||
155 | shift, bitfield_width, | ||
156 | do_mask ? 0 : (1 << bitfield_width) - 1); | ||
157 | } | ||
158 | |||
159 | static int intc_irqpin_set_sense(struct intc_irqpin_priv *p, int irq, int value) | ||
160 | { | ||
161 | int bitfield_width = p->config.sense_bitfield_width; | ||
162 | int shift = (7 - irq) * bitfield_width; /* SENSE assumed to be 32-bit */ | ||
163 | |||
164 | dev_dbg(&p->pdev->dev, "sense irq = %d, mode = %d\n", irq, value); | ||
165 | |||
166 | if (value >= (1 << bitfield_width)) | ||
167 | return -EINVAL; | ||
168 | |||
169 | intc_irqpin_read_modify_write(p, INTC_IRQPIN_REG_SENSE, shift, | ||
170 | bitfield_width, value); | ||
171 | return 0; | ||
172 | } | ||
173 | |||
174 | static void intc_irqpin_dbg(struct intc_irqpin_irq *i, char *str) | ||
175 | { | ||
176 | dev_dbg(&i->p->pdev->dev, "%s (%d:%d:%d)\n", | ||
177 | str, i->requested_irq, i->hw_irq, i->domain_irq); | ||
178 | } | ||
179 | |||
180 | static void intc_irqpin_irq_enable(struct irq_data *d) | ||
181 | { | ||
182 | struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d); | ||
183 | int hw_irq = irqd_to_hwirq(d); | ||
184 | |||
185 | intc_irqpin_dbg(&p->irq[hw_irq], "enable"); | ||
186 | intc_irqpin_irq_write_hwirq(p, INTC_IRQPIN_REG_CLEAR, hw_irq); | ||
187 | } | ||
188 | |||
189 | static void intc_irqpin_irq_disable(struct irq_data *d) | ||
190 | { | ||
191 | struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d); | ||
192 | int hw_irq = irqd_to_hwirq(d); | ||
193 | |||
194 | intc_irqpin_dbg(&p->irq[hw_irq], "disable"); | ||
195 | intc_irqpin_irq_write_hwirq(p, INTC_IRQPIN_REG_MASK, hw_irq); | ||
196 | } | ||
197 | |||
198 | static void intc_irqpin_shared_irq_enable(struct irq_data *d) | ||
199 | { | ||
200 | struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d); | ||
201 | int hw_irq = irqd_to_hwirq(d); | ||
202 | |||
203 | intc_irqpin_dbg(&p->irq[hw_irq], "shared enable"); | ||
204 | intc_irqpin_irq_write_hwirq(p, INTC_IRQPIN_REG_CLEAR, hw_irq); | ||
205 | |||
206 | p->shared_irq_mask &= ~BIT(hw_irq); | ||
207 | } | ||
208 | |||
209 | static void intc_irqpin_shared_irq_disable(struct irq_data *d) | ||
210 | { | ||
211 | struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d); | ||
212 | int hw_irq = irqd_to_hwirq(d); | ||
213 | |||
214 | intc_irqpin_dbg(&p->irq[hw_irq], "shared disable"); | ||
215 | intc_irqpin_irq_write_hwirq(p, INTC_IRQPIN_REG_MASK, hw_irq); | ||
216 | |||
217 | p->shared_irq_mask |= BIT(hw_irq); | ||
218 | } | ||
219 | |||
220 | static void intc_irqpin_irq_enable_force(struct irq_data *d) | ||
221 | { | ||
222 | struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d); | ||
223 | int irq = p->irq[irqd_to_hwirq(d)].requested_irq; | ||
224 | |||
225 | intc_irqpin_irq_enable(d); | ||
226 | |||
227 | /* enable interrupt through parent interrupt controller, | ||
228 | * assumes non-shared interrupt with 1:1 mapping | ||
229 | * needed for busted IRQs on some SoCs like sh73a0 | ||
230 | */ | ||
231 | irq_get_chip(irq)->irq_unmask(irq_get_irq_data(irq)); | ||
232 | } | ||
233 | |||
234 | static void intc_irqpin_irq_disable_force(struct irq_data *d) | ||
235 | { | ||
236 | struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d); | ||
237 | int irq = p->irq[irqd_to_hwirq(d)].requested_irq; | ||
238 | |||
239 | /* disable interrupt through parent interrupt controller, | ||
240 | * assumes non-shared interrupt with 1:1 mapping | ||
241 | * needed for busted IRQs on some SoCs like sh73a0 | ||
242 | */ | ||
243 | irq_get_chip(irq)->irq_mask(irq_get_irq_data(irq)); | ||
244 | intc_irqpin_irq_disable(d); | ||
245 | } | ||
246 | |||
247 | #define INTC_IRQ_SENSE_VALID 0x10 | ||
248 | #define INTC_IRQ_SENSE(x) (x + INTC_IRQ_SENSE_VALID) | ||
249 | |||
250 | static unsigned char intc_irqpin_sense[IRQ_TYPE_SENSE_MASK + 1] = { | ||
251 | [IRQ_TYPE_EDGE_FALLING] = INTC_IRQ_SENSE(0x00), | ||
252 | [IRQ_TYPE_EDGE_RISING] = INTC_IRQ_SENSE(0x01), | ||
253 | [IRQ_TYPE_LEVEL_LOW] = INTC_IRQ_SENSE(0x02), | ||
254 | [IRQ_TYPE_LEVEL_HIGH] = INTC_IRQ_SENSE(0x03), | ||
255 | [IRQ_TYPE_EDGE_BOTH] = INTC_IRQ_SENSE(0x04), | ||
256 | }; | ||
257 | |||
258 | static int intc_irqpin_irq_set_type(struct irq_data *d, unsigned int type) | ||
259 | { | ||
260 | unsigned char value = intc_irqpin_sense[type & IRQ_TYPE_SENSE_MASK]; | ||
261 | struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d); | ||
262 | |||
263 | if (!(value & INTC_IRQ_SENSE_VALID)) | ||
264 | return -EINVAL; | ||
265 | |||
266 | return intc_irqpin_set_sense(p, irqd_to_hwirq(d), | ||
267 | value ^ INTC_IRQ_SENSE_VALID); | ||
268 | } | ||
269 | |||
270 | static irqreturn_t intc_irqpin_irq_handler(int irq, void *dev_id) | ||
271 | { | ||
272 | struct intc_irqpin_irq *i = dev_id; | ||
273 | struct intc_irqpin_priv *p = i->p; | ||
274 | unsigned long bit; | ||
275 | |||
276 | intc_irqpin_dbg(i, "demux1"); | ||
277 | bit = intc_irqpin_hwirq_mask(p, INTC_IRQPIN_REG_SOURCE, i->hw_irq); | ||
278 | |||
279 | if (intc_irqpin_read(p, INTC_IRQPIN_REG_SOURCE) & bit) { | ||
280 | intc_irqpin_write(p, INTC_IRQPIN_REG_SOURCE, ~bit); | ||
281 | intc_irqpin_dbg(i, "demux2"); | ||
282 | generic_handle_irq(i->domain_irq); | ||
283 | return IRQ_HANDLED; | ||
284 | } | ||
285 | return IRQ_NONE; | ||
286 | } | ||
287 | |||
288 | static irqreturn_t intc_irqpin_shared_irq_handler(int irq, void *dev_id) | ||
289 | { | ||
290 | struct intc_irqpin_priv *p = dev_id; | ||
291 | unsigned int reg_source = intc_irqpin_read(p, INTC_IRQPIN_REG_SOURCE); | ||
292 | irqreturn_t status = IRQ_NONE; | ||
293 | int k; | ||
294 | |||
295 | for (k = 0; k < 8; k++) { | ||
296 | if (reg_source & BIT(7 - k)) { | ||
297 | if (BIT(k) & p->shared_irq_mask) | ||
298 | continue; | ||
299 | |||
300 | status |= intc_irqpin_irq_handler(irq, &p->irq[k]); | ||
301 | } | ||
302 | } | ||
303 | |||
304 | return status; | ||
305 | } | ||
306 | |||
307 | static int intc_irqpin_irq_domain_map(struct irq_domain *h, unsigned int virq, | ||
308 | irq_hw_number_t hw) | ||
309 | { | ||
310 | struct intc_irqpin_priv *p = h->host_data; | ||
311 | |||
312 | p->irq[hw].domain_irq = virq; | ||
313 | p->irq[hw].hw_irq = hw; | ||
314 | |||
315 | intc_irqpin_dbg(&p->irq[hw], "map"); | ||
316 | irq_set_chip_data(virq, h->host_data); | ||
317 | irq_set_chip_and_handler(virq, &p->irq_chip, handle_level_irq); | ||
318 | set_irq_flags(virq, IRQF_VALID); /* kill me now */ | ||
319 | return 0; | ||
320 | } | ||
321 | |||
322 | static struct irq_domain_ops intc_irqpin_irq_domain_ops = { | ||
323 | .map = intc_irqpin_irq_domain_map, | ||
324 | .xlate = irq_domain_xlate_twocell, | ||
325 | }; | ||
326 | |||
327 | static int intc_irqpin_probe(struct platform_device *pdev) | ||
328 | { | ||
329 | struct renesas_intc_irqpin_config *pdata = pdev->dev.platform_data; | ||
330 | struct intc_irqpin_priv *p; | ||
331 | struct intc_irqpin_iomem *i; | ||
332 | struct resource *io[INTC_IRQPIN_REG_NR]; | ||
333 | struct resource *irq; | ||
334 | struct irq_chip *irq_chip; | ||
335 | void (*enable_fn)(struct irq_data *d); | ||
336 | void (*disable_fn)(struct irq_data *d); | ||
337 | const char *name = dev_name(&pdev->dev); | ||
338 | int ref_irq; | ||
339 | int ret; | ||
340 | int k; | ||
341 | |||
342 | p = devm_kzalloc(&pdev->dev, sizeof(*p), GFP_KERNEL); | ||
343 | if (!p) { | ||
344 | dev_err(&pdev->dev, "failed to allocate driver data\n"); | ||
345 | ret = -ENOMEM; | ||
346 | goto err0; | ||
347 | } | ||
348 | |||
349 | /* deal with driver instance configuration */ | ||
350 | if (pdata) | ||
351 | memcpy(&p->config, pdata, sizeof(*pdata)); | ||
352 | if (!p->config.sense_bitfield_width) | ||
353 | p->config.sense_bitfield_width = 4; /* default to 4 bits */ | ||
354 | |||
355 | p->pdev = pdev; | ||
356 | platform_set_drvdata(pdev, p); | ||
357 | |||
358 | /* get hold of manadatory IOMEM */ | ||
359 | for (k = 0; k < INTC_IRQPIN_REG_NR; k++) { | ||
360 | io[k] = platform_get_resource(pdev, IORESOURCE_MEM, k); | ||
361 | if (!io[k]) { | ||
362 | dev_err(&pdev->dev, "not enough IOMEM resources\n"); | ||
363 | ret = -EINVAL; | ||
364 | goto err0; | ||
365 | } | ||
366 | } | ||
367 | |||
368 | /* allow any number of IRQs between 1 and INTC_IRQPIN_MAX */ | ||
369 | for (k = 0; k < INTC_IRQPIN_MAX; k++) { | ||
370 | irq = platform_get_resource(pdev, IORESOURCE_IRQ, k); | ||
371 | if (!irq) | ||
372 | break; | ||
373 | |||
374 | p->irq[k].p = p; | ||
375 | p->irq[k].requested_irq = irq->start; | ||
376 | } | ||
377 | |||
378 | p->number_of_irqs = k; | ||
379 | if (p->number_of_irqs < 1) { | ||
380 | dev_err(&pdev->dev, "not enough IRQ resources\n"); | ||
381 | ret = -EINVAL; | ||
382 | goto err0; | ||
383 | } | ||
384 | |||
385 | /* ioremap IOMEM and setup read/write callbacks */ | ||
386 | for (k = 0; k < INTC_IRQPIN_REG_NR; k++) { | ||
387 | i = &p->iomem[k]; | ||
388 | |||
389 | switch (resource_size(io[k])) { | ||
390 | case 1: | ||
391 | i->width = 8; | ||
392 | i->read = intc_irqpin_read8; | ||
393 | i->write = intc_irqpin_write8; | ||
394 | break; | ||
395 | case 4: | ||
396 | i->width = 32; | ||
397 | i->read = intc_irqpin_read32; | ||
398 | i->write = intc_irqpin_write32; | ||
399 | break; | ||
400 | default: | ||
401 | dev_err(&pdev->dev, "IOMEM size mismatch\n"); | ||
402 | ret = -EINVAL; | ||
403 | goto err0; | ||
404 | } | ||
405 | |||
406 | i->iomem = devm_ioremap_nocache(&pdev->dev, io[k]->start, | ||
407 | resource_size(io[k])); | ||
408 | if (!i->iomem) { | ||
409 | dev_err(&pdev->dev, "failed to remap IOMEM\n"); | ||
410 | ret = -ENXIO; | ||
411 | goto err0; | ||
412 | } | ||
413 | } | ||
414 | |||
415 | /* mask all interrupts using priority */ | ||
416 | for (k = 0; k < p->number_of_irqs; k++) | ||
417 | intc_irqpin_mask_unmask_prio(p, k, 1); | ||
418 | |||
419 | /* clear all pending interrupts */ | ||
420 | intc_irqpin_write(p, INTC_IRQPIN_REG_SOURCE, 0x0); | ||
421 | |||
422 | /* scan for shared interrupt lines */ | ||
423 | ref_irq = p->irq[0].requested_irq; | ||
424 | p->shared_irqs = true; | ||
425 | for (k = 1; k < p->number_of_irqs; k++) { | ||
426 | if (ref_irq != p->irq[k].requested_irq) { | ||
427 | p->shared_irqs = false; | ||
428 | break; | ||
429 | } | ||
430 | } | ||
431 | |||
432 | /* use more severe masking method if requested */ | ||
433 | if (p->config.control_parent) { | ||
434 | enable_fn = intc_irqpin_irq_enable_force; | ||
435 | disable_fn = intc_irqpin_irq_disable_force; | ||
436 | } else if (!p->shared_irqs) { | ||
437 | enable_fn = intc_irqpin_irq_enable; | ||
438 | disable_fn = intc_irqpin_irq_disable; | ||
439 | } else { | ||
440 | enable_fn = intc_irqpin_shared_irq_enable; | ||
441 | disable_fn = intc_irqpin_shared_irq_disable; | ||
442 | } | ||
443 | |||
444 | irq_chip = &p->irq_chip; | ||
445 | irq_chip->name = name; | ||
446 | irq_chip->irq_mask = disable_fn; | ||
447 | irq_chip->irq_unmask = enable_fn; | ||
448 | irq_chip->irq_enable = enable_fn; | ||
449 | irq_chip->irq_disable = disable_fn; | ||
450 | irq_chip->irq_set_type = intc_irqpin_irq_set_type; | ||
451 | irq_chip->flags = IRQCHIP_SKIP_SET_WAKE; | ||
452 | |||
453 | p->irq_domain = irq_domain_add_simple(pdev->dev.of_node, | ||
454 | p->number_of_irqs, | ||
455 | p->config.irq_base, | ||
456 | &intc_irqpin_irq_domain_ops, p); | ||
457 | if (!p->irq_domain) { | ||
458 | ret = -ENXIO; | ||
459 | dev_err(&pdev->dev, "cannot initialize irq domain\n"); | ||
460 | goto err0; | ||
461 | } | ||
462 | |||
463 | if (p->shared_irqs) { | ||
464 | /* request one shared interrupt */ | ||
465 | if (devm_request_irq(&pdev->dev, p->irq[0].requested_irq, | ||
466 | intc_irqpin_shared_irq_handler, | ||
467 | IRQF_SHARED, name, p)) { | ||
468 | dev_err(&pdev->dev, "failed to request low IRQ\n"); | ||
469 | ret = -ENOENT; | ||
470 | goto err1; | ||
471 | } | ||
472 | } else { | ||
473 | /* request interrupts one by one */ | ||
474 | for (k = 0; k < p->number_of_irqs; k++) { | ||
475 | if (devm_request_irq(&pdev->dev, | ||
476 | p->irq[k].requested_irq, | ||
477 | intc_irqpin_irq_handler, | ||
478 | 0, name, &p->irq[k])) { | ||
479 | dev_err(&pdev->dev, | ||
480 | "failed to request low IRQ\n"); | ||
481 | ret = -ENOENT; | ||
482 | goto err1; | ||
483 | } | ||
484 | } | ||
485 | } | ||
486 | |||
487 | /* unmask all interrupts on prio level */ | ||
488 | for (k = 0; k < p->number_of_irqs; k++) | ||
489 | intc_irqpin_mask_unmask_prio(p, k, 0); | ||
490 | |||
491 | dev_info(&pdev->dev, "driving %d irqs\n", p->number_of_irqs); | ||
492 | |||
493 | /* warn in case of mismatch if irq base is specified */ | ||
494 | if (p->config.irq_base) { | ||
495 | if (p->config.irq_base != p->irq[0].domain_irq) | ||
496 | dev_warn(&pdev->dev, "irq base mismatch (%d/%d)\n", | ||
497 | p->config.irq_base, p->irq[0].domain_irq); | ||
498 | } | ||
499 | |||
500 | return 0; | ||
501 | |||
502 | err1: | ||
503 | irq_domain_remove(p->irq_domain); | ||
504 | err0: | ||
505 | return ret; | ||
506 | } | ||
507 | |||
508 | static int intc_irqpin_remove(struct platform_device *pdev) | ||
509 | { | ||
510 | struct intc_irqpin_priv *p = platform_get_drvdata(pdev); | ||
511 | |||
512 | irq_domain_remove(p->irq_domain); | ||
513 | |||
514 | return 0; | ||
515 | } | ||
516 | |||
517 | static const struct of_device_id intc_irqpin_dt_ids[] = { | ||
518 | { .compatible = "renesas,intc-irqpin", }, | ||
519 | {}, | ||
520 | }; | ||
521 | MODULE_DEVICE_TABLE(of, intc_irqpin_dt_ids); | ||
522 | |||
523 | static struct platform_driver intc_irqpin_device_driver = { | ||
524 | .probe = intc_irqpin_probe, | ||
525 | .remove = intc_irqpin_remove, | ||
526 | .driver = { | ||
527 | .name = "renesas_intc_irqpin", | ||
528 | .of_match_table = intc_irqpin_dt_ids, | ||
529 | .owner = THIS_MODULE, | ||
530 | } | ||
531 | }; | ||
532 | |||
533 | static int __init intc_irqpin_init(void) | ||
534 | { | ||
535 | return platform_driver_register(&intc_irqpin_device_driver); | ||
536 | } | ||
537 | postcore_initcall(intc_irqpin_init); | ||
538 | |||
539 | static void __exit intc_irqpin_exit(void) | ||
540 | { | ||
541 | platform_driver_unregister(&intc_irqpin_device_driver); | ||
542 | } | ||
543 | module_exit(intc_irqpin_exit); | ||
544 | |||
545 | MODULE_AUTHOR("Magnus Damm"); | ||
546 | MODULE_DESCRIPTION("Renesas INTC External IRQ Pin Driver"); | ||
547 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/irqchip/irq-renesas-irqc.c b/drivers/irqchip/irq-renesas-irqc.c new file mode 100644 index 000000000000..927bff373aac --- /dev/null +++ b/drivers/irqchip/irq-renesas-irqc.c | |||
@@ -0,0 +1,307 @@ | |||
1 | /* | ||
2 | * Renesas IRQC Driver | ||
3 | * | ||
4 | * Copyright (C) 2013 Magnus Damm | ||
5 | * | ||
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 | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
18 | */ | ||
19 | |||
20 | #include <linux/init.h> | ||
21 | #include <linux/platform_device.h> | ||
22 | #include <linux/spinlock.h> | ||
23 | #include <linux/interrupt.h> | ||
24 | #include <linux/ioport.h> | ||
25 | #include <linux/io.h> | ||
26 | #include <linux/irq.h> | ||
27 | #include <linux/irqdomain.h> | ||
28 | #include <linux/err.h> | ||
29 | #include <linux/slab.h> | ||
30 | #include <linux/module.h> | ||
31 | #include <linux/platform_data/irq-renesas-irqc.h> | ||
32 | |||
33 | #define IRQC_IRQ_MAX 32 /* maximum 32 interrupts per driver instance */ | ||
34 | |||
35 | #define IRQC_REQ_STS 0x00 | ||
36 | #define IRQC_EN_STS 0x04 | ||
37 | #define IRQC_EN_SET 0x08 | ||
38 | #define IRQC_INT_CPU_BASE(n) (0x000 + ((n) * 0x10)) | ||
39 | #define DETECT_STATUS 0x100 | ||
40 | #define IRQC_CONFIG(n) (0x180 + ((n) * 0x04)) | ||
41 | |||
42 | struct irqc_irq { | ||
43 | int hw_irq; | ||
44 | int requested_irq; | ||
45 | int domain_irq; | ||
46 | struct irqc_priv *p; | ||
47 | }; | ||
48 | |||
49 | struct irqc_priv { | ||
50 | void __iomem *iomem; | ||
51 | void __iomem *cpu_int_base; | ||
52 | struct irqc_irq irq[IRQC_IRQ_MAX]; | ||
53 | struct renesas_irqc_config config; | ||
54 | unsigned int number_of_irqs; | ||
55 | struct platform_device *pdev; | ||
56 | struct irq_chip irq_chip; | ||
57 | struct irq_domain *irq_domain; | ||
58 | }; | ||
59 | |||
60 | static void irqc_dbg(struct irqc_irq *i, char *str) | ||
61 | { | ||
62 | dev_dbg(&i->p->pdev->dev, "%s (%d:%d:%d)\n", | ||
63 | str, i->requested_irq, i->hw_irq, i->domain_irq); | ||
64 | } | ||
65 | |||
66 | static void irqc_irq_enable(struct irq_data *d) | ||
67 | { | ||
68 | struct irqc_priv *p = irq_data_get_irq_chip_data(d); | ||
69 | int hw_irq = irqd_to_hwirq(d); | ||
70 | |||
71 | irqc_dbg(&p->irq[hw_irq], "enable"); | ||
72 | iowrite32(BIT(hw_irq), p->cpu_int_base + IRQC_EN_SET); | ||
73 | } | ||
74 | |||
75 | static void irqc_irq_disable(struct irq_data *d) | ||
76 | { | ||
77 | struct irqc_priv *p = irq_data_get_irq_chip_data(d); | ||
78 | int hw_irq = irqd_to_hwirq(d); | ||
79 | |||
80 | irqc_dbg(&p->irq[hw_irq], "disable"); | ||
81 | iowrite32(BIT(hw_irq), p->cpu_int_base + IRQC_EN_STS); | ||
82 | } | ||
83 | |||
84 | #define INTC_IRQ_SENSE_VALID 0x10 | ||
85 | #define INTC_IRQ_SENSE(x) (x + INTC_IRQ_SENSE_VALID) | ||
86 | |||
87 | static unsigned char irqc_sense[IRQ_TYPE_SENSE_MASK + 1] = { | ||
88 | [IRQ_TYPE_LEVEL_LOW] = INTC_IRQ_SENSE(0x01), | ||
89 | [IRQ_TYPE_LEVEL_HIGH] = INTC_IRQ_SENSE(0x02), | ||
90 | [IRQ_TYPE_EDGE_FALLING] = INTC_IRQ_SENSE(0x04), /* Synchronous */ | ||
91 | [IRQ_TYPE_EDGE_RISING] = INTC_IRQ_SENSE(0x08), /* Synchronous */ | ||
92 | [IRQ_TYPE_EDGE_BOTH] = INTC_IRQ_SENSE(0x0c), /* Synchronous */ | ||
93 | }; | ||
94 | |||
95 | static int irqc_irq_set_type(struct irq_data *d, unsigned int type) | ||
96 | { | ||
97 | struct irqc_priv *p = irq_data_get_irq_chip_data(d); | ||
98 | int hw_irq = irqd_to_hwirq(d); | ||
99 | unsigned char value = irqc_sense[type & IRQ_TYPE_SENSE_MASK]; | ||
100 | unsigned long tmp; | ||
101 | |||
102 | irqc_dbg(&p->irq[hw_irq], "sense"); | ||
103 | |||
104 | if (!(value & INTC_IRQ_SENSE_VALID)) | ||
105 | return -EINVAL; | ||
106 | |||
107 | tmp = ioread32(p->iomem + IRQC_CONFIG(hw_irq)); | ||
108 | tmp &= ~0x3f; | ||
109 | tmp |= value ^ INTC_IRQ_SENSE_VALID; | ||
110 | iowrite32(tmp, p->iomem + IRQC_CONFIG(hw_irq)); | ||
111 | return 0; | ||
112 | } | ||
113 | |||
114 | static irqreturn_t irqc_irq_handler(int irq, void *dev_id) | ||
115 | { | ||
116 | struct irqc_irq *i = dev_id; | ||
117 | struct irqc_priv *p = i->p; | ||
118 | unsigned long bit = BIT(i->hw_irq); | ||
119 | |||
120 | irqc_dbg(i, "demux1"); | ||
121 | |||
122 | if (ioread32(p->iomem + DETECT_STATUS) & bit) { | ||
123 | iowrite32(bit, p->iomem + DETECT_STATUS); | ||
124 | irqc_dbg(i, "demux2"); | ||
125 | generic_handle_irq(i->domain_irq); | ||
126 | return IRQ_HANDLED; | ||
127 | } | ||
128 | return IRQ_NONE; | ||
129 | } | ||
130 | |||
131 | static int irqc_irq_domain_map(struct irq_domain *h, unsigned int virq, | ||
132 | irq_hw_number_t hw) | ||
133 | { | ||
134 | struct irqc_priv *p = h->host_data; | ||
135 | |||
136 | p->irq[hw].domain_irq = virq; | ||
137 | p->irq[hw].hw_irq = hw; | ||
138 | |||
139 | irqc_dbg(&p->irq[hw], "map"); | ||
140 | irq_set_chip_data(virq, h->host_data); | ||
141 | irq_set_chip_and_handler(virq, &p->irq_chip, handle_level_irq); | ||
142 | set_irq_flags(virq, IRQF_VALID); /* kill me now */ | ||
143 | return 0; | ||
144 | } | ||
145 | |||
146 | static struct irq_domain_ops irqc_irq_domain_ops = { | ||
147 | .map = irqc_irq_domain_map, | ||
148 | .xlate = irq_domain_xlate_twocell, | ||
149 | }; | ||
150 | |||
151 | static int irqc_probe(struct platform_device *pdev) | ||
152 | { | ||
153 | struct renesas_irqc_config *pdata = pdev->dev.platform_data; | ||
154 | struct irqc_priv *p; | ||
155 | struct resource *io; | ||
156 | struct resource *irq; | ||
157 | struct irq_chip *irq_chip; | ||
158 | const char *name = dev_name(&pdev->dev); | ||
159 | int ret; | ||
160 | int k; | ||
161 | |||
162 | p = kzalloc(sizeof(*p), GFP_KERNEL); | ||
163 | if (!p) { | ||
164 | dev_err(&pdev->dev, "failed to allocate driver data\n"); | ||
165 | ret = -ENOMEM; | ||
166 | goto err0; | ||
167 | } | ||
168 | |||
169 | /* deal with driver instance configuration */ | ||
170 | if (pdata) | ||
171 | memcpy(&p->config, pdata, sizeof(*pdata)); | ||
172 | |||
173 | p->pdev = pdev; | ||
174 | platform_set_drvdata(pdev, p); | ||
175 | |||
176 | /* get hold of manadatory IOMEM */ | ||
177 | io = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
178 | if (!io) { | ||
179 | dev_err(&pdev->dev, "not enough IOMEM resources\n"); | ||
180 | ret = -EINVAL; | ||
181 | goto err1; | ||
182 | } | ||
183 | |||
184 | /* allow any number of IRQs between 1 and IRQC_IRQ_MAX */ | ||
185 | for (k = 0; k < IRQC_IRQ_MAX; k++) { | ||
186 | irq = platform_get_resource(pdev, IORESOURCE_IRQ, k); | ||
187 | if (!irq) | ||
188 | break; | ||
189 | |||
190 | p->irq[k].p = p; | ||
191 | p->irq[k].requested_irq = irq->start; | ||
192 | } | ||
193 | |||
194 | p->number_of_irqs = k; | ||
195 | if (p->number_of_irqs < 1) { | ||
196 | dev_err(&pdev->dev, "not enough IRQ resources\n"); | ||
197 | ret = -EINVAL; | ||
198 | goto err1; | ||
199 | } | ||
200 | |||
201 | /* ioremap IOMEM and setup read/write callbacks */ | ||
202 | p->iomem = ioremap_nocache(io->start, resource_size(io)); | ||
203 | if (!p->iomem) { | ||
204 | dev_err(&pdev->dev, "failed to remap IOMEM\n"); | ||
205 | ret = -ENXIO; | ||
206 | goto err2; | ||
207 | } | ||
208 | |||
209 | p->cpu_int_base = p->iomem + IRQC_INT_CPU_BASE(0); /* SYS-SPI */ | ||
210 | |||
211 | irq_chip = &p->irq_chip; | ||
212 | irq_chip->name = name; | ||
213 | irq_chip->irq_mask = irqc_irq_disable; | ||
214 | irq_chip->irq_unmask = irqc_irq_enable; | ||
215 | irq_chip->irq_enable = irqc_irq_enable; | ||
216 | irq_chip->irq_disable = irqc_irq_disable; | ||
217 | irq_chip->irq_set_type = irqc_irq_set_type; | ||
218 | irq_chip->flags = IRQCHIP_SKIP_SET_WAKE; | ||
219 | |||
220 | p->irq_domain = irq_domain_add_simple(pdev->dev.of_node, | ||
221 | p->number_of_irqs, | ||
222 | p->config.irq_base, | ||
223 | &irqc_irq_domain_ops, p); | ||
224 | if (!p->irq_domain) { | ||
225 | ret = -ENXIO; | ||
226 | dev_err(&pdev->dev, "cannot initialize irq domain\n"); | ||
227 | goto err2; | ||
228 | } | ||
229 | |||
230 | /* request interrupts one by one */ | ||
231 | for (k = 0; k < p->number_of_irqs; k++) { | ||
232 | if (request_irq(p->irq[k].requested_irq, irqc_irq_handler, | ||
233 | 0, name, &p->irq[k])) { | ||
234 | dev_err(&pdev->dev, "failed to request IRQ\n"); | ||
235 | ret = -ENOENT; | ||
236 | goto err3; | ||
237 | } | ||
238 | } | ||
239 | |||
240 | dev_info(&pdev->dev, "driving %d irqs\n", p->number_of_irqs); | ||
241 | |||
242 | /* warn in case of mismatch if irq base is specified */ | ||
243 | if (p->config.irq_base) { | ||
244 | if (p->config.irq_base != p->irq[0].domain_irq) | ||
245 | dev_warn(&pdev->dev, "irq base mismatch (%d/%d)\n", | ||
246 | p->config.irq_base, p->irq[0].domain_irq); | ||
247 | } | ||
248 | |||
249 | return 0; | ||
250 | err3: | ||
251 | for (; k >= 0; k--) | ||
252 | free_irq(p->irq[k - 1].requested_irq, &p->irq[k - 1]); | ||
253 | |||
254 | irq_domain_remove(p->irq_domain); | ||
255 | err2: | ||
256 | iounmap(p->iomem); | ||
257 | err1: | ||
258 | kfree(p); | ||
259 | err0: | ||
260 | return ret; | ||
261 | } | ||
262 | |||
263 | static int irqc_remove(struct platform_device *pdev) | ||
264 | { | ||
265 | struct irqc_priv *p = platform_get_drvdata(pdev); | ||
266 | int k; | ||
267 | |||
268 | for (k = 0; k < p->number_of_irqs; k++) | ||
269 | free_irq(p->irq[k].requested_irq, &p->irq[k]); | ||
270 | |||
271 | irq_domain_remove(p->irq_domain); | ||
272 | iounmap(p->iomem); | ||
273 | kfree(p); | ||
274 | return 0; | ||
275 | } | ||
276 | |||
277 | static const struct of_device_id irqc_dt_ids[] = { | ||
278 | { .compatible = "renesas,irqc", }, | ||
279 | {}, | ||
280 | }; | ||
281 | MODULE_DEVICE_TABLE(of, irqc_dt_ids); | ||
282 | |||
283 | static struct platform_driver irqc_device_driver = { | ||
284 | .probe = irqc_probe, | ||
285 | .remove = irqc_remove, | ||
286 | .driver = { | ||
287 | .name = "renesas_irqc", | ||
288 | .of_match_table = irqc_dt_ids, | ||
289 | .owner = THIS_MODULE, | ||
290 | } | ||
291 | }; | ||
292 | |||
293 | static int __init irqc_init(void) | ||
294 | { | ||
295 | return platform_driver_register(&irqc_device_driver); | ||
296 | } | ||
297 | postcore_initcall(irqc_init); | ||
298 | |||
299 | static void __exit irqc_exit(void) | ||
300 | { | ||
301 | platform_driver_unregister(&irqc_device_driver); | ||
302 | } | ||
303 | module_exit(irqc_exit); | ||
304 | |||
305 | MODULE_AUTHOR("Magnus Damm"); | ||
306 | MODULE_DESCRIPTION("Renesas IRQC Driver"); | ||
307 | MODULE_LICENSE("GPL v2"); | ||