/**
 * \file    drv_spi.h
 * \brief   SPI interface drivers header file
 *
 * \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
 * 
 */
 
#ifndef __DRV_SPI_H_
#define __DRV_SPI_H_


#include "types.h"
#include "drv_interrupt.h"
#include "drv_reg_intf.h"
#include "reg_def_spi.h"
#include "timer.h"

#if SPI_MODULE_ENABLED

#pragma pack(1)

// SPI status register bits
/// Serial Peripheral buf Transfer Flag
#define SPI_STA_SPIF    0x80
/// Write collision Flag
#define SPI_STA_WCOL    0x40
/// Synchronous Serial Slave Error Flag
#define SPI_STA_SSERR   0x20
/// Mode Fault Flag
#define SPI_STA_MODF    0x10
/// SPI timeout
#define SPI_STA_TIMEOUT 0xFF

// SPI control register bits
///Serial Peripheral Enable 
#define SPCON_EN        0xF00
#define SPCON_SLA_EN    0x800
///Serial Peripheral Frame Mode, 1: 16bits mode, 0: 8bits mode 
#define SPCON_SPFRM     0x80
///Serial Peripheral Rate 2
#define SPCON_SPR2      0x40
/// SS Disable
#define SPCON_SSDIS     0x20
/// Serial Peripheral Master bit
#define SPCON_MSTR      0x10
/// Clock Polarity
#define SPCON_CPOL      0x08
/// Clock Phase
#define SPCON_CPHA      0x04
/// Serial Peripheral Rate 1
#define SPCON_SPR1      0x02
/// Serial Peripheral Rate 0
#define SPCON_SPR0      0x01


// setting SPI clock
#define SPI_SPEED_FCLK_DIVIDE_BY_2      0
#define SPI_SPEED_FCLK_DIVIDE_BY_4      1
#define SPI_SPEED_FCLK_DIVIDE_BY_8      2
#define SPI_SPEED_FCLK_DIVIDE_BY_16     3
#define SPI_SPEED_FCLK_DIVIDE_BY_32     4
#define SPI_SPEED_FCLK_DIVIDE_BY_64     5
#define SPI_SPEED_FCLK_DIVIDE_BY_128    6
#define SPI_CLOCK_NOT_GENERATED         7

/** \name  SPI clock polarity and phase mode defintion
    Clock Polarity (CPOL): 
        When cleared the "sck" is set to 0 in idle state.
        When set the "sck" is set to 1 in idle state.
    Clock Phase (CPHA): 
        When cleared, data is sampled when the "sck" leaves the idle state (see CPOL).
        When set, data is sampled when the "sck" returns to idle state (see CPOL). 
 * \{ 
 */
#define SPI_CPOL_CPHA_00                0x00
#define SPI_CPOL_CPHA_01                0x01
#define SPI_CPOL_CPHA_10                0x02
#define SPI_CPOL_CPHA_11                0x03
/** \} */


/** SPI SS Index */
#define SPI_SSN_0                       0x00
#define SPI_SSN_1                       0x01
#define SPI_SSN_2                       0x02
#define SPI_SSN_3                       0x03

/// size of send buffer
#define SPI_SLA_TX_BUF_SIZE             32

/// size of receive buffer
#define SPI_SLA_RX_BUF_SIZE             32

///< SPI slave transmition timeout, unit is microsecond
#define SPI_SLA_TX_TIMEOUT              (2000000)


typedef struct DRV_SPI_INFO_s
{
    u32_t last_status:8; ///< last status
    u32_t isr_dbg_enabled:1; ///< interrupt debug flag
    u32_t master_mode:1; ///< master mode
    u32_t frame_mode:1; ///< 0: 8 bit data, 1: 16 bits data
    u32_t transmit_cmplt:1; ///< check if transmission is finish
    u32_t receive_cmplt:1; ///< check if reception is finish
    u32_t buf_len:8; ///< number of bytes to send/receive
    u32_t rx_buf_inc:4; ///< RX Buffer offset increment step
    u32_t tx_buf_inc:4; ///< TX Buffer offset increment step
    u32_t dma_handling:1; ///< check if interface is using DMA
    u32_t reserved:2;

    /*!
     * \var rx_data
     * \brief global RX buffer point on this variable
     * if one's doesn't want to read anything from the SPI interface
     * but wants only write data
     */
    u16_t rx_data;
    /*!
     * \var tx_data
     * \brief global TX buffer point on this variable
     * if one's doesn't want to write anything to the SPI interface
     * but wants only read data from it
     */
    u16_t tx_data;

    /// Pointer on data to write buffer
    u8_t *tx_buf_ptr;
    /// Pointer on data to read buffer
    u8_t *rx_buf_ptr;

#if SPI_MODULE_SLAVE_ENABLED
    u32_t sla_rx_buf_index:8;
    u32_t sla_rx_buf_cnt:8;
    u32_t sla_tx_buf_index:8;
    u32_t sla_tx_buf_cnt:8;

    u8_t sla_tx_buf[SPI_SLA_TX_BUF_SIZE];
    u8_t sla_rx_buf[SPI_SLA_RX_BUF_SIZE];

#if SPI_MODULE_SLAVE_DMA_ENABLED
    timer_t sla_session_timer;
#endif /* SPI_MODULE_SLAVE_DMA_ENABLED */

#endif /* SPI_MODULE_SLAVE_ENABLED */

#if 0  /* TODO: modified by James Gan - 2019-07-12 */
    /* bus lock for multi-task */
    void * bus_lock;
#endif /* #if 0 */

} DRV_SPI_INFO_t;

#pragma pack()

extern volatile DRV_SPI_INFO_t g_spi_info;


#define drv_spi_sla_sel_on(ssn) \
    do { \
        DRV_REG_SET(SPSSN_REG,~(1<<(ssn))); \
    } while(0)

#define drv_spi_sla_sel_off() \
    do { \
        DRV_REG_SET(SPSSN_REG,0xF); \
    } while(0)

/** enable spi interrupt */
#define drv_spi_intr_en()               (drv_intr_ext_en_set(IPIC_CFG_INTR_IDX_SPI_INT,TRUE))

/** disable spi interrupt */
#define drv_spi_intr_disable()          (drv_intr_ext_en_set(IPIC_CFG_INTR_IDX_SPI_INT,FALSE))

#define drv_spi_en_get()                (drv_intr_ext_en_get(IPIC_CFG_INTR_IDX_SPI_INT))

#define drv_spi_ssdis_set(flg) \
    do { \
        DRV_REG_FIELD_SET(SPCON_REG,SSDIS,(flg)); \
    } while(0)

#define drv_spi_ssdis_get() \
    (DRV_REG_FIELD_GET(SPCON_REG,SSDIS))

/** spi transmit one byte or halfword */
#define drv_spi_transmit_data(tx_data)  (DRV_REG_SET(SPDAT_REG,tx_data))

/** spi get one byte or halfword */
#define drv_spi_get_data()              (DRV_REG_GET(SPDAT_REG))

#define drv_spi_isr_dbg_set(en) \
    do { \
        g_spi_info.isr_dbg_enabled = (en); \
    } while(0)

#define drv_spi_isr_dbg_get() \
    (g_spi_info.isr_dbg_enabled)


/**
 * Enable and initializatie SPI interface
 *
 *
 * \param master         SPI master mode, 0 is slave, 1 is master
 * \param ssn            SPI SSN, valid values 0~3
 * \param data_rate      SPI Transfer rate; valid values 0-7
 * \param cpol_cpha      Clock Polarity and Clock Phase
 * \return               return 0 if everything is ok or if some
 *                       errors appears return error number
 *
 * \see
 */
u8_t drv_spi_open(u8_t master, u8_t ssn, u8_t data_rate, u8_t cpol_cpha);

/**
 * Disable Serial Peripheral Interface
 *
 *
 * \see
 */
void drv_spi_close(void);

#if SPI_MODULE_SLAVE_ENABLED
/**
 * Flush spi slave buffer
 *
 *
 * \see
 */
void drv_spi_sla_flush(void);

#if SPI_MODULE_SLAVE_DMA_ENABLED
void drv_spi_sla_dma_start(void);
#endif /* SPI_MODULE_SLAVE_DMA_ENABLED */

/**
 * Set spi slave receiving target bytes
 *
 *
 * \see
 */
#define drv_spi_sla_rx_bytes_set(rxbytes) \
    do { \
        g_spi_info.sla_rx_buf_cnt = rxbytes; \
    } while (0)

/**
 * Set spi slave transmition target bytes
 *
 *
 * \see
 */
#define drv_spi_sla_tx_bytes_set(txbytes) \
    do { \
        g_spi_info.sla_tx_buf_cnt = txbytes; \
    } while (0)

#endif /* SPI_MODULE_SLAVE_ENABLED */

/**
 * SPI master transmit interface
 *
 * \param tx_buf       data to write
 * \param rx_buf       buffer on read data
 * \param len          number of bytes to transfer
 * \return             return 0 if everything is ok or if some
 *                     errors appears return error number
 *
 * \see
 */
u8_t drv_spi_transmit(u8_t *tx_buf, u8_t *rx_buf, int len);


#endif /* SPI_MODULE_ENABLED */

#endif /* #ifndef __DRV_SPI_H_ */

