Commit 3108e6ab21a9b9dbd88f0b2ff99f73e95b8b1580
Committed by
Russell King
1 parent
69964ea4c7
Exists in
master
and in
20 other branches
ARM: 7389/2: plat-versatile: modernize FPGA IRQ controller
This does two things to the FPGA IRQ controller in the versatile family: - Convert to MULTI_IRQ_HANDLER so we can drop the entry macro from the Integrator. The C IRQ handler was inspired from arch/arm/common/vic.c, recent bug discovered in this handler was accounted for. - Convert to using IRQ domains so we can get rid of the NO_IRQ mess and proceed with device tree and such stuff. As part of the exercise, bump all the low IRQ numbers on the Integrator PIC to start from 1 rather than 0, since IRQ 0 is now NO_IRQ. The Linux IRQ numbers are thus entirely decoupled from the hardware IRQ numbers in this controller. I was unable to split this patch. The main reason is the half-done conversion to device tree in Versatile. Tested on Integrator/AP and Integrator/CP. Cc: Grant Likely <grant.likely@secretlab.ca> Acked-by: Rob Herring <rob.herring@calxeda.com> Signed-off-by: Linus Walleij <linus.walleij@linaro.org> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Showing 9 changed files with 162 additions and 130 deletions Side-by-side Diff
- arch/arm/Kconfig
- arch/arm/mach-integrator/include/mach/entry-macro.S
- arch/arm/mach-integrator/include/mach/irqs.h
- arch/arm/mach-integrator/integrator_ap.c
- arch/arm/mach-integrator/integrator_cp.c
- arch/arm/mach-versatile/core.c
- arch/arm/plat-versatile/Kconfig
- arch/arm/plat-versatile/fpga-irq.c
- arch/arm/plat-versatile/include/plat/fpga-irq.h
arch/arm/Kconfig
arch/arm/mach-integrator/include/mach/entry-macro.S
1 | -/* | |
2 | - * arch/arm/mach-integrator/include/mach/entry-macro.S | |
3 | - * | |
4 | - * Low-level IRQ helper macros for Integrator 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 | -#include <mach/hardware.h> | |
11 | -#include <mach/platform.h> | |
12 | -#include <mach/irqs.h> | |
13 | - | |
14 | - .macro get_irqnr_preamble, base, tmp | |
15 | - .endm | |
16 | - | |
17 | - .macro get_irqnr_and_base, irqnr, irqstat, base, tmp | |
18 | -/* FIXME: should not be using soo many LDRs here */ | |
19 | - ldr \base, =IO_ADDRESS(INTEGRATOR_IC_BASE) | |
20 | - mov \irqnr, #IRQ_PIC_START | |
21 | - ldr \irqstat, [\base, #IRQ_STATUS] @ get masked status | |
22 | - ldr \base, =IO_ADDRESS(INTEGRATOR_HDR_BASE) | |
23 | - teq \irqstat, #0 | |
24 | - ldreq \irqstat, [\base, #(INTEGRATOR_HDR_IC_OFFSET+IRQ_STATUS)] | |
25 | - moveq \irqnr, #IRQ_CIC_START | |
26 | - | |
27 | -1001: tst \irqstat, #15 | |
28 | - bne 1002f | |
29 | - add \irqnr, \irqnr, #4 | |
30 | - movs \irqstat, \irqstat, lsr #4 | |
31 | - bne 1001b | |
32 | -1002: tst \irqstat, #1 | |
33 | - bne 1003f | |
34 | - add \irqnr, \irqnr, #1 | |
35 | - movs \irqstat, \irqstat, lsr #1 | |
36 | - bne 1002b | |
37 | -1003: /* EQ will be set if no irqs pending */ | |
38 | - .endm |
arch/arm/mach-integrator/include/mach/irqs.h
... | ... | @@ -22,37 +22,37 @@ |
22 | 22 | /* |
23 | 23 | * Interrupt numbers |
24 | 24 | */ |
25 | -#define IRQ_PIC_START 0 | |
26 | -#define IRQ_SOFTINT 0 | |
27 | -#define IRQ_UARTINT0 1 | |
28 | -#define IRQ_UARTINT1 2 | |
29 | -#define IRQ_KMIINT0 3 | |
30 | -#define IRQ_KMIINT1 4 | |
31 | -#define IRQ_TIMERINT0 5 | |
32 | -#define IRQ_TIMERINT1 6 | |
33 | -#define IRQ_TIMERINT2 7 | |
34 | -#define IRQ_RTCINT 8 | |
35 | -#define IRQ_AP_EXPINT0 9 | |
36 | -#define IRQ_AP_EXPINT1 10 | |
37 | -#define IRQ_AP_EXPINT2 11 | |
38 | -#define IRQ_AP_EXPINT3 12 | |
39 | -#define IRQ_AP_PCIINT0 13 | |
40 | -#define IRQ_AP_PCIINT1 14 | |
41 | -#define IRQ_AP_PCIINT2 15 | |
42 | -#define IRQ_AP_PCIINT3 16 | |
43 | -#define IRQ_AP_V3INT 17 | |
44 | -#define IRQ_AP_CPINT0 18 | |
45 | -#define IRQ_AP_CPINT1 19 | |
46 | -#define IRQ_AP_LBUSTIMEOUT 20 | |
47 | -#define IRQ_AP_APCINT 21 | |
48 | -#define IRQ_CP_CLCDCINT 22 | |
49 | -#define IRQ_CP_MMCIINT0 23 | |
50 | -#define IRQ_CP_MMCIINT1 24 | |
51 | -#define IRQ_CP_AACIINT 25 | |
52 | -#define IRQ_CP_CPPLDINT 26 | |
53 | -#define IRQ_CP_ETHINT 27 | |
54 | -#define IRQ_CP_TSPENINT 28 | |
55 | -#define IRQ_PIC_END 31 | |
25 | +#define IRQ_PIC_START 1 | |
26 | +#define IRQ_SOFTINT 1 | |
27 | +#define IRQ_UARTINT0 2 | |
28 | +#define IRQ_UARTINT1 3 | |
29 | +#define IRQ_KMIINT0 4 | |
30 | +#define IRQ_KMIINT1 5 | |
31 | +#define IRQ_TIMERINT0 6 | |
32 | +#define IRQ_TIMERINT1 7 | |
33 | +#define IRQ_TIMERINT2 8 | |
34 | +#define IRQ_RTCINT 9 | |
35 | +#define IRQ_AP_EXPINT0 10 | |
36 | +#define IRQ_AP_EXPINT1 11 | |
37 | +#define IRQ_AP_EXPINT2 12 | |
38 | +#define IRQ_AP_EXPINT3 13 | |
39 | +#define IRQ_AP_PCIINT0 14 | |
40 | +#define IRQ_AP_PCIINT1 15 | |
41 | +#define IRQ_AP_PCIINT2 16 | |
42 | +#define IRQ_AP_PCIINT3 17 | |
43 | +#define IRQ_AP_V3INT 18 | |
44 | +#define IRQ_AP_CPINT0 19 | |
45 | +#define IRQ_AP_CPINT1 20 | |
46 | +#define IRQ_AP_LBUSTIMEOUT 21 | |
47 | +#define IRQ_AP_APCINT 22 | |
48 | +#define IRQ_CP_CLCDCINT 23 | |
49 | +#define IRQ_CP_MMCIINT0 24 | |
50 | +#define IRQ_CP_MMCIINT1 25 | |
51 | +#define IRQ_CP_AACIINT 26 | |
52 | +#define IRQ_CP_CPPLDINT 27 | |
53 | +#define IRQ_CP_ETHINT 28 | |
54 | +#define IRQ_CP_TSPENINT 29 | |
55 | +#define IRQ_PIC_END 29 | |
56 | 56 | |
57 | 57 | #define IRQ_CIC_START 32 |
58 | 58 | #define IRQ_CM_SOFTINT 32 |
arch/arm/mach-integrator/integrator_ap.c
... | ... | @@ -162,12 +162,6 @@ |
162 | 162 | |
163 | 163 | #define INTEGRATOR_SC_VALID_INT 0x003fffff |
164 | 164 | |
165 | -static struct fpga_irq_data sc_irq_data = { | |
166 | - .base = VA_IC_BASE, | |
167 | - .irq_start = 0, | |
168 | - .chip.name = "SC", | |
169 | -}; | |
170 | - | |
171 | 165 | static void __init ap_init_irq(void) |
172 | 166 | { |
173 | 167 | /* Disable all interrupts initially. */ |
... | ... | @@ -178,7 +172,8 @@ |
178 | 172 | writel(-1, VA_IC_BASE + IRQ_ENABLE_CLEAR); |
179 | 173 | writel(-1, VA_IC_BASE + FIQ_ENABLE_CLEAR); |
180 | 174 | |
181 | - fpga_irq_init(-1, INTEGRATOR_SC_VALID_INT, &sc_irq_data); | |
175 | + fpga_irq_init(VA_IC_BASE, "SC", IRQ_PIC_START, | |
176 | + -1, INTEGRATOR_SC_VALID_INT, NULL); | |
182 | 177 | } |
183 | 178 | |
184 | 179 | #ifdef CONFIG_PM |
... | ... | @@ -478,6 +473,7 @@ |
478 | 473 | .nr_irqs = NR_IRQS_INTEGRATOR_AP, |
479 | 474 | .init_early = integrator_init_early, |
480 | 475 | .init_irq = ap_init_irq, |
476 | + .handle_irq = fpga_handle_irq, | |
481 | 477 | .timer = &ap_timer, |
482 | 478 | .init_machine = ap_init, |
483 | 479 | .restart = integrator_restart, |
arch/arm/mach-integrator/integrator_cp.c
... | ... | @@ -143,30 +143,14 @@ |
143 | 143 | iotable_init(intcp_io_desc, ARRAY_SIZE(intcp_io_desc)); |
144 | 144 | } |
145 | 145 | |
146 | -static struct fpga_irq_data cic_irq_data = { | |
147 | - .base = INTCP_VA_CIC_BASE, | |
148 | - .irq_start = IRQ_CIC_START, | |
149 | - .chip.name = "CIC", | |
150 | -}; | |
151 | - | |
152 | -static struct fpga_irq_data pic_irq_data = { | |
153 | - .base = INTCP_VA_PIC_BASE, | |
154 | - .irq_start = IRQ_PIC_START, | |
155 | - .chip.name = "PIC", | |
156 | -}; | |
157 | - | |
158 | -static struct fpga_irq_data sic_irq_data = { | |
159 | - .base = INTCP_VA_SIC_BASE, | |
160 | - .irq_start = IRQ_SIC_START, | |
161 | - .chip.name = "SIC", | |
162 | -}; | |
163 | - | |
164 | 146 | static void __init intcp_init_irq(void) |
165 | 147 | { |
166 | - u32 pic_mask, sic_mask; | |
148 | + u32 pic_mask, cic_mask, sic_mask; | |
167 | 149 | |
150 | + /* These masks are for the HW IRQ registers */ | |
168 | 151 | pic_mask = ~((~0u) << (11 - IRQ_PIC_START)); |
169 | 152 | pic_mask |= (~((~0u) << (29 - 22))) << 22; |
153 | + cic_mask = ~((~0u) << (1 + IRQ_CIC_END - IRQ_CIC_START)); | |
170 | 154 | sic_mask = ~((~0u) << (1 + IRQ_SIC_END - IRQ_SIC_START)); |
171 | 155 | |
172 | 156 | /* |
173 | 157 | |
174 | 158 | |
... | ... | @@ -179,12 +163,14 @@ |
179 | 163 | writel(sic_mask, INTCP_VA_SIC_BASE + IRQ_ENABLE_CLEAR); |
180 | 164 | writel(sic_mask, INTCP_VA_SIC_BASE + FIQ_ENABLE_CLEAR); |
181 | 165 | |
182 | - fpga_irq_init(-1, pic_mask, &pic_irq_data); | |
166 | + fpga_irq_init(INTCP_VA_PIC_BASE, "PIC", IRQ_PIC_START, | |
167 | + -1, pic_mask, NULL); | |
183 | 168 | |
184 | - fpga_irq_init(-1, ~((~0u) << (1 + IRQ_CIC_END - IRQ_CIC_START)), | |
185 | - &cic_irq_data); | |
169 | + fpga_irq_init(INTCP_VA_CIC_BASE, "CIC", IRQ_CIC_START, | |
170 | + -1, cic_mask, NULL); | |
186 | 171 | |
187 | - fpga_irq_init(IRQ_CP_CPPLDINT, sic_mask, &sic_irq_data); | |
172 | + fpga_irq_init(INTCP_VA_SIC_BASE, "SIC", IRQ_SIC_START, | |
173 | + IRQ_CP_CPPLDINT, sic_mask, NULL); | |
188 | 174 | } |
189 | 175 | |
190 | 176 | /* |
... | ... | @@ -467,6 +453,7 @@ |
467 | 453 | .nr_irqs = NR_IRQS_INTEGRATOR_CP, |
468 | 454 | .init_early = intcp_init_early, |
469 | 455 | .init_irq = intcp_init_irq, |
456 | + .handle_irq = fpga_handle_irq, | |
470 | 457 | .timer = &cp_timer, |
471 | 458 | .init_machine = intcp_init, |
472 | 459 | .restart = integrator_restart, |
arch/arm/mach-versatile/core.c
... | ... | @@ -66,12 +66,6 @@ |
66 | 66 | #define VA_VIC_BASE __io_address(VERSATILE_VIC_BASE) |
67 | 67 | #define VA_SIC_BASE __io_address(VERSATILE_SIC_BASE) |
68 | 68 | |
69 | -static struct fpga_irq_data sic_irq = { | |
70 | - .base = VA_SIC_BASE, | |
71 | - .irq_start = IRQ_SIC_START, | |
72 | - .chip.name = "SIC", | |
73 | -}; | |
74 | - | |
75 | 69 | #if 1 |
76 | 70 | #define IRQ_MMCI0A IRQ_VICSOURCE22 |
77 | 71 | #define IRQ_AACI IRQ_VICSOURCE24 |
... | ... | @@ -105,8 +99,11 @@ |
105 | 99 | |
106 | 100 | writel(~0, VA_SIC_BASE + SIC_IRQ_ENABLE_CLEAR); |
107 | 101 | |
108 | - fpga_irq_init(IRQ_VICSOURCE31, ~PIC_MASK, &sic_irq); | |
109 | - irq_domain_generate_simple(sic_of_match, VERSATILE_SIC_BASE, IRQ_SIC_START); | |
102 | + np = of_find_matching_node_by_address(NULL, sic_of_match, | |
103 | + VERSATILE_SIC_BASE); | |
104 | + | |
105 | + fpga_irq_init(VA_SIC_BASE, "SIC", IRQ_SIC_START, | |
106 | + IRQ_VICSOURCE31, ~PIC_MASK, np); | |
110 | 107 | |
111 | 108 | /* |
112 | 109 | * Interrupts on secondary controller from 0 to 8 are routed to |
arch/arm/plat-versatile/Kconfig
arch/arm/plat-versatile/fpga-irq.c
... | ... | @@ -3,7 +3,10 @@ |
3 | 3 | */ |
4 | 4 | #include <linux/irq.h> |
5 | 5 | #include <linux/io.h> |
6 | +#include <linux/irqdomain.h> | |
7 | +#include <linux/module.h> | |
6 | 8 | |
9 | +#include <asm/exception.h> | |
7 | 10 | #include <asm/mach/irq.h> |
8 | 11 | #include <plat/fpga-irq.h> |
9 | 12 | |
10 | 13 | |
... | ... | @@ -12,10 +15,32 @@ |
12 | 15 | #define IRQ_ENABLE_SET 0x08 |
13 | 16 | #define IRQ_ENABLE_CLEAR 0x0c |
14 | 17 | |
18 | +/** | |
19 | + * struct fpga_irq_data - irq data container for the FPGA IRQ controller | |
20 | + * @base: memory offset in virtual memory | |
21 | + * @irq_start: first IRQ number handled by this instance | |
22 | + * @chip: chip container for this instance | |
23 | + * @domain: IRQ domain for this instance | |
24 | + * @valid: mask for valid IRQs on this controller | |
25 | + * @used_irqs: number of active IRQs on this controller | |
26 | + */ | |
27 | +struct fpga_irq_data { | |
28 | + void __iomem *base; | |
29 | + unsigned int irq_start; | |
30 | + struct irq_chip chip; | |
31 | + u32 valid; | |
32 | + struct irq_domain *domain; | |
33 | + u8 used_irqs; | |
34 | +}; | |
35 | + | |
36 | +/* we cannot allocate memory when the controllers are initially registered */ | |
37 | +static struct fpga_irq_data fpga_irq_devices[CONFIG_PLAT_VERSATILE_FPGA_IRQ_NR]; | |
38 | +static int fpga_irq_id; | |
39 | + | |
15 | 40 | static void fpga_irq_mask(struct irq_data *d) |
16 | 41 | { |
17 | 42 | struct fpga_irq_data *f = irq_data_get_irq_chip_data(d); |
18 | - u32 mask = 1 << (d->irq - f->irq_start); | |
43 | + u32 mask = 1 << d->hwirq; | |
19 | 44 | |
20 | 45 | writel(mask, f->base + IRQ_ENABLE_CLEAR); |
21 | 46 | } |
... | ... | @@ -23,7 +48,7 @@ |
23 | 48 | static void fpga_irq_unmask(struct irq_data *d) |
24 | 49 | { |
25 | 50 | struct fpga_irq_data *f = irq_data_get_irq_chip_data(d); |
26 | - u32 mask = 1 << (d->irq - f->irq_start); | |
51 | + u32 mask = 1 << d->hwirq; | |
27 | 52 | |
28 | 53 | writel(mask, f->base + IRQ_ENABLE_SET); |
29 | 54 | } |
30 | 55 | |
31 | 56 | |
32 | 57 | |
33 | 58 | |
34 | 59 | |
35 | 60 | |
... | ... | @@ -41,33 +66,94 @@ |
41 | 66 | do { |
42 | 67 | irq = ffs(status) - 1; |
43 | 68 | status &= ~(1 << irq); |
44 | - | |
45 | - generic_handle_irq(irq + f->irq_start); | |
69 | + generic_handle_irq(irq_find_mapping(f->domain, irq)); | |
46 | 70 | } while (status); |
47 | 71 | } |
48 | 72 | |
49 | -void __init fpga_irq_init(int parent_irq, u32 valid, struct fpga_irq_data *f) | |
73 | +/* | |
74 | + * Handle each interrupt in a single FPGA IRQ controller. Returns non-zero | |
75 | + * if we've handled at least one interrupt. This does a single read of the | |
76 | + * status register and handles all interrupts in order from LSB first. | |
77 | + */ | |
78 | +static int handle_one_fpga(struct fpga_irq_data *f, struct pt_regs *regs) | |
50 | 79 | { |
51 | - unsigned int i; | |
80 | + int handled = 0; | |
81 | + int irq; | |
82 | + u32 status; | |
52 | 83 | |
84 | + while ((status = readl(f->base + IRQ_STATUS))) { | |
85 | + irq = ffs(status) - 1; | |
86 | + handle_IRQ(irq_find_mapping(f->domain, irq), regs); | |
87 | + handled = 1; | |
88 | + } | |
89 | + | |
90 | + return handled; | |
91 | +} | |
92 | + | |
93 | +/* | |
94 | + * Keep iterating over all registered FPGA IRQ controllers until there are | |
95 | + * no pending interrupts. | |
96 | + */ | |
97 | +asmlinkage void __exception_irq_entry fpga_handle_irq(struct pt_regs *regs) | |
98 | +{ | |
99 | + int i, handled; | |
100 | + | |
101 | + do { | |
102 | + for (i = 0, handled = 0; i < fpga_irq_id; ++i) | |
103 | + handled |= handle_one_fpga(&fpga_irq_devices[i], regs); | |
104 | + } while (handled); | |
105 | +} | |
106 | + | |
107 | +static int fpga_irqdomain_map(struct irq_domain *d, unsigned int irq, | |
108 | + irq_hw_number_t hwirq) | |
109 | +{ | |
110 | + struct fpga_irq_data *f = d->host_data; | |
111 | + | |
112 | + /* Skip invalid IRQs, only register handlers for the real ones */ | |
113 | + if (!(f->valid & (1 << hwirq))) | |
114 | + return -ENOTSUPP; | |
115 | + irq_set_chip_data(irq, f); | |
116 | + irq_set_chip_and_handler(irq, &f->chip, | |
117 | + handle_level_irq); | |
118 | + set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); | |
119 | + f->used_irqs++; | |
120 | + return 0; | |
121 | +} | |
122 | + | |
123 | +static struct irq_domain_ops fpga_irqdomain_ops = { | |
124 | + .map = fpga_irqdomain_map, | |
125 | + .xlate = irq_domain_xlate_onetwocell, | |
126 | +}; | |
127 | + | |
128 | +void __init fpga_irq_init(void __iomem *base, const char *name, int irq_start, | |
129 | + int parent_irq, u32 valid, struct device_node *node) | |
130 | +{ | |
131 | + struct fpga_irq_data *f; | |
132 | + | |
133 | + if (fpga_irq_id >= ARRAY_SIZE(fpga_irq_devices)) { | |
134 | + printk(KERN_ERR "%s: too few FPGA IRQ controllers, increase CONFIG_PLAT_VERSATILE_FPGA_IRQ_NR\n", __func__); | |
135 | + return; | |
136 | + } | |
137 | + | |
138 | + f = &fpga_irq_devices[fpga_irq_id]; | |
139 | + f->base = base; | |
140 | + f->irq_start = irq_start; | |
141 | + f->chip.name = name; | |
53 | 142 | f->chip.irq_ack = fpga_irq_mask; |
54 | 143 | f->chip.irq_mask = fpga_irq_mask; |
55 | 144 | f->chip.irq_unmask = fpga_irq_unmask; |
145 | + f->valid = valid; | |
56 | 146 | |
57 | 147 | if (parent_irq != -1) { |
58 | 148 | irq_set_handler_data(parent_irq, f); |
59 | 149 | irq_set_chained_handler(parent_irq, fpga_irq_handle); |
60 | 150 | } |
61 | 151 | |
62 | - for (i = 0; i < 32; i++) { | |
63 | - if (valid & (1 << i)) { | |
64 | - unsigned int irq = f->irq_start + i; | |
152 | + f->domain = irq_domain_add_legacy(node, fls(valid), f->irq_start, 0, | |
153 | + &fpga_irqdomain_ops, f); | |
154 | + pr_info("FPGA IRQ chip %d \"%s\" @ %p, %u irqs\n", | |
155 | + fpga_irq_id, name, base, f->used_irqs); | |
65 | 156 | |
66 | - irq_set_chip_data(irq, f); | |
67 | - irq_set_chip_and_handler(irq, &f->chip, | |
68 | - handle_level_irq); | |
69 | - set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); | |
70 | - } | |
71 | - } | |
157 | + fpga_irq_id++; | |
72 | 158 | } |
arch/arm/plat-versatile/include/plat/fpga-irq.h
1 | 1 | #ifndef PLAT_FPGA_IRQ_H |
2 | 2 | #define PLAT_FPGA_IRQ_H |
3 | 3 | |
4 | -struct fpga_irq_data { | |
5 | - void __iomem *base; | |
6 | - unsigned int irq_start; | |
7 | - struct irq_chip chip; | |
8 | -}; | |
4 | +struct device_node; | |
5 | +struct pt_regs; | |
9 | 6 | |
10 | -void fpga_irq_init(int, u32, struct fpga_irq_data *); | |
7 | +void fpga_handle_irq(struct pt_regs *regs); | |
8 | +void fpga_irq_init(void __iomem *, const char *, int, int, u32, | |
9 | + struct device_node *node); | |
11 | 10 | |
12 | 11 | #endif |