/*************************************************************************** * Copyright (c) 2003-2011, Broadcom Corporation * All Rights Reserved * Confidential Property of Broadcom Corporation * * THIS SOFTWARE MAY ONLY BE USED SUBJECT TO AN EXECUTED SOFTWARE LICENSE * AGREEMENT BETWEEN THE USER AND BROADCOM. YOU HAVE NO RIGHT TO USE OR * EXPLOIT THIS MATERIAL EXCEPT SUBJECT TO THE TERMS OF SUCH AN AGREEMENT. * * $brcm_Workfile: bint.c $ * $brcm_Revision: Hydra_Software_Devel/66 $ * $brcm_Date: 3/1/11 12:27p $ * * Module Description: * * Revision History: * * $brcm_Log: /magnum/basemodules/int/bint.c $ * * Hydra_Software_Devel/66 3/1/11 12:27p jtna * SW7420-972: add comment about needing BCHP_PWR around BINT_Open * * Hydra_Software_Devel/65 2/7/11 11:06a erickson * SW7340-249: clarify need for re-enabling L2's in BINT_Isr * * Hydra_Software_Devel/64 8/9/10 9:49a pntruong * SW3548-2526: Tighten the use of _isr function. * * Hydra_Software_Devel/63 8/2/10 1:33p erickson * SW7420-880: use BDBG_OBJECT to protect INT. do not BDBG_OBJECT_ASSERT * in BINT_Isr because it's a critical inner loop. * * Hydra_Software_Devel/62 10/15/09 12:50p erickson * SW7405-3221: add BDBG_MSG for callback->StatInfo if available. clear * stats on every timer. default off. * * Hydra_Software_Devel/61 7/24/09 6:11p pntruong * PR55861: Further refactored the new int macro. * * Hydra_Software_Devel/61 7/24/09 6:09p pntruong * PR55861: Further refactored the new int macro. * * Hydra_Software_Devel/61 7/24/09 6:05p pntruong * PR55861: Further refactored the new int macro to ease porting of new * chips. * * Hydra_Software_Devel/60 7/24/09 2:24p mward * PR55545: Add 7125 to BINT_NEW_INT_MODEL list. * * Hydra_Software_Devel/59 7/10/09 4:40p erickson * PR56517: add #ifndef BINT_OPEN_BYPASS_L2INIT option for power standby * driver * * Hydra_Software_Devel/58 4/23/09 10:56a jhaberf * PR53796: Adding BCM35130 support. * * Hydra_Software_Devel/57 4/8/09 4:23p vsilyaev * PR 54015: Don't include bkni_multi.h into the bdbg.h. All thread-aware * modules should include explicitly bkni_multi.h * * Hydra_Software_Devel/56 1/31/09 1:55a jrubio * PR51629: add 7336 support * * Hydra_Software_Devel/55 12/3/08 9:25p nickh * PR49691: Enable interrupt support for 7420 for new user mode driver * * Hydra_Software_Devel/54 9/29/08 4:54p rpan * PR47411: Enable interrupt support for 3548/3556 for new user mode * driver. * * Hydra_Software_Devel/53 9/2/08 10:46a erickson * PR46415: added goto so we don't skip BINT_UNLOCK * * Hydra_Software_Devel/52 7/30/08 4:04p vishk * PR45177: uintptr_t is now defined in linux 2.6.18-5.1 header files * * Hydra_Software_Devel/51 4/25/08 10:27a vsilyaev * PR 41896: Added support to reentrant configuration * * Hydra_Software_Devel/PR41896/1 4/17/08 6:49p vsilyaev * PR 41896: Added support to reentrant configuration * * Hydra_Software_Devel/50 11/28/07 11:47a katrep * PR37430: fixed compiler error. * * Hydra_Software_Devel/49 11/28/07 10:58a katrep * PR37430: Extended interrupt interface to 128 bits for 7405,7325,7335 * * Hydra_Software_Devel/48 7/30/07 1:45p vsilyaev * PR 33617: Added BINT_P_CreateCallback_Tag symbol for release build * * Hydra_Software_Devel/47 7/24/07 10:41a jgarrett * PR 33363: Revising check for missing interrupt * * Hydra_Software_Devel/46 2/15/07 12:01p erickson * PR26657: optimized BINT_Isr. added BINT_IS_STANDARD to allow standard * interrupts to be processed inside bint.c. * * Hydra_Software_Devel/45 2/9/07 5:23p albertl * PR24115: Added warning messages for long executing interrupts and * runaway interrupts with adjustable compile time thresholds. * * Hydra_Software_Devel/43 5/25/06 4:13p albertl * PR21392: BINT_Stats functions now split off into bint_stats.h to solve * BTMR circular dependency. * * Hydra_Software_Devel/42 5/24/06 6:56p albertl * PR21392: Changed BINT stats tracking to use timers from TMR module. * Added BINT_Stats_Enable and BINT_Stats_Disable. * * Hydra_Software_Devel/41 2/15/06 5:30p vsilyaev * PR 19693: Added support for acquiring interrupt rate * * Hydra_Software_Devel/40 6/30/05 4:36p hongtaoz * PR15921: keep L2 handle's enable count consistent with number of * enabled callbacks; * * Hydra_Software_Devel/39 4/13/05 5:49p albertl * PR10596: Added parenthesis to conditionals in BINT_Stats_AddBin. * * Hydra_Software_Devel/38 4/13/05 5:16p albertl * PR10596: Fixed error checking. Moved bDefaultBins to * BINT_Stats_CallbackStats. * * Hydra_Software_Devel/37 4/11/05 5:30p albertl * PR10596: Added BINT_Stats_DestroyBins and BINT_Stats_Reset. * Implemented default bin configuration. * * Hydra_Software_Devel/36 4/7/05 4:17p albertl * PR10596: Changed BINT_STATS_TRACK to BINT_STATS_ENABLE. Added warning * message for calling BINT_Stats_Get without enabling stats tracking. * * Hydra_Software_Devel/35 4/6/05 2:29p albertl * PR10596: pReadTimer and pGetElapsedTime function pointers now checked * before executing. Removed unnecessary #ifdef BINT_STATS_TRACK cases * for better readability. * * Hydra_Software_Devel/34 4/5/05 7:12p albertl * PR10596: Added new statistics tracking functionality. * * Hydra_Software_Devel/33 9/2/04 1:29p marcusk * PR12445: Check usage count before unmasking an interrupt. * * Hydra_Software_Devel/32 8/31/04 12:17p marcusk * PR12445: Updated to unmask interrupts required to support shared L1 * between GFX and Softmodem * * Hydra_Software_Devel/31 8/11/04 1:57p marcusk * PR12255: UPdated to allow BINT_ClearCallback() to always clear * callback. * * Hydra_Software_Devel/30 8/9/04 5:12p marcusk * PR12233: UPdated so that disabling a callback that is already disabled * does nothing. * * Hydra_Software_Devel/29 5/24/04 3:01p marcusk * PR10666: Mergedown from B0 * * Hydra_Software_Devel/Refsw_Devel_7038_B0/2 4/19/04 11:52a marcusk * PR10666: No longer clear previous interrupts when enabling a callback. * * Hydra_Software_Devel/28 4/16/04 5:12p marcusk * PR10666: Partially merged functionality from B0 specific branch. Full * merge will occur once B0 is released. * * Hydra_Software_Devel/Refsw_Devel_7038_B0/2 4/16/04 4:55p marcusk * PR10666: Do not clear interrupts when enabling and added * BINT_ClearCallback() routines. * * Hydra_Software_Devel/27 4/2/04 12:12p marcusk * PR10462: Removed while loop when processing interrupts. * * Hydra_Software_Devel/26 3/5/04 10:41a marcusk * PR9994: Updated to check both interrupt register and offset when * creating a callback. * * Hydra_Software_Devel/25 2/3/04 8:38p vsilyaev * PR 9606: Clear all managed L2 interrupts. * * Hydra_Software_Devel/24 1/9/04 12:00p marcusk * PR9241: Updated to insert on tail rather than on head. * * Hydra_Software_Devel/23 1/6/04 9:45a marcusk * PR9117: Properly look up interrupt ignore mask during isr() routine. * * Hydra_Software_Devel/22 1/5/04 4:26p marcusk * PR9117: Updated to support PI provided L2 interrupt handler (for * transport message and overflow interrupts). Updated documentation. * * Hydra_Software_Devel/21 12/30/03 12:02p dlwin * PR 9117: Fixed a problem with creating Callback for UPG interrupts. * * Hydra_Software_Devel/20 12/29/03 4:16p marcusk * PR9117: Updated with changes required to support interrupt ids rather * than strings. * * Hydra_Software_Devel/19 12/23/03 11:13a marcusk * PR8985: No longer assert since this causes problems with the UPG * interrupts. * * Hydra_Software_Devel/18 12/18/03 2:55p marcusk * PR8985: Updated to assert if somebody is messing with the interrupt * mask. * * Hydra_Software_Devel/17 12/18/03 2:37p marcusk * PR8985: Removed bint_priv.h since it is no longer needed. * * Hydra_Software_Devel/16 12/18/03 2:08p marcusk * PR8985: Refactored to use single ISR() routine. Removed reserved names. * Placed all platform specific defines in bint_plat.h * * Hydra_Software_Devel/15 12/1/03 4:51p jasonh * Added interrupt clear when callback is enabled. * * Hydra_Software_Devel/14 11/24/03 3:27p marcusk * PR 8719: Clear and mask all managed interrupts at open time. * * Hydra_Software_Devel/13 9/16/03 10:30a marcusk * Updated to comply with DocJet requirements. Fixes for PR8055. * * Hydra_Software_Devel/12 8/26/03 10:43a marcusk * Removed default settings (they are not valid) * * Hydra_Software_Devel/11 8/22/03 3:00p erickson * added BINT_GetDefaultSettings * * Hydra_Software_Devel/10 6/18/03 3:26p dlwin * Added support to allow for more general implementation of Interrupt * manager. * * Hydra_Software_Devel/9 4/2/03 10:38a marcusk * Updated to support flag to specify if the interrupt can be triggered by * the CPU. * * Hydra_Software_Devel/7 3/31/03 4:07p marcusk * Detect interrupts that get enabled outside the interrupt interface * module. * * Hydra_Software_Devel/6 3/31/03 8:57a marcusk * Updated out of memory error returns. * * Hydra_Software_Devel/5 3/31/03 8:45a marcusk * Fixed a small bug when all 32 bits of an L2 interrupt register is used. * * Hydra_Software_Devel/4 3/28/03 10:22a marcusk * Fixed many bugs. Updated to trace when returning errors. Added support * for unit test environment (without hardware). * * Hydra_Software_Devel/3 3/25/03 4:08p marcusk * Updated some comments. * * Hydra_Software_Devel/2 3/21/03 6:29p marcusk * Initial version (that compiles). * * Hydra_Software_Devel/1 3/21/03 5:51p marcusk * In development. * ***************************************************************************/ #include "bstd.h" #include "bint_plat.h" /* include other interrupt interface headers */ #include "bint_stats.h" #include "bkni.h" #include "bkni_multi.h" #include "blst_squeue.h" #include "bchp.h" #include "btmr.h" BDBG_MODULE(int); #define BINT_REENTRANT 1 #define BINT_NON_REENTRANT 0 #ifndef BINT_REENTRANT_CONFIG #define BINT_REENTRANT_CONFIG BINT_REENTRANT #endif #define BINT_P_ComputeIndex( L1Shift, Hash ) (Hash[L1Shift]) /* Dummy typedef to alloc circular dependencies between structures */ typedef struct BINT_P_Callback *BINT_P_CallbackHandle; /* Summary: This structure defines the head element for creating a linked list. */ typedef struct BINT_P_cblHead BINT_P_cblHead; BLST_SQ_HEAD(BINT_P_cblHead, BINT_P_Callback); /* Summary: This strcuture defines a single element in the hash table used in the InterruptInterface. Description: One and only instance of this structure will exist for each L2 interrupt bit. */ typedef struct BINT_P_L2Int { BLST_SQ_ENTRY(BINT_P_L2Int) link; /* doubly-linked list support */ BINT_Handle intHandle; /* handle to the main InterruptInterface */ int intMapIndex; /* Index into the interrupt map */ BINT_Id intId; /* Interrupt ID (contains L2 base and L2 shift) */ int enableCount; /* Number of callbacks that are enabled for this interrupt */ BINT_P_cblHead callbackList; /* list of callbacks associated with this interrupt */ unsigned count; /* number of times when L2 interrupt was fired */ } BINT_P_L2Int, *BINT_P_L2Handle; /* Summary: This structure defines the head element for creating a linked list. */ typedef struct BINT_P_L2Head BINT_P_L2Head; BLST_SQ_HEAD(BINT_P_L2Head, BINT_P_L2Int); /* Summary: This structure defines the a callback handle/context. It includes doubly linked list support. Description: One of these structures exists for each callback created. There may be multiple callbacks assigned for a single interrupt. */ typedef struct BINT_P_Callback { BLST_SQ_ENTRY(BINT_P_Callback) link; /* doubly-linked list support */ BLST_SQ_ENTRY(BINT_P_Callback) allCbLink; /* links all callbacks created for traversing */ BINT_P_L2Handle L2Handle; /* L2 handle for this interrupt */ BINT_CallbackFunc func; /* function to call when the interrupt triggers */ void * pParm1; /* returned when callback is executed */ int parm2; /* returned when callback is executed */ bool enabled; /* false: callback will never be executed, true: callback will be executed when interrupt triggers */ uint32_t ulStartTime; unsigned count; /* number of times when callback interrupt was fired */ const char *callbackName; /* callback name saved for the debug builds */ BINT_Stats_CallbackStats StatInfo; } BINT_P_Callback; BDBG_OBJECT_ID(BINT); typedef struct BINT_P_Context { BDBG_OBJECT(BINT) #if BINT_REENTRANT_CONFIG==BINT_REENTRANT BKNI_MutexHandle lock; #define BINT_LOCK(h) BKNI_AcquireMutex((h)->lock) #define BINT_UNLOCK(h) BKNI_ReleaseMutex((h)->lock) #else #define BINT_LOCK(h) #define BINT_UNLOCK(h) #endif BINT_P_L2Head IntArray[BINT_P_L1_SIZE]; /* Array of L2 interrupt lists (one list per L1 interrupt). NULL if not used. */ const BINT_P_IntMap *pIntMap; /* ptr to the interrupt map, REQUIRED */ int callbackCount; /* Number of callbacks installed in InterruptInterface */ unsigned numInts; /* Number of L2 interrupts managed by this instance of the InterruptInterface */ BREG_Handle regHandle; /* regHandle for accessing interrupt registers */ BINT_SetIntFunc pSetInt; /* ptr to Set Interrupt, NULL if none */ BINT_ClearIntFunc pClearInt; /* ptr to Clear Interrupt, NULL if none */ BINT_SetMaskFunc pSetMask; /* ptr to Set Interrupt Mask, REQUIRED */ BINT_ClearMaskFunc pClearMask; /* ptr to Clear Interrupt Mask, REQUIRED */ BINT_ReadStatusFunc pReadStatus; /* ptr to Read Status, REQUIRED */ BINT_ReadMaskFunc pReadMask; /* ptr to Read Mask, REQUIRED */ BINT_P_cblHead allCbList; /* list of all callbacks registered */ BINT_Settings settings; /* BINT_Settings */ BTMR_TimerHandle hTimer; /* timer used for stats tracking */ bool bStatsEnable; /* enables stats tracking */ } BINT_P_Context; /* Default bin configuration */ static const BINT_Stats_CallbackBin g_aDefaultBins[] = { /* range min, range max, bin hit count */ { 0, 50, 0 }, { 51, 100, 0 }, { 101, 200, 0 }, { 201, 500, 0 }, { 501, 1000, 0 }, { 1000, 2500, 0 }, { 2501, 10000, 0 }, { 10001, 50000, 0 }, { 50001, 100000, 0 }, { 100001, 500000, 0 }, }; #define BINT_P_STATS_DEFAULT_BINS_NUM \ (sizeof (g_aDefaultBins) / sizeof (BINT_Stats_CallbackBin)) BERR_Code BINT_P_Stats_ComputeStats( BINT_CallbackHandle cbHandle, uint32_t ulStart, uint32_t ulEnd ); uint32_t BINT_P_GetElapsedTime( uint32_t ulTimerStart, uint32_t ulTimerEnd ); BERR_Code BINT_Open( BINT_Handle *pHandle, BREG_Handle regHandle, const BINT_Settings *pDefSettings ) { int i; BERR_Code rc = BERR_SUCCESS; *pHandle = (BINT_Handle) BKNI_Malloc( sizeof( BINT_P_Context ) ); if( *pHandle == NULL ) { rc = BERR_TRACE(BERR_OUT_OF_SYSTEM_MEMORY); goto error; } BKNI_Memset( *pHandle, 0, sizeof( BINT_P_Context ) ); BDBG_OBJECT_SET(*pHandle, BINT); #if BINT_REENTRANT_CONFIG==BINT_REENTRANT rc = BKNI_CreateMutex(&(*pHandle)->lock); if(rc!=BERR_SUCCESS) { rc=BERR_TRACE(rc); goto error; } #endif (*pHandle)->numInts = 0; (*pHandle)->callbackCount = 0; (*pHandle)->pIntMap = pDefSettings->pIntMap; (*pHandle)->pSetInt = pDefSettings->pSetInt; (*pHandle)->pClearInt = pDefSettings->pClearInt; (*pHandle)->pSetMask = pDefSettings->pSetMask; (*pHandle)->pClearMask = pDefSettings->pClearMask; (*pHandle)->pReadStatus = pDefSettings->pReadStatus; (*pHandle)->pReadMask = pDefSettings->pReadMask; (*pHandle)->settings = *pDefSettings; (*pHandle)->hTimer = NULL; (*pHandle)->bStatsEnable = false; (*pHandle)->regHandle = regHandle; if( (*pHandle)->pReadMask == NULL || (*pHandle)->pReadStatus == NULL || (*pHandle)->pSetMask == NULL || (*pHandle)->pClearMask == NULL ) { BDBG_ERR(("Invalid function points passed into BINT_Open()")); rc = BERR_TRACE(BERR_INVALID_PARAMETER); BDBG_ASSERT(0); goto error; } for( i=0; iIntArray[i]) ); } /* If your platform has BCHP_PWR-based power management, then the SW layer that is calling BINT_Open must also acquire/release power around BINT_Open. Otherwise, you'll likely hit GISB errors below, when registers are accessed without power to their respective 108M clocks. See /rockford/appframework/src/common/appframework/framework.c or nexus_core.c for examples on how to do this */ #ifndef BINT_OPEN_BYPASS_L2INIT /* Clear all L2 interrupts */ for( i=0;;i++) { unsigned bit; const BINT_P_IntMap *L2Register = &(*pHandle)->pIntMap[i]; if (L2Register->L1Shift<0) { break; } for(bit=0; bit<32; bit++) { if ( (L2Register->L2InvalidMask&(1<pSetMask((*pHandle)->regHandle, L2Register->L2RegOffset, bit); } } } #endif return rc; error: if( (*pHandle) != NULL ) { #if BINT_REENTRANT_CONFIG==BINT_REENTRANT if((*pHandle)->lock) { BKNI_DestroyMutex((*pHandle)->lock); } #endif BKNI_Free( (*pHandle) ); } return rc; } BERR_Code BINT_Close( BINT_Handle intHandle ) { int L1Shift; BINT_P_L2Handle L2Handle; BINT_CallbackHandle cbHandle; BDBG_OBJECT_ASSERT(intHandle, BINT); BDBG_ASSERT ( intHandle->bStatsEnable == false ); for(L1Shift=0; L1ShiftIntArray[L1Shift])); L2Handle ; L2Handle=BLST_SQ_FIRST(&(intHandle->IntArray[L1Shift]))) { for(cbHandle=BLST_SQ_FIRST(&(L2Handle->callbackList)); cbHandle ; cbHandle=BLST_SQ_FIRST(&(L2Handle->callbackList))) { BINT_DestroyCallback(cbHandle); } BKNI_EnterCriticalSection(); BLST_SQ_REMOVE_HEAD(&(intHandle->IntArray[L1Shift]), link); BKNI_LeaveCriticalSection(); BKNI_Free( L2Handle ); } } if( intHandle->callbackCount != 0 ) return BERR_UNKNOWN; #if BINT_REENTRANT_CONFIG==BINT_REENTRANT BKNI_DestroyMutex(intHandle->lock); #endif BDBG_OBJECT_DESTROY(intHandle, BINT); BKNI_Free( intHandle ); return BERR_SUCCESS; } /** BINT_Isr is the main inner loop of the entire refsw architecture. Optimization of every line of code matters greatly. **/ void BINT_Isr( BINT_Handle intHandle, int L1Shift ) { BINT_P_Callback *cbHandle; uint32_t intStatus = 0; BINT_P_L2Handle L2Handle; /* Optimization: Dereference this pointers once */ BINT_ClearIntFunc pClearInt = intHandle->pClearInt; BINT_ClearMaskFunc pClearMask = intHandle->pClearMask; BINT_ReadStatusFunc pReadStatus = intHandle->pReadStatus; BREG_Handle regHandle = intHandle->regHandle; #ifdef BINT_STATS_ENABLE bool bStatsEnable = intHandle->bStatsEnable; #endif /* Optimization: The IntArray is sorted by L2Reg. Therefore we can remember the value of the previous status read. If we have the same L2Reg, avoid the extra read. */ uint32_t prevStatusReg = 0; #if 0 /* for performance, this ASSERT is compiled out. you can temporarily enable for debug. */ BDBG_OBJECT_ASSERT(intHandle, BINT); #endif for( L2Handle=BLST_SQ_FIRST(&(intHandle->IntArray[L1Shift])) ; L2Handle ; L2Handle=BLST_SQ_NEXT(L2Handle, link)) { /* Optimization: Dereference once */ int intId = L2Handle->intId; uint32_t L2BaseRegister = BCHP_INT_ID_GET_REG( intId ); uint32_t L2Shift = BCHP_INT_ID_GET_SHIFT( intId ); const BINT_P_IntMap *L2Register = &intHandle->pIntMap[L2Handle->intMapIndex]; /* DumpInfo accounting */ L2Handle->count++; /* BINT_IS_STANDARD assumes these offsets from L1BaseRegister are correct. */ #define BINT_P_STD_STATUS 0x00 #define BINT_P_STD_CLEAR 0x08 #define BINT_P_STD_MASK_CLEAR 0x14 /* Standard registers can be handled internal to bint.c which results in dramatic performance improvement. Each chip must set BINT_IS_STANDARD as appropriate. */ if (L2Register->L1Shift & BINT_IS_STANDARD) { if (L2BaseRegister != prevStatusReg) { intStatus = BREG_Read32_isr(regHandle, L2BaseRegister); /* read status */ prevStatusReg = L2BaseRegister; } /* find any interrupts that are triggered and enabled */ if( (intStatus & (1ul<enableCount ) { BREG_Write32_isr(regHandle, L2BaseRegister + BINT_P_STD_CLEAR, 1<callbackList)); cbHandle ; cbHandle=BLST_SQ_NEXT(cbHandle, link)) { if( cbHandle->enabled ) { (*cbHandle->func)( cbHandle->pParm1, cbHandle->parm2 ); cbHandle->count++; } } if( L2Handle->enableCount ) { /* Shared L1 interrupts require that the L2 be masked in bcmdriver.ko, so BINT unmasks here to reverse that. For unshared L1's, this is harmless. */ BREG_Write32_isr(regHandle, L2BaseRegister + BINT_P_STD_MASK_CLEAR, 1<L2InvalidMask==BINT_DONT_PROCESS_L2 ) { /* If the L2InvalidMask is BINT_DONT_PROCESS_L2 that means that the interrupt interface does not process the L2 interrupts for this L1 shift. Instead a separate L2 isr routine should be installed as the callback for the specified L1 shift that will handle this interrupt. */ cbHandle=BLST_SQ_FIRST(&(L2Handle->callbackList)); (*cbHandle->func)( cbHandle->pParm1, cbHandle->parm2 ); } else { /* Read the status and mask each time we obtain a new L2Handle. This is to handle L1 interrupts that map to multiple L2 interrupt registers (the L2 register offset is stored in the L2 handle). */ #if 0 /* PR 27936 - this is optimal code, but is causes problems with I2C (aka BSC). This is likely a bug in the I2C PI. */ if (L2BaseRegister != prevStatusReg) { intStatus = (*pReadStatus)( regHandle, L2BaseRegister); prevStatusReg = L2BaseRegister; } #else intStatus = (*pReadStatus)( regHandle, L2BaseRegister); #endif /* find any interrupts that are triggered and enabled */ if( (intStatus & (1ul<enableCount ) { /* Since L2 interrupts are edge triggered they must be cleared before processing!! */ if(pClearInt) { (*pClearInt)( regHandle, L2BaseRegister, L2Shift); } /* Call all callbacks that are enabled */ for(cbHandle=BLST_SQ_FIRST(&(L2Handle->callbackList)); cbHandle ; cbHandle=BLST_SQ_NEXT(cbHandle, link)) { if( cbHandle->enabled ) { #ifdef BINT_STATS_ENABLE uint32_t ulStart, ulEnd = 0; BERR_Code rc; if (bStatsEnable) { rc = BTMR_ReadTimer_isr(intHandle->hTimer, &ulStart); if (rc != BERR_SUCCESS) { BDBG_WRN(("Error reading timer for statistics.")); } } (*cbHandle->func)( cbHandle->pParm1, cbHandle->parm2 ); if (bStatsEnable) { rc = BTMR_ReadTimer_isr(intHandle->hTimer, &ulEnd); if (rc != BERR_SUCCESS) { BDBG_WRN(("Error reading timer for statistics.")); } BINT_P_Stats_ComputeStats( cbHandle, ulStart, ulEnd ); } #else (*cbHandle->func)( cbHandle->pParm1, cbHandle->parm2 ); #endif /* BINT_STATS_ENABLE */ cbHandle->count++; } } if( L2Handle->enableCount ) { /* Shared L1 interrupts require that the L2 be masked in bcmdriver.ko, so BINT unmasks here to reverse that. For unshared L1's, this is harmless. */ (*pClearMask)( regHandle, L2BaseRegister, L2Shift); } } } } } } #if BDBG_DEBUG_BUILD #undef BINT_CreateCallback BERR_Code BINT_CreateCallback( BINT_CallbackHandle *pCbHandle, BINT_Handle intHandle, BINT_Id intId, BINT_CallbackFunc func, void * pParm1, int parm2 ) { BDBG_WRN(("BINT_CallbackFunc shall be never called in the debug builds")); return BINT_P_CreateCallback_Tag(pCbHandle, intHandle, intId, func, pParm1, parm2, ""); } BERR_Code BINT_P_CreateCallback_Tag( BINT_CallbackHandle *pCbHandle, BINT_Handle intHandle, BINT_Id intId, BINT_CallbackFunc func, void * pParm1, int parm2, const char *callbackName) #else BERR_Code BINT_P_CreateCallback_Tag( BINT_CallbackHandle *pCbHandle, BINT_Handle intHandle, BINT_Id intId, BINT_CallbackFunc func, void * pParm1, int parm2) { return BINT_CreateCallback(pCbHandle, intHandle, intId, func, pParm1, parm2); } BERR_Code BINT_CreateCallback( BINT_CallbackHandle *pCbHandle, BINT_Handle intHandle, BINT_Id intId, BINT_CallbackFunc func, void * pParm1, int parm2 ) #endif { uint32_t L2Reg = BCHP_INT_ID_GET_REG(intId); uint32_t L2Shift = BCHP_INT_ID_GET_SHIFT(intId); int intMapIndex, L1Shift; BINT_P_L2Handle L2Handle; BINT_P_L2Handle SameL2RegHandle = NULL; BERR_Code rc; BDBG_OBJECT_ASSERT(intHandle, BINT); BINT_LOCK(intHandle); /* We must find the L1 interrupt associated with this L2 interrupt by looking through our interrupt map */ for( intMapIndex=0; intHandle->pIntMap[intMapIndex].L1Shift != -1; intMapIndex++ ) { /* We must find the matching L2 register offset and ensure that the specified L2 interrupt is actually handled by the specified L1->L2 mapping. This is because some wacky L2 interrupt registers actually map to multiple L1 interrupts (i.e. 8 bits of the L2 register map to L1=X, while the other bits map to L1=Y). This also properly handles multiple L2 interrupt registers that are mapped to a single L1 bit. Also, if the L2InvalidMask is BINT_DONT_PROCESS_L2 for this register it means that the interrupt interface does not handle the L2 interrupts. Rather it should just create the first callback associated with that L1 interrupt shift. */ if( (intHandle->pIntMap[intMapIndex].L2RegOffset == L2Reg) && ( !((1ul<pIntMap[intMapIndex].L2InvalidMask) || (intHandle->pIntMap[intMapIndex].L2InvalidMask==BINT_DONT_PROCESS_L2) )) { break; } } L1Shift = intHandle->pIntMap[intMapIndex].L1Shift; if( L1Shift == -1 ) { rc = BERR_TRACE(BERR_INVALID_PARAMETER); goto done; } L1Shift &= ~BINT_IS_STANDARD; /* Determine if we need to allocate a new L2 interrupt context */ for(L2Handle=BLST_SQ_FIRST(&(intHandle->IntArray[L1Shift])); L2Handle ; L2Handle=BLST_SQ_NEXT(L2Handle, link)) { if( BCHP_INT_ID_GET_REG( L2Handle->intId ) == L2Reg && BCHP_INT_ID_GET_SHIFT( L2Handle->intId ) == L2Shift ) { break; } else if( BCHP_INT_ID_GET_REG( L2Handle->intId ) == L2Reg ) { /* Optimization: Remember if we share the same L2, but not the same L2Shift. This makes the IntArray sorted by L2 register. This allows us to avoid extra BREG_Read32 calls in BINT_Isr. */ SameL2RegHandle = L2Handle; } } if( L2Handle == NULL ) { /* We need to create a new L2 element to manage this interrupt bit */ L2Handle = (BINT_P_L2Int *) BKNI_Malloc( sizeof(BINT_P_L2Int) ); if( L2Handle == NULL ) { rc = BERR_TRACE(BERR_INVALID_PARAMETER); goto done; } BLST_SQ_INIT( &(L2Handle->callbackList) ); L2Handle->enableCount = 0; L2Handle->intId = intId; L2Handle->intHandle = intHandle; L2Handle->intMapIndex = intMapIndex; L2Handle->count = 0; BKNI_EnterCriticalSection(); if (SameL2RegHandle) { BLST_SQ_INSERT_AFTER(&(intHandle->IntArray[L1Shift]), SameL2RegHandle, L2Handle, link); } else { BLST_SQ_INSERT_TAIL(&(intHandle->IntArray[L1Shift]), L2Handle, link); } BKNI_LeaveCriticalSection(); intHandle->numInts++; /* clear previous status */ if( intHandle->pClearInt != NULL ) { intHandle->pClearInt( intHandle->regHandle, BCHP_INT_ID_GET_REG(intId), BCHP_INT_ID_GET_SHIFT(intId) ); } } *pCbHandle = (BINT_CallbackHandle) BKNI_Malloc( sizeof(BINT_P_Callback) ); if( *pCbHandle == NULL ) { rc = BERR_TRACE(BERR_OUT_OF_SYSTEM_MEMORY); goto done; } BKNI_Memset( *pCbHandle, 0, sizeof(BINT_P_Callback) ); (*pCbHandle)->func = func; (*pCbHandle)->pParm1 = pParm1; (*pCbHandle)->parm2 = parm2; (*pCbHandle)->L2Handle = L2Handle; (*pCbHandle)->StatInfo.ulTimeMin = UINT32_MAX; (*pCbHandle)->StatInfo.ulTimeMax = 0; (*pCbHandle)->StatInfo.ulTimeAvg = 0; (*pCbHandle)->StatInfo.ulCbHitCount = 0; (*pCbHandle)->StatInfo.ulTimeStampStartIdx = 0; BKNI_Memset((*pCbHandle)->StatInfo.aulTimeStamp, 0, sizeof(uint32_t) * BINT_P_STATS_RECENT_CB_HIT_COUNT); /* set up default bins */ (*pCbHandle)->StatInfo.ulActiveBins = BINT_P_STATS_DEFAULT_BINS_NUM; BKNI_Memcpy((*pCbHandle)->StatInfo.aBinInfo, g_aDefaultBins, sizeof(g_aDefaultBins)); (*pCbHandle)->StatInfo.bDefaultBins = true; BKNI_EnterCriticalSection(); BLST_SQ_INSERT_TAIL(&(L2Handle->callbackList), *pCbHandle, link); BLST_SQ_INSERT_TAIL(&(intHandle->allCbList), *pCbHandle, allCbLink); BKNI_LeaveCriticalSection(); (*pCbHandle)->count = 0; #if BDBG_DEBUG_BUILD (*pCbHandle)->callbackName = callbackName; #else (*pCbHandle)->callbackName = NULL; #endif intHandle->callbackCount++; rc = BERR_SUCCESS; done: BINT_UNLOCK(intHandle); return rc; } BERR_Code BINT_DestroyCallback( BINT_CallbackHandle cbHandle ) { BINT_P_Context *intHandle; BDBG_ASSERT( cbHandle != NULL ); intHandle = cbHandle->L2Handle->intHandle; BINT_LOCK(intHandle); if( cbHandle->enabled == true ) { BINT_DisableCallback(cbHandle ); } BKNI_EnterCriticalSection(); BLST_SQ_REMOVE(&(cbHandle->L2Handle->callbackList), cbHandle, BINT_P_Callback, link); BLST_SQ_REMOVE(&(intHandle->allCbList), cbHandle, BINT_P_Callback, allCbLink); BKNI_LeaveCriticalSection(); BKNI_Free( cbHandle ); intHandle->callbackCount--; BINT_UNLOCK(intHandle); return BERR_SUCCESS; } BERR_Code BINT_EnableCallback( BINT_CallbackHandle cbHandle ) { BERR_Code rc; BKNI_EnterCriticalSection(); rc = BINT_EnableCallback_isr( cbHandle ); BKNI_LeaveCriticalSection(); return rc; } BERR_Code BINT_EnableCallback_isr( BINT_CallbackHandle cbHandle ) { BINT_P_Context *intHandle; BDBG_ASSERT( cbHandle != NULL ); /* If enabled, we are already done... */ if( cbHandle->enabled ) { return BERR_SUCCESS; } intHandle = cbHandle->L2Handle->intHandle; /* Flag callback as enabled so that we execute it if we get an interrupt immediately after it is unmasked */ cbHandle->enabled = true; cbHandle->L2Handle->enableCount++; if( cbHandle->L2Handle->enableCount == 1 ) { if( intHandle->pClearMask != NULL ) { intHandle->pClearMask( intHandle->regHandle, BCHP_INT_ID_GET_REG(cbHandle->L2Handle->intId), BCHP_INT_ID_GET_SHIFT(cbHandle->L2Handle->intId) ); } } return BERR_SUCCESS; } BERR_Code BINT_DisableCallback( BINT_CallbackHandle cbHandle ) { BERR_Code rc; BKNI_EnterCriticalSection(); rc = BINT_DisableCallback_isr( cbHandle ); BKNI_LeaveCriticalSection(); return rc; } BERR_Code BINT_DisableCallback_isr( BINT_CallbackHandle cbHandle ) { BINT_P_Context *intHandle; BDBG_ASSERT( cbHandle != NULL ); /* If not enabled, we are already done... */ if( cbHandle->enabled == false ) { return BERR_SUCCESS; } intHandle = cbHandle->L2Handle->intHandle; cbHandle->L2Handle->enableCount--; if( cbHandle->L2Handle->enableCount == 0 ) { if( intHandle->pSetMask != NULL ) { intHandle->pSetMask( intHandle->regHandle, BCHP_INT_ID_GET_REG(cbHandle->L2Handle->intId), BCHP_INT_ID_GET_SHIFT(cbHandle->L2Handle->intId) ); } } /* Flag callback as disabled only after it is masked so that we can execute if we get an interrupt */ cbHandle->enabled = false; return BERR_SUCCESS; } BERR_Code BINT_ClearCallback( BINT_CallbackHandle cbHandle ) { BERR_Code rc; BKNI_EnterCriticalSection(); rc = BINT_ClearCallback_isr( cbHandle ); BKNI_LeaveCriticalSection(); return rc; } BERR_Code BINT_ClearCallback_isr( BINT_CallbackHandle cbHandle ) { BERR_Code rc = BERR_SUCCESS; BINT_P_Context *intHandle; BDBG_ASSERT( cbHandle != NULL ); intHandle = cbHandle->L2Handle->intHandle; if( intHandle->pClearInt != NULL ) { intHandle->pClearInt( intHandle->regHandle, BCHP_INT_ID_GET_REG(cbHandle->L2Handle->intId), BCHP_INT_ID_GET_SHIFT(cbHandle->L2Handle->intId) ); } return rc; } BERR_Code BINT_TriggerInterruptByHandle( BINT_CallbackHandle cbHandle ) { return BINT_TriggerInterruptByHandle_isr( cbHandle ); } BERR_Code BINT_TriggerInterruptByHandle_isr( BINT_CallbackHandle cbHandle ) { BINT_P_Context *intHandle; BDBG_ASSERT( cbHandle != NULL ); intHandle = cbHandle->L2Handle->intHandle; if( intHandle->pSetInt == NULL ) { return BERR_TRACE(BERR_INVALID_PARAMETER); } else { intHandle->pSetInt( intHandle->regHandle, BCHP_INT_ID_GET_REG(cbHandle->L2Handle->intId), BCHP_INT_ID_GET_SHIFT(cbHandle->L2Handle->intId) ); } return BERR_SUCCESS; } #if (BINT_NEW_INT_MODEL) void BINT_GetL1BitMask(BINT_Handle intHandle, uint32_t BitMask[BINT_MAX_INTC_SIZE]) { int i; BDBG_OBJECT_ASSERT(intHandle, BINT); for(i=0;ipIntMap[i].L1Shift != -1; i++ ) { int L1Shift = intHandle->pIntMap[i].L1Shift&~BINT_IS_STANDARD; if(L1Shift >= 96) { BitMask[3] |= 1ul<<(L1Shift-96); } else if(L1Shift >= 64 && L1Shift < 96) { BitMask[2] |= 1ul<<(L1Shift-64); } else if( L1Shift >= 32 && L1Shift < 64 ) { BitMask[1] |= 1ul<<(L1Shift-32); } else { BitMask[0] |= 1ul<pIntMap[i].L1Shift != -1; i++ ) { int L1Shift = intHandle->pIntMap[i].L1Shift&~BINT_IS_STANDARD; if( L1Shift >= 32 ) { *pBitMaskHi |= 1ul<<(L1Shift-32); } else { *pBitMaskLo |= 1ul<allCbList)); } BINT_CallbackHandle BINT_GetCallbackNext( BINT_CallbackHandle cbHandle ) { return BLST_SQ_NEXT(cbHandle, allCbLink); } BERR_Code BINT_GetInterruptId( BINT_CallbackHandle cbHandle, BINT_Id *pIntId ) { if (( cbHandle == NULL ) || ( pIntId == NULL )) { return BERR_TRACE(BERR_INVALID_PARAMETER); } *pIntId = cbHandle->L2Handle->intId; return BERR_SUCCESS; } BERR_Code BINT_GetCallbackStatus( BINT_CallbackHandle cbHandle, bool *pbEnabled) { if ( cbHandle == NULL ) { return BERR_TRACE(BERR_INVALID_PARAMETER); } *pbEnabled = cbHandle->enabled; return BERR_SUCCESS; } BERR_Code BINT_Stats_AddBin( BINT_CallbackHandle cbHandle, uint32_t ulRangeMin, uint32_t ulRangeMax ) { uint32_t ulActiveBins; BINT_Stats_CallbackBin *pCurBinInfo = NULL; BINT_Stats_CallbackBin aTmpBinInfo[BINT_P_STATS_BIN_MAX]; uint16_t i = 0; if (( cbHandle == NULL ) || ( ulRangeMin > ulRangeMax)) { return BERR_TRACE(BERR_INVALID_PARAMETER); } if ( cbHandle->StatInfo.bDefaultBins ) { BINT_Stats_DestroyBins(cbHandle); cbHandle->StatInfo.bDefaultBins = false; } ulActiveBins = cbHandle->StatInfo.ulActiveBins; if ( ulActiveBins == BINT_P_STATS_BIN_MAX ) { return BERR_TRACE(BERR_INVALID_PARAMETER); } for (i = 0; i <= ulActiveBins; i++) { pCurBinInfo = &(cbHandle->StatInfo.aBinInfo[i]); /* check for range overlap with current bin */ if (( ulActiveBins > 0 ) && ((( ulRangeMin >= pCurBinInfo->ulBinRangeMin ) && ( ulRangeMin <= pCurBinInfo->ulBinRangeMax )) || (( ulRangeMax >= pCurBinInfo->ulBinRangeMin ) && ( ulRangeMax <= pCurBinInfo->ulBinRangeMax )))) { return BERR_TRACE(BERR_INVALID_PARAMETER); } /* check for bin insertion here */ if ( ulRangeMax < pCurBinInfo->ulBinRangeMin ) { /* shift bins for insertion */ uint32_t ulBinCopyNum = ulActiveBins - i; BKNI_Memcpy(aTmpBinInfo, pCurBinInfo, sizeof(BINT_Stats_CallbackBin) * ulBinCopyNum); BKNI_Memcpy(pCurBinInfo + 1, aTmpBinInfo, sizeof(BINT_Stats_CallbackBin) * ulBinCopyNum); } /* set bin */ if (( ulRangeMax < pCurBinInfo->ulBinRangeMin ) || (i == ulActiveBins)) { pCurBinInfo->ulBinRangeMin = ulRangeMin; pCurBinInfo->ulBinRangeMax = ulRangeMax; pCurBinInfo->ulBinHitCount = 0; cbHandle->StatInfo.ulActiveBins++; break; } } return BERR_SUCCESS; } BERR_Code BINT_Stats_DestroyBins( BINT_CallbackHandle cbHandle ) { if ( cbHandle == NULL ) { return BERR_TRACE(BERR_INVALID_PARAMETER); } cbHandle->StatInfo.ulActiveBins = 0; BKNI_Memset(cbHandle->StatInfo.aBinInfo, 0, sizeof(BINT_Stats_CallbackBin) * BINT_P_STATS_BIN_MAX); return BERR_SUCCESS; } BERR_Code BINT_Stats_Get( BINT_CallbackHandle cbHandle, BINT_Stats_CallbackStats **ppCbStats ) { BINT_P_Context *intHandle; if ( cbHandle == NULL ) { return BERR_TRACE(BERR_INVALID_PARAMETER); } intHandle = cbHandle->L2Handle->intHandle; #ifndef BINT_STATS_ENABLE BDBG_WRN(("Stats tracking not enabled in compile.")); #endif /* BINT_STATS_ENABLE */ *ppCbStats = &(cbHandle->StatInfo); return BERR_SUCCESS; } BERR_Code BINT_Stats_Reset( BINT_CallbackHandle cbHandle ) { uint16_t i; BINT_Stats_CallbackStats *pStatInfo = &(cbHandle->StatInfo); if ( cbHandle == NULL ) { return BERR_TRACE(BERR_INVALID_PARAMETER); } BKNI_EnterCriticalSection(); pStatInfo->ulTimeMin = UINT32_MAX; pStatInfo->ulTimeMax = 0; pStatInfo->ulTimeAvg = 0; pStatInfo->ulCbHitCount = 0; for (i = 0; i < pStatInfo->ulActiveBins; i++) { pStatInfo->aBinInfo[i].ulBinHitCount = 0; } BKNI_LeaveCriticalSection(); return BERR_SUCCESS; } BERR_Code BINT_Stats_Enable( BINT_Handle intHandle, BTMR_Handle hTmrHandle ) { BERR_Code rc = BERR_SUCCESS; BTMR_TimerHandle hTimer = NULL; BTMR_Settings stSettings = { BTMR_Type_eSharedFreeRun, NULL, NULL, 0, false }; if ( intHandle == NULL ) { return BERR_TRACE(BERR_INVALID_PARAMETER); } if ( intHandle->bStatsEnable == true ) { return BERR_TRACE(BINT_STATS_ERR_ALREADY_ENABLED); } rc = BTMR_CreateTimer( hTmrHandle, &hTimer, &stSettings ); if (rc != BERR_SUCCESS) { return rc; } intHandle->hTimer = hTimer; intHandle->bStatsEnable = true; return BERR_SUCCESS; } BERR_Code BINT_Stats_Disable( BINT_Handle intHandle ) { if ( intHandle == NULL ) { return BERR_TRACE(BERR_INVALID_PARAMETER); } if ( intHandle->bStatsEnable == false ) { return BERR_TRACE(BINT_STATS_ERR_ALREADY_DISABLED); } BDBG_ASSERT( intHandle->hTimer != NULL ); BTMR_DestroyTimer(intHandle->hTimer); intHandle->bStatsEnable = false; return BERR_SUCCESS; } /* returns elapsed time in microseconds */ uint32_t BINT_P_GetElapsedTime( uint32_t ulTimerStart, uint32_t ulTimerEnd ) { uint32_t ulTimerMax; uint32_t ulTimerElapsed; ulTimerMax = BTMR_ReadTimerMax(); if (ulTimerEnd < ulTimerStart) { ulTimerElapsed = ((ulTimerMax - ulTimerStart) + ulTimerEnd); } else { ulTimerElapsed = (ulTimerEnd - ulTimerStart); } return ulTimerElapsed; } BERR_Code BINT_P_Stats_ComputeStats( BINT_CallbackHandle cbHandle, uint32_t ulStart, uint32_t ulEnd ) { uint16_t i; uint32_t ulSampleNum = BINT_P_STATS_SAMPLE_MAX; uint32_t ulElapsedTime = BINT_P_GetElapsedTime( ulStart, ulEnd ); BINT_Stats_CallbackStats *pStatInfo = &(cbHandle->StatInfo); pStatInfo->ulCbHitCount++; /* calculate min, max and average times */ if ( ulElapsedTime < pStatInfo->ulTimeMin) { pStatInfo->ulTimeMin = ulElapsedTime; } if ( ulElapsedTime > pStatInfo->ulTimeMax) { pStatInfo->ulTimeMax = ulElapsedTime; } if (ulSampleNum > pStatInfo->ulCbHitCount) { ulSampleNum = pStatInfo->ulCbHitCount; } pStatInfo->ulTimeAvg = ((pStatInfo->ulTimeAvg * (ulSampleNum - 1)) + ulElapsedTime) / ulSampleNum; pStatInfo->aulTimeStamp[pStatInfo->ulTimeStampStartIdx] = ulStart; /* check for callbacks that take too long */ if (ulElapsedTime > BINT_P_STATS_EXECUTION_TIME_MAX_THRESHOLD) { BDBG_WRN(("BINT_Isr(%s) took %d msec", cbHandle->L2Handle->intHandle->pIntMap[cbHandle->L2Handle->intMapIndex].L2Name, ulElapsedTime/1000)); } /* check for runaway interrupts */ if (ulSampleNum >= BINT_P_STATS_RECENT_CB_HIT_COUNT) { uint32_t ulTotalPeriod, ulAvgPeriod; uint32_t ulTimeStampEndIdx; if (pStatInfo->ulTimeStampStartIdx == BINT_P_STATS_RECENT_CB_HIT_COUNT - 1) { ulTimeStampEndIdx = 0; } else { ulTimeStampEndIdx = pStatInfo->ulTimeStampStartIdx + 1; } ulTotalPeriod = BINT_P_GetElapsedTime(pStatInfo->aulTimeStamp[ulTimeStampEndIdx], pStatInfo->aulTimeStamp[pStatInfo->ulTimeStampStartIdx]); ulAvgPeriod = ulTotalPeriod / BINT_P_STATS_RECENT_CB_HIT_COUNT; #if 0 /* Commenting out this code because some interrupts fire faster than this for a period of time. */ if (ulAvgPeriod < BINT_P_STATS_AVG_PERIOD_MIN_THRESHOLD) { BDBG_WRN(("BINT_Isr(%s) overflow, %d msec between hits", cbHandle->L2Handle->intHandle->pIntMap[cbHandle->L2Handle->intMapIndex].L2Name, ulAvgPeriod/1000)); } #endif pStatInfo->ulTimeStampStartIdx++; pStatInfo->ulTimeStampStartIdx = pStatInfo->ulTimeStampStartIdx % BINT_P_STATS_RECENT_CB_HIT_COUNT; } /* mark bin according to elapsed time */ for (i = 0; i < pStatInfo->ulActiveBins; i++) { if ((ulElapsedTime >= pStatInfo->aBinInfo[i].ulBinRangeMin) && (ulElapsedTime <= pStatInfo->aBinInfo[i].ulBinRangeMax)) { pStatInfo->aBinInfo[i].ulBinHitCount++; break; } } return BERR_SUCCESS; } void BINT_DumpInfo(BINT_Handle intHandle) { BINT_P_L2Handle L2Handle; int L1Shift, i; BINT_CallbackHandle callback; bool l1_head, l2_head, int_head; BDBG_OBJECT_ASSERT(intHandle, BINT); BINT_LOCK(intHandle); for(i=0,int_head=false; intHandle->pIntMap[i].L1Shift != -1; i++) { L1Shift = intHandle->pIntMap[i].L1Shift&~BINT_IS_STANDARD; for(l1_head=false,L2Handle=BLST_SQ_FIRST(&(intHandle->IntArray[L1Shift])); L2Handle ; L2Handle=BLST_SQ_NEXT(L2Handle, link)) { if (!L2Handle->count) { continue; } if (!int_head) { BDBG_MSG(("------[%s dump]--------", intHandle->settings.name?intHandle->settings.name:"XXX")); int_head=true; } if(!l1_head) { BDBG_MSG((" %#x:%s", (unsigned)L1Shift, intHandle->pIntMap[i].L2Name?intHandle->pIntMap[i].L2Name:"")); l1_head=true; } for(l2_head=false,callback=BLST_SQ_FIRST(&L2Handle->callbackList); callback; callback=BLST_SQ_NEXT(callback, link)) { if (!callback->count) { continue; } if (!l2_head) { BDBG_MSG((" %#x:%u %s[%#x]:(%#x,%#x) %u %s", BCHP_INT_ID_GET_REG(L2Handle->intId), BCHP_INT_ID_GET_SHIFT(L2Handle->intId), callback->callbackName, (unsigned)callback->func, (unsigned)callback->pParm1, (unsigned)callback->parm2, callback->count, callback->enabled?"":"disabled")); l2_head=true; } else { BDBG_MSG((" >>> %s[%#x]:(%#x,%#x) %u %s", callback->callbackName, (unsigned)callback->func, (unsigned)callback->pParm1, (unsigned)callback->parm2, callback->count, callback->enabled?"":"disabled")); } #if 0 #ifdef BINT_STATS_ENABLE if (intHandle->bStatsEnable) { BDBG_MSG((" elapsed: min %d, max %d, avg %d (usec)", callback->StatInfo.ulTimeMin, callback->StatInfo.ulTimeMax, callback->StatInfo.ulTimeAvg)); callback->StatInfo.ulTimeMin = 0; callback->StatInfo.ulTimeMax = 0; callback->StatInfo.ulTimeAvg = 0; callback->StatInfo.ulCbHitCount = 0; } #endif #endif callback->count=0; } L2Handle->count=0; } } BINT_UNLOCK(intHandle); return; }