From ca851edf100bd3fd2b74d723b80822c8c2ff5559 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Sun, 18 May 2008 17:31:25 +0000 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. It also removes the unused file int-handler.S. Signed-off-by: Axel Gembe SVN-Revision: 11178 --- ...0-bcm963xx_rewrite_irq_handling_code.patch | 460 ++++++++++++++++++ ...0-bcm963xx_remove_unused_int_handler.patch | 95 ++++ 2 files changed, 555 insertions(+) create mode 100644 target/linux/brcm63xx/patches-2.6.25/060-bcm963xx_rewrite_irq_handling_code.patch create mode 100644 target/linux/brcm63xx/patches-2.6.25/080-bcm963xx_remove_unused_int_handler.patch diff --git a/target/linux/brcm63xx/patches-2.6.25/060-bcm963xx_rewrite_irq_handling_code.patch b/target/linux/brcm63xx/patches-2.6.25/060-bcm963xx_rewrite_irq_handling_code.patch new file mode 100644 index 0000000000..4f1e360a1d --- /dev/null +++ b/target/linux/brcm63xx/patches-2.6.25/060-bcm963xx_rewrite_irq_handling_code.patch @@ -0,0 +1,460 @@ +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(-) + +diff --git a/arch/mips/bcm963xx/irq.c b/arch/mips/bcm963xx/irq.c +index 62a848b..11583c9 100644 +--- a/arch/mips/bcm963xx/irq.c ++++ b/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 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_irq_type = { ++ .name = "bcm963xx", ++ .unmask = bcm963xx_unmask_irq, ++ .mask = bcm963xx_mask_irq, ++ .ack = bcm963xx_ack_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 irq_chip bcm963xx_ext_irq_type = { ++ .name = "bcm963xx_ext", ++ .unmask = bcm963xx_unmask_ext_irq, ++ .mask = bcm963xx_mask_ext_irq, ++ .ack = bcm963xx_ack_ext_irq, + }; + +-void __init arch_init_irq(void) ++static struct irqaction bcm963xx_cascade_action = { ++ .handler = no_action, ++ .name = "BCM963xx cascade interrupt" ++}; ++ ++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)); +- +- 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; ++ bcm963xx_irq_base = base; ++ ++ /* 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; ++ unsigned int pending = read_c0_status() & read_c0_cause() & ST0_IM; + +- local_irq_save(flags); +- +- 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); +- +diff --git a/drivers/serial/bcm63xx_cons.c b/drivers/serial/bcm63xx_cons.c +index 8fff16d..2302ea6 100644 +--- a/drivers/serial/bcm63xx_cons.c ++++ b/drivers/serial/bcm63xx_cons.c +@@ -267,7 +267,7 @@ static void bcm_interrupt(int irq, void *dev, struct pt_regs *regs) + } + + // 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_struct *tty, struct file *filp) + 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(void) + */ + 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 +diff --git a/include/asm-mips/mach-bcm963xx/bcm_intr.h b/include/asm-mips/mach-bcm963xx/bcm_intr.h +index 8c56840..920f783 100644 +--- a/include/asm-mips/mach-bcm963xx/bcm_intr.h ++++ b/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 + } +-- +1.5.5.1 + diff --git a/target/linux/brcm63xx/patches-2.6.25/080-bcm963xx_remove_unused_int_handler.patch b/target/linux/brcm63xx/patches-2.6.25/080-bcm963xx_remove_unused_int_handler.patch new file mode 100644 index 0000000000..ad4484e319 --- /dev/null +++ b/target/linux/brcm63xx/patches-2.6.25/080-bcm963xx_remove_unused_int_handler.patch @@ -0,0 +1,95 @@ +From e3abd028e7631ee952fe73d8f9ee97bc615526a8 Mon Sep 17 00:00:00 2001 +From: Axel Gembe +Date: Sat, 17 May 2008 16:07:46 +0200 +Subject: [PATCH] bcm963xx: remove unused int-handler.S + +The code is not used anymore. + +Signed-off-by: Axel Gembe +--- + arch/mips/bcm963xx/Makefile | 2 +- + arch/mips/bcm963xx/int-handler.S | 59 -------------------------------------- + 2 files changed, 1 insertions(+), 60 deletions(-) + delete mode 100644 arch/mips/bcm963xx/int-handler.S + +diff --git a/arch/mips/bcm963xx/Makefile b/arch/mips/bcm963xx/Makefile +index a9d1e55..77fbd84 100644 +--- a/arch/mips/bcm963xx/Makefile ++++ b/arch/mips/bcm963xx/Makefile +@@ -3,7 +3,7 @@ + # + # Copyright (C) 2004 Broadcom Corporation + # +-obj-y := irq.o prom.o setup.o time.o ser_init.o int-handler.o info.o wdt.o ++obj-y := irq.o prom.o setup.o time.o ser_init.o info.o wdt.o + + SRCBASE := $(TOPDIR) + EXTRA_CFLAGS += -I$(SRCBASE)/include +diff --git a/arch/mips/bcm963xx/int-handler.S b/arch/mips/bcm963xx/int-handler.S +deleted file mode 100644 +index a7a9c9d..0000000 +--- a/arch/mips/bcm963xx/int-handler.S ++++ /dev/null +@@ -1,59 +0,0 @@ +-/* +-<: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. +-:> +-*/ +-/* +- * Generic interrupt handler for Broadcom MIPS boards +- */ +- +-#include +- +-#include +-#include +-#include +-#include +- +-/* +- * MIPS IRQ Source +- * -------- ------ +- * 0 Software (ignored) +- * 1 Software (ignored) +- * 2 Combined hardware interrupt (hw0) +- * 3 Hardware +- * 4 Hardware +- * 5 Hardware +- * 6 Hardware +- * 7 R4k timer +- */ +- +- .text +- .set noreorder +- .set noat +- .align 5 +- NESTED(brcmIRQ, PT_SIZE, sp) +- SAVE_ALL +- CLI +- .set noreorder +- .set at +- +- jal plat_irq_dispatch +- move a0, sp +- +- j ret_from_irq +- nop +- +- END(brcmIRQ) +-- +1.5.5.1 +