/**************************************************************************//**
 * @file     SpiRead.c
 * @brief    SPI functions for Spiloader
 *
 * SPDX-License-Identifier: Apache-2.0
 * @copyright (C) 2020 Nuvoton Technology Corp. All rights reserved.
*****************************************************************************/

#include "N9H20.h"

#define  SPICMD_DUMMY         0x00
#define  SPICMD_READ_DATA     0x03

#define CACHE_BIT31  BIT31

int spiActive(int port)
{
    outp32(REG_SPI0_CNTRL, inp32(REG_SPI0_CNTRL)|0x01);
    while(inp32(REG_SPI0_CNTRL) & 0x01);
    return 0;
}

int spiTxLen(int port, int count, int bitLen)
{
    UINT32 reg;

    reg = inp32(REG_SPI0_CNTRL);

    if ((count < 0) || (count > 3))
        return 1;

    if ((bitLen <= 0) || (bitLen > 32))
        return 1;

    if (bitLen == 32)
        reg = reg & 0xffffff07;
    else
        reg = (reg & 0xffffff07) | (bitLen << 3);

    reg = (reg & 0xfffffcff) | (count << 8);

    outp32(REG_SPI0_CNTRL, reg);

    return 0;
}

int usiCheckBusy()
{
    /* check status */
    outp32(REG_SPI0_SSR, inp32(REG_SPI0_SSR) | 1);    /* CS0 */

    /* status command */
    outp32(REG_SPI0_TX0, 0x05);
    spiTxLen(0, 0, 8);
    spiActive(0);

    /* get status */
    while(1)
    {
        outp32(REG_SPI0_TX0, 0xff);
        spiTxLen(0, 0, 8);
        spiActive(0);
        if (((inp32(REG_SPI0_RX0) & 0xff) & 0x01) != 0x01)
            break;
    }

    outp32(REG_SPI0_SSR, inp32(REG_SPI0_SSR) & 0xfe);  /* CS0 */

    return 0;
}

void EDMAMasterRead(UINT32 u32DestAddr, UINT32 u32Length)
{
    UINT32 u32SFRCSR,u32Value,u32SPIBitLen;

    /* Set Byte Endian */
    outp32(REG_SPI0_CNTRL, inp32(REG_SPI0_CNTRL) | 1<<20);

    u32SPIBitLen=0;

    outp32(REG_SPI0_CNTRL, (inp32(REG_SPI0_CNTRL) & ~Tx_BIT_LEN) | u32SPIBitLen<<3);    

    u32SFRCSR = REG_PDMA_CSR1;

    outp32(REG_PDMA_SAR1, REG_SPI0_RX0);             /* EDMA Source */
    outp32(REG_PDMA_DAR1,(u32DestAddr| 0x80000000)); /* EDMA Destination */
    outp32(REG_PDMA_BCR1, u32Length);                /* EDMA Byte Count */
    u32Value = 0x00000005;                           /* Src:Fix, Dest:Inc, IP2Mem, EDMA enable */
    //u32Value = u32Value | (eTransferWidth<<19);    /* Change APB_TWS */

    outp32(u32SFRCSR,u32Value);                      /* EDMA Transer width */

    outp32(u32SFRCSR,inp32(u32SFRCSR) | TRIG_EN);    /* Enable EDMA Trig_En */

    outp32(REG_SPI0_EDMA,0x03);                      /* Enable SPI EDMA Read & Start */
    outp32(REG_SPI0_CNTRL, inp32(REG_SPI0_CNTRL) |1);    /* master start */
    
    while(inp32(u32SFRCSR) & TRIG_EN);
    
}

int SPIReadFast(BOOL bEDMAread, UINT32 addr, UINT32 len, UINT32 *buf)
{
    int volatile i, j;
    UINT32 u32Count;
    //sysprintf("Load file length 0x%x, execute address 0x%x\n", len, (UINT32)buf);

    outp32(REG_SPI0_CNTRL, inp32(REG_SPI0_CNTRL) & ~(1<<20));    /* disabe BYTE endian */

    outp32(REG_SPI0_SSR, inp32(REG_SPI0_SSR) | 0x01);    /* CS0 */

    buf = (UINT32 *) ((UINT32)buf | (UINT32)CACHE_BIT31);

    /* read command */
    outp32(REG_SPI0_TX0, 0x0B);
    spiTxLen(0, 0, 8);
    spiActive(0);

    /* address */
    outp32(REG_SPI0_TX0, addr);
    spiTxLen(0, 0, 24);
    spiActive(0);

    /* dummy byte */
    outp32(REG_SPI0_TX0, 0xff);
    spiTxLen(0, 0, 8);
    spiActive(0);
    if(bEDMAread)
    {
        EDMAMasterRead((UINT32)buf, len);
    }
    else
    {
#if  0
        u32Count = len/4;
        if(len % 4)
            u32Count++;

        /* data */
        for (i=0; i<u32Count; i++)
        {
            outp32(REG_SPI0_TX0, 0xffffffff);
            spiTxLen(0, 0, 32);
            spiActive(0);
            u32Tmp = inp32(REG_SPI0_RX0);
            *buf++ = ((u32Tmp & 0xFF) << 24) | ((u32Tmp & 0xFF00) << 8) | ((u32Tmp & 0xFF0000) >> 8)| ((u32Tmp & 0xFF000000) >> 24);
        }
#else
        u32Count = len/16;
        if(len % 16)
            u32Count++;

        outpw(REG_SPI0_CNTRL, inpw(REG_SPI0_CNTRL) |BYTE_ENDIN);
        spiTxLen(0, 3, 32);

        // data
        for (i=0; i<u32Count; i++)
        {
            //spiTxLen(0, 3, 32);
            outpw(REG_SPI0_TX0, 0xffffffff);
            outpw(REG_SPI0_TX1, 0xffffffff);
            outpw(REG_SPI0_TX2, 0xffffffff);
            outpw(REG_SPI0_TX3, 0xffffffff);
            spiActive(0);
            for(j=0; j<4; j++)
            {
                *buf++ = inp32(REG_SPI0_RX0 + j*4);
            }
        }

        outpw(REG_SPI0_CNTRL, inpw(REG_SPI0_CNTRL) & ~BYTE_ENDIN);
        spiTxLen(0, 0, 32);
#endif
    }
    outp32(REG_SPI0_SSR, inp32(REG_SPI0_SSR) & 0xfe);    /* CS0 */

    return 0;
}

VOID SPI_OpenSPI(VOID)
{
    outp32(REG_APBCLK, inp32(REG_APBCLK) | SPIMS0_CKE);
    outp32(REG_GPDFUN, inp32(REG_GPDFUN) | (MF_GPD15|MF_GPD14|MF_GPD13|MF_GPD12));
    outp32(REG_SPI0_DIVIDER, 0x00);
}

VOID SPI_CloseSPI(VOID)
{
    outp32(REG_APBCLK, inp32(REG_APBCLK) & ~SPIMS0_CKE);
    /* Disable Pin function */
    outp32(REG_GPDFUN, inp32(REG_GPDFUN) & ~(MF_GPD15|MF_GPD14|MF_GPD13|MF_GPD12));
}