1. 程式人生 > >Linux驅動修煉之道-SPI驅動框架原始碼分析(上)

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主機控制器的驅動。

  1. <span style="font-size:18px;">struct spi_master {  
  2. struct device    dev;/*匯流排編號,從0開始*/
  3. s16    bus_num;/*支援的片選的數量,從裝置的片選號不能大於這個數量*/
  4. u16 num_chipselect;  
  5. u16  dma_alignment;/*改變spi_device的特性如:傳輸模式,字長,時鐘頻率*/
  6. int  (*setup)(struct spi_device *spi);/*新增訊息到佇列的方法,這個函式不可睡眠,他的任務是安排發生的傳送並且呼叫註冊的回撥函式complete()*/
  7. int (*transfer)(struct spi_device *spi,struct spi_message *mesg);  
  8. void   (*cleanup)(struct spi_device *spi);  
  9. };</span>  

分配,註冊和登出的SPI主機的API由SPI核心提供:

  1. struct spi_master *spi_alloc_master(struct device *host, unsigned size);  
  2. int spi_register_master(struct spi_master *master);  
  3. void spi_unregister_master(struct spi_master *master);    
在Linux中用spi_driver來描述一個SPI外設驅動。
  1. struct spi_driver {  
  2. int   (*probe)(struct spi_device *spi);  
  3. int   (*remove)(struct spi_device *spi);  
  4. void  (*shutdown)(struct spi_device *spi);  
  5. int   (*suspend)(struct spi_device *spi, pm_message_t mesg);  
  6. int   (*resume)(struct spi_device *spi);  
  7. struct device_driver  driver;  
  8. };   

可以看出,spi_driver結構體和platform_driver結構體有極大的相似性,都有probe(),remove(),suspend(),resume()這樣的介面。

Linux用spi_device來描述一個SPI外設裝置。

  1. struct spi_device {  
  2. struct device        dev;  
  3. struct spi_master   *master;       //對應的控制器指標u32    
  4. max_speed_hz;  //spi通訊的時鐘u8       
  5. chip_select;   //片選,用於區分同一總線上的不同裝置
  6. u8  mode;  
  7. #define    SPI_CPHA    0x01            /* clock phase */
  8. #define    SPI_CPOL    0x02            /* clock polarity */
  9. #define SPI_MODE_0  (0|0)           /* (original MicroWire) */#define   SPI_MODE_1  (0|SPI_CPHA)
  10. #define SPI_MODE_2  (SPI_CPOL|0)
  11. #define SPI_MODE_3  (SPI_CPOL|SPI_CPHA)#define  SPI_CS_HIGH 0x04            /* chipselect active high? */
  12. #define    SPI_LSB_FIRST   0x08            /* per-word bits-on-wire */
  13. #define  SPI_3WIRE   0x10            /* SI/SO signals shared */
  14. #define   SPI_LOOP    0x20            /* loopback mode */
  15. u8      bits_per_word;    //每個字長的位元數
  16. int      irq;              //使用的中斷
  17. void     *controller_state;  
  18. void     *controller_data;  
  19. char     modalias[32];    //名字
  20. };    
如下圖,看這三個結構的關係,這裡spi_device與spi_master是同一個父裝置,這是在spi_new_device函式中設定的,一般這個裝置是一個物理裝置。

這裡的spi_master_class,spi_bus_type又是什麼呢,看下邊兩個結構體:

  1. struct bus_type spi_bus_type = {     
  2.    .name       = "spi",  
  3.    .dev_attrs  = spi_dev_attrs,  
  4.    .match    = spi_match_device,  
  5.    .uevent   = spi_uevent,   
  6.    .suspend  = spi_suspend,  
  7.    .resume   = spi_resume,  
  8. };   
  9. staticstructclass spi_master_class = {     
  10.     .name             = "spi_master",   
  11.     .owner           = THIS_MODULE,  
  12.     .dev_release    = spi_master_release,  
  13. };    
spi_bus_type對應spi中的spi bus匯流排,spidev的類定義如下:
  1. staticstructclass *spidev_class;   
建立這個類的主要目的是使mdev/udev能在/dev下建立裝置節點/dev/spiB.C。B代表匯流排,C代表片外裝置的片選號。

下邊來看兩個板級的結構,其中spi_board_info用來初始化spi_device,s3c2410_spi_info用來初始化spi_master。這兩個板級的結構需要在移植的時候在arch/arm/mach-s3c2440/mach-smdk2440.c中初始化。

  1. struct spi_board_info {  
  2. char     modalias[32];   //裝置與驅動匹配的唯一標識
  3. constvoid    *platform_data;  
  4. void     *controller_data;  
  5. int        irq;  
  6. u32     max_speed_hz;  
  7. u16        bus_num;       //裝置所歸屬的匯流排編號
  8. u16      chip_select;  
  9. u8      mode;  
  10. };  
  11. struct s3c2410_spi_info {  
  12. int     pin_cs;         //晶片選擇管腳
  13. unsigned int    num_cs;         //總線上的裝置數
  14. int        bus_num;        //匯流排號
  15. void (*gpio_setup)(struct s3c2410_spi_info *spi, int enable);     //spi管腳配置函式
  16. void (*set_cs)(struct s3c2410_spi_info *spi, int cs, int pol);  
  17. };    
boardinfo是用來管理spi_board_info的結構,spi_board_info通過spi_register_board_info(struct spi_board_info const *info, unsigned n)交由boardinfo來管理,並掛到board_list連結串列上,list_add_tail(&bi->list,&board_list);
  1. struct boardinfo {   
  2.  /*用於掛到連結串列頭board_list上*/
  3. struct list_head  list;  
  4. /*管理的spi_board_info的數量*/
  5. unsigned  n_board_info;  
  6. /*存放結構體spi_board_info*/
  7. struct spi_board_info    board_info[0];  
  8. };   
s3c24xx_spi是S3C2440的SPI控制器在Linux核心中的具體描述,該結構包含spi_bitbang內嵌結構,控制器時鐘頻率和佔用的中斷資源等重要成員,其中spi_bitbang具體負責SPI資料的傳輸。
  1. struct s3c24xx_spi {  
  2. /* bitbang has to be first */
  3. struct spi_bitbang  bitbang;  
  4. struct completion   done;  
  5. void __iomem      *regs;  
  6. int            irq;  
  7. int             len;  
  8. int             count;  
  9. void         (*set_cs)(struct s3c2410_spi_info *spi,  int cs, int pol);  
  10. /* data buffers */const unsigned char *tx;  
  11. unsigned char       *rx;  
  12. struct clk      *clk;  
  13. struct resource        *ioarea;  
  14. struct spi_master   *master;  
  15. struct spi_device   *curdev;  
  16. struct device       *dev;  
  17. struct s3c2410_spi_info *pdata;  
  18. };  
為了解決多個不同的SPI裝置共享SPI控制器而帶來的訪問衝突,spi_bitbang使用核心提供的工作佇列(workqueue)。workqueue是Linux核心中定義的一種回撥處理方式。採用這種方式需要傳輸資料時,不直接完成資料的傳輸,而是將要傳輸的工作分裝成相應的訊息(spi_message),傳送給對應的workqueue,由與workqueue關聯的核心守護執行緒(daemon)負責具體的執行。由於workqueue會將收到的訊息按時間先後順序排列,這樣就是對裝置的訪問嚴格序列化,解決了衝突。
  1. <span style="font-size:18px;">struct spi_bitbang {  
  2. struct workqueue_struct *workqueue;      //工作佇列頭
  3. struct work_struct  work;            //每一次傳輸都傳遞下來一個spi_message,都向工作佇列頭新增一個
  4. workspinlock_t        lock;  
  5. struct list_head   queue;           //掛接spi_message,如果上一次的spi_message還沒有處理完,接下來的spi_message就掛接在queue上等待處理
  6. u8            busy;            //忙碌標誌
  7. u8           use_dma;  
  8. u8          flags;  
  9. struct spi_master *master;/*一下3個函式都是在函式s3c24xx_spi_probe()中被初始化*/
  10. int  (*setup_transfer)(struct spi_device *spi,struct spi_transfer *t);   //設定傳輸模式
  11. void    (*chipselect)(struct spi_device *spi, int is_on);                    //片選
  12. #define    BITBANG_CS_ACTIVE   1   /* normally nCS, active low */
  13. #define   BITBANG_CS_INACTIVE 0/*傳輸函式,由s3c24xx_spi_txrx來實現*/
  14. int   (*txrx_bufs)(struct spi_device *spi, struct spi_transfer *t);  
  15. u32    (*txrx_word[4])(struct spi_device *spi,unsigned nsecs,u32 word, u8 bits);  
  16. };</span>  
下面來看看spi_message:
  1. struct spi_message {  
  2. struct list_head    transfers;   //此次訊息的傳輸佇列,一個訊息可以包含多個傳輸段
  3. struct spi_device *spi;