/**
 * \file    drv_spi.c
 * \brief   SPI interface drivers
 *
 * \par     Copyright (c) 2018 UWE Technologies Corp., Ltd. All Rights Reserved.
 * \author  James
 * \date    2018-04-23
 * \version 
 * 
 * \history 
 *  Date        Note
 *  2018-04-23  Original Creation
 * 
 */

#include "libc.h"
#include "drv_uart.h"
#include "drv_spi.h"
#include "sync.h"
#if SAL_MSQ_ENABLED
#include "msq.h"
#endif

#if SPI_MODULE_ENABLED


#if SYS_FREERTOS_ENABLED
extern QueueHandle_t xInterfaceQueue;
#endif /* SYS_FREERTOS_ENABLED */

#define DRV_SPI_READ_TIMEOUT         (1000000UL)

// *******************************************************************
// Global variables section

volatile DRV_SPI_INFO_t g_spi_info;

// *******************************************************************
//  Code implementation

void drv_spi_intr_handler(u32_t arg)
{
    //disable SPI interrupt
    drv_spi_intr_disable();

    g_spi_info.last_status = DRV_REG_GET(SPSTA_REG);

    if (g_spi_info.isr_dbg_enabled)
    {
        drv_uart_putstr("SPSTA=");
        drv_uart_puthex8(g_spi_info.last_status);
        drv_uart_putchar(UART_LF);
    }

    if(g_spi_info.last_status & SPI_STA_SPIF)
    {
        if (g_spi_info.master_mode)
        {
            *(g_spi_info.rx_buf_ptr) = DRV_REG_GET(SPDAT_REG);

            if(g_spi_info.buf_len != 0)
            {
                DRV_REG_GET(SPDAT_REG) = *(g_spi_info.tx_buf_ptr);
                g_spi_info.tx_buf_ptr += g_spi_info.tx_buf_inc;
                g_spi_info.rx_buf_ptr += g_spi_info.rx_buf_inc;
                g_spi_info.buf_len--;
            }
            else {
                //all bytes has been transfered
                //transmission complete
                g_spi_info.transmit_cmplt = 1;
            }
        }
#if SPI_MODULE_SLAVE_ENABLED
        else { //SPI_MODULE_SLAVE_ENABLED
            g_spi_info.rx_data = DRV_REG_GET(SPDAT_REG);
            if (g_spi_info.isr_dbg_enabled)
            {
                drv_uart_putstr("SPDAT=");
                drv_uart_puthex8(g_spi_info.rx_data);
                drv_uart_putchar(UART_LF);
            }
            if (g_spi_info.sla_rx_buf_index < SPI_SLA_RX_BUF_SIZE)
            {
                g_spi_info.sla_rx_buf[g_spi_info.sla_rx_buf_index++] = g_spi_info.rx_data;
                if (g_spi_info.sla_rx_buf_index == g_spi_info.sla_rx_buf_cnt)
                {
                    //all necessary bytes has been received
                    //receiption complete
                    g_spi_info.receive_cmplt = 1;
#if SAL_MSQ_ENABLED
                    msq_push_msg(SAL_MSQ_SPI_SLA, 0, 0, 0, 0);
#else
                    xQueueSendFromISR(xInterfaceQueue, (void *)&arg, NULL);
#endif /* SAL_MSQ_ENABLED */
                    if (g_spi_info.isr_dbg_enabled)
                    {
                        drv_uart_putstr("SPXQ\n");
                    }
                }
            }
            //if send buffer is not empty send one byte from it
            if (g_spi_info.sla_tx_buf_cnt > 0)
            {
                g_spi_info.sla_tx_buf_cnt--;
                DRV_REG_GET(SPDAT_REG) = g_spi_info.sla_tx_buf[g_spi_info.sla_tx_buf_index++];
                if (g_spi_info.sla_tx_buf_cnt == 0)
                {
                    g_spi_info.sla_rx_buf_index = 0;
                }
            }
        }
#endif /* SPI_MODULE_SLAVE_ENABLED */
    }

    //enable SPI interrupt
    drv_spi_intr_en();
}

u8_t drv_spi_open(u8_t master, u8_t ssn, u8_t data_rate, u8_t cpol_cpha)
{
    memset((void *)(&g_spi_info), 0, sizeof(DRV_SPI_INFO_t));
    //read status to clear interrupt flag ...
    drv_reg_get(SPSTA_REG);

    //set speed of SPI interface
    switch (data_rate)
    {
        case SPI_CLOCK_NOT_GENERATED:
            DRV_REG_FIELD_SET(SPCON_REG, SPR0, 1);
            DRV_REG_FIELD_SET(SPCON_REG, SPR1, 1);
            DRV_REG_FIELD_SET(SPCON_REG, SPR2, 1);
            break;
        case SPI_SPEED_FCLK_DIVIDE_BY_128:
            DRV_REG_FIELD_SET(SPCON_REG, SPR0, 0);
            DRV_REG_FIELD_SET(SPCON_REG, SPR1, 1);
            DRV_REG_FIELD_SET(SPCON_REG, SPR2, 1);
            break;
        case SPI_SPEED_FCLK_DIVIDE_BY_64:
            DRV_REG_FIELD_SET(SPCON_REG, SPR0, 1);
            DRV_REG_FIELD_SET(SPCON_REG, SPR1, 0);
            DRV_REG_FIELD_SET(SPCON_REG, SPR2, 1);
            break;
        case SPI_SPEED_FCLK_DIVIDE_BY_32:
            DRV_REG_FIELD_SET(SPCON_REG, SPR0, 0);
            DRV_REG_FIELD_SET(SPCON_REG, SPR1, 0);
            DRV_REG_FIELD_SET(SPCON_REG, SPR2, 1);
            break;
        case SPI_SPEED_FCLK_DIVIDE_BY_16:
            DRV_REG_FIELD_SET(SPCON_REG, SPR0, 1);
            DRV_REG_FIELD_SET(SPCON_REG, SPR1, 1);
            DRV_REG_FIELD_SET(SPCON_REG, SPR2, 0);
            break;
        case SPI_SPEED_FCLK_DIVIDE_BY_4:
            DRV_REG_FIELD_SET(SPCON_REG, SPR0, 1);
            DRV_REG_FIELD_SET(SPCON_REG, SPR1, 0);
            DRV_REG_FIELD_SET(SPCON_REG, SPR2, 0);
            break;
        case SPI_SPEED_FCLK_DIVIDE_BY_2:
            DRV_REG_FIELD_SET(SPCON_REG, SPR0, 0);
            DRV_REG_FIELD_SET(SPCON_REG, SPR1, 0);
            DRV_REG_FIELD_SET(SPCON_REG, SPR2, 0);
            break;
        case SPI_SPEED_FCLK_DIVIDE_BY_8:
        default:
            DRV_REG_FIELD_SET(SPCON_REG, SPR0, 0);
            DRV_REG_FIELD_SET(SPCON_REG, SPR1, 1);
            DRV_REG_FIELD_SET(SPCON_REG, SPR2, 0);
            break;
    }

    switch (cpol_cpha)
    {
        case SPI_CPOL_CPHA_11:
            DRV_REG_FIELD_SET(SPCON_REG, CPOL, 1);
            DRV_REG_FIELD_SET(SPCON_REG, CPHA, 1);
            break;
        case SPI_CPOL_CPHA_10:
            DRV_REG_FIELD_SET(SPCON_REG, CPOL, 1);
            DRV_REG_FIELD_SET(SPCON_REG, CPHA, 0);
            break;
        case SPI_CPOL_CPHA_01:
            DRV_REG_FIELD_SET(SPCON_REG, CPOL, 0);
            DRV_REG_FIELD_SET(SPCON_REG, CPHA, 1);
            break;
        case SPI_CPOL_CPHA_00:
        default:
            DRV_REG_FIELD_SET(SPCON_REG, CPOL, 0);
            DRV_REG_FIELD_SET(SPCON_REG, CPHA, 0);
            break;
    }

    g_spi_info.master_mode = master;
    if (master)
    {
        //set master mode
        DRV_REG_FIELD_SET(SPCON_REG, MSTR, 1);
        //enable 
        drv_reg_field_set(SPCON_REG, SPCON_REG_SPEN + ssn, 1, 1);
    }
#if SPI_MODULE_SLAVE_ENABLED
    else {
        //disable master mode
        DRV_REG_FIELD_SET(SPCON_REG, MSTR, 0);
        //enable slave
        DRV_REG_FIELD_SET(SPCON_REG, SPEN, (1 << SPI_SSN_3));
    }

    drv_spi_sla_flush();
#endif /* SPI_MODULE_SLAVE_ENABLED */

    g_spi_info.tx_data = 0xFFFF;

    //enable SPI interrupt
    drv_intr_ext_set(IPIC_CFG_INTR_IDX_SPI_INT, TRUE, IPIC_INTR_PRI_6, drv_spi_intr_handler);


    return 0;
}


void drv_spi_close(void)
{
    if (g_spi_info.dma_handling)
    {
        drv_dma1_stop();
        g_spi_info.dma_handling = 0;
    }

    //disable SPI interrupt
    drv_intr_ext_set(IPIC_CFG_INTR_IDX_SPI_INT, FALSE, IPIC_INTR_PRI_6, NULL);

    //disable SPI interface
    DRV_REG_GET(SPCON_REG) = 0;
}

#if SPI_MODULE_SLAVE_ENABLED
void drv_spi_sla_flush(void)
{
    // Flush buffer
    g_spi_info.sla_tx_buf_index = 0;
    g_spi_info.sla_tx_buf_cnt = 0;
    g_spi_info.sla_rx_buf_index = 0;
    g_spi_info.sla_rx_buf_cnt = 1;
}

#if SPI_MODULE_SLAVE_DMA_ENABLED
static void drv_spi_sla_dma_handler(u32_t arg)
{
    drv_dma1_intr_en_set(FALSE);
    // transmition
    if (g_spi_info.receive_cmplt)
    {
        g_spi_info.sla_tx_buf_index += g_spi_info.sla_tx_buf_cnt;
        g_spi_info.transmit_cmplt = 1;
        drv_dma1_stop();
    }
    else { // receiption
        g_spi_info.sla_rx_buf_index++;
        //all necessary bytes has been received, then prepare to transfer data
        if ((drv_dma1_dst_addr_get() == (u32_t)&(g_spi_info.sla_rx_buf[0])) || 
            (g_spi_info.sla_rx_buf_index == g_spi_info.sla_rx_buf_cnt))
        {
            // receiption complete
            drv_dma1_memcpy((void *)SPDAT_REG, (const void *)&(g_spi_info.sla_tx_buf[0]), g_spi_info.sla_tx_buf_cnt, DRV_DMA_MODE_DES_ADDR_HOLD, TRUE, drv_spi_sla_dma_handler);
            g_spi_info.receive_cmplt = 1;
            timer_start(SPI_SLA_TX_TIMEOUT, &(g_spi_info.sla_session_timer));
        }
    }
    drv_dma1_intr_en_set(TRUE);
}

void drv_spi_sla_dma_start(void)
{
    g_spi_info.dma_handling = 1;
    drv_intr_ext_set(IPIC_CFG_INTR_IDX_SPI_INT, FALSE, IPIC_INTR_PRI_6, NULL);
    g_spi_info.transmit_cmplt = 0;
    g_spi_info.receive_cmplt = 0;
    g_spi_info.sla_tx_buf_index = 0;
    g_spi_info.sla_rx_buf_index = 0;
    if (g_spi_info.sla_tx_buf_cnt > SPI_SLA_TX_BUF_SIZE)
        g_spi_info.sla_tx_buf_cnt = SPI_SLA_TX_BUF_SIZE;
    if (g_spi_info.sla_rx_buf_cnt > SPI_SLA_RX_BUF_SIZE)
        g_spi_info.sla_rx_buf_cnt = SPI_SLA_RX_BUF_SIZE;

    if (g_spi_info.isr_dbg_enabled)
    {
        drv_uart_putstr("SPDMA-RX:");
        drv_uart_puthex8(g_spi_info.sla_rx_buf_cnt);
        drv_uart_putchar(UART_LF);
    }
    drv_dma1_memcpy((void *)&(g_spi_info.sla_rx_buf[0]), (const void *)SPDAT_REG, g_spi_info.sla_rx_buf_cnt, DRV_DMA_MODE_SRC_ADDR_HOLD, TRUE, drv_spi_sla_dma_handler);
}
#endif /* SPI_MODULE_SLAVE_DMA_ENABLED */
#endif /* SPI_MODULE_SLAVE_ENABLED */

u8_t drv_spi_transmit(u8_t *tx_buf, u8_t *rx_buf, int len)
{
    u8_t sta;

    //if Reecive buffer is NULL that is mean the data
    //will be only sending
    //so Rx buffer is not needed
    if(rx_buf == NULL)
    {
        // for little endian
        g_spi_info.rx_buf_ptr = (u8_t *)(&(g_spi_info.rx_data));
        g_spi_info.rx_buf_inc = 0;
    }
    else
    {
        g_spi_info.rx_buf_ptr = rx_buf;
        g_spi_info.rx_buf_inc = 1;
    }

    g_spi_info.transmit_cmplt = 0;

    //if sending buffer is NULL that is mean the data
    //will be only reciving
    //so Tx buffer is not needed
    if(tx_buf == NULL)
    {
        // for little endian
        g_spi_info.tx_buf_ptr = (u8_t *)(&(g_spi_info.tx_data));
        g_spi_info.tx_buf_inc = 0;
    }
    else
    {
        g_spi_info.tx_buf_ptr = tx_buf;
        g_spi_info.tx_buf_inc = 1;
    }

    g_spi_info.buf_len = len - 1;
    //send first data byte
    DRV_REG_GET(SPDAT_REG) = *(g_spi_info.tx_buf_ptr);
    g_spi_info.tx_buf_ptr += g_spi_info.tx_buf_inc;

    //wait for send all data
    sys_tick_t start = now();
    g_spi_info.last_status = SPI_STA_TIMEOUT;
    while ((g_spi_info.transmit_cmplt == 0) && ((u32_t)(now() - start) < DRV_SPI_READ_TIMEOUT));

    sta = g_spi_info.last_status;
    g_spi_info.last_status = 0;
    return sta;
}

#endif /* SPI_MODULE_ENABLED */

