/**
 * ============================================================================
 * = COPYRIGHT
 *              INTEL CORPORATION PROPRIETARY INFORMATION
 *   This software is supplied under the terms of a license agreement or
 *   nondisclosure agreement with Intel Corporation and may not be copied 
 *   or disclosed except in accordance with the terms in that agreement.
 *      Copyright (C) 2000-2002 Intel Corporation. All rights reserved.
 *
 * = PRODUCT
 *      Intel(r) IXA SDK for the IXP2X00 Network Processor
 *
 * = FILENAME
 *      ix_cc_sched_internal.c
 *
 * = DESCRIPTION
 *      The file contains functions that represent internal functionality of Scheduler core component. 
 *      The functions in this file are not exposed to the clients of Scheduler, 
 *
 *
 * = AUTHOR
 *      Herman Zaks
 *      Herman.Zaks@intel.com 
 **/
#define IX_ERROR_FILE_IDENT "$Id: ix_cc_sched_internal.c,v 1.22 2003/11/14 21:34:37 rranjeet Exp $"

#include "ix_cc_error.h"
#include "ix_types.h"
#include "ix_error.h"
#include "ix_rm.h"

#if !defined(IX_EXCLUDE_CCI)
    #include "ix_cci.h"
#endif /* end !defined(IX_EXCLUDE_CCI) */

#include "ix_cc.h"
#include "cc/ix_cc_sched.h"
#include "cc/internal/ix_cc_sched_internal.h"

#if !defined(IX_INCLUDE_REGISTRY) /* include configuration header for config values */
    #include "ix_config.h"
#endif

 
/**
 * NAME: _ix_cc_sched_update_configuration()
 *
 * DESCRIPTION: Populates configuration datastructure with configuration data for Scheduler microblock
 *              and core
 * 
 * @Param:  - ix_cc_sched_context* pContext - pointer to the Scheduler configuration data structure 
 *
 * @Return: IX_SUCCESS or valid ix_error;
 */

ix_error _ix_cc_sched_update_configuration(ix_cc_sched_context* pContext)
{
    ix_error err = IX_SUCCESS;
#if defined (IX_INCLUDE_REGISTRY)
    ix_configuration_property_handle hProperty;
    /* get Ingress /Egress side */

    IX_ERROR_CRT(ix_rm_cp_property_open( IX_CP_CORE_PROPERTY_ROOT,
                              REPOSITORY_PATH_INGRESS_EGRESS,
                              &hProperty), IX_CC_SCHED_ERROR_REPOSITORY_OPEN, IX_ERROR_LEVEL_WARNING);

/*
    err = ix_rm_cp_property_open( IX_CP_CORE_PROPERTY_ROOT,
                              REPOSITORY_PATH_INGRESS_EGRESS,
                              &hProperty);
    if (IX_SUCCESS != err)
    {
        return IX_ERROR_WARNING(IX_CC_SCHED_ERROR_REPOSITORY_OPEN, ("Can not open REPOSITORY_PATH_INGRESS_EGRESS"));
    }
*/
    IX_ERROR_CRT(ix_rm_cp_property_get_value_uint32(hProperty, &pContext->ingressEgress),
                    IX_CC_SCHED_ERROR_REPOSITORY_READ, IX_ERROR_LEVEL_WARNING);
    ix_rm_cp_property_close( hProperty); /* the return value has not been checked for this function because of following
                                            reasons: The only error condition could be if we passing invalid handle,
                                            and in this case, if property_open was success, the handle guaranteed
                                            to be correct. second reason is that there no much can be done in that point
                                            with the error.
                                          */
    /* get what type of scheduler we running */
    IX_ERROR_CRT(ix_rm_cp_property_open( IX_CP_CORE_PROPERTY_ROOT,
                              REPOSITORY_PATH_SCHEDULER_TYPE,
                              &hProperty), IX_CC_SCHED_ERROR_REPOSITORY_OPEN, IX_ERROR_LEVEL_WARNING);
    IX_ERROR_CRT(ix_rm_cp_property_get_value_uint32(hProperty, (ix_int32*)&pContext->schedulerType),
                    IX_CC_SCHED_ERROR_REPOSITORY_READ, IX_ERROR_LEVEL_WARNING);
    ix_rm_cp_property_close( hProperty);


    /* get the uengine number for Scheduler ublock */
    IX_ERROR_CRT(ix_rm_cp_property_open( IX_CP_CORE_PROPERTY_ROOT,
                              REPOSITORY_PATH_SCHED_SCHED_UENG_NUM,
                              &hProperty), IX_CC_SCHED_ERROR_REPOSITORY_OPEN, IX_ERROR_LEVEL_WARNING);
    IX_ERROR_CRT(ix_rm_cp_property_get_value_uint32(hProperty, &pContext->pUblockConfig->ueng_number),
                    IX_CC_SCHED_ERROR_REPOSITORY_READ, IX_ERROR_LEVEL_WARNING);
    ix_rm_cp_property_close( hProperty);


    /* get the SRAM channel number */
    IX_ERROR_CRT(ix_rm_cp_property_open( IX_CP_CORE_PROPERTY_ROOT,
                              REPOSITORY_PATH_SRAM_CHANNEL_NUM,
                              &hProperty), IX_CC_SCHED_ERROR_REPOSITORY_OPEN, IX_ERROR_LEVEL_WARNING);
    IX_ERROR_CRT(ix_rm_cp_property_get_value_uint32(hProperty, &pContext->pUblockConfig->sram_channel_num),
                    IX_CC_SCHED_ERROR_REPOSITORY_READ, IX_ERROR_LEVEL_WARNING);
    ix_rm_cp_property_close( hProperty);

    /* get number of ports */
    IX_ERROR_CRT(ix_rm_cp_property_open( IX_CP_CORE_PROPERTY_ROOT,
                              REPOSITORY_PATH_SCHED_NUMBER_OF_PORTS,
                              &hProperty), IX_CC_SCHED_ERROR_REPOSITORY_OPEN, IX_ERROR_LEVEL_WARNING);
    IX_ERROR_CRT(ix_rm_cp_property_get_value_uint32(hProperty, &pContext->pUblockConfig->number_of_ports),
                    IX_CC_SCHED_ERROR_REPOSITORY_READ, IX_ERROR_LEVEL_WARNING);
    ix_rm_cp_property_close( hProperty);


    /* if csix scheduler - ingress */
    if (IX_CSIX_SCHEDULER == pContext->schedulerType)
    {

        pContext->pUblockConfig->ingress_voq_size = IX_CSIX_SCHED_WEIGHT_LIST_SIZE; /*  1024 */
        /* get number of weights per port  - should not be more than 16*/
        IX_ERROR_CRT(ix_rm_cp_property_open( IX_CP_CORE_PROPERTY_ROOT,
                                  REPOSITORY_PATH_SCHED_VOQ_WEIGHTS_PER_PORT,
                                  &hProperty), IX_CC_SCHED_ERROR_REPOSITORY_OPEN, IX_ERROR_LEVEL_WARNING);
        IX_ERROR_CRT(ix_rm_cp_property_get_value_uint32(hProperty, &pContext->pUblockConfig->ingress_voq_weights_per_port),
                        IX_CC_SCHED_ERROR_REPOSITORY_READ, IX_ERROR_LEVEL_WARNING);
        ix_rm_cp_property_close( hProperty);
        
        /* get weight value - in this release it will be used equally between the ports */
        IX_ERROR_CRT(ix_rm_cp_property_open( IX_CP_CORE_PROPERTY_ROOT,
                                  REPOSITORY_PATH_SCHED_VOQ_WEIGHT_VALUE,
                                  &hProperty), IX_CC_SCHED_ERROR_REPOSITORY_OPEN, IX_ERROR_LEVEL_WARNING);
        IX_ERROR_CRT(ix_rm_cp_property_get_value_uint32(hProperty, &pContext->pUblockConfig->ingress_weight_value),
                        IX_CC_SCHED_ERROR_REPOSITORY_READ, IX_ERROR_LEVEL_WARNING);
        ix_rm_cp_property_close( hProperty);
        

    }
    else if (IX_PACKET_SCHEDULER == pContext->schedulerType)
    {
        /* get weights value */
        IX_ERROR_CRT(ix_rm_cp_property_open( IX_CP_CORE_PROPERTY_ROOT,
                                  REPOSITORY_PATH_SCHED_PKT_WEIGHT_VALUE,
                                  &hProperty), IX_CC_SCHED_ERROR_REPOSITORY_OPEN, IX_ERROR_LEVEL_WARNING);
        IX_ERROR_CRT(ix_rm_cp_property_get_value_uint32(hProperty, &pContext->pUblockConfig->egress_weight_value),
                        IX_CC_SCHED_ERROR_REPOSITORY_READ, IX_ERROR_LEVEL_WARNING);
        ix_rm_cp_property_close( hProperty);

        /* get total number of queues */
        IX_ERROR_CRT(ix_rm_cp_property_open( IX_CP_CORE_PROPERTY_ROOT,
                                  REPOSITORY_PATH_SCHED_QUEUE_PORT_SIZE,
                                  &hProperty), IX_CC_SCHED_ERROR_REPOSITORY_OPEN, IX_ERROR_LEVEL_WARNING);
        IX_ERROR_CRT(ix_rm_cp_property_get_value_uint32(hProperty, &pContext->pUblockConfig->egress_queue_port_size),
                        IX_CC_SCHED_ERROR_REPOSITORY_READ, IX_ERROR_LEVEL_WARNING);
        ix_rm_cp_property_close( hProperty);
    }

#else /* no registry */
    /* read config data from config header file */
    
    pContext->ingressEgress                                     = INGRESS_EGRESS;
    pContext->schedulerType                                     = IX_SCHED_TYPE;
    pContext->pUblockConfig->sram_channel_num                   = IX_SCHED_SRAM_CHANNEL_NUM;
    pContext->pUblockConfig->ueng_number                        = IX_SCHED_UENG_NUM;
    pContext->pUblockConfig->number_of_ports                    = IX_SCHED_NUMBER_OF_PORTS;
    if (IX_CSIX_SCHEDULER == pContext->schedulerType)
    {   
        pContext->pUblockConfig->ingress_voq_size               = IX_SCHED_ING_VOQ_SIZE; /* 1024 */
        pContext->pUblockConfig->ingress_voq_weights_per_port   = IX_SCHED_ING_VOQ_WEIGHTS_PORT;   
        pContext->pUblockConfig->ingress_weight_value           = IX_SCHED_ING_WEIGHT_VALUE;       
    }
    else if (IX_PACKET_SCHEDULER == pContext->schedulerType)
    {
        pContext->pUblockConfig->egress_queue_port_size          = IX_SCHED_EG_PORT_QUEUE_SIZE; /* 256 */
        pContext->pUblockConfig->egress_weight_value             = IX_SCHED_EG_WEIGHT_VALUE; /* 1 */

    } 
#endif /* registry */

    {
        void* pPhysicalAddress = NULL; /* holder of physical memory */
        ix_uint32 offset;
        ix_uint32 channel;
        ix_memory_type memType; 
        
        if (IX_CSIX_SCHEDULER == pContext->schedulerType)
        {
            /* allocate SRAM memory for Ingress Scheduler microblock */
            IX_ERROR_CRT(ix_rm_mem_alloc(IX_MEMORY_TYPE_SRAM,pContext->pUblockConfig->sram_channel_num, 
                                 pContext->pUblockConfig->ingress_voq_size * sizeof (ix_uint32) , 
                                  &pContext->pUblockConfig->virtual_base),IX_CC_ERROR_OOM_SRAM,IX_ERROR_LEVEL_WARNING);
        
            ix_ossl_memset(pContext->pUblockConfig->virtual_base,0,pContext->pUblockConfig->ingress_voq_size * sizeof (ix_uint32));
        }
        else if (IX_PACKET_SCHEDULER == pContext->schedulerType)
        {
            /* allocate SRAM memory for Egress Scheduler microblock */
            IX_ERROR_CRT(ix_rm_mem_alloc(IX_MEMORY_TYPE_SRAM,pContext->pUblockConfig->sram_channel_num, 
                                  (pContext->pUblockConfig->egress_queue_port_size + pContext->pUblockConfig->number_of_ports) * sizeof (ix_uint32), 
                                  &pContext->pUblockConfig->virtual_base),IX_CC_ERROR_OOM_SRAM,IX_ERROR_LEVEL_WARNING);
            ix_ossl_memset(pContext->pUblockConfig->virtual_base,0,(pContext->pUblockConfig->egress_queue_port_size + pContext->pUblockConfig->number_of_ports) * sizeof (ix_uint32));
        }
        
#ifdef IX_SCHED_DEBUG
            ix_ossl_message_log("Shared memory has been allocated for CSIX Scheduler - pContext->pUblockConfig->virtual_base = %p\n",
                                    pContext->pUblockConfig->virtual_base);
#endif

        err = ix_rm_get_phys_offset(pContext->pUblockConfig->virtual_base, &memType, 
                                    &channel, &offset, &pPhysicalAddress); 
                        
        if (IX_SUCCESS != err)
        {
            /* free previously allocated memory */
            ix_rm_mem_free(pContext->pUblockConfig->virtual_base);
            return IX_ERROR_NEW(IX_CC_SCHED_ERROR_VIRTUAL_TO_PHYSICAL, IX_ERROR_LEVEL_WARNING);
        }
        pContext->pUblockConfig->base_offset = offset;
    }
    return err;
}



/**
 * NAME: _ix_cc_sched_patch_microblock()
 *
 * DESCRIPTION: Patches Scheduler microblock
 * 
 * @Param:  - ix_cc_sched_context* pContext - pointer to the Scheduler context data structure 
 *
 * @Return: IX_SUCCESS or valid ix_error;
 */

ix_error _ix_cc_sched_patch_microblock(const ix_cc_sched_context* pContext)
{
    ix_error err = IX_SUCCESS;
    ix_imported_symbol importSymbol;
    ix_uint32 offset = 0;
    ix_uint32 chanNum = 0;
#if defined(IX_SCHED_DEBUG_2)
    char* schedType = NULL;
#endif /* IX_SCHED_DEBUG_2 */
    ix_uint32 i = 0;
    ix_uint32 y = 0;

    importSymbol.m_Value = 0;
    importSymbol.m_Name = '\0';

    /* populate shared memory with credit values */
    if (IX_CSIX_SCHEDULER == pContext->schedulerType)
    {
        /* with the following algorithm:
         *    for csix scheduler (ingress) 
         *    There is maximum 64 output ports and 16 queues/weights per port, so total number of queues is 1024.
         *    For each queue we associate weight_value, or credit for microblock configuration.
         *    The shared memory pointed by voq_base will be 1024 long words  and credits will be set to 
         *    weight_values. This array will be populated in following fashion -   
         *    weight values are set in indecies  [0 + number of queues per port] - so if queues are three, the value 
         *    will be set in index [0],[1],[2] for first port, [16],[17],[18] for second port, and so on
         *    up to 64 ports
        */

        ix_uint32* pBase = (ix_uint32*)pContext->pUblockConfig->virtual_base;
        ix_uint32 numPorts = pContext->pUblockConfig->number_of_ports;
        ix_uint32 voq_size = pContext->pUblockConfig->ingress_voq_size;
        ix_uint32 weightsPort = pContext->pUblockConfig->ingress_voq_weights_per_port;
        ix_uint32 weightsValue = pContext->pUblockConfig->ingress_weight_value;
        ix_uint32 port_index = 0;
        /* preset memory to 0 */
        ix_ossl_memset(pBase, 0, voq_size * sizeof(ix_uint32));
        for (i=0;i<numPorts;i++)
        {
            port_index = i*IX_CSIX_SCHED_CLASSES_PER_PORT;
            for (y = 0; y < weightsPort; y++)
            {
                pBase[y+port_index] = weightsValue;
            }
            
        }
    }
    else if (IX_PACKET_SCHEDULER == pContext->schedulerType)
    {
        /* 
        *  for packet scheduler algorithm: 
        *  the memory allocated is (16*16)+16 = 272 long words 
        *  first 16 entries are populated according to weights for the ports 
        *  rest is populated with credits for the queues on the ports - there 16 queues
        *  for each port 
        */
        ix_uint32* pBase = (ix_uint32*)pContext->pUblockConfig->virtual_base;
        ix_uint32 numPorts = pContext->pUblockConfig->number_of_ports;
        ix_uint32 weightValue = pContext->pUblockConfig->egress_weight_value;
        ix_uint32 creditValue = IX_SCHED_DRR_MAX_CREDIT;
        ix_uint32 WeightCreditSize = pContext->pUblockConfig->egress_queue_port_size+pContext->pUblockConfig->number_of_ports;
        /* should be 272 above */
        /* set memory to 0 */
        ix_ossl_memset(pBase,0,WeightCreditSize*sizeof(ix_uint32));
        
        /* for 0 to numPorts set entry to weightValue, for entries from 16 to 271,
            each sixteen represents queueus per one port, for current release set only 
            first queues with creditValue */
        
        for (i = 0, y = 0; i < numPorts; i++)
        {
            pBase[i] = weightValue;/* set in first 16 entries */
            y = IX_PKT_SCHED_MAX_PORTS + (i*IX_PKT_SCHED_MAX_QUEUES);/* set one per port starting from
                                                                        index 16 */
            pBase[y] = creditValue;
        }

    }    
/*  the core components will patch only SRAM physical offset,
*    in the physical offset two high bits will be set according to channel number in the following pattern
*   00 - channel 0
*   01 - channel 1
*   10 - channel 2
*   11 - channel 3
*/
    /* pack channel number into the offset */
    offset = pContext->pUblockConfig->base_offset;
    chanNum = pContext->pUblockConfig->sram_channel_num;
    IX_CC_SET_SRAM_CHANNEL_INTO_OFFSET(chanNum, offset);

    if (IX_CSIX_SCHEDULER == pContext->schedulerType)
    {
        
        IX_SCHED_SET_UBLOCK_SYMBOLS(importSymbol,offset, IX_SCHED_SYMBOL_VOQ_CREDIT_BASE); 
    }
    else if (IX_PACKET_SCHEDULER == pContext->schedulerType)
    {
        
        IX_SCHED_SET_UBLOCK_SYMBOLS(importSymbol,offset, IX_SCHED_SYMBOL_WEIGHT_CREDIT_BASE); 
    }
#ifdef IX_SCHED_DEBUG_2
    schedType = (IX_CSIX_SCHEDULER == pContext->schedulerType)?"CSIX":"PACKET"; 
    ix_ossl_message_log("Patch %s Scheduler symbols to microengine #%d\n", schedType, pContext->pUblockConfig->ueng_number);
    ix_ossl_message_log("importSymbol.m_Name = %s, importSymbol.m_Value = %p \n",
                            importSymbol.m_Name, importSymbol.m_Value);
    ix_ossl_message_log("Dump context of Scheduler shared memory...\n");
    if (IX_CSIX_SCHEDULER == pContext->schedulerType)
    {
        ix_uint32* pT = pContext->pUblockConfig->virtual_base;
        ix_uint32 voqSize = pContext->pUblockConfig->ingress_voq_size;
        for (i=0;i<voqSize;i++,pT++)
        {
            printf(" %d,", *pT);
            if (((i+1) % 16) == 0)
            {
                printf("\n");
            }
        }
    }
    else if (IX_PACKET_SCHEDULER == pContext->schedulerType)
    {
        ix_uint32* pT = pContext->pUblockConfig->virtual_base;
        ix_uint32  memSize = pContext->pUblockConfig->egress_queue_port_size+pContext->pUblockConfig->number_of_ports; 
        for (i=0;i<memSize;i++,pT++)
        {
            ix_ossl_message_log(" %d,", *pT);
            if (((i+1) % 16) == 0)
            {
                ix_ossl_message_log("\n");
            }
        }
    }

#endif

/* For 2800, there is not a symbol to patch */
#if (_IX_HARDWARE_TYPE_ != _IX_HW_2800_)

    err = ix_rm_ueng_patch_symbols(pContext->pUblockConfig->ueng_number,IX_SCHED_NUM_PATCH_SYMBOLS,&importSymbol);
    if (IX_SUCCESS != err)
    {
        /* delete previously allocated shared SRAM memory */
        if (IX_SUCCESS != ix_rm_mem_free(pContext->pUblockConfig->virtual_base))
        {
            ix_ossl_message_log("Error deleting shared memory");
        }
        return IX_ERROR_NEW(IX_CC_SCHED_ERROR_PATCH_SYMBOLS, IX_ERROR_LEVEL_WARNING);
    }

#else

#if defined(IX_PLATFORM_2801)
    /*
     * Temporary fix - remove when 2800 scheduler will be integrated in uCode
     */
    err = ix_rm_ueng_patch_symbols(pContext->pUblockConfig->ueng_number,IX_SCHED_NUM_PATCH_SYMBOLS,&importSymbol);
    if (IX_SUCCESS != err)
    {
        /* delete previously allocated shared SRAM memory */
        if (IX_SUCCESS != ix_rm_mem_free(pContext->pUblockConfig->virtual_base))
        {
            ix_ossl_message_log("Error deleting shared memory");
        }
        return IX_ERROR_NEW(IX_CC_SCHED_ERROR_PATCH_SYMBOLS, IX_ERROR_LEVEL_WARNING);
    }

#endif    
    
#endif /* (_IX_HARDWARE_TYPE_ != _IX_HW_2800_) */

    /* it assumed that ix_rm_ueng_load() will be called by the system app after all core components are initialized */
 
    return err;
}



