驅動_spi驅動框架


spi驅動


 Spi總線上可以掛MMC、SD,flash、wifi網卡等常見設備。

 

 

 

 

 

 

 

 

 

 

 

 

應用


(從設備驅動層)

spi_driver層

 (自己編寫)


 

 (核心層)

           

spi_core層                                 spi_bus_type

        

 (spi_core.c)


  (控制器層)

spi_master層                                      

 (spi_xxx.c)


 硬件

 

目錄:linux/driver/spi/ 

 

<從設備驅動層>  spidev.c                           自己編寫

<核      心     層>  spi.c                              內核提供

<控   制   器  層>    spi-xxx.c(瑞芯微:spi-rockchip.c  全志:spi-sunxi.c)      原廠提供

 

 Makefile:

 

 

 

 

 

 

<結構體>


 

 

struct spi_transfer是對一次完整的數據傳輸的描述.每個spi_transfer總是讀取和寫入同樣長度的比特數,但是可以很容易的使用空指針舍棄讀或寫.為spi_transfer和spi_message分配的內存應該在消息處理期間保證是完整的.

 

struct spi_transfer {

  const void *tx_buf;

 

  /*發送緩沖區地址,這里存放要寫入設備的數據(必須是dma_safe),或者為NULL*/

 

  void *rx_buf;

 

  /*接收緩沖區地址,從設備中讀取的數據(必須是dma_safe)就放在這里,或者為NULL*/

 

  unsigned len;

 

  /*傳輸數據的長度.記錄了tx和rx的大小(字節數),這里不是指它的和,而是各自的長度,他們總是相等的*/

 

  dma_addr_t tx_dma;

 

  /*如果spi_message.is_dma_mapped是真,這個是tx的dma地址*/

 

  dma_addr_t rx_dma;

 

  /*如果spi_message.is_dma_mapped是真,這個是rx的dma地址*/

 

  unsigned cs_change:1;

 

  /*影響此次傳輸之后的片選.指示本次transfer結束之后是否要重新片選並調用setup改變設置.若為1則表示當該transfer
    傳輸完后,改變片選信號.這個標志可以減少系統開銷*/

 

  u8 bits_per_word;

 

  /*每個字長的比特數.如果是0,使用默認值*/

 

  u16 delay_usecs;

 

  /*此次傳輸結束和片選改變之間的延時,之后就會啟動另一個傳輸或者結束整個消息*/

 

  u32 speed_hz;

 

  /*通信時鍾.如果是0,使用默認值*/

 

  struct list_head transfer_list;

 

  /*用來連接的雙向鏈表節點,用於將該transfer鏈入message*/
};

 

 

 

struct spi_message:就是對多個spi_transfer的封裝.spi_message用來原子的執行spi_transfer表示的一串數組傳輸請求.這個傳輸隊列是原子的,這意味着在這個消息完成之前不會有其它消息占用總線.消息的執行總是按照FIFO的順序.向底層提交spi_message的代碼要負責管理它的內存空間.未顯示初始化的內存需要使用0來初始化.為spi_transfer和spi_message分配的內存應該在消息處理期間保證是完整的。

struct spi_message {

  struct list_head transfers; 

 /*此次消息的傳輸段(spi_transfer)隊列,一個消息可以包含多個傳輸段*/

  struct spi_device *spi;

 /*傳輸的目的設備,無論如何這里都是spi從設備,至於數據流向(是從主機到從設備還是從從設備到主機)這是由write/read
  每個傳輸段(spi_transfer)內部的tx_buf或者是rx_buf決定的*/

  unsigned is_dma_mapped:1;

 /*如果為真,此次調用提供dma和cpu虛擬地址.spi主機提供了dma緩存池.如果此消息確定要使用dma(那當然更好
  了).則從那個緩存池中申請高速緩存.替代傳輸段(spi_transfer)中的tx_buf/rx_buf*/

  void (*complete)(void*context);

 /*用於異步傳輸完成時調用的回調函數*/

  void *context; 

 /*回調函數的參數*/

  unsigned actual_length;

 /*此次傳輸的實際長度,這個長度包括了此消息spi_message中所有傳輸段spi_transfer傳輸的長度之和(不管每個傳
  輸段spi_transfer到底是輸入還是輸出,因為本來具體的傳輸就是針對每一個傳輸段spi_transfer來進行的)*/

  int status;

 /*執行的結果.成功被置0,否則是一個負的錯誤碼*/

  struct list_head queue;

 /*用於將該message鏈入bitbang等待隊列*/

  void *state;

};

 

struct spi_driver:用於描述SPI(從)設備驅動.驅動核心將根據driver.name和spi_board_info的modalias進行匹配,如過modalia和name相等,則綁定驅動程序和arch/.../mach-xxx/board-xxx.c中調用spi_register_board_info注冊的信息對應的spi_device設備.它的形式和struct platform_driver是一致的. 

struct spi_driver {
  const struct spi_device_id *id_table;

  int (*probe)(struct spi_device *spi);

  /*和spi_device匹配成功之后會調用這個方法.因此這個方法需要對設備和私有數據進行初始化*/

  int (*remove)(struct spi_device *spi);

  /*解除spi_device和spi_driver的綁定,釋放probe申請的資源*/

  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;
};

 

 

 

struct spi_device用來描述一個SPI從設備信息

SPI子系統只支持主模式,也就是說SOC上的SPI只能工作在master模式,外圍設備只能為slave模式
struct spi_device {
  struct device dev;
  struct spi_master *master;  //對應的控制器指針
  u32 max_speed_hz;      //spi傳輸時鍾
  u8 chip_select;    //片選號,用來區分同一主控制器上的設備
  u8 mode;  //各bit的定義如下,主要是傳輸模式/片選極性
  #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設備的名字*/
  int cs_gpio;    /* chip select gpio */

};

 

 

 

 

struct spi_board_info是板級信息,是在移植時就寫好的,並且要將其注冊.

該結構也是對SPI(從)設備(spi_device)的描述,只不過它是板級信息,最終該結構的所有字段都將用於初始化SPI設備結構體spi_device

struct spi_board_info {

  char modalias[SPI_NAME_SIZE];

  /*spi設備名,會拷貝到spi_device的相應字段中.這是設備spi_device在SPI總線spi_bus_type上匹配驅動的唯一標識*/

  const void *platform_data;  /*平台數據*/

  void *controller_data;

  int irq;  /*中斷號*/

  u32 max_speed_hz;/*SPI設備工作時的波特率*/

  u16 bus_num;

  /*該SPI(從)設備所在總線的總線號,就記錄了所屬的spi_master之中的bus_num編號.一個spi_master就對應一條總線*/

  u16 chip_select;/*片選號.該SPI(從)設備在該條SPI總線上的設備號的唯一標識*/

  u8 mode;/*參考spi_device中的成員*/

};

 

 

struct spi_bitbang結構用於控制實際的數據傳輸.

struct spi_bitbang {
  struct workqueue_struct *workqueue;   /*工作隊列*/

  struct work_struct work;

  spinlock_t lock;

  struct list_head queue;

  u8 busy;

  u8 use_dma;

  u8 flags;    /* extra spi->mode support */

  struct spi_master *master;  /*bitbang所屬的master*/

  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

  int (*txrx_bufs)(struct spi_device *spi, struct spi_transfer *t);

  /* txrx_word[SPI_MODE_*]() just looks like a shift register */

  u32 (*txrx_word[4])(struct spi_device *spi,unsigned nsecs,u32 word, u8 bits);

};

 

 

struct spi_master用來描述一個SPI主控制器,我們一般不需要自己編寫spi控制器驅動. 

結構體master代表一個SPI接口,或者叫一個SPI主機控制器,一個接口對應一條SPI總線,master->bus_num則記錄了這個總線號
struct spi_master {
  struct device dev;

  struct list_head list;

  s16 bus_num;

  /*總線編號,從零開始.系統會用這個值去和系統中board_list鏈表中加入的每一個boardinfo結構
  (每個boardinfo結構都是一個spi_board_info的集合,每一個spi_board_info都是對應一個SPI(從)設備的描述)中的每一個
    spi_board_info中的bus_num進行匹配,如果匹配上就說明這個spi_board_info描述的SPI(從)設備是鏈接在此總線上           的,因此就會調用spi_new_device去創建一個spi_device*/

  u16 num_chipselect;

  //支持的片選的數量.從設備的片選號不能大於這個數.該值當然不能為0,否則會注冊失敗

  u16 dma_alignment;

  u16 mode_bits;

  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;

  bool bus_lock_flag;

  int (*setup)(struct spi_device *spi);

  //根據spi設備更新硬件配置

  int (*transfer)(struct spi_device *spi,struct spi_message *mesg);

  /*添加消息到隊列的方法.此函數不可睡眠,其作用只是安排需要的傳送,並且在適當的時候(傳\
     送完成或者失敗)調用spi_message中的complete方法,來將結果報告給用戶*/

  void (*cleanup)(struct spi_device *spi);

  /*cleanup函數會在spidev_release函數中被調用,spidev_release被登記為spi dev的release
     函數*/

  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);

  int *cs_gpios;
};

解釋:spi控制器的驅動一般在arch/.../mach-*/board-*.c聲明,注冊一個平台設備,然后在driver/spi下面建立一個平台驅動.spi_master注冊過程中會掃描arch/.../mach-*/board-*.c 中調用spi_register_board_info注冊的信息,為每一個與本總線編號相同的信息建立一個spi_device.根據Linux內核的驅動模型,注冊在同一總線下的驅動和設備會進行匹配.spi_bus_type總線匹配的依據是名字.這樣當自己編寫的spi_driver和spi_device同名的時候,spi_driver的probe方法就會被調用.spi_driver就能看到與自己匹配的spi_device了. 

 

 

 

數據傳輸接口

 

 

spidev_sync_write

spidev_sync_read  

   ↓

spi_transfer       →    spi_massage →    spi_sync

 

 

 

 

 

 

 

 

 

 

<編寫框架>


 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

<筆記>


 

1. 從設備驅動層,也叫協議驅動層

2. 

 

 

 

博客:

http://blog.chinaunix.net/uid-21501855-id-5211900.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM