/**
 * @file IxEthAccCommon.c
 *
 * @author Intel Corporation
 * @date 12-Feb-2002
 *
 * @brief This file contains the implementation common support routines for the component
 *
 * Design Notes:
 *
 * @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 --
 */

/*
 * Component header files
 */

#include "IxOsal.h"
#include "IxEthAcc.h"
#include "IxEthDB.h"
#include "IxNpeMh.h"
#include "IxEthDBPortDefs.h"
#include "IxFeatureCtrl.h"
#include "IxEthAcc_p.h"
#include "IxEthAccQueueAssign_p.h"

#include "IxEthAccDataPlane_p.h"
#include "IxEthAccMii_p.h"

/**
 * @addtogroup IxEthAccPri
 *@{
 */

extern IxEthAccInfo   ixEthAccDataInfo;

/**
 *
 * @brief Maximum number of RX queues set to be the maximum number
 * of traffic calsses.
 *
 */
#define IX_ETHACC_MAX_RX_QUEUES \
      (IX_ETH_DB_QOS_TRAFFIC_CLASS_7_RX_QUEUE_PROPERTY \
      - IX_ETH_DB_QOS_TRAFFIC_CLASS_0_RX_QUEUE_PROPERTY \
      + 1)

/**
 *
 * @brief Maximum number of 128 entry RX queues
 *
 */
#define IX_ETHACC_MAX_LARGE_RX_QUEUES 4

/**
 *
 * @brief Data structure template for Default RX Queues
 *
 */
IX_ETH_ACC_PRIVATE
IxEthAccQregInfo ixEthAccQmgrRxDefaultTemplate =
  {
    IX_ETH_ACC_RX_FRAME_ETH_Q,	     /**< Queue ID */
    "Eth Rx Q",
    ixEthRxFrameQMCallback,          /**< Functional callback */
    (IxQMgrCallbackId) 0,	     /**< Callback tag	      */
    IX_QMGR_Q_SIZE128,		     /**< Allocate Max Size Q */
    IX_QMGR_Q_ENTRY_SIZE1,	     /**< Queue Entry Sizes - all Q entries are single word entries   */
    TRUE,			     /**< Enable Q notification at startup */
    IX_ETH_ACC_RX_FRAME_ETH_Q_SOURCE,/**< Q Condition to drive callback   */
    IX_QMGR_Q_WM_LEVEL0,	     /**< Q Low water mark */
    IX_QMGR_Q_WM_LEVEL1,	     /**< Q High water mark - needed by NPE */
  };

/**
 *
 * @brief Data structure template for Small RX Queues
 *
 */
IX_ETH_ACC_PRIVATE
IxEthAccQregInfo ixEthAccQmgrRxSmallTemplate =
  {
    IX_ETH_ACC_RX_FRAME_ETH_Q,	     /**< Queue ID */
    "Eth Rx Q",
    ixEthRxFrameQMCallback,          /**< Functional callback */
    (IxQMgrCallbackId) 0,	     /**< Callback tag	      */
    IX_QMGR_Q_SIZE64,		     /**< Allocate Smaller Q */
    IX_QMGR_Q_ENTRY_SIZE1,	     /**< Queue Entry Sizes - all Q entries are single word entries   */
    TRUE,			     /**< Enable Q notification at startup */
    IX_ETH_ACC_RX_FRAME_ETH_Q_SOURCE,/**< Q Condition to drive callback   */
    IX_QMGR_Q_WM_LEVEL0,	     /**< Q Low water mark */
    IX_QMGR_Q_WM_LEVEL1,	     /**< Q High water mark - needed by NPE */
  };


/**
 *
 * @brief Data structure used to register & initialize the Queues
 *
 */
IX_ETH_ACC_PRIVATE
IxEthAccQregInfo ixEthAccQmgrStaticInfo[]=
{
  {
    IX_ETH_ACC_RX_FREE_BUFF_ENET0_Q,
    "Eth Rx Fr Q 1",
    ixEthRxFreeQMCallback,
    (IxQMgrCallbackId) IX_ETH_PORT_1,
    IX_QMGR_Q_SIZE128,		     /**< Allocate Max Size Q */
    IX_QMGR_Q_ENTRY_SIZE1,	     /**< Queue Entry Sizes - all Q entries are single word entries   */
    FALSE,			     /**< Disable Q notification at startup */
    IX_ETH_ACC_RX_FREE_BUFF_ENET0_Q_SOURCE, /**< Q Condition to drive callback  */
    IX_QMGR_Q_WM_LEVEL0,	     /***< Q Low water mark */
    IX_QMGR_Q_WM_LEVEL64,	     /**< Q High water mark */
  },

  {
    IX_ETH_ACC_RX_FREE_BUFF_ENET1_Q,
    "Eth Rx Fr Q 2",
    ixEthRxFreeQMCallback,
    (IxQMgrCallbackId) IX_ETH_PORT_2,
    IX_QMGR_Q_SIZE128,		     /**< Allocate Max Size Q */
    IX_QMGR_Q_ENTRY_SIZE1,	     /**< Queue Entry Sizes - all Q entries are single word entries   */
    FALSE,			     /**< Disable Q notification at startup */
    IX_ETH_ACC_RX_FREE_BUFF_ENET1_Q_SOURCE,  /**< Q Condition to drive callback  */
    IX_QMGR_Q_WM_LEVEL0,	     /**< Q Low water mark */
    IX_QMGR_Q_WM_LEVEL64,	     /**< Q High water mark */
  },
#ifdef __ixp46X
  {
    IX_ETH_ACC_RX_FREE_BUFF_ENET2_Q,
    "Eth Rx Fr Q 3",
    ixEthRxFreeQMCallback,
    (IxQMgrCallbackId) IX_ETH_PORT_3,
    IX_QMGR_Q_SIZE128,		     /**< Allocate Max Size Q */
    IX_QMGR_Q_ENTRY_SIZE1,	     /**< Queue Entry Sizes - all Q entries are single word entries   */
    FALSE,			     /**< Disable Q notification at startup */
    IX_ETH_ACC_RX_FREE_BUFF_ENET2_Q_SOURCE,  /**< Q Condition to drive callback  */
    IX_QMGR_Q_WM_LEVEL0,	     /**< Q Low water mark */
    IX_QMGR_Q_WM_LEVEL64,	     /**< Q High water mark */
  },
#endif
  {
     IX_ETH_ACC_TX_FRAME_ENET0_Q,
    "Eth Tx Q 1",
     ixEthTxFrameQMCallback,
     (IxQMgrCallbackId) IX_ETH_PORT_1,
    IX_QMGR_Q_SIZE128,		     /**< Allocate Max Size Q */
    IX_QMGR_Q_ENTRY_SIZE1,	     /**< Queue Entry Sizes - all Q entries are single word entries   */
    FALSE,			     /**< Disable Q notification at startup */
    IX_ETH_ACC_TX_FRAME_ENET0_Q_SOURCE,	 /**< Q Condition to drive callback  */
    IX_QMGR_Q_WM_LEVEL0,	     /**< Q Low water mark */
    IX_QMGR_Q_WM_LEVEL64,	     /**< Q High water mark */
  },

  {
     IX_ETH_ACC_TX_FRAME_ENET1_Q,
    "Eth Tx Q 2",
     ixEthTxFrameQMCallback,
     (IxQMgrCallbackId) IX_ETH_PORT_2,
    IX_QMGR_Q_SIZE128,		     /**< Allocate Max Size Q */
    IX_QMGR_Q_ENTRY_SIZE1,	     /**< Queue Entry Sizes - all Q entries are single word entries   */
    FALSE,			     /**< Disable Q notification at startup */
    IX_ETH_ACC_TX_FRAME_ENET1_Q_SOURCE,	     /**< Q Condition to drive callback  */
    IX_QMGR_Q_WM_LEVEL0,	     /**< Q Low water mark */
    IX_QMGR_Q_WM_LEVEL64,	     /**< Q High water mark */
  },
#ifdef __ixp46X
  {
     IX_ETH_ACC_TX_FRAME_ENET2_Q,
    "Eth Tx Q 3",
     ixEthTxFrameQMCallback,
     (IxQMgrCallbackId) IX_ETH_PORT_3,
    IX_QMGR_Q_SIZE128,		     /**< Allocate Max Size Q */
    IX_QMGR_Q_ENTRY_SIZE1,	     /** Queue Entry Sizes - all Q entries are single ord entries   */
    FALSE,			     /** Disable Q notification at startup */
    IX_ETH_ACC_TX_FRAME_ENET2_Q_SOURCE,	     /** Q Condition to drive callback  */
    IX_QMGR_Q_WM_LEVEL0,	     /* No queues use almost empty */
    IX_QMGR_Q_WM_LEVEL64,	      /** Q High water mark - needed used  */
  },
#endif
  {
     IX_ETH_ACC_TX_FRAME_DONE_ETH_Q,
    "Eth Tx Done Q",
     ixEthTxFrameDoneQMCallback,
     (IxQMgrCallbackId) 0,
    IX_QMGR_Q_SIZE128,		     /**< Allocate Max Size Q */
    IX_QMGR_Q_ENTRY_SIZE1,	     /**< Queue Entry Sizes - all Q entries are single word entries   */
    TRUE,			     /**< Enable Q notification at startup */
    IX_ETH_ACC_TX_FRAME_DONE_ETH_Q_SOURCE, /**< Q Condition to drive callback  */
    IX_QMGR_Q_WM_LEVEL0,	     /**< Q Low water mark */
    IX_QMGR_Q_WM_LEVEL2,	     /**< Q High water mark - needed by NPE */
  },

  {  /* Null Termination entry
      */
     (IxQMgrQId)0,
     (char *) NULL,
     (IxQMgrCallback) NULL,
     (IxQMgrCallbackId) 0,
     0,
     0,
     0,
     0,
     0,
     0
  }

};

/**
 *
 * @brief Data structure used to register & initialize the Queues
 *
 * The structure will be filled at run time depending on the NPE
 * image already loaded and the QoS configured in ethDB.
 *
 */
IX_ETH_ACC_PRIVATE
IxEthAccQregInfo ixEthAccQmgrRxQueuesInfo[IX_ETHACC_MAX_RX_QUEUES+1]=
{
  {  /* PlaceHolder for rx queues
      * depending on the QoS configured
      */
     (IxQMgrQId)0,
     (char *) NULL,
     (IxQMgrCallback) NULL,
     (IxQMgrCallbackId) 0,
     0,
     0,
     0,
     0,
     0,
     0
  },

  {  /* PlaceHolder for rx queues
      * depending on the QoS configured
      */
     (IxQMgrQId)0,
     (char *) NULL,
     (IxQMgrCallback) NULL,
     (IxQMgrCallbackId) 0,
     0,
     0,
     0,
     0,
     0,
     0
  },

  {  /* PlaceHolder for rx queues
      * depending on the QoS configured
      */
     (IxQMgrQId)0,
     (char *) NULL,
     (IxQMgrCallback) NULL,
     (IxQMgrCallbackId) 0,
     0,
     0,
     0,
     0,
     0,
     0
  },

  {  /* PlaceHolder for rx queues
      * depending on the QoS configured
      */
     (IxQMgrQId)0,
     (char *) NULL,
     (IxQMgrCallback) NULL,
     (IxQMgrCallbackId) 0,
     0,
     0,
     0,
     0,
     0,
     0
  },

  {  /* PlaceHolder for rx queues
      * depending on the QoS configured
      */
     (IxQMgrQId)0,
     (char *) NULL,
     (IxQMgrCallback) NULL,
     (IxQMgrCallbackId) 0,
     0,
     0,
     0,
     0,
     0,
     0
  },

  {  /* PlaceHolder for rx queues
      * depending on the QoS configured
      */
      (IxQMgrQId)0,
     (char *) NULL,
     (IxQMgrCallback) NULL,
     (IxQMgrCallbackId) 0,
     0,
     0,
     0,
     0,
     0,
     0
  },

  {  /* PlaceHolder for rx queues
      * depending on the QoS configured
      */
     (IxQMgrQId)0,
     (char *) NULL,
     (IxQMgrCallback) NULL,
     (IxQMgrCallbackId) 0,
     0,
     0,
     0,
     0,
     0,
     0
  },

  {  /* PlaceHolder for rx queues
      * depending on the QoS configured
      */
     (IxQMgrQId)0,
     (char *) NULL,
     (IxQMgrCallback) NULL,
     (IxQMgrCallbackId) 0,
     0,
     0,
     0,
     0,
     0,
     0
  },

  {  /* Null Termination entry
      */
     (IxQMgrQId)0,
     (char *) NULL,
     (IxQMgrCallback) NULL,
     (IxQMgrCallbackId) 0,
     0,
     0,
     0,
     0,
     0,
     0
  }

};

/* forward declarations */
IX_ETH_ACC_PRIVATE IxEthAccStatus
ixEthAccQMgrQueueSetup(IxEthAccQregInfo *qInfoDes);

/**
 * @fn ixEthAccQMgrQueueSetup(void)
 *
 * @brief Setup one queue and its event, and register the callback required
 * by this component to the QMgr
 *
 * @internal
 */
IX_ETH_ACC_PRIVATE IxEthAccStatus
ixEthAccQMgrQueueSetup(IxEthAccQregInfo *qInfoDes)
{
    /*
     * Configure each Q.
     */
    if ( ixQMgrQConfig( qInfoDes->qName,
			qInfoDes->qId,
			qInfoDes->qSize,
			qInfoDes->qWords) != IX_SUCCESS)
    {
	return IX_ETH_ACC_FAIL;
    }

    if ( ixQMgrWatermarkSet( qInfoDes->qId,
			     qInfoDes->AlmostEmptyThreshold,
			     qInfoDes->AlmostFullThreshold
			     ) != IX_SUCCESS)
    {
	return IX_ETH_ACC_FAIL;
    }

    /*
     * Set dispatcher priority.
     */
    if ( ixQMgrDispatcherPrioritySet( qInfoDes->qId,
				      IX_ETH_ACC_QM_QUEUE_DISPATCH_PRIORITY)
	 != IX_SUCCESS)
    {
	return IX_ETH_ACC_FAIL;
    }

    /*
     * Register callbacks for each Q.
     */
    if ( ixQMgrNotificationCallbackSet(qInfoDes->qId,
				       qInfoDes->qCallback,
				       qInfoDes->callbackTag)
	 != IX_SUCCESS )
    {
	return IX_ETH_ACC_FAIL;
    }

    /*
     * Set notification condition for Q
     */
    if ( qInfoDes->qNotificationEnableAtStartup == TRUE )
    {
	if (   ixQMgrNotificationEnable(qInfoDes->qId,
					qInfoDes->qConditionSource)
	       != IX_SUCCESS )
	{
	    return IX_ETH_ACC_FAIL;
	}
    }

    return(IX_ETH_ACC_SUCCESS);
}

/**
 * @fn ixEthAccQMgrQueuesConfig(void)
 *
 * @brief Setup all the queues and register all callbacks required
 * by this component to the QMgr
 *
 * The RxFree queues, tx queues, rx queues are configured statically
 *
 * Rx queues configuration is driven by QoS setup.
 * Many Rx queues may be required when QoS is enabled (this depends
 * on IxEthDB setup and the images being downloaded). The configuration
 * of the rxQueues is done in many steps as follows:
 *
 * @li select all Rx queues as configured by ethDB for all ports
 * @li sort the queues by traffic class
 * @li build the priority dependency for all queues
 * @li fill the configuration for all rx queues
 * @li configure all statically configured queues
 * @li configure all dynamically configured queues
 *
 * @param none
 *
 * @return IxEthAccStatus
 *
 * @internal
 */
IX_ETH_ACC_PUBLIC
IxEthAccStatus ixEthAccQMgrQueuesConfig(void)
{
    struct
    {
	int npeCount;
	UINT32 npeId;
	IxQMgrQId qId;
	IxEthDBProperty trafficClass;
    } rxQueues[IX_ETHACC_MAX_RX_QUEUES];

    UINT32 rxQueue = 0;
    UINT32 rxQueueCount = 0;
    IxQMgrQId ixQId =IX_QMGR_MAX_NUM_QUEUES;
    IxEthDBStatus ixEthDBStatus = IX_ETH_DB_SUCCESS;
    IxEthDBPortId ixEthDbPortId = 0;
    IxEthAccPortId ixEthAccPortId = 0;
    UINT32 ixNpeId = 0;
    UINT32 ixHighestNpeId = 0;
    UINT32 sortIterations = 0;
    IxEthAccStatus ret = IX_ETH_ACC_SUCCESS;
    IxEthAccQregInfo *qInfoDes = NULL;
    IxEthDBProperty ixEthDBTrafficClass = IX_ETH_DB_QOS_TRAFFIC_CLASS_0_RX_QUEUE_PROPERTY;
    IxEthDBPropertyType ixEthDBPropertyType = IX_ETH_DB_INTEGER_PROPERTY;
    UINT32 ixEthDBParameter = 0;
    BOOL completelySorted = FALSE;

    /* Fill the corspondance between ports and queues
     * This defines the mapping from port to queue Ids.
     */

    ixEthAccPortData[IX_ETH_PORT_1].ixEthAccRxData.rxFreeQueue
	= IX_ETH_ACC_RX_FREE_BUFF_ENET0_Q;
    ixEthAccPortData[IX_ETH_PORT_2].ixEthAccRxData.rxFreeQueue
	= IX_ETH_ACC_RX_FREE_BUFF_ENET1_Q;
#ifdef __ixp46X
    ixEthAccPortData[IX_ETH_PORT_3].ixEthAccRxData.rxFreeQueue
	= IX_ETH_ACC_RX_FREE_BUFF_ENET2_Q;
#endif
    ixEthAccPortData[IX_ETH_PORT_1].ixEthAccTxData.txQueue
	= IX_ETH_ACC_TX_FRAME_ENET0_Q;
    ixEthAccPortData[IX_ETH_PORT_2].ixEthAccTxData.txQueue
	= IX_ETH_ACC_TX_FRAME_ENET1_Q;
#ifdef __ixp46X
    ixEthAccPortData[IX_ETH_PORT_3].ixEthAccTxData.txQueue
	= IX_ETH_ACC_TX_FRAME_ENET2_Q;
#endif
    /* Fill the corspondance between ports and NPEs
     * This defines the mapping from port to npeIds.
     */

    ixEthAccPortData[IX_ETH_PORT_1].npeId = IX_NPEMH_NPEID_NPEB;
    ixEthAccPortData[IX_ETH_PORT_2].npeId = IX_NPEMH_NPEID_NPEC;
#ifdef __ixp46X
    ixEthAccPortData[IX_ETH_PORT_3].npeId = IX_NPEMH_NPEID_NPEA;
#endif
    /* set the default rx scheduling discipline */
    ixEthAccDataInfo.schDiscipline = FIFO_NO_PRIORITY;

    /*
     * Queue Selection step:
     *
     * The following code selects all the queues and build
     * a temporary array which contains for each queue
     * - the queue Id,
     * - the highest traffic class (in case of many
     * priorities configured for the same queue on different
     * ports)
     * - the number of different Npes which are
     * configured to write to this queue.
     *
     * The output of this loop is a temporary array of RX queues
     * in any order.
     *
     */
#ifdef CONFIG_IXP425_COMPONENT_ETHDB
    for (ixEthAccPortId = 0;
	 (ixEthAccPortId < IX_ETH_ACC_NUMBER_OF_PORTS)
	     && (ret == IX_ETH_ACC_SUCCESS);
	 ixEthAccPortId++)
    {
	/* map between ethDb and ethAcc port Ids */
	ixEthDbPortId = (IxEthDBPortId)ixEthAccPortId;

	/* map between npeId and ethAcc port Ids */
	ixNpeId = IX_ETH_ACC_PORT_TO_NPE_ID(ixEthAccPortId);

	/* Iterate thru the different priorities */
	for (ixEthDBTrafficClass = IX_ETH_DB_QOS_TRAFFIC_CLASS_0_RX_QUEUE_PROPERTY;
	     ixEthDBTrafficClass <= IX_ETH_DB_QOS_TRAFFIC_CLASS_7_RX_QUEUE_PROPERTY;
	     ixEthDBTrafficClass++)
	{
	    ixEthDBStatus = ixEthDBFeaturePropertyGet(
	      ixEthDbPortId,
	      IX_ETH_DB_VLAN_QOS,
	      ixEthDBTrafficClass,
	      &ixEthDBPropertyType,
	      (void *)&ixEthDBParameter);

	    if (ixEthDBStatus == IX_ETH_DB_SUCCESS)
	    {
		/* This port and QoS class are mapped to
		 * a RX queue.
		 */
		if (ixEthDBPropertyType == IX_ETH_DB_INTEGER_PROPERTY)
		{
		    /* remember the highest npe Id supporting ethernet */
		    if (ixNpeId > ixHighestNpeId)
		    {
			ixHighestNpeId = ixNpeId;
		    }

		    /* search the queue in the list of queues
		     * already used by an other port or QoS
		     */
		    for (rxQueue = 0;
			 rxQueue < rxQueueCount;
			 rxQueue++)
		    {
			if (rxQueues[rxQueue].qId == (IxQMgrQId)ixEthDBParameter)
			{
			    /* found an existing setup, update the number of ports
			     * for this queue if the port maps to
			     * a different NPE.
			     */
			    if (rxQueues[rxQueue].npeId != ixNpeId)
			    {
				rxQueues[rxQueue].npeCount++;
				rxQueues[rxQueue].npeId = ixNpeId;
			    }
			    /* get the highest traffic class for this queue */
			    if (rxQueues[rxQueue].trafficClass > ixEthDBTrafficClass)
			    {
				rxQueues[rxQueue].trafficClass = ixEthDBTrafficClass;
			    }
			    break;
			}
		    }
		    if (rxQueue == rxQueueCount)
		    {
			/* new queue not found in the current list,
			 * add a new entry.
			 */
			IX_OSAL_ASSERT(rxQueueCount < IX_ETHACC_MAX_RX_QUEUES);
			rxQueues[rxQueueCount].qId = ixEthDBParameter;
			rxQueues[rxQueueCount].npeCount = 1;
			rxQueues[rxQueueCount].npeId = ixNpeId;
			rxQueues[rxQueueCount].trafficClass = ixEthDBTrafficClass;
			rxQueueCount++;
		    }
		}
		else
		{
		    /* unexpected property type (not Integer) */
		    ret = IX_ETH_ACC_FAIL;

                    IX_ETH_ACC_WARNING_LOG("ixEthAccQMgrQueuesConfig: unexpected property type returned by EthDB\n", 0, 0, 0, 0, 0, 0);

		    /* no point to continue to iterate */
		    break;
		}
	    }
	    else
	    {
		/* No Rx queue configured for this port
		 * and this traffic class. Do nothing.
		 */
	    }
	}

        /* notify EthDB that queue initialization is complete and traffic class allocation is frozen */
        ixEthDBFeaturePropertySet(ixEthDbPortId,
            IX_ETH_DB_VLAN_QOS,
            IX_ETH_DB_QOS_QUEUE_CONFIGURATION_COMPLETE,
            NULL /* ignored */);
    }

#else

    ixNpeId = IX_ETH_ACC_PORT_TO_NPE_ID(ixEthAccPortId);
    rxQueues[0].qId = 4;
    rxQueues[0].npeCount = 1;
    rxQueues[0].npeId = ixNpeId;
    rxQueues[0].trafficClass = IX_ETH_DB_QOS_TRAFFIC_CLASS_0_RX_QUEUE_PROPERTY;
    rxQueueCount++;

#endif

    /* check there is at least 1 rx queue : there is no point
     * to continue if there is no rx queue configured
     */
    if ((rxQueueCount == 0) || (ret == IX_ETH_ACC_FAIL))
    {
        IX_ETH_ACC_WARNING_LOG("ixEthAccQMgrQueuesConfig: no queues configured, bailing out\n", 0, 0, 0, 0, 0, 0);
	return (IX_ETH_ACC_FAIL);
    }

    /* Queue sort step:
     *
     * Re-order the array of queues by decreasing traffic class
     * using a bubble sort. (trafficClass 0 is the lowest
     * priority traffic, trafficClass 7 is the highest priority traffic)
     *
     * Primary sort order is traffic class
     * Secondary sort order is npeId
     *
     * Note that a bubble sort algorithm is not very efficient when
     * the number of queues grows . However, this is not a very bad choice
     * considering the very small number of entries to sort. Also, bubble
     * sort is extremely fast when the list is already sorted.
     *
     * The output of this loop is a sorted array of queues.
     *
     */
    sortIterations = 0;
    do
    {
	sortIterations++;
	completelySorted = TRUE;
	for (rxQueue = 0;
	     rxQueue < rxQueueCount - sortIterations;
	     rxQueue++)
	{
	    /* compare adjacent elements */
	    if ((rxQueues[rxQueue].trafficClass <
		rxQueues[rxQueue+1].trafficClass)
		|| ((rxQueues[rxQueue].trafficClass ==
		     rxQueues[rxQueue+1].trafficClass)
		    &&(rxQueues[rxQueue].npeId <
		       rxQueues[rxQueue+1].npeId)))
	    {
		/* swap adjacent elements */
		int npeCount = rxQueues[rxQueue].npeCount;
		UINT32 npeId = rxQueues[rxQueue].npeId;
		IxQMgrQId qId = rxQueues[rxQueue].qId;
		IxEthDBProperty trafficClass = rxQueues[rxQueue].trafficClass;
		rxQueues[rxQueue].npeCount = rxQueues[rxQueue+1].npeCount;
		rxQueues[rxQueue].npeId = rxQueues[rxQueue+1].npeId;
		rxQueues[rxQueue].qId = rxQueues[rxQueue+1].qId;
		rxQueues[rxQueue].trafficClass = rxQueues[rxQueue+1].trafficClass;
		rxQueues[rxQueue+1].npeCount = npeCount;
		rxQueues[rxQueue+1].npeId = npeId;
		rxQueues[rxQueue+1].qId = qId;
		rxQueues[rxQueue+1].trafficClass = trafficClass;
		completelySorted = FALSE;
	    }
	}
    }
    while (!completelySorted);

    /* Queue traffic class list:
     *
     * Fill an array of rx queues linked by ascending traffic classes.
     *
     * If the queues are configured as follows
     *   qId 6 -> traffic class 0 (lowest)
     *   qId 7 -> traffic class 0
     *   qId 8 -> traffic class 6
     *   qId 12 -> traffic class 7 (highest)
     *
     * Then the output of this loop will be
     *
     * higherPriorityQueue[6] = 8
     * higherPriorityQueue[7] = 8
     * higherPriorityQueue[8] = 12
     * higherPriorityQueue[12] = Invalid queueId
     * higherPriorityQueue[...] = Invalid queueId
     *
     * Note that this queue ordering does not handle all possibilities
     * that could result from different rules associated with different
     * ports, and inconsistencies in the rules. In all cases, the
     * output of this  algorithm is a simple linked list of queues,
     * without closed circuit.

     * This list is implemented as an array with invalid values initialized
     * with an "invalid" queue id which is the maximum number of queues.
     *
     */

    /*
     * Initialise the rx queue list.
     */
    for (rxQueue = 0; rxQueue < IX_QMGR_MAX_NUM_QUEUES; rxQueue++)
    {
	ixEthAccDataInfo.higherPriorityQueue[rxQueue] = IX_QMGR_MAX_NUM_QUEUES;
    }

    /* build the linked list for this NPE.
     */
    for (ixNpeId = 0;
	 ixNpeId <= ixHighestNpeId;
	 ixNpeId++)
    {
	/* iterate thru the sorted list of queues
	 */
	ixQId = IX_QMGR_MAX_NUM_QUEUES;
	for (rxQueue = 0;
	     rxQueue < rxQueueCount;
	     rxQueue++)
	{
	    if (rxQueues[rxQueue].npeId == ixNpeId)
	    {
		ixEthAccDataInfo.higherPriorityQueue[rxQueues[rxQueue].qId] = ixQId;
		/* iterate thru queues with the same traffic class
		 * than the current queue. (queues are ordered by descending
		 * traffic classes and npeIds).
		 */
		while ((rxQueue < rxQueueCount - 1)
		       && (rxQueues[rxQueue].trafficClass
			   == rxQueues[rxQueue+1].trafficClass)
		       && (ixNpeId == rxQueues[rxQueue].npeId))
		{
		    rxQueue++;
		    ixEthAccDataInfo.higherPriorityQueue[rxQueues[rxQueue].qId] = ixQId;
		}
		ixQId = rxQueues[rxQueue].qId;
	    }
	}
    }

    /* point on the first dynamic queue description */
    qInfoDes = ixEthAccQmgrRxQueuesInfo;

    /* update the list of queues with the rx queues */
    for (rxQueue = 0;
	 (rxQueue < rxQueueCount) && (ret == IX_ETH_ACC_SUCCESS);
	 rxQueue++)
    {
	/* Don't utilize more than IX_ETHACC_MAX_LARGE_RX_QUEUES queues
	 * with the full 128 entries.  For the lower priority queues, use
	 * a smaller number of entries.  This ensures queue resources
	 * remain available for other components.
	 */
	if( (rxQueueCount > IX_ETHACC_MAX_LARGE_RX_QUEUES) &&
	    (rxQueue < rxQueueCount - IX_ETHACC_MAX_LARGE_RX_QUEUES) )
	{
	    /* add the small RX Queue setup template to the list of queues */
	    memcpy(qInfoDes, &ixEthAccQmgrRxSmallTemplate, sizeof(*qInfoDes));
	} else {
	    /* add the default RX Queue setup template to the list of queues */
	    memcpy(qInfoDes, &ixEthAccQmgrRxDefaultTemplate, sizeof(*qInfoDes));
	}

	/* setup the RxQueue ID */
	qInfoDes->qId = rxQueues[rxQueue].qId;

	/* setup the RxQueue watermark level
	 *
	 * Each queue can be filled by many NPEs. To avoid the
	 * NPEs to write to a full queue, need to set the
	 * high watermark level for nearly full condition.
	 * (the high watermark level are a power of 2
	 * starting from the top of the queue)
	 *
	 * Number of     watermark
         *   ports        level
         *    1             0
	 *    2             1
	 *    3             2
	 *    4             4
	 *    5             4
	 *    6             8
	 *    n          approx. 2**ceil(log2(n))
	 */
	if (rxQueues[rxQueue].npeCount == 1)
	{
	    qInfoDes->AlmostFullThreshold = IX_QMGR_Q_WM_LEVEL0;
	}
	else if (rxQueues[rxQueue].npeCount == 2)
	{
	    qInfoDes->AlmostFullThreshold = IX_QMGR_Q_WM_LEVEL1;
	}
	else if (rxQueues[rxQueue].npeCount == 3)
	{
	    qInfoDes->AlmostFullThreshold = IX_QMGR_Q_WM_LEVEL2;
	}
	else
	{
	    /* reach the maximum number for CSR 2.0 */
            IX_ETH_ACC_WARNING_LOG("ixEthAccQMgrQueuesConfig: maximum number of NPEs per queue reached, bailing out\n", 0, 0, 0, 0, 0, 0);
	    ret = IX_ETH_ACC_FAIL;
	    break;
	}

	/* move to next queue entry */
	++qInfoDes;
    }

    /* configure the static list (RxFree, Tx and TxDone queues) */
    for (qInfoDes = ixEthAccQmgrStaticInfo;
	 (qInfoDes->qCallback != (IxQMgrCallback) NULL )
	     && (ret == IX_ETH_ACC_SUCCESS);
	 ++qInfoDes)
    {
	ret = ixEthAccQMgrQueueSetup(qInfoDes);
    }

    /* configure the dynamic list (Rx queues) */
    for (qInfoDes = ixEthAccQmgrRxQueuesInfo;
	 (qInfoDes->qCallback != (IxQMgrCallback) NULL )
	     && (ret == IX_ETH_ACC_SUCCESS);
	 ++qInfoDes)
    {
	ret = ixEthAccQMgrQueueSetup(qInfoDes);
    }

    return(ret);
}

/**
 * @fn ixEthAccQMgrRxQEntryGet(UINT32 *rxQueueEntries)
 *
 * @brief Add and return the total number of entries in all Rx queues
 *
 * @param UINT32 rxQueueEntries[in] number of entries in all queues
 *
 * @return void
 *
 * @note Rx queues configuration is driven by Qos Setup. There is a
 * variable number of rx queues which are set at initialisation.
 *
 * @internal
 */
IX_ETH_ACC_PUBLIC
void ixEthAccQMgrRxQEntryGet(UINT32 *numRxQueueEntries)
{
    UINT32 rxQueueLevel;
    IxEthAccQregInfo *qInfoDes;;

    *numRxQueueEntries = 0;

    /* iterate thru rx queues */
    for (qInfoDes = ixEthAccQmgrRxQueuesInfo;
	 qInfoDes->qCallback != (IxQMgrCallback)NULL;
	 ++qInfoDes)
    {
	/* retrieve the rx queue level */
	rxQueueLevel = 0;
	ixQMgrQNumEntriesGet(qInfoDes->qId, &rxQueueLevel);
	(*numRxQueueEntries) += rxQueueLevel;
    }
}

/**
 * @fn ixEthAccQMgrRxCallbacksRegister(IxQMgrCallback ixQMgrCallback)
 *
 * @brief Change the callback registered to all rx queues.
 *
 * @param IxQMgrCallback ixQMgrCallback[in] QMgr callback to register
 *
 * @return IxEthAccStatus
 *
 * @note The user may decide to use different Rx mechanisms
 * (e.g. receive many frames at the same time , or receive
 *  one frame at a time, depending on the overall application
 *  performances). A different QMgr callback is registered. This
 *  way, there is no excessive pointer checks in the datapath.
 *
 * @internal
 */
IX_ETH_ACC_PUBLIC
IxEthAccStatus ixEthAccQMgrRxCallbacksRegister(IxQMgrCallback ixQMgrCallback)
{
    IxEthAccQregInfo *qInfoDes;
    IxEthAccStatus ret = IX_ETH_ACC_SUCCESS;

    /* parameter check */
    if (NULL == ixQMgrCallback)
    {
	ret = IX_ETH_ACC_FAIL;
    }

    /* iterate thru rx queues */
    for (qInfoDes = ixEthAccQmgrRxQueuesInfo;
	 (qInfoDes->qCallback != (IxQMgrCallback) NULL )
	     && (ret == IX_ETH_ACC_SUCCESS);
	 ++qInfoDes)
    {
	/* register the rx callback for all queues */
	if (ixQMgrNotificationCallbackSet(qInfoDes->qId,
					     ixQMgrCallback,
					     qInfoDes->callbackTag
					     ) != IX_SUCCESS)
	{
	    ret = IX_ETH_ACC_FAIL;
	}
    }
    return(ret);
}

/**
 * @fn ixEthAccSingleEthNpeCheck(IxEthAccPortId portId)
 *
 * @brief Check the npe exists for this port
 *
 * @param IxEthAccPortId portId[in] port
 *
 * @return IxEthAccStatus
 *
 * @internal
 */
IX_ETH_ACC_PUBLIC
IxEthAccStatus ixEthAccSingleEthNpeCheck(IxEthAccPortId portId)
{

    /* If not IXP42X A0 stepping, proceed to check for existence of coprocessors */
    if ((IX_FEATURE_CTRL_SILICON_TYPE_A0 !=
        (ixFeatureCtrlProductIdRead() & IX_FEATURE_CTRL_SILICON_STEPPING_MASK))
        || (IX_FEATURE_CTRL_DEVICE_TYPE_IXP42X != ixFeatureCtrlDeviceRead ()))
      {
            if ((IX_ETH_PORT_1 == portId) &&
                (ixFeatureCtrlComponentCheck(IX_FEATURECTRL_ETH0) ==
                 IX_FEATURE_CTRL_COMPONENT_ENABLED))
            {
                return IX_ETH_ACC_SUCCESS;
            }

            if ((IX_ETH_PORT_2 == portId) &&
                (ixFeatureCtrlComponentCheck(IX_FEATURECTRL_ETH1) ==
                 IX_FEATURE_CTRL_COMPONENT_ENABLED))
            {
                return IX_ETH_ACC_SUCCESS;
            }

            if ((IX_ETH_PORT_3 == portId) &&
                (ixFeatureCtrlComponentCheck(IX_FEATURECTRL_NPEA_ETH) ==
                 IX_FEATURE_CTRL_COMPONENT_ENABLED))
            {
                return IX_ETH_ACC_SUCCESS;
            }

            return IX_ETH_ACC_FAIL;
      }

    return IX_ETH_ACC_SUCCESS;
}

/**
 * @fn ixEthAccStatsShow(void)
 *
 * @brief Displays all EthAcc stats
 *
 * @return void
 *
 */
void ixEthAccStatsShow(IxEthAccPortId portId)
{
    ixEthAccMdioShow();

    printf("\nPort %u\nUnicast MAC : ", portId);
    ixEthAccPortUnicastAddressShow(portId);
    ixEthAccPortMulticastAddressShow(portId);
    printf("\n");

    ixEthAccDataPlaneShow();
}