From 9a70f2dcb24a5aab29386373c86ba035acba4891 Mon Sep 17 00:00:00 2001 From: Axel Gembe Date: Sun, 18 May 2008 12:07:21 +0200 Subject: [PATCH] bcm963xx: rewrite irq handling code This patch adds interrupt handling as on AR7. The old code was very messy and didn't work too well. Signed-off-by: Axel Gembe --- arch/mips/bcm963xx/irq.c | 308 ++++++++++------------------- drivers/serial/bcm63xx_cons.c | 13 +- include/asm-mips/mach-bcm963xx/bcm_intr.h | 18 +-- 3 files changed, 119 insertions(+), 220 deletions(-) Index: linux-2.6.25.4/arch/mips/bcm963xx/irq.c =================================================================== --- linux-2.6.25.4.orig/arch/mips/bcm963xx/irq.c +++ linux-2.6.25.4/arch/mips/bcm963xx/irq.c @@ -1,259 +1,159 @@ /* -<:copyright-gpl - Copyright 2002 Broadcom Corp. All Rights Reserved. - - This program is free software; you can distribute it and/or modify it - under the terms of the GNU General Public License (Version 2) as - published by the Free Software Foundation. - - This program is distributed in the hope it will be useful, but WITHOUT - ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. -:> -*/ -/* - * Interrupt control functions for Broadcom 963xx MIPS boards + * Copyright (C) 2006,2007 Felix Fietkau + * Copyright (C) 2006,2007 Eugene Konev + * Copyright (C) 2008 Axel Gembe + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include - -#include -#include -#include -#include #include -#include -#include -#include +#include -#include +#include #include -#include -#include + #include <6348_map_part.h> #include <6348_intr.h> #include #include -static void irq_dispatch_int(void) -{ - unsigned int pendingIrqs; - static unsigned int irqBit; - static unsigned int isrNumber = 31; - - pendingIrqs = PERF->IrqStatus & PERF->IrqMask; - if (!pendingIrqs) { - return; - } +static int bcm963xx_irq_base; - while (1) { - irqBit <<= 1; - isrNumber++; - if (isrNumber == 32) { - isrNumber = 0; - irqBit = 0x1; - } - if (pendingIrqs & irqBit) { - PERF->IrqMask &= ~irqBit; // mask - do_IRQ(isrNumber + INTERNAL_ISR_TABLE_OFFSET); - break; - } - } +void bcm963xx_unmask_irq(unsigned int irq) +{ + PERF->IrqMask |= (1 << (irq - bcm963xx_irq_base)); } -static void irq_dispatch_ext(uint32 irq) +void bcm963xx_mask_irq(unsigned int irq) { - if (!(PERF->ExtIrqCfg & (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_MASK_SHFT)))) { - printk("**** Ext IRQ mask. Should not dispatch ****\n"); - } - /* disable and clear interrupt in the controller */ - PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_CLEAR_SHFT)); - PERF->ExtIrqCfg &= ~(1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_MASK_SHFT)); - do_IRQ(irq); + PERF->IrqMask &= ~(1 << (irq - bcm963xx_irq_base)); } - -//extern void brcm_timer_interrupt(struct pt_regs *regs); - -asmlinkage void plat_irq_dispatch(void) +void bcm963xx_ack_irq(unsigned int irq) { - unsigned long cause; - - cause = read_c0_status() & read_c0_cause() & ST0_IM; - if (cause & CAUSEF_IP7) - do_IRQ(7); - else if (cause & CAUSEF_IP2) - irq_dispatch_int(); - else if (cause & CAUSEF_IP3) - irq_dispatch_ext(INTERRUPT_ID_EXTERNAL_0); - else if (cause & CAUSEF_IP4) - irq_dispatch_ext(INTERRUPT_ID_EXTERNAL_1); - else if (cause & CAUSEF_IP5) - irq_dispatch_ext(INTERRUPT_ID_EXTERNAL_2); - else if (cause & CAUSEF_IP6) { - irq_dispatch_ext(INTERRUPT_ID_EXTERNAL_3); - local_irq_disable(); - } + PERF->IrqStatus &= ~(1 << (irq - bcm963xx_irq_base)); } - -void enable_brcm_irq(unsigned int irq) +void bcm963xx_unmask_ext_irq(unsigned int irq) { - unsigned long flags; - - local_irq_save(flags); - if( irq >= INTERNAL_ISR_TABLE_OFFSET ) { - PERF->IrqMask |= (1 << (irq - INTERNAL_ISR_TABLE_OFFSET)); - } - else if (irq >= INTERRUPT_ID_EXTERNAL_0 && irq <= INTERRUPT_ID_EXTERNAL_3) { - /* enable and clear interrupt in the controller */ - PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_CLEAR_SHFT)); PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_MASK_SHFT)); - } - local_irq_restore(flags); } -void disable_brcm_irq(unsigned int irq) +void bcm963xx_mask_ext_irq(unsigned int irq) { - unsigned long flags; - - local_irq_save(flags); - if( irq >= INTERNAL_ISR_TABLE_OFFSET ) { - PERF->IrqMask &= ~(1 << (irq - INTERNAL_ISR_TABLE_OFFSET)); - } - else if (irq >= INTERRUPT_ID_EXTERNAL_0 && irq <= INTERRUPT_ID_EXTERNAL_3) { - /* disable interrupt in the controller */ PERF->ExtIrqCfg &= ~(1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_MASK_SHFT)); - } - local_irq_restore(flags); } -void ack_brcm_irq(unsigned int irq) +void bcm963xx_ack_ext_irq(unsigned int irq) { - /* Already done in brcm_irq_dispatch */ + PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_CLEAR_SHFT)); } -unsigned int startup_brcm_irq(unsigned int irq) +static void bcm963xx_dispatch_ext_irq(unsigned int irq) { - enable_brcm_irq(irq); - - return 0; /* never anything pending */ + bcm963xx_ack_ext_irq(irq); + bcm963xx_mask_ext_irq(irq); + do_IRQ(irq); } -unsigned int startup_brcm_none(unsigned int irq) +static void bcm963xx_cascade(void) { - return 0; -} + uint32_t pending, bit, irq; -void end_brcm_irq(unsigned int irq) -{ - if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) - enable_brcm_irq(irq); -} + if (!(pending = PERF->IrqStatus & PERF->IrqMask)) + return; -void end_brcm_none(unsigned int irq) -{ -} + for (irq = 0, bit = 1; irq < 32; irq++, bit <<= 1) { + if (pending & bit) { + bcm963xx_ack_irq(irq + bcm963xx_irq_base); + bcm963xx_mask_irq(irq + bcm963xx_irq_base); + do_IRQ(irq + bcm963xx_irq_base); + return; + } + } + + spurious_interrupt(); +} + +static struct irq_chip bcm963xx_irq_type = { + .name = "bcm963xx", + .unmask = bcm963xx_unmask_irq, + .mask = bcm963xx_mask_irq, + .ack = bcm963xx_ack_irq +}; -static struct hw_interrupt_type brcm_irq_type = { - .typename = "MIPS", - .startup = startup_brcm_irq, - .shutdown = disable_brcm_irq, - .enable = enable_brcm_irq, - .disable = disable_brcm_irq, - .ack = ack_brcm_irq, - .end = end_brcm_irq, - .set_affinity = NULL +static struct irq_chip bcm963xx_ext_irq_type = { + .name = "bcm963xx_ext", + .unmask = bcm963xx_unmask_ext_irq, + .mask = bcm963xx_mask_ext_irq, + .ack = bcm963xx_ack_ext_irq, }; -static struct hw_interrupt_type brcm_irq_no_end_type = { - .typename = "MIPS", - .startup = startup_brcm_none, - .shutdown = disable_brcm_irq, - .enable = enable_brcm_irq, - .disable = disable_brcm_irq, - .ack = ack_brcm_irq, - .end = end_brcm_none, - .set_affinity = NULL +static struct irqaction bcm963xx_cascade_action = { + .handler = no_action, + .name = "BCM963xx cascade interrupt" }; -void __init arch_init_irq(void) +static void __init bcm963xx_irq_init(int base) { int i; - clear_c0_status(ST0_BEV); - change_c0_status(ST0_IM, (IE_IRQ0 | IE_IRQ1 | IE_IRQ2 | IE_IRQ3 | IE_IRQ4)); + bcm963xx_irq_base = base; - for (i = 0; i < NR_IRQS; i++) { - irq_desc[i].status = IRQ_DISABLED; - irq_desc[i].action = 0; - irq_desc[i].depth = 1; - irq_desc[i].chip = &brcm_irq_type; - } + /* External IRQs */ + set_irq_chip_and_handler(INTERRUPT_ID_EXTERNAL_0, &bcm963xx_ext_irq_type, + handle_level_irq); + set_irq_chip_and_handler(INTERRUPT_ID_EXTERNAL_1, &bcm963xx_ext_irq_type, + handle_level_irq); + set_irq_chip_and_handler(INTERRUPT_ID_EXTERNAL_2, &bcm963xx_ext_irq_type, + handle_level_irq); + set_irq_chip_and_handler(INTERRUPT_ID_EXTERNAL_3, &bcm963xx_ext_irq_type, + handle_level_irq); + + for (i = 0; i < 32; i++) { + set_irq_chip_and_handler(base + i, &bcm963xx_irq_type, + handle_level_irq); + } + + setup_irq(2, &bcm963xx_cascade_action); + setup_irq(bcm963xx_irq_base, &bcm963xx_cascade_action); + set_c0_status(IE_IRQ0); } -int request_external_irq(unsigned int irq, - FN_HANDLER handler, - unsigned long irqflags, - const char * devname, - void *dev_id) +asmlinkage void plat_irq_dispatch(void) { - unsigned long flags; - - local_irq_save(flags); + unsigned int pending = read_c0_status() & read_c0_cause() & ST0_IM; - PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_CLEAR_SHFT)); // Clear - PERF->ExtIrqCfg &= ~(1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_MASK_SHFT)); // Mask - PERF->ExtIrqCfg &= ~(1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_INSENS_SHFT)); // Edge insesnsitive - PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_LEVEL_SHFT)); // Level triggered - PERF->ExtIrqCfg &= ~(1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_SENSE_SHFT)); // Low level - - local_irq_restore(flags); - - return( request_irq(irq, handler, irqflags, devname, dev_id) ); + if (pending & STATUSF_IP7) /* cpu timer */ + do_IRQ(7); + else if (pending & STATUSF_IP2) /* internal interrupt cascade */ + bcm963xx_cascade(); + else if (pending & STATUSF_IP3) + bcm963xx_dispatch_ext_irq(INTERRUPT_ID_EXTERNAL_0); + else if (pending & STATUSF_IP4) + bcm963xx_dispatch_ext_irq(INTERRUPT_ID_EXTERNAL_1); + else if (pending & STATUSF_IP5) + bcm963xx_dispatch_ext_irq(INTERRUPT_ID_EXTERNAL_2); + else if (pending & STATUSF_IP6) + bcm963xx_dispatch_ext_irq(INTERRUPT_ID_EXTERNAL_3); + else + spurious_interrupt(); } -/* VxWorks compatibility function(s). */ - -unsigned int BcmHalMapInterrupt(FN_HANDLER pfunc, unsigned int param, - unsigned int interruptId) +void __init arch_init_irq(void) { - int nRet = -1; - char *devname; - - devname = kmalloc(16, GFP_KERNEL); - if (devname) - sprintf( devname, "brcm_%d", interruptId ); - - /* Set the IRQ description to not automatically enable the interrupt at - * the end of an ISR. The driver that handles the interrupt must - * explicitly call BcmHalInterruptEnable or enable_brcm_irq. This behavior - * is consistent with interrupt handling on VxWorks. - */ - irq_desc[interruptId].chip = &brcm_irq_no_end_type; - - if( interruptId >= INTERNAL_ISR_TABLE_OFFSET ) - { - printk("BcmHalMapInterrupt : internal IRQ\n"); - nRet = request_irq( interruptId, pfunc, IRQF_DISABLED, devname, (void *) param ); - } - else if (interruptId >= INTERRUPT_ID_EXTERNAL_0 && interruptId <= INTERRUPT_ID_EXTERNAL_3) - { - printk("BcmHalMapInterrupt : external IRQ\n"); - nRet = request_external_irq( interruptId, pfunc, IRQF_DISABLED, devname, (void *) param ); - } - - return( nRet ); + mips_cpu_irq_init(); + bcm963xx_irq_init(INTERNAL_ISR_TABLE_OFFSET); } - - -EXPORT_SYMBOL(enable_brcm_irq); -EXPORT_SYMBOL(disable_brcm_irq); -EXPORT_SYMBOL(request_external_irq); -EXPORT_SYMBOL(BcmHalMapInterrupt); - Index: linux-2.6.25.4/drivers/serial/bcm63xx_cons.c =================================================================== --- linux-2.6.25.4.orig/drivers/serial/bcm63xx_cons.c +++ linux-2.6.25.4/drivers/serial/bcm63xx_cons.c @@ -267,7 +267,7 @@ static void bcm_interrupt(int irq, void } // Clear the interrupt - enable_brcm_irq(INTERRUPT_ID_UART); +// bcm963xx_unmask_irq(INTERRUPT_ID_UART); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) return IRQ_HANDLED; #endif @@ -880,7 +880,7 @@ static int bcm63xx_cons_open(struct tty_ info->count++; tty->driver_data = info; info->tty = tty; - enable_brcm_irq(INTERRUPT_ID_UART); + bcm963xx_unmask_irq(INTERRUPT_ID_UART); // Start up serial port retval = startup(info); @@ -927,7 +927,7 @@ static struct tty_operations rs_ops = { -------------------------------------------------------------------------- */ static int __init bcm63xx_serialinit(void) { - int i, flags; + int i, flags, res; struct bcm_serial *info; // Print the driver version information @@ -981,7 +981,12 @@ static int __init bcm63xx_serialinit(voi */ if (!info->port) return 0; - BcmHalMapInterrupt(bcm_interrupt, 0, INTERRUPT_ID_UART); + + res = request_irq(INTERRUPT_ID_UART, bcm_interrupt, 0, "bcm-uart", NULL); + if (res) { + spin_unlock_irqrestore(&bcm963xx_serial_lock, flags); + return res; + } } /* order matters here... the trick is that flags Index: linux-2.6.25.4/include/asm-mips/mach-bcm963xx/bcm_intr.h =================================================================== --- linux-2.6.25.4.orig/include/asm-mips/mach-bcm963xx/bcm_intr.h +++ linux-2.6.25.4/include/asm-mips/mach-bcm963xx/bcm_intr.h @@ -39,18 +39,12 @@ struct pt_regs; typedef int (*FN_HANDLER) (int, void *); /* prototypes */ -extern void enable_brcm_irq(unsigned int irq); -extern void disable_brcm_irq(unsigned int irq); -extern int request_external_irq(unsigned int irq, - FN_HANDLER handler, unsigned long irqflags, - const char * devname, void *dev_id); -extern unsigned int BcmHalMapInterrupt(FN_HANDLER isr, unsigned int param, - unsigned int interruptId); -extern void dump_intr_regs(void); - -/* compatibility definitions */ -#define BcmHalInterruptEnable(irq) enable_brcm_irq( irq ) -#define BcmHalInterruptDisable(irq) disable_brcm_irq( irq ) +extern void bcm963xx_unmask_irq(unsigned int irq); +extern void bcm963xx_mask_irq(unsigned int irq); +extern void bcm963xx_ack_irq(unsigned int irq); +extern void bcm963xx_unmask_ext_irq(unsigned int irq); +extern void bcm963xx_mask_ext_irq(unsigned int irq); +extern void bcm963xx_ack_ext_irq(unsigned int irq); #ifdef __cplusplus }