u-boot/drivers/net/npe/IxQMgrDispatcher.c
Wolfgang Denk cb3761ea99 SPDX-License-Identifier: convert BSD-3-Clause files
Signed-off-by: Wolfgang Denk <wd@denx.de>
[trini Don't remove some copyrights by accident]
Signed-off-by: Tom Rini <trini@ti.com>
2013-08-19 15:45:35 -04:00

1320 lines
38 KiB
C

/**
* @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;
}