Commit 4c6773c3954cb1192f70a63e2dc61adc55bb0948

Authored by Daniel Hellstrom
Committed by David S. Miller
1 parent d61a38b2ce

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);