/**********************************************************************************************************************
*  Copyright (C), 2022-2026, SMARTLOGIC TECHNOLOGY LTD.
*  File Name: ecpri_que.s.c
*  Create Date: 23/05/15
*  Description: eCPRI Queue Module Source File
*  Change History: 
*      <author>      <time>      <version>   <desc>
*    1. ShangH      23/05/15        1.0       Build this module
**********************************************************************************************************************/


/* -------------------------------------------------- <INC FILE> --------------------------------------------------- */
#include "ecpri_que.h"


/* --------------------------------------------------- <VAR DEF> --------------------------------------------------- */
/* eCPRI ring buffer management structure */
EcpriRingBuf_t g_ecpri_ring_buf = {0};
/* eCPRI callback function */
EcpriCallback g_ecpri_callback = NULL;
/* eCPRI queue poll count */
uint64_t g_ecpri_que_poll_cnt = 0;
/* PCS SerDes Loopback Flag */
bool g_pcs_loopback = true;


/* -------------------------------------------------- <FUNC DEF> --------------------------------------------------- */
/**********************************************************************************************************************
*  Function: ecpri_que_init
*  Description: eCPRI queue init
*  Input: none
*  Output: none
*  Return: operation result - success or failure.
*  Others: none
**********************************************************************************************************************/
int32_t ecpri_que_init(void)
{
    /* write position */
    uint32_t wr_pos = 0;
    /* write address */
    uint32_t wr_addr = 0;

    /* eCPRI ring buffer management structure clear */
    memset_ucp((void *)&g_ecpri_ring_buf, 0x00, sizeof(g_ecpri_ring_buf));

    /* get write position */
    wr_pos = do_read_volatile(&DESC_PTR_2);

    /* calculate write address and verify validity */
    wr_addr = wr_pos * ECPRI_DESC_ADDR_UNIT;
    if ((wr_addr < ECPRI_RX_BUF_ADDR) || 
        (wr_addr >= (ECPRI_RX_BUF_ADDR + ECPRI_RX_BUF_SIZE)) || 
        (((wr_addr - ECPRI_RX_BUF_ADDR) % ECPRI_PKT_SIZE) != 0))
    {
        debug_write(ECPRI_DBG_IDX0(0), (uint32_t)ERROR); /* ECS_RFM0: 0xB7E30000(0) */
        debug_write(ECPRI_DBG_IDX0(1), wr_pos); /* ECS_RFM0: 0xB7E30004(1) */
        debug_write(ECPRI_DBG_IDX0(2), wr_addr); /* ECS_RFM0: 0xB7E30008(2) */

        return ERROR;
    }

    /* update next valid address for descriptor */
    do_write(&DESC_TAIL_ADDR_2, wr_pos);

    /* update write/read index */
    g_ecpri_ring_buf.wr_idx = (wr_addr - ECPRI_RX_BUF_ADDR) / ECPRI_PKT_SIZE;
    g_ecpri_ring_buf.rd_idx = g_ecpri_ring_buf.wr_idx;

    debug_write(ECPRI_DBG_IDX0(0), (uint32_t)SUCCESS); /* ECS_RFM0: 0xB7E30000(0) */
    debug_write(ECPRI_DBG_IDX0(1), wr_pos); /* ECS_RFM0: 0xB7E30004(1) */
    debug_write(ECPRI_DBG_IDX0(2), wr_addr); /* ECS_RFM0: 0xB7E30008(2) */
    debug_write(ECPRI_DBG_IDX0(3), g_ecpri_ring_buf.wr_idx); /* ECS_RFM0: 0xB7E3000C(3) */
    debug_write(ECPRI_DBG_IDX0(4), g_ecpri_ring_buf.rd_idx); /* ECS_RFM0: 0xB7E30010(4) */

    return SUCCESS;
}

/**********************************************************************************************************************
*  Function: ecpri_que_polling
*  Description: eCPRI queue polling
*  Input: none
*  Output: none
*  Return: operation result - success or failure.
*  Others: none
**********************************************************************************************************************/
int32_t ecpri_que_polling(void)
{
    /* write position */
    uint32_t wr_pos = 0;
    /* write address */
    uint32_t wr_addr = 0;
    /* read number */
    uint32_t rd_num = 0;
    /* read address */
    uint32_t rd_addr = 0;
    /* descriptor full flag */
    bool desc_full_flag = false;
    /* count index */
    uint32_t i = 0;

    if (g_pcs_loopback)
    {
        /* solve start packet impact, recover normal mode */
        do_write(&MC_CFG_RX, BIT3 + BIT1); //rx_elastic_buffer_initial_fill 5

        g_pcs_loopback = false;
    }

    /* update eCPRI queue poll count */
    g_ecpri_que_poll_cnt++;

    /* get write position */
    wr_pos = do_read_volatile(&DESC_PTR_2);

    /* calculate write address and verify validity */
    wr_addr = wr_pos * ECPRI_DESC_ADDR_UNIT;
    if ((wr_addr < ECPRI_RX_BUF_ADDR) || 
        (wr_addr >= (ECPRI_RX_BUF_ADDR + ECPRI_RX_BUF_SIZE)) || 
        (((wr_addr - ECPRI_RX_BUF_ADDR) % ECPRI_PKT_SIZE) != 0))
    {
        debug_write(ECPRI_DBG_IDX0(8), (uint32_t)ERROR); /* ECS_RFM0: 0xB7E30020(8) */
        debug_write(ECPRI_DBG_IDX0(10), g_ecpri_que_poll_cnt & 0xFFFFFFFF); /* ECS_RFM0: 0xB7E30028(10) */
        debug_write(ECPRI_DBG_IDX0(11), (g_ecpri_que_poll_cnt >> 32) & 0xFFFFFFFF); /* ECS_RFM0: 0xB7E3002C(11) */
        debug_write(ECPRI_DBG_IDX0(12), wr_pos); /* ECS_RFM0: 0xB7E30030(12) */
        debug_write(ECPRI_DBG_IDX0(13), wr_addr); /* ECS_RFM0: 0xB7E30034(13) */

        return ERROR;
    }

    /* get descriptor full flag */
    if (0 != (do_read_volatile(&RX_DESC_INTR_CTRL_2) & BIT10))
    {
        desc_full_flag = true;
    }

    /* calculate write index */
    g_ecpri_ring_buf.wr_idx = (wr_addr - ECPRI_RX_BUF_ADDR) / ECPRI_PKT_SIZE;

    /* descriptor is not full, also read index equals to write index, no packet need read */
    if ((!desc_full_flag) && (g_ecpri_ring_buf.wr_idx == g_ecpri_ring_buf.rd_idx))
    {
        debug_write(ECPRI_DBG_IDX0(8), (uint32_t)SUCCESS); /* ECS_RFM0: 0xB7E30020(8) */
        debug_write(ECPRI_DBG_IDX0(10), g_ecpri_que_poll_cnt & 0xFFFFFFFF); /* ECS_RFM0: 0xB7E30028(10) */
        debug_write(ECPRI_DBG_IDX0(11), (g_ecpri_que_poll_cnt >> 32) & 0xFFFFFFFF); /* ECS_RFM0: 0xB7E3002C(11) */
        debug_write(ECPRI_DBG_IDX0(12), wr_pos); /* ECS_RFM0: 0xB7E30030(12) */
        debug_write(ECPRI_DBG_IDX0(13), wr_addr); /* ECS_RFM0: 0xB7E30034(13) */
        debug_write(ECPRI_DBG_IDX0(14), g_ecpri_ring_buf.wr_idx); /* ECS_RFM0: 0xB7E30038(14) */
        debug_write(ECPRI_DBG_IDX0(15), g_ecpri_ring_buf.rd_idx); /* ECS_RFM0: 0xB7E3003C(15) */

        return SUCCESS;
    }

    /* calculate number of packets need to read */
    if (desc_full_flag && (g_ecpri_ring_buf.wr_idx == g_ecpri_ring_buf.rd_idx))
    {
        rd_num = ECPRI_PKT_MAX;
    }
    else
    {
        rd_num = (g_ecpri_ring_buf.wr_idx + ECPRI_PKT_MAX - g_ecpri_ring_buf.rd_idx) % ECPRI_PKT_MAX;
    }

    for (i = 0; i < rd_num; i++)
    {
        /* calculate read address */
        rd_addr = ECPRI_RX_BUF_ADDR + g_ecpri_ring_buf.rd_idx * ECPRI_PKT_SIZE;

        /* eCPRI packet process */
        ecpri_pkt_proc(rd_addr);

        /* update next valid address for descriptor */
        do_write(&DESC_TAIL_ADDR_2, ((rd_addr + ECPRI_PKT_SIZE) / ECPRI_DESC_ADDR_UNIT));

        /* update read index */
        g_ecpri_ring_buf.rd_idx++;
        g_ecpri_ring_buf.rd_idx %= ECPRI_PKT_MAX;
    }

    /* update total packets */
    g_ecpri_ring_buf.ecpri_pkt_stats.total_pkts += rd_num;

    debug_write(ECPRI_DBG_IDX0(8), (uint32_t)SUCCESS); /* ECS_RFM0: 0xB7E30020(8) */
    debug_write(ECPRI_DBG_IDX0(10), g_ecpri_que_poll_cnt & 0xFFFFFFFF); /* ECS_RFM0: 0xB7E30028(10) */
    debug_write(ECPRI_DBG_IDX0(11), (g_ecpri_que_poll_cnt >> 32) & 0xFFFFFFFF); /* ECS_RFM0: 0xB7E3002C(11) */
    debug_write(ECPRI_DBG_IDX0(12), wr_pos); /* ECS_RFM0: 0xB7E30030(12) */
    debug_write(ECPRI_DBG_IDX0(13), wr_addr); /* ECS_RFM0: 0xB7E30034(13) */
    debug_write(ECPRI_DBG_IDX0(14), g_ecpri_ring_buf.wr_idx); /* ECS_RFM0: 0xB7E30038(14) */
    debug_write(ECPRI_DBG_IDX0(15), g_ecpri_ring_buf.rd_idx); /* ECS_RFM0: 0xB7E3003C(15) */
    debug_write(ECPRI_DBG_IDX0(16), rd_num); /* ECS_RFM0: 0xB7E30040(16) */
    debug_write(ECPRI_DBG_IDX0(18), g_ecpri_ring_buf.ecpri_pkt_stats.total_pkts & 0xFFFFFFFF); /* ECS_RFM0: 0xB7E30048(18)(LSB) */
    debug_write(ECPRI_DBG_IDX0(19), (g_ecpri_ring_buf.ecpri_pkt_stats.total_pkts >> 32) & 0xFFFFFFFF); /* ECS_RFM0: 0xB7E3004C(19)(MSB) */

    return SUCCESS;
}

/**********************************************************************************************************************
*  Function: ecpri_pkt_proc
*  Description: eCPRI packet process
*  Input: 
*    <name>                    <type>                      <desc>
*     pkt_addr                  uint32_t                    packet address
*  Output: none
*  Return: none
*  Others: none
**********************************************************************************************************************/
void ecpri_pkt_proc(uint32_t pkt_addr)
{
    /* VLAN tag & flag */
    uint16_t vlan_tag = 0;
    bool vlan_flag = false;
    /* ethernet type */
    uint16_t ether_type = 0;
#ifdef ECPRI_STRIP_ETH_HEAD_ENABLE
    /* payload address */
    uint32_t payload_addr = 0;
#endif
    /* payload length */
    uint16_t payload_len = 0;
    /* packet length */
    uint16_t pkt_len = 0;

    /* get VLAN tag of packet */
    vlan_tag = do_read_short(pkt_addr + ECPRI_VLAN_TAG_OFFSET);
    vlan_tag = ((vlan_tag & 0xFF) << 8) | ((vlan_tag & 0xFF00) >> 8);
    if (ECPRI_VLAN_TAG_VAL == vlan_tag)
    {
        vlan_flag = true;
    }

    /* get ethernet type of packet */
    ether_type = do_read_short(pkt_addr + ECPRI_PKT_TYPE_OFFSET + (uint8_t)vlan_flag * ECPRI_VLAN_TAG_LEN);
    ether_type = ((ether_type & 0xFF) << 8) | ((ether_type & 0xFF00) >> 8);

    /* eCPRI packet */
    if ((uint16_t)E_Ether_Type_ECPRI == ether_type)
    {
        /* update eCPRI packets */
        g_ecpri_ring_buf.ecpri_pkt_stats.ecpri_pkts++;
        debug_write(ECPRI_DBG_IDX0(24), g_ecpri_ring_buf.ecpri_pkt_stats.ecpri_pkts & 0xFFFFFFFF); /* ECS_RFM0: 0xB7E30060(24)(LSB) */
        debug_write(ECPRI_DBG_IDX0(25), (g_ecpri_ring_buf.ecpri_pkt_stats.ecpri_pkts >> 32) & 0xFFFFFFFF); /* ECS_RFM0: 0xB7E30064(25)(MSB) */

        /* get eCPRI packet payload length */
        payload_len = do_read_short(pkt_addr + ECPRI_PKT_LEN_OFFSET + (uint8_t)vlan_flag * ECPRI_VLAN_TAG_LEN);
        payload_len = ((payload_len & 0xFF) << 8) | ((payload_len & 0xFF00) >> 8);

#ifdef ECPRI_STRIP_ETH_HEAD_ENABLE
        /* calculate eCPRI packet payload address(strip ethernet head and eCPRI common head) */
        payload_addr = pkt_addr + ECPRI_PKT_HEAD_LEN + (uint8_t)vlan_flag * ECPRI_VLAN_TAG_LEN;

        debug_write(ECPRI_DBG_IDX0(26), payload_addr); /* ECS_RFM0: 0xB7E30068(26) */
        debug_write(ECPRI_DBG_IDX0(27), payload_len); /* ECS_RFM0: 0xB7E3006C(27) */

        /* inform PL about eCPRI packet payload address and payload length */
        (void)ecpri_inform_pl(payload_addr, payload_len);
#else
        /* calculate eCPRI packet length */
        pkt_len = payload_len + ECPRI_PKT_HEAD_LEN + (uint8_t)vlan_flag * ECPRI_VLAN_TAG_LEN;

        debug_write(ECPRI_DBG_IDX0(26), pkt_addr); /* ECS_RFM0: 0xB7E30068(26) */
        debug_write(ECPRI_DBG_IDX0(27), pkt_len); /* ECS_RFM0: 0xB7E3006C(27) */

#ifndef TEST_ENABLE
        /* inform PL about eCPRI packet address and packet length */
        (void)ecpri_inform_pl(pkt_addr, pkt_len);
#else
        /* eCPRI test PL packet process */
        ecpri_test_pl_pkt_proc(pkt_addr, pkt_len);
#endif
#endif
    }
    else /* other packet */
    {
        /* update other packets */
        g_ecpri_ring_buf.ecpri_pkt_stats.other_pkts++;
        debug_write(ECPRI_DBG_IDX0(28), g_ecpri_ring_buf.ecpri_pkt_stats.other_pkts & 0xFFFFFFFF); /* ECS_RFM0: 0xB7E30070(28) */
        debug_write(ECPRI_DBG_IDX0(29), (g_ecpri_ring_buf.ecpri_pkt_stats.other_pkts >> 32) & 0xFFFFFFFF); /* ECS_RFM0: 0xB7E30074(29) */

        debug_write(ECPRI_DBG_IDX0(30), pkt_addr); /* ECS_RFM0: 0xB7E30078(30) */

        /* inform ARM about other packet address */
        (void)ecpri_inform_arm(pkt_addr);
    }
}

/**********************************************************************************************************************
*  Function: ecpri_inform_pl
*  Description: eCPRI inform PL
*  Input: 
*    <name>                    <type>                      <desc>
*     pkt_addr                  uint32_t                    packet address
*     pkt_len                   uint16_t                    packet length
*  Output: none
*  Return: operation result - success or failure.
*  Others: none
**********************************************************************************************************************/
int32_t ecpri_inform_pl(uint32_t pkt_addr, uint16_t pkt_len)
{
    /* input para check */
    if ((pkt_addr < ECPRI_RX_BUF_ADDR) || 
        (pkt_addr >= (ECPRI_RX_BUF_ADDR + ECPRI_RX_BUF_SIZE)) || 
        (pkt_len > ECPRI_PKT_SIZE))
    {
        debug_write(ECPRI_DBG_IDX0(36), (uint32_t)ERROR); /* ECS_RFM0: 0xB7E30090(36) */
        debug_write(ECPRI_DBG_IDX0(37), pkt_addr); /* ECS_RFM0: 0xB7E30094(37) */
        debug_write(ECPRI_DBG_IDX0(38), pkt_len); /* ECS_RFM0: 0xB7E30098(38) */

        return ERROR;
    }

    /* check eCPRI callback function is NULL or not */
    if (NULL == g_ecpri_callback)
    {
        debug_write(ECPRI_DBG_IDX0(36), (uint32_t)ERROR); /* ECS_RFM0: 0xB7E30090(36) */
        debug_write(ECPRI_DBG_IDX0(37), pkt_addr); /* ECS_RFM0: 0xB7E30094(37) */
        debug_write(ECPRI_DBG_IDX0(38), pkt_len); /* ECS_RFM0: 0xB7E30098(38) */
        debug_write(ECPRI_DBG_IDX0(39), (uint32_t)ERROR); /* ECS_RFM0: 0xB7E3009C(39) */

        return ERROR;
    }

    /* call eCPRI callback function */
    g_ecpri_callback(pkt_addr, pkt_len);

    debug_write(ECPRI_DBG_IDX0(36), (uint32_t)SUCCESS); /* ECS_RFM0: 0xB7E30090(36) */
    debug_write(ECPRI_DBG_IDX0(37), pkt_addr); /* ECS_RFM0: 0xB7E30094(37) */
    debug_write(ECPRI_DBG_IDX0(38), pkt_len); /* ECS_RFM0: 0xB7E30098(38) */
    debug_write(ECPRI_DBG_IDX0(39), (uint32_t)SUCCESS); /* ECS_RFM0: 0xB7E3009C(39) */

    return SUCCESS;
}

/**********************************************************************************************************************
*  Function: ecpri_inform_arm
*  Description: eCPRI inform ARM
*  Input: 
*    <name>                    <type>                      <desc>
*     pkt_addr                  uint32_t                    packet address
*  Output: none
*  Return: operation result - success or failure.
*  Others: none
**********************************************************************************************************************/
int32_t ecpri_inform_arm(uint32_t pkt_addr)
{
    /* return value */
    int32_t ret = 0;

    /* input para check */
    if ((pkt_addr < ECPRI_RX_BUF_ADDR) || 
        (pkt_addr >= (ECPRI_RX_BUF_ADDR + ECPRI_RX_BUF_SIZE)))
    {
        debug_write(ECPRI_DBG_IDX0(44), (uint32_t)ERROR); /* ECS_RFM0: 0xB7E300B0(44) */
        debug_write(ECPRI_DBG_IDX0(45), pkt_addr); /* ECS_RFM0: 0xB7E300B4(45) */

        return ERROR;
    }

    /* smartos put data in hardware queue */
    ret = smart_in_que((int)E_CORE_ID_ARM0, pkt_addr);
    if (SUCCESS != ret)
    {
        debug_write(ECPRI_DBG_IDX0(44), (uint32_t)ERROR); /* ECS_RFM0: 0xB7E300B0(44) */
        debug_write(ECPRI_DBG_IDX0(45), pkt_addr); /* ECS_RFM0: 0xB7E300B4(45) */
        debug_write(ECPRI_DBG_IDX0(46), (uint32_t)ret); /* ECS_RFM0: 0xB7E300B8(46) */

        return ret;
    }

    debug_write(ECPRI_DBG_IDX0(44), (uint32_t)SUCCESS); /* ECS_RFM0: 0xB7E300B0(44) */
    debug_write(ECPRI_DBG_IDX0(45), pkt_addr); /* ECS_RFM0: 0xB7E300B4(45) */
    debug_write(ECPRI_DBG_IDX0(46), (uint32_t)ret); /* ECS_RFM0: 0xB7E300B8(46) */

    return SUCCESS;
}

/**********************************************************************************************************************
*  Function: ecpri_callback_register
*  Description: eCPRI callback function register
*  Input: 
*    <name>                    <type>                      <desc>
*     ecpri_callback            EcpriCallback               eCPRI callback function
*  Output: none
*  Return: none
*  Others: none
**********************************************************************************************************************/
void ecpri_callback_register(EcpriCallback ecpri_callback)
{
    /* register eCPRI callback function */
    g_ecpri_callback = ecpri_callback;
}

/**********************************************************************************************************************
*  Function: ecpri_pkt_stats_get
*  Description: eCPRI packet statistic get
*  Input: 
*    <name>                    <type>                      <desc>
*     ecpri_pkt_stats           EcpriPktStats_t *           eCPRI packet statistic
*  Output: none
*  Return: operation result - success or failure.
*  Others: none
**********************************************************************************************************************/
int32_t ecpri_pkt_stats_get(EcpriPktStats_t *ecpri_pkt_stats)
{
    /* input para check */
    if (NULL == ecpri_pkt_stats)
    {
        debug_write(ECPRI_DBG_IDX0(52), (uint32_t)ERROR); /* ECS_RFM0: 0xB7E300D0(52) */

        return ERROR;
    }

    /* get eCPRI packet statistic info */
    memcpy_ucp((void *)ecpri_pkt_stats, 
               (void *)&g_ecpri_ring_buf.ecpri_pkt_stats, 
               sizeof(EcpriPktStats_t));

    debug_write(ECPRI_DBG_IDX0(52), (uint32_t)SUCCESS); /* ECS_RFM0: 0xB7E300D0(52) */

    return SUCCESS;
}



