| /** |
| * @file IxQMgrDispatcher.c |
| * |
| * @author Intel Corporation |
| * @date 20-Dec-2001 |
| * |
| * @brief This file contains the implementation of the Dispatcher sub component |
| * |
| * |
| * @par |
| * IXP400 SW Release version 2.0 |
| * |
| * -- Copyright Notice -- |
| * |
| * @par |
| * Copyright 2001-2005, Intel Corporation. |
| * All rights reserved. |
| * |
| * @par |
| * SPDX-License-Identifier: BSD-3-Clause |
| * @par |
| * -- End of Copyright Notice -- |
| */ |
| |
| /* |
| * User defined include files. |
| */ |
| #include "IxQMgr.h" |
| #include "IxQMgrAqmIf_p.h" |
| #include "IxQMgrQCfg_p.h" |
| #include "IxQMgrDispatcher_p.h" |
| #include "IxQMgrLog_p.h" |
| #include "IxQMgrDefines_p.h" |
| #include "IxFeatureCtrl.h" |
| #include "IxOsal.h" |
| |
| |
| |
| /* |
| * #defines and macros used in this file. |
| */ |
| |
| |
| /* |
| * This constant is used to indicate the number of priority levels supported |
| */ |
| #define IX_QMGR_NUM_PRIORITY_LEVELS 3 |
| |
| /* |
| * This constant is used to set the size of the array of status words |
| */ |
| #define MAX_Q_STATUS_WORDS 4 |
| |
| /* |
| * This macro is used to check if a given priority is valid |
| */ |
| #define IX_QMGR_DISPATCHER_PRIORITY_CHECK(priority) \ |
| (((priority) >= IX_QMGR_Q_PRIORITY_0) && ((priority) <= IX_QMGR_Q_PRIORITY_2)) |
| |
| /* |
| * This macto is used to check that a given interrupt source is valid |
| */ |
| #define IX_QMGR_DISPATCHER_SOURCE_ID_CHECK(srcSel) \ |
| (((srcSel) >= IX_QMGR_Q_SOURCE_ID_E) && ((srcSel) <= IX_QMGR_Q_SOURCE_ID_NOT_F)) |
| |
| /* |
| * Number of times a dummy callback is called before logging a trace |
| * message |
| */ |
| #define LOG_THROTTLE_COUNT 1000000 |
| |
| /* Priority tables limits */ |
| #define IX_QMGR_MIN_LOW_QUE_PRIORITY_TABLE_INDEX (0) |
| #define IX_QMGR_MID_LOW_QUE_PRIORITY_TABLE_INDEX (16) |
| #define IX_QMGR_MAX_LOW_QUE_PRIORITY_TABLE_INDEX (31) |
| #define IX_QMGR_MIN_UPP_QUE_PRIORITY_TABLE_INDEX (32) |
| #define IX_QMGR_MID_UPP_QUE_PRIORITY_TABLE_INDEX (48) |
| #define IX_QMGR_MAX_UPP_QUE_PRIORITY_TABLE_INDEX (63) |
| |
| /* |
| * This macro is used to check if a given callback type is valid |
| */ |
| #define IX_QMGR_DISPATCHER_CALLBACK_TYPE_CHECK(type) \ |
| (((type) >= IX_QMGR_TYPE_REALTIME_OTHER) && \ |
| ((type) <= IX_QMGR_TYPE_REALTIME_SPORADIC)) |
| |
| /* |
| * define max index in lower queue to use in loops |
| */ |
| #define IX_QMGR_MAX_LOW_QUE_TABLE_INDEX (31) |
| |
| /* |
| * Typedefs whose scope is limited to this file. |
| */ |
| |
| /* |
| * Information on a queue needed by the Dispatcher |
| */ |
| typedef struct |
| { |
| IxQMgrCallback callback; /* Notification callback */ |
| IxQMgrCallbackId callbackId; /* Notification callback identifier */ |
| unsigned dummyCallbackCount; /* Number of times runs of dummy callback */ |
| IxQMgrPriority priority; /* Dispatch priority */ |
| unsigned int statusWordOffset; /* Offset to the status word to check */ |
| UINT32 statusMask; /* Status mask */ |
| UINT32 statusCheckValue; /* Status check value */ |
| UINT32 intRegCheckMask; /* Interrupt register check mask */ |
| } IxQMgrQInfo; |
| |
| /* |
| * Variable declarations global to this file. Externs are followed by |
| * statics. |
| */ |
| |
| /* |
| * Flag to keep record of what dispatcher set in featureCtrl when ixQMgrInit() |
| * is called. This is needed because it is possible that a client might |
| * change whether the live lock prevention dispatcher is used between |
| * calls to ixQMgrInit() and ixQMgrDispatcherLoopGet(). |
| */ |
| PRIVATE IX_STATUS ixQMgrOrigB0Dispatcher = IX_FEATURE_CTRL_COMPONENT_ENABLED; |
| |
| /* |
| * keep record of Q types - not in IxQMgrQInfo for performance as |
| * it is only used with ixQMgrDispatcherLoopRunB0LLP() |
| */ |
| PRIVATE IxQMgrType ixQMgrQTypes[IX_QMGR_MAX_NUM_QUEUES]; |
| |
| /* |
| * This array contains a list of queue identifiers ordered by priority. The table |
| * is split logically between queue identifiers 0-31 and 32-63. |
| */ |
| static IxQMgrQId priorityTable[IX_QMGR_MAX_NUM_QUEUES]; |
| |
| /* |
| * This flag indicates to the dispatcher that the priority table needs to be rebuilt. |
| */ |
| static BOOL rebuildTable = false; |
| |
| /* Dispatcher statistics */ |
| static IxQMgrDispatcherStats dispatcherStats; |
| |
| /* Table of queue information */ |
| static IxQMgrQInfo dispatchQInfo[IX_QMGR_MAX_NUM_QUEUES]; |
| |
| /* Masks use to identify the first queues in the priority tables |
| * when comparing with the interrupt register |
| */ |
| static unsigned int lowPriorityTableFirstHalfMask; |
| static unsigned int uppPriorityTableFirstHalfMask; |
| |
| /* |
| * Static function prototypes |
| */ |
| |
| /* |
| * This function is the default callback for all queues |
| */ |
| PRIVATE void |
| dummyCallback (IxQMgrQId qId, |
| IxQMgrCallbackId cbId); |
| |
| PRIVATE void |
| ixQMgrDispatcherReBuildPriorityTable (void); |
| |
| /* |
| * Function definitions. |
| */ |
| void |
| ixQMgrDispatcherInit (void) |
| { |
| int i; |
| IxFeatureCtrlProductId productId = 0; |
| IxFeatureCtrlDeviceId deviceId = 0; |
| BOOL stickyIntSilicon = true; |
| |
| /* Set default priorities */ |
| for (i=0; i< IX_QMGR_MAX_NUM_QUEUES; i++) |
| { |
| dispatchQInfo[i].callback = dummyCallback; |
| dispatchQInfo[i].callbackId = 0; |
| dispatchQInfo[i].dummyCallbackCount = 0; |
| dispatchQInfo[i].priority = IX_QMGR_Q_PRIORITY_2; |
| dispatchQInfo[i].statusWordOffset = 0; |
| dispatchQInfo[i].statusCheckValue = 0; |
| dispatchQInfo[i].statusMask = 0; |
| /* |
| * There are two interrupt registers, 32 bits each. One for the lower |
| * queues(0-31) and one for the upper queues(32-63). Therefore need to |
| * mod by 32 i.e the min upper queue identifier. |
| */ |
| dispatchQInfo[i].intRegCheckMask = (1<<(i%(IX_QMGR_MIN_QUEUPP_QID))); |
| |
| /* |
| * Set the Q types - will only be used with livelock |
| */ |
| ixQMgrQTypes[i] = IX_QMGR_TYPE_REALTIME_OTHER; |
| |
| /* Reset queue statistics */ |
| dispatcherStats.queueStats[i].callbackCnt = 0; |
| dispatcherStats.queueStats[i].priorityChangeCnt = 0; |
| dispatcherStats.queueStats[i].intNoCallbackCnt = 0; |
| dispatcherStats.queueStats[i].intLostCallbackCnt = 0; |
| dispatcherStats.queueStats[i].notificationEnabled = false; |
| dispatcherStats.queueStats[i].srcSel = 0; |
| |
| } |
| |
| /* Priority table. Order the table from queue 0 to 63 */ |
| ixQMgrDispatcherReBuildPriorityTable(); |
| |
| /* Reset statistics */ |
| dispatcherStats.loopRunCnt = 0; |
| |
| /* Get the device ID for the underlying silicon */ |
| deviceId = ixFeatureCtrlDeviceRead(); |
| |
| /* Get the product ID for the underlying silicon */ |
| productId = ixFeatureCtrlProductIdRead(); |
| |
| /* |
| * Check featureCtrl to see if Livelock prevention is required |
| */ |
| ixQMgrOrigB0Dispatcher = ixFeatureCtrlSwConfigurationCheck( |
| IX_FEATURECTRL_ORIGB0_DISPATCHER); |
| |
| /* |
| * Check if the silicon supports the sticky interrupt feature. |
| * IF (IXP42X AND A0) -> No sticky interrupt feature supported |
| */ |
| if ((IX_FEATURE_CTRL_DEVICE_TYPE_IXP42X == |
| (IX_FEATURE_CTRL_DEVICE_TYPE_MASK & deviceId)) && |
| (IX_FEATURE_CTRL_SILICON_TYPE_A0 == |
| (IX_FEATURE_CTRL_SILICON_STEPPING_MASK & productId))) |
| { |
| stickyIntSilicon = false; |
| } |
| |
| /* |
| * IF user wants livelock prev option AND silicon supports sticky interrupt |
| * feature -> enable the sticky interrupt bit |
| */ |
| if ((IX_FEATURE_CTRL_SWCONFIG_DISABLED == ixQMgrOrigB0Dispatcher) && |
| stickyIntSilicon) |
| { |
| ixQMgrStickyInterruptRegEnable(); |
| } |
| } |
| |
| IX_STATUS |
| ixQMgrDispatcherPrioritySet (IxQMgrQId qId, |
| IxQMgrPriority priority) |
| { |
| int ixQMgrLockKey; |
| |
| if (!ixQMgrQIsConfigured(qId)) |
| { |
| return IX_QMGR_Q_NOT_CONFIGURED; |
| } |
| |
| if (!IX_QMGR_DISPATCHER_PRIORITY_CHECK(priority)) |
| { |
| return IX_QMGR_Q_INVALID_PRIORITY; |
| } |
| |
| ixQMgrLockKey = ixOsalIrqLock(); |
| |
| /* Change priority */ |
| dispatchQInfo[qId].priority = priority; |
| /* Set flag */ |
| rebuildTable = true; |
| |
| ixOsalIrqUnlock(ixQMgrLockKey); |
| |
| #ifndef NDEBUG |
| /* Update statistics */ |
| dispatcherStats.queueStats[qId].priorityChangeCnt++; |
| #endif |
| |
| return IX_SUCCESS; |
| } |
| |
| IX_STATUS |
| ixQMgrNotificationCallbackSet (IxQMgrQId qId, |
| IxQMgrCallback callback, |
| IxQMgrCallbackId callbackId) |
| { |
| if (!ixQMgrQIsConfigured(qId)) |
| { |
| return IX_QMGR_Q_NOT_CONFIGURED; |
| } |
| |
| if (NULL == callback) |
| { |
| /* Reset to dummy callback */ |
| dispatchQInfo[qId].callback = dummyCallback; |
| dispatchQInfo[qId].dummyCallbackCount = 0; |
| dispatchQInfo[qId].callbackId = 0; |
| } |
| else |
| { |
| dispatchQInfo[qId].callback = callback; |
| dispatchQInfo[qId].callbackId = callbackId; |
| } |
| |
| return IX_SUCCESS; |
| } |
| |
| IX_STATUS |
| ixQMgrNotificationEnable (IxQMgrQId qId, |
| IxQMgrSourceId srcSel) |
| { |
| IxQMgrQStatus qStatusOnEntry;/* The queue status on entry/exit */ |
| IxQMgrQStatus qStatusOnExit; /* to this function */ |
| int ixQMgrLockKey; |
| |
| #ifndef NDEBUG |
| if (!ixQMgrQIsConfigured (qId)) |
| { |
| return IX_QMGR_Q_NOT_CONFIGURED; |
| } |
| |
| if ((qId < IX_QMGR_MIN_QUEUPP_QID) && |
| !IX_QMGR_DISPATCHER_SOURCE_ID_CHECK(srcSel)) |
| { |
| /* QId 0-31 source id invalid */ |
| return IX_QMGR_INVALID_INT_SOURCE_ID; |
| } |
| |
| if ((IX_QMGR_Q_SOURCE_ID_NE != srcSel) && |
| (qId >= IX_QMGR_MIN_QUEUPP_QID)) |
| { |
| /* |
| * For queues 32-63 the interrupt source is fixed to the Nearly |
| * Empty status flag and therefore should have a srcSel of NE. |
| */ |
| return IX_QMGR_INVALID_INT_SOURCE_ID; |
| } |
| #endif |
| |
| #ifndef NDEBUG |
| dispatcherStats.queueStats[qId].notificationEnabled = true; |
| dispatcherStats.queueStats[qId].srcSel = srcSel; |
| #endif |
| |
| /* Get the current queue status */ |
| ixQMgrAqmIfQueStatRead (qId, &qStatusOnEntry); |
| |
| /* |
| * Enabling interrupts results in Read-Modify-Write |
| * so need critical section |
| */ |
| |
| ixQMgrLockKey = ixOsalIrqLock(); |
| |
| /* Calculate the checkMask and checkValue for this q */ |
| ixQMgrAqmIfQStatusCheckValsCalc (qId, |
| srcSel, |
| &dispatchQInfo[qId].statusWordOffset, |
| &dispatchQInfo[qId].statusCheckValue, |
| &dispatchQInfo[qId].statusMask); |
| |
| |
| /* Set the interrupt source is this queue is in the range 0-31 */ |
| if (qId < IX_QMGR_MIN_QUEUPP_QID) |
| { |
| ixQMgrAqmIfIntSrcSelWrite (qId, srcSel); |
| } |
| |
| /* Enable the interrupt */ |
| ixQMgrAqmIfQInterruptEnable (qId); |
| |
| ixOsalIrqUnlock(ixQMgrLockKey); |
| |
| /* Get the current queue status */ |
| ixQMgrAqmIfQueStatRead (qId, &qStatusOnExit); |
| |
| /* If the status has changed return a warning */ |
| if (qStatusOnEntry != qStatusOnExit) |
| { |
| return IX_QMGR_WARNING; |
| } |
| |
| return IX_SUCCESS; |
| } |
| |
| |
| IX_STATUS |
| ixQMgrNotificationDisable (IxQMgrQId qId) |
| { |
| int ixQMgrLockKey; |
| |
| #ifndef NDEBUG |
| /* Validate parameters */ |
| if (!ixQMgrQIsConfigured (qId)) |
| { |
| return IX_QMGR_Q_NOT_CONFIGURED; |
| } |
| #endif |
| |
| /* |
| * Enabling interrupts results in Read-Modify-Write |
| * so need critical section |
| */ |
| #ifndef NDEBUG |
| dispatcherStats.queueStats[qId].notificationEnabled = false; |
| #endif |
| |
| ixQMgrLockKey = ixOsalIrqLock(); |
| |
| ixQMgrAqmIfQInterruptDisable (qId); |
| |
| ixOsalIrqUnlock(ixQMgrLockKey); |
| |
| return IX_SUCCESS; |
| } |
| |
| void |
| ixQMgrStickyInterruptRegEnable(void) |
| { |
| /* Use Aqm If function to set Interrupt Register0 Bit-3 */ |
| ixQMgrAqmIfIntSrcSelReg0Bit3Set (); |
| } |
| |
| #if !defined __XSCALE__ || defined __linux |
| |
| /* Count the number of leading zero bits in a word, |
| * and return the same value than the CLZ instruction. |
| * |
| * word (in) return value (out) |
| * 0x80000000 0 |
| * 0x40000000 1 |
| * ,,, ,,, |
| * 0x00000002 30 |
| * 0x00000001 31 |
| * 0x00000000 32 |
| * |
| * The C version of this function is used as a replacement |
| * for system not providing the equivalent of the CLZ |
| * assembly language instruction. |
| * |
| * Note that this version is big-endian |
| */ |
| unsigned int |
| ixQMgrCountLeadingZeros(UINT32 word) |
| { |
| unsigned int leadingZerosCount = 0; |
| |
| if (word == 0) |
| { |
| return 32; |
| } |
| /* search the first bit set by testing the MSB and shifting the input word */ |
| while ((word & 0x80000000) == 0) |
| { |
| word <<= 1; |
| leadingZerosCount++; |
| } |
| return leadingZerosCount; |
| } |
| #endif /* not __XSCALE__ or __linux */ |
| |
| void |
| ixQMgrDispatcherLoopGet (IxQMgrDispatcherFuncPtr *qDispatcherFuncPtr) |
| { |
| IxFeatureCtrlProductId productId = 0; |
| IxFeatureCtrlDeviceId deviceId = 0; |
| |
| /* Get the device ID for the underlying silicon */ |
| deviceId = ixFeatureCtrlDeviceRead(); |
| |
| /* Get the product ID for the underlying silicon */ |
| productId = ixFeatureCtrlProductIdRead (); |
| |
| /* IF (IXP42X AND A0 silicon) -> use ixQMgrDispatcherLoopRunA0 */ |
| if ((IX_FEATURE_CTRL_DEVICE_TYPE_IXP42X == |
| (IX_FEATURE_CTRL_DEVICE_TYPE_MASK & deviceId)) && |
| (IX_FEATURE_CTRL_SILICON_TYPE_A0 == |
| (IX_FEATURE_CTRL_SILICON_STEPPING_MASK & productId))) |
| { |
| /*For IXP42X A0 silicon */ |
| *qDispatcherFuncPtr = &ixQMgrDispatcherLoopRunA0 ; |
| } |
| else /*For IXP42X B0 or IXP46X silicon*/ |
| { |
| if (IX_FEATURE_CTRL_SWCONFIG_ENABLED == ixQMgrOrigB0Dispatcher) |
| { |
| /* Default for IXP42X B0 and IXP46X silicon */ |
| *qDispatcherFuncPtr = &ixQMgrDispatcherLoopRunB0; |
| } |
| else |
| { |
| /* FeatureCtrl indicated that livelock dispatcher be used */ |
| *qDispatcherFuncPtr = &ixQMgrDispatcherLoopRunB0LLP; |
| } |
| } |
| } |
| |
| void |
| ixQMgrDispatcherLoopRunA0 (IxQMgrDispatchGroup group) |
| { |
| UINT32 intRegVal; /* Interrupt reg val */ |
| UINT32 intRegValAfterWrite; /* Interrupt reg val after writing back */ |
| UINT32 intRegCheckMask; /* Mask for checking interrupt bits */ |
| UINT32 qStatusWordsB4Write[MAX_Q_STATUS_WORDS]; /* Status b4 interrupt write */ |
| UINT32 qStatusWordsAfterWrite[MAX_Q_STATUS_WORDS]; /* Status after interrupt write */ |
| IxQMgrQInfo *currDispatchQInfo; |
| BOOL statusChangeFlag; |
| |
| int priorityTableIndex;/* Priority table index */ |
| int qIndex; /* Current queue being processed */ |
| int endIndex; /* Index of last queue to process */ |
| |
| #ifndef NDEBUG |
| IX_OSAL_ASSERT((group == IX_QMGR_QUEUPP_GROUP) || |
| (group == IX_QMGR_QUELOW_GROUP)); |
| #endif |
| |
| /* Read Q status registers before interrupt status read/write */ |
| ixQMgrAqmIfQStatusRegsRead (group, qStatusWordsB4Write); |
| |
| /* Read the interrupt register */ |
| ixQMgrAqmIfQInterruptRegRead (group, &intRegVal); |
| |
| /* No bit set : nothing to process (the reaminder of the algorithm is |
| * based on the fact that the interrupt register value contains at |
| * least one bit set |
| */ |
| if (intRegVal == 0) |
| { |
| #ifndef NDEBUG |
| /* Update statistics */ |
| dispatcherStats.loopRunCnt++; |
| #endif |
| |
| /* Rebuild the priority table if needed */ |
| if (rebuildTable) |
| { |
| ixQMgrDispatcherReBuildPriorityTable (); |
| } |
| |
| return; |
| } |
| |
| /* Write it back to clear the interrupt */ |
| ixQMgrAqmIfQInterruptRegWrite (group, intRegVal); |
| |
| /* Read Q status registers after interrupt status read/write */ |
| ixQMgrAqmIfQStatusRegsRead (group, qStatusWordsAfterWrite); |
| |
| /* get the first queue Id from the interrupt register value */ |
| qIndex = (BITS_PER_WORD - 1) - ixQMgrCountLeadingZeros(intRegVal); |
| |
| /* check if any change occured during hw register modifications */ |
| if (IX_QMGR_QUELOW_GROUP == group) |
| { |
| statusChangeFlag = |
| (qStatusWordsB4Write[0] != qStatusWordsAfterWrite[0]) || |
| (qStatusWordsB4Write[1] != qStatusWordsAfterWrite[1]) || |
| (qStatusWordsB4Write[2] != qStatusWordsAfterWrite[2]) || |
| (qStatusWordsB4Write[3] != qStatusWordsAfterWrite[3]); |
| } |
| else |
| { |
| statusChangeFlag = |
| (qStatusWordsB4Write[0] != qStatusWordsAfterWrite[0]); |
| /* Set the queue range based on the queue group to proccess */ |
| qIndex += IX_QMGR_MIN_QUEUPP_QID; |
| } |
| |
| if (statusChangeFlag == false) |
| { |
| /* check if the interrupt register contains |
| * only 1 bit set (happy day scenario) |
| */ |
| currDispatchQInfo = &dispatchQInfo[qIndex]; |
| if (intRegVal == currDispatchQInfo->intRegCheckMask) |
| { |
| /* only 1 queue event triggered a notification * |
| * Call the callback function for this queue |
| */ |
| currDispatchQInfo->callback (qIndex, |
| currDispatchQInfo->callbackId); |
| #ifndef NDEBUG |
| /* Update statistics */ |
| dispatcherStats.queueStats[qIndex].callbackCnt++; |
| #endif |
| } |
| else |
| { |
| /* the event is triggered by more than 1 queue, |
| * the queue search will be starting from the beginning |
| * or the middle of the priority table |
| * |
| * the serach will end when all the bits of the interrupt |
| * register are cleared. There is no need to maintain |
| * a seperate value and test it at each iteration. |
| */ |
| if (IX_QMGR_QUELOW_GROUP == group) |
| { |
| /* check if any bit related to queues in the first |
| * half of the priority table is set |
| */ |
| if (intRegVal & lowPriorityTableFirstHalfMask) |
| { |
| priorityTableIndex = IX_QMGR_MIN_LOW_QUE_PRIORITY_TABLE_INDEX; |
| } |
| else |
| { |
| priorityTableIndex = IX_QMGR_MID_LOW_QUE_PRIORITY_TABLE_INDEX; |
| } |
| } |
| else |
| { |
| /* check if any bit related to queues in the first |
| * half of the priority table is set |
| */ |
| if (intRegVal & uppPriorityTableFirstHalfMask) |
| { |
| priorityTableIndex = IX_QMGR_MIN_UPP_QUE_PRIORITY_TABLE_INDEX; |
| } |
| else |
| { |
| priorityTableIndex = IX_QMGR_MID_UPP_QUE_PRIORITY_TABLE_INDEX; |
| } |
| } |
| |
| /* iterate following the priority table until all the bits |
| * of the interrupt register are cleared. |
| */ |
| do |
| { |
| qIndex = priorityTable[priorityTableIndex++]; |
| currDispatchQInfo = &dispatchQInfo[qIndex]; |
| intRegCheckMask = currDispatchQInfo->intRegCheckMask; |
| |
| /* If this queue caused this interrupt to be raised */ |
| if (intRegVal & intRegCheckMask) |
| { |
| /* Call the callback function for this queue */ |
| currDispatchQInfo->callback (qIndex, |
| currDispatchQInfo->callbackId); |
| #ifndef NDEBUG |
| /* Update statistics */ |
| dispatcherStats.queueStats[qIndex].callbackCnt++; |
| #endif |
| |
| /* Clear the interrupt register bit */ |
| intRegVal &= ~intRegCheckMask; |
| } |
| } |
| while(intRegVal); |
| } |
| } |
| else |
| { |
| /* A change in queue status occured during the hw interrupt |
| * register update. To maintain the interrupt consistency, it |
| * is necessary to iterate through all queues of the queue group. |
| */ |
| |
| /* Read interrupt status again */ |
| ixQMgrAqmIfQInterruptRegRead (group, &intRegValAfterWrite); |
| |
| if (IX_QMGR_QUELOW_GROUP == group) |
| { |
| priorityTableIndex = IX_QMGR_MIN_LOW_QUE_PRIORITY_TABLE_INDEX; |
| endIndex = IX_QMGR_MAX_LOW_QUE_PRIORITY_TABLE_INDEX; |
| } |
| else |
| { |
| priorityTableIndex = IX_QMGR_MIN_UPP_QUE_PRIORITY_TABLE_INDEX; |
| endIndex = IX_QMGR_MAX_UPP_QUE_PRIORITY_TABLE_INDEX; |
| } |
| |
| for ( ; priorityTableIndex<=endIndex; priorityTableIndex++) |
| { |
| qIndex = priorityTable[priorityTableIndex]; |
| currDispatchQInfo = &dispatchQInfo[qIndex]; |
| intRegCheckMask = currDispatchQInfo->intRegCheckMask; |
| |
| /* If this queue caused this interrupt to be raised */ |
| if (intRegVal & intRegCheckMask) |
| { |
| /* Call the callback function for this queue */ |
| currDispatchQInfo->callback (qIndex, |
| currDispatchQInfo->callbackId); |
| #ifndef NDEBUG |
| /* Update statistics */ |
| dispatcherStats.queueStats[qIndex].callbackCnt++; |
| #endif |
| |
| } /* if (intRegVal .. */ |
| |
| /* |
| * If interrupt bit is set in intRegValAfterWrite don't |
| * proceed as this will be caught in next interrupt |
| */ |
| else if ((intRegValAfterWrite & intRegCheckMask) == 0) |
| { |
| /* Check if an interrupt was lost for this Q */ |
| if (ixQMgrAqmIfQStatusCheck(qStatusWordsB4Write, |
| qStatusWordsAfterWrite, |
| currDispatchQInfo->statusWordOffset, |
| currDispatchQInfo->statusCheckValue, |
| currDispatchQInfo->statusMask)) |
| { |
| /* Call the callback function for this queue */ |
| currDispatchQInfo->callback (qIndex, |
| dispatchQInfo[qIndex].callbackId); |
| #ifndef NDEBUG |
| /* Update statistics */ |
| dispatcherStats.queueStats[qIndex].callbackCnt++; |
| dispatcherStats.queueStats[qIndex].intLostCallbackCnt++; |
| #endif |
| } /* if ixQMgrAqmIfQStatusCheck(.. */ |
| } /* else if ((intRegValAfterWrite ... */ |
| } /* for (priorityTableIndex=0 ... */ |
| } |
| |
| /* Rebuild the priority table if needed */ |
| if (rebuildTable) |
| { |
| ixQMgrDispatcherReBuildPriorityTable (); |
| } |
| |
| #ifndef NDEBUG |
| /* Update statistics */ |
| dispatcherStats.loopRunCnt++; |
| #endif |
| } |
| |
| |
| |
| void |
| ixQMgrDispatcherLoopRunB0 (IxQMgrDispatchGroup group) |
| { |
| UINT32 intRegVal; /* Interrupt reg val */ |
| UINT32 intRegCheckMask; /* Mask for checking interrupt bits */ |
| IxQMgrQInfo *currDispatchQInfo; |
| |
| |
| int priorityTableIndex; /* Priority table index */ |
| int qIndex; /* Current queue being processed */ |
| |
| #ifndef NDEBUG |
| IX_OSAL_ASSERT((group == IX_QMGR_QUEUPP_GROUP) || |
| (group == IX_QMGR_QUELOW_GROUP)); |
| IX_OSAL_ASSERT((group == IX_QMGR_QUEUPP_GROUP) || |
| (group == IX_QMGR_QUELOW_GROUP)); |
| #endif |
| |
| /* Read the interrupt register */ |
| ixQMgrAqmIfQInterruptRegRead (group, &intRegVal); |
| |
| |
| /* No queue has interrupt register set */ |
| if (intRegVal != 0) |
| { |
| |
| /* Write it back to clear the interrupt */ |
| ixQMgrAqmIfQInterruptRegWrite (group, intRegVal); |
| |
| /* get the first queue Id from the interrupt register value */ |
| qIndex = (BITS_PER_WORD - 1) - ixQMgrCountLeadingZeros(intRegVal); |
| |
| if (IX_QMGR_QUEUPP_GROUP == group) |
| { |
| /* Set the queue range based on the queue group to proccess */ |
| qIndex += IX_QMGR_MIN_QUEUPP_QID; |
| } |
| |
| /* check if the interrupt register contains |
| * only 1 bit set |
| * For example: |
| * intRegVal = 0x0010 |
| * currDispatchQInfo->intRegCheckMask = 0x0010 |
| * intRegVal == currDispatchQInfo->intRegCheckMask is true. |
| */ |
| currDispatchQInfo = &dispatchQInfo[qIndex]; |
| if (intRegVal == currDispatchQInfo->intRegCheckMask) |
| { |
| /* only 1 queue event triggered a notification * |
| * Call the callback function for this queue |
| */ |
| currDispatchQInfo->callback (qIndex, |
| currDispatchQInfo->callbackId); |
| #ifndef NDEBUG |
| /* Update statistics */ |
| dispatcherStats.queueStats[qIndex].callbackCnt++; |
| #endif |
| } |
| else |
| { |
| /* the event is triggered by more than 1 queue, |
| * the queue search will be starting from the beginning |
| * or the middle of the priority table |
| * |
| * the serach will end when all the bits of the interrupt |
| * register are cleared. There is no need to maintain |
| * a seperate value and test it at each iteration. |
| */ |
| if (IX_QMGR_QUELOW_GROUP == group) |
| { |
| /* check if any bit related to queues in the first |
| * half of the priority table is set |
| */ |
| if (intRegVal & lowPriorityTableFirstHalfMask) |
| { |
| priorityTableIndex = IX_QMGR_MIN_LOW_QUE_PRIORITY_TABLE_INDEX; |
| } |
| else |
| { |
| priorityTableIndex = IX_QMGR_MID_LOW_QUE_PRIORITY_TABLE_INDEX; |
| } |
| } |
| else |
| { |
| /* check if any bit related to queues in the first |
| * half of the priority table is set |
| */ |
| if (intRegVal & uppPriorityTableFirstHalfMask) |
| { |
| priorityTableIndex = IX_QMGR_MIN_UPP_QUE_PRIORITY_TABLE_INDEX; |
| } |
| else |
| { |
| priorityTableIndex = IX_QMGR_MID_UPP_QUE_PRIORITY_TABLE_INDEX; |
| } |
| } |
| |
| /* iterate following the priority table until all the bits |
| * of the interrupt register are cleared. |
| */ |
| do |
| { |
| qIndex = priorityTable[priorityTableIndex++]; |
| currDispatchQInfo = &dispatchQInfo[qIndex]; |
| intRegCheckMask = currDispatchQInfo->intRegCheckMask; |
| |
| /* If this queue caused this interrupt to be raised */ |
| if (intRegVal & intRegCheckMask) |
| { |
| /* Call the callback function for this queue */ |
| currDispatchQInfo->callback (qIndex, |
| currDispatchQInfo->callbackId); |
| #ifndef NDEBUG |
| /* Update statistics */ |
| dispatcherStats.queueStats[qIndex].callbackCnt++; |
| #endif |
| |
| /* Clear the interrupt register bit */ |
| intRegVal &= ~intRegCheckMask; |
| } |
| } |
| while(intRegVal); |
| } /*End of intRegVal == currDispatchQInfo->intRegCheckMask */ |
| } /* End of intRegVal != 0 */ |
| |
| #ifndef NDEBUG |
| /* Update statistics */ |
| dispatcherStats.loopRunCnt++; |
| #endif |
| |
| /* Rebuild the priority table if needed */ |
| if (rebuildTable) |
| { |
| ixQMgrDispatcherReBuildPriorityTable (); |
| } |
| } |
| |
| void |
| ixQMgrDispatcherLoopRunB0LLP (IxQMgrDispatchGroup group) |
| { |
| UINT32 intRegVal =0; /* Interrupt reg val */ |
| UINT32 intRegCheckMask; /* Mask for checking interrupt bits */ |
| IxQMgrQInfo *currDispatchQInfo; |
| |
| int priorityTableIndex; /* Priority table index */ |
| int qIndex; /* Current queue being processed */ |
| |
| UINT32 intRegValCopy = 0; |
| UINT32 intEnableRegVal = 0; |
| UINT8 i = 0; |
| |
| #ifndef NDEBUG |
| IX_OSAL_ASSERT((group == IX_QMGR_QUEUPP_GROUP) || |
| (group == IX_QMGR_QUELOW_GROUP)); |
| #endif |
| |
| /* Read the interrupt register */ |
| ixQMgrAqmIfQInterruptRegRead (group, &intRegVal); |
| |
| /* |
| * mask any interrupts that are not enabled |
| */ |
| ixQMgrAqmIfQInterruptEnableRegRead (group, &intEnableRegVal); |
| intRegVal &= intEnableRegVal; |
| |
| /* No queue has interrupt register set */ |
| if (intRegVal != 0) |
| { |
| if (IX_QMGR_QUELOW_GROUP == group) |
| { |
| /* |
| * As the sticky bit is set, the interrupt register will |
| * not clear if write back at this point because the condition |
| * has not been cleared. Take a copy and write back later after |
| * the condition has been cleared |
| */ |
| intRegValCopy = intRegVal; |
| } |
| else |
| { |
| /* no sticky for upper Q's, so write back now */ |
| ixQMgrAqmIfQInterruptRegWrite (group, intRegVal); |
| } |
| |
| /* get the first queue Id from the interrupt register value */ |
| qIndex = (BITS_PER_WORD - 1) - ixQMgrCountLeadingZeros(intRegVal); |
| |
| if (IX_QMGR_QUEUPP_GROUP == group) |
| { |
| /* Set the queue range based on the queue group to proccess */ |
| qIndex += IX_QMGR_MIN_QUEUPP_QID; |
| } |
| |
| /* check if the interrupt register contains |
| * only 1 bit set |
| * For example: |
| * intRegVal = 0x0010 |
| * currDispatchQInfo->intRegCheckMask = 0x0010 |
| * intRegVal == currDispatchQInfo->intRegCheckMask is true. |
| */ |
| currDispatchQInfo = &dispatchQInfo[qIndex]; |
| if (intRegVal == currDispatchQInfo->intRegCheckMask) |
| { |
| |
| /* |
| * check if Q type periodic - only lower queues can |
| * have there type set to periodic |
| */ |
| if (IX_QMGR_TYPE_REALTIME_PERIODIC == ixQMgrQTypes[qIndex]) |
| { |
| /* |
| * Disable the notifications on any sporadics |
| */ |
| for (i=0; i <= IX_QMGR_MAX_LOW_QUE_TABLE_INDEX; i++) |
| { |
| if (IX_QMGR_TYPE_REALTIME_SPORADIC == ixQMgrQTypes[i]) |
| { |
| ixQMgrNotificationDisable(i); |
| #ifndef NDEBUG |
| /* Update statistics */ |
| dispatcherStats.queueStats[i].disableCount++; |
| #endif |
| } |
| } |
| } |
| |
| currDispatchQInfo->callback (qIndex, |
| currDispatchQInfo->callbackId); |
| #ifndef NDEBUG |
| /* Update statistics */ |
| dispatcherStats.queueStats[qIndex].callbackCnt++; |
| #endif |
| } |
| else |
| { |
| /* the event is triggered by more than 1 queue, |
| * the queue search will be starting from the beginning |
| * or the middle of the priority table |
| * |
| * the serach will end when all the bits of the interrupt |
| * register are cleared. There is no need to maintain |
| * a seperate value and test it at each iteration. |
| */ |
| if (IX_QMGR_QUELOW_GROUP == group) |
| { |
| /* check if any bit related to queues in the first |
| * half of the priority table is set |
| */ |
| if (intRegVal & lowPriorityTableFirstHalfMask) |
| { |
| priorityTableIndex = |
| IX_QMGR_MIN_LOW_QUE_PRIORITY_TABLE_INDEX; |
| } |
| else |
| { |
| priorityTableIndex = |
| IX_QMGR_MID_LOW_QUE_PRIORITY_TABLE_INDEX; |
| } |
| } |
| else |
| { |
| /* check if any bit related to queues in the first |
| * half of the priority table is set |
| */ |
| if (intRegVal & uppPriorityTableFirstHalfMask) |
| { |
| priorityTableIndex = |
| IX_QMGR_MIN_UPP_QUE_PRIORITY_TABLE_INDEX; |
| } |
| else |
| { |
| priorityTableIndex = |
| IX_QMGR_MID_UPP_QUE_PRIORITY_TABLE_INDEX; |
| } |
| } |
| |
| /* iterate following the priority table until all the bits |
| * of the interrupt register are cleared. |
| */ |
| do |
| { |
| qIndex = priorityTable[priorityTableIndex++]; |
| currDispatchQInfo = &dispatchQInfo[qIndex]; |
| intRegCheckMask = currDispatchQInfo->intRegCheckMask; |
| |
| /* If this queue caused this interrupt to be raised */ |
| if (intRegVal & intRegCheckMask) |
| { |
| /* |
| * check if Q type periodic - only lower queues can |
| * have there type set to periodic. There can only be one |
| * periodic queue, so the sporadics are only disabled once. |
| */ |
| if (IX_QMGR_TYPE_REALTIME_PERIODIC == ixQMgrQTypes[qIndex]) |
| { |
| /* |
| * Disable the notifications on any sporadics |
| */ |
| for (i=0; i <= IX_QMGR_MAX_LOW_QUE_TABLE_INDEX; i++) |
| { |
| if (IX_QMGR_TYPE_REALTIME_SPORADIC == |
| ixQMgrQTypes[i]) |
| { |
| ixQMgrNotificationDisable(i); |
| /* |
| * remove from intRegVal as we don't want |
| * to service any sporadics now |
| */ |
| intRegVal &= ~dispatchQInfo[i].intRegCheckMask; |
| #ifndef NDEBUG |
| /* Update statistics */ |
| dispatcherStats.queueStats[i].disableCount++; |
| #endif |
| } |
| } |
| } |
| |
| currDispatchQInfo->callback (qIndex, |
| currDispatchQInfo->callbackId); |
| #ifndef NDEBUG |
| /* Update statistics */ |
| dispatcherStats.queueStats[qIndex].callbackCnt++; |
| #endif |
| /* Clear the interrupt register bit */ |
| intRegVal &= ~intRegCheckMask; |
| } |
| } |
| while(intRegVal); |
| } /*End of intRegVal == currDispatchQInfo->intRegCheckMask */ |
| } /* End of intRegVal != 0 */ |
| |
| #ifndef NDEBUG |
| /* Update statistics */ |
| dispatcherStats.loopRunCnt++; |
| #endif |
| |
| if ((intRegValCopy != 0) && (IX_QMGR_QUELOW_GROUP == group)) |
| { |
| /* |
| * lower groups (therefore sticky) AND at least one enabled interrupt |
| * Write back to clear the interrupt |
| */ |
| ixQMgrAqmIfQInterruptRegWrite (IX_QMGR_QUELOW_GROUP, intRegValCopy); |
| } |
| |
| /* Rebuild the priority table if needed */ |
| if (rebuildTable) |
| { |
| ixQMgrDispatcherReBuildPriorityTable (); |
| } |
| } |
| |
| PRIVATE void |
| ixQMgrDispatcherReBuildPriorityTable (void) |
| { |
| UINT32 qIndex; |
| UINT32 priority; |
| int lowQuePriorityTableIndex = IX_QMGR_MIN_LOW_QUE_PRIORITY_TABLE_INDEX; |
| int uppQuePriorityTableIndex = IX_QMGR_MIN_UPP_QUE_PRIORITY_TABLE_INDEX; |
| |
| /* Reset the rebuild flag */ |
| rebuildTable = false; |
| |
| /* initialize the mak used to identify the queues in the first half |
| * of the priority table |
| */ |
| lowPriorityTableFirstHalfMask = 0; |
| uppPriorityTableFirstHalfMask = 0; |
| |
| /* For each priority level */ |
| for(priority=0; priority<IX_QMGR_NUM_PRIORITY_LEVELS; priority++) |
| { |
| /* Foreach low queue in this priority */ |
| for(qIndex=0; qIndex<IX_QMGR_MIN_QUEUPP_QID; qIndex++) |
| { |
| if (dispatchQInfo[qIndex].priority == priority) |
| { |
| /* build the priority table bitmask which match the |
| * queues of the first half of the priority table |
| */ |
| if (lowQuePriorityTableIndex < IX_QMGR_MID_LOW_QUE_PRIORITY_TABLE_INDEX) |
| { |
| lowPriorityTableFirstHalfMask |= dispatchQInfo[qIndex].intRegCheckMask; |
| } |
| /* build the priority table */ |
| priorityTable[lowQuePriorityTableIndex++] = qIndex; |
| } |
| } |
| /* Foreach upp queue */ |
| for(qIndex=IX_QMGR_MIN_QUEUPP_QID; qIndex<=IX_QMGR_MAX_QID; qIndex++) |
| { |
| if (dispatchQInfo[qIndex].priority == priority) |
| { |
| /* build the priority table bitmask which match the |
| * queues of the first half of the priority table |
| */ |
| if (uppQuePriorityTableIndex < IX_QMGR_MID_UPP_QUE_PRIORITY_TABLE_INDEX) |
| { |
| uppPriorityTableFirstHalfMask |= dispatchQInfo[qIndex].intRegCheckMask; |
| } |
| /* build the priority table */ |
| priorityTable[uppQuePriorityTableIndex++] = qIndex; |
| } |
| } |
| } |
| } |
| |
| IxQMgrDispatcherStats* |
| ixQMgrDispatcherStatsGet (void) |
| { |
| return &dispatcherStats; |
| } |
| |
| PRIVATE void |
| dummyCallback (IxQMgrQId qId, |
| IxQMgrCallbackId cbId) |
| { |
| /* Throttle the trace message */ |
| if ((dispatchQInfo[qId].dummyCallbackCount % LOG_THROTTLE_COUNT) == 0) |
| { |
| IX_QMGR_LOG_WARNING2("--> dummyCallback: qId (%d), callbackId (%d)\n",qId,cbId); |
| } |
| dispatchQInfo[qId].dummyCallbackCount++; |
| |
| #ifndef NDEBUG |
| /* Update statistcs */ |
| dispatcherStats.queueStats[qId].intNoCallbackCnt++; |
| #endif |
| } |
| void |
| ixQMgrLLPShow (int resetStats) |
| { |
| #ifndef NDEBUG |
| UINT8 i = 0; |
| UINT32 intEnableRegVal = 0; |
| |
| printf ("Livelock statistics are printed on the fly.\n"); |
| printf ("qId Type EnableCnt DisableCnt IntEnableState Callbacks\n"); |
| printf ("=== ======== ========= ========== ============== =========\n"); |
| |
| for (i=0; i<= IX_QMGR_MAX_LOW_QUE_TABLE_INDEX; i++) |
| { |
| if (ixQMgrQTypes[i] != IX_QMGR_TYPE_REALTIME_OTHER) |
| { |
| printf (" %2d ", i); |
| |
| if (ixQMgrQTypes[i] == IX_QMGR_TYPE_REALTIME_SPORADIC) |
| { |
| printf ("Sporadic"); |
| } |
| else |
| { |
| printf ("Periodic"); |
| } |
| |
| |
| ixQMgrAqmIfQInterruptEnableRegRead (IX_QMGR_QUELOW_GROUP, |
| &intEnableRegVal); |
| |
| |
| intEnableRegVal &= dispatchQInfo[i].intRegCheckMask; |
| intEnableRegVal = intEnableRegVal >> i; |
| |
| printf (" %10d %10d %10d %10d\n", |
| dispatcherStats.queueStats[i].enableCount, |
| dispatcherStats.queueStats[i].disableCount, |
| intEnableRegVal, |
| dispatcherStats.queueStats[i].callbackCnt); |
| |
| if (resetStats) |
| { |
| dispatcherStats.queueStats[i].enableCount = |
| dispatcherStats.queueStats[i].disableCount = |
| dispatcherStats.queueStats[i].callbackCnt = 0; |
| } |
| } |
| } |
| #else |
| IX_QMGR_LOG0("Livelock Prevention statistics are only collected in debug mode\n"); |
| #endif |
| } |
| |
| void |
| ixQMgrPeriodicDone (void) |
| { |
| UINT32 i = 0; |
| UINT32 ixQMgrLockKey = 0; |
| |
| /* |
| * for the lower queues |
| */ |
| for (i=0; i <= IX_QMGR_MAX_LOW_QUE_TABLE_INDEX; i++) |
| { |
| /* |
| * check for sporadics |
| */ |
| if (IX_QMGR_TYPE_REALTIME_SPORADIC == ixQMgrQTypes[i]) |
| { |
| /* |
| * enable any sporadics |
| */ |
| ixQMgrLockKey = ixOsalIrqLock(); |
| ixQMgrAqmIfQInterruptEnable(i); |
| ixOsalIrqUnlock(ixQMgrLockKey); |
| #ifndef NDEBUG |
| /* |
| * Update statistics |
| */ |
| dispatcherStats.queueStats[i].enableCount++; |
| dispatcherStats.queueStats[i].notificationEnabled = true; |
| #endif |
| } |
| } |
| } |
| IX_STATUS |
| ixQMgrCallbackTypeSet (IxQMgrQId qId, |
| IxQMgrType type) |
| { |
| UINT32 ixQMgrLockKey = 0; |
| IxQMgrType ixQMgrOldType =0; |
| |
| #ifndef NDEBUG |
| if (!ixQMgrQIsConfigured(qId)) |
| { |
| return IX_QMGR_Q_NOT_CONFIGURED; |
| } |
| if (qId >= IX_QMGR_MIN_QUEUPP_QID) |
| { |
| return IX_QMGR_PARAMETER_ERROR; |
| } |
| if(!IX_QMGR_DISPATCHER_CALLBACK_TYPE_CHECK(type)) |
| { |
| return IX_QMGR_PARAMETER_ERROR; |
| } |
| #endif |
| |
| ixQMgrOldType = ixQMgrQTypes[qId]; |
| ixQMgrQTypes[qId] = type; |
| |
| /* |
| * check if Q has been changed from type SPORADIC |
| */ |
| if (IX_QMGR_TYPE_REALTIME_SPORADIC == ixQMgrOldType) |
| { |
| /* |
| * previously Q was a SPORADIC, this means that LLP |
| * might have had it disabled. enable it now. |
| */ |
| ixQMgrLockKey = ixOsalIrqLock(); |
| ixQMgrAqmIfQInterruptEnable(qId); |
| ixOsalIrqUnlock(ixQMgrLockKey); |
| |
| #ifndef NDEBUG |
| /* |
| * Update statistics |
| */ |
| dispatcherStats.queueStats[qId].enableCount++; |
| #endif |
| } |
| |
| return IX_SUCCESS; |
| } |
| |
| IX_STATUS |
| ixQMgrCallbackTypeGet (IxQMgrQId qId, |
| IxQMgrType *type) |
| { |
| #ifndef NDEBUG |
| if (!ixQMgrQIsConfigured(qId)) |
| { |
| return IX_QMGR_Q_NOT_CONFIGURED; |
| } |
| if (qId >= IX_QMGR_MIN_QUEUPP_QID) |
| { |
| return IX_QMGR_PARAMETER_ERROR; |
| } |
| if(type == NULL) |
| { |
| return IX_QMGR_PARAMETER_ERROR; |
| } |
| #endif |
| |
| *type = ixQMgrQTypes[qId]; |
| return IX_SUCCESS; |
| } |