Commit 4c6773c3954cb1192f70a63e2dc61adc55bb0948
Committed by
David S. Miller
1 parent
d61a38b2ce
Exists in
master
and in
4 other branches
sparc32,leon: add support for extended interrupt controller
The extended IRQ controller gives the LEON 16 more IRQs. The patch installs a custom handler for the exetended controller IRQ, where a register is read and the "real" IRQ causing IRQ is determined. Signed-off-by: Daniel Hellstrom <daniel@gaisler.com> Acked-by: Sam Ravnborg <sam@ravnborg.org> Signed-off-by: David S. Miller <davem@davemloft.net>
Showing 2 changed files with 74 additions and 28 deletions Side-by-side Diff
arch/sparc/include/asm/leon.h
... | ... | @@ -183,7 +183,6 @@ |
183 | 183 | /* macro access for leon_readnobuffer_reg() */ |
184 | 184 | #define LEON_BYPASSCACHE_LOAD_VA(x) leon_readnobuffer_reg((unsigned long)(x)) |
185 | 185 | |
186 | -extern void sparc_leon_eirq_register(int eirq); | |
187 | 186 | extern void leon_init(void); |
188 | 187 | extern void leon_switch_mm(void); |
189 | 188 | extern void leon_init_IRQ(void); |
... | ... | @@ -339,9 +338,9 @@ |
339 | 338 | #include <linux/interrupt.h> |
340 | 339 | |
341 | 340 | struct device_node; |
342 | -extern int sparc_leon_eirq_get(int eirq, int cpu); | |
343 | -extern irqreturn_t sparc_leon_eirq_isr(int dummy, void *dev_id); | |
344 | -extern void sparc_leon_eirq_register(int eirq); | |
341 | +extern unsigned int leon_build_device_irq(unsigned int real_irq, | |
342 | + irq_flow_handler_t flow_handler, | |
343 | + const char *name, int do_ack); | |
345 | 344 | extern void leon_clear_clock_irq(void); |
346 | 345 | extern void leon_load_profile_irq(int cpu, unsigned int limit); |
347 | 346 | extern void leon_init_timers(irq_handler_t counter_fn); |
arch/sparc/kernel/leon_kernel.c
... | ... | @@ -19,6 +19,7 @@ |
19 | 19 | #include <asm/leon_amba.h> |
20 | 20 | #include <asm/traps.h> |
21 | 21 | #include <asm/cacheflush.h> |
22 | +#include <asm/smp.h> | |
22 | 23 | |
23 | 24 | #include "prom.h" |
24 | 25 | #include "irq.h" |
25 | 26 | |
26 | 27 | |
27 | 28 | |
28 | 29 | |
29 | 30 | |
30 | 31 | |
31 | 32 | |
... | ... | @@ -36,37 +37,51 @@ |
36 | 37 | unsigned long leon3_gptimer_irq; /* interrupt controller irq number */ |
37 | 38 | unsigned long leon3_gptimer_idx; /* Timer Index (0..6) within Timer Core */ |
38 | 39 | unsigned int sparc_leon_eirq; |
39 | -#define LEON_IMASK ((&leon3_irqctrl_regs->mask[0])) | |
40 | +#define LEON_IMASK (&leon3_irqctrl_regs->mask[0]) | |
41 | +#define LEON_IACK (&leon3_irqctrl_regs->iclear) | |
42 | +#define LEON_DO_ACK_HW 1 | |
40 | 43 | |
41 | -/* Return the IRQ of the pending IRQ on the extended IRQ controller */ | |
42 | -int sparc_leon_eirq_get(int eirq, int cpu) | |
44 | +/* Return the last ACKed IRQ by the Extended IRQ controller. It has already | |
45 | + * been (automatically) ACKed when the CPU takes the trap. | |
46 | + */ | |
47 | +static inline unsigned int leon_eirq_get(int cpu) | |
43 | 48 | { |
44 | 49 | return LEON3_BYPASS_LOAD_PA(&leon3_irqctrl_regs->intid[cpu]) & 0x1f; |
45 | 50 | } |
46 | 51 | |
47 | -irqreturn_t sparc_leon_eirq_isr(int dummy, void *dev_id) | |
52 | +/* Handle one or multiple IRQs from the extended interrupt controller */ | |
53 | +static void leon_handle_ext_irq(unsigned int irq, struct irq_desc *desc) | |
48 | 54 | { |
49 | - printk(KERN_ERR "sparc_leon_eirq_isr: ERROR EXTENDED IRQ\n"); | |
50 | - return IRQ_HANDLED; | |
55 | + unsigned int eirq; | |
56 | + int cpu = hard_smp_processor_id(); | |
57 | + | |
58 | + eirq = leon_eirq_get(cpu); | |
59 | + if ((eirq & 0x10) && irq_map[eirq]->irq) /* bit4 tells if IRQ happened */ | |
60 | + generic_handle_irq(irq_map[eirq]->irq); | |
51 | 61 | } |
52 | 62 | |
53 | 63 | /* The extended IRQ controller has been found, this function registers it */ |
54 | -void sparc_leon_eirq_register(int eirq) | |
64 | +void leon_eirq_setup(unsigned int eirq) | |
55 | 65 | { |
56 | - int irq; | |
66 | + unsigned long mask, oldmask; | |
67 | + unsigned int veirq; | |
57 | 68 | |
58 | - /* Register a "BAD" handler for this interrupt, it should never happen */ | |
59 | - irq = request_irq(eirq, sparc_leon_eirq_isr, | |
60 | - (IRQF_DISABLED | SA_STATIC_ALLOC), "extirq", NULL); | |
61 | - | |
62 | - if (irq) { | |
63 | - printk(KERN_ERR | |
64 | - "sparc_leon_eirq_register: unable to attach IRQ%d\n", | |
65 | - eirq); | |
66 | - } else { | |
67 | - sparc_leon_eirq = eirq; | |
69 | + if (eirq < 1 || eirq > 0xf) { | |
70 | + printk(KERN_ERR "LEON EXT IRQ NUMBER BAD: %d\n", eirq); | |
71 | + return; | |
68 | 72 | } |
69 | 73 | |
74 | + veirq = leon_build_device_irq(eirq, leon_handle_ext_irq, "extirq", 0); | |
75 | + | |
76 | + /* | |
77 | + * Unmask the Extended IRQ, the IRQs routed through the Ext-IRQ | |
78 | + * controller have a mask-bit of their own, so this is safe. | |
79 | + */ | |
80 | + irq_link(veirq); | |
81 | + mask = 1 << eirq; | |
82 | + oldmask = LEON3_BYPASS_LOAD_PA(LEON_IMASK); | |
83 | + LEON3_BYPASS_STORE_PA(LEON_IMASK, (oldmask | mask)); | |
84 | + sparc_leon_eirq = eirq; | |
70 | 85 | } |
71 | 86 | |
72 | 87 | static inline unsigned long get_irqmask(unsigned int irq) |
73 | 88 | |
74 | 89 | |
... | ... | @@ -119,16 +134,33 @@ |
119 | 134 | irq_unlink(data->irq); |
120 | 135 | } |
121 | 136 | |
137 | +/* Used by external level sensitive IRQ handlers on the LEON: ACK IRQ ctrl */ | |
138 | +static void leon_eoi_irq(struct irq_data *data) | |
139 | +{ | |
140 | + unsigned long mask = (unsigned long)data->chip_data; | |
141 | + | |
142 | + if (mask & LEON_DO_ACK_HW) | |
143 | + LEON3_BYPASS_STORE_PA(LEON_IACK, mask & ~LEON_DO_ACK_HW); | |
144 | +} | |
145 | + | |
122 | 146 | static struct irq_chip leon_irq = { |
123 | 147 | .name = "leon", |
124 | 148 | .irq_startup = leon_startup_irq, |
125 | 149 | .irq_shutdown = leon_shutdown_irq, |
126 | 150 | .irq_mask = leon_mask_irq, |
127 | 151 | .irq_unmask = leon_unmask_irq, |
152 | + .irq_eoi = leon_eoi_irq, | |
128 | 153 | }; |
129 | 154 | |
130 | -static unsigned int leon_build_device_irq(struct platform_device *op, | |
131 | - unsigned int real_irq) | |
155 | +/* | |
156 | + * Build a LEON IRQ for the edge triggered LEON IRQ controller: | |
157 | + * Edge (normal) IRQ - handle_simple_irq, ack=DONT-CARE, never ack | |
158 | + * Level IRQ (PCI|Level-GPIO) - handle_fasteoi_irq, ack=1, ack after ISR | |
159 | + * Per-CPU Edge - handle_percpu_irq, ack=0 | |
160 | + */ | |
161 | +unsigned int leon_build_device_irq(unsigned int real_irq, | |
162 | + irq_flow_handler_t flow_handler, | |
163 | + const char *name, int do_ack) | |
132 | 164 | { |
133 | 165 | unsigned int irq; |
134 | 166 | unsigned long mask; |
135 | 167 | |
136 | 168 | |
137 | 169 | |
... | ... | @@ -142,17 +174,26 @@ |
142 | 174 | if (irq == 0) |
143 | 175 | goto out; |
144 | 176 | |
177 | + if (do_ack) | |
178 | + mask |= LEON_DO_ACK_HW; | |
179 | + | |
145 | 180 | irq_set_chip_and_handler_name(irq, &leon_irq, |
146 | - handle_simple_irq, "edge"); | |
181 | + flow_handler, name); | |
147 | 182 | irq_set_chip_data(irq, (void *)mask); |
148 | 183 | |
149 | 184 | out: |
150 | 185 | return irq; |
151 | 186 | } |
152 | 187 | |
188 | +static unsigned int _leon_build_device_irq(struct platform_device *op, | |
189 | + unsigned int real_irq) | |
190 | +{ | |
191 | + return leon_build_device_irq(real_irq, handle_simple_irq, "edge", 0); | |
192 | +} | |
193 | + | |
153 | 194 | void __init leon_init_timers(irq_handler_t counter_fn) |
154 | 195 | { |
155 | - int irq; | |
196 | + int irq, eirq; | |
156 | 197 | struct device_node *rootnp, *np, *nnp; |
157 | 198 | struct property *pp; |
158 | 199 | int len; |
159 | 200 | |
... | ... | @@ -262,11 +303,17 @@ |
262 | 303 | icsel = LEON3_BYPASS_LOAD_PA(&leon3_irqctrl_regs->icsel[cpu/8]); |
263 | 304 | icsel = (icsel >> ((7 - (cpu&0x7)) * 4)) & 0xf; |
264 | 305 | leon3_irqctrl_regs += icsel; |
306 | + | |
307 | + /* Probe extended IRQ controller */ | |
308 | + eirq = (LEON3_BYPASS_LOAD_PA(&leon3_irqctrl_regs->mpstatus) | |
309 | + >> 16) & 0xf; | |
310 | + if (eirq != 0) | |
311 | + leon_eirq_setup(eirq); | |
265 | 312 | } else { |
266 | 313 | goto bad; |
267 | 314 | } |
268 | 315 | |
269 | - irq = leon_build_device_irq(NULL, leon3_gptimer_irq + leon3_gptimer_idx); | |
316 | + irq = _leon_build_device_irq(NULL, leon3_gptimer_irq+leon3_gptimer_idx); | |
270 | 317 | err = request_irq(irq, counter_fn, IRQF_TIMER, "timer", NULL); |
271 | 318 | |
272 | 319 | if (err) { |
... | ... | @@ -394,7 +441,7 @@ |
394 | 441 | void __init leon_init_IRQ(void) |
395 | 442 | { |
396 | 443 | sparc_irq_config.init_timers = leon_init_timers; |
397 | - sparc_irq_config.build_device_irq = leon_build_device_irq; | |
444 | + sparc_irq_config.build_device_irq = _leon_build_device_irq; | |
398 | 445 | |
399 | 446 | BTFIXUPSET_CALL(clear_clock_irq, leon_clear_clock_irq, |
400 | 447 | BTFIXUPCALL_NORM); |