Files
2026-01-12 18:46:01 +08:00

475 lines
14 KiB
C

/**
* @file sdrv_spi.h
* @brief SemiDrive SPI driver header file.
*
* @copyright Copyright (c) 2022 Semidrive Semiconductor.
* All rights reserved.
*/
#ifndef SDRV_SPI_H
#define SDRV_SPI_H
#ifdef __cplusplus
extern "C" {
#endif
#include <sdrv_ckgen.h>
#include <stdbool.h>
#include <stdint.h>
#include "reg.h"
#include "sdrv_dma.h"
#include "spi_reg.h"
#define SPI_PIPE_LINE_SIZE_8B 3U
#define SPI_PIPE_LINE_SIZE_4B 4U
#define SPI_PIPE_LINE_SIZE_2B 5U
#define SPI_PIPE_LINE_SIZE_OB 2U
// 2 mst_idle 3 mst_start 7 mst_data_wait
#define SPI_FSM_WAIT_DATA_STA 7U
#define SPI_FSM_M_STR_STA 3U
#define SPI_FSM_IDLE_STA 2U
#define MAX_TIMEOUT_SWRST 2560U
/**
* @brief Spi sclk idle mode.
*/
enum spi_cpol { SCK_IDLE_LOW, SCK_IDLE_HIGH };
/**
* @brief Spi sclk phase mode.
*/
enum spi_cpha {
DATA_CPT_ON_FIRST_SCK_EDGE,
DATA_CPT_ON_SECOND_SCK_EDGE,
};
/**
* @brief Spi cs mode.
*/
enum spi_cs_polarity { CS_ACTIVE_LOW, CS_ACTIVE_HIGH };
/**
* @brief Spi hardware cs select .
*/
enum spi_cs_select { CS_SEL_SS0 = 0, CS_SEL_SS1, CS_SEL_SS2, CS_SEL_SS3 };
/**
* @brief Spi Bus state flags.
*/
enum spi_state {
SPI_STATE_UNINIT = 0x0,
SPI_STATE_INITED = 0x1,
SPI_STATE_BUSY_TX = 0x02,
SPI_STATE_BUSY_RX = 0x04,
SPI_STATE_IRQ_ACT = 0x08,
SPI_STATE_DMA_TX = 0x10,
SPI_STATE_DMA_RX = 0x20,
SPI_STATE_IRQ_TX = 0x40,
SPI_STATE_IRQ_RX = 0x80,
SPI_STATE_IS_SLAVE = 0x100,
SPI_STATE_CS_ACTIVEED = 0x200,
SPI_STATE_IS_UNS_EN = 0x400,
SPI_STATE_IS_RO_END = 0x800,
SPI_STATE_DMA_TX_RD = 0x1000,
SPI_STATE_DMA_RX_RD = 0x2000,
};
/**
* @brief Spi fifo state flags.
*/
enum fifo_state {
SPI_RX_FIFO_READ = (0x01 << 0),
SPI_TX_FIFO_WRITE = (0x01 << 1),
SPI_TX_FIFO_UDR = (0x01 << 2),
SPI_RX_FIFO_OVR = (0x01 << 3),
SPI_TX_DMA_ERR = (0x01 << 4),
SPI_RX_DMA_ERR = (0x01 << 5),
SPI_TX_ONLY_DONE = (0x01 << 6),
};
/**
* @brief Spi irq type .
*/
enum irq_type {
SPI_TRASPORT_FINISH = 0x01 << 0,
SPI_RX_READ_REQ = 0x01 << 1,
SPI_TX_WRITE_REQ = 0x01 << 2,
SPI_CS_INVLD_REQ = 0x01 << 3,
};
/**
* @brief Spi bus idle/busy state .
*/
enum spi_bus_state { SPI_IDLE = 0, SPI_BUSY };
/**
* @brief Spi transmit mode .
*/
enum spi_ops_type {
OP_MODE_SYNC = 0,
OP_MODE_IRQ,
OP_MODE_DMA,
};
/**
* @brief Spi async transmit callback type .
*/
typedef enum {
SPI_TRANS_DONE = 0,
SPI_TRANS_FAIL,
SPI_TRANS_PASS
} spi_event_type;
/**
* @brief Spi transmit data direction type .
*/
enum data_dir { SPI_RX_DIR = 0x01, SPI_TX_DIR = 0x02 };
/**
* @brief Spi async transmit callback type .
*/
typedef void (*spi_callback_t)(void *spi, spi_event_type Event);
/**
* @brief SPI device configuration.
*/
struct spi_device_config {
/* sclk frea in hz */
uint32_t sclk_freq;
/* Idle level */
enum spi_cpol cpol;
/* Sampling phase */
enum spi_cpha cpha;
/* cs active polarity */
enum spi_cs_polarity cs_pol;
/* ss0-ss3 or GPIO_xx */
uint8_t cs_sel;
/* deivce raw data width 4-32*/
uint8_t width;
/* minimum interval between two transmissions in n*sclk */
uint8_t fream_delay;
/* cs setup time in n*sclk */
uint8_t clk2cs_delay;
/* cs hold time in n*sclk */
uint8_t clk2cs_end_delay;
/* lsb or msb */
bool is_lsb_mode;
/* Control the cs signal through IO or peripherals */
bool is_soft_cs;
/* enable miso sampling delay half sclk on master mode */
bool is_tx_delay;
};
/**
* @brief Spi controller configuration .
*/
struct spi_common_config {
/* motor rola mode or ti mode */
bool is_spi_mode;
/* half mode */
bool is_half_mode;
/* master or slave mode */
bool is_master;
/* polling mode */
bool is_polling_mode;
#if CONFIG_SPI_ENABLE_DMA
/* rx dma channel id */
sdrv_dma_channel_id_e rx_ch_id;
/* tx dma channel id */
sdrv_dma_channel_id_e tx_ch_id;
/* dma handler */
sdrv_dma_t *dma_ins;
#endif
/* reg base */
uint32_t base;
/* irq num */
uint32_t irq;
/* root clk node */
const sdrv_ckgen_slice_node_t *clk;
};
/**
* @brief Spi transmition data ptr .
*/
union spi_data_ptr {
/* data */
uint32_t val;
uint8_t *u8_ptr;
uint16_t *u16_ptr;
uint32_t *u32_ptr;
};
/**
* @brief Spi dma context type .
*/
struct spi_dma_context {
enum data_dir dir;
void *bus;
bool is_need_handle;
};
/**
* @brief Spi transmit context type .
*/
struct spi_transmit_cb {
/* transmit_sche mode need this*/
uint32_t len;
/* tansmit data width type */
uint8_t width_type;
/* prxdata */
union spi_data_ptr prxdata;
/* ptxdata */
union spi_data_ptr ptxdata;
/* prxdata */
uint32_t rx_cur;
/* ptxdata */
uint32_t tx_cur;
/* remain of this item */
uint32_t cur_remian;
/* once except of this item */
uint32_t expect_len;
/* vector transmit need */
struct spi_transmit_cb *next;
};
/**
* @brief Spi bus module handler type .
*/
typedef struct sdrv_spi {
/* get frame static config */
uint32_t base;
/* get frame static config */
uint32_t irq;
/* Max baudrate when divide by 0 */
uint32_t max_baudrate;
/* Max baudrate when divide by 256 */
uint32_t min_baudrate;
/* spi preinstance config */
const struct spi_common_config *com_config;
/* default Config or slave mode cfg */
const struct spi_device_config *dev_config;
/* */
spi_callback_t callback;
/* */
struct spi_transmit_cb transmit_sche;
/* status */
uint32_t state;
/* cur cmd */
uint32_t cur_cmd;
#if CONFIG_SPI_ENABLE_DMA
/* dma rx channel */
sdrv_dma_channel_t rx_dma_chan;
/* dma rx channel */
struct spi_dma_context rx_context;
/* dma rx channel */
sdrv_dma_channel_t tx_dma_chan;
/* dma rx channel */
struct spi_dma_context tx_context;
/* dma error */
uint32_t dma_err;
#endif
} sdrv_spi_t;
#define SPI_POL_VEL_ILLEGAL (~0xFu)
#define SPI_START_DELAY_MAX 0xFFu
#define SPI_END_DELAY_MAX 0xFFu
#define SPI_FRAME_DELAY_MAX 0xFFu
#define SPI_CLK_PRESSCALE_MAX 0xFFu
#define SPI_FIFO_LEN 16u
#define SPI_FRAME_SIZE_MAX 1024U
#define SPI_SLAVE_IRQ_NICE_ADJUST (-1) /* slave irq nice val */
/**
* @brief Spi transmit data width type only for internal .
*/
#define SPI_DATA_WIDTH_MIN 4U
#define SPI_DATA_WIDTH_BYTE 8U
#define SPI_DATA_WIDTH_HALF_WORD 16U
#define SPI_DATA_WIDTH_WORD 32U
#define SPI_DATA_WIDTH_MAX SPI_DATA_WIDTH_WORD
#define SPI_INTERNAL_DIV_SHIFT (1u)
#define SPI_CLK_PRESSCALE_MAX_SHIFT (8u)
#define SPI_TIMMING_CFG(f, s, e) \
(FV_SPI_TIM_CTRL_START_DLY(s) | FV_SPI_TIM_CTRL_END_DLY(e) | \
FV_SPI_TIM_CTRL_FRM_DLY(f))
#define BUS_BUSY_MASK (SPI_STATE_BUSY_TX | SPI_STATE_BUSY_RX)
#define BUS_BUSY_DMA_MASK (SPI_STATE_DMA_TX | SPI_STATE_DMA_RX)
#define BUS_BUSY_IRQ_MASK (SPI_STATE_IRQ_TX | SPI_STATE_IRQ_RX)
#define BUS_FIFO_STATE_MASK (SPI_RX_FIFO_READ | SPI_TX_FIFO_WRITE)
#define BUS_BUSY_ASYNC_MASK (BUS_BUSY_DMA_MASK | BUS_BUSY_IRQ_MASK)
#define BUS_BUSY_STATUS_MASK (BUS_BUSY_MASK | BUS_BUSY_ASYNC_MASK)
#define BUS_FIFO_STATE_ERR_MASK \
(SPI_TX_FIFO_UDR | SPI_RX_FIFO_OVR | SPI_TX_DMA_ERR | SPI_RX_DMA_ERR)
#define BUS_FIFO_EMPTYE_TIMEOUT(f_delay, s_delay, e_delay, baudrate) \
(((f_delay) + (s_delay) + (e_delay) + SPI_DATA_WIDTH_MAX) * 1000000 / \
(baudrate))
#define BUS_STATE_TX_MASK \
(SPI_STATE_DMA_TX | SPI_STATE_BUSY_TX | SPI_STATE_IRQ_TX)
#define BUS_STATE_RX_MASK \
(SPI_STATE_DMA_RX | SPI_STATE_BUSY_RX | SPI_STATE_IRQ_RX)
#define SPI_SATE_EXT_FLAGS \
(SPI_STATE_IS_UNS_EN | SPI_STATE_IS_RO_END | SPI_STATE_CS_ACTIVEED)
#define SPI_GET_WIDTH_SHIFT(width_type) \
(((width_type) == SPI_DATA_WIDTH_WORD \
? 2 \
: ((width_type) / SPI_DATA_WIDTH_BYTE) - 1))
#define SPI_GET_WIDTH_TYPE(width) \
(((width) > 16U) \
? (SPI_DATA_WIDTH_WORD) \
: ((width) > 8U ? SPI_DATA_WIDTH_HALF_WORD : SPI_DATA_WIDTH_BYTE))
#define SPI_IS_VALID_LEN(bytes, width) \
(!((bytes) & ((0x1 << SPI_GET_WIDTH_SHIFT(SPI_GET_WIDTH_TYPE(width))) - 1)))
/* (16 * 32 + 9 * delay * 1000000)/baudrate == us */
#define SPI_READ_FIFO_TIMEOUT_MAX(f_delay, s_delay, e_delay, baudrate) \
((((f_delay) + (s_delay) + (e_delay)) * 9 + \
SPI_DATA_WIDTH_MAX * SPI_FIFO_LEN) * \
1000000 / (baudrate))
/**
* @brief spi status return code
*/
enum {
SDRV_SPI_STATUS_MODE_ERROR = SDRV_ERROR_STATUS(
SDRV_STATUS_GROUP_SPI, 1), /* Spi configuration mode usage mismatch. */
SDRV_SPI_STATUS_GCMD_NO_SUPPORT = SDRV_ERROR_STATUS(
SDRV_STATUS_GROUP_SPI, 2), /* Spi IP version is not supported. */
SDRV_SPI_STATUS_GCMD_TIMEOUT_WRITE = SDRV_ERROR_STATUS(
SDRV_STATUS_GROUP_SPI, 3), /* Spi first Write CMD wait timeout. */
SDRV_SPI_STATUS_GCMD_TIMEOUT_FINISH = SDRV_ERROR_STATUS(
SDRV_STATUS_GROUP_SPI,
4), /* Spi first write CMD last transfer ended wait timeout. */
SDRV_SPI_STATUS_SOFT_CS_TIMEOUT =
SDRV_ERROR_STATUS(SDRV_STATUS_GROUP_SPI,
5), /* Spi CLK idle level switching wait timeout. */
SDRV_SPI_STATUS_UCMD_TIMEOUT_WRITE = SDRV_ERROR_STATUS(
SDRV_STATUS_GROUP_SPI,
6), /* Spi Write CMD wait timeout(Not the first time). */
SDRV_SPI_STATUS_UCMD_TIMEOUT_FINISH = SDRV_ERROR_STATUS(
SDRV_STATUS_GROUP_SPI,
7), /* Spi last transfer ended wait timeout.(Not the first time) */
SDRV_SPI_STATUS_SYNC_TIMEOUT = SDRV_ERROR_STATUS(
SDRV_STATUS_GROUP_SPI, 8), /* Spi Synchronous transfer timeout. */
SDRV_SPI_STATUS_SOFT_CS_WAIT_TIMEOUT = SDRV_ERROR_STATUS(
SDRV_STATUS_GROUP_SPI,
9), /* Spi Synchronous transfer(Tx only) wait clk ended timeout. */
SDRV_SPI_STATUS_FAIL_DMA_SETUP = SDRV_ERROR_STATUS(
SDRV_STATUS_GROUP_SPI, 10), /* Spi dma configuration failed. */
SDRV_SPI_STATUS_FAIL_IRQ_SETUP = SDRV_ERROR_STATUS(
SDRV_STATUS_GROUP_SPI, 11), /* Spi irq configuration failed. */
SDRV_SPI_STATUS_PARAM_ILLEGAL = SDRV_ERROR_STATUS(
SDRV_STATUS_GROUP_SPI, 12), /* Spi user params error.*/
};
/**
* @brief Initialize the SPI module.
*
* @param[in] spi SPI bus
* @param[in] cfg SPI device configuration.
*
* @note SPI device configuration memory must not be recycled during transfer.
* @return 0 is ok, otherwise bus error occurred.
*/
int sdrv_spi_init(struct sdrv_spi *spi, struct spi_common_config *cfg);
/**
* @brief Deinitialize the SPI device.
*
* @param[in] bus SPI bus
*
* @return 0 is ok, otherwise bus error occurred.
*/
int sdrv_spi_deinit(struct sdrv_spi *bus);
/**
* @brief SPI device configuration.
*
* This function select and activate an external device. Data transfers are
* performed according to this device configuration.
*
* @param[in] spi SPI bus
* @param[in] cfg SPI device configuration.
*
* @note SPI device configuration memory must not be recycled during
* transfer.
* @return 0 is ok, and other values mean bus is busy.
*/
int sdrv_spi_config_device(struct sdrv_spi *spi,
const struct spi_device_config *cfg);
/**
* @brief SPI sync transmit.
*
* @param[in] spi SPI bus.
* @param[in] tx_buf Pointer to data to transmit, which must be aligned with the
* corresponding data width, otherwise unaligned access will occur and cause an
* exception.
* @param[out] rx_buf Pointer to receive data storage, which must be aligned
* with the corresponding data width, otherwise unaligned access will occur and
* cause an exception.
* @param[in] bytes The amount of data in bytes.
* @param[in] timeout Timeout time >= total transmitted bytes / data-width*2.
* @return 0 is ok, and other values for error.
*/
int sdrv_spi_sync_transmit(struct sdrv_spi *spi, void *tx_buf, void *rx_buf,
uint32_t bytes, uint32_t timeout);
/**
* @brief Setup the async callback handler.
*
* @param[in] spi SPI bus
* @param[in] cb The async callback handler.
*/
void sdrv_spi_attach_async_callback(struct sdrv_spi *spi, spi_callback_t cb);
/**
* @brief SPI async transmit in IRQ or DMA mode.
*
* @param[in] spi SPI bus
* @param[in] tx_buf Pointer to data to transmit, which must be aligned with the
* corresponding data width, otherwise unaligned access will occur and cause an
* exception.
* @param[out] rx_buf Pointer to receive data storage, which must be aligned
* with the corresponding data width, otherwise unaligned access will occur and
* cause an exception.
* @param[in] bytes The amount of data in bytes.
* @param[in] type OP_MODE_IRQ for IRQ Mode. OP_MODE_DMA for DMA Mode.
* @return 0 is ok, and other values for error.
*/
int sdrv_spi_async_transmit(struct sdrv_spi *spi, void *tx_buf, void *rx_buf,
uint32_t bytes, enum spi_ops_type type);
/**
* @brief Get SPI bus state
*
* @param[in] spi SPI bus
* @return SPI_BUSY: spi bus is busy. SPI_IDLE: spi bus is idle.
*/
enum spi_bus_state sdrv_spi_get_status(struct sdrv_spi *spi);
/**
* @brief Get SPI bus polling
*
* @param spi SPI bus
* @return None
*/
void sdrv_spi_polling(struct sdrv_spi *bus);
/**
* @brief Get slave transmit cnt
*
* @param[in] spi SPI bus
* @return uint32: transmited bytes.
*/
uint32_t sdrv_spi_slave_get_transmit_len(struct sdrv_spi *bus);
#ifdef __cplusplus
}
#endif
#endif