SPI體系結構
主要由三部分組成:
(1) SPI核心
(2) SPI控制器驅動
(3) SPI設備驅動
基本和I2C的架構差不多
重要結構體
內核版本:3.7.6
-
spi_master
//SPI控制器
struct spi_master {
struct device dev;
struct list_head list; //控制器鏈表
//控制器對應的SPI總線號 SPI-2 對應bus_num= 2
s16 bus_num;
u16 num_chipselect;//控制器支持的片選數量,即能支持多少個spi設備
u16 dma_alignment;//DMA緩衝區對齊方式
u16 mode_bits;// mode標誌
/* other constraints relevant to this driver */
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 */
// 併發同步時使用
spinlock_t bus_lock_spinlock;
struct mutex bus_lock_mutex;
/* flag indicating that the SPI bus is locked for exclusive use */
bool bus_lock_flag;
//設置SPI mode和時鐘, 在spi_add_device中調用
int (*setup)(struct spi_device *spi);
//傳輸數據函數, 實現數據的雙向傳輸
int (*transfer)(struct spi_device *spi,
struct spi_message *mesg);
//註銷時回調
void (*cleanup)(struct spi_device *spi);
/*
* These hooks are for drivers that want to use the generic
* master transfer queueing mechanism. If these are used, the
* transfer() function above must NOT be specified by the driver.
* Over time we expect SPI drivers to be phased over to this API.
*/
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;
bool busy;
bool running;
bool rt;
int (*prepare_transfer_hardware)(struct spi_master *master);
int (*transfer_one_message)(struct spi_master *master,
struct spi_message *mesg);
int (*unprepare_transfer_hardware)(struct spi_master *master);
}
-
spi_driver
//SPI驅動,和platform_driver,i2c_driver類似
struct spi_driver {
const struct spi_device_id *id_table;
int (*probe)(struct spi_device *spi);
int (*remove)(struct spi_device *spi);
void (*shutdown)(struct spi_device *spi);
int (*suspend)(struct spi_device *spi, pm_message_t mesg);
int (*resume)(struct spi_device *spi);
struct device_driver driver;
};
-
spi_device
//SPI 設備
struct spi_device {
struct device dev;
struct spi_master *master; //指向SPI控制器
u32 max_speed_hz; //最大速率
u8 chip_select; //片選
u8 mode; //SPI設備模式,使用下面的宏
#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 */
u8 bits_per_word;
int irq;
void *controller_state; //控制器運行狀態
void *controller_data; //特定板子爲控制器定義的數據
char modalias[SPI_NAME_SIZE];
};
-
spi_message
//SPI傳輸數據結構體
struct spi_message {
struct list_head transfers; // spi_transfer鏈表頭
struct spi_device *spi; //spi設備
unsigned is_dma_mapped:1;
//發送完成回調
void (*complete)(void *context);
void *context;
unsigned actual_length;
int status;
/* for optional use by whatever driver currently owns the
* spi_message ... between calls to spi_async and then later
* complete(), that's the spi_master controller driver.
*/
struct list_head queue;
void *state;
};
-
spi_transfer
// 該結構體是spi_message下的子單元,
struct spi_transfer {
const void *tx_buf;// 發送的數據緩存區
void *rx_buf;// 接收的數據緩存區
unsigned len;
dma_addr_t tx_dma; //tx_buf的DMA地址
dma_addr_t rx_dma; //rx_buf的DMA地址
unsigned cs_change:1;
u8 bits_per_word;
u16 delay_usecs;
u32 speed_hz;
struct list_head transfer_list;
};
總結上面結構體關係:
1. spi_driver和spi_device
spi_driver對應一套驅動方法,包含probe,remove等方法。spi_device對應真實的物理設備,每個spi設備都需要一個spi_device來描述。spi_driver與spi_device是一對多的關係,一個spi_driver上可以支持多個同類型的spi_device。
2. spi_master和spi_device
spi_master 與 spi_device 的關係和硬件上控制器與設備的關係一致,即spi_device依附於spi_master。
3. spi_message和spi_transfer
spi傳輸數據是以 spi_message 爲單位的,我們需要傳輸的內容在 spi_transfer 中。spi_transfer是spi_message的子單元。
1 . 將本次需要傳輸的 spi_transfer 以 spi_transfer->transfer_list 爲鏈表項,連接成一個transfer_list鏈表,掛接在本次傳輸的spi_message spi_message->transfers鏈表下。
2 . 將所有等待傳輸的 spi_message 以 spi_message->queue 爲鏈表項,連接成個鏈表掛接在queue下。
API函數
//分配一個spi_master
struct spi_master *spi_alloc_master(struct device *dev, unsigned size)
//註冊和註銷spi_master
int spi_register_master(struct spi_master *master)
void spi_unregister_master(struct spi_master *master)
//註冊和註銷spi_driver
int spi_register_driver(struct spi_driver *sdrv)
void spi_unregister_driver(struct spi_driver *sdrv)
//初始化spi_message
void spi_message_init(struct spi_message *m)
//向spi_message添加transfers
void spi_message_add_tail(struct spi_transfer *t, struct spi_message *m)
//異步發送spi_message
int spi_async(struct spi_device *spi, struct spi_message *message)
//同步發送spi_message
int spi_sync(struct spi_device *spi, struct spi_message *message)
//spi同步寫(封裝了上面的函數)
int spi_write(struct spi_device *spi, const void *buf, size_t len)
//spi同步讀(封裝了上面的函數)
int spi_read(struct spi_device *spi, void *buf, size_t len)
//同步寫並讀取(封裝了上面的函數)
int spi_write_then_read(struct spi_device *spi,
const void *txbuf, unsigned n_tx,
void *rxbuf, unsigned n_rx)
使用spi_async()需要注意的是,在complete未返回前不要輕易訪問你一提交的spi_transfer中的buffer。也不能釋放SPI系統正在使用的buffer。一旦你的complete返回了,這些buffer就又是你的了。
spi_sync是同步的,spi_sync提交完spi_message後不會立即返回,會一直等待其被處理。一旦返回就可以重新使用buffer了。spi_sync()調用了spi_async(),並休眠直至complete返回。
上面的傳輸函數最終都是調用spi_master的transfer()函數。
總結
SPI的架構和之前的I2C的結構基本差不多,我們會發現其實驅動中大量的結構體都是對參數和數據的封裝。站在宏觀的角度看,就是填充結構體,調用函數註冊或發送。
上面是對Linux中SPI相關架構的分析,後面依然會拿出一些相對應的驅動來進行具體分析。希望能做到理論和實踐相結合!