upstream u-boot with additional patches for our devices/boards:
https://lists.denx.de/pipermail/u-boot/2017-March/282789.html (AXP crashes) ;
Gbit ethernet patch for some LIME2 revisions ;
with SPI flash support
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1348 lines
40 KiB
1348 lines
40 KiB
19 years ago
|
/**
|
||
|
* @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
|
||
|
* Redistribution and use in source and binary forms, with or without
|
||
|
* modification, are permitted provided that the following conditions
|
||
|
* are met:
|
||
|
* 1. Redistributions of source code must retain the above copyright
|
||
|
* notice, this list of conditions and the following disclaimer.
|
||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||
|
* notice, this list of conditions and the following disclaimer in the
|
||
|
* documentation and/or other materials provided with the distribution.
|
||
|
* 3. Neither the name of the Intel Corporation nor the names of its contributors
|
||
|
* may be used to endorse or promote products derived from this software
|
||
|
* without specific prior written permission.
|
||
|
*
|
||
|
* @par
|
||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
|
||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||
|
* SUCH DAMAGE.
|
||
|
*
|
||
|
* @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 interupt 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;
|
||
|
IxQMgrQInfo *q;
|
||
|
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++)
|
||
|
{
|
||
|
q = &dispatchQInfo[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;
|
||
|
}
|