Commit | Line | Data |
---|---|---|
752bcb4d G |
1 | /* |
2 | * linux/arch/unicore32/kernel/irq.c | |
3 | * | |
4 | * Code specific to PKUnity SoC and UniCore ISA | |
5 | * | |
6 | * Copyright (C) 2001-2010 GUAN Xue-tao | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or modify | |
9 | * it under the terms of the GNU General Public License version 2 as | |
10 | * published by the Free Software Foundation. | |
11 | */ | |
12 | #include <linux/kernel_stat.h> | |
13 | #include <linux/module.h> | |
14 | #include <linux/signal.h> | |
15 | #include <linux/ioport.h> | |
16 | #include <linux/interrupt.h> | |
17 | #include <linux/irq.h> | |
18 | #include <linux/random.h> | |
19 | #include <linux/smp.h> | |
20 | #include <linux/init.h> | |
21 | #include <linux/seq_file.h> | |
22 | #include <linux/errno.h> | |
23 | #include <linux/list.h> | |
24 | #include <linux/kallsyms.h> | |
25 | #include <linux/proc_fs.h> | |
26 | #include <linux/sysdev.h> | |
27 | #include <linux/gpio.h> | |
28 | ||
29 | #include <asm/system.h> | |
30 | #include <mach/hardware.h> | |
31 | ||
32 | #include "setup.h" | |
33 | ||
34 | /* | |
35 | * PKUnity GPIO edge detection for IRQs: | |
36 | * IRQs are generated on Falling-Edge, Rising-Edge, or both. | |
37 | * Use this instead of directly setting GRER/GFER. | |
38 | */ | |
39 | static int GPIO_IRQ_rising_edge; | |
40 | static int GPIO_IRQ_falling_edge; | |
41 | static int GPIO_IRQ_mask = 0; | |
42 | ||
43 | #define GPIO_MASK(irq) (1 << (irq - IRQ_GPIO0)) | |
44 | ||
36a8b8c3 | 45 | static int puv3_gpio_type(struct irq_data *d, unsigned int type) |
752bcb4d G |
46 | { |
47 | unsigned int mask; | |
48 | ||
36a8b8c3 G |
49 | if (d->irq < IRQ_GPIOHIGH) |
50 | mask = 1 << d->irq; | |
752bcb4d | 51 | else |
36a8b8c3 | 52 | mask = GPIO_MASK(d->irq); |
752bcb4d G |
53 | |
54 | if (type == IRQ_TYPE_PROBE) { | |
55 | if ((GPIO_IRQ_rising_edge | GPIO_IRQ_falling_edge) & mask) | |
56 | return 0; | |
57 | type = IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING; | |
58 | } | |
59 | ||
60 | if (type & IRQ_TYPE_EDGE_RISING) | |
61 | GPIO_IRQ_rising_edge |= mask; | |
62 | else | |
63 | GPIO_IRQ_rising_edge &= ~mask; | |
64 | if (type & IRQ_TYPE_EDGE_FALLING) | |
65 | GPIO_IRQ_falling_edge |= mask; | |
66 | else | |
67 | GPIO_IRQ_falling_edge &= ~mask; | |
68 | ||
e5abf78b G |
69 | writel(GPIO_IRQ_rising_edge & GPIO_IRQ_mask, GPIO_GRER); |
70 | writel(GPIO_IRQ_falling_edge & GPIO_IRQ_mask, GPIO_GFER); | |
752bcb4d G |
71 | |
72 | return 0; | |
73 | } | |
74 | ||
75 | /* | |
76 | * GPIO IRQs must be acknowledged. This is for IRQs from 0 to 7. | |
77 | */ | |
36a8b8c3 | 78 | static void puv3_low_gpio_ack(struct irq_data *d) |
752bcb4d | 79 | { |
e5abf78b | 80 | writel((1 << d->irq), GPIO_GEDR); |
752bcb4d G |
81 | } |
82 | ||
36a8b8c3 | 83 | static void puv3_low_gpio_mask(struct irq_data *d) |
752bcb4d | 84 | { |
e5abf78b | 85 | writel(readl(INTC_ICMR) & ~(1 << d->irq), INTC_ICMR); |
752bcb4d G |
86 | } |
87 | ||
36a8b8c3 | 88 | static void puv3_low_gpio_unmask(struct irq_data *d) |
752bcb4d | 89 | { |
e5abf78b | 90 | writel(readl(INTC_ICMR) | (1 << d->irq), INTC_ICMR); |
752bcb4d G |
91 | } |
92 | ||
36a8b8c3 | 93 | static int puv3_low_gpio_wake(struct irq_data *d, unsigned int on) |
752bcb4d G |
94 | { |
95 | if (on) | |
e5abf78b | 96 | writel(readl(PM_PWER) | (1 << d->irq), PM_PWER); |
752bcb4d | 97 | else |
e5abf78b | 98 | writel(readl(PM_PWER) & ~(1 << d->irq), PM_PWER); |
752bcb4d G |
99 | return 0; |
100 | } | |
101 | ||
102 | static struct irq_chip puv3_low_gpio_chip = { | |
103 | .name = "GPIO-low", | |
36a8b8c3 G |
104 | .irq_ack = puv3_low_gpio_ack, |
105 | .irq_mask = puv3_low_gpio_mask, | |
106 | .irq_unmask = puv3_low_gpio_unmask, | |
107 | .irq_set_type = puv3_gpio_type, | |
108 | .irq_set_wake = puv3_low_gpio_wake, | |
752bcb4d G |
109 | }; |
110 | ||
111 | /* | |
112 | * IRQ8 (GPIO0 through 27) handler. We enter here with the | |
113 | * irq_controller_lock held, and IRQs disabled. Decode the IRQ | |
114 | * and call the handler. | |
115 | */ | |
116 | static void | |
117 | puv3_gpio_handler(unsigned int irq, struct irq_desc *desc) | |
118 | { | |
119 | unsigned int mask; | |
120 | ||
e5abf78b | 121 | mask = readl(GPIO_GEDR); |
752bcb4d G |
122 | do { |
123 | /* | |
124 | * clear down all currently active IRQ sources. | |
125 | * We will be processing them all. | |
126 | */ | |
e5abf78b | 127 | writel(mask, GPIO_GEDR); |
752bcb4d G |
128 | |
129 | irq = IRQ_GPIO0; | |
130 | do { | |
131 | if (mask & 1) | |
132 | generic_handle_irq(irq); | |
133 | mask >>= 1; | |
134 | irq++; | |
135 | } while (mask); | |
e5abf78b | 136 | mask = readl(GPIO_GEDR); |
752bcb4d G |
137 | } while (mask); |
138 | } | |
139 | ||
140 | /* | |
141 | * GPIO0-27 edge IRQs need to be handled specially. | |
142 | * In addition, the IRQs are all collected up into one bit in the | |
143 | * interrupt controller registers. | |
144 | */ | |
36a8b8c3 | 145 | static void puv3_high_gpio_ack(struct irq_data *d) |
752bcb4d | 146 | { |
36a8b8c3 | 147 | unsigned int mask = GPIO_MASK(d->irq); |
752bcb4d | 148 | |
e5abf78b | 149 | writel(mask, GPIO_GEDR); |
752bcb4d G |
150 | } |
151 | ||
36a8b8c3 | 152 | static void puv3_high_gpio_mask(struct irq_data *d) |
752bcb4d | 153 | { |
36a8b8c3 | 154 | unsigned int mask = GPIO_MASK(d->irq); |
752bcb4d G |
155 | |
156 | GPIO_IRQ_mask &= ~mask; | |
157 | ||
e5abf78b G |
158 | writel(readl(GPIO_GRER) & ~mask, GPIO_GRER); |
159 | writel(readl(GPIO_GFER) & ~mask, GPIO_GFER); | |
752bcb4d G |
160 | } |
161 | ||
36a8b8c3 | 162 | static void puv3_high_gpio_unmask(struct irq_data *d) |
752bcb4d | 163 | { |
36a8b8c3 | 164 | unsigned int mask = GPIO_MASK(d->irq); |
752bcb4d G |
165 | |
166 | GPIO_IRQ_mask |= mask; | |
167 | ||
e5abf78b G |
168 | writel(GPIO_IRQ_rising_edge & GPIO_IRQ_mask, GPIO_GRER); |
169 | writel(GPIO_IRQ_falling_edge & GPIO_IRQ_mask, GPIO_GFER); | |
752bcb4d G |
170 | } |
171 | ||
36a8b8c3 | 172 | static int puv3_high_gpio_wake(struct irq_data *d, unsigned int on) |
752bcb4d G |
173 | { |
174 | if (on) | |
e5abf78b | 175 | writel(readl(PM_PWER) | PM_PWER_GPIOHIGH, PM_PWER); |
752bcb4d | 176 | else |
e5abf78b | 177 | writel(readl(PM_PWER) & ~PM_PWER_GPIOHIGH, PM_PWER); |
752bcb4d G |
178 | return 0; |
179 | } | |
180 | ||
181 | static struct irq_chip puv3_high_gpio_chip = { | |
182 | .name = "GPIO-high", | |
36a8b8c3 G |
183 | .irq_ack = puv3_high_gpio_ack, |
184 | .irq_mask = puv3_high_gpio_mask, | |
185 | .irq_unmask = puv3_high_gpio_unmask, | |
186 | .irq_set_type = puv3_gpio_type, | |
187 | .irq_set_wake = puv3_high_gpio_wake, | |
752bcb4d G |
188 | }; |
189 | ||
190 | /* | |
191 | * We don't need to ACK IRQs on the PKUnity unless they're GPIOs | |
192 | * this is for internal IRQs i.e. from 8 to 31. | |
193 | */ | |
36a8b8c3 | 194 | static void puv3_mask_irq(struct irq_data *d) |
752bcb4d | 195 | { |
e5abf78b | 196 | writel(readl(INTC_ICMR) & ~(1 << d->irq), INTC_ICMR); |
752bcb4d G |
197 | } |
198 | ||
36a8b8c3 | 199 | static void puv3_unmask_irq(struct irq_data *d) |
752bcb4d | 200 | { |
e5abf78b | 201 | writel(readl(INTC_ICMR) | (1 << d->irq), INTC_ICMR); |
752bcb4d G |
202 | } |
203 | ||
204 | /* | |
205 | * Apart form GPIOs, only the RTC alarm can be a wakeup event. | |
206 | */ | |
36a8b8c3 | 207 | static int puv3_set_wake(struct irq_data *d, unsigned int on) |
752bcb4d | 208 | { |
36a8b8c3 | 209 | if (d->irq == IRQ_RTCAlarm) { |
752bcb4d | 210 | if (on) |
e5abf78b | 211 | writel(readl(PM_PWER) | PM_PWER_RTC, PM_PWER); |
752bcb4d | 212 | else |
e5abf78b | 213 | writel(readl(PM_PWER) & ~PM_PWER_RTC, PM_PWER); |
752bcb4d G |
214 | return 0; |
215 | } | |
216 | return -EINVAL; | |
217 | } | |
218 | ||
219 | static struct irq_chip puv3_normal_chip = { | |
220 | .name = "PKUnity-v3", | |
36a8b8c3 G |
221 | .irq_ack = puv3_mask_irq, |
222 | .irq_mask = puv3_mask_irq, | |
223 | .irq_unmask = puv3_unmask_irq, | |
224 | .irq_set_wake = puv3_set_wake, | |
752bcb4d G |
225 | }; |
226 | ||
227 | static struct resource irq_resource = { | |
228 | .name = "irqs", | |
1cf46c42 G |
229 | .start = io_v2p(PKUNITY_INTC_BASE), |
230 | .end = io_v2p(PKUNITY_INTC_BASE) + 0xFFFFF, | |
752bcb4d G |
231 | }; |
232 | ||
233 | static struct puv3_irq_state { | |
234 | unsigned int saved; | |
235 | unsigned int icmr; | |
236 | unsigned int iclr; | |
237 | unsigned int iccr; | |
238 | } puv3_irq_state; | |
239 | ||
240 | static int puv3_irq_suspend(struct sys_device *dev, pm_message_t state) | |
241 | { | |
242 | struct puv3_irq_state *st = &puv3_irq_state; | |
243 | ||
244 | st->saved = 1; | |
e5abf78b G |
245 | st->icmr = readl(INTC_ICMR); |
246 | st->iclr = readl(INTC_ICLR); | |
247 | st->iccr = readl(INTC_ICCR); | |
752bcb4d G |
248 | |
249 | /* | |
250 | * Disable all GPIO-based interrupts. | |
251 | */ | |
e5abf78b | 252 | writel(readl(INTC_ICMR) & ~(0x1ff), INTC_ICMR); |
752bcb4d G |
253 | |
254 | /* | |
255 | * Set the appropriate edges for wakeup. | |
256 | */ | |
e5abf78b G |
257 | writel(readl(PM_PWER) & GPIO_IRQ_rising_edge, GPIO_GRER); |
258 | writel(readl(PM_PWER) & GPIO_IRQ_falling_edge, GPIO_GFER); | |
752bcb4d G |
259 | |
260 | /* | |
261 | * Clear any pending GPIO interrupts. | |
262 | */ | |
e5abf78b | 263 | writel(readl(GPIO_GEDR), GPIO_GEDR); |
752bcb4d G |
264 | |
265 | return 0; | |
266 | } | |
267 | ||
268 | static int puv3_irq_resume(struct sys_device *dev) | |
269 | { | |
270 | struct puv3_irq_state *st = &puv3_irq_state; | |
271 | ||
272 | if (st->saved) { | |
e5abf78b G |
273 | writel(st->iccr, INTC_ICCR); |
274 | writel(st->iclr, INTC_ICLR); | |
752bcb4d | 275 | |
e5abf78b G |
276 | writel(GPIO_IRQ_rising_edge & GPIO_IRQ_mask, GPIO_GRER); |
277 | writel(GPIO_IRQ_falling_edge & GPIO_IRQ_mask, GPIO_GFER); | |
752bcb4d | 278 | |
e5abf78b | 279 | writel(st->icmr, INTC_ICMR); |
752bcb4d G |
280 | } |
281 | return 0; | |
282 | } | |
283 | ||
284 | static struct sysdev_class puv3_irq_sysclass = { | |
285 | .name = "pkunity-irq", | |
286 | .suspend = puv3_irq_suspend, | |
287 | .resume = puv3_irq_resume, | |
288 | }; | |
289 | ||
290 | static struct sys_device puv3_irq_device = { | |
291 | .id = 0, | |
292 | .cls = &puv3_irq_sysclass, | |
293 | }; | |
294 | ||
295 | static int __init puv3_irq_init_devicefs(void) | |
296 | { | |
297 | sysdev_class_register(&puv3_irq_sysclass); | |
298 | return sysdev_register(&puv3_irq_device); | |
299 | } | |
300 | ||
301 | device_initcall(puv3_irq_init_devicefs); | |
302 | ||
303 | void __init init_IRQ(void) | |
304 | { | |
305 | unsigned int irq; | |
306 | ||
307 | request_resource(&iomem_resource, &irq_resource); | |
308 | ||
309 | /* disable all IRQs */ | |
e5abf78b | 310 | writel(0, INTC_ICMR); |
752bcb4d G |
311 | |
312 | /* all IRQs are IRQ, not REAL */ | |
e5abf78b | 313 | writel(0, INTC_ICLR); |
752bcb4d G |
314 | |
315 | /* clear all GPIO edge detects */ | |
e5abf78b G |
316 | writel(FMASK(8, 0) & ~FIELD(1, 1, GPI_SOFF_REQ), GPIO_GPIR); |
317 | writel(0, GPIO_GFER); | |
318 | writel(0, GPIO_GRER); | |
319 | writel(0x0FFFFFFF, GPIO_GEDR); | |
752bcb4d | 320 | |
e5abf78b | 321 | writel(1, INTC_ICCR); |
752bcb4d G |
322 | |
323 | for (irq = 0; irq < IRQ_GPIOHIGH; irq++) { | |
e1f5ce81 TG |
324 | irq_set_chip(irq, &puv3_low_gpio_chip); |
325 | irq_set_handler(irq, handle_edge_irq); | |
752bcb4d G |
326 | irq_modify_status(irq, |
327 | IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN, | |
328 | 0); | |
329 | } | |
330 | ||
331 | for (irq = IRQ_GPIOHIGH + 1; irq < IRQ_GPIO0; irq++) { | |
e1f5ce81 TG |
332 | irq_set_chip(irq, &puv3_normal_chip); |
333 | irq_set_handler(irq, handle_level_irq); | |
752bcb4d G |
334 | irq_modify_status(irq, |
335 | IRQ_NOREQUEST | IRQ_NOAUTOEN, | |
336 | IRQ_NOPROBE); | |
337 | } | |
338 | ||
339 | for (irq = IRQ_GPIO0; irq <= IRQ_GPIO27; irq++) { | |
e1f5ce81 TG |
340 | irq_set_chip(irq, &puv3_high_gpio_chip); |
341 | irq_set_handler(irq, handle_edge_irq); | |
752bcb4d G |
342 | irq_modify_status(irq, |
343 | IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN, | |
344 | 0); | |
345 | } | |
346 | ||
347 | /* | |
348 | * Install handler for GPIO 0-27 edge detect interrupts | |
349 | */ | |
e1f5ce81 TG |
350 | irq_set_chip(IRQ_GPIOHIGH, &puv3_normal_chip); |
351 | irq_set_chained_handler(IRQ_GPIOHIGH, puv3_gpio_handler); | |
752bcb4d G |
352 | |
353 | #ifdef CONFIG_PUV3_GPIO | |
354 | puv3_init_gpio(); | |
355 | #endif | |
356 | } | |
357 | ||
358 | int show_interrupts(struct seq_file *p, void *v) | |
359 | { | |
360 | int i = *(loff_t *) v, cpu; | |
361 | struct irq_desc *desc; | |
362 | struct irqaction *action; | |
363 | unsigned long flags; | |
364 | ||
365 | if (i == 0) { | |
366 | char cpuname[12]; | |
367 | ||
368 | seq_printf(p, " "); | |
369 | for_each_present_cpu(cpu) { | |
370 | sprintf(cpuname, "CPU%d", cpu); | |
371 | seq_printf(p, " %10s", cpuname); | |
372 | } | |
373 | seq_putc(p, '\n'); | |
374 | } | |
375 | ||
376 | if (i < nr_irqs) { | |
377 | desc = irq_to_desc(i); | |
378 | raw_spin_lock_irqsave(&desc->lock, flags); | |
379 | action = desc->action; | |
380 | if (!action) | |
381 | goto unlock; | |
382 | ||
383 | seq_printf(p, "%3d: ", i); | |
384 | for_each_present_cpu(cpu) | |
385 | seq_printf(p, "%10u ", kstat_irqs_cpu(i, cpu)); | |
36a8b8c3 | 386 | seq_printf(p, " %10s", desc->irq_data.chip->name ? : "-"); |
752bcb4d G |
387 | seq_printf(p, " %s", action->name); |
388 | for (action = action->next; action; action = action->next) | |
389 | seq_printf(p, ", %s", action->name); | |
390 | ||
391 | seq_putc(p, '\n'); | |
392 | unlock: | |
393 | raw_spin_unlock_irqrestore(&desc->lock, flags); | |
394 | } else if (i == nr_irqs) { | |
395 | seq_printf(p, "Error in interrupt!\n"); | |
396 | } | |
397 | return 0; | |
398 | } | |
399 | ||
400 | /* | |
401 | * do_IRQ handles all hardware IRQ's. Decoded IRQs should not | |
402 | * come via this function. Instead, they should provide their | |
403 | * own 'handler' | |
404 | */ | |
405 | asmlinkage void asm_do_IRQ(unsigned int irq, struct pt_regs *regs) | |
406 | { | |
407 | struct pt_regs *old_regs = set_irq_regs(regs); | |
408 | ||
409 | irq_enter(); | |
410 | ||
411 | /* | |
412 | * Some hardware gives randomly wrong interrupts. Rather | |
413 | * than crashing, do something sensible. | |
414 | */ | |
415 | if (unlikely(irq >= nr_irqs)) { | |
416 | if (printk_ratelimit()) | |
417 | printk(KERN_WARNING "Bad IRQ%u\n", irq); | |
418 | ack_bad_irq(irq); | |
419 | } else { | |
420 | generic_handle_irq(irq); | |
421 | } | |
422 | ||
423 | irq_exit(); | |
424 | set_irq_regs(old_regs); | |
425 | } | |
426 |