aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/irqchip/Kconfig4
-rw-r--r--drivers/irqchip/Makefile1
-rw-r--r--drivers/irqchip/irq-renesas-intc-irqpin.c464
-rw-r--r--include/linux/platform_data/irq-renesas-intc-irqpin.h10
4 files changed, 479 insertions, 0 deletions
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index a350969e5efe..0f5f1c3825bc 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -25,6 +25,10 @@ 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
28config RENESAS_INTC_IRQPIN
29 bool
30 select IRQ_DOMAIN
31
28config VERSATILE_FPGA_IRQ 32config VERSATILE_FPGA_IRQ
29 bool 33 bool
30 select IRQ_DOMAIN 34 select IRQ_DOMAIN
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index 98e3b87bdf1b..1aaa4073ab60 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -8,4 +8,5 @@ obj-$(CONFIG_ARCH_SUNXI) += irq-sunxi.o
8obj-$(CONFIG_ARCH_SPEAR3XX) += spear-shirq.o 8obj-$(CONFIG_ARCH_SPEAR3XX) += spear-shirq.o
9obj-$(CONFIG_ARM_GIC) += irq-gic.o 9obj-$(CONFIG_ARM_GIC) += irq-gic.o
10obj-$(CONFIG_ARM_VIC) += irq-vic.o 10obj-$(CONFIG_ARM_VIC) += irq-vic.o
11obj-$(CONFIG_RENESAS_INTC_IRQPIN) += irq-renesas-intc-irqpin.o
11obj-$(CONFIG_VERSATILE_FPGA_IRQ) += irq-versatile-fpga.o 12obj-$(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..1e5058a56517
--- /dev/null
+++ b/drivers/irqchip/irq-renesas-intc-irqpin.c
@@ -0,0 +1,464 @@
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
55struct 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
62struct intc_irqpin_irq {
63 int hw_irq;
64 int irq;
65 struct intc_irqpin_priv *p;
66};
67
68struct intc_irqpin_priv {
69 struct intc_irqpin_iomem iomem[INTC_IRQPIN_REG_NR];
70 struct intc_irqpin_irq irq[INTC_IRQPIN_MAX];
71 struct renesas_intc_irqpin_config config;
72 unsigned int number_of_irqs;
73 struct platform_device *pdev;
74 struct irq_chip irq_chip;
75 struct irq_domain *irq_domain;
76};
77
78static unsigned long intc_irqpin_read32(void __iomem *iomem)
79{
80 return ioread32(iomem);
81}
82
83static unsigned long intc_irqpin_read8(void __iomem *iomem)
84{
85 return ioread8(iomem);
86}
87
88static void intc_irqpin_write32(void __iomem *iomem, unsigned long data)
89{
90 iowrite32(data, iomem);
91}
92
93static void intc_irqpin_write8(void __iomem *iomem, unsigned long data)
94{
95 iowrite8(data, iomem);
96}
97
98static inline unsigned long intc_irqpin_read(struct intc_irqpin_priv *p,
99 int reg)
100{
101 struct intc_irqpin_iomem *i = &p->iomem[reg];
102 return i->read(i->iomem);
103}
104
105static inline void intc_irqpin_write(struct intc_irqpin_priv *p,
106 int reg, unsigned long data)
107{
108 struct intc_irqpin_iomem *i = &p->iomem[reg];
109 i->write(i->iomem, data);
110}
111
112static inline unsigned long intc_irqpin_hwirq_mask(struct intc_irqpin_priv *p,
113 int reg, int hw_irq)
114{
115 return BIT((p->iomem[reg].width - 1) - hw_irq);
116}
117
118static inline void intc_irqpin_irq_write_hwirq(struct intc_irqpin_priv *p,
119 int reg, int hw_irq)
120{
121 intc_irqpin_write(p, reg, intc_irqpin_hwirq_mask(p, reg, hw_irq));
122}
123
124static DEFINE_RAW_SPINLOCK(intc_irqpin_lock); /* only used by slow path */
125
126static void intc_irqpin_read_modify_write(struct intc_irqpin_priv *p,
127 int reg, int shift,
128 int width, int value)
129{
130 unsigned long flags;
131 unsigned long tmp;
132
133 raw_spin_lock_irqsave(&intc_irqpin_lock, flags);
134
135 tmp = intc_irqpin_read(p, reg);
136 tmp &= ~(((1 << width) - 1) << shift);
137 tmp |= value << shift;
138 intc_irqpin_write(p, reg, tmp);
139
140 raw_spin_unlock_irqrestore(&intc_irqpin_lock, flags);
141}
142
143static void intc_irqpin_mask_unmask_prio(struct intc_irqpin_priv *p,
144 int irq, int do_mask)
145{
146 int bitfield_width = 4; /* PRIO assumed to have fixed bitfield width */
147 int shift = (7 - irq) * bitfield_width; /* PRIO assumed to be 32-bit */
148
149 intc_irqpin_read_modify_write(p, INTC_IRQPIN_REG_PRIO,
150 shift, bitfield_width,
151 do_mask ? 0 : (1 << bitfield_width) - 1);
152}
153
154static int intc_irqpin_set_sense(struct intc_irqpin_priv *p, int irq, int value)
155{
156 int bitfield_width = p->config.sense_bitfield_width;
157 int shift = (7 - irq) * bitfield_width; /* SENSE assumed to be 32-bit */
158
159 dev_dbg(&p->pdev->dev, "sense irq = %d, mode = %d\n", irq, value);
160
161 if (value >= (1 << bitfield_width))
162 return -EINVAL;
163
164 intc_irqpin_read_modify_write(p, INTC_IRQPIN_REG_SENSE, shift,
165 bitfield_width, value);
166 return 0;
167}
168
169static void intc_irqpin_dbg(struct intc_irqpin_irq *i, char *str)
170{
171 dev_dbg(&i->p->pdev->dev, "%s (%d:%d:%d)\n",
172 str, i->irq, i->hw_irq,
173 irq_find_mapping(i->p->irq_domain, i->hw_irq));
174}
175
176static void intc_irqpin_irq_enable(struct irq_data *d)
177{
178 struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d);
179 int hw_irq = irqd_to_hwirq(d);
180
181 intc_irqpin_dbg(&p->irq[hw_irq], "enable");
182 intc_irqpin_irq_write_hwirq(p, INTC_IRQPIN_REG_CLEAR, hw_irq);
183}
184
185static void intc_irqpin_irq_disable(struct irq_data *d)
186{
187 struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d);
188 int hw_irq = irqd_to_hwirq(d);
189
190 intc_irqpin_dbg(&p->irq[hw_irq], "disable");
191 intc_irqpin_irq_write_hwirq(p, INTC_IRQPIN_REG_MASK, hw_irq);
192}
193
194static void intc_irqpin_irq_enable_force(struct irq_data *d)
195{
196 struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d);
197 int irq = p->irq[irqd_to_hwirq(d)].irq;
198
199 intc_irqpin_irq_enable(d);
200 irq_get_chip(irq)->irq_unmask(irq_get_irq_data(irq));
201}
202
203static void intc_irqpin_irq_disable_force(struct irq_data *d)
204{
205 struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d);
206 int irq = p->irq[irqd_to_hwirq(d)].irq;
207
208 irq_get_chip(irq)->irq_mask(irq_get_irq_data(irq));
209 intc_irqpin_irq_disable(d);
210}
211
212#define INTC_IRQ_SENSE_VALID 0x10
213#define INTC_IRQ_SENSE(x) (x + INTC_IRQ_SENSE_VALID)
214
215static unsigned char intc_irqpin_sense[IRQ_TYPE_SENSE_MASK + 1] = {
216 [IRQ_TYPE_EDGE_FALLING] = INTC_IRQ_SENSE(0x00),
217 [IRQ_TYPE_EDGE_RISING] = INTC_IRQ_SENSE(0x01),
218 [IRQ_TYPE_LEVEL_LOW] = INTC_IRQ_SENSE(0x02),
219 [IRQ_TYPE_LEVEL_HIGH] = INTC_IRQ_SENSE(0x03),
220 [IRQ_TYPE_EDGE_BOTH] = INTC_IRQ_SENSE(0x04),
221};
222
223static int intc_irqpin_irq_set_type(struct irq_data *d, unsigned int type)
224{
225 unsigned char value = intc_irqpin_sense[type & IRQ_TYPE_SENSE_MASK];
226 struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d);
227
228 if (!(value & INTC_IRQ_SENSE_VALID))
229 return -EINVAL;
230
231 return intc_irqpin_set_sense(p, irqd_to_hwirq(d),
232 value ^ INTC_IRQ_SENSE_VALID);
233}
234
235static irqreturn_t intc_irqpin_irq_handler(int irq, void *dev_id)
236{
237 struct intc_irqpin_irq *i = dev_id;
238 struct intc_irqpin_priv *p = i->p;
239 unsigned long bit;
240
241 intc_irqpin_dbg(i, "demux1");
242 bit = intc_irqpin_hwirq_mask(p, INTC_IRQPIN_REG_SOURCE, i->hw_irq);
243
244 if (intc_irqpin_read(p, INTC_IRQPIN_REG_SOURCE) & bit) {
245 intc_irqpin_write(p, INTC_IRQPIN_REG_SOURCE, ~bit);
246 intc_irqpin_dbg(i, "demux2");
247 generic_handle_irq(irq_find_mapping(p->irq_domain, i->hw_irq));
248 return IRQ_HANDLED;
249 }
250 return IRQ_NONE;
251}
252
253static int intc_irqpin_irq_domain_map(struct irq_domain *h, unsigned int virq,
254 irq_hw_number_t hw)
255{
256 struct intc_irqpin_priv *p = h->host_data;
257
258 intc_irqpin_dbg(&p->irq[hw], "map");
259 irq_set_chip_data(virq, h->host_data);
260 irq_set_chip_and_handler(virq, &p->irq_chip, handle_level_irq);
261 set_irq_flags(virq, IRQF_VALID); /* kill me now */
262 return 0;
263}
264
265static struct irq_domain_ops intc_irqpin_irq_domain_ops = {
266 .map = intc_irqpin_irq_domain_map,
267};
268
269static int intc_irqpin_probe(struct platform_device *pdev)
270{
271 struct renesas_intc_irqpin_config *pdata = pdev->dev.platform_data;
272 struct intc_irqpin_priv *p;
273 struct intc_irqpin_iomem *i;
274 struct resource *io[INTC_IRQPIN_REG_NR];
275 struct resource *irq;
276 struct irq_chip *irq_chip;
277 void (*enable_fn)(struct irq_data *d);
278 void (*disable_fn)(struct irq_data *d);
279 const char *name = dev_name(&pdev->dev);
280 int ret;
281 int k;
282
283 p = kzalloc(sizeof(*p), GFP_KERNEL);
284 if (!p) {
285 dev_err(&pdev->dev, "failed to allocate driver data\n");
286 ret = -ENOMEM;
287 goto err0;
288 }
289
290 /* deal with driver instance configuration */
291 if (pdata)
292 memcpy(&p->config, pdata, sizeof(*pdata));
293 if (!p->config.sense_bitfield_width)
294 p->config.sense_bitfield_width = 4; /* default to 4 bits */
295
296 p->pdev = pdev;
297 platform_set_drvdata(pdev, p);
298
299 /* get hold of manadatory IOMEM */
300 for (k = 0; k < INTC_IRQPIN_REG_NR; k++) {
301 io[k] = platform_get_resource(pdev, IORESOURCE_MEM, k);
302 if (!io[k]) {
303 dev_err(&pdev->dev, "not enough IOMEM resources\n");
304 ret = -EINVAL;
305 goto err1;
306 }
307 }
308
309 /* allow any number of IRQs between 1 and INTC_IRQPIN_MAX */
310 for (k = 0; k < INTC_IRQPIN_MAX; k++) {
311 irq = platform_get_resource(pdev, IORESOURCE_IRQ, k);
312 if (!irq)
313 break;
314
315 p->irq[k].hw_irq = k;
316 p->irq[k].p = p;
317 p->irq[k].irq = irq->start;
318 }
319
320 p->number_of_irqs = k;
321 if (p->number_of_irqs < 1) {
322 dev_err(&pdev->dev, "not enough IRQ resources\n");
323 ret = -EINVAL;
324 goto err1;
325 }
326
327 /* ioremap IOMEM and setup read/write callbacks */
328 for (k = 0; k < INTC_IRQPIN_REG_NR; k++) {
329 i = &p->iomem[k];
330
331 switch (resource_size(io[k])) {
332 case 1:
333 i->width = 8;
334 i->read = intc_irqpin_read8;
335 i->write = intc_irqpin_write8;
336 break;
337 case 4:
338 i->width = 32;
339 i->read = intc_irqpin_read32;
340 i->write = intc_irqpin_write32;
341 break;
342 default:
343 dev_err(&pdev->dev, "IOMEM size mismatch\n");
344 ret = -EINVAL;
345 goto err2;
346 }
347
348 i->iomem = ioremap_nocache(io[k]->start, resource_size(io[k]));
349 if (!i->iomem) {
350 dev_err(&pdev->dev, "failed to remap IOMEM\n");
351 ret = -ENXIO;
352 goto err2;
353 }
354 }
355
356 /* mask all interrupts using priority */
357 for (k = 0; k < p->number_of_irqs; k++)
358 intc_irqpin_mask_unmask_prio(p, k, 1);
359
360 /* use more severe masking method if requested */
361 if (p->config.control_parent) {
362 enable_fn = intc_irqpin_irq_enable_force;
363 disable_fn = intc_irqpin_irq_disable_force;
364 } else {
365 enable_fn = intc_irqpin_irq_enable;
366 disable_fn = intc_irqpin_irq_disable;
367 }
368
369 irq_chip = &p->irq_chip;
370 irq_chip->name = name;
371 irq_chip->irq_mask = disable_fn;
372 irq_chip->irq_unmask = enable_fn;
373 irq_chip->irq_enable = enable_fn;
374 irq_chip->irq_disable = disable_fn;
375 irq_chip->irq_set_type = intc_irqpin_irq_set_type;
376 irq_chip->flags = IRQCHIP_SKIP_SET_WAKE;
377
378 p->irq_domain = irq_domain_add_simple(pdev->dev.of_node,
379 p->number_of_irqs,
380 p->config.irq_base,
381 &intc_irqpin_irq_domain_ops, p);
382 if (!p->irq_domain) {
383 ret = -ENXIO;
384 dev_err(&pdev->dev, "cannot initialize irq domain\n");
385 goto err2;
386 }
387
388 /* request and set priority on interrupts one by one */
389 for (k = 0; k < p->number_of_irqs; k++) {
390 if (request_irq(p->irq[k].irq, intc_irqpin_irq_handler,
391 0, name, &p->irq[k])) {
392 dev_err(&pdev->dev, "failed to request low IRQ\n");
393 ret = -ENOENT;
394 goto err3;
395 }
396 intc_irqpin_mask_unmask_prio(p, k, 0);
397 }
398
399 dev_info(&pdev->dev, "driving %d irqs\n", p->number_of_irqs);
400
401 /* warn in case of mismatch if irq base is specified */
402 if (p->config.irq_base) {
403 k = irq_find_mapping(p->irq_domain, 0);
404 if (p->config.irq_base != k)
405 dev_warn(&pdev->dev, "irq base mismatch (%d/%d)\n",
406 p->config.irq_base, k);
407 }
408
409 return 0;
410
411err3:
412 for (; k >= 0; k--)
413 free_irq(p->irq[k - 1].irq, &p->irq[k - 1]);
414
415 irq_domain_remove(p->irq_domain);
416err2:
417 for (k = 0; k < INTC_IRQPIN_REG_NR; k++)
418 iounmap(p->iomem[k].iomem);
419err1:
420 kfree(p);
421err0:
422 return ret;
423}
424
425static int intc_irqpin_remove(struct platform_device *pdev)
426{
427 struct intc_irqpin_priv *p = platform_get_drvdata(pdev);
428 int k;
429
430 for (k = 0; k < p->number_of_irqs; k++)
431 free_irq(p->irq[k].irq, &p->irq[k]);
432
433 irq_domain_remove(p->irq_domain);
434
435 for (k = 0; k < INTC_IRQPIN_REG_NR; k++)
436 iounmap(p->iomem[k].iomem);
437
438 kfree(p);
439 return 0;
440}
441
442static struct platform_driver intc_irqpin_device_driver = {
443 .probe = intc_irqpin_probe,
444 .remove = intc_irqpin_remove,
445 .driver = {
446 .name = "renesas_intc_irqpin",
447 }
448};
449
450static int __init intc_irqpin_init(void)
451{
452 return platform_driver_register(&intc_irqpin_device_driver);
453}
454postcore_initcall(intc_irqpin_init);
455
456static void __exit intc_irqpin_exit(void)
457{
458 platform_driver_unregister(&intc_irqpin_device_driver);
459}
460module_exit(intc_irqpin_exit);
461
462MODULE_AUTHOR("Magnus Damm");
463MODULE_DESCRIPTION("Renesas INTC External IRQ Pin Driver");
464MODULE_LICENSE("GPL v2");
diff --git a/include/linux/platform_data/irq-renesas-intc-irqpin.h b/include/linux/platform_data/irq-renesas-intc-irqpin.h
new file mode 100644
index 000000000000..00ccac34dac8
--- /dev/null
+++ b/include/linux/platform_data/irq-renesas-intc-irqpin.h
@@ -0,0 +1,10 @@
1#ifndef __IRQ_RENESAS_INTC_IRQPIN_H__
2#define __IRQ_RENESAS_INTC_IRQPIN_H__
3
4struct renesas_intc_irqpin_config {
5 unsigned int sense_bitfield_width;
6 unsigned int irq_base;
7 bool control_parent;
8};
9
10#endif /* __IRQ_RENESAS_INTC_IRQPIN_H__ */