diff options
-rw-r--r-- | drivers/irqchip/Kconfig | 4 | ||||
-rw-r--r-- | drivers/irqchip/Makefile | 1 | ||||
-rw-r--r-- | drivers/irqchip/irq-renesas-intc-irqpin.c | 464 | ||||
-rw-r--r-- | include/linux/platform_data/irq-renesas-intc-irqpin.h | 10 |
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 | ||
28 | config RENESAS_INTC_IRQPIN | ||
29 | bool | ||
30 | select IRQ_DOMAIN | ||
31 | |||
28 | config VERSATILE_FPGA_IRQ | 32 | config 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 | |||
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 | ||
11 | obj-$(CONFIG_VERSATILE_FPGA_IRQ) += irq-versatile-fpga.o | 12 | 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..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 | |||
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 irq; | ||
65 | struct intc_irqpin_priv *p; | ||
66 | }; | ||
67 | |||
68 | struct 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 | |||
78 | static unsigned long intc_irqpin_read32(void __iomem *iomem) | ||
79 | { | ||
80 | return ioread32(iomem); | ||
81 | } | ||
82 | |||
83 | static unsigned long intc_irqpin_read8(void __iomem *iomem) | ||
84 | { | ||
85 | return ioread8(iomem); | ||
86 | } | ||
87 | |||
88 | static void intc_irqpin_write32(void __iomem *iomem, unsigned long data) | ||
89 | { | ||
90 | iowrite32(data, iomem); | ||
91 | } | ||
92 | |||
93 | static void intc_irqpin_write8(void __iomem *iomem, unsigned long data) | ||
94 | { | ||
95 | iowrite8(data, iomem); | ||
96 | } | ||
97 | |||
98 | static 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 | |||
105 | static 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 | |||
112 | static 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 | |||
118 | static 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 | |||
124 | static DEFINE_RAW_SPINLOCK(intc_irqpin_lock); /* only used by slow path */ | ||
125 | |||
126 | static 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 | |||
143 | static 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 | |||
154 | static 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 | |||
169 | static 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 | |||
176 | static 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 | |||
185 | static 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 | |||
194 | static 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 | |||
203 | static 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 | |||
215 | static 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 | |||
223 | static 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 | |||
235 | static 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 | |||
253 | static 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 | |||
265 | static struct irq_domain_ops intc_irqpin_irq_domain_ops = { | ||
266 | .map = intc_irqpin_irq_domain_map, | ||
267 | }; | ||
268 | |||
269 | static 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 | |||
411 | err3: | ||
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); | ||
416 | err2: | ||
417 | for (k = 0; k < INTC_IRQPIN_REG_NR; k++) | ||
418 | iounmap(p->iomem[k].iomem); | ||
419 | err1: | ||
420 | kfree(p); | ||
421 | err0: | ||
422 | return ret; | ||
423 | } | ||
424 | |||
425 | static 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 | |||
442 | static 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 | |||
450 | static int __init intc_irqpin_init(void) | ||
451 | { | ||
452 | return platform_driver_register(&intc_irqpin_device_driver); | ||
453 | } | ||
454 | postcore_initcall(intc_irqpin_init); | ||
455 | |||
456 | static void __exit intc_irqpin_exit(void) | ||
457 | { | ||
458 | platform_driver_unregister(&intc_irqpin_device_driver); | ||
459 | } | ||
460 | module_exit(intc_irqpin_exit); | ||
461 | |||
462 | MODULE_AUTHOR("Magnus Damm"); | ||
463 | MODULE_DESCRIPTION("Renesas INTC External IRQ Pin Driver"); | ||
464 | MODULE_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 | |||
4 | struct 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__ */ | ||