1. 程式人生 > >Qualcomm平臺的SPI驅動框架分析

Qualcomm平臺的SPI驅動框架分析

  1. 一、SPI匯流排概述
  2. SPI是英語Serial Peripheral interface的縮寫,顧名思義就是序列外圍裝置介面,是Motorola首先在其MC68HCXX系列處理器上定義的。SPI介面主要應用在 EEPROM,FLASH,實時時鐘,AD轉換器,還有數字訊號處理器和數字訊號解碼器之間。SPI是一種高速的,全雙工,同步的通訊匯流排,並且在晶片的管腳上只佔用四根線,節約了晶片的管腳,同時為PCB的佈局上節省空間,提供方便。
  3. SPI的通訊原理很簡單,它以主從方式工作,這種模式通常有一個主裝置和一個或多個從裝置,需要4根線,事實上3根也可以。也是所有基於SPI的裝置共有的,它們是SDI(
    資料輸入),SDO(資料輸出),SCLK(時鐘),CS(片選)
  4. MOSI(SDO):主器件資料輸出,從器件資料輸入。
  5. MISO(SDI):主器件資料輸入,從器件資料輸出。
  6. SCLK :時鐘訊號,由主器件產生。
  7. CS:從器件使能訊號,由主器件控制。
  8. 其中CS是控制晶片是否被選中的,也就是說只有片選訊號為預先規定的使能訊號時(高電位或低電位),對此晶片的操作才有效,這就允許在同一總線上連線多個SPI裝置成為可能。需要注意的是,在具體的應用中,當一條SPI總線上連線有多個裝置時,SPI本身的CS有可能被其他的GPIO腳代替,即每個裝置的CS腳被連線到處理器端不同的GPIO,通過操作不同的GPIO口來控制具體的需要操作的SPI裝置,減少各個SPI裝置間的干擾。
  9. SPI是序列通訊協議,也就是說資料是一位一位從MSB或者LSB開始傳輸的,這就是SCK時鐘線存在的原因,由SCK提供時鐘脈衝,MISO、MOSI則基於此脈衝完成資料傳輸。 SPI支援4-32bits的序列資料傳輸,支援MSB和LSB,每次資料傳輸時當從裝置的大小端發生變化時需要重新設定SPI Master的大小端。
  10. 二、Linux SPI驅動總體架構
  11. 在2.6的linux核心中,SPI的驅動架構可以分為如下三個層次:SPI 核心層、SPI控制器驅動層和SPI裝置驅動層。Linux 中SPI驅動程式碼位於drivers/spi目錄。
  12. 1.SPI核心層
  13. SPI核心層是Linux的SPI核心部分,提供了核心資料結構的定義、SPI控制器驅動和裝置驅動的註冊、登出管理等API。其為硬體平臺無關層,向下遮蔽了物理匯流排控制器的差異,定義了統一的訪問策略和介面;其向上提供了統一的介面,以便SPI裝置驅動通過匯流排控制器進行資料收發。
  14. Linux中,SPI核心層的程式碼位於driver/spi/spi.c。
  15. 2.SPI控制器驅動層
  16. SPI控制器驅動層,每種處理器平臺都有自己的控制器驅動,屬於平臺移植相關層。它的職責是為系統中每條SPI匯流排實現相應的讀寫方法。在物理上,每個SPI控制器可以連線若干個SPI從裝置。
  17. 在系統開機時,SPI控制器驅動被首先裝載。一個控制器驅動用於支援一條特定的SPI匯流排的讀寫。一個控制器驅動可以用資料結構struct spi_master來描述。
  18. 3.SPI裝置驅動層
  19. SPI裝置驅動層為使用者介面層,其為使用者提供了通過SPI匯流排訪問具體裝置的介面。
  20. SPI裝置驅動層可以用兩個模組來描述,struct spi_driver和struct spi_device。
  21. Driver是為device服務的,spi_driver註冊時會掃描SPI bus上的裝置,進行驅動和裝置的繫結,probe函式用於驅動和裝置匹配時被呼叫。從上面的結構體註釋中我們可以知道,SPI的通訊是通過訊息佇列機制,而不是像I2C那樣通過與從裝置進行對話的方式。
  22. 三、spi子系統主要資料結構
  23. 1.在Linux中,使用spi_master結構來描述一個SPI主機控制器的驅動。
  24. struct spi_master { 
  25.     struct device dev; 
  26.     s16 bus_num; //為該控制器對應的SPI匯流排號
  27.     u16 num_chipselect; //控制器支援的片選數量,即能支援多少個spi裝置,從裝置的片選號不能大於這個數量
  28.     u16 dma_alignment; 
  29.     int (*setup)(struct spi_device *spi);//函式是設定SPI匯流排的模式,時鐘等的初始化函式, 針對裝置設定SPI的工作時鐘及資料傳輸模式等。在spi_add_device函式中呼叫
  30.     int (*transfer)(struct spi_device *spi,struct spi_message *mesg);//實現SPI匯流排讀寫方法的函式。實現資料的雙向傳輸,可能會睡眠 
  31.     void (*cleanup)(struct spi_device *spi); //登出時候呼叫
  32. };
  33. //分配,註冊和登出的SPI主機的API由SPI核心提供:
  34. struct spi_master *spi_alloc_master(struct device *host, unsigned size); 
  35. int spi_register_master(struct spi_master *master); 
  36. void spi_unregister_master(struct spi_master *master); 
  37. 2.在Linux中用spi_driver來描述一個SPI外設驅動。
  38. struct spi_driver {
  39.     const struct spi_device_id *id_table;//匹配的裝置表
  40.     int            (*probe)(struct spi_device *spi);
  41.     int            (*remove)(struct spi_device *spi);
  42.     void        (*shutdown)(struct spi_device *spi);
  43.     int            (*suspend)(struct spi_device *spi, pm_message_t mesg);
  44.     int            (*resume)(struct spi_device *spi);
  45.     struct device_driver    driver;
  46. }; 
  47. 3.Linux用spi_device來描述一個SPI外設裝置。
  48. struct spi_device { 
  49.     struct device        dev;
  50.     struct spi_master    *master;//對應的控制器指標
  51.     u32            max_speed_hz;//spi通訊的時鐘
  52.     u8            chip_select; //片選,用於區分同一總線上的不同裝置
  53.     u8            mode;
  54. #define    SPI_CPHA    0x01            /* clock phase */
  55. #define    SPI_CPOL    0x02            /* clock polarity */
  56. #define    SPI_MODE_0    (0|0)            /* (original MicroWire) */
  57. #define    SPI_MODE_1    (0|SPI_CPHA)
  58. #define    SPI_MODE_2    (SPI_CPOL|0)
  59. #define    SPI_MODE_3    (SPI_CPOL|SPI_CPHA)
  60. #define    SPI_CS_HIGH    0x04            /* chipselect active high? */
  61. #define    SPI_LSB_FIRST    0x08            /* per-word bits-on-wire */
  62. #define    SPI_3WIRE    0x10            /* SI/SO signals shared */
  63. #define    SPI_LOOP    0x20            /* loopback mode */
  64. #define    SPI_NO_CS    0x40            /* 1 dev/bus, no chipselect */
  65. #define    SPI_READY    0x80            /* slave pulls low to pause */
  66.     u8            bits_per_word;//每個字長的位元數
  67.     int            irq;//使用的中斷號
  68.     void            *controller_state;
  69.     void            *controller_data;
  70.     char            modalias[SPI_NAME_SIZE];//名字 
  71. }; 
  72. 看這三個結構的關係,這裡spi_device與spi_master是同一個父裝置,這是在spi_new_device函式中設定的,一般這個裝置是一個物理裝置。

  73. 4.為了解決多個不同的SPI裝置共享SPI控制器而帶來的訪問衝突,spi_bitbang使用核心提供的工作佇列(workqueue)。 workqueue是Linux核心中定義的一種回撥處理方式。採用這種方式需要傳輸資料時,不直接完成資料的傳輸,而是將要傳輸的工作分裝成相應的訊息 (spi_message),傳送給對應的workqueue,由與workqueue關聯的核心守護執行緒(daemon)負責具體的執行。由於 workqueue會將收到的訊息按時間先後順序排列,這樣就是對裝置的訪問嚴格序列化,解決了衝突。
  74. struct spi_bitbang { 
  75.     struct workqueue_struct *workqueue;//工作佇列頭 
  76.     struct work_struct work;//每一次傳輸都傳遞下來一個spi_message,都向工作佇列頭新增一個 
  77.     workspinlock_t lock; 
  78.     struct list_head queue;//掛接spi_message,如果上一次的spi_message還沒有處理完,接下來的spi_message就掛接在queue上等待處理 
  79.     u8 busy;//忙碌標誌 
  80.     u8 use_dma; 
  81.     u8 flags; 
  82.     struct spi_master *master;//指向spi控制器
  83.     int (*setup_transfer)(struct spi_device *spi,struct spi_transfer *t);//設定傳輸模式 
  84.     void (*chipselect)(struct spi_device *spi, int is_on);//片選 
  85. #define BITBANG_CS_ACTIVE 1 /* normally nCS, active low */ 
  86. #define BITBANG_CS_INACTIVE 0 
  87.     int (*txrx_bufs)(struct spi_device *spi, struct spi_transfer *t);//傳輸函式
  88.     u32 (*txrx_word[4])(struct spi_device *spi,unsigned nsecs,u32 word, u8 bits); 
  89. }; 
  90. 5.下面來看看spi_message:
  91. struct spi_message { 
  92.     struct list_head transfers; //此次訊息的傳輸佇列,一個訊息可以包含多個傳輸段 
  93.     struct spi_device *spi; //傳輸的目的裝置 
  94.     unsigned is_dma_mapped:1; //如果為真,此次呼叫提供dma和cpu虛擬地址 
  95.     void (*complete)(void *context); //非同步呼叫完成後的回撥函式 
  96.     void *context; //回撥函式的引數 
  97.     unsigned actual_length; //此次傳輸的實際長度 
  98.     int status; //執行的結果,成功被置0,否則是一個負的錯誤碼 
  99.     struct list_head queue; 
  100.     void *state; 
  101. }; 
  102. 在有訊息需要傳遞的時候,會將spi_transfer通過自己的transfer_list欄位掛到spi_message的transfers連結串列頭上。spi_message用來原子的執行spi_transfer表示的一串陣列傳輸請求。這個傳輸佇列是原子的,這意味著在這個訊息完成之前不會有其 他訊息佔用匯流排。訊息的執行總是按照FIFO的順序。
  103. 6.下面看一看spi_transfer:
  104. struct spi_transfer { 
  105.     const void *tx_buf; //要寫入裝置的資料(必須是dma_safe),或者為NULL 
  106.     void *rx_buf; //要讀取的資料緩衝(必須是dma_safe),或者為NULL 
  107.     unsigned len; //tx和rx的大小(位元組數),這裡不是指它的和,而是各自的長度,他們總是相等的 
  108.     dma_addr_t tx_dma; //如果spi_message.is_dma_mapped是真,這個是tx的dma地址 
  109.     dma_addr_t rx_dma; //如果spi_message.is_dma_mapped是真,這個是rx的dma地址 
  110.     unsigned cs_change:1; //影響此次傳輸之後的片選,指示本次tranfer結束之後是否要重新片選並呼叫setup改變設定,這個標誌可以較少系統開銷u8 
  111.     bits_per_word; //每個字長的位元數,如果是0,使用預設值 
  112.     u16 delay_usecs; //此次傳輸結束和片選改變之間的延時,之後就會啟動另一個傳輸或者結束整個訊息 
  113.     u32 speed_hz; //通訊時鐘。如果是0,使用預設值 
  114.     struct list_head transfer_list; //用來連線的雙向連結串列節點 
  115. };
  116. 7.spi控制器驅動的私有資料
  117. struct davinci_spi {
  118.     struct spi_bitbang    bitbang;
  119.     struct clk        *clk;//時鐘
  120.     u8            version;
  121.     resource_size_t        pbase;//spi暫存器實體地址
  122.     void __iomem        *base;//spi暫存器虛擬地址
  123.     u32            irq;//spi中斷號
  124.     struct completion    done;
  125.     const void    *tx;//傳送buffer
  126.     void            *rx;//讀buffer
  127. #define SPI_TMP_BUFSZ    (SMP_CACHE_BYTES + 1)
  128.     u8            rx_tmp_buf[SPI_TMP_BUFSZ];
  129.     int            rcount;//讀資料個數
  130.     int            wcount;//寫資料個數
  131.     struct davinci_spi_dma    dma;
  132.     struct davinci_spi_platform_data *pdata;
  133.     void        (*get_rx)(u32 rx_data, struct davinci_spi *);//讀資料函式
  134.     u32            (*get_tx)(struct davinci_spi *);//傳送資料函式
  135.     u8            bytes_per_word[SPI_MAX_CHIPSELECT];
  136.     u32            speed;
  137.     u32            cs_num;
  138.     bool        in_use;
  139. #ifdef CONFIG_CPU_FREQ
  140.     struct notifier_block freq_transition;
  141. #endif
  142. };
  143. 四、spi驅動初始化
  144. 1.spi匯流排註冊
  145. //spi匯流排結構
  146. struct bus_type spi_bus_type = {
  147.     .name        = "spi",
  148.     .dev_attrs    = spi_dev_attrs,
  149.     .match        = spi_match_device,
  150.     .uevent        = spi_uevent,
  151.     .suspend    = spi_suspend,
  152.     .resume        = spi_resume,
  153. };
  154. //spi_master類,會在sysfs檔案系統建立該類
  155. static struct class spi_master_class = {
  156.     .name        = "spi_master",
  157.     .owner        = THIS_MODULE,
  158.     .dev_release    = spi_master_release,
  159. };
  160. static int __init spi_init(void)
  161. {
  162.     int    status;
  163.     buf = kmalloc(SPI_BUFSIZ, GFP_KERNEL);
  164.     

    相關推薦

    Qualcomm平臺SPI驅動框架分析

    一、SPI匯流排概述 SPI是英語Serial Peripheral interface的縮寫,顧名思義就是序列外圍裝置介面,是Motorola首先在其MC68HCXX系列處理器上定義的。SPI介面主要應用在 EEPROM,FLASH,實時時鐘,AD轉換器,還有數字訊號處理器和數字訊號解碼器之間

    Linux驅動修煉之道-SPI驅動框架原始碼分析(中-續)

    然後看這裡是怎樣註冊spi主機控制器驅動的: int spi_register_master(struct spi_master *master)   {       。。。。。。。。。。。。。。。。       /*將spi新增到核心,這

    Linux驅動修煉之道-SPI驅動框架原始碼分析(中)

    來自:http://blog.csdn.net/woshixingaaa/article/details/6574220 這篇來分析spi子系統的建立過程。 嵌入式微處理器訪問SPI裝置有兩種方式:使用GPIO模擬SPI介面的工作時序或者使用SPI控制

    Linux驅動修煉之道-SPI驅動框架原始碼分析(上)

    SPI驅動架構,以前用過,不過沒這個詳細,跟各位一起分享: 來自:http://blog.csdn.net/woshixingaaa/article/details/6574215 SPI協議是一種同步的序列資料連線標準,由摩托羅拉公司命名,可工作於全雙工模式。相

    Linux驅動修煉之道-SPI驅動框架原始碼分析(下-續)

    spi_async在spi.h中定義的: <span style="font-size:18px;">staticinlineint spi_async(struct spi_device *spi, struct spi_mess

    SPI驅動框架-1(DM8127 Linux2.6.37為例)

    orm span remove mac 設備 single 隊列 drive for 一、驅動程序結構 1、platform_device 文件:/arch/arm/mach-omap2/device.c static struct omap2_mcspi_platfor

    5.7.6.framebuffer驅動框架分析1

    iop 之間 write ioctl 程序 硬件 struct 於平 完成 http://www.mamicode.com/info-detail-1209620.html 5.7.6.1、fbmem_init函數[driver/video/fbmem.c] (1)#ifd

    v4l2驅動框架分析-1

    需要思考的問題: (1) cimutils應用程式維護了哪些結構體,v4l2驅動框架維護了哪些結構體 (2)/dev/video0 這個節點怎麼建立的 (3)應用層open 裝置節點/dev/video0 的時候,核心中的呼叫關係和具體乾的工作 (4)應用層ioctl 操作後,核心中

    tty驅動框架分析

    tty框架如下圖所示: 整個 uart 框架大概的樣子如上圖所示,大致可以分為四層,一層是下層我們的串列埠驅動層,它直接與硬體相接觸,我們需要填充一個 struct uart_ops 的結構體,再向上是tty核心層,在向上是線路規程,再向上是是直接和使用者空間對接的,它們每一層都有一個

    MTK平臺camera驅動架構分析

    MTK6580 AndroidO(android8.1)版本camera 驅動分析 CAMERA驅動整個框架分為:三個部分hal部分邏輯呼叫,kernel層的通用驅動sensorlist.c 和具體IC的驅動xxxx_mipi_raw.c 這裡主要介紹kernel部分和HAL層部分。

    USB攝像頭驅動框架分析

    怎樣構造一個攝像頭驅動程式: 1. 分配video_device:video_device_alloc 2. 設定 .fops .ioctl_ops (裡面需要設定11項) 如果要用核心提供的緩衝區操作函式,還需要構造一個videobuf

    linux驅動由淺入深系列:usb子系統之四(android平臺滑鼠驅動程式碼分析

    android上的usb口是支援OTG(on the go)的,USB OTG既可以作為Host又可以作為Device,我們本文來看一下android手機作為Host連線滑鼠的情況。OTG是如何做到既可以做Host又可以作為Device的呢 標準usb接頭中有四根線:vbu

    tty初探—uart驅動框架分析(二)uart_add_one_port

        在前面的一篇文章中,我們分析了一個 uart_driver 的向上註冊過程,主要是 tty 的一些東西,知道了 tty 註冊了一個字元裝置驅動,我們在使用者空間 open 時將呼叫到 uart_port.ops.startup ,在使用者空間 write 則呼叫 u

    usb gadget驅動框架分析

    以renesas BSP為例: 1.composite_driver層註冊流程 usb_composite_probe usb_gadget_probe_driver /*driver->udc_name沒有指定為e659000.usb那麼就預設是使用第一個UDC,找裡面的第一個還

    嵌入式Linux——IIC驅動(2):i2c驅動框架分析

    簡介:         本文主要介紹i2c匯流排框架,即對i2c的各個層次(i2c匯流排,i2c核心,i2c裝置)進行分析。同時我也會結合程式對框架進行說明。所以本文將分為兩部分,第一部分對i2c的框架進行介紹,而第二部分就是結合程式碼分析。 核心:linux-2.6.2

    Qualcomm 平臺觸控式螢幕驅動移植 筆記

    TP觸控式螢幕,應該是驅動開發中比較簡單並且適合新手入手的模組。不過雖然簡單,但涉及到的內容還是比較多的,其中Linux相關主要的機制: 1. input 機制 2. 中斷、定時器 3. I2C 1

    學習筆記 --- LINUX網絡卡驅動框架分析

    網絡卡的驅動很簡單,就是填充net_device結構體,其應用層到網路協議層核心已經完成了,我們的工作就是填寫這個net_device,然後註冊就可以了。 修正一下:上面第三步應該是:register_netdev 下面程式碼實現一個虛擬網絡卡,這裡沒有實際的網絡卡,只是

    Linux系統spi驅動程式分析---(一)

    說明:本文將分析Linux-2.6.17原始碼中的spi驅動程式,其內容為本人閱讀原始碼後的一些理解。由於本人水平有限,所以內容可能比較雜亂零散,權當個人筆記記錄之用。而以下內容均以powerpc架構為例說明。 在Linux系統中,spi驅動的設計採用了分層設計模式的思想

    linux SPI驅動框架(二) -- 裝置驅動

    裝置驅動關注的結構體主要有兩個,struct spi_device描述spi從裝置,struct spi_driver是從裝置的裝置驅動。 struct spi_device { struct device dev; str

    SylixOS中AHCI驅動框架分析

    1、概述 本文件介紹SATA和AHCI相關協議,以IMX6Q實驗平臺為基礎,分析SylixOS中AHCI驅動框架的具體實現。 2、SATA簡介 2.1 SATA硬碟 串列埠硬碟SATA(Serial ATA)與以往的並口硬碟PATA(Parallel ATA)相比,資料傳