Linux驅動修煉之道-SPI驅動框架原始碼分析(上)
SPI驅動架構,以前用過,不過沒這個詳細,跟各位一起分享:
來自:http://blog.csdn.net/woshixingaaa/article/details/6574215
SPI協議是一種同步的序列資料連線標準,由摩托羅拉公司命名,可工作於全雙工模式。相關通訊裝置可工作於m/s模式。主裝置發起資料幀,允許多個從裝置的存在。每個從裝置
有獨立的片選訊號,SPI一般來說是四線序列匯流排結構。
介面:
SCLK——Serial Clock(output from master)時鐘(主裝置發出)
MOSI/SIMO——Master Output, Slave Input(output from master)資料訊號線mosi(主裝置發出)
MISO/SOMI——Master Input,Slave Outpu(output from slave)資料訊號線(從裝置)
SS——Slave Select(active low;output from master)片選訊號
下面來看一下Linux中的SPI驅動。在Linux裝置驅動框架的設計中,有一個重要的主機,外設驅動框架分離的思想,如下圖。
外設a,b,c的驅動與主機控制器A,B,C的驅動不相關,主機控制器驅動不關心外設,而外設驅動也不關心主機,外設只是訪問核心層的通用的API進行資料的傳輸,主機和外設之間可以進行任意的組合。如果我們不進行如圖的主機和外設分離,外設a,b,c和主機A,B,C進行組合的時候,需要9種不同的驅動。設想一共有個主機控制器,n個外設,分離的結構是需要m+n個驅動,不分離則需要m*n個驅動。
下面介紹spi子系統的資料結構:
在Linux中,使用spi_master結構來描述一個SPI主機控制器的驅動。
- <span style="font-size:18px;">struct spi_master {
- struct device dev;/*匯流排編號,從0開始*/
- s16 bus_num;/*支援的片選的數量,從裝置的片選號不能大於這個數量*/
- u16 num_chipselect;
- u16 dma_alignment;/*改變spi_device的特性如:傳輸模式,字長,時鐘頻率*/
-
int (*setup)(struct spi_device *spi);/*新增訊息到佇列的方法,這個函式不可睡眠,他的任務是安排發生的傳送並且呼叫註冊的回撥函式complete()*/
- int (*transfer)(struct spi_device *spi,struct spi_message *mesg);
- void (*cleanup)(struct spi_device *spi);
- };</span>
分配,註冊和登出的SPI主機的API由SPI核心提供:
- struct spi_master *spi_alloc_master(struct device *host, unsigned size);
- int spi_register_master(struct spi_master *master);
- void spi_unregister_master(struct spi_master *master);
- struct spi_driver {
- 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_driver結構體和platform_driver結構體有極大的相似性,都有probe(),remove(),suspend(),resume()這樣的介面。
Linux用spi_device來描述一個SPI外設裝置。
- struct spi_device {
- struct device dev;
- struct spi_master *master; //對應的控制器指標u32
- max_speed_hz; //spi通訊的時鐘u8
- chip_select; //片選,用於區分同一總線上的不同裝置
- u8 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 */
- u8 bits_per_word; //每個字長的位元數
- int irq; //使用的中斷
- void *controller_state;
- void *controller_data;
- char modalias[32]; //名字
- };
這裡的spi_master_class,spi_bus_type又是什麼呢,看下邊兩個結構體:
- struct bus_type spi_bus_type = {
- .name = "spi",
- .dev_attrs = spi_dev_attrs,
- .match = spi_match_device,
- .uevent = spi_uevent,
- .suspend = spi_suspend,
- .resume = spi_resume,
- };
- staticstructclass spi_master_class = {
- .name = "spi_master",
- .owner = THIS_MODULE,
- .dev_release = spi_master_release,
- };
- staticstructclass *spidev_class;
下邊來看兩個板級的結構,其中spi_board_info用來初始化spi_device,s3c2410_spi_info用來初始化spi_master。這兩個板級的結構需要在移植的時候在arch/arm/mach-s3c2440/mach-smdk2440.c中初始化。
- struct spi_board_info {
- char modalias[32]; //裝置與驅動匹配的唯一標識
- constvoid *platform_data;
- void *controller_data;
- int irq;
- u32 max_speed_hz;
- u16 bus_num; //裝置所歸屬的匯流排編號
- u16 chip_select;
- u8 mode;
- };
- struct s3c2410_spi_info {
- int pin_cs; //晶片選擇管腳
- unsigned int num_cs; //總線上的裝置數
- int bus_num; //匯流排號
- void (*gpio_setup)(struct s3c2410_spi_info *spi, int enable); //spi管腳配置函式
- void (*set_cs)(struct s3c2410_spi_info *spi, int cs, int pol);
- };
- struct boardinfo {
- /*用於掛到連結串列頭board_list上*/
- struct list_head list;
- /*管理的spi_board_info的數量*/
- unsigned n_board_info;
- /*存放結構體spi_board_info*/
- struct spi_board_info board_info[0];
- };
- struct s3c24xx_spi {
- /* bitbang has to be first */
- struct spi_bitbang bitbang;
- struct completion done;
- void __iomem *regs;
- int irq;
- int len;
- int count;
- void (*set_cs)(struct s3c2410_spi_info *spi, int cs, int pol);
- /* data buffers */const unsigned char *tx;
- unsigned char *rx;
- struct clk *clk;
- struct resource *ioarea;
- struct spi_master *master;
- struct spi_device *curdev;
- struct device *dev;
- struct s3c2410_spi_info *pdata;
- };
- <span style="font-size:18px;">struct spi_bitbang {
- struct workqueue_struct *workqueue; //工作佇列頭
- struct work_struct work; //每一次傳輸都傳遞下來一個spi_message,都向工作佇列頭新增一個
- workspinlock_t lock;
- struct list_head queue; //掛接spi_message,如果上一次的spi_message還沒有處理完,接下來的spi_message就掛接在queue上等待處理
- u8 busy; //忙碌標誌
- u8 use_dma;
- u8 flags;
- struct spi_master *master;/*一下3個函式都是在函式s3c24xx_spi_probe()中被初始化*/
- int (*setup_transfer)(struct spi_device *spi,struct spi_transfer *t); //設定傳輸模式
- void (*chipselect)(struct spi_device *spi, int is_on); //片選
- #define BITBANG_CS_ACTIVE 1 /* normally nCS, active low */
- #define BITBANG_CS_INACTIVE 0/*傳輸函式,由s3c24xx_spi_txrx來實現*/
- int (*txrx_bufs)(struct spi_device *spi, struct spi_transfer *t);
- u32 (*txrx_word[4])(struct spi_device *spi,unsigned nsecs,u32 word, u8 bits);
- };</span>
- struct spi_message {
- struct list_head transfers; //此次訊息的傳輸佇列,一個訊息可以包含多個傳輸段
- struct spi_device *spi;