/**
  *********************************************************************************
  *
  * @file    hal_spi.c
  * @brief   Header file of SPI module driver.
  *
  * @version V1.0
  * @date    13 Nov 2017
  * @author  AE Team
  * @note
  *
  * Copyright (C) Shanghai Eastsoft Microelectronics Co. Ltd. All rights reserved.
  *
  *********************************************************************************
  */

#ifndef __HAL_SPI_H__
#define __HAL_SPI_H__

#ifdef __cplusplus
 extern "C" {
#endif

#include "utils.h"
#include "hal_dma.h"

/** @addtogroup ES32FXXX_HAL
  * @{
  */

/** @addtogroup SPI
  * @{
  */

/** @defgroup SPI_Public_Types SPI Public Types
  * @{
  */

/**
  * @brief clock phase
  */
typedef enum {
	SPI_CPHA_FIRST  = 0,	/**< Transiting data in the first edge */
	SPI_CPHA_SECOND = 1,	/**< Transiting data in the seconde edge */
} spi_cpha_t;

/**
  * @brief clock polarity
  */
typedef enum {
	SPI_CPOL_LOW  = 0,	/**< Polarity hold low when spi-bus is idle */
	SPI_CPOL_HIGH = 1,	/**< Polarity hold high when spi-bus is idle */
} spi_cpol_t;

/**
  * @brief master selection
  */
typedef enum {
	SPI_MODE_SLAVER = 0,	/**< Slave mode */
	SPI_MODE_MASTER = 1,	/**< Master mode */
} spi_mode_t;

/**
  * @brief baud rate control
  */
typedef enum {
	SPI_BR_2   = 0,		/**< fpclk/2 */
	SPI_BR_4   = 1,		/**< fpclk/4 */
	SPI_BR_8   = 2,		/**< fpclk/8 */
	SPI_BR_16  = 3,		/**< fpclk/16 */
	SPI_BR_32  = 4,		/**< fpclk/32 */
	SPI_BR_64  = 5,		/**< fpclk/64 */
	SPI_BR_128 = 6,		/**< fpclk/128 */
	SPI_BR_256 = 7,		/**< fpclk/256 */
} spi_br_t;

/**
  * @brief frame format
  */
typedef enum {
	SPI_FIRSTBIT_MSB = 0,	/**< MSB transmitted first */
	SPI_FIRSTBIT_LSB = 1,	/**< LSB transmitted first */
} spi_firstbit_t;

/**
  * @brief data frame format
  */
typedef enum {
	SPI_DATA_SIZE_4 = 3,   /**< 4 -bit data frame format is selected for transmission/reception */
	SPI_DATA_SIZE_5 = 4,   /**< 5 -bit data frame format is selected for transmission/reception */
	SPI_DATA_SIZE_6 = 5,   /**< 6 -bit data frame format is selected for transmission/reception */
	SPI_DATA_SIZE_7 = 6,   /**< 7 -bit data frame format is selected for transmission/reception */
	SPI_DATA_SIZE_8 = 7,   /**< 8 -bit data frame format is selected for transmission/reception */
	SPI_DATA_SIZE_9 = 8,   /**< 9 -bit data frame format is selected for transmission/reception */
	SPI_DATA_SIZE_10 = 9,  /**< 10-bit data frame format is selected for transmission/reception */
	SPI_DATA_SIZE_11 = 10, /**< 11-bit data frame format is selected for transmission/reception */
	SPI_DATA_SIZE_12 = 11, /**< 12-bit data frame format is selected for transmission/reception */
	SPI_DATA_SIZE_13 = 12, /**< 13-bit data frame format is selected for transmission/reception */
	SPI_DATA_SIZE_14 = 13, /**< 14-bit data frame format is selected for transmission/reception */
	SPI_DATA_SIZE_15 = 14, /**< 15-bit data frame format is selected for transmission/reception */
	SPI_DATA_SIZE_16 = 15  /**< 16-bit data frame format is selected for transmission/reception */
} spi_datasize_t;

/**
  * @brief output enable in bidirectional mode
  *
  */
typedef enum {
	SPI_BIDI_RX = 0,	/**< output disable(receive-only mode) */
	SPI_BIDI_TX = 1,	/**< output enable(transmit-only mode) */
} spi_bidi_t;

/**
  * @brief bidirectional data mode enable
  */
typedef enum {
	SPI_BIDIMODE_2LINES = 0,	/**< 2-line unidirectional data mode selected */
	SPI_BIDIMODE_1LINE  = 1,	/**< 1-line bidirectional data mode selected */
} spi_bidimode_t;


/**
  * @brief interrupt control
  */
typedef enum {
	SPI_IT_TXE    = (1U << 0), /**< Receive buffer not empty */
	SPI_IT_TXOV   = (1U << 2),
	SPI_IT_TXUD   = (1U << 3),
	SPI_IT_TXTH   = (1U << 4),
	SPI_IT_RXF    = (1U << 9),
	SPI_IT_RXOV   = (1U << 10),
	SPI_IT_RXUD   = (1U << 11),
	SPI_IT_RXTH   = (1U << 12),
	SPI_IT_CRCERR = (1U << 16),
	SPI_IT_MODF   = (1U << 17),
	SPI_IT_FRE    = (1U << 18),
} spi_it_t;

/**
  * @brief Status flag
  */
typedef enum {
	SPI_FLAG_TXE    = (1U << 0), /**< Receive buffer not empty */
	SPI_FLAG_TXF    = (1U << 1),
	SPI_FLAG_TXOV   = (1U << 2),
	SPI_FLAG_TXUD   = (1U << 3),
	SPI_FLAG_TXTH   = (1U << 4),
	SPI_FLAG_RXE    = (1U << 8),
	SPI_FLAG_RXF    = (1U << 9),
	SPI_FLAG_RXOV   = (1U << 10),
	SPI_FLAG_RXUD   = (1U << 11),
	SPI_FLAG_RXTH   = (1U << 12),
	SPI_FLAG_CHSIDE = (1U << 14),
	SPI_FLAG_BSY    = (1U << 15),
} spi_flag_t;

/**
  * @brief Interrupt flag
  */
typedef enum {
	SPI_IF_TXE    = (1U << 0), /**< Receive buffer not empty */
	SPI_IF_TXOV   = (1U << 2),
	SPI_IF_TXUD   = (1U << 3),
	SPI_IF_TXTH   = (1U << 4),
	SPI_IF_RXF    = (1U << 9),
	SPI_IF_RXOV   = (1U << 10),
	SPI_IF_RXUD   = (1U << 11),
	SPI_IF_RXTH   = (1U << 12),
	SPI_IF_CRCERR = (1U << 16),
	SPI_IF_MODF   = (1U << 17),
	SPI_IF_FRE    = (1U << 18),
} spi_it_flag_t;

/**
  * @brief SPI error status
  */
typedef enum {
	SPI_ERROR_NONE = 0,	/**< none */
	SPI_ERROR_MODF = 1,	/**< mode fault */
	SPI_ERROR_CRC  = 2,	/**< crc error */
	SPI_ERROR_OVR  = 4,	/**< overrun error */
	SPI_ERROR_DMA  = 8,	/**< dma error  */
	SPI_ERROR_FLAG = 0x10,	/**< interrupt flag error */
} spi_error_t;



/**
  * @brief SPI state structures definition
  */
typedef enum {
	SPI_STATE_RESET      = 0x00,	/**< Peripheral is not initialized */
	SPI_STATE_READY      = 0x01,	/**< Peripheral Initialized and ready for use */
	SPI_STATE_BUSY       = 0x02,	/**< an internal process is ongoing */
	SPI_STATE_BUSY_TX    = 0x12,	/**< transmit is ongoing */
	SPI_STATE_BUSY_RX    = 0x22,	/**< receive is ongoing */
	SPI_STATE_BUSY_TX_RX = 0x32,	/**< transmit and receive are ongoing */
	SPI_STATE_TIMEOUT    = 0x03,	/**< Timeout state */
	SPI_STATE_ERROR      = 0x04,	/**< Error */
} spi_state_t;


/**
  * @brief SPI direction definition
  */
typedef enum {
	SPI_DIRECTION_2LINES        = 0,	/**< 2 lines */
	SPI_DIRECTION_2LINES_RXONLY = 1,	/**< 2 lines only rx */
	SPI_DIRECTION_1LINE_RX      = 2,	/**< 1 line only rx*/
	SPI_DIRECTION_1LINE_TX      = 3,	/**< 1 line only tx*/
} spi_direction_t;

/**
  * @brief SPI dma request definition
  */
typedef enum {
	SPI_DMA_REQ_TX = 0,	/**< TX dma request */
	SPI_DMA_REQ_RX = 1,	/**< RX dma request */
} spi_dma_req_t;

/**
  * @brief SPI crc length definition
  */
typedef enum {
	SPI_CRC_LEN_8 = 0, /**< 8-bit crc length */
	SPI_CRC_LEN_16 = 1 /**< 16-bit crc length  */
} spi_crc_len_t;

/**
  * @brief SPI crc length definition
  */
typedef enum {
	SPI_FRAME_MOTOROLA = 0, /**< SPI motorola mode */
	SPI_FRAME_TI = 1        /**< SPI TI mode */
} spi_frame_t;

/**
  * @brief SPI init structure definition
  */
typedef struct {
	spi_mode_t mode;		/**< SPI mode */
	spi_direction_t dir;		/**< SPI direction */
	spi_datasize_t data_size;	/**< SPI data size */
	spi_br_t psc;			/**< SPI baudrate prescaler */
	spi_cpha_t phase;		/**< SPI clock phase */
	spi_cpol_t polarity;		/**< SPI clock polarity */
	spi_firstbit_t first_bit;	/**< SPI first bit */
	type_func_t ssm_en;		/**< SPI ssm enable or disable */
	type_func_t crc_calc;		/**< SPI crc calculation */
	spi_crc_len_t crc_len;          /**< SPI crc length */
	spi_frame_t frame;              /**< SPI frame format */
	uint16_t crc_poly;		/**< SPI crc polynomial */
} spi_init_t;

/**
  * @brief  SPI handle structure definition
  */
typedef struct spi_handle_s {
	SPI_I2S_TypeDef *perh;	/**< SPI registers base address */
	spi_init_t init;	/**< SPI communication parameters */
	uint8_t *tx_buf;	/**< Pointer to SPI Tx transfer buffer */
	uint16_t tx_size;	/**< SPI Tx transfer size */
	uint16_t tx_count;	/**< SPI Tx transfer counter */
	uint8_t *rx_buf;	/**< Pointer to SPI Rx transfer buffer */
	uint16_t rx_size;	/**< SPI Rx Transfer size */
	uint16_t rx_count;	/**< SPI Rx Transfer Counter */
#ifdef HAL_DMA
	dma_handle_t hdmatx;	/**< SPI Tx DMA handle parameters */
	dma_handle_t hdmarx;	/**< SPI Rx DMA handle parameters */
#endif
	lock_state_t lock;	/**< Locking object */
	spi_state_t state;	/**< SPI communication state */
	uint32_t err_code;	/**< SPI error code */

	void (*tx_cplt_cbk)(struct spi_handle_s *arg);		/**< Tx completed callback */
	void (*rx_cplt_cbk)(struct spi_handle_s *arg);		/**< Rx completed callback */
	void (*tx_rx_cplt_cbk)(struct spi_handle_s *arg);	/**< Tx & Rx completed callback */
	void (*err_cbk)(struct spi_handle_s *arg);		/**< error callback */
} spi_handle_t;
/**
  * @}
  */

/** @defgroup SPI_Public_Macros SPI Public Macros
  * @{
  */
#define SPI_RESET_HANDLE_STATE(x)	((x)->state = SPI_STATE_RESET)
#define SPI_ENABLE(x)			((x)->perh->CR1.SPE = 1)
#define SPI_DISABLE(x)			((x)->perh->CR1.SPE = 0)
#define SPI_CRC_DISABLE(x)		((x)->perh->CR1.CRCEN = 0;)
#define SPI_CRC_RESET(x)		\
do {					\
	(x)->perh->CR1.CRCEN = 0;	\
	(x)->perh->CR1.CRCEN = 1;	\
} while (0)
#define SPI_CRCNEXT_ENABLE(x)	((x)->perh->CR1.CRCNEXT = 1)
#define SPI_CRCNEXT_DISABLE(x)	((x)->perh->CR1.CRCNEXT = 0)
#define SPI_RXONLY_ENABLE(x)	((x)->perh->CR1.RXONLY = 1)
#define SPI_RXONLY_DISABLE(x)	((x)->perh->CR1.RXONLY = 0)
#define SPI_1LINE_TX(x)		((x)->perh->CR1.BIDIOE = 1)
#define SPI_1LINE_RX(x)		((x)->perh->CR1.BIDIOE = 0)
#define SPI_SSI_HIGH(x)		((x)->perh->CR1.SSI = 1)
#define SPI_SSI_LOW(x)		((x)->perh->CR1.SSI = 0)
#define SPI_SSOE_ENABLE(x)	((x)->perh->CR2.SSOE= 1)
#define SPI_SSOE_DISABLE(x)	((x)->perh->CR2.SSOE= 0)
/**
  * @}
  */

/** @defgroup SPI_Private_Macros   SPI Private Macros
  * @{
  */
#define IS_SPI(x)	(((x) == SPI0) || \
                         ((x) == SPI1) || \
                         ((x) == SPI2))
#define IS_SPI_CPHA(x)	(((x) == SPI_CPHA_FIRST) || \
                         ((x) == SPI_CPHA_SECOND))
#define IS_SPI_CPOL(x)	(((x) == SPI_CPOL_LOW) || \
                         ((x) == SPI_CPOL_HIGH))
#define IS_SPI_MODE(x)	(((x) == SPI_MODE_SLAVER) || \
                         ((x) == SPI_MODE_MASTER))
#define IS_SPI_BR(x)	(((x) == SPI_BR_2)   || \
                         ((x) == SPI_BR_4)   || \
                         ((x) == SPI_BR_8)   || \
                         ((x) == SPI_BR_16)  || \
                         ((x) == SPI_BR_32)  || \
                         ((x) == SPI_BR_64)  || \
                         ((x) == SPI_BR_128) || \
                         ((x) == SPI_BR_256))
#define IS_SPI_DATASIZE(x)	(((x) == SPI_DATA_SIZE_4) || \
				 ((x) == SPI_DATA_SIZE_5) || \
				 ((x) == SPI_DATA_SIZE_6) || \
				 ((x) == SPI_DATA_SIZE_7) || \
				 ((x) == SPI_DATA_SIZE_8) || \
				 ((x) == SPI_DATA_SIZE_9) || \
				 ((x) == SPI_DATA_SIZE_10) || \
				 ((x) == SPI_DATA_SIZE_11) || \
				 ((x) == SPI_DATA_SIZE_12) || \
				 ((x) == SPI_DATA_SIZE_13) || \
				 ((x) == SPI_DATA_SIZE_14) || \
				 ((x) == SPI_DATA_SIZE_15) || \
                                 ((x) == SPI_DATA_SIZE_16))
#define IS_SPI_BIDIOE(x)	(((x) == SPI_BIDI_RX) || \
                                 ((x) == SPI_BIDI_TX))
#define IS_SPI_BIDIMODE(x)	(((x) == SPI_BIDIMODE_DUAL) || \
                                 ((x) == SPI_BIDIMODE_SOLE))
#define IS_SPI_DIRECTION(x)	(((x) == SPI_DIRECTION_2LINES)         || \
                                 ((x) == SPI_DIRECTION_2LINES_RXONLY)  || \
                                 ((x) == SPI_DIRECTION_1LINE_RX)       || \
				 ((x) == SPI_DIRECTION_1LINE_TX))
#define IS_SPI_DMA_REQ(x)	(((x) == SPI_DMA_REQ_TX) || \
                                 ((x) == SPI_DMA_REQ_RX))
#define IS_SPI_FLAG(x)	(((x) == SPI_FLAG_TXE)    || \
			 ((x) == SPI_FLAG_TXF)    || \
			 ((x) == SPI_FLAG_TXOV)   || \
			 ((x) == SPI_FLAG_TXUD)   || \
			 ((x) == SPI_FLAG_TXTH)   || \
			 ((x) == SPI_FLAG_RXE)    || \
			 ((x) == SPI_FLAG_RXF)    || \
			 ((x) == SPI_FLAG_RXOV)   || \
			 ((x) == SPI_FLAG_RXUD)   || \
			 ((x) == SPI_FLAG_RXTH)   || \
			 ((x) == SPI_FLAG_CHSIDE) || \
			 ((x) == SPI_FLAG_BSY))
#define IS_SPI_IT(x)	(((x) == SPI_IT_TXE)   || \
			 ((x) == SPI_IT_TXOV)    || \
			 ((x) == SPI_IT_TXUD)    || \
			 ((x) == SPI_IT_TXTH) || \
			 ((x) == SPI_IT_RXF)   || \
			 ((x) == SPI_IT_RXOV)    || \
			 ((x) == SPI_IT_RXUD)    || \
			 ((x) == SPI_IT_RXTH)    || \
			 ((x) == SPI_IT_CRCERR)    || \
			 ((x) == SPI_IT_MODF)    || \
			 ((x) == SPI_IT_FRE))
#define IS_SPI_IF(x)	(((x) == SPI_IF_TXE)    || \
			 ((x) == SPI_IF_TXOV)   || \
                         ((x) == SPI_IF_TXUD)   || \
                         ((x) == SPI_IF_TXTH)   || \
                         ((x) == SPI_IF_RXF)    || \
			 ((x) == SPI_IF_RXOV)   || \
			 ((x) == SPI_IF_RXUD)   || \
			 ((x) == SPI_IF_RXTH)   || \
			 ((x) == SPI_IF_CRCERR) || \
			 ((x) == SPI_IF_MODF)   || \
			 ((x) == SPI_IF_FRE))
#define IS_SPI_FIFO_THRESHOLD(x)	(((x) > 0) && \
					 ((x) < 32))
#define IS_SPI_FRAME(x)			(((x) == SPI_FRAME_MOTOROLA) || \
					 ((x) == SPI_FRAME_TI))
/**
  * @}
  */

/** @addtogroup SPI_Public_Functions
  * @{
  */

/** @addtogroup SPI_Public_Functions_Group1
  * @{
  */

hal_status_t spi_init(spi_handle_t *hperh);
void spi_reset(spi_handle_t *hperh);
hal_status_t spi_fifo_threshold_config(spi_handle_t *hperh, uint8_t threshold);
hal_status_t spi_bidirection_line_config(spi_handle_t *hperh, spi_bidi_t direction);
/**
  * @}
  */

/** @addtogroup SPI_Public_Functions_Group2
  * @{
  */
hal_status_t spi_send(spi_handle_t *hperh, uint8_t *buf, uint16_t size, uint32_t timeout);
hal_status_t spi_recv(spi_handle_t *hperh, uint8_t *buf, uint16_t size, uint32_t timeout);
hal_status_t spi_send_recv(spi_handle_t *hperh, uint8_t *tx_buf, uint8_t *rx_buf, uint16_t size, uint32_t timeout);
hal_status_t spi_send_by_it(spi_handle_t *hperh, uint8_t *buf, uint16_t size);
hal_status_t spi_recv_by_it(spi_handle_t *hperh, uint8_t *buf, uint16_t size);
hal_status_t spi_send_recv_by_it(spi_handle_t *hperh, uint8_t *tx_buf, uint8_t *rx_buf, uint16_t size);
#ifdef HAL_DMA
hal_status_t spi_send_by_dma(spi_handle_t *hperh, uint8_t *buf, uint16_t size, uint8_t channel);
hal_status_t spi_recv_by_dma(spi_handle_t *hperh, uint8_t *buf, uint16_t size, uint8_t channel);
hal_status_t spi_send_recv_by_dma(spi_handle_t *hperh, uint8_t *tx_buf, uint8_t *rx_buf, uint16_t size, uint8_t tx_channel, uint8_t rx_channel);
hal_status_t spi_dma_pause(spi_handle_t *hperh);
hal_status_t spi_dma_resume(spi_handle_t *hperh);
hal_status_t spi_dma_stop(spi_handle_t *hperh);
#endif
/**
  * @}
  */

/** @addtogroup SPI_Public_Functions_Group3
  * @{
  */
void spi_irq_handle(spi_handle_t *hperh);
void spi_interrupt_config(spi_handle_t *hperh, spi_it_t it, type_func_t state);
void spi_dma_req_config(spi_handle_t *hperh, spi_dma_req_t req, type_func_t state);
flag_status_t spi_get_flag_status(spi_handle_t *hperh, spi_flag_t flag);
it_status_t spi_get_it_status(spi_handle_t *hperh, spi_it_t it);
flag_status_t spi_get_it_flag_status(spi_handle_t *hperh, spi_it_flag_t flag);
void spi_clear_it_flag_status(spi_handle_t *hperh, spi_it_flag_t flag);
/**
  * @}
  */

/** @addtogroup SPI_Public_Functions_Group4
  * @{
  */
spi_state_t spi_get_state(spi_handle_t *hperh);
uint32_t spi_get_error(spi_handle_t *hperh);
/**
  * @}
  */

/**
  * @}
  */

/**
  * @}
  */

/**
  * @}
  */
#ifdef __cplusplus
}
#endif
#endif
