Linux kernel SPI驅動解釋

來源:互聯網
上載者:User

From: http://www.cnblogs.com/liugf05/archive/2012/12/03/2800457.html

下面有兩個大的模組:

一個是SPI匯流排驅動的分析            (研究了具體實現的過程)

另一個是SPI匯流排驅動的編寫(不用研究具體的實現過程)

SPI匯流排驅動分析

 

1 SPI概述
      SPI是英語Serial Peripheral interface的縮寫,顧名思義就是串列外圍裝置介面,是Motorola首先在其MC68HCXX系列處理器上定義的。SPI介面主要應用在 EEPROM,FLASH,系統時鐘,AD轉換器,還有數位訊號處理器和數字訊號解碼器之間。SPI是一種高速的,全雙工系統,同步的通訊匯流排,並且在晶片的管腳上只佔用四根線,節約了晶片的管腳,同時為PCB的布局上節省空間的,提供方便。
      SPI的通訊原理很簡單,它以主從方式工作,這種模式通常有一個主裝置和一個或多個從裝置,需要4根線,事實上3根也可以。也是所有基於SPI的裝置共有的,它們是SDI(資料輸入),SDO(資料輸出),SCLK(時鐘),CS(片選)。
      MOSI(SDO):主器件資料輸出,從器件資料輸入。
      MISO(SDI):主器件資料輸入,從器件資料輸出。
      SCLK :時鐘訊號,由主器件產生。
      CS:從器件使能訊號,由主器件控制。
      其中CS是控制晶片是否被選中的,也就是說只有片選訊號為預先規定的使能訊號時(高電位或低電位),對此晶片的操作才有效,這就允許在同一匯流排上串連多個SPI裝置成為可能。需要注意的是,在具體的應用中,當一條SPI匯流排上串連有多個裝置時,SPI本身的CS有可能被其他的GPIO腳代替,即每個裝置的CS腳被串連到處理器端不同的GPIO,通過操作不同的GPIO口來控制具體的需要操作的SPI裝置,減少各個SPI裝置間的幹擾。
      SPI是串列通訊協議,也就是說資料是一位一位從MSB或者LSB開始傳輸的,這就是SCK時鐘線存在的原因,由SCK提供時鐘脈衝,MISO、MOSI則基於此脈衝完成資料轉送。 SPI支援4-32bits的串列資料轉送,支援MSB和LSB,每次資料轉送時當從裝置的大小端發生變化時需要重新設定SPI Master的大小端。

 

2 Linux SPI驅動總體架構
      在2.6的linux核心中,SPI的驅動架構可以分為如下三個層次:SPI 核心層、SPI控制器驅動層和SPI裝置驅動層。
      Linux 中SPI驅動代碼位於drivers/spi目錄。
2.1 SPI核心層
      SPI核心層是Linux的SPI核心部分,提供了核心資料結構的定義、SPI控制器驅動和裝置驅動的註冊、登出管理等API。其為硬體平台無關層,向下屏蔽了物理匯流排控制器的差異,定義了統一的存取原則和介面;其向上提供了統一的介面,以便SPI裝置驅動通過匯流排控制器進行資料收發。
      Linux中,SPI核心層的代碼位於driver/spi/ spi.c。由於該層是平台無關層,本文將不再敘述,有興趣可以查閱相關資料。
2.2 SPI控制器驅動層
      SPI控制器驅動層,每種處理器平台都有自己的控制器驅動,屬於平台移植相關層。它的職責是為系統中每條SPI匯流排實現相應的讀寫方法。在物理上,每個SPI控制器可以串連若干個SPI從裝置。
      在系統開機時,SPI控制器驅動被首先裝載。一個控制器驅動用於支援一條特定的SPI匯流排的讀寫。一個控制器驅動可以用資料結構struct spi_master來描述。

   在include/liunx/spi/spi.h檔案中,在資料結構struct spi_master定義如下:

 

  1. struct spi_master {  
  2.     struct device   dev;  
  3.     s16         bus_num;  
  1.     u16         num_chipselect;  
  2.     int         (*setup)(struct spi_device *spi);  
  3.     int         (*transfer)(struct spi_device *spi, struct spi_message *mesg);  
  4.     void        (*cleanup)(struct spi_device *spi);  
  5. };  

 

     bus_num為該控制器對應的SPI匯流排號。
      num_chipselect 控制器支援的片選數量,即能支援多少個spi裝置 
      setup函數是設定SPI匯流排的模式,時鐘等的初始化函數, 針對裝置設定SPI的工作時鐘及資料轉送模式等。在spi_add_device函數中調用。 
      transfer函數是實現SPI匯流排讀寫方法的函數。實現資料的雙向傳輸,可能會睡眠

    cleanup登出時候調用

2.3 SPI裝置驅動層
      SPI裝置驅動層為使用者介面層,其為使用者提供了通過SPI匯流排訪問具體裝置的介面。
      SPI裝置驅動層可以用兩個模組來描述,struct spi_driver和struct spi_device。
      相關的資料結構如下:

 

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

 

  Driver是為device服務的,spi_driver註冊時會掃描SPI bus上的裝置,進行驅動和裝置的綁定,probe函數用於驅動和裝置匹配時被調用。從上面的結構體注釋中我們可以知道,SPI的通訊是通過訊息佇列機制,而不是像I2C那樣通過與從裝置進行對話的方式。

 

  1. struct spi_device {  
  2.     struct device       dev;  
  3.     struct spi_master   *master;  
  4.     u32         max_speed_hz;  
  5.     u8          chip_select;  
  6.     u8          mode;    
  7.     u8          bits_per_word;  
  8.     int         irq;  
  9.     void            *controller_state;  
  10.     void            *controller_data;  
  11.     char            modalias[32];   
  12. }; 

 

        .modalias   = "m25p10",

        .mode   =SPI_MODE_0,   //CPOL=0, CPHA=0 此處選擇具體資料轉送模式

        .max_speed_hz    = 10000000, //最大的spi時鐘頻率

        /* Connected to SPI-0 as 1st Slave */

        .bus_num    = 0,   //裝置串連在spi控制器0上

        .chip_select    = 0, //片選線號,在S5PC100的控制器驅動中沒有使用它作為片選的依據,而是選擇了下文controller_data裡的方法。

        .controller_data = &smdk_spi0_csi[0],  

通常來說spi_device對應著SPI匯流排上某個特定的slave。並且spi_device封裝了一個spi_master結構體。spi_device結構體包含了私人的特定的slave裝置特性,包括它最大的頻率,片選那個,輸入輸出模式等等

3 OMAP3630 SPI控制器
      OMAP3630上SPI是一個主/從的同步串列匯流排,這邊有4個獨立的SPI模組(SPI1,SPI2,SPI3,SPI4),各個模組之間的區別在於SPI1支援多達4個SPI裝置,SPI2和SPI3支援2個SPI裝置,而SPI4隻支援1個SPI裝置。

SPI控制器具有以下特徵:
     1.可程式化的串列時鐘,包括頻率,相位,極性。
     2.支援4到32位元據傳輸
     3.支援4通道或者單通道的從模式
     4.支援主的多通道模式
         4.1全雙工系統/半雙工
         4.2隻發送/只接收/收發都支援模式
         4.3靈活的I/O連接埠控制
         4.4每個通道都支援DMA讀寫
     5.支援多個中斷源的停機時間
     6.支援wake-up的電源管理
     7.內建64位元組的FIFO

 

4 spi_device以下一系列的操作是在platform板檔案中完成!spi_device的板資訊用spi_board_info結構體來描述:struct spi_board_info {charmodalias[SPI_NAME_SIZE];const void*platform_data;void*controller_data;intirq;u32max_speed_hz;u16bus_num;u16chip_select;u8mode;};

 

這個結構體記錄了SPI外設使用的主機控制器序號、片選訊號、資料位元速率、SPI傳輸方式等

構建的操作是以下的兩個步驟:

1.

 

static struct spi_board_info s3c_spi_devs[] __initdata = {

{

.modalias = "m25p10a",

.mode = SPI_MODE_0,

.max_speed_hz = 1000000,

.bus_num = 0,

.chip_select = 0,

.controller_data = &smdk_spi0_csi[SMDK_MMCSPI_CS],

},

};

2.

 

而這個info在init函數調用的時候會初始化:

spi_register_board_info(s3c_spi_devs,ARRAY_SIZE(s3c_spi_devs));

 

spi_register_board_info(s3c_spi_devs,ARRAY_SIZE(s3c_spi_devs));//註冊spi_board_info。這個代碼會把spi_board_info註冊到鏈表board_list上。spi_device封裝了一個spi_master結構體,事實上spi_master的註冊會在spi_register_board_info之後,spi_master註冊的過程中會調用scan_boardinfo掃描board_list,找到掛接在它上面的spi裝置,然後建立並註冊spi_device。

 

至此spi_device就構建並註冊完成了!!!!!!!!!!!!!

 

5 spi_driver的構建與註冊

 

driver有幾個重要的結構體:spi_driver、spi_transfer、spi_message

driver有幾個重要的函數    :spi_message_init、spi_message_add_tail、spi_sync

 

   //spi_driver的構建

static struct spi_driver   m25p80_driver = { 

.driver = {

        .name   ="m25p80",

        .bus    =&spi_bus_type,

        .owner  = THIS_MODULE,

    },

    .probe  = m25p_probe,

    .remove =__devexit_p(m25p_remove),

};

//spidriver的註冊

 

spi_register_driver(&m25p80_driver);

在有匹配的spi_device時,會調用m25p_probe

 

probe裡完成了spi_transfer、spi_message的構建;

spi_message_init、spi_message_add_tail、spi_sync、spi_write_then_read函數的調用

 

例如:

 

  1.  */  
  2. static int m25p10a_read( struct m25p10a *flash, loff_t from,   
  3.         size_t len, char *buf )  
  4. {  
  5.     int r_count = 0, i;  
  6.     struct spi_transfer st[2];  
  7.     struct spi_message  msg;  
  8.       
  9.     spi_message_init( &msg );  
  10.     memset( st, 0, sizeof(st) );  
  11.   
  12.     flash->cmd[0] = CMD_READ_BYTES;  
  13.     flash->cmd[1] = from >> 16;  
  14.     flash->cmd[2] = from >> 8;  
  15.     flash->cmd[3] = from;  
  16.   
  17.     st[ 0 ].tx_buf = flash->cmd;  
  18.     st[ 0 ].len = CMD_SZ;  
  19.     spi_message_add_tail( &st[0], &msg );  
  20.   
  21.     st[ 1 ].rx_buf = buf;  
  22.     st[ 1 ].len = len;  
  23.     spi_message_add_tail( &st[1], &msg );  
  24.   
  25.     mutex_lock( &flash->lock );  
  26.       
  27.     /* Wait until finished previous write command. */  
  28.     if (wait_till_ready(flash)) {  
  29.         mutex_unlock( &flash->lock );  
  30.         return -1;  
  31.     }  
  32.   
  33.     spi_sync( flash->spi, &msg );  
  34.     r_count = msg.actual_length - CMD_SZ;  
  35.     printk( "in (%s): read %d bytes\n", __func__, r_count );  
  36.     for( i = 0; i < r_count; i++ ) {  
  37.         printk( "0x%02x\n", buf[ i ] );  
  38.     }  
  39.   
  40.     mutex_unlock( &flash->lock );  
  41.     return 0;  
  42. }  
  43. static int m25p10a_write( struct m25p10a *flash, loff_t to,   
  44.         size_t len, const char *buf )  
  45. {  
  46.     int w_count = 0, i, page_offset;
  47.   
  48.     struct spi_transfer st[2]; 
  49.  
  50.     struct spi_message  msg;  
  51.     write_enable( flash );  //寫使能  
  52.       

        spi_message_init( &msg );  

  1.     memset( st, 0, sizeof(st) );  
  2.   
  3.     flash->cmd[0] = CMD_PAGE_PROGRAM;  
  4.     flash->cmd[1] = to >> 16;  
  5.     flash->cmd[2] = to >> 8;  
  6.     flash->cmd[3] = to;  
  7.   
  8.     st[ 0 ].tx_buf = flash->cmd;  
  9.     st[ 0 ].len = CMD_SZ;  
  10.   //填充spi_transfer,將transfer放在隊列後面
  11.     spi_message_add_tail( &st[0], &msg );  
  12.   
  13.     st[ 1 ].tx_buf = buf;  
  14.     st[ 1 ].len = len;  
  15.     spi_message_add_tail( &st[1], &msg );  
  16.   
  1.  
  2.         spi_sync( flash->spi, &msg );   調用spi_master發送spi_message
  3.     
  4.     return 0;  

 

 

  1.   
  2. static int m25p10a_probe(struct spi_device *spi)   
  3. {   
  4.     int ret = 0;  
  5.     struct m25p10a  *flash;  
  6.     char buf[ 256 ];  
  7.     flash = kzalloc( sizeof(struct m25p10a), GFP_KERNEL );  
  8.     flash->spi = spi;  
  9.     /* save flash as driver's private data */  
  10.     spi_set_drvdata( spi, flash );    
  1.     memset( buf, 0x7, 256 );  
  2.     m25p10a_write( flash, 0, 20, buf); //0地址寫入20個7  
  3.     memset( buf, 0, 256 );  
  4.     m25p10a_read( flash, 0, 25, buf ); //0地址讀出25個數  
  5.  
  6.     return 0;   
  7. }   

 

 

 

 

到目前為止,完成了SPI的驅動和應用

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.