/* 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