diff options
-rw-r--r-- | arch/arm/Kconfig | 1 | ||||
-rw-r--r-- | arch/arm/mach-s3c24xx/include/mach/entry-macro.S | 70 | ||||
-rw-r--r-- | arch/arm/mach-s3c24xx/irq.c | 58 |
3 files changed, 59 insertions, 70 deletions
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 46fcfa8805f8..6820ffde359f 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig | |||
@@ -778,6 +778,7 @@ config ARCH_S3C24XX | |||
778 | select HAVE_S3C2410_I2C if I2C | 778 | select HAVE_S3C2410_I2C if I2C |
779 | select HAVE_S3C2410_WATCHDOG if WATCHDOG | 779 | select HAVE_S3C2410_WATCHDOG if WATCHDOG |
780 | select HAVE_S3C_RTC if RTC_CLASS | 780 | select HAVE_S3C_RTC if RTC_CLASS |
781 | select MULTI_IRQ_HANDLER | ||
781 | select NEED_MACH_GPIO_H | 782 | select NEED_MACH_GPIO_H |
782 | select NEED_MACH_IO_H | 783 | select NEED_MACH_IO_H |
783 | help | 784 | help |
diff --git a/arch/arm/mach-s3c24xx/include/mach/entry-macro.S b/arch/arm/mach-s3c24xx/include/mach/entry-macro.S deleted file mode 100644 index 6a21beeba1da..000000000000 --- a/arch/arm/mach-s3c24xx/include/mach/entry-macro.S +++ /dev/null | |||
@@ -1,70 +0,0 @@ | |||
1 | /* | ||
2 | * arch/arm/mach-s3c2410/include/mach/entry-macro.S | ||
3 | * | ||
4 | * Low-level IRQ helper macros for S3C2410-based platforms | ||
5 | * | ||
6 | * This file is licensed under the terms of the GNU General Public | ||
7 | * License version 2. This program is licensed "as is" without any | ||
8 | * warranty of any kind, whether express or implied. | ||
9 | */ | ||
10 | |||
11 | /* We have a problem that the INTOFFSET register does not always | ||
12 | * show one interrupt. Occasionally we get two interrupts through | ||
13 | * the prioritiser, and this causes the INTOFFSET register to show | ||
14 | * what looks like the logical-or of the two interrupt numbers. | ||
15 | * | ||
16 | * Thanks to Klaus, Shannon, et al for helping to debug this problem | ||
17 | */ | ||
18 | |||
19 | #define INTPND (0x10) | ||
20 | #define INTOFFSET (0x14) | ||
21 | |||
22 | #include <mach/hardware.h> | ||
23 | #include <asm/irq.h> | ||
24 | |||
25 | .macro get_irqnr_preamble, base, tmp | ||
26 | .endm | ||
27 | |||
28 | .macro get_irqnr_and_base, irqnr, irqstat, base, tmp | ||
29 | |||
30 | mov \base, #S3C24XX_VA_IRQ | ||
31 | |||
32 | @@ try the interrupt offset register, since it is there | ||
33 | |||
34 | ldr \irqstat, [\base, #INTPND ] | ||
35 | teq \irqstat, #0 | ||
36 | beq 1002f | ||
37 | ldr \irqnr, [\base, #INTOFFSET ] | ||
38 | mov \tmp, #1 | ||
39 | tst \irqstat, \tmp, lsl \irqnr | ||
40 | bne 1001f | ||
41 | |||
42 | @@ the number specified is not a valid irq, so try | ||
43 | @@ and work it out for ourselves | ||
44 | |||
45 | mov \irqnr, #0 @@ start here | ||
46 | |||
47 | @@ work out which irq (if any) we got | ||
48 | |||
49 | movs \tmp, \irqstat, lsl#16 | ||
50 | addeq \irqnr, \irqnr, #16 | ||
51 | moveq \irqstat, \irqstat, lsr#16 | ||
52 | tst \irqstat, #0xff | ||
53 | addeq \irqnr, \irqnr, #8 | ||
54 | moveq \irqstat, \irqstat, lsr#8 | ||
55 | tst \irqstat, #0xf | ||
56 | addeq \irqnr, \irqnr, #4 | ||
57 | moveq \irqstat, \irqstat, lsr#4 | ||
58 | tst \irqstat, #0x3 | ||
59 | addeq \irqnr, \irqnr, #2 | ||
60 | moveq \irqstat, \irqstat, lsr#2 | ||
61 | tst \irqstat, #0x1 | ||
62 | addeq \irqnr, \irqnr, #1 | ||
63 | |||
64 | @@ we have the value | ||
65 | 1001: | ||
66 | adds \irqnr, \irqnr, #IRQ_EINT0 | ||
67 | 1002: | ||
68 | @@ exit here, Z flag unset if IRQ | ||
69 | |||
70 | .endm | ||
diff --git a/arch/arm/mach-s3c24xx/irq.c b/arch/arm/mach-s3c24xx/irq.c index 8bc29313c341..5c9f8b7a1fd6 100644 --- a/arch/arm/mach-s3c24xx/irq.c +++ b/arch/arm/mach-s3c24xx/irq.c | |||
@@ -26,6 +26,7 @@ | |||
26 | #include <linux/device.h> | 26 | #include <linux/device.h> |
27 | #include <linux/irqdomain.h> | 27 | #include <linux/irqdomain.h> |
28 | 28 | ||
29 | #include <asm/exception.h> | ||
29 | #include <asm/mach/irq.h> | 30 | #include <asm/mach/irq.h> |
30 | 31 | ||
31 | #include <mach/regs-irq.h> | 32 | #include <mach/regs-irq.h> |
@@ -282,6 +283,56 @@ static void s3c_irq_demux(unsigned int irq, struct irq_desc *desc) | |||
282 | chained_irq_exit(chip, desc); | 283 | chained_irq_exit(chip, desc); |
283 | } | 284 | } |
284 | 285 | ||
286 | static struct s3c_irq_intc *main_intc; | ||
287 | static struct s3c_irq_intc *main_intc2; | ||
288 | |||
289 | static inline int s3c24xx_handle_intc(struct s3c_irq_intc *intc, | ||
290 | struct pt_regs *regs) | ||
291 | { | ||
292 | int pnd; | ||
293 | int offset; | ||
294 | int irq; | ||
295 | |||
296 | pnd = __raw_readl(intc->reg_intpnd); | ||
297 | if (!pnd) | ||
298 | return false; | ||
299 | |||
300 | /* We have a problem that the INTOFFSET register does not always | ||
301 | * show one interrupt. Occasionally we get two interrupts through | ||
302 | * the prioritiser, and this causes the INTOFFSET register to show | ||
303 | * what looks like the logical-or of the two interrupt numbers. | ||
304 | * | ||
305 | * Thanks to Klaus, Shannon, et al for helping to debug this problem | ||
306 | */ | ||
307 | offset = __raw_readl(intc->reg_intpnd + 4); | ||
308 | |||
309 | /* Find the bit manually, when the offset is wrong. | ||
310 | * The pending register only ever contains the one bit of the next | ||
311 | * interrupt to handle. | ||
312 | */ | ||
313 | if (!(pnd & (1 << offset))) | ||
314 | offset = __ffs(pnd); | ||
315 | |||
316 | irq = irq_find_mapping(intc->domain, offset); | ||
317 | handle_IRQ(irq, regs); | ||
318 | return true; | ||
319 | } | ||
320 | |||
321 | asmlinkage void __exception_irq_entry s3c24xx_handle_irq(struct pt_regs *regs) | ||
322 | { | ||
323 | do { | ||
324 | if (likely(main_intc)) | ||
325 | if (s3c24xx_handle_intc(main_intc, regs)) | ||
326 | continue; | ||
327 | |||
328 | if (main_intc2) | ||
329 | if (s3c24xx_handle_intc(main_intc2, regs)) | ||
330 | continue; | ||
331 | |||
332 | break; | ||
333 | } while (1); | ||
334 | } | ||
335 | |||
285 | #ifdef CONFIG_FIQ | 336 | #ifdef CONFIG_FIQ |
286 | /** | 337 | /** |
287 | * s3c24xx_set_fiq - set the FIQ routing | 338 | * s3c24xx_set_fiq - set the FIQ routing |
@@ -502,6 +553,13 @@ static struct s3c_irq_intc *s3c24xx_init_intc(struct device_node *np, | |||
502 | goto err; | 553 | goto err; |
503 | } | 554 | } |
504 | 555 | ||
556 | if (address == 0x4a000000) | ||
557 | main_intc = intc; | ||
558 | else if (address == 0x4a000040) | ||
559 | main_intc2 = intc; | ||
560 | |||
561 | set_handle_irq(s3c24xx_handle_irq); | ||
562 | |||
505 | return intc; | 563 | return intc; |
506 | 564 | ||
507 | err: | 565 | err: |