本次的spi專題,主要參考:這位大佬的部落格
感覺大佬們的無私奉獻,而我寫部落格的初衷除了記錄自己的學習經曆之外,同時也分享給大家,分享知識。
系列文章:
Linux spi驅動架構分析(一)
Linux spi驅動架構分析(二)
Linux spi驅動架構分析(三)
Linux spi驅動架構分析(四)
Linux spi驅動架構
Linux下spi的驅動架構如下圖:
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiclRnblN2XjlGcjAzNfRHLGZkRGZkRfJ3bs92YsYTMfVmepNHL0EkeNFTSU5kMRpHW3BjMMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnL4IjM3IDO1MTM0AzMwEjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
從圖中可以觀察到spi系統的整個架構,發現跟i2c的架構很十分相似;spi驅動架構主要分為三個部分,spi控制器驅動,spi裝置驅動,和spi核心。
核心把spi控制器抽象為struct spi_master結構體,裝置驅動抽象為spi_driver,spi裝置抽象spi_device結構體。
spi與i2c核心空間的類比:
(1)spi_master就相當于i2c中的i2c_adapter
(2)spi_driver即i2c_driver
(3)spi_device即i2c中的i2c_client
(4)spi core 即i2c core
主要相關結構體的定義
spi_driver,描述一個spi驅動,定義如下:
struct spi_driver {
//id比對表
const struct spi_device_id *id_table;
//與裝置比對時,會調用probe函數
int (*probe)(struct spi_device *spi);
int (*remove)(struct spi_device *spi);
void (*shutdown)(struct spi_device *spi);
struct device_driver driver;
};
spi_device ,描述一個spi裝置:
struct spi_device {
struct device dev;
//所屬spi_master
struct spi_master *master;
//該裝置支援的最大時鐘頻率
u32 max_speed_hz;
//片選索引
u8 chip_select;
//傳輸位寬
u8 bits_per_word;
//裝置的工作模式
u16 mode;
#define SPI_CPHA 0x01 /* clock phase */
#define SPI_CPOL 0x02 /* clock polarity */
#define SPI_MODE_0 (0|0) /* (original MicroWire) */
#define SPI_MODE_1 (0|SPI_CPHA)
#define SPI_MODE_2 (SPI_CPOL|0)
#define SPI_MODE_3 (SPI_CPOL|SPI_CPHA)
#define SPI_CS_HIGH 0x04 /* chipselect active high? */
#define SPI_LSB_FIRST 0x08 /* per-word bits-on-wire */
#define SPI_3WIRE 0x10 /* SI/SO signals shared */
#define SPI_LOOP 0x20 /* loopback mode */
#define SPI_NO_CS 0x40 /* 1 dev/bus, no chipselect */
#define SPI_READY 0x80 /* slave pulls low to pause */
#define SPI_TX_DUAL 0x100 /* transmit with 2 wires */
#define SPI_TX_QUAD 0x200 /* transmit with 4 wires */
#define SPI_RX_DUAL 0x400 /* receive with 2 wires */
#define SPI_RX_QUAD 0x800 /* receive with 4 wires */
int irq;
void *controller_state;
void *controller_data;
//裝置的name
char modalias[SPI_NAME_SIZE];
//片選對應的gpio
int cs_gpio; /* chip select gpio */
/* the statistics */
struct spi_statistics statistics;
};
spi_transfer,描述一個spi的讀/寫緩存區:
struct spi_transfer {
//發送buf
const void *tx_buf;
//接收buf
void *rx_buf;
//資料長度
unsigned len;
//DMA
dma_addr_t tx_dma;
dma_addr_t rx_dma;
struct sg_table tx_sg;
struct sg_table rx_sg;
unsigned cs_change:1;
unsigned tx_nbits:3;
unsigned rx_nbits:3;
#define SPI_NBITS_SINGLE 0x01 /* 1bit transfer */
#define SPI_NBITS_DUAL 0x02 /* 2bits transfer */
#define SPI_NBITS_QUAD 0x04 /* 4bits transfer */
//資料位寬
u8 bits_per_word;
u16 delay_usecs;
//速度頻率
u32 speed_hz;
//節點,用于連結到spi_message的transfers連結清單上
struct list_head transfer_list;
};
tx_buf和rx_buf提供了非dma模式下的資料緩沖區位址,tx_dma和rx_dma則給出了dma模式下的緩沖區位址。
對于spi_master來說,一次資料傳輸的機關由spi_message結構體描述,多個spi_transfer會連結到spi_message的transfers連結清單,可以認為spi_message就是一次SPI資料交換的原子操作。spi_master接收了一個spi_message,其中的spi_transfer應該按順序被發送,并且不能被其它spi_message打斷。
struct spi_message {
//連結清單頭,連結該spi_message的所有spi_transfer
struct list_head transfers;
struct spi_device *spi;
unsigned is_dma_mapped:1;
//該message下的所有spi_transfer都被傳輸完成時complete會被調用
void (*complete)(void *context);
void *context;
unsigned frame_length;
unsigned actual_length;
int status;
//通過該節點,把spi_message加入到消息隊列上
struct list_head queue;
void *state;
/* list of spi_res reources when the spi message is processed */
struct list_head resources;
};
spi_master,描述一個spi控制器,定義如下:
struct spi_master {
struct device dev;
//系統上的spi_master,會鍊在一個全局連結清單上
struct list_head list;
//總線号
s16 bus_num;
//片選信号的個數
u16 num_chipselect;
......
/* spi_device.mode flags understood by this controller driver */
u16 mode_bits;
/* bitmask of supported bits_per_word for transfers */
u32 bits_per_word_mask;
#define SPI_BPW_MASK(bits) BIT((bits) - 1)
#define SPI_BIT_MASK(bits) (((bits) == 32) ? ~0U : (BIT(bits) - 1))
#define SPI_BPW_RANGE_MASK(min, max) (SPI_BIT_MASK(max) - SPI_BIT_MASK(min - 1))
//支援的最小時鐘頻率
u32 min_speed_hz;
//支援的最大時鐘頻率
u32 max_speed_hz;
/* 用于設定某些限制條件的标志位 */
u16 flags;
#define SPI_MASTER_HALF_DUPLEX BIT(0) /* can't do full duplex */
#define SPI_MASTER_NO_RX BIT(1) /* can't do buffer read */
#define SPI_MASTER_NO_TX BIT(2) /* can't do buffer write */
#define SPI_MASTER_MUST_RX BIT(3) /* requires rx */
#define SPI_MASTER_MUST_TX BIT(4) /* requires tx */
......
//用于設定模式、工作時鐘等
int (*setup)(struct spi_device *spi);
/* 如果想采用消息隊列機制,注冊spi_matser時該成員要設定為NULL
之後會設定為spi_queued_transfer,該函數一般把spi_message加入到消息隊列上
*/
int (*transfer)(struct spi_device *spi,
struct spi_message *mesg);
//spi_master被釋放時,該函數會調用
void (*cleanup)(struct spi_device *spi);
//如果spi_master支援DMA,則設定該成員
bool (*can_dma)(struct spi_master *master,
struct spi_device *spi,
struct spi_transfer *xfer);
//如果spi_master提供消息隊列機制則設定該成員
bool queued;
//消息隊列機制相關
struct kthread_worker kworker;
struct task_struct *kworker_task;
struct kthread_work pump_messages;
spinlock_t queue_lock;
//支援消息隊列機制的話,所有等待傳輸的消息會挂在該連結清單上
struct list_head queue;
//目前處理的消息
struct spi_message *cur_msg;
......
//單個spi_message傳輸函數
int (*transfer_one_message)(struct spi_master *master,
struct spi_message *mesg);
/* 這兩個回調函數用于在發起一個資料傳送過程前和後,給控制器驅動一個
機會,申請或釋放某些必要的硬體資源,例如DMA資源和記憶體資源等等
*/
int (*prepare_transfer_hardware)(struct spi_master *master);
int (*unprepare_transfer_hardware)(struct spi_master *master);
/* 這兩個回調函數也是用于在發起一個資料傳送過程前和後,給控制器驅動
一個機會,對message進行必要的預處理或後處理,如設定控制器的正确工作時鐘、字長和工作模式等 */
int (*prepare_message)(struct spi_master *master,
struct spi_message *message);
int (*unprepare_message)(struct spi_master *master,
struct spi_message *message);
......
//片選函數
void (*set_cs)(struct spi_device *spi, bool enable);
//用于傳輸單個spi_transfer
int (*transfer_one)(struct spi_master *master, struct spi_device *spi,
struct spi_transfer *transfer);
void (*handle_err)(struct spi_master *master,
struct spi_message *message);
/* 片選信号所用到的gpio引腳 */
int *cs_gpios;
/* statistics */
struct spi_statistics statistics;
......
};