#include "ecpri_csu.h"


int32_t ecpri_csu_init(void)
{
    do_write(&JECS_CSU_EM_BS_SMSEL_PREDATANUM, ((0x1<<25) | (0x1<<14) | (0x5<<5) | 0x8)); // bit25: ecpri mode， bit14: FIFO1
    do_write(&JECS_CSU_FINDDMATAG, 0x60); // DMA搬移完成才能查到结束状态
    do_write(&JECS_CSU_TAGMASK0, 0xFFFFFFFF);

    for (uint8_t i = 0; i < ECPRI_CSU_REG_NUM; i++)
    {
        uint8_t offset = (i<<3);

        do_write((&JECS_CSU_DMAADDRL0 + offset), 0);
        do_write((&JECS_CSU_DMAADDRH0 + offset), 0);
        do_write((&JECS_CSU_DMAYNUMXNUM0 + offset), ((4 << 16) | 1024));
        do_write((&JECS_CSU_DMAYSTEPL0 + offset), 1024);
        do_write((&JECS_CSU_DMAYSTEPH0 + offset), 0);
        do_write((&JECS_CSU_DMAZSTEPL0 + offset), 4096);
        do_write((&JECS_CSU_DMAZSTEPH0 + offset), 0);
    }

    return 0;
}

/*!
* @brief:  设置ecpri的PreDataNum参数，只能设置FIFO1
* @author:  xinxin.li
* @Date:  2023年6月1日
* @param: preData0 :        [bit[4:0]: Global端未收到读响应的读请求个数未超过PREDATANUM[4:0]，CSU开始向总线发起读请求，直到Global端未收到读响应的读请求个数超过PREDATANUM[4:0]，CSU暂停向总线发起读请求]
* @param: preData1 :        [bit[9:5]: 未收到读响应的读请求个数不超过PREDATANUM[9:5]的值时，CSU再次向总线发起读请求 ]
* @return: -1：参数错误，0：配置成功
*/
int32_t ecpri_csu_set_predatanum(uint8_t preData0, uint8_t preData1)
{
    do_write(&JECS_CSU_EM_BS_SMSEL_PREDATANUM, ((0x1<<25) | (0x1<<14) | (preData1<<5) | preData0)); // bit25: ecpri mode， bit14: FIFO1

    return 0;
}

//tag           0                               reg_group = 0(0->1)     CmdFIFO =   1   rfm
//tag           1                                           1(2->3)                 1
//tag           2                                           2(4->5)                 1
//tag           3                                           3(6->7)                 1
//tag           4                                           4(8->9)                 1
//tag           5                                           5(10->11)               1
//tag           6                                           6(12->13)               1
//tag           7                                           7(14->15)               1
//tag           8                                           8(16->17)               1
//tag           9                                           9(18->19)               1
//tag           10                                          10(20->21)              1
//tag           11                                          11(22->23)              1


//tag           12                              reg_group = 12(24->25)  CmdFIFO =   1   arm
//tag           13                                          13(26->27)              1
//tag           14                                          14(28->29)              1
//tag           15                                          15(30->31)              1


// get unused register group according to tag number
int32_t ecpri_csu_get_free_reg_group(uint8_t tag)
{
    if (ECPRI_CSU_RFM_REG_END < tag)
    {
        return -1;
    }

    uint8_t regGroup = tag; //0~11
    uint32_t bitMap = (0x1<<regGroup);
    uint32_t dmaStatus = do_read_volatile(&JECS_CSU_DMASTATUS);
    __ucps2_synch(0);
    if (bitMap != (dmaStatus & bitMap))
    {
        return -1;
    }

    return regGroup; // reg group(0~11)
}

uint32_t ecpri_csu_get_free_channel(void)
{
    while (1)
    {
        for (int tag = ECPRI_CSU_RFM_REG_START; tag <= ECPRI_CSU_RFM_REG_END; tag++)
        {
            if (0 == (do_read_volatile(&JECS_CSU_DMASTATUS) & (1<<tag)))
            {
                continue;
            }
            else
            {
                int32_t temp = ecpri_csu_get_free_reg_group(tag);
                if (-1 == temp)
                {
                    continue;
                }
                else
                {
                    uint8_t regGroup = temp & 0xFF;
                    uint32_t ret = regGroup | (tag<<8);
                    return ret;
                }
            }
        }
    }
}

/* ecpri内存拷贝测试DDR--->DDR */
uint32_t ecpri_csu_send(uint64_t dataAddr, uint32_t dataLen, uint8_t isWait)
{
    uint32_t temp = 0;
    uint8_t regGroup = 0;
    uint8_t tag = 0;
    uint16_t offset_w = 0;

    temp = ecpri_csu_get_free_channel();
    regGroup = temp & 0xFF;
    tag      = (temp>>8) & 0xFF;
    offset_w = regGroup << 4;

    //src reg
    do_write((&JECS_CSU_DMAADDRL0 + offset_w), (dataAddr & 0xFFFFFFFF));
    do_write((&JECS_CSU_DMAADDRH0 + offset_w), (uint32_t)(dataAddr >> 32));
    do_write((&JECS_CSU_DMASIZEGRANALLNUM0 + offset_w), ((0xF<<28) | (0xE<<24) | dataLen)); // DM,CGran=1,Gran=6;size=0xF
    //dst reg
    //do_write((&JECS_CSU_DMAADDRL1 + offset_w), 0);
    //do_write((&JECS_CSU_DMAADDRH1 + offset_w), 0);
    //do_write((&JECS_CSU_DMASIZEGRANALLNUM1 + offset_w), ((0x8<<28) | (0x0<<24) | dataLen)); // 1<<{GRAN[3],SIZE[3]}
    do_write((&JECS_CSU_DMASIZEGRANALLNUM1 + offset_w), ((0x9<<28) | (0x0<<24) | dataLen)); // 1<<{GRAN[3],SIZE[3]}, SIZE[2:0]->packet_end, GRAN[2:0]->section_end

    stCsuDmaCmdL dmaCmdL;
    *(uint32_t *)(&dmaCmdL) = 0;
    dmaCmdL.dmaType = 1;
    dmaCmdL.idSrc = 0 + (regGroup<<1);
    dmaCmdL.idDst = 1 + (regGroup<<1);
    dmaCmdL.dmaTag = tag;
    dmaCmdL.ecpriEnd = 1; // packet end
    do_write(((stCsuDmaCmdL *)(&JECS_CSU_CMDFIFO1)), (*(uint32_t *)(&dmaCmdL)));
    __ucps2_synch(0);

    if (1 == isWait)
    {
        while (0 == (do_read_volatile(&JECS_CSU_DMASTATUS) & (1 << tag)));
    }

    return tag;
}

int32_t ecpri_csu_tx_list_init(uint32_t listAddr, uint32_t nodeNum, stEcpriCsuNodePara* pListNode)
{
    // 设置链表节点CMD
    uint32_t txListCmdL = 0;
    uint32_t txListCmdH = 0;
    // bit4=1，多维DMA
    // bit5=0，普通模式
    // bit[7:6]=3，该DMA可跟随上一个DMA，下一个DMA可跟随本DMA
    txListCmdL = (1<<4)+(0<<5)+(1<<24);

    stCsuLinkDesc1L3D* pLinkDesc = NULL;
    stCsuLinkDesc1L3D stLinkDesc;

    // tx: ddr -> jesd
    int32_t i = 0;
    for(i = 0; i < nodeNum; i++)
    {
        // src
        pLinkDesc = (stCsuLinkDesc1L3D*)listAddr + 2*i;
        memset(&stLinkDesc, 0, sizeof(stCsuLinkDesc1L3D));
        stLinkDesc.cmdFifoL = txListCmdL;
        stLinkDesc.cmdFifoH = txListCmdH & 0x3FFF;
        stLinkDesc.dmaAddrL = pListNode[i].dataAddr;
        stLinkDesc.dmaXNum = 0x200;
        stLinkDesc.dmaYStep = 0x200;
        stLinkDesc.dmaYNum = 1;
        stLinkDesc.dmaZStep = 0x200;
        stLinkDesc.dmaAllNum = pListNode[i].allNum;
        stLinkDesc.dmaSize = 0xF;
        stLinkDesc.dmaGran = 6;
        stLinkDesc.dmaCGran = 1;
        stLinkDesc.nAddrMode = 0;   // list addr is contiguous
        memcpy_ext(pLinkDesc, &stLinkDesc, sizeof(stCsuLinkDesc1L3D));
        // dst
        pLinkDesc = (stCsuLinkDesc1L3D*)listAddr + 2*i + 1;
        memset(&stLinkDesc, 0, sizeof(stCsuLinkDesc1L3D));
        stLinkDesc.cmdFifoL = txListCmdL;
        stLinkDesc.cmdFifoH = txListCmdH & 0x3FFF;
        stLinkDesc.dmaAddrL = 0;
        stLinkDesc.dmaXNum = 0x10;
        stLinkDesc.dmaYStep = 0x10;
        stLinkDesc.dmaYNum = 1;
        stLinkDesc.dmaZStep = 0x10;
        stLinkDesc.dmaAllNum = pListNode[i].allNum;
        stLinkDesc.dmaSize = 0x9;
        stLinkDesc.dmaGran = 0;
        stLinkDesc.dmaCGran = 0;
        stLinkDesc.nAddrMode = 0;   // list addr is contiguous
        memcpy_ext(pLinkDesc, &stLinkDesc, sizeof(stCsuLinkDesc1L3D));
    }

    return 0;
}

int32_t ecpri_csu_tx_dmaReg_Cfg(uint8_t nRegGroupId, uint32_t nListAddr, uint8_t nNodeNum) // uint32_t listAddr, uint32_t nodeNum)
{
    //if ((0 == nListAddr) || (0 == nNodeNum))
    {
        //return -1;
    }

    uint16_t offset_w = nRegGroupId << 4;

    //src reg
    do_write((&JECS_CSU_DMAADDRL0 + offset_w), nListAddr);
    do_write((&JECS_CSU_DMAADDRH0 + offset_w), 0);
    do_write((&JECS_CSU_DMASIZEGRANALLNUM0 + offset_w), nNodeNum);

    return 0;
}

int32_t ecpri_csu_link_send(uint32_t listAddr, uint32_t nodeNum, stEcpriCsuNodePara* pListNode, uint8_t isWait)
{
    uint8_t regGroup = 0;
    uint8_t tag      = 0;
    uint32_t temp    = ecpri_csu_get_free_channel();

    regGroup = temp & 0xFF;
    tag      = (temp>>8) & 0xFF;

#if 0
    // link list init
    if (0 != ecpri_csu_tx_list_init(listAddr, nodeNum, pListNode))
    {
        return -1;
    }
#endif

    // dma regisger config
    if (0 != ecpri_csu_tx_dmaReg_Cfg(regGroup, listAddr, nodeNum))
    {
        return -1;
    }

    // write cmd fifo
#if 0
    stCsuDmaCmdL dmaCmdL;
    *(uint32_t *)(&dmaCmdL) = 0;
    dmaCmdL.rCmd = 1;
    dmaCmdL.wCmd = 1;
    dmaCmdL.dmaType = 1;
    dmaCmdL.idSrc = 0 + (regGroup<<1);
    dmaCmdL.idDst = 0;
    dmaCmdL.dmaTag = tag;
    //dmaCmdL.ecpriEnd = 1; // packet end
    do_write(((stCsuDmaCmdL *)(&JECS_CSU_CMDFIFO1)), (*(uint32_t *)(&dmaCmdL)));
#else
    do_write(((stCsuDmaCmdL *)(&JECS_CSU_CMDFIFO1)), (0x1+(0x1<<2)+(0x1<<4)+((regGroup<<1)<<8)+(tag<<18)));
#endif
    __ucps2_synch(0);

    if (1 == isWait)
    {
        while (0 == (do_read_volatile(&JECS_CSU_DMASTATUS) & (1 << tag)));
    }

    return tag;
}

/*!
* @brief:  查看tag号对应的dma任务是否完成，未完成返回0，完成返回1
*          如果isWait==1，则等待该tag对应的任务完成，再返回，返回1
* @author:  xinxin.li
* @Date:  2023年6月1日
* @param: task_tag :         [DMA Tag: 0~31用于查询是否完成 ]
* @param: isWait :           [是否等DMA结束函数返回0:不等待,1:等待 ]
* @return: 1：已完成，0：未完成
*/
int32_t ecpri_csu_task_lookup(uint8_t task_tag, uint8_t isWait)
{
    if (0 == isWait)
    {
        return (do_read_volatile(&JECS_CSU_DMASTATUS) & (1 << task_tag));
    }
    else
    {
        while (0 == (do_read_volatile(&JECS_CSU_DMASTATUS) & (1 << task_tag)))
        {
            ucp_nop(1);
        }

        return 1;
    }
}



