diff options
| author | Tom Rini <trini@ti.com> | 2012-09-27 12:06:07 -0700 | 
|---|---|---|
| committer | Tom Rini <trini@ti.com> | 2012-09-27 12:06:07 -0700 | 
| commit | cec2655c3b3b86f14a6a5c2cbb01833f7e3974be (patch) | |
| tree | 8428f50b579b3656fd24056ae3c6304f58aee620 /drivers/net/npe/IxQMgrDispatcher.c | |
| parent | c57b953da923c6402afc1c890c21fdc7d5a2cc4a (diff) | |
| parent | ee0f60df0b71092cd632fc6651f4157a2d252598 (diff) | |
| download | olio-uboot-2014.01-cec2655c3b3b86f14a6a5c2cbb01833f7e3974be.tar.xz olio-uboot-2014.01-cec2655c3b3b86f14a6a5c2cbb01833f7e3974be.zip | |
Merge branch 'master' of git://git.denx.de/u-boot-net
Diffstat (limited to 'drivers/net/npe/IxQMgrDispatcher.c')
| -rw-r--r-- | drivers/net/npe/IxQMgrDispatcher.c | 1344 | 
1 files changed, 1344 insertions, 0 deletions
| diff --git a/drivers/net/npe/IxQMgrDispatcher.c b/drivers/net/npe/IxQMgrDispatcher.c new file mode 100644 index 000000000..9cb143998 --- /dev/null +++ b/drivers/net/npe/IxQMgrDispatcher.c @@ -0,0 +1,1344 @@ +/** + * @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 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; +} |