diff options
Diffstat (limited to 'c/src/lib/libbsp/powerpc/beatnik/irq/discovery_pic.c')
-rw-r--r-- | c/src/lib/libbsp/powerpc/beatnik/irq/discovery_pic.c | 995 |
1 files changed, 995 insertions, 0 deletions
diff --git a/c/src/lib/libbsp/powerpc/beatnik/irq/discovery_pic.c b/c/src/lib/libbsp/powerpc/beatnik/irq/discovery_pic.c new file mode 100644 index 0000000000..8b0eba27fc --- /dev/null +++ b/c/src/lib/libbsp/powerpc/beatnik/irq/discovery_pic.c @@ -0,0 +1,995 @@ +/* $Id$ */ + +/* Interrupt driver + dispatcher for the discovery host controller */ + +/* Author: T. Straumann, 2005-2007 + * + * Acknowledgements: + * Valuable information was obtained from the following drivers + * netbsd: (C) Allegro Networks Inc; Wasabi Systems Inc. + * linux: (C) MontaVista, Software, Inc; Chris Zankel, Mark A. Greer. + * rtems: (C) Brookhaven National Laboratory; K. Feng + * but this implementation is original work by the author. + */ + +/* + * Authorship + * ---------- + * This software ('beatnik' RTEMS BSP for MVME6100 and MVME5500) was + * created by Till Straumann <strauman@slac.stanford.edu>, 2005-2007, + * Stanford Linear Accelerator Center, Stanford University. + * + * Acknowledgement of sponsorship + * ------------------------------ + * The 'beatnik' BSP was produced by + * the Stanford Linear Accelerator Center, Stanford University, + * under Contract DE-AC03-76SFO0515 with the Department of Energy. + * + * Government disclaimer of liability + * ---------------------------------- + * Neither the United States nor the United States Department of Energy, + * nor any of their employees, makes any warranty, express or implied, or + * assumes any legal liability or responsibility for the accuracy, + * completeness, or usefulness of any data, apparatus, product, or process + * disclosed, or represents that its use would not infringe privately owned + * rights. + * + * Stanford disclaimer of liability + * -------------------------------- + * Stanford University makes no representations or warranties, express or + * implied, nor assumes any liability for the use of this software. + * + * Stanford disclaimer of copyright + * -------------------------------- + * Stanford University, owner of the copyright, hereby disclaims its + * copyright and all other rights in this software. Hence, anyone may + * freely use it for any purpose without restriction. + * + * Maintenance of notices + * ---------------------- + * In the interest of clarity regarding the origin and status of this + * SLAC software, this and all the preceding Stanford University notices + * are to remain affixed to any copy or derivative of this software made + * or distributed by the recipient and are to be affixed to any copy of + * software made or distributed by the recipient that contains a copy or + * derivative of this software. + * + * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03 + */ +#include <rtems.h> +#include <bsp.h> +#include <bsp/irq.h> +#include <bsp/gtreg.h> +#include <bsp/gtintrreg.h> +#include <rtems/bspIo.h> +#include <bsp/vectors.h> +#include <libcpu/byteorder.h> +#include <libcpu/spr.h> + +/* dont change the order (main_lo, main_hi, gpp) which + * matches the interrupt numbers! + */ +#define MAIN_LO_IDX 0 +#define MAIN_HI_IDX 1 +#define GPP_IDX 2 +#define NUM_INTR_REGS 3 + + +#define SYNC() asm volatile("sync") + +/* How many times should the ISR dispatcher check for + * pending interrupts until it decides that something's + * fishy (i.e., a user ISR fails to clear the interrupt + * source) + */ +#define MAX_SPIN_LOOPS 100 + +/* If FASTER is defined, a few obscure I/O statements found in the linux + * driver are removed + */ +#define FASTER + +/* Array helper */ +#define NumberOf(arr) (sizeof(arr)/sizeof((arr)[0])) + + +/* MVME6100 specific; re-define watchdog NMI pin to be a normal output + * so we have a way to raise an interrupt in software (GPP[26] is wired to + * GPP[6] on the MVME6100). + */ +#define MVME6100_IRQ_DEBUG 4 + +#define GPP_WIRED_OUT_BIT_6100 26 /* CAVEAT: this is bit 26 on the 6100 */ +#define GPP_WIRED_OUT_BIT_5500 24 /* CAVEAT: this is bit 24 on the 5500 */ +#define GPP_WIRED_IN_BIT 6 + +/* Ored mask of debugging features to enable */ +#define IRQ_DEBUG_BASIC 1 +/* This is _very_ lowlevel */ +#define IRQ_DEBUG_DISPATCHER 2 +/* Record maximal dispatching latency */ +#define IRQ_DEBUG_MAXLAT 8 /* PPC only */ + +#define IRQ_DEBUG (0 /*|(IRQ_DEBUG_BASIC)*/|(MVME6100_IRQ_DEBUG)|(IRQ_DEBUG_MAXLAT)) + +/********** + * Typedefs + **********/ + +/* Set of the three relevant cause registers */ +typedef volatile unsigned IrqMask[NUM_INTR_REGS]; + +#define REGP(x) ((volatile uint32_t *)(x)) + +/* Information we keep about the PIC */ +typedef struct _Mv64x60PicRec { + /* base address as seen from CPU */ + uintptr_t reg_base; + + /* addresses of 'cause' registers */ + volatile uint32_t *causes[NUM_INTR_REGS]; + + /* addresses of 'mask' registers */ + volatile uint32_t *masks[NUM_INTR_REGS]; + + /* masks for all priorities. If an + * interrupt source has priority X, + * its corresponding bit is set + * (enabled) in mcache[i] for all + * i < X and cleared for i >= X + */ + volatile IrqMask mcache[BSP_IRQ_MAX_PRIO+1]; + + /* Priority we're executing at. + * Thread-level is priority 0, + * ISRs range from 1..MAX_PRIO + */ + volatile rtems_irq_prio current_priority; +} Mv64x60PicRec, *Mv64x60Pic; + +/********** + * Globals + **********/ + + +/* Copy of the configuration */ +static rtems_irq_global_settings theConfig; +/* PIC description */ +static Mv64x60PicRec thePic; + +#if (IRQ_DEBUG) & MVME6100_IRQ_DEBUG +static unsigned long gpp_out_bit = 0; +#endif + +#if (IRQ_DEBUG) & IRQ_DEBUG_MAXLAT +unsigned long discovery_pic_max_dispatching_latency = 0; +#ifdef __PPC__ +static inline unsigned long mftb(void) +{ +unsigned long rval; + asm volatile("mftb %0":"=r"(rval)); + return rval; +} +#else +#define mftb() 0 +#endif +#endif + +/********** + * Functions + **********/ + +/* Debugging helper routines */ +static void pregs(volatile uint32_t **p) +{ +int i; + for (i=NUM_INTR_REGS-1; i>=0; i--) { + printk(" 0x%08x", ld_le32(p[i])); + printk( i ? " --":"\n"); + } +} + +static void pmsks(volatile IrqMask p) +{ +int i; + for (i=NUM_INTR_REGS-1; i>=0; i--) { + printk(" 0x%08x", p[i]); + printk( i ? " --":"\n"); + } +} + +void discovery_dump_picregs(void) +{ + printk(" ..GPP_IRQ. -- ..MAIN_HI. -- ..MAIN_LO.\n"); + printk("Cause:"); pregs(thePic.causes); + printk("Mask: "); pregs(thePic.masks); +} + +/* Small inline helpers */ + +/* return 0 if this PIC is not 'responsible' for a given irq number + * we also 'ignore' the GPP summary bits - these must always remain + * enabled. + */ +static inline int +validIrqNo(rtems_irq_number irq) +{ + return + irq >= BSP_PCI_IRQ_LOWEST_OFFSET + && irq <= BSP_PCI_IRQ_MAX_OFFSET + && ! (IMH_GPP_SUM & (1<<(irq-32))); +} + +/* return 0 if a given priority is outside the valid range */ +static inline int +validPri(rtems_irq_prio pri) +{ + /* silence compiler warning about limited range of type; + * hope it never changes... + */ + return /* pri>=0 && */ pri <=BSP_IRQ_MAX_PRIO; +} + +/* Return position of the most significant bit that is set in 'x' */ +static inline int +__ilog2(unsigned x) +{ + asm volatile("cntlzw %0, %0":"=&r"(x):"0"(x)); + return 31-x; +} + +/* Convert irq number to cause register index + * (array of handles in the PicRec). + * ASSUMES: 'irq' within valid range. + */ +static inline unsigned +irqDiv32(unsigned irq) +{ + return (irq-BSP_PCI_IRQ_LOWEST_OFFSET)>>5; +} + +/* Convert irq number to cause/mask bit number. + * ASSUMES: 'irq' within valid range. + */ + +static inline unsigned +irqMod32(unsigned irq) +{ + return (irq-BSP_PCI_IRQ_LOWEST_OFFSET)&31; +} + +/* NON-ATOMICALLY set/clear bits in a MV64x60 register + * + * register contents at offset 'off' are ANDed with + * complement of the 'clr' mask and ORed with 'set' mask: + * + * *off = (*off & ~clr) | set + * + * ASSUMES: executed from IRQ-disabled section + */ +static inline void +gt_bitmod(unsigned off, unsigned set, unsigned clr) +{ + st_le32(REGP(thePic.reg_base + off), + (ld_le32(REGP(thePic.reg_base+off)) & ~clr) | set); +} + +static inline unsigned +gt_read(unsigned off) +{ + return ld_le32(REGP(thePic.reg_base + off)); +} + +static inline void +gt_write(unsigned off, unsigned val) +{ + st_le32(REGP(thePic.reg_base + off), val); +} + +/* Enable interrupt number 'irq' at the PIC. + * + * Checks for valid arguments but has no way of + * communicating violation; prints to console + * if illegal arguments are given. + * + * This routine may be called from ISR level. + * + * Algorithm: set corresponding bit in masks + * for all priorities lower than the + * target irq's priority and push + * mask for the currently executing + * priority out to the PIC. + */ + +void +BSP_enable_irq_at_pic(rtems_irq_number irq) +{ +unsigned i,j; +unsigned long flags; +volatile uint32_t *p; +uint32_t v,m; + + if ( !validIrqNo(irq) ) { +/* API change - must silently ignore... + printk("BSP_enable_irq_at_pic: Invalid argument (irq #%i)\n",irq); + */ + return; + } + +#if (IRQ_DEBUG) & IRQ_DEBUG_BASIC + printk("IRQ: Enable #%i;",irq); +#endif + + if ( (i=irqDiv32(irq)) > NUM_INTR_REGS ) { + /* This is probably a more serious error; don't ignore silently */ + printk("BSP_enable_irq_at_pic: illegal argument\n"); + return; + } + /* compute register pointer and bit mask */ + p = thePic.masks[i]; + m = 1<<irqMod32(irq); + + rtems_interrupt_disable(flags); + { + /* access table from protected section to be thread-safe */ + rtems_irq_prio pri = theConfig.irqPrioTbl[irq]; + for ( j=0; j<pri; j++ ) { + thePic.mcache[j][i] |= m; + } + st_le32(p, (v=thePic.mcache[thePic.current_priority][i])); + /* linux driver reads back GPP mask; maybe it's wise to do the same */ + (void)ld_le32(thePic.masks[GPP_IDX]); + } + SYNC(); + rtems_interrupt_enable(flags); + +#if (IRQ_DEBUG) & IRQ_DEBUG_BASIC + printk(" Mask[%i]: 0x%08x -> 0x%08x\n",i,v,ld_le32(p)); + +#endif +} + +/* Disable interrupt number 'irq' at the PIC. + * + * Checks for valid arguments but has no way of + * communicating violation; prints to console + * if illegal arguments are given. + * + * This routine may be called from ISR level. + * + * Algorithm: clear corresponding bit in masks + * for all priorities and push the + * mask for the currently executing + * priority out to the PIC. + */ + +int +BSP_disable_irq_at_pic(rtems_irq_number irq) +{ +unsigned i,j; +unsigned long flags; +volatile uint32_t *p; +uint32_t v,m; +int rval; + + if ( !validIrqNo(irq) ) { +/* API change - must silently ignore... + printk("BSP_disable_irq_at_pic: Invalid argument (irq #%i)\n",irq); + */ + return -1; + } + +#if (IRQ_DEBUG) & IRQ_DEBUG_BASIC + printk("IRQ: Disable #%i;",irq); +#endif + + if ( (i=irqDiv32(irq)) > NUM_INTR_REGS ) { + /* This is probably a more serious error; don't ignore silently */ + printk("BSP_enable_irq_at_pic: illegal argument\n"); + return -1; + } + + /* compute register pointer and bit mask */ + p = thePic.masks[i]; + m = (1<<irqMod32(irq)); + + rtems_interrupt_disable(flags); + { + rval = thePic.mcache[thePic.current_priority][i] & m; + for (j=0; j<=BSP_IRQ_MAX_PRIO; j++) + thePic.mcache[j][i] &= ~m; + st_le32(p, (v=thePic.mcache[thePic.current_priority][i])); + /* linux driver reads back GPP mask; maybe it's wise to do the same */ + (void)ld_le32(thePic.masks[GPP_IDX]); + } + SYNC(); + rtems_interrupt_enable(flags); + +#if (IRQ_DEBUG) & IRQ_DEBUG_BASIC + printk(" Mask[%i]: 0x%08x -> 0x%08x\n",i,v,ld_le32(p)); +#endif + + return rval ? 1 : 0; +} + +int +BSP_irq_is_enabled_at_pic(rtems_irq_number irq) +{ +unsigned i; + if ( !validIrqNo(irq) ) { + printk("BSP_irq_is_enabled_at_pic: Invalid argument (irq #%i)\n",irq); + return -1; + } + + if ( (i=irqDiv32(irq)) > NUM_INTR_REGS ) { + printk("BSP_enable_irq_at_pic: illegal argument\n"); + return -1; + } + return ld_le32(thePic.masks[i]) & (1<<irqMod32(irq)) ? 1 : 0; +} + + +/* Change priority of interrupt number 'irq' to 'pri' + * + * RETURNS: 0 on success, nonzero on failure (illegal args) + * + * NOTE: This routine must not be called from ISR level. + * + * Algorithm: Set bit corresponding to 'irq' in the masks for + * all priorities < pri and clear in all masks + * for priorities >=pri + */ +int +BSP_irq_set_priority(rtems_irq_number irq, rtems_irq_prio pri) +{ +unsigned long flags; +volatile uint32_t *p; +uint32_t v,m; +unsigned i,j; + + if ( thePic.current_priority > 0 ) { + printk("BSP_irq_set_priority: must not be called from ISR level\n"); + return -1; + } + + if ( !validPri(pri) ) { + printk("BSP_irq_set_priority: invalid argument (pri #%i)\n",pri); + return -1; + } + + if ( BSP_DECREMENTER != irq ) { + if ( !validIrqNo(irq) ) { + printk("BSP_irq_set_priority: invalid argument (irq #%i)\n",irq); + return -1; + } + + if ( (i=irqDiv32(irq)) > NUM_INTR_REGS ) { + printk("BSP_irq_set_priority: illegal argument (irq #%i not PCI?)\n", irq); + return -1; + } + } + +#if (IRQ_DEBUG) & IRQ_DEBUG_BASIC + printk("IRQ: Set Priority #%i -> %i;",irq,pri); +#endif + + if ( BSP_DECREMENTER == irq ) { + theConfig.irqPrioTbl[irq] = pri; + return 0; + } + + /* compute register pointer and bit mask */ + p = thePic.masks[i]; + m = 1<<irqMod32(irq); + + rtems_interrupt_disable(flags); + { + for (j=0; j<=BSP_IRQ_MAX_PRIO; j++) { + if ( j<pri ) + thePic.mcache[j][i] |= m; + else + thePic.mcache[j][i] &= ~m; + } + theConfig.irqPrioTbl[irq] = pri; + st_le32(p, (v=thePic.mcache[thePic.current_priority][i])); + /* linux driver reads back GPP mask; maybe it's wise to do the same */ + (void)ld_le32(thePic.masks[GPP_IDX]); + } + SYNC(); + rtems_interrupt_enable(flags); + +#if (IRQ_DEBUG) & IRQ_DEBUG_BASIC + printk(" Mask[%i]: 0x%08x -> 0x%08x\n",i,v,ld_le32(p)); +#endif + + return 0; +} + +/* Initialize the PIC; routine needed by BSP framework + * + * RETURNS: NONZERO on SUCCESS, 0 on error! + */ +int +BSP_setup_the_pic(rtems_irq_global_settings* config) +{ +int i; + /* + * Store copy of configuration + */ + theConfig = *config; + + /* check config */ + if ( theConfig.irqNb <= BSP_PCI_IRQ_MAX_OFFSET ) { + printk("BSP_setup_the_pic: FATAL ERROR: configured IRQ table too small???\n"); + return 0; + } + + for ( i=0; i<theConfig.irqNb; i++ ) { + if ( !validPri(theConfig.irqPrioTbl[i]) ) { + printk("BSP_setup_the_pic: invalid priority (%i) for irg #%i; setting to 1\n", theConfig.irqPrioTbl[i], i); + theConfig.irqPrioTbl[i]=1; + } + } + + /* TODO: Detect; Switch wired-out bit; */ + thePic.reg_base = BSP_MV64x60_BASE; + + thePic.current_priority = 0; + +#if (IRQ_DEBUG) & MVME6100_IRQ_DEBUG +#endif + + switch ( BSP_getDiscoveryVersion(/* assert */ 1) ) { + case MV_64360: + thePic.causes[MAIN_LO_IDX] = REGP(thePic.reg_base + ICR_360_MIC_LO); + thePic.causes[MAIN_HI_IDX] = REGP(thePic.reg_base + ICR_360_MIC_HI); + thePic.masks[MAIN_LO_IDX] = REGP(thePic.reg_base + ICR_360_C0IM_LO); + thePic.masks[MAIN_HI_IDX] = REGP(thePic.reg_base + ICR_360_C0IM_HI); + break; + + case GT_64260_A: + case GT_64260_B: + thePic.causes[MAIN_LO_IDX] = REGP(thePic.reg_base + ICR_260_MIC_LO); + thePic.causes[MAIN_HI_IDX] = REGP(thePic.reg_base + ICR_260_MIC_HI); + thePic.masks[MAIN_LO_IDX] = REGP(thePic.reg_base + ICR_260_CIM_LO); + thePic.masks[MAIN_HI_IDX] = REGP(thePic.reg_base + ICR_260_CIM_HI); + break; + + default: + BSP_panic("Unable to initialize interrupt controller; unknown chip"); + break; + } + + thePic.causes[GPP_IDX] = REGP(thePic.reg_base + GT_GPP_Interrupt_Cause); + thePic.masks[GPP_IDX] = REGP(thePic.reg_base + GT_GPP_Interrupt_Mask); + + /* Initialize mask cache */ + for ( i=0; i<=BSP_IRQ_MAX_PRIO; i++ ) { + thePic.mcache[i][MAIN_LO_IDX] = 0; + /* Always enable the summary bits. Otherwise, GPP interrupts dont + * make it 'through' to the GPP cause + */ + thePic.mcache[i][MAIN_HI_IDX] = IMH_GPP_SUM; + thePic.mcache[i][GPP_IDX] = 0; + } + + /* mask and clear everything */ + for ( i=0; i<NUM_INTR_REGS; i++ ) { + st_le32(thePic.causes[i], 0); + st_le32(thePic.masks[i], 0); + } + + /* make sure GPP Irqs are level sensitive */ + gt_bitmod( + GT_CommUnitArb_Ctrl, /* reg */ + GT_CommUnitArb_Ctrl_GPP_Ints_Level_Sensitive, /* set */ + 0); /* clr */ + + /* enable summaries */ + st_le32(thePic.masks[MAIN_LO_IDX], thePic.mcache[thePic.current_priority][MAIN_LO_IDX]); + st_le32(thePic.masks[MAIN_HI_IDX], thePic.mcache[thePic.current_priority][MAIN_HI_IDX]); + st_le32(thePic.masks[GPP_IDX ], thePic.mcache[thePic.current_priority][GPP_IDX ]); + + /* believe the interrupts are all level sensitive (which is good); we leave all the + * inputs configured they way the are by MotLoad... + */ + + /* Finally, enable all interrupts for which the configuration table has already + * a handler installed. + */ + for ( i=BSP_PCI_IRQ_LOWEST_OFFSET; i<=BSP_PCI_IRQ_MAX_OFFSET; i++ ) { + if ( theConfig.irqHdlTbl[i].hdl != theConfig.defaultEntry.hdl ) { + BSP_enable_irq_at_pic(i); + } + } + + return 1; +} + +int discovery_pic_max_loops = 0; + + +/* Change the priority level we're executing at and mask all interrupts of + * the same and lower priorities + * + * RETURNS old priority; + */ + +static inline rtems_irq_prio +change_executing_prio_level(rtems_irq_prio pri) +{ +register rtems_irq_prio rval = thePic.current_priority; + thePic.current_priority = pri; + st_le32(thePic.masks[MAIN_LO_IDX], thePic.mcache[pri][MAIN_LO_IDX]); + st_le32(thePic.masks[MAIN_HI_IDX], thePic.mcache[pri][MAIN_HI_IDX]); + st_le32(thePic.masks[GPP_IDX ], thePic.mcache[pri][GPP_IDX ]); + /* this DOES seem to be necessary */ + (void)ld_le32(thePic.masks[GPP_IDX]); + return rval; +} + +/* Scan the three cause register and find the pending interrupt with + * the highest priority. + * + * Two facts make this quite efficient + * a) the PPC has an opcode for finding the number of leading zero-bits + * in a register (__ilog2()). + * b) as we proceed we mask all sources of equal or lower priorites; they won't be + * seen while scanning: + * + * maxpri = 0; + * bits = in_le32(cause); + * while ( bits &= mask[maxpri] ) { + * irq_no = __ilog2(bits); + * maxpri = priority[irq_no]; + * } + * + * a) __ilog() is 1-2 machine instructions + * b) while loop is only executed as many times as interrupts of different + * priorities are pending at the same time (and only if lower-priority + * ones are found first; otherwise, the iteration terminates quicker). + * + * ==> highest priority source is found quickly. It takes at most + * + * BSP_IRQ_MAX_PRIO * ( ~3 reg-only instructions + 2 memory access ) + * + 2 reg-only instructions + 1 I/O + 1 memory access. + * + * + */ + +static unsigned mlc, mhc, gpc; + +static int decrementerPending = 0; +#if (IRQ_DEBUG) & IRQ_DEBUG_DISPATCHER +int decrementerIrqs = 0; +#endif + +static inline unsigned +find_highest_priority_pending_irq(rtems_irq_prio *ppri) +{ +register int rval = -1; +register rtems_irq_prio *pt = theConfig.irqPrioTbl + BSP_PCI_IRQ_LOWEST_OFFSET; +register rtems_irq_prio pmax = *ppri; +register unsigned cse,ocse; + +#if (IRQ_DEBUG) & IRQ_DEBUG_DISPATCHER + discovery_dump_picregs(); +#endif + + if ( decrementerPending ) { +/* Don't flood +#if (IRQ_DEBUG) & IRQ_DEBUG_DISPATCHER + printk("Decrementer IRQ pending\n"); +#endif +*/ + if ( theConfig.irqPrioTbl[BSP_DECREMENTER] > pmax ) { + pmax = theConfig.irqPrioTbl[BSP_DECREMENTER]; + rval = BSP_DECREMENTER; + } + } + + mlc = cse = ld_le32(thePic.causes[MAIN_LO_IDX]); +#if (IRQ_DEBUG) & IRQ_DEBUG_DISPATCHER + printk("MAIN_LO; cse: 0x%08x, msk 0x%08x\n", cse ,thePic.mcache[pmax][MAIN_LO_IDX]); +#endif + while ( cse &= thePic.mcache[pmax][MAIN_LO_IDX] ) { + rval = __ilog2(cse); + pmax = pt[rval]; +#if (IRQ_DEBUG) & IRQ_DEBUG_DISPATCHER + printk("Max pri IRQ now %i\n",rval); +#endif + } + mhc = cse = ocse = ld_le32(thePic.causes[MAIN_HI_IDX]); +#if (IRQ_DEBUG) & IRQ_DEBUG_DISPATCHER + printk("MAIN_HI; cse: 0x%08x, msk 0x%08x\n", cse, thePic.mcache[pmax][MAIN_HI_IDX]); +#endif + /* don't look at the GPP summary; only check for 'real' MAIN_HI sources */ + cse &= ~IMH_GPP_SUM; + while ( cse &= thePic.mcache[pmax][MAIN_HI_IDX] ) { + rval = __ilog2(cse) + 32; + pmax = pt[rval]; +#if (IRQ_DEBUG) & IRQ_DEBUG_DISPATCHER + printk("Max pri IRQ now %i\n",rval); +#endif + } + gpc = cse = ld_le32(thePic.causes[GPP_IDX ]); + /* if there were GPP ints, scan the GPP cause now */ + if ( ocse & IMH_GPP_SUM ) { +#if (IRQ_DEBUG) & IRQ_DEBUG_DISPATCHER + printk("GPP; cse: 0x%08x, msk 0x%08x\n", cse, thePic.mcache[pmax][GPP_IDX ]); +#endif + cse &= thePic.mcache[pmax][GPP_IDX ]; + ocse = cse; + while ( cse ) { + rval = __ilog2(cse) + 64; + pmax = pt[rval]; + cse &= thePic.mcache[pmax][GPP_IDX ]; +#if (IRQ_DEBUG) & IRQ_DEBUG_DISPATCHER + printk("Max pri IRQ now %i\n",rval); +#endif + } +#ifndef FASTER + /* this doesn't seem to be necessary -- however, the linux people do it... */ + out_le32(thePic.causes[GPP_IDX], ~ocse); +#endif + } +#ifndef FASTER + /* this doesn't seem to be necessary -- however, the linux people do it... */ + (void)in_le32(thePic.causes[GPP_IDX]); +#endif + + *ppri = pmax; + + if ( BSP_DECREMENTER == rval ) { +#if (IRQ_DEBUG) & IRQ_DEBUG_DISPATCHER + decrementerIrqs++; +#endif + decrementerPending = 0; + } + + return rval; +} + +#if 0 /* TODO: should this be cleaned up ? */ +#define _IRQ_DEBUG IRQ_DEBUG_DISPATCHER +static inline unsigned +ffind_highest_priority_pending_irq(rtems_irq_prio *ppri) +{ +register int rval = -1; +register rtems_irq_prio *pt = theConfig.irqPrioTbl + BSP_PCI_IRQ_LOWEST_OFFSET; +register rtems_irq_prio pmax = *ppri; +register unsigned cse,ocse; + +#if (_IRQ_DEBUG) & IRQ_DEBUG_DISPATCHER + discovery_dump_picregs(); +#endif + + cse = in_le32(thePic.causes[MAIN_LO_IDX]); +#if (_IRQ_DEBUG) & IRQ_DEBUG_DISPATCHER + printk("MAIN_LO; cse: 0x%08x, msk 0x%08x\n", cse ,thePic.mcache[pmax][MAIN_LO_IDX]); +#endif + while ( cse &= thePic.mcache[pmax][MAIN_LO_IDX] ) { + rval = __ilog2(cse); + pmax = pt[rval]; +#if (_IRQ_DEBUG) & IRQ_DEBUG_DISPATCHER + printk("Max pri IRQ now %i\n",rval); +#endif + } + cse = ocse = in_le32(thePic.causes[MAIN_HI_IDX]); +#if (_IRQ_DEBUG) & IRQ_DEBUG_DISPATCHER + printk("MAIN_HI; cse: 0x%08x, msk 0x%08x\n", cse, thePic.mcache[pmax][MAIN_HI_IDX]); +#endif + /* don't look at the GPP summary; only check for 'real' MAIN_HI sources */ + cse &= ~IMH_GPP_SUM; + while ( cse &= thePic.mcache[pmax][MAIN_HI_IDX] ) { + rval = __ilog2(cse) + 32; + pmax = pt[rval]; +#if (_IRQ_DEBUG) & IRQ_DEBUG_DISPATCHER + printk("Max pri IRQ now %i\n",rval); +#endif + } + /* if there were GPP ints, scan the GPP cause now */ + if ( ocse & IMH_GPP_SUM ) { + cse = in_le32(thePic.causes[GPP_IDX ]); +#if (_IRQ_DEBUG) & IRQ_DEBUG_DISPATCHER + printk("GPP; cse: 0x%08x, msk 0x%08x\n", cse, thePic.mcache[pmax][GPP_IDX ]); +#endif + cse &= thePic.mcache[pmax][GPP_IDX ]; + ocse = cse; + while ( cse ) { + rval = __ilog2(cse) + 64; + pmax = pt[rval]; + cse &= thePic.mcache[pmax][GPP_IDX ]; +#if (_IRQ_DEBUG) & IRQ_DEBUG_DISPATCHER + printk("Max pri IRQ now %i\n",rval); +#endif + } + /* this doesn't seem to be necessary -- however, the linux people do it... */ + out_le32(thePic.causes[GPP_IDX], ~ocse); + } + /* this doesn't seem to be necessary -- however, the linux people do it... */ + (void)in_le32(thePic.causes[GPP_IDX]); + + *ppri = pmax; + return rval; +} +#endif + + +/* Here's our dispatcher; the BSP framework uses the same one for EE and decrementer + * exceptions... + */ +int C_dispatch_irq_handler (BSP_Exception_frame *frame, unsigned int excNum) +{ +register int irq; +int loop, last_irq; +rtems_irq_prio pri; +#if (IRQ_DEBUG) & IRQ_DEBUG_MAXLAT +unsigned long diff; +#endif + +#if (IRQ_DEBUG) & IRQ_DEBUG_MAXLAT + diff = mftb(); +#endif + + if (excNum == ASM_DEC_VECTOR) { + decrementerPending = 1; + } + + /* Tradeoff: EITHER we loop as long as interrupts are pending + * incurring the overhead of one extra run of the 'find_pending_irq' routine. + * OR we do rely on the handler just being invoked again if multiple + * interrupts are pending. + * + * The first solution gives better worst-case behavior + * the second slightly better average performance. + * --> we go for the first solution. This also enables us to catch + * runaway interrupts, i.e., bad drivers that don't clear interrupts + * at the device. Can be very handy during driver development... + */ + for ( loop=0, last_irq=-1, pri = thePic.current_priority; + (irq=find_highest_priority_pending_irq(&pri)) >=0; + loop++, last_irq = irq ) { + + /* raise priority level and remember current one */ + pri = change_executing_prio_level(pri); + + SYNC(); + +#if (IRQ_DEBUG) & IRQ_DEBUG_MAXLAT + if ( 0 == loop ) { + diff = mftb()-diff; + if ( diff > discovery_pic_max_dispatching_latency ) + discovery_pic_max_dispatching_latency = diff; + } +#endif + +#if (IRQ_DEBUG) & IRQ_DEBUG_DISPATCHER + if ( BSP_DECREMENTER == irq ) { + printk("IRQ: dispatching DECREMENTER\n"); + } else { + int idx = irqDiv32(irq); + printk("IRQ: dispatching #%i; causes[%i]=0x%08x\n", irq, idx, ld_le32(thePic.causes[idx])); + } +#endif + + bsp_irq_dispatch_list( theConfig.irqHdlTbl, irq, theConfig.defaultEntry.hdl ); + + /* restore executing priority level */ + (void)change_executing_prio_level(pri); + + if ( (loop > MAX_SPIN_LOOPS) && (last_irq == irq) ) { + /* try to catch run-away interrupts without disabling a 'legal' one; + * this should never happen with the decrementer (and + * BSP_disable_irq_at_pic(BSP_DECREMENTER) would fail) + */ + printk("Runaway IRQ #%i; disabling\n", irq); + BSP_disable_irq_at_pic(irq); + loop = 0; + } + } + + if (!loop) { + if ( decrementerPending && pri >= theConfig.irqPrioTbl[BSP_DECREMENTER] ) { + /* we cannot mask the decrementer interrupt so it is possible that it + * gets delivered even though it has a lower priority than what we're + * currently executing at. + * In this case, we ignore the zero loop count and return; + * the interrupted instance of C_dispatch_irq_handler() will eventually + * lower the executing priority and catch the 'decrementerPending' flag + * we just set. + */ + } else { + printk("Discovery: Spurious interrupt; causes were gpp: 0x%x, mhc: 0x%x, mlc: 0x%x\n", gpc, mhc, mlc); + printk("Current priority level %i, decrementerPending %i\n", pri, decrementerPending); + { + rtems_irq_prio p=pri; + printk("PIC register dump:\n"); + discovery_dump_picregs(); + printk("Current Priority: %i, found %i\n",pri,find_highest_priority_pending_irq(&p)); + discovery_dump_picregs(); + for (p=0; p<=BSP_IRQ_MAX_PRIO; p++) { + printk("M[%i] :",p);pmsks(thePic.mcache[p]); + } + } + } + } + else if (loop>discovery_pic_max_loops) + discovery_pic_max_loops = loop; + + return 0; +} + + +#if (IRQ_DEBUG) & MVME6100_IRQ_DEBUG +void +discovery_pic_install_debug_irq(void) +{ + switch ( BSP_getBoardType() ) { + case MVME6100: gpp_out_bit = GPP_WIRED_OUT_BIT_6100; break; + case MVME5500: gpp_out_bit = GPP_WIRED_OUT_BIT_5500; break; + default: + gpp_out_bit = 0; break; + break; + } + if ( gpp_out_bit ) { + unsigned mppoff; + switch (gpp_out_bit / 8) { + default: /* silence warning; this is never reached */ + case 0: mppoff = GT_MPP_Control0; break; + case 1: mppoff = GT_MPP_Control1; break; + case 2: mppoff = GT_MPP_Control2; break; + case 3: mppoff = GT_MPP_Control3; break; + } + + /* switch GPP pin allocated to watchdog (value 4) to + * GPP I/O (value 0 ??; have no doc, found out by experimenting) + */ + gt_bitmod(mppoff, 0, (0xf<<(4*(gpp_out_bit % 8)))); + + /* make it an output */ + gt_bitmod(GT_GPP_IO_Control, (1<<gpp_out_bit), 0); + + /* don't invert levels */ + gt_bitmod(GT_GPP_Level_Control, 0, (1<<GPP_WIRED_IN_BIT) | (1<<gpp_out_bit)); + + /* clear output */ + gt_bitmod(GT_GPP_Value, 0, 1<<gpp_out_bit); + + printk("GPP levelctl now 0x%08x\n", gt_read(GT_GPP_Level_Control)); + printk("GPP value now 0x%08x\n", gt_read(GT_GPP_Value)); + printk("MPP ctl 0 now 0x%08x\n", gt_read(GT_MPP_Control0)); + printk("MPP ctl 1 now 0x%08x\n", gt_read(GT_MPP_Control1)); + printk("MPP ctl 2 now 0x%08x\n", gt_read(GT_MPP_Control2)); + printk("MPP ctl 3 now 0x%08x\n", gt_read(GT_MPP_Control3)); + + } +} + +/* Control the state of the external 'wire' that connects the + * GPP_WIRED_OUT --> GPP_WIRED_IN pins + */ +void +discovery_pic_set_debug_irq(int on) +{ +unsigned long flags, clr; + if ( !gpp_out_bit ) { + printk("discovery_pic_set_debug_irq(): unknown wire output\n"); + return; + } + if (on) { + on = 1<<gpp_out_bit; + clr = 0; + } else { + clr = 1<<gpp_out_bit; + on = 0; + } + rtems_interrupt_disable(flags); + gt_bitmod(GT_GPP_Value, on, clr); + rtems_interrupt_enable(flags); +} +#endif + +#if 0 +/* Here's some code for testing */ +#endif |