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