#include "omw_spim.h"
#include "t1001_rtc.h"

#define SR_TX_NOTFIN            0x1
#define SR_TRANS_EMPTY          0x4
#define SR_RCV_NOTEMPTY         0x8

// SPI
#define CTRLR0               0x00  //*(volatile int *)(OMW_SPIS_BASE + 0x00)
#define CTRLR1               0x01  //*(volatile int *)(OMW_SPIS_BASE + 0x04)
#define SSIENR               0x02  //*(volatile int *)(OMW_SPIS_BASE + 0x08)
#define SER                  0x04  //*(volatile int *)(OMW_SPIS_BASE + 0x10)
#define BAUDR                0x05  //*(volatile int *)(OMW_SPIS_BASE + 0x14)
#define TXFLR                0x08  //*(volatile int *)(OMW_SPIS_BASE + 0x20)
#define RXFLR                0x09  //*(volatile int *)(OMW_SPIS_BASE + 0x24)
#define SR                   0x0A  //*(volatile int *)(OMW_SPIS_BASE + 0x28)
#define IMR                  0x0B  //*(volatile int *)(OMW_SPIS_BASE + 0x2C)
#define DR                   0x18  //*(volatile int *)(OMW_SPIS_BASE + 0x60)

 //RTC
#define RTC_CTRL             0x14  //*(volatile int *)(0x40080000 + 0x50)
#define AON_RTC              0x2C  //*(volatile int *)(0x40000000 + 0xB0)

#define SPI_DIR_TX        0x01
#define SPI_DIR_RX        0x02
#define SPI_DIR_BIT_POS   8
#define SPI_DIR_MASK      (3 << SPI_DIR_BIT_POS)

#define SPI_TX_THR        8 // <17
#define DELAY_TIMES       5 // ms

#define MAX_SPIM0_PIN_NUM     4
#define MAX_SPIM1_PIN_NUM     4

#ifdef OMW_SPIM0
const struct gpio_cfg_tag spim0_pin_cfg[] = {
    {OMW_SPIM0_PIN_NCS,  OMW_SPIM0_FUN_CFG_NCS},
    {OMW_SPIM0_PIN_SCK,  OMW_SPIM0_FUN_CFG_SCK},
    {OMW_SPIM0_PIN_MISO, OMW_SPIM0_FUN_CFG_MISO},
    {OMW_SPIM0_PIN_MOSI, OMW_SPIM0_FUN_CFG_MOSI}
};

uint8_t g_spim0_line_mode;  //0 3lines, 1 4lines
#endif

#ifdef OMW_SPIM1
const struct gpio_cfg_tag spim1_pin_cfg[] = {
    {OMW_SPIM1_PIN_NCS,  OMW_SPIM1_FUN_CFG_NCS},
    {OMW_SPIM1_PIN_SCK,  OMW_SPIM1_FUN_CFG_SCK},
    {OMW_SPIM1_PIN_MISO, OMW_SPIM1_FUN_CFG_MISO},
    {OMW_SPIM1_PIN_MOSI, OMW_SPIM1_FUN_CFG_MOSI}
};

uint8_t g_spim1_line_mode;  //0 3lines, 1 4lines
#endif

static void spim_init(uint32_t spim_base_addr, uint16_t baud)
{
    volatile uint32_t * spi_cfg_addr = (volatile uint32_t *)spim_base_addr;
    uint8_t  lm = OMW_SPIM_4LINES;

    if(spim_base_addr == OMW_SPIM0_BASE)
    {
        #ifdef OMW_SPIM0
        lm = g_spim0_line_mode;
        #endif
    }
    else
    {
        #ifdef OMW_SPIM1
        lm = g_spim1_line_mode;
        #endif
    }
    ////TODO: if more than one SPIM, need to cmp spim base address, and get line mode

    //make spi device in idle state before starting to config
    spi_cfg_addr[SSIENR] = 0x0;   //disable
    spi_cfg_addr[CTRLR0] = ((SPI_DIR_TX << SPI_DIR_BIT_POS) | 0x7); //default: TX
    spi_cfg_addr[IMR]    = 0x0;   //INT disable
    spi_cfg_addr[BAUDR]  = baud;   //must be EVEN???

    if (lm == OMW_SPIM_3LINES)
        spi_cfg_addr[SSIENR] = 0x03;  //enable
    else
        spi_cfg_addr[SSIENR] = 0x01;
}

void omw_spim_init(uint32_t spim_base_addr, uint8_t line_mode, uint16_t baud)
{
    const struct gpio_cfg_tag  * pin_cfg = NULL;


    if(spim_base_addr == OMW_SPIM0_BASE)
    {
        #ifdef OMW_SPIM0
        pin_cfg = spim0_pin_cfg;
        g_spim0_line_mode =line_mode;
        #endif
    }
    else
    {
        #ifdef OMW_SPIM1
        pin_cfg = spim1_pin_cfg;
        g_spim1_line_mode =line_mode;
        #endif
    }
    ////TODO: if more than one SPIM, need to cmp spim base address, and get pin cfg info, and line mode info

    spim_init(spim_base_addr,baud);
    if(spim_base_addr == OMW_SPIM0_BASE)
    {
        omw_gpio_set_func_ex(pin_cfg, MAX_SPIM0_PIN_NUM);

    }
    else
    {
        omw_gpio_set_func_ex(pin_cfg, MAX_SPIM1_PIN_NUM);
    }
}

static void spim_tx_sw(uint32_t spim_base_addr)
{
    volatile uint32_t * spi_cfg_addr = (volatile uint32_t *)spim_base_addr;
    uint8_t  lm = OMW_SPIM_4LINES;
    if(spim_base_addr == OMW_SPIM0_BASE)
    {
        #ifdef OMW_SPIM0
        lm = g_spim0_line_mode;
        #endif
    }
    else
    {
        #ifdef OMW_SPIM1
        lm = g_spim1_line_mode;
        #endif
    }
    ////TODO: if more than one SPIM, need to cmp spim base address, and get line mode

    //make spi device in idle state before starting to config
    if (lm == OMW_SPIM_3LINES)
        spi_cfg_addr[SSIENR] = 0x2 ;  // 3 lines, disable
    else
        spi_cfg_addr[SSIENR] = 0;

    spi_cfg_addr[CTRLR0] = ((SPI_DIR_TX << SPI_DIR_BIT_POS) | 0x7); //[9:8] 1 tx, 2 rx

    if (lm == OMW_SPIM_3LINES)
        spi_cfg_addr[SSIENR] = 0x03;  // 3 lines, enable
    else
        spi_cfg_addr[SSIENR] = 0x01;

}

static void spim_rx_sw(uint32_t spim_base_addr, uint32_t blen)
{
    volatile uint32_t * spi_cfg_addr = (volatile uint32_t *)spim_base_addr;
    uint8_t  lm = OMW_SPIM_4LINES;
    if(spim_base_addr == OMW_SPIM0_BASE)
    {
        #ifdef OMW_SPIM0
        lm = g_spim0_line_mode;
        #endif
    }
    else
    {
        #ifdef OMW_SPIM1
        lm = g_spim1_line_mode;
        #endif
    }
    ////TODO: if more than one SPIM, need to cmp spim base address, and get line mode

    //make spi device in idle state before starting to config
    if (lm == OMW_SPIM_3LINES)
        spi_cfg_addr[SSIENR] = 0x2 ;  // 3 lines, disable
    else
        spi_cfg_addr[SSIENR] = 0;

    spi_cfg_addr[CTRLR0] = ((SPI_DIR_RX << SPI_DIR_BIT_POS) | 0x7); //[9:8] 1 tx, 2 rx
    spi_cfg_addr[CTRLR1] = blen;

    if (lm == OMW_SPIM_3LINES)
        spi_cfg_addr[SSIENR] = 0x03;  // 3 lines, enable
    else
        spi_cfg_addr[SSIENR] = 0x01;
}

static void rtc_delay_ms(int ms) {
    unsigned long t = 0;

    volatile uint32_t * rtc_cfg_addr = (volatile uint32_t *)(0x40080000);
    volatile uint32_t * rtc_val_addr = (volatile uint32_t *)(0x40000000);

	rtc_cfg_addr[RTC_CTRL] =1;

    t = (unsigned long)(rtc_val_addr[AON_RTC]);
    t += (unsigned long)(ms * 32);
    unsigned long current = (unsigned long)(rtc_val_addr[AON_RTC]);
    while (current < t) {
        current = (unsigned long)(rtc_val_addr[AON_RTC]);
    }
	rtc_cfg_addr[RTC_CTRL] =1;
}

void omw_spim_send(uint32_t spim_base_addr, void * pdata, uint32_t len)
{
    uint8_t  *ptmp = pdata;
    uint32_t pos = 0;

    volatile uint32_t * spi_cfg_addr = (volatile uint32_t *)spim_base_addr;

    spim_tx_sw(spim_base_addr);
    spi_cfg_addr[SER] =0x0;
    if(len <= SPI_TX_THR)
    {
        while (pos < len)
        {
            spi_cfg_addr[DR] = ptmp[pos++];
        }
        spi_cfg_addr[SER] =0x1;
    }
    else
    {
        while(spi_cfg_addr[TXFLR]<SPI_TX_THR)
        {
            spi_cfg_addr[DR] = ptmp[pos++];
        }
        spi_cfg_addr[SER] =0x1;
        while (pos < len)
        {
            while (spi_cfg_addr[TXFLR] > SPI_TX_THR);
            spi_cfg_addr[DR] = ptmp[pos++];
        }
    }

    while(spi_cfg_addr[SR] & SR_TX_NOTFIN || (spi_cfg_addr[SR] & SR_TRANS_EMPTY) == 0);
    spi_cfg_addr[SER] =0x0;

}

uint32_t omw_spim_rcv(uint32_t spim_base_addr, void * pbuf, uint32_t blen)
{
    uint32_t pos   = 0;
    uint8_t  *ptmp = pbuf;

    volatile uint32_t * spi_cfg_addr = (volatile uint32_t *)spim_base_addr;
    uint8_t  lm = OMW_SPIM_4LINES;
    if(spim_base_addr == OMW_SPIM0_BASE)
    {
        #ifdef OMW_SPIM0
        lm = g_spim0_line_mode;
        #endif
    }
    else
    {
        #ifdef OMW_SPIM1
        lm = g_spim1_line_mode;
        #endif
    }
    spim_rx_sw(spim_base_addr,blen);
    rtc_delay_ms(DELAY_TIMES);
    spi_cfg_addr[SER] =0x1;
    spi_cfg_addr[DR] = 0x00;
    ////TODO: if more than one SPIM, need to cmp spim base address, and get line mode

    while ((pos < blen) && (((lm == OMW_SPIM_3LINES) && (spi_cfg_addr[SR] & SR_RCV_NOTEMPTY))
                            || ((lm == OMW_SPIM_4LINES) && spi_cfg_addr[RXFLR])))
    {
        ptmp[pos++] = spi_cfg_addr[DR];
    }

    return pos;
}

