bcm963xx: rewrite irq handling code
[openwrt.git] / target / linux / brcm63xx / patches-2.6.25 / 060-bcm963xx_rewrite_irq_handling_code.patch
1 From 9a70f2dcb24a5aab29386373c86ba035acba4891 Mon Sep 17 00:00:00 2001
2 From: Axel Gembe <ago@bastart.eu.org>
3 Date: Sun, 18 May 2008 12:07:21 +0200
4 Subject: [PATCH] bcm963xx: rewrite irq handling code
5
6 This patch adds interrupt handling as on AR7. The old code was very messy and
7 didn't work too well.
8
9 Signed-off-by: Axel Gembe <ago@bastart.eu.org>
10 ---
11  arch/mips/bcm963xx/irq.c                  |  308 ++++++++++-------------------
12  drivers/serial/bcm63xx_cons.c             |   13 +-
13  include/asm-mips/mach-bcm963xx/bcm_intr.h |   18 +--
14  3 files changed, 119 insertions(+), 220 deletions(-)
15
16 diff --git a/arch/mips/bcm963xx/irq.c b/arch/mips/bcm963xx/irq.c
17 index 62a848b..11583c9 100644
18 --- a/arch/mips/bcm963xx/irq.c
19 +++ b/arch/mips/bcm963xx/irq.c
20 @@ -1,259 +1,159 @@
21  /*
22 -<:copyright-gpl 
23 - Copyright 2002 Broadcom Corp. All Rights Reserved. 
24
25 - This program is free software; you can distribute it and/or modify it 
26 - under the terms of the GNU General Public License (Version 2) as 
27 - published by the Free Software Foundation. 
28
29 - This program is distributed in the hope it will be useful, but WITHOUT 
30 - ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
31 - FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License 
32 - for more details. 
33
34 - You should have received a copy of the GNU General Public License along 
35 - with this program; if not, write to the Free Software Foundation, Inc., 
36 - 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. 
37 -:>
38 -*/
39 -/*
40 - * Interrupt control functions for Broadcom 963xx MIPS boards
41 + * Copyright (C) 2006,2007 Felix Fietkau <nbd@openwrt.org>
42 + * Copyright (C) 2006,2007 Eugene Konev <ejka@openwrt.org>
43 + * Copyright (C) 2008 Axel Gembe <ago@bastart.eu.org>
44 + *
45 + * This program is free software; you can redistribute it and/or modify
46 + * it under the terms of the GNU General Public License as published by
47 + * the Free Software Foundation; either version 2 of the License, or
48 + * (at your option) any later version.
49 + *
50 + * This program is distributed in the hope that it will be useful,
51 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
52 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
53 + * GNU General Public License for more details.
54 + *
55 + * You should have received a copy of the GNU General Public License
56 + * along with this program; if not, write to the Free Software
57 + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
58   */
59  
60 -#include <asm/atomic.h>
61 -
62 -#include <linux/delay.h>
63 -#include <linux/init.h>
64 -#include <linux/ioport.h>
65 -#include <linux/irq.h>
66  #include <linux/interrupt.h>
67 -#include <linux/kernel.h>
68 -#include <linux/slab.h>
69 -#include <linux/module.h>
70 +#include <linux/io.h>
71  
72 -#include <asm/irq.h>
73 +#include <asm/irq_cpu.h>
74  #include <asm/mipsregs.h>
75 -#include <asm/addrspace.h>
76 -#include <asm/signal.h>
77 +
78  #include <6348_map_part.h>
79  #include <6348_intr.h>
80  #include <bcm_map_part.h>
81  #include <bcm_intr.h>
82  
83 -static void irq_dispatch_int(void)
84 -{
85 -       unsigned int pendingIrqs;
86 -       static unsigned int irqBit;
87 -       static unsigned int isrNumber = 31;
88 -
89 -       pendingIrqs = PERF->IrqStatus & PERF->IrqMask;
90 -       if (!pendingIrqs) {
91 -               return;
92 -       }
93 +static int bcm963xx_irq_base;
94  
95 -       while (1) {
96 -       irqBit <<= 1;
97 -       isrNumber++;
98 -       if (isrNumber == 32) {
99 -               isrNumber = 0;
100 -               irqBit = 0x1;
101 -       }
102 -       if (pendingIrqs & irqBit) {
103 -                       PERF->IrqMask &= ~irqBit; // mask
104 -                       do_IRQ(isrNumber + INTERNAL_ISR_TABLE_OFFSET);
105 -               break;
106 -       }
107 -       }
108 +void bcm963xx_unmask_irq(unsigned int irq)
109 +{
110 +       PERF->IrqMask |= (1 << (irq - bcm963xx_irq_base));
111  }
112  
113 -static void irq_dispatch_ext(uint32 irq)
114 +void bcm963xx_mask_irq(unsigned int irq)
115  {
116 -       if (!(PERF->ExtIrqCfg & (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_MASK_SHFT)))) {
117 -       printk("**** Ext IRQ mask. Should not dispatch ****\n");
118 -       }
119 -       /* disable and clear interrupt in the controller */
120 -       PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_CLEAR_SHFT));
121 -       PERF->ExtIrqCfg &= ~(1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_MASK_SHFT));
122 -       do_IRQ(irq);
123 +       PERF->IrqMask &= ~(1 << (irq - bcm963xx_irq_base));
124  }
125  
126 -
127 -//extern void brcm_timer_interrupt(struct pt_regs *regs);
128 -
129 -asmlinkage void plat_irq_dispatch(void)
130 +void bcm963xx_ack_irq(unsigned int irq)
131  {
132 -       unsigned long cause;
133 -
134 -       cause = read_c0_status() & read_c0_cause() & ST0_IM;
135 -       if (cause & CAUSEF_IP7)
136 -               do_IRQ(7);
137 -       else if (cause & CAUSEF_IP2)
138 -               irq_dispatch_int();
139 -       else if (cause & CAUSEF_IP3)
140 -               irq_dispatch_ext(INTERRUPT_ID_EXTERNAL_0);
141 -       else if (cause & CAUSEF_IP4)
142 -               irq_dispatch_ext(INTERRUPT_ID_EXTERNAL_1);
143 -       else if (cause & CAUSEF_IP5)
144 -               irq_dispatch_ext(INTERRUPT_ID_EXTERNAL_2);
145 -       else if (cause & CAUSEF_IP6) {
146 -               irq_dispatch_ext(INTERRUPT_ID_EXTERNAL_3);
147 -               local_irq_disable();
148 -       }
149 +       PERF->IrqStatus &= ~(1 << (irq - bcm963xx_irq_base));
150  }
151  
152 -
153 -void enable_brcm_irq(unsigned int irq)
154 +void bcm963xx_unmask_ext_irq(unsigned int irq)
155  {
156 -       unsigned long flags;
157 -
158 -       local_irq_save(flags);
159 -       if( irq >= INTERNAL_ISR_TABLE_OFFSET ) {
160 -       PERF->IrqMask |= (1 << (irq - INTERNAL_ISR_TABLE_OFFSET));
161 -       }
162 -       else if (irq >= INTERRUPT_ID_EXTERNAL_0 && irq <= INTERRUPT_ID_EXTERNAL_3) {
163 -       /* enable and clear interrupt in the controller */
164 -       PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_CLEAR_SHFT));
165         PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_MASK_SHFT));
166 -       }
167 -       local_irq_restore(flags);
168  }
169  
170 -void disable_brcm_irq(unsigned int irq)
171 +void bcm963xx_mask_ext_irq(unsigned int irq)
172  {
173 -       unsigned long flags;
174 -
175 -       local_irq_save(flags);
176 -       if( irq >= INTERNAL_ISR_TABLE_OFFSET ) {
177 -       PERF->IrqMask &= ~(1 << (irq - INTERNAL_ISR_TABLE_OFFSET));
178 -       }
179 -       else if (irq >= INTERRUPT_ID_EXTERNAL_0 && irq <= INTERRUPT_ID_EXTERNAL_3) {
180 -       /* disable interrupt in the controller */
181         PERF->ExtIrqCfg &= ~(1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_MASK_SHFT));
182 -       }
183 -       local_irq_restore(flags);
184  }
185  
186 -void ack_brcm_irq(unsigned int irq)
187 +void bcm963xx_ack_ext_irq(unsigned int irq)
188  {
189 -       /* Already done in brcm_irq_dispatch */
190 +       PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_CLEAR_SHFT));
191  }
192  
193 -unsigned int startup_brcm_irq(unsigned int irq)
194 +static void bcm963xx_dispatch_ext_irq(unsigned int irq)
195  {
196 -       enable_brcm_irq(irq);
197 -
198 -       return 0; /* never anything pending */
199 +       bcm963xx_ack_ext_irq(irq);
200 +       bcm963xx_mask_ext_irq(irq);
201 +       do_IRQ(irq);
202  }
203  
204 -unsigned int startup_brcm_none(unsigned int irq)
205 +static void bcm963xx_cascade(void)
206  {
207 -       return 0;
208 -}
209 +       uint32_t pending, bit, irq;
210  
211 -void end_brcm_irq(unsigned int irq)
212 -{
213 -       if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
214 -               enable_brcm_irq(irq);
215 -}
216 +       if (!(pending = PERF->IrqStatus & PERF->IrqMask))
217 +               return;
218  
219 -void end_brcm_none(unsigned int irq)
220 -{
221 +       for (irq = 0, bit = 1; irq < 32; irq++, bit <<= 1) {
222 +               if (pending & bit) {
223 +                       bcm963xx_ack_irq(irq + bcm963xx_irq_base);
224 +                       bcm963xx_mask_irq(irq + bcm963xx_irq_base);
225 +                       do_IRQ(irq + bcm963xx_irq_base);
226 +                       return;
227 +               }
228 +       }
229 +
230 +       spurious_interrupt();
231  }
232  
233 -static struct hw_interrupt_type brcm_irq_type = {
234 -       .typename       = "MIPS",
235 -       .startup        = startup_brcm_irq,
236 -       .shutdown       = disable_brcm_irq,
237 -       .enable = enable_brcm_irq,
238 -       .disable        = disable_brcm_irq,
239 -       .ack    = ack_brcm_irq,
240 -       .end    = end_brcm_irq,
241 -       .set_affinity = NULL
242 +static struct irq_chip bcm963xx_irq_type = {
243 +       .name = "bcm963xx",
244 +       .unmask = bcm963xx_unmask_irq,
245 +       .mask = bcm963xx_mask_irq,
246 +       .ack = bcm963xx_ack_irq
247  };
248  
249 -static struct hw_interrupt_type brcm_irq_no_end_type = {
250 -       .typename       = "MIPS",
251 -       .startup        = startup_brcm_none,
252 -       .shutdown       = disable_brcm_irq,
253 -       .enable = enable_brcm_irq,
254 -       .disable        = disable_brcm_irq,
255 -       .ack    = ack_brcm_irq,
256 -       .end    = end_brcm_none,
257 -       .set_affinity = NULL
258 +static struct irq_chip bcm963xx_ext_irq_type = {
259 +       .name = "bcm963xx_ext",
260 +       .unmask = bcm963xx_unmask_ext_irq,
261 +       .mask = bcm963xx_mask_ext_irq,
262 +       .ack = bcm963xx_ack_ext_irq,
263  };
264  
265 -void __init arch_init_irq(void)
266 +static struct irqaction bcm963xx_cascade_action = {
267 +       .handler = no_action,
268 +       .name = "BCM963xx cascade interrupt"
269 +};
270 +
271 +static void __init bcm963xx_irq_init(int base)
272  {
273         int i;
274  
275 -       clear_c0_status(ST0_BEV);
276 -       change_c0_status(ST0_IM, (IE_IRQ0 | IE_IRQ1 | IE_IRQ2 | IE_IRQ3 | IE_IRQ4));
277 -
278 -       for (i = 0; i < NR_IRQS; i++) {
279 -               irq_desc[i].status = IRQ_DISABLED;
280 -               irq_desc[i].action = 0;
281 -               irq_desc[i].depth = 1;
282 -               irq_desc[i].chip = &brcm_irq_type;
283 +       bcm963xx_irq_base = base;
284 +
285 +       /* External IRQs */
286 +       set_irq_chip_and_handler(INTERRUPT_ID_EXTERNAL_0, &bcm963xx_ext_irq_type,
287 +                                handle_level_irq);
288 +       set_irq_chip_and_handler(INTERRUPT_ID_EXTERNAL_1, &bcm963xx_ext_irq_type,
289 +                                handle_level_irq);
290 +       set_irq_chip_and_handler(INTERRUPT_ID_EXTERNAL_2, &bcm963xx_ext_irq_type,
291 +                                handle_level_irq);
292 +       set_irq_chip_and_handler(INTERRUPT_ID_EXTERNAL_3, &bcm963xx_ext_irq_type,
293 +                                handle_level_irq);
294 +
295 +       for (i = 0; i < 32; i++) {
296 +               set_irq_chip_and_handler(base + i, &bcm963xx_irq_type,
297 +                                        handle_level_irq);
298         }
299 +
300 +       setup_irq(2, &bcm963xx_cascade_action);
301 +       setup_irq(bcm963xx_irq_base, &bcm963xx_cascade_action);
302 +       set_c0_status(IE_IRQ0);
303  }
304  
305 -int request_external_irq(unsigned int irq, 
306 -       FN_HANDLER handler,
307 -               unsigned long irqflags, 
308 -               const char * devname,
309 -               void *dev_id)
310 +asmlinkage void plat_irq_dispatch(void)
311  {
312 -       unsigned long flags;
313 +       unsigned int pending = read_c0_status() & read_c0_cause() & ST0_IM;
314  
315 -       local_irq_save(flags);
316 -
317 -       PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_CLEAR_SHFT));      // Clear
318 -       PERF->ExtIrqCfg &= ~(1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_MASK_SHFT));      // Mask
319 -       PERF->ExtIrqCfg &= ~(1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_INSENS_SHFT));    // Edge insesnsitive
320 -       PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_LEVEL_SHFT));      // Level triggered
321 -       PERF->ExtIrqCfg &= ~(1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_SENSE_SHFT));     // Low level
322 -
323 -       local_irq_restore(flags);
324 -
325 -       return( request_irq(irq, handler, irqflags, devname, dev_id) );
326 +       if (pending & STATUSF_IP7)              /* cpu timer */
327 +               do_IRQ(7);
328 +       else if (pending & STATUSF_IP2)         /* internal interrupt cascade */
329 +               bcm963xx_cascade();
330 +       else if (pending & STATUSF_IP3)
331 +               bcm963xx_dispatch_ext_irq(INTERRUPT_ID_EXTERNAL_0);
332 +       else if (pending & STATUSF_IP4)
333 +               bcm963xx_dispatch_ext_irq(INTERRUPT_ID_EXTERNAL_1);
334 +       else if (pending & STATUSF_IP5)
335 +               bcm963xx_dispatch_ext_irq(INTERRUPT_ID_EXTERNAL_2);
336 +       else if (pending & STATUSF_IP6)
337 +               bcm963xx_dispatch_ext_irq(INTERRUPT_ID_EXTERNAL_3);
338 +       else
339 +               spurious_interrupt();
340  }
341  
342 -/* VxWorks compatibility function(s). */
343 -
344 -unsigned int BcmHalMapInterrupt(FN_HANDLER pfunc, unsigned int param,
345 -       unsigned int interruptId)
346 +void __init arch_init_irq(void)
347  {
348 -       int nRet = -1;
349 -       char *devname;
350 -
351 -       devname = kmalloc(16, GFP_KERNEL);
352 -       if (devname)
353 -               sprintf( devname, "brcm_%d", interruptId );
354 -
355 -       /* Set the IRQ description to not automatically enable the interrupt at
356 -        * the end of an ISR.  The driver that handles the interrupt must
357 -        * explicitly call BcmHalInterruptEnable or enable_brcm_irq.  This behavior
358 -        * is consistent with interrupt handling on VxWorks.
359 -        */
360 -       irq_desc[interruptId].chip = &brcm_irq_no_end_type;
361 -
362 -       if( interruptId >= INTERNAL_ISR_TABLE_OFFSET )
363 -       {       
364 -               printk("BcmHalMapInterrupt : internal IRQ\n");
365 -               nRet = request_irq( interruptId, pfunc, IRQF_DISABLED, devname, (void *) param );
366 -       }
367 -       else if (interruptId >= INTERRUPT_ID_EXTERNAL_0 && interruptId <= INTERRUPT_ID_EXTERNAL_3)
368 -       {
369 -               printk("BcmHalMapInterrupt : external IRQ\n");
370 -               nRet = request_external_irq( interruptId, pfunc, IRQF_DISABLED, devname, (void *) param );
371 -       }
372 -
373 -       return( nRet );
374 +       mips_cpu_irq_init();
375 +       bcm963xx_irq_init(INTERNAL_ISR_TABLE_OFFSET);
376  }
377 -
378 -
379 -EXPORT_SYMBOL(enable_brcm_irq);
380 -EXPORT_SYMBOL(disable_brcm_irq);
381 -EXPORT_SYMBOL(request_external_irq);
382 -EXPORT_SYMBOL(BcmHalMapInterrupt);
383 -
384 diff --git a/drivers/serial/bcm63xx_cons.c b/drivers/serial/bcm63xx_cons.c
385 index 8fff16d..2302ea6 100644
386 --- a/drivers/serial/bcm63xx_cons.c
387 +++ b/drivers/serial/bcm63xx_cons.c
388 @@ -267,7 +267,7 @@ static void bcm_interrupt(int irq, void *dev, struct pt_regs *regs)
389         }
390  
391         // Clear the interrupt
392 -       enable_brcm_irq(INTERRUPT_ID_UART);
393 +//     bcm963xx_unmask_irq(INTERRUPT_ID_UART);
394  #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
395         return IRQ_HANDLED;
396  #endif
397 @@ -880,7 +880,7 @@ static int bcm63xx_cons_open(struct tty_struct *tty, struct file *filp)
398         info->count++;
399         tty->driver_data = info;
400         info->tty = tty;
401 -       enable_brcm_irq(INTERRUPT_ID_UART);
402 +       bcm963xx_unmask_irq(INTERRUPT_ID_UART);
403  
404         // Start up serial port
405         retval = startup(info);
406 @@ -927,7 +927,7 @@ static struct tty_operations rs_ops = {
407  -------------------------------------------------------------------------- */
408  static int __init bcm63xx_serialinit(void)
409  {
410 -       int i, flags;
411 +       int i, flags, res;
412         struct bcm_serial *info;
413  
414         // Print the driver version information
415 @@ -981,7 +981,12 @@ static int __init bcm63xx_serialinit(void)
416                  */
417                 if (!info->port)
418                         return 0;
419 -               BcmHalMapInterrupt(bcm_interrupt, 0, INTERRUPT_ID_UART);
420 +
421 +               res = request_irq(INTERRUPT_ID_UART, bcm_interrupt, 0, "bcm-uart", NULL);
422 +               if (res) {
423 +                       spin_unlock_irqrestore(&bcm963xx_serial_lock, flags);
424 +                       return res;
425 +               }
426         }
427  
428         /* order matters here... the trick is that flags
429 diff --git a/include/asm-mips/mach-bcm963xx/bcm_intr.h b/include/asm-mips/mach-bcm963xx/bcm_intr.h
430 index 8c56840..920f783 100644
431 --- a/include/asm-mips/mach-bcm963xx/bcm_intr.h
432 +++ b/include/asm-mips/mach-bcm963xx/bcm_intr.h
433 @@ -39,18 +39,12 @@ struct pt_regs;
434  typedef int (*FN_HANDLER) (int, void *);
435  
436  /* prototypes */
437 -extern void enable_brcm_irq(unsigned int irq);
438 -extern void disable_brcm_irq(unsigned int irq);
439 -extern int request_external_irq(unsigned int irq,
440 -    FN_HANDLER handler, unsigned long irqflags, 
441 -    const char * devname, void *dev_id);
442 -extern unsigned int BcmHalMapInterrupt(FN_HANDLER isr, unsigned int param,
443 -    unsigned int interruptId);
444 -extern void dump_intr_regs(void);
445 -
446 -/* compatibility definitions */
447 -#define BcmHalInterruptEnable(irq)      enable_brcm_irq( irq )
448 -#define BcmHalInterruptDisable(irq)     disable_brcm_irq( irq )
449 +extern void bcm963xx_unmask_irq(unsigned int irq);
450 +extern void bcm963xx_mask_irq(unsigned int irq);
451 +extern void bcm963xx_ack_irq(unsigned int irq);
452 +extern void bcm963xx_unmask_ext_irq(unsigned int irq);
453 +extern void bcm963xx_mask_ext_irq(unsigned int irq);
454 +extern void bcm963xx_ack_ext_irq(unsigned int irq);
455  
456  #ifdef __cplusplus
457      }
458 -- 
459 1.5.5.1
460