/* * Copyright (C) 2006 Ikanos Communications. All rights reserved. * The information and source code contained herein is the property * of Ikanos Communications. */ /* * BRIEF MODULE DESCRIPTION * ADI 6843 interrupt routines. * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * 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., * 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #undef DEBUG_IRQ #ifdef DEBUG_IRQ /* note: prints function name for you */ #define DPRINTK(fmt, args...) printk("%s: " fmt, __FUNCTION__ , ## args) #else #define DPRINTK(fmt, args...) #endif #define ALL_INTS (IE_IRQ0 | IE_IRQ1 | IE_IRQ2 | IE_IRQ3 | IE_IRQ4 | IE_IRQ5) // #define ALL_INTS (IE_IRQ2 | IE_IRQ5) /* Priority Array * 5 = highest, 0 = Lowest. * Please note that timer1 should always be priority 5 since the low * level assembly code hardwires CAUSEF_IP7 to go to the timer ISR. */ extern int cpu_wait_end(void); static unsigned char ikan6850_prio[38] = { 0, /* DMA = 0 */ 0, /* ARB1 */ 6, /* SCU */ 1, /* MIPS_TIMER */ 0, /* GPIO2 */ 3, /* DBUSA_ARB */ 2, /* UART1 = 6 */ 0, /* SPI */ 0, /* GPIO */ 5, /* Timer 1 */ 0, /* Timer 2 */ 0, /* Timer 3 */ 0, /* DBUSB */ 3, /* GIGE2 */ 3, /* GIGE1 */ 0, /* VDSP_IDMA2 = 15 */ 0, /* VDSP_IDMA1 */ 4, /* DSP0_FL0 */ #if defined(CONFIG_FUSIV_DSP_BASED_VOICE_V1_ARCH) && CONFIG_FUSIV_DSP_BASED_VOICE_V1_ARCH 0, /* DSP0_FL1 */ #else 3, /* DSP0_FL1 */ #endif 0, /* DSP0_PF0 */ 0, /* DSP0_PF1 */ 3, /* DSP1_FL0 */ #if defined(CONFIG_FUSIV_DSP_BASED_VOICE_V1_ARCH) && CONFIG_FUSIV_DSP_BASED_VOICE_V1_ARCH 0, /* DSP1_FL1 */ #else 3, /* DSP1_FL1 */ #endif 0, /* DSP1_PF0 */ 0, /* DSP1_PF1 */ 3, /* PCI = 25 */ 0, /* VDSL_PHY */ 0, /* DBUSC */ 0, /* SPA */ 0, /* UART2 */ 0, /* WAP_GIGE */ 4, /* BMU_GIGE */ 0, /* AP DMA */ 0, /* MIPS_SW0 */ 0, /* MIPS_SW1 */ 0, /* USBH */ 0, /* VDSL_AP */ 0 /* MIPS_PC */ }; static volatile ipc_t *ipc = (volatile ipc_t *)KSEG1ADDR(0x19050000); #ifdef CONFIG_KGDB extern void breakpoint(void); #endif extern void set_debug_traps(void); extern irq_cpustat_t irq_stat [NR_CPUS]; // extern asmlinkage void adi_6843_irq(void); unsigned int local_bh_count[NR_CPUS]; unsigned int local_irq_count[NR_CPUS]; static unsigned int startup_irq(unsigned int irq); static inline void end_irq(unsigned int irq_nr); static inline void local_enable_irq(unsigned int irq_nr); // static inline void local_disable_irq(unsigned int irq_nr); inline void local_disable_irq(unsigned int irq_nr); static inline void ack_irq(unsigned int irq_nr); static inline void mask_irq(unsigned int irq_nr); static inline void mask_ack_irq(unsigned int irq_nr); static inline void unmask_irq(unsigned int irq_nr); static inline void eoi_irq(unsigned int irq_nr); void fusiv_set_irq_prio(unsigned int irq_nr, unsigned int prio) { if(irq_nr >= sizeof(ikan6850_prio) / sizeof(ikan6850_prio[0])) return; ikan6850_prio[irq_nr] = prio; /*--- irq is disabled ---*/ if((ipc->cpe_prio[irq_nr >> 2] & (0xff << ((irq_nr & 0x3) * 8))) != (0xff << ((irq_nr & 0x3) * 8))) return; /*--- change prio of enabled irq ---*/ ipc->cpe_prio[irq_nr >> 2] &= ~(0xff << ((irq_nr & 0x3) * 8)); ipc->cpe_prio[irq_nr >> 2] |= (ikan6850_prio[irq_nr] << ((irq_nr & 0x3) * 8)); } static inline void local_enable_irq(unsigned int irq_nr) { ipc->cpe_prio[irq_nr >> 2] &= ~(0xff << ((irq_nr & 0x3) * 8)); ipc->cpe_prio[irq_nr >> 2] |= (ikan6850_prio[irq_nr] << ((irq_nr & 0x3) * 8)); /* Ensure the write before we go back */ // wbflush(); } // static inline void local_disable_irq(unsigned int irq_nr) inline void local_disable_irq(unsigned int irq_nr) { ipc->cpe_prio[irq_nr >> 2] |= (0xff << ((irq_nr & 0x3) * 8)); /* Ensure the write before we go back */ // wbflush(); } static inline void ack_irq(unsigned int irq_nr) { /* We have already done it from our dispatcher code so nothing to do when kernel calls this . */ //local_disable_irq(irq_nr); } static inline void mask_irq(unsigned int irq_nr) { /* We have already done it from our dispatcher code so nothing to do when kernel calls this . */ //local_disable_irq(irq_nr); } static inline void mask_ack_irq(unsigned int irq_nr) { /* We have already done it from our dispatcher code so nothing to do when kernel calls this . */ //local_disable_irq(irq_nr); } static inline void unmask_irq(unsigned int irq_nr) { /* We have already done it from our dispatcher code so nothing to do when kernel calls this . */ //local_disable_irq(irq_nr); } static inline void eoi_irq(unsigned int irq_nr) { /* We have already done it from our dispatcher code so nothing to do when kernel calls this . */ //local_disable_irq(irq_nr); } static inline void end_irq(unsigned int irq_nr) { if (!(irq_desc[irq_nr].status & (IRQ_DISABLED|IRQ_INPROGRESS))) local_enable_irq(irq_nr); } static unsigned int startup_irq(unsigned int irq_nr) { local_enable_irq(irq_nr); return 0; } static void shutdown_irq(unsigned int irq_nr) { local_disable_irq(irq_nr); return; } static struct hw_interrupt_type adi_6843 = { .name = "ADI fusiv", .startup = startup_irq, .shutdown = shutdown_irq, .enable = local_enable_irq, .disable = local_disable_irq, .ack = ack_irq, .mask = mask_irq, .mask_ack = mask_ack_irq, .unmask = unmask_irq, .eoi = eoi_irq, .end = end_irq, .set_affinity = NULL, .retrigger = NULL, .set_type = NULL, .set_wake = NULL, }; static spinlock_t irq_spinlock; void adi_6843_dispatch_irq(int prio, struct pt_regs *regs) { /* This is over lovely dispatcher, which gets called from Interrupt specific code */ /* Let us put some memory rather then few clocks. Memory is always cheaper then CPU time . */ unsigned char IRQCnt1 = 0; unsigned char IRQCnt2 = 0; unsigned char i; unsigned char IRQSet1[32]; unsigned char IRQSet2[32]; unsigned long stat; int irq; unsigned long flags; cpu_wait_end(); stat = ipc->cpe_stat[prio].l; if(likely(stat != 0)) { /* Very first thing to check if there is some thing to do */ while (stat) { irq = ffs(stat) - 1; stat &= ~(1 << irq); if(irq == DSP0_FL0_INT) { /*--- exklusive sofortige Behandlung von pcmlink: ---*/ /*--- spin_lock_irqsave(&irq_spinlock, flags); ---*/ do_IRQ(irq); /*--- spin_unlock_irqrestore(&irq_spinlock, flags); ---*/ } else { IRQSet1[IRQCnt1++] = irq; } } } stat = ipc->cpe_stat[prio].h; if(likely(stat != 0)) { /* Very first thing to check if there is some thing to do */ while (stat) { irq = ffs(stat) - 1; stat &= ~(1 << irq); IRQSet2[IRQCnt2++] = irq + 32; } } /* We should not call do_IRQ before we acknowldege the Interupt Controller , one or more IRQ handler may not have SA_INTERRUPT bit set and kernel will enable the interrupts (which was set by processor ), which will again drag some body here . */ /* Let us acknowldege Interupt Controller that we got the message */ for(i = 0; i < IRQCnt1; i++) { local_disable_irq(IRQSet1[i]); } /* Let us acknowldege Interupt Controller that we got the message */ for(i = 0; i < IRQCnt2; i++) { local_disable_irq(IRQSet2[i]); } /* Now we are safe to enable the interrupts becauase we have told Interupt Controller that we are all set to go. */ for(i = 0; i < IRQCnt1; i++) { do_IRQ(IRQSet1[i]); } /* Now we are safe to enable the interrupts becauase we have told Interupt Controller that we are all set to go. */ for(i = 0; i < IRQCnt2; i++) { do_IRQ(IRQSet2[i]); } } static void __init mips_nmi_setup(void) { void *base; extern char except_vec_nmi; base = cpu_has_veic ? (void *)(CAC_BASE + 0xa80) : (void *)(CAC_BASE + 0x380); memcpy(base, &except_vec_nmi, 0x80); printk(KERN_ERR "[%s] setup NMI vector to base 0x%p\n", __FUNCTION__, base); flush_icache_range((unsigned long)base, (unsigned long)base + 0x80); } void __init fusiv_init_IRQ(void) { int i; unsigned long cp0_status; cp0_status = read_c0_status(); spin_lock_init(&irq_spinlock); /* Set all interrupt priorities to 0x1f so that they are not routed * anywhere. */ #if defined(CONFIG_FUSIV_VX200) && CONFIG_FUSIV_VX200 for (i = 0; i < 8; i++) { ipc->cpe_prio[i] = 0x1f1f1f1f; } ipc->ap_prio = 0x1f1f1f1f; #endif for (i = 0; i < 10; i++) ipc->cpe_prio[i] = 0x0f0f0f0f; /* let us initialize all the IRQs Descriptors. */ for (i = 0; i <= ADI_6843_MAX_INTS; i++) { irq_desc[i].chip = &adi_6843; irq_desc[i].status = IRQ_DISABLED; irq_desc[i].action = 0; irq_desc[i].depth = 1; spin_lock_init(&irq_desc[i].lock); } // write_c0_cause(0x0); clear_c0_status(ALL_INTS); set_c0_status(ALL_INTS); #ifdef CONFIG_KGDB /* If local serial I/O used for debug port, enter kgdb at once */ puts("Waiting for kgdb to connect..."); set_debug_traps(); breakpoint(); #endif board_nmi_handler_setup = mips_nmi_setup; } #ifdef CONFIG_HIGH_RES_TIMERS /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ void mips_timer_configure(void) { write_c0_compare(read_c0_count()); /*--- resete pending timer-irq ---*/ fusiv_set_irq_prio(GPT1_INT, 1); /*--- der wird nicht gebraucht: auf niedrigere Prio ---*/ /*--------------------------------------------------------------------------------*\ * mbahr: * Setze MIPS-Timer-IRQ auf IP7 * Der Irq-Dispatcher für diesen Cause wird bereits im Assemblerfile "int-handler.S" * auf fusiv_timer_interrupt ("plat_time.c" ) gebogen * Das Setzen des Routings an dieser Stelle ist notwendig, da, * sonst die die MIPS-Timer-Erkennung (Fkt. c0_compare_int_usable()) nicht funktioniert \*--------------------------------------------------------------------------------*/ fusiv_set_irq_prio(MIPS_TIMER_INT, 5); /*--- Hack: wird eigentlich per Coprozessor-Reg IntCtl (Bit29-31) ausgelesen, ist bei Ikanaos aber ---*/ cp0_compare_irq = 7; local_enable_irq(MIPS_TIMER_INT); } /*--------------------------------------------------------------------------------*\ * wird von mips-timer benoetigt: \*--------------------------------------------------------------------------------*/ void unmask_mips_timer_irq(unsigned int irq) { set_c0_status(STATUSF_IP7); } /*--------------------------------------------------------------------------------*\ * wird von mips-timer benoetigt: \*--------------------------------------------------------------------------------*/ void mask_mips_timer_irq(unsigned int irq) { clear_c0_status(STATUSF_IP7); } #endif/*--- #ifdef CONFIG_HIGH_RES_TIMERS ---*/