/*!
    \file    spi.h
    \brief   the header file of spi

*/

#ifndef _SPI_H_
#define _SPI_H_

/*********************************************************************************
* CTRL_R0 register bits macro
*********************************************************************************/
#define APB_SPI_DFS_BITOFS                      0u
#define APB_SPI_DFS_BITMASK                     (0xFu << APB_SPI_DFS_BITOFS)
#define APB_SPI_DAT_FRM_SIZ_4bit                0x03u
#define APB_SPI_DAT_FRM_SIZ_8bit                0x07u
#define APB_SPI_DAT_FRM_SIZ_16bit               0x0Fu

#define APB_SPI_FRF_BITOFS                      4u
#define APB_SPI_FRF_BITMASK                     (0x03u << APB_SPI_FRF_BITOFS)
#define APB_SPI_FRM_FMT_SPI                     0x00u
#define APB_SPI_FRM_FMT_SSP                     0x01u
#define APB_SPI_FRM_FMT_MICW                    0x02u

#define APB_SPI_SCPH_BITOFS                     6u
#define APB_SPI_SCLK_PHAS_BITMASK               (0x01u << APB_SPI_SCPH_BITOFS)

typedef enum {
  APB_SPI_CLOCK_PHASE_MIDDLE = 0u,
  APB_SPI_CLOCK_PHASE_START = 1u,
} APB_SPI_PHASE;


#define APB_SPI_SCPOL_BITOFS                    7u
#define APB_SPI_SCLK_POL_BITMASK                (0x01u << APB_SPI_SCPOL_BITOFS)
typedef enum {
  APB_SPI_CLOCK_POLARITY_LOW = 0u,
  APB_SPI_CLOCK_POLARITY_HIGH = 1u,
} APB_SPI_POLARITY;

#define APB_SPI_TMOD_BITOFS                     8u
#define APB_SPI_TMOD_BITMASK                    (0x03u << APB_SPI_TMOD_BITOFS)
#define APB_SPI_TMOD_TX_RX                      0x00u
#define APB_SPI_TMOD_TX                         0x01u
#define APB_SPI_TMOD_RX                         0x02u
#define APB_SPI_TMOD_E2PROM                     0x03u

#define APB_SPI_SLV_OE_BITOFS                   10u
#define APB_SPI_SLV_OE_BITMASK                  (0x01u << APB_SPI_SLV_OE_BITOFS)

#define APB_SPI_SRL_BITOFS                      11u
#define APB_SPI_SRL_BITMASK                     (0x01u << APB_SPI_SRL_BITOFS)

#define APB_SPI_CFS_BITOFS                      12u
#define APB_SPI_CFS_BITMASK                     (0x0Fu << APB_SPI_CFS_BITOFS)

#define APB_SPI_DFS_32_BITOFS                   16u
#define APB_SPI_DFS_32_BITMASK                  (0x0Fu << APB_SPI_DFS_32_BITOFS)

#define APB_SPI_SPI_FRF_BITOFS                  21u
#define APB_SPI_SPI_FRF_BITMASK                 (0x03u << APB_SPI_SPI_FRF_BITOFS)
#define APB_SPI_SPI_FRF_STD                     0x00u
#define APB_SPI_SPI_FRF_DUAL                    0x01u
#define APB_SPI_SPI_FRF_QUAD                    0x02u
#define APB_SPI_SPI_FRF_OCTAL                   0x03u

#define APB_SPI_SSTE_BITOFS                     24u
#define APB_SPI_SLVSEL_TOGG_EN_BITMASK          (0x01u << APB_SPI_SSTE_BITOFS)
typedef enum {
  APB_SPI_TOGGLE_DISABLE = 0u,
  APB_SPI_TOGGLE_ENABLE,
} APB_SPI_TOGGLE;

#define APB_SPI_BUSY_BITOFS                     0u
#define APB_SPI_BUSY_BITMASK                    (0x01u << APB_SPI_BUSY_BITOFS)
#define APB_SPI_TFNF_BITOFS                     1u
#define APB_SPI_TFNF_BITMASK                    (0x01u << APB_SPI_TFNF_BITOFS)
#define APB_SPI_TFE_BITOFS                      2u
#define APB_SPI_TFE_BITMASK                     (0x01u << APB_SPI_TFE_BITOFS)
#define APB_SPI_RFNE_BITOFS                     3u
#define APB_SPI_RFNE_BITMASK                    (0x01u << APB_SPI_RFNE_BITOFS)
#define APB_SPI_RFF_BITOFS                      4u
#define APB_SPI_RFF_BITMASK                     (0x01u << APB_SPI_RFF_BITOFS)

#define APB_SPI_CS0                             1u
#define APB_SPI_TXFIFO_DEPTH                    8u

/*******************************************************************************
* Set CTRLR0.FRF: SPI_FRM_FMT_SPI, SPI_FRM_FMT_SSI, or SPI_FRM_FMT_MICW
********************************************************************************/
__INLINE void spi_master_SetFrmFormat(uint32_t fmt)
{
  SPI0_MST_CTRLR0 = SPI0_MST_CTRLR0 & (~APB_SPI_FRF_BITMASK) | (fmt << APB_SPI_FRF_BITOFS);
}

/*******************************************************************************
* Set CTRLR0.SPI_FRF: SPI_SPI_FRF_STD, SPI_SPI_FRF_DUAL, or SPI_SPI_FRF_QUAD
********************************************************************************/
__INLINE void spi_master_SetSpiFrmFormat(uint32_t spi_format)
{
  SPI0_MST_CTRLR0 = SPI0_MST_CTRLR0 & (~APB_SPI_SPI_FRF_BITMASK) | (spi_format << APB_SPI_SPI_FRF_BITOFS);
}

/*******************************************************************************
* Set CTRLR0.TMOD (spi Tx Rx trans mode):    SPI_TXRX_MOD_TX or SPI_TXRX_MOD_RX
********************************************************************************/
__INLINE void spi_master_SetMode(uint32_t mode)
{
  SPI0_MST_CTRLR0 = SPI0_MST_CTRLR0 & (~APB_SPI_TMOD_BITMASK) | (mode << APB_SPI_TMOD_BITOFS);
}

/*******************************************************************************
*set spi polarity
********************************************************************************/
__INLINE void spi_master_SetPolarity(APB_SPI_POLARITY polarity)
{
  switch(polarity)
  {
    case APB_SPI_CLOCK_POLARITY_LOW:
      SPI0_MST_CTRLR0 &= (~APB_SPI_SCLK_POL_BITMASK);
      break;

    case APB_SPI_CLOCK_POLARITY_HIGH:
      SPI0_MST_CTRLR0 |= APB_SPI_SCLK_POL_BITMASK;
      break;

    default:
      break;
  }
}

/*******************************************************************************
*set spi phase
********************************************************************************/
__INLINE void spi_master_SetPhase(APB_SPI_PHASE phase)
{
  switch(phase)
  {
    case APB_SPI_CLOCK_PHASE_MIDDLE:
      SPI0_MST_CTRLR0 &= (~APB_SPI_SCLK_PHAS_BITMASK);
      break;

    case APB_SPI_CLOCK_PHASE_START:
      SPI0_MST_CTRLR0 |= APB_SPI_SCLK_PHAS_BITMASK;
      break;

    default:
      break;
  }
}

/*******************************************************************************
*set spi data width
********************************************************************************/
__INLINE void spi_master_SetDataWidth(uint32_t datawidth)
{
  SPI0_MST_CTRLR0 = SPI0_MST_CTRLR0 & (~APB_SPI_DFS_BITMASK) | (datawidth << APB_SPI_DFS_BITOFS);
}

/*******************************************************************************
*set spi behavior of the slave select line (ss_*_n) between data frames.
********************************************************************************/
__INLINE void spi_master_SetSlaveSlectToggleEn(APB_SPI_TOGGLE toggle)
{
  switch(toggle)
  {
    case APB_SPI_TOGGLE_DISABLE:
      SPI0_MST_CTRLR0 &= (~APB_SPI_SLVSEL_TOGG_EN_BITMASK);
      break;

    case APB_SPI_TOGGLE_ENABLE:
      SPI0_MST_CTRLR0 |= APB_SPI_SLVSEL_TOGG_EN_BITMASK;
      break;

    default:
      break;
  }
}

/*******************************************************************************
*select slave,spi master1 has 2 cs,suport two  slaves.spi mater0 only one cs.
********************************************************************************/
__INLINE void spi_master_SelectSlave(uint8_t slave)
{
  SPI0_MST_SER = slave;
}

/*******************************************************************************
*set bitrate
********************************************************************************/
__INLINE void spi_master_SetBaudrate(uint32_t baudrate)
{
  SPI0_MST_BAUDR = baudrate;
}

/*******************************************************************************
*mask the spi master interrupt
********************************************************************************/
__INLINE void spi_master_MaskInt(uint32_t mask)
{
  SPI0_MST_IMR = mask;
}

/*!
    \brief      spi_master_disable
    \param[in]  none
    \param[out] none
    \retval     none
*/
__INLINE void spi_master_disable(void)
{
  SPI0_MST_SSIENR = 0;
}

/*!
    \brief      spi_master_enable
    \param[in]  none
    \param[out] none
    \retval     none
*/
__INLINE void spi_master_enable(void)
{
  SPI0_MST_SSIENR = 1;
}

/*******************************************************************************
* Set CTRLR0.FRF: SPI_FRM_FMT_SPI, SPI_FRM_FMT_SSI, or SPI_FRM_FMT_MICW
********************************************************************************/
__INLINE void spi_slave_SetFrmFormat(uint32_t fmt)
{
  SPI0_SLV_CTRLR0 = SPI0_SLV_CTRLR0 & (~APB_SPI_FRF_BITMASK) | (fmt << APB_SPI_FRF_BITOFS);
}

/*******************************************************************************
* Set CTRLR0.SPI_FRF: SPI_SPI_FRF_STD, SPI_SPI_FRF_DUAL, or SPI_SPI_FRF_QUAD
********************************************************************************/
__INLINE void spi_slave_SetSpiFrmFormat(uint32_t spi_format)
{
  SPI0_SLV_CTRLR0 = SPI0_SLV_CTRLR0 & (~APB_SPI_SPI_FRF_BITMASK) | (spi_format << APB_SPI_SPI_FRF_BITOFS);
}

/*******************************************************************************
* Set CTRLR0.TMOD (spi Tx Rx trans mode):    SPI_TXRX_MOD_TX or SPI_TXRX_MOD_RX
********************************************************************************/
__INLINE void spi_slave_SetMode(uint32_t mode)
{
  SPI0_SLV_CTRLR0 = SPI0_SLV_CTRLR0 & (~APB_SPI_TMOD_BITMASK) | (mode << APB_SPI_TMOD_BITOFS);
}


/*******************************************************************************
*set spi data width
********************************************************************************/
__INLINE void spi_slave_SetDataWidth(uint32_t datawidth)
{
  SPI0_SLV_CTRLR0 = SPI0_SLV_CTRLR0 & (~APB_SPI_DFS_BITMASK) | (datawidth << APB_SPI_DFS_BITOFS);
}

/*******************************************************************************
*set spi behavior of the slave select line (ss_*_n) between data frames.
********************************************************************************/
__INLINE void spi_slave_SetSlaveSlectToggleEn(APB_SPI_TOGGLE toggle)
{
  switch(toggle)
  {
    case APB_SPI_TOGGLE_DISABLE:
      SPI0_SLV_CTRLR0 &= (~APB_SPI_SLVSEL_TOGG_EN_BITMASK);
      break;

    case APB_SPI_TOGGLE_ENABLE:
      SPI0_SLV_CTRLR0 |= APB_SPI_SLVSEL_TOGG_EN_BITMASK;
      break;

    default:
      break;
  }
}

/*******************************************************************************
*mask the spi master interrupt
********************************************************************************/
__INLINE void spi_slave_MaskInt(uint32_t mask)
{
  SPI0_SLV_IMR = mask;
}

/*!
    \brief      spi_master_disable
    \param[in]  none
    \param[out] none
    \retval     none
*/
__INLINE void spi_slave_disable(void)
{
  SPI0_SLV_SSIENR = 0;
}

/*!
    \brief      spi_master_enable
    \param[in]  none
    \param[out] none
    \retval     none
*/
__INLINE void spi_slave_enable(void)
{
  SPI0_SLV_SSIENR = 1;
}

/*!
    \brief      spi_master_SetFIFOTransThrsld
    \param[in]  none
    \param[out] none
    \retval     none
*/
__INLINE void spi_master_SetFIFOTransThrsld(uint8_t trans_thrsld)
{
  SPI0_MST_TXFTLR = trans_thrsld;
}

/*!
    \brief      spi_master_SetFIFORecvThrsld
    \param[in]  none
    \param[out] none
    \retval     none
*/
__INLINE void spi_master_SetFIFORecvThrsld(uint8_t recv_thrsld)
{
  SPI0_MST_RXFTLR = recv_thrsld;
}

/*!
    \brief      spi_slave_SetFIFOTransThrsld
    \param[in]  none
    \param[out] none
    \retval     none
*/
__INLINE void spi_slave_SetFIFOTransThrsld(uint8_t trans_thrsld)
{
  SPI0_SLV_TXFTLR = trans_thrsld;
}

/*!
    \brief      spi_slave_SetFIFORecvThrsld
    \param[in]  none
    \param[out] none
    \retval     none
*/
__INLINE void spi_slave_SetFIFORecvThrsld(uint8_t recv_thrsld)
{
  SPI0_SLV_RXFTLR = recv_thrsld;
}

/*!
    \brief      spi_slave_SetOutDisable
    \param[in]  none
    \param[out] none
    \retval     none
*/
__INLINE void spi_slave_SetOutDisable(void)
{
  SPI0_SLV_CTRLR0 |= APB_SPI_SLV_OE_BITMASK;
}

/*!
    \brief      spi_slave_SetOutEnable
    \param[in]  none
    \param[out] none
    \retval     none
*/
__INLINE void spi_slave_SetOutEnable(void)
{
  SPI0_SLV_CTRLR0 &= APB_SPI_SLV_OE_BITMASK;
}
/*!
    \brief      spi_master_GetTxFIFOLevel
    \param[in]  none
    \param[out] none
    \retval     none
*/
__INLINE uint8_t spi_master_GetTxFIFOLevel(void)
{
  return SPI0_MST_TXFLR;
}

/*!
    \brief      spi_master_GetRxFIFOLevel
    \param[in]  none
    \param[out] none
    \retval     none
*/
__INLINE uint8_t spi_master_GetRxFIFOLevel(void)
{
  return SPI0_MST_RXFLR;
}

/*!
    \brief      spi_slave_GetTxFIFOLevel
    \param[in]  none
    \param[out] none
    \retval     none
*/
__INLINE uint8_t spi_slave_GetTxFIFOLevel(void)
{
  return SPI0_SLV_TXFLR;
}

/*!
    \brief      spi_slave_GetRxFIFOLevel
    \param[in]  none
    \param[out] none
    \retval     none
*/
__INLINE uint8_t spi_slave_GetRxFIFOLevel(void)
{
  return SPI0_SLV_RXFLR;
}

/*!
    \brief      spi_slave_ReadByteData
    \param[in]  none
    \param[out] none
    \retval     none
*/
__INLINE uint8_t spi_slave_ReadByteData(void)
{
  return SPI0_SLV_DR0;
}

/*!
    \brief      spi_slave_WriteByteData
    \param[in]  none
    \param[out] none
    \retval     none
*/
__INLINE void spi_slave_WriteByteData(uint8_t data)
{
  SPI0_SLV_DR0 = data;
}

/*!
    \brief      spi_slave_GetBusySts
    \param[in]  none
    \param[out] none
    \retval     none
*/
__INLINE uint8_t spi_slave_GetBusySts(void)
{
  return ((SPI0_SLV_SR & APB_SPI_BUSY_BITMASK) >> APB_SPI_BUSY_BITOFS);
}

/*!
    \brief      spi_slave_GetTxFIFOEmptySts
    \param[in]  none
    \param[out] none
    \retval     none
*/
__INLINE uint8_t spi_slave_GetTxFIFOEmptySts(void)
{
  return ((SPI0_SLV_SR & APB_SPI_TFE_BITMASK) >> APB_SPI_TFE_BITOFS);
}

/*!
    \brief      spi_slave_GetTxFIFONotFullSts
    \param[in]  none
    \param[out] none
    \retval     none
*/
__INLINE uint8_t spi_slave_GetTxFIFONotFullSts(void)
{
  return ((SPI0_SLV_SR & APB_SPI_TFNF_BITMASK) >> APB_SPI_TFNF_BITOFS);
}

/*!
    \brief      spi_slave_GetRxFIFONotEmptySts
    \param[in]  none
    \param[out] none
    \retval     none
*/
__INLINE uint8_t spi_slave_GetRxFIFONotEmptySts(void)
{
  return ((SPI0_SLV_SR & APB_SPI_RFNE_BITMASK) >> APB_SPI_RFNE_BITOFS);
}

/*!
    \brief      spi_slave_GetRxFIFOFullSts
    \param[in]  none
    \param[out] none
    \retval     none
*/
__INLINE uint8_t spi_slave_GetRxFIFOFullSts(void)
{
  return ((SPI0_SLV_SR & APB_SPI_RFF_BITMASK) >> APB_SPI_RFF_BITOFS);
}


/* SPI slave driver uses Rx cascaded IRQ or not */
#define CFG_SPIS_CASCADE_IRQ            1

/* SPI slave driver Tx time-out constant */
#define SPIS_TX_TMO_MS                  100

/* SPI slave message format constants */
#define SPIS_MSG_LEN_SZ                 1u
#define SPIS_MSG_CMD_SZ                 1u
#define SPIS_MSG_PARAM_SZ               2048u + 2u	//(256u - 3u)
#define SPIS_MSG_CRC_SZ                 1u


#define SPIS_MSG_PAYLOAD_SZ             (SPIS_MSG_CMD_SZ + SPIS_MSG_PARAM_SZ)
#define SPIS_MSG_TOTAL_SZ               (SPIS_MSG_LEN_SZ + SPIS_MSG_PAYLOAD_SZ + SPIS_MSG_CRC_SZ)

/* SPI slave driver buffer size constants */
#define SPIS_TXBUF_SIZ                  0x100u      /* unit: 16-bit word */
#define SPIS_RXBUF_SIZ                  0x1004u +6u     /* unit: 16-bit word */
#define SPIS_HW_FIFO_SIZ                0x8u        /* unit: 16-bit word */
#define SPIS_TX_FIFO_THRES              0x4u        /* unit: 16-bit word */

/* SPI slave driver state machine */
#define SPIS_ST_IRQTEST                 0u
#define SPIS_ST_IDLE                    1u
#define SPIS_ST_RX                      2u
#define SPIS_ST_TX                      3u

/* SPI slave driver message title */
#define SPIS_RX_MSG_RCVD                0x1001u
#define SPIS_RX_LEN_ERR                 0x1081u
#define SPIS_RX_FIFO_OVRFLW             0x1082u
#define SPIS_TX_MSG_SENT                0x2001u
#define SPIS_TX_MSG_TMO                 0x2081u

/* SPI slave upper layer message title (payload commands) */
#define SPIS_REQ_CONNECT                0x0100u
#define SPIS_REQ_ALIVE_CHK              0x1000u
#define SPIS_REQ_RD_SYS_PAR             0x0200u
#define SPIS_REQ_RD_ISP_PAR             0x0300u
#define SPIS_REQ_RD_SENS_PAR            0x0500u
#define SPIS_REQ_RD_VCM_PAR            	0x0600u
#define SPIS_REQ_WR_SYS_PAR             0x2000u
#define SPIS_REQ_WR_ISP_PAR             0x3000u
#define SPIS_REQ_WR_RISr_PAR            0x4000u
#define SPIS_REQ_WR_SENS_PAR            0x5000u
#define SPIS_REQ_WR_VCM_PAR            	0x6000u

#define SPIS_RSP_CONNECT                (SPIS_REQ_CONNECT + 1u)
#define SPIS_RSP_ALIVE_CHK              (SPIS_REQ_ALIVE_CHK + 1u)
#define SPIS_RSP_RD_SYS_PAR             (SPIS_REQ_RD_SYS_PAR + 1u)
#define SPIS_RSP_RD_ISP_PAR             (SPIS_REQ_RD_ISP_PAR + 1u)
#define SPIS_RSP_WR_SYS_PAR             (SPIS_REQ_WR_SYS_PAR + 1u)
#define SPIS_RSP_WR_ISP_PAR             (SPIS_REQ_WR_ISP_PAR + 1u)
#define SPIS_RSP_WR_1_PAR               (SPIS_REQ_WR_1_PAR + 1u)
#define SPIS_RSP_GENERIC_ERR            0x9999u
#define SPIS_RSP_CRC_ERR                0xFFFFu
#define SPIS_RSP_LEN_ERR                0xFFFEu
#define SPIS_RSP_FIFO_ERR               0xFFFDu


/* SPI slave upper layer message */
typedef struct tag_spis_msg_t
{
  uint16_t len;                         /* LENGTH of "cmd and parameter data" fields (excluding the trailing CRC_16 word) */
  uint16_t cmd;                         /* COMMAND */
  uint16_t data[SPIS_MSG_PARAM_SZ + SPIS_MSG_CRC_SZ]; /* PARAMETER (+ 1 trailing CRC_16 word) */
                                        /* CCITT CRC-16 0x1021: x^16+x^12+x^5+x^0, range = cmd+len+params */
}
spis_msg_t;

/* SPI slave driver state variable */
extern uint32_t spis_state;

/* SPI slave driver Tx time-out timer handle */
extern TimerHandle_t spis_txtmo_tmr;

/* SPI slave task msg queue handles */
extern QueueHandle_t ADJ_Queue;
extern QueueHandle_t ISP_Queue;


void spi_master_init(void);
void spi_master_send(uint8_t *buff, uint32_t txlen);
void spi_slave_init(void);
uint32_t spi_slave_recv(uint8_t *buff, uint32_t rxlen);
uint32_t spi_slave_GetIntSts(void);
uint32_t spi_master_GetIntSts(void);
void spi_master_SetIntMask(uint32_t mask);
void spi_slave_SetIntMask(uint32_t mask);
uint32_t spi_slave_ClrIntSts(void);
uint32_t spi_master_ClrIntSts(void);
void spi_slave_send(uint8_t *buff, uint32_t txlen);
void spi_master_recv(uint8_t *buff, uint32_t rxlen);
uint32_t spi_master_int_recv(uint8_t *buff, uint32_t rxlen);

void spis_isr_init(void);
void spis_rxbuf_wr(uint16_t dat);
uint16_t spis_rxbuf_rd(void);
uint16_t spis_rxbuf_space(void);
void spis_cb_txtmo(TimerHandle_t xTimer);
BaseType_t spis_tx(const uint16_t * tx_buf, uint16_t data_send, uint16_t timeout_ms);
void spis_rx(void);

#endif  /* SPI_H */

