/*
 * Copyright (c) 2021 HPMicro
 *
 * SPDX-License-Identifier: BSD-3-Clause
 *
 */

#ifndef HPM_SPI_DRV_H
#define HPM_SPI_DRV_H
#include "hpm_spi_regs.h"
#include "hpm_soc_feature.h"

/**
 * @brief SPI driver APIs
 * @defgroup spi_interface SPI driver APIs
 * @ingroup io_interfaces
 * @{
 */

/**
 * @brief spi dma enable
 */
typedef enum {
    spi_tx_dma_enable = SPI_CTRL_TXDMAEN_MASK,
    spi_rx_dma_enable = SPI_CTRL_RXDMAEN_MASK
} spi_dma_enable_t;

/**
 * @brief spi interrupt mask
 */
typedef enum {
    spi_rx_fifo_overflow_int  = SPI_INTREN_RXFIFOORINTEN_MASK,
    spi_tx_fifo_underflow_int = SPI_INTREN_TXFIFOURINTEN_MASK,
    spi_rx_fifo_threshold_int = SPI_INTREN_RXFIFOINTEN_MASK,
    spi_tx_fifo_threshold_int = SPI_INTREN_TXFIFOINTEN_MASK,
    spi_end_int               = SPI_INTREN_ENDINTEN_MASK,
    spi_slave_cmd_int         = SPI_INTREN_SLVCMDEN_MASK,
} spi_interrupt_t;

/**
 * @brief spi mode selection
 */
typedef enum {
    spi_master_mode = 0,
    spi_slave_mode
} spi_mode_selection_t;

/**
 * @brief spi clock polarity
 */
typedef enum {
    spi_sclk_low_idle = 0,
    spi_sclk_high_idle
} spi_sclk_idle_state_t;

/**
 * @brief spi clock phase
 */
typedef enum {
    spi_sclk_sampling_odd_clk_edges = 0,
    spi_sclk_sampling_even_clk_edges
} spi_sclk_sampling_clk_edges_t;

/**
 * @brief spi cs to sclk edge duration
 */
typedef enum {
    spi_cs2sclk_half_sclk_1 = 0,
    spi_cs2sclk_half_sclk_2,
    spi_cs2sclk_half_sclk_3,
    spi_cs2sclk_half_sclk_4
} spi_cs2sclk_duration_t;

/**
 * @brief spi cs high level duration
 */
typedef enum {
    spi_csht_half_sclk_1 = 0,
    spi_csht_half_sclk_2,
    spi_csht_half_sclk_3,
    spi_csht_half_sclk_4,
    spi_csht_half_sclk_5,
    spi_csht_half_sclk_6,
    spi_csht_half_sclk_7,
    spi_csht_half_sclk_8,
    spi_csht_half_sclk_9,
    spi_csht_half_sclk_10,
    spi_csht_half_sclk_11,
    spi_csht_half_sclk_12,
    spi_csht_half_sclk_13,
    spi_csht_half_sclk_14,
    spi_csht_half_sclk_15,
    spi_csht_half_sclk_16,
} spi_csht_duration_t;

/**
 * @brief spi address phase format
 */
typedef enum {
    spi_address_phase_format_single_io_mode = 0,
    spi_address_phase_format_dualquad_io_mode
} spi_addr_phase_format_t;

/**
 * @brief spi transfer mode
 */
typedef enum {
    spi_trans_write_read_together = 0,
    spi_trans_write_only,
    spi_trans_read_only,
    spi_trans_write_read,
    spi_trans_read_write,
    spi_trans_write_dummy_read,
    spi_trans_read_dummy_write,
    spi_trans_no_data,
    spi_trans_dummy_write,
    spi_trans_dummy_read
} spi_trans_mode_t;

/**
 * @brief spi data phase format
 */
typedef enum {
    spi_single_io_mode = 0,
    spi_dual_io_mode,
    spi_quad_io_mode,
} spi_data_phase_format_t;

/**
 * @brief spi token value
 */
typedef enum {
    spi_token_value_0x00 = 0,
    spi_token_value_0x69
} spi_token_value_t;

/**
 * @brief spi dummy count
 */
typedef enum {
    spi_dummy_count_1 = 0,
    spi_dummy_count_2,
    spi_dummy_count_3,
    spi_dummy_count_4
} spi_dummy_count_t;

typedef enum {
    msb_first = 0,
    lsb_first,
} spi_shift_direction_t;

/**
 * @brief spi master interface timing config structure
 */
typedef struct {
    uint32_t clk_src_freq_in_hz;
    uint32_t sclk_freq_in_hz;
    uint8_t cs2sclk;
    uint8_t csht;
} spi_master_timing_config_t;

/**
 * @brief spi interface timing config structure
 */
typedef struct {
    spi_master_timing_config_t master_config;
} spi_timing_config_t;

/**
 * @brief spi master transfer format config structure
 */
typedef struct {
    uint8_t addr_len_in_bytes;
} spi_master_format_config_t;

/**
 * @brief spi common format config structure
 */
typedef struct {
    uint8_t data_len_in_bits;
    bool data_merge;
    bool mosi_bidir;
    bool lsb;
    uint8_t mode;
    uint8_t cpol;
    uint8_t cpha;
} spi_common_format_config_t;

/**
 * @brief spi format config structure
 */
typedef struct {
    spi_master_format_config_t master_config;
    spi_common_format_config_t common_config;
} spi_format_config_t;

/**
 * @brief spi master transfer control config structure
 */
typedef struct {
    bool cmd_enable;
    bool addr_enable;
    uint8_t addr_phase_fmt;
    bool token_enable;
    uint8_t token_value;
} spi_master_control_config_t;

/**
 * @brief spi slave transfer control config structure
 */
typedef struct {
    bool slave_data_only;
} spi_slave_control_config_t;

/**
 * @brief spi common transfer control config structure
 */
typedef struct {
    bool tx_dma_enable;
    bool rx_dma_enable;
    uint8_t trans_mode;
    uint8_t data_phase_fmt;
    uint8_t dummy_cnt;
#if defined(HPM_IP_FEATURE_SPI_CS_SELECT) && (HPM_IP_FEATURE_SPI_CS_SELECT == 1)
    uint8_t cs_index;
#endif
} spi_common_control_config_t; /*!< value in spi_cs_index_t */

/**
 * @brief spi control config structure
 */
typedef struct {
    spi_master_control_config_t master_config;
    spi_slave_control_config_t  slave_config;
    spi_common_control_config_t common_config;
} spi_control_config_t;

#if defined(HPM_IP_FEATURE_SPI_CS_SELECT) && (HPM_IP_FEATURE_SPI_CS_SELECT == 1)
typedef enum {
    spi_cs_0 = 1,
    spi_cs_1 = 2,
    spi_cs_2 = 4,
    spi_cs_3 = 8,
} spi_cs_index_t;
#endif

typedef enum {
    addrlen_8bit = 0,
    addrlen_16bit,
    addrlen_24bit,
    addrlen_32bit
} spi_address_len_t;

#if defined(HPM_IP_FEATURE_SPI_SUPPORT_DIRECTIO) && (HPM_IP_FEATURE_SPI_SUPPORT_DIRECTIO == 1)
typedef enum {
    hold_pin = 0,
    wp_pin,
    miso_pin,
    mosi_pin,
    sclk_pin,
    cs_pin
} spi_directio_pin_t;

#endif

/**
 * @brief SPI status
 */
enum {
    status_spi_master_busy = MAKE_STATUS(status_group_spi, 1),
};

#if defined(__cplusplus)
extern "C" {
#endif /* __cplusplus */

/**
 * @brief spi master get default timing config
 *
 * @param [out] config spi_timing_config_t
 */
void spi_master_get_default_timing_config(spi_timing_config_t *config);

/**
 * @brief spi master get default format config
 *
 * @param [out] config spi_format_config_t
 */
void spi_master_get_default_format_config(spi_format_config_t *config);

/**
 * @brief spi master get default control config
 *
 * @param [out] config spi_control_config_t
 */
void spi_master_get_default_control_config(spi_control_config_t *config);

/**
 * @brief spi slave get default format config
 *
 * @param [out] config spi_format_config_t
 */
void spi_slave_get_default_format_config(spi_format_config_t *config);

/**
 * @brief spi slave get default control config
 *
 * @param [out] config spi_control_config_t
 */
void spi_slave_get_default_control_config(spi_control_config_t *config);

/**
 * @brief spi master timing initialization
 *
 * @param [in] ptr SPI base address
 * @param [in] config spi_timing_config_t
 * @retval hpm_stat_t status_invalid_argument or status_success
 */
hpm_stat_t spi_master_timing_init(SPI_Type *ptr, spi_timing_config_t *config);

/**
 * @brief spi format initialization
 *
 * @param [in] ptr SPI base address
 * @param [in] config spi_format_config_t
 */
void spi_format_init(SPI_Type *ptr, spi_format_config_t *config);

/**
 * @brief spi transfer
 *
 * @param [in] ptr SPI base address
 * @param [in] config spi_control_config_t
 * @param [in,out] cmd spi transfer command address
 * @param [in] addr spi transfer target address
 * @param [in] wbuff spi sent data buff address
 * @param [in] wcount spi sent data count, not greater than SPI_SOC_TRANSFER_COUNT_MAX
 * @param [out] rbuff spi receive data buff address
 * @param [in] rcount spi receive data count, not greater than SPI_SOC_TRANSFER_COUNT_MAX
 * @retval hpm_stat_t status_success if spi transfer without any error
 */
hpm_stat_t spi_transfer(SPI_Type *ptr,
                        spi_control_config_t *config,
                        uint8_t *cmd, uint32_t *addr,
                        uint8_t *wbuff, uint32_t wcount,  uint8_t *rbuff, uint32_t rcount);

/**
 * @brief spi setup dma transfer
 *
 * @param [in] ptr SPI base address
 * @param [in] config spi_control_config_t
 * @param [in] cmd spi transfer command address
 * @param [in] addr spi transfer target address
 * @param [in] wcount spi sent data count, not greater than SPI_SOC_TRANSFER_COUNT_MAX
 * @param [in] rcount spi receive data count, not greater than SPI_SOC_TRANSFER_COUNT_MAX
 * @retval hpm_stat_t status_success if spi setup dma transfer without any error
 */
hpm_stat_t spi_setup_dma_transfer(SPI_Type *ptr,
                        spi_control_config_t *config,
                        uint8_t *cmd, uint32_t *addr,
                        uint32_t wcount, uint32_t rcount);

/**
 * @brief spi wait for idle status
 *
 * @note on slave mode, if CS signal is asserted, take it as busy; if SPI CS signal is de-asserted, take it as idle.
 *
 * @param [in] ptr SPI base address
 * @retval hpm_stat_t status_success if spi in idle status
 */
hpm_stat_t spi_wait_for_idle_status(SPI_Type *ptr);

/**
 * @brief spi wait for busy status
 *
 * @note on slave mode, if CS signal is asserted, take it as busy; if SPI CS signal is de-asserted, take it as idle.
 *
 * @param [in] ptr SPI base address
 * @retval hpm_stat_t status_success if spi in busy status
 */
hpm_stat_t spi_wait_for_busy_status(SPI_Type *ptr);

/**
 * @brief SPI set TX FIFO threshold
 *
 * This function configures SPI TX FIFO threshold.
 *
 * @param ptr SPI base address.
 * @param threshold The FIFO threshold value, the value should not greater than FIFO size.
 */
static inline void spi_set_tx_fifo_threshold(SPI_Type *ptr, uint32_t threshold)
{
    ptr->CTRL = (ptr->CTRL & ~SPI_CTRL_TXTHRES_MASK) | SPI_CTRL_TXTHRES_SET(threshold);
}

/**
 * @brief SPI set RX FIFO threshold
 *
 * This function configures SPI RX FIFO threshold.
 *
 * @param ptr SPI base address.
 * @param threshold The FIFO threshold value, the value should not greater than FIFO size.
 */
static inline void spi_set_rx_fifo_threshold(SPI_Type *ptr, uint32_t threshold)
{
    ptr->CTRL = (ptr->CTRL & ~SPI_CTRL_RXTHRES_MASK) | SPI_CTRL_RXTHRES_SET(threshold);
}

/**
 * @brief Enables the SPI DMA request.
 *
 * This function configures the Rx and Tx DMA mask of the SPI. The parameters are base and a DMA mask.
 *
 * @param ptr SPI base address.
 * @param mask The dma enable mask; Use the spi_dma_enable_t.
 */
static inline void spi_enable_dma(SPI_Type *ptr, uint32_t mask)
{
    ptr->CTRL |= mask;
}

/*!
 * @brief Disables the SPI DMA request.
 *
 * This function configures the Rx and Tx DMA mask of the SPI.  The parameters are base and a DMA mask.
 *
 * @param ptr SPI base address.
 * @param mask The dma enable mask; Use the spi_dma_enable_t.
 */
static inline void spi_disable_dma(SPI_Type *ptr, uint32_t mask)
{
    ptr->CTRL &= ~mask;
}

/**
 * @brief Get the SPI interrupt status.
 *
 * This function gets interrupt status of the SPI.
 *
 * @param ptr SPI base address.
 * @retval SPI interrupt status register value
 */
static inline uint32_t spi_get_interrupt_status(SPI_Type *ptr)
{
    return ptr->INTRST;
}

/**
 * @brief Clear the SPI interrupt status.
 *
 * This function clears interrupt status of the SPI.
 *
 * @param ptr SPI base address.
 * @param mask The interrupt mask; Use the spi_interrupt_t.
 *
 */
static inline void spi_clear_interrupt_status(SPI_Type *ptr, uint32_t mask)
{
    /* write 1 to clear */
    ptr->INTRST = mask;
}

/**
 * @brief Enables the SPI interrupt.
 *
 * This function configures interrupt of the SPI. The parameters are base and a interrupt mask.
 *
 * @param ptr SPI base address.
 * @param mask The interrupt mask; Use the spi_interrupt_t.
 */
static inline void spi_enable_interrupt(SPI_Type *ptr, uint32_t mask)
{
    ptr->INTREN |= mask;
}

/*!
 * @brief Disables the SPI interrupt.
 *
 * This function configures interrupt of the SPI. The parameters are base and a interrupt mask.
 *
 * @param ptr SPI base address.
 * @param mask The interrupt mask; Use the spi_interrupt_t.
 */
static inline void spi_disable_interrupt(SPI_Type *ptr, uint32_t mask)
{
    ptr->INTREN &= ~mask;
}

/**
 * @brief spi write and read data
 *
 * @note Call this function after SPI CONTROL is configured by spi_control_init.
 * The order of reading and writing is controlled by spi_control_init.
 *
 * @param [in] ptr SPI base address
 * @param [in] data_len_in_bytes data length in bytes
 * @param [in] wbuff spi sent data buff address
 * @param [in] wcount spi sent data count, not greater than SPI_SOC_TRANSFER_COUNT_MAX
 * @param [out] rbuff spi receive data buff address
 * @param [in] rcount spi receive data count, not greater than SPI_SOC_TRANSFER_COUNT_MAX
 * @retval hpm_stat_t status_success if spi transfer without any error
 */
hpm_stat_t spi_write_read_data(SPI_Type *ptr, uint8_t data_len_in_bytes, uint8_t *wbuff, uint32_t wcount, uint8_t *rbuff, uint32_t rcount);

/**
 * @brief spi read data
 *
 * @note Call this function after SPI CONTROL is configured by spi_control_init.
 *
 * @param [in] ptr SPI base address
 * @param [in] data_len_in_bytes data length in bytes
 * @param [out] buff spi receive data buff address
 * @param [in] count spi receive data count, not greater than SPI_SOC_TRANSFER_COUNT_MAX
 * @retval hpm_stat_t status_success if spi transfer without any error
 */
hpm_stat_t spi_read_data(SPI_Type *ptr, uint8_t data_len_in_bytes, uint8_t *buff, uint32_t count);

/**
 * @brief spi write data
 *
 * @note Call this function after SPI CONTROL is configured by spi_control_init.
 *
 * @param [in] ptr SPI base address
 * @param [in] data_len_in_bytes data length in bytes
 * @param [in] buff spi sent data buff address
 * @param [in] count spi sent data count, not greater than SPI_SOC_TRANSFER_COUNT_MAX
 * @retval hpm_stat_t status_success if spi transfer without any error
 */
hpm_stat_t spi_write_data(SPI_Type *ptr, uint8_t data_len_in_bytes, uint8_t *buff, uint32_t count);

/**
 * @brief spi write command
 *
 * Writing operations on this register will trigger SPI transfers, call this function on master mode.
 *
 * @param [in] ptr SPI base address
 * @param [in] mode spi mode, use the spi_mode_selection_t
 * @param [in] config point to spi_control_config_t
 * @param [in] cmd command data address
 * @retval hpm_stat_t status_success if spi transfer without any error
 */
hpm_stat_t spi_write_command(SPI_Type *ptr, spi_mode_selection_t mode, spi_control_config_t *config, uint8_t *cmd);

/**
 * @brief spi read command
 *
 * On slave mode, the command field of the last received SPI transaction is stored in this SPI Command Register
 *
 * @param [in] ptr SPI base address
 * @param [in] mode spi mode, use the spi_mode_selection_t
 * @param [in] config point to spi_control_config_t
 * @param [out] cmd command data address
 * @retval hpm_stat_t status_success if spi transfer without any error
 */
hpm_stat_t spi_read_command(SPI_Type *ptr, spi_mode_selection_t mode, spi_control_config_t *config, uint8_t *cmd);

/**
 * @brief spi write address
 *
 * @note Call this function on master mode.
 *
 * @param [in] ptr SPI base address
 * @param [in] mode spi mode, use the spi_mode_selection_t
 * @param [in] config point to spi_control_config_t
 * @param [in] addr point to address
 * @retval hpm_stat_t status_success if spi transfer without any error
 */
hpm_stat_t spi_write_address(SPI_Type *ptr, spi_mode_selection_t mode, spi_control_config_t *config, uint32_t *addr);

/**
 * @brief spi control initialization
 *
 * @param [in] ptr SPI base address
 * @param [in] config point to spi_control_config_t
 * @param [in] wcount spi sent data count, not greater than SPI_SOC_TRANSFER_COUNT_MAX
 * @param [in] rcount spi receive count, not greater than SPI_SOC_TRANSFER_COUNT_MAX
 * @retval hpm_stat_t status_success if spi transfer without any error
 */
hpm_stat_t spi_control_init(SPI_Type *ptr, spi_control_config_t *config, uint32_t wcount, uint32_t rcount);

/**
 * @brief Get the SPI data length in bits.
 *
 * @param ptr SPI base address.
 * @retval SPI data length in bits
 */
static inline uint8_t spi_get_data_length_in_bits(SPI_Type *ptr)
{
    return ((ptr->TRANSFMT & SPI_TRANSFMT_DATALEN_MASK) >> SPI_TRANSFMT_DATALEN_SHIFT) + 1;
}

/**
 * @brief Get the SPI data length in bytes.
 *
 * @param ptr SPI base address.
 * @retval SPI data length in bytes
 */
static inline uint8_t spi_get_data_length_in_bytes(SPI_Type *ptr)
{
    return ((spi_get_data_length_in_bits(ptr) + 7U) / 8U);
}

/**
 * @brief SPI get active status.
 *
 * @param ptr SPI base address.
 * @retval bool true for active, false for inactive
 */
static inline bool spi_is_active(SPI_Type *ptr)
{
    return ((ptr->STATUS & SPI_STATUS_SPIACTIVE_MASK) == SPI_STATUS_SPIACTIVE_MASK) ? true : false;
}

/**
 * @brief SPI enable tx dma
 *
 * @param ptr SPI base address
 */
static inline void spi_enable_tx_dma(SPI_Type *ptr)
{
    ptr->CTRL |= SPI_CTRL_TXDMAEN_MASK;
}

/**
 * @brief SPI disable tx dma
 *
 * @param ptr SPI base address
 */
static inline void spi_disable_tx_dma(SPI_Type *ptr)
{
    ptr->CTRL &= ~SPI_CTRL_TXDMAEN_MASK;
}

/**
 * @brief SPI enable rx dma
 *
 * @param ptr SPI base address
 */
static inline void spi_enable_rx_dma(SPI_Type *ptr)
{
    ptr->CTRL |= SPI_CTRL_RXDMAEN_MASK;
}

/**
 * @brief SPI disable rx dma
 *
 * @param ptr SPI base address
 */
static inline void spi_disable_rx_dma(SPI_Type *ptr)
{
    ptr->CTRL &= ~SPI_CTRL_RXDMAEN_MASK;
}

/**
 * @brief SPI slave get sent data count
 *
 * @param ptr SPI base address
 * @retval uint32_t data count
 */
static inline uint32_t spi_slave_get_sent_data_count(SPI_Type *ptr)
{
#if defined(HPM_IP_FEATURE_SPI_NEW_TRANS_COUNT) && (HPM_IP_FEATURE_SPI_NEW_TRANS_COUNT == 1)
    return ptr->SLVDATAWCNT;
#else
    return SPI_SLVDATACNT_WCNT_GET(ptr->SLVDATACNT);
#endif
}

/**
 * @brief SPI slave get received data count
 *
 * @param ptr SPI base address
 * @retval uint32_t data count
 */
static inline uint32_t spi_slave_get_received_data_count(SPI_Type *ptr)
{
#if defined(HPM_IP_FEATURE_SPI_NEW_TRANS_COUNT) && (HPM_IP_FEATURE_SPI_NEW_TRANS_COUNT == 1)
    return ptr->SLVDATARCNT;
#else
    return SPI_SLVDATACNT_RCNT_GET(ptr->SLVDATACNT);
#endif
}

/**
 * @brief set spi clock phase
 *
 * @param [in] ptr SPI base address
 * @param [in] clock_phase clock phase enum
 */
static inline void spi_set_clock_phase(SPI_Type *ptr, spi_sclk_sampling_clk_edges_t clock_phase)
{
    ptr->TRANSFMT = (ptr->TRANSFMT & ~SPI_TRANSFMT_CPHA_MASK) | SPI_TRANSFMT_CPHA_SET(clock_phase);
}

/**
 * @brief get spi clock phase
 *
 * @param [in] ptr SPI base address
 * @retval spi_sclk_sampling_clk_edges_t spi_sclk_sampling_odd_clk_edges if CPHA is 0
 */
static inline spi_sclk_sampling_clk_edges_t spi_get_clock_phase(SPI_Type *ptr)
{
    return (spi_sclk_sampling_clk_edges_t)SPI_TRANSFMT_CPHA_GET(ptr->TRANSFMT);
}

/**
 * @brief set spi clock polarity
 *
 * @param [in] ptr SPI base address
 * @param [in] clock_polarity clock polarity enum
 */
static inline void spi_set_clock_polarity(SPI_Type *ptr, spi_sclk_idle_state_t clock_polarity)
{
    ptr->TRANSFMT = (ptr->TRANSFMT & ~SPI_TRANSFMT_CPOL_MASK) | SPI_TRANSFMT_CPOL_SET(clock_polarity);
}

/**
 * @brief get spi clock phase
 *
 * @param [in] ptr SPI base address
 * @retval spi_sclk_idle_state_t spi_sclk_low_idle if CPOL is 0
 */
static inline spi_sclk_idle_state_t spi_get_clock_polarity(SPI_Type *ptr)
{
    return (spi_sclk_idle_state_t)SPI_TRANSFMT_CPOL_GET(ptr->TRANSFMT);
}

/**
 * @brief set spi the length of each data unit in bits
 *
 * @param [in] ptr SPI base address
 * @param [in] nbits the actual bits number of a data
 * @retval hpm_stat_t status_success if spi transfer without any error
 */
static inline hpm_stat_t spi_set_data_bits(SPI_Type *ptr, uint8_t nbits)
{
    if (nbits > 32) {
        return status_invalid_argument;
    } else {
        ptr->TRANSFMT = (ptr->TRANSFMT & ~SPI_TRANSFMT_DATALEN_MASK) | SPI_TRANSFMT_DATALEN_SET(nbits - 1);
        return status_success;
    }
}

/**
 * @brief SPI transmit fifo reset
 *
 * @param ptr SPI base address
 */
static inline void spi_transmit_fifo_reset(SPI_Type *ptr)
{
    ptr->CTRL |= SPI_CTRL_TXFIFORST_MASK;
}

/**
 * @brief SPI receive fifo reset
 *
 * @param ptr SPI base address
 */
static inline void spi_receive_fifo_reset(SPI_Type *ptr)
{
    ptr->CTRL |= SPI_CTRL_RXFIFORST_MASK;
}

/**
 * @brief SPI reset
 *
 * @param ptr SPI base address
 */
static inline void spi_reset(SPI_Type *ptr)
{
    ptr->CTRL |= SPI_CTRL_SPIRST_MASK;
}

/**
 * @brief set spi the length of address
 *
 * @param [in] ptr SPI base address
 * @param [in] addrlen address lenth enum
 */
static inline void spi_set_address_len(SPI_Type *ptr, spi_address_len_t addrlen)
{
    ptr->TRANSFMT = (ptr->TRANSFMT & ~SPI_TRANSFMT_ADDRLEN_MASK) | SPI_TRANSFMT_ADDRLEN_SET(addrlen);
}

/**
 * @brief   Enable SPI data merge
 *
 * @param [in] ptr SPI base address
 */
static inline void spi_enable_data_merge(SPI_Type *ptr)
{
    ptr->TRANSFMT |= SPI_TRANSFMT_DATAMERGE_MASK;
}

/**
 * @brief   Disable SPI data merge
 *
 * @param [in] ptr SPI base address
 */
static inline void spi_disable_data_merge(SPI_Type *ptr)
{
    ptr->TRANSFMT &= ~SPI_TRANSFMT_DATAMERGE_MASK;
}

#if defined(HPM_IP_FEATURE_SPI_SUPPORT_DIRECTIO) && (HPM_IP_FEATURE_SPI_SUPPORT_DIRECTIO == 1)
/**
 * @brief enable specific pin output for spi directio
 *
 * @note must be used spi_enable_directio API before enable output function
 *
 * @param [in] ptr SPI base address
 * @param [in] pin spi_directio_pin_t enum
 */
hpm_stat_t spi_directio_enable_output(SPI_Type *ptr, spi_directio_pin_t pin);

/**
 * @brief disable specific pin output for spi directio
 *
 * @param [in] ptr SPI base address
 * @param [in] pin spi_directio_pin_t enum
 */
hpm_stat_t spi_directio_disable_output(SPI_Type *ptr, spi_directio_pin_t pin);

/**
 * @brief write specified pin level  for spi directio
 *
 * @param [in] ptr SPI base address
 * @param [in] pin spi_directio_pin_t enum
 * @param [in] high Pin level set to high when it is set to true
 */
hpm_stat_t spi_directio_write(SPI_Type *ptr, spi_directio_pin_t pin, bool high);

/**
 * @brief   Read specified pin level for spi directio
 *
 * @param [in] ptr SPI base address
 * @param pin spi_directio_pin_t enum
 *
 * @return Pin status
 */
uint8_t spi_directio_read(SPI_Type *ptr, spi_directio_pin_t pin);

/**
 * @brief   Enable SPI directIO control function
 *
 * @note if SPI transmission is required, the function must be disable
 *
 * @param [in] ptr SPI base address
 */
static inline void spi_enable_directio(SPI_Type *ptr)
{
    ptr->DIRECTIO |= SPI_DIRECTIO_DIRECTIOEN_MASK;
}

/**
 * @brief   Disable SPI directIO control function
 *
 * @param [in] ptr SPI base address
 */
static inline void spi_disable_directio(SPI_Type *ptr)
{
    ptr->DIRECTIO &= ~SPI_DIRECTIO_DIRECTIOEN_MASK;
}

/**
 * @brief  get whether spi directio function is enabled
 *
 * @param [in] ptr SPI base address
 *
 * @return if pi directio function is enable, it will return 1
 */
static inline uint8_t spi_get_directio_enable_status(SPI_Type *ptr)
{
    return SPI_DIRECTIO_DIRECTIOEN_GET(ptr->DIRECTIO);
}

#endif

/**
 * @brief  Get valid data size in receive FIFO
 *
 * @param [in] ptr SPI base address
 *
 * @return rx fifo valid data size
 */
static inline uint8_t spi_get_rx_fifo_valid_data_size(SPI_Type *ptr)
{
    return ((SPI_STATUS_RXNUM_7_6_GET(ptr->STATUS) << 6) | SPI_STATUS_RXNUM_5_0_GET(ptr->STATUS));
}

/**
 * @brief  Get valid data size in transmit FIFO
 *
 * @param [in] ptr SPI base address
 *
 * @return tx fifo valid data size
 */
static inline uint8_t spi_get_tx_fifo_valid_data_size(SPI_Type *ptr)
{
    return ((SPI_STATUS_TXNUM_7_6_GET(ptr->STATUS) << 6) | SPI_STATUS_TXNUM_5_0_GET(ptr->STATUS));
}

/**
 * @brief  Get SPI RXFIFO size
 *
 * @param [in] ptr SPI base address
 *
 * @return RXFIFO size, the unit is word
 */
static inline uint8_t spi_get_rx_fifo_size(SPI_Type *ptr)
{
    uint8_t size = SPI_CONFIG_RXFIFOSIZE_GET(ptr->CONFIG);
    return (2 << size);
}

/**
 * @brief  Get SPI TXFIFO size
 *
 * @param [in] ptr SPI base address
 *
 * @return TXFIFO size, the unit is word
 */
static inline uint8_t spi_get_tx_fifo_size(SPI_Type *ptr)
{
    uint8_t size = SPI_CONFIG_TXFIFOSIZE_GET(ptr->CONFIG);
    return (2 << size);
}

/**
 * @brief SPI slave enable only date mode
 *
 * @note This mode only works in the uni-directional regular (single) mode
 * @param [in] ptr SPI base address
 */
static inline void spi_slave_enable_data_only(SPI_Type *ptr)
{
    ptr->TRANSCTRL |= SPI_TRANSCTRL_SLVDATAONLY_MASK;
}

/**
 * @brief SPI slave disable only date mode
 *
 * @param [in] ptr SPI base address
 */
static inline void spi_slave_disable_data_only(SPI_Type *ptr)
{
    ptr->TRANSCTRL &= ~SPI_TRANSCTRL_SLVDATAONLY_MASK;
}

/**
 * @brief SPI master enable command phase
 *
 * @param [in] ptr SPI base address
 */
static inline void spi_master_enable_command_phase(SPI_Type *ptr)
{
    ptr->TRANSCTRL |= SPI_TRANSCTRL_CMDEN_MASK;
}

/**
 * @brief SPI master disable command phase
 *
 * @param [in] ptr SPI base address
 */
static inline void spi_master_disable_command_phase(SPI_Type *ptr)
{
    ptr->TRANSCTRL &= ~SPI_TRANSCTRL_CMDEN_MASK;
}

/**
 * @brief SPI master enable address phase
 *
 * @param [in] ptr SPI base address
 */
static inline void spi_master_enable_address_phase(SPI_Type *ptr)
{
    ptr->TRANSCTRL |= SPI_TRANSCTRL_ADDREN_MASK;
}

/**
 * @brief SPI master disable address phase
 *
 * @param [in] ptr SPI base address
 */
static inline void spi_master_disable_address_phase(SPI_Type *ptr)
{
    ptr->TRANSCTRL &= ~SPI_TRANSCTRL_ADDREN_MASK;
}

/**
 * @brief SPI master set address phase format
 *
 * @param [in] ptr SPI base address
 * @param [in] format spi_addr_phase_format_t enum
 */
static inline void spi_master_set_address_phase_format(SPI_Type *ptr, spi_addr_phase_format_t format)
{
    ptr->TRANSCTRL = (ptr->TRANSCTRL & ~SPI_TRANSCTRL_ADDRFMT_MASK) | SPI_TRANSCTRL_ADDRFMT_SET(format);
}

/**
 * @brief SPI master set transfer mode
 *
 * @param [in] ptr SPI base address
 * @param [in] mode spi_trans_mode_t enum
 */
static inline void spi_set_transfer_mode(SPI_Type *ptr, spi_trans_mode_t mode)
{
    ptr->TRANSCTRL = (ptr->TRANSCTRL & ~SPI_TRANSCTRL_TRANSMODE_MASK) | SPI_TRANSCTRL_TRANSMODE_SET(mode);
}

/**
 * @brief SPI master enable token transfer
 *
 * @param [in] ptr SPI base address
 */
static inline void spi_master_enable_token_transfer(SPI_Type *ptr)
{
    ptr->TRANSCTRL |= SPI_TRANSCTRL_TOKENEN_MASK;
}

/**
 * @brief SPI master disable token transfer
 *
 * @param [in] ptr SPI base address
 */
static inline void spi_master_disable_token_transfer(SPI_Type *ptr)
{
    ptr->TRANSCTRL &= ~SPI_TRANSCTRL_TOKENEN_MASK;
}

/**
 * @brief SPI master set transfer count for write data
 *
 * @param [in] ptr SPI base address
 * @param [in] count the number of units of data to be transmitted to the SPI bus from the Data
 */
static inline void spi_set_write_data_count(SPI_Type *ptr, uint32_t count)
{
#if defined(HPM_IP_FEATURE_SPI_NEW_TRANS_COUNT) && (HPM_IP_FEATURE_SPI_NEW_TRANS_COUNT == 1)
    ptr->WR_TRANS_CNT = (count - 1);
#else
    ptr->TRANSCTRL = (ptr->TRANSCTRL & ~SPI_TRANSCTRL_WRTRANCNT_MASK) | SPI_TRANSCTRL_WRTRANCNT_SET(count - 1);
#endif
}

/**
 * @brief SPI master set transfer count for read data
 *
 * @param [in] ptr SPI base address
 * @param [in] count  the number of units of data to be received from SPI bus and stored to the Data
 */
static inline void spi_set_read_data_count(SPI_Type *ptr, uint32_t count)
{
#if defined(HPM_IP_FEATURE_SPI_NEW_TRANS_COUNT) && (HPM_IP_FEATURE_SPI_NEW_TRANS_COUNT == 1)
    ptr->RD_TRANS_CNT = (count - 1);
#else
    ptr->TRANSCTRL = (ptr->TRANSCTRL & ~SPI_TRANSCTRL_RDTRANCNT_MASK) | SPI_TRANSCTRL_RDTRANCNT_SET(count - 1);
#endif
}

/**
 * @brief SPI master set the value of the one-byte special token following the address phase for SPI read transfers
 *
 * @param [in] ptr SPI base address
 * @param [in] value spi_token_value_t enum
 */
static inline void spi_master_set_token_value(SPI_Type *ptr, spi_token_value_t value)
{
    ptr->TRANSCTRL = (ptr->TRANSCTRL & ~SPI_TRANSCTRL_TOKENVALUE_MASK) | SPI_TRANSCTRL_TOKENVALUE_SET(value);
}

/**
 * @brief SPI master set dummy data count
 *
 * @param [in] ptr SPI base address
 * @param [in] count spi_dummy_count_t enum
 */
static inline void spi_set_dummy_count(SPI_Type *ptr, spi_dummy_count_t count)
{
    ptr->TRANSCTRL = (ptr->TRANSCTRL & ~SPI_TRANSCTRL_DUMMYCNT_MASK) | SPI_TRANSCTRL_DUMMYCNT_SET(count);
}

/**
 * @brief SPI master set the minimum time between the edges of SPI CS and the edges of SCLK
 *
 * @param [in] ptr SPI base address
 * @param [in] duration spi_cs2sclk_duration_t enum
 */
static inline void spi_master_set_cs2sclk_timing(SPI_Type *ptr, spi_cs2sclk_duration_t duration)
{
    ptr->TIMING = (ptr->TIMING & ~SPI_TIMING_CS2SCLK_MASK) | SPI_TIMING_CS2SCLK_SET(duration);
}

/**
 * @brief SPI master get the minimum time between the edges of SPI CS and the edges of SCLK
 *
 * @param [in] ptr SPI base address
 * @retval spi_cs2sclk_duration_t enum
 */
static inline spi_cs2sclk_duration_t spi_master_get_cs2sclk_timing(SPI_Type *ptr)
{
    return (spi_cs2sclk_duration_t)SPI_TIMING_CS2SCLK_GET(ptr->TIMING);
}

/**
 * @brief SPI master set the minimum time that SPI CS should stay HIGH.
 *
 * @param [in] ptr SPI base address
 * @param [in] duration spi_csht_duration_t enum
 */
static inline void spi_master_set_csht_timing(SPI_Type *ptr, spi_csht_duration_t duration)
{
    ptr->TIMING = (ptr->TIMING & ~SPI_TIMING_CSHT_MASK) | SPI_TIMING_CSHT_SET(duration);
}

/**
 * @brief SPI master get the minimum time that SPI CS should stay HIGH.
 *
 * @param [in] ptr SPI base address
 * @retval spi_csht_duration_t enum
 */
static inline spi_csht_duration_t spi_master_get_csht_timing(SPI_Type *ptr)
{
    return (spi_csht_duration_t)SPI_TIMING_CSHT_GET(ptr->TIMING);
}

/**
 * @brief SPI master set the clock frequency ratio between the clock source and SPI SCLK
 *
 * @param [in] ptr SPI base address
 * @param [in] div the frequency ratio value
 */
static inline void spi_master_set_sclk_div(SPI_Type *ptr, uint8_t div)
{
    ptr->TIMING = (ptr->TIMING & ~SPI_TIMING_SCLK_DIV_MASK) | SPI_TIMING_SCLK_DIV_SET(div);
}

/**
 * @brief SPI master get the clock frequency ratio between the clock source and SPI SCLK
 *
 * @param [in] ptr SPI base address
 *
 * @retval return the frequency ratio value
 */
static inline uint8_t spi_master_get_sclk_div(SPI_Type *ptr)
{
    return SPI_TIMING_SCLK_DIV_GET(ptr->TIMING);
}

/**
 * @brief SPI slave set the user defined status flags
 *
 * @note in Non-SlvDataOnly, the command is read status command, such as 0x05\0x15\0x25 command
 *
 * @param [in] ptr SPI base address
 * @param [in] user_status user status value
 */
static inline void spi_slave_set_user_status(SPI_Type *ptr, uint16_t user_status)
{
    ptr->SLVST = (ptr->SLVST & ~SPI_SLVST_USR_STATUS_MASK) | SPI_SLVST_USR_STATUS_SET(user_status);
}

/**
 * @brief SPI master set data phase format
 *
 * @param [in] ptr SPI base address
 * @param [in] format spi_data_phase_format_t enum
 */
static inline void spi_master_set_data_phase_format(SPI_Type *ptr, spi_data_phase_format_t format)
{
    ptr->TRANSCTRL = (ptr->TRANSCTRL & ~SPI_TRANSCTRL_DUALQUAD_MASK) | SPI_TRANSCTRL_DUALQUAD_SET(format);
}

/**
 * @brief SPI master get data phase format
 *
 * @param [in] ptr SPI base address
 *
 * @retval return the data phase format
 */
static inline spi_data_phase_format_t spi_master_get_data_phase_format(SPI_Type *ptr)
{
    return (spi_data_phase_format_t)SPI_TRANSCTRL_DUALQUAD_GET(ptr->TRANSCTRL);
}

/**
 * @brief set spi shift direction
 *
 * @param [in] ptr SPI base address
 * @param [in] shift_direction shift direction enum
 */
static inline void spi_set_shift_direction(SPI_Type *ptr, spi_shift_direction_t shift_direction)
{
    ptr->TRANSFMT = (ptr->TRANSFMT & ~SPI_TRANSFMT_LSB_MASK) | SPI_TRANSFMT_LSB_SET(shift_direction);
}

/**
 * @brief get spi shift direction
 *
 * @param [in] ptr SPI base address
 * @retval spi_shift_direction_t msb_first if frist MSB
 */
static inline spi_shift_direction_t spi_get_shift_direction(SPI_Type *ptr)
{
    return (spi_shift_direction_t)SPI_TRANSFMT_LSB_GET(ptr->TRANSFMT);
}

/**
 * @}
 */

#if defined(__cplusplus)
}
#endif /* __cplusplus */
#endif /* HPM_SPI_DRV_H */
