spi驅動框架全面分析,從master驅動到設備驅動


內核版本:linux2.6.32.2 
硬件資源:s3c2440
參考:  韋東山SPI視頻教程


內容概括:
    1、I2C 驅動框架回顧
    2、SPI 框架簡單介紹
    3、master 驅動框架

        3.1 驅動側
        3.2 設備側

    4、SPI 設備驅動框架

         4.1 設備冊
        4.2 驅動側

    5、設備驅動程序實例

1、I2C 驅動框架回顧
    在前面學習 I2C 驅動程序的時候我們知道,I2C 驅動框架分為兩層,一層是控制器驅動程序 i2c_adapter,它一般是由芯片廠商寫好的,主要提供一個 algorithm 底層的 i2c 協議的收發函數。i2c_adapter 驅動是基於 platform 模型,在driver側的probe函數里,取出資源信息進行設置,最后將adapter注冊到i2c_bus_type,注冊時會調用 i2c_scan_static_board_info,掃描並使用 i2c_new_device 創建設備(設備層的設備)。我們還提到了4種創建device的方式。

    另一層是設備驅動層,基於 i2c_bus_type ,這個就很簡單了,在設備驅動層 device 只需要提供一個從設備地址和名字,在 driver 里使用 i2c_smbus_read_byte_data 等類似的函數進行收發即可了,i2c_smbus_read_byte_data 等函數最終就會調用到 我們的 i2c_adapter->algorithm 里的收發函數進行收發。

2、SPI 框架簡單介紹
    對於SPI的大框架,與I2C是完全一致的,也分為兩層,控制器驅動程序層叫 spi_master ,主要提供transfer函數,進行spi協議的收發。spi_master 也是基於 Platform 模型的,注冊 spi_master 時也會掃描一個鏈表進行注冊設備,簡直太相似了。

    另一層是設備驅動層,基於 spi_bus_type,相比 i2c 在device中需要提供的信息多一些,需要有名字、片選、最大速率、模式、中斷號等等,在driver里則使用spi_read、spi_writer 等函數,最終也會調用到 master->transfer 函數進行發送接收。

    相比 I2C ,SPI驅動的框架是要簡單的,因為它少了兩種注冊device的方式,另外它不需要像I2C一樣去發送Start信號和設備地址去探測設備,SPI只需要片選選中就行了。但是它的底層收發的控制,相對I2C要復雜一點,畢竟4根線。

3、master 驅動框架
    之前,分析的驅動程序都是 S3C2410\S3C2440 平台的,由於我的開發板 Mini2440 沒有SPI設備,因此廠商帶的內核里關於 SPI 驅動部分不完整,而且在s3c23xx_spi_probe函數里注冊master的時候寫的十分復雜,干擾信息太多,不適合分析學習,因此,我搜索了一下其他平台的代碼,發現 atmel_spi.c (drivers\spi),里 atmel 實現的底層控制器驅動簡單清晰多了,因此就拿它開刀,分析Master驅動框架。

  3.1 驅動側

    前面簡介里,我提到 master 驅動框架是基於 platform 平台的(我分析的這倆都是,其它的不清楚),那么肯定就要注冊platform_driver了,下面我們就開看看。

分配一個platfrom_driver結構

[cpp]  view plain  copy
  1. static struct platform_driver atmel_spi_driver = {  
  2.     .driver     = {  
  3.         .name   = "atmel_spi",  
  4.         .owner  = THIS_MODULE,  
  5.     },  
  6.     .suspend    = atmel_spi_suspend,  
  7.     .resume     = atmel_spi_resume,  
  8.     .remove     = __exit_p(atmel_spi_remove),  
  9. };  

    將 atmel_spi_driver 注冊到 platform_bus_type ,匹配設備 probe

[cpp]  view plain  copy
  1. static int __init atmel_spi_init(void)  
  2. {  
  3.     return platform_driver_probe(&atmel_spi_driver, atmel_spi_probe);  
  4. }     

    我們之前都是將 probe 函數,直接放在driver結構體里,這里不是,而是調用了 platform_driver_probe ,就不貼代碼了,還看段函數介紹,大致了解下什么意思。
/**
 * platform_driver_probe - register driver for non-hotpluggable device
 * @drv: platform driver structure
 * @probe: the driver probe routine, probably from an __init section
 *
 * Use this instead of platform_driver_register() when you know the device
 * is not hotpluggable and has already been registered, and you want to
 * remove its run-once probe() infrastructure from memory after the driver
 * has bound to the device.
 *
 * One typical use for this would be with drivers for controllers integrated
 * into system-on-chip processors, where the controller devices have been
 * configured as part of board setup.
 *
 * Returns zero if the driver registered and bound to a device, else returns
 * a negative error code and with the driver not registered.
 */
    1、適用於非熱插拔設備
    2、通常Probe位於__init段3、當你知道device是非熱拔插的,而且設備已經被注冊了,而且你想在probe函數調用一次之后就銷毀它節省空間,使用 platform_driver_probe 而非 platform_driver_register。
    4、一個典型的應用是,用在完整的控制器驅動,控制器設備被當作 board setup 的一部分(在板子初始化的時候,設備就已經被注冊了,放在board_info里)
    5、返回0 ,如果driver注冊成功且匹配到一個device ,以后再也無法被別的device probe了。
    6、否則,返回一個錯誤,且driver未注冊。
    顯然,我們寫的正式一個控制器驅動程序,設備側確實是早已注冊(后邊會講)。
    疑問:有人說使用 platform_driver_probe 時 driver 只能被一個 device 匹配綁定,之后再也無法被別的device probe,難道說,我有倆spi控制器還需要寫兩個控制器驅動程序么?我認為這種說法是不對的,我猜大概是driver注冊時,會匹配一遍device鏈表,把能支持的device都probe,之后再有deivce注冊進來就不行了。這個有待驗證。

    i2c驅動框架里,是在driver->probe 分配設置注冊adapter,想必spi也是在driver->probe里分配設置注冊master。

[cpp]  view plain  copy
  1. static int __init atmel_spi_probe(struct platform_device *pdev)  
  2. {  
  3.     struct resource     *regs;  
  4.     int         irq;  
  5.     struct clk      *clk;  
  6.     int         ret;  
  7.     struct spi_master   *master;  
  8.     struct atmel_spi    *as;  
  9.       
  10.     /* 獲取 device 側提供的Io內存以及中斷 */  
  11.     regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);  
  12.     irq = platform_get_irq(pdev, 0);  
  13.       
  14.     /* 獲取 spi 時鍾,一會好使能它 */  
  15.     clk = clk_get(&pdev->dev, "spi_clk");  
  16.   
  17.     /* 分配一個spi_master結構 額外加上一個 atmel_spi 用來存放其它信息 */  
  18.     master = spi_alloc_master(&pdev->dev, sizeof *as);  
  19.   
  20.     /* 設置 master  */  
  21.     master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;   // 所支持的模式  
  22.     master->bus_num = pdev->id;   // 控制器編號,用於分辨外圍spi設備是連接在哪一個控制器上  
  23.     master->num_chipselect = 4;  // 片選最大值+1,spi設備的片選值要小於它  
  24.     master->setup = atmel_spi_setup; // 一個控制器上可能接有多個spi設備,它們的頻率和模式是不一樣的,用於設備之間切換時設置這些信息。  
  25.     master->transfer = atmel_spi_transfer;   // 最重要的發送函數  
  26.     master->cleanup = atmel_spi_cleanup;  
  27.       
  28.     /* 將 Master 放入 pdev->dev->p->driver_data 里*/  
  29.     platform_set_drvdata(pdev, master);   
  30.       
  31.     /* as 指向 master->dev->p->driver_data ,填充多出來那個 atmel_spi 結構 */  
  32.     as = spi_master_get_devdata(master);  
  33.     as->buffer = dma_alloc_coherent(&pdev->dev, BUFFER_SIZE,  
  34.                     &as->buffer_dma, GFP_KERNEL);  
  35.     spin_lock_init(&as->lock);  
  36.     INIT_LIST_HEAD(&as->queue);  
  37.     as->pdev = pdev;  
  38.     as->regs = ioremap(regs->start, (regs->end - regs->start) + 1);  
  39.     as->irq = irq;  
  40.     as->clk = clk;  
  41.       
  42.     /* 注冊中斷 使能時鍾 */  
  43.     ret = request_irq(irq, atmel_spi_interrupt, 0,  
  44.             dev_name(&pdev->dev), master);  
  45.     clk_enable(clk);  
  46.       
  47.     /* 設置硬件寄存器 */  
  48.     spi_writel(as, CR, SPI_BIT(SWRST));  
  49.     spi_writel(as, CR, SPI_BIT(SWRST)); /* AT91SAM9263 Rev B workaround */  
  50.     spi_writel(as, MR, SPI_BIT(MSTR) | SPI_BIT(MODFDIS));  
  51.     spi_writel(as, PTCR, SPI_BIT(RXTDIS) | SPI_BIT(TXTDIS));  
  52.     spi_writel(as, CR, SPI_BIT(SPIEN));  
  53.   
  54.     /* 注冊master */  
  55.     ret = spi_register_master(master);  
  56.   
  57.     return 0;  
  58. }  

    對於master的設置過程注釋已經說的很明白了,我們還得看看分配和注冊過程。

[cpp]  view plain  copy
  1. struct spi_master *spi_alloc_master(struct device *dev, unsigned size)  
  2. {  
  3.     struct spi_master   *master;  
  4.   
  5.     master = kzalloc(size + sizeof *master, GFP_KERNEL);  
  6.   
  7.     device_initialize(&master->dev); // 初始化設備  
  8.     master->dev.class = &spi_master_class;     
  9.     master->dev.parent = get_device(dev);    // 在 sysfs 平台設備xxx目錄下創建目錄  
  10.     spi_master_set_devdata(master, &master[1]);  
  11.   
  12.     return master;  
  13. }  

    1、spi_alloc_master 實際申請的內存大小為一個struct master + struct atmel_spi,並用master->dev->p->driver_data 指向這個多出來的 struct atmel_spi 空間,用來存放 master 的中斷 、寄存器等東西。

    2、初始化 master->dev ,設置它的父設備等。

[cpp]  view plain  copy
  1. int spi_register_master(struct spi_master *master)  
  2. {  
  3.     /* 將master注冊到內核中去 */  
  4.     dev_set_name(&master->dev, "spi%u", master->bus_num);  
  5.     status = device_add(&master->dev);  
  6.       
  7.     /* 掃描spi設備信息,創建設備 */  
  8.     scan_boardinfo(master);  
  9. }  

    1、設置 master->dev 的名字,例如 spi0、spi1 ...

    2、device_add 注冊設備

    3、掃描spi設備信息:scan_boardinfo(master)

[cpp]  view plain  copy
  1. static void scan_boardinfo(struct spi_master *master)  
  2. {  
  3.     struct boardinfo    *bi;  
  4.   
  5.     mutex_lock(&board_lock);  
  6.     list_for_each_entry(bi, &board_list, list) {  
  7.         struct spi_board_info   *chip = bi->board_info;  
  8.         unsigned        n;  
  9.         /* 如果說  board_info 提供的bus_num 和 master—>bus_num 一致,則調用 spi_new_device */  
  10.         for (n = bi->n_board_info; n > 0; n--, chip++) {  
  11.             if (chip->bus_num != master->bus_num)  
  12.                 continue;  
  13.   
  14.             (void) spi_new_device(master, chip);    // 我們放到設備驅動層,在分析它  
  15.         }  
  16.     }  
  17.     mutex_unlock(&board_lock);  
  18. }  

    掃描 board_list ,取出每一個 boardinfo ,比對,如果 boardinfo 里的 bus_num 和 master 的 bus_num 相等,則認為這個spi設備在硬件物理連接上是接到這個控制器的,則使用 spi_new_device 創建 spi 設備。這個過程和i2c是多么的相似。至於在哪里填充的 board_list ,到后邊設備層驅動框架時再說不遲。


  3.2 設備側
    有 platform_driver 必然有 platform_device 與之對應,At91sam9260_devices.c 中定義

[cpp]  view plain  copy
  1. static struct resource spi0_resources[] = {  
  2.     [0] = {  
  3.         .start  = AT91SAM9260_BASE_SPI0,  
  4.         .end    = AT91SAM9260_BASE_SPI0 + SZ_16K - 1,  
  5.         .flags  = IORESOURCE_MEM,  
  6.     },  
  7.     [1] = {  
  8.         .start  = AT91SAM9260_ID_SPI0,  
  9.         .end    = AT91SAM9260_ID_SPI0,  
  10.         .flags  = IORESOURCE_IRQ,  
  11.     },  
  12. };  

    資源文件,提供寄存器范圍,spi中斷。

[cpp]  view plain  copy
  1. static struct platform_device at91sam9260_spi0_device = {  
  2.     .name       = "atmel_spi",      // 名字與driver一致  
  3.     .id     = 0,  
  4.     .dev        = {  
  5.                 .dma_mask       = &spi_dmamask,  
  6.                 .coherent_dma_mask  = DMA_BIT_MASK(32),  
  7.     },  
  8.     .resource   = spi0_resources,   // 資源文件  
  9.     .num_resources  = ARRAY_SIZE(spi0_resources),  
  10. };  

    與 driver 所配對的設備,顯然名字是一樣的。一般會有兩個spi控制器,at91sam9260_spi1_device 和 at91sam9260_spi0_device 一樣一樣的,這里就不貼代碼了。

    既然分配了 platform_device 那么肯定會在某個地方調用 platform_device_register 將它注冊到 platform_bus_type , 就是在 at91_add_device_spi 。

[cpp]  view plain  copy
  1. void __init at91_add_device_spi(struct spi_board_info *devices, int nr_devices)  
  2. {  
  3.     ...  
  4.     spi_register_board_info(devices, nr_devices);  
  5.   
  6.     /* Configure SPI bus(es) */  
  7.     if (enable_spi0) {  
  8.         ...  
  9.         platform_device_register(&at91sam9260_spi0_device);  
  10.     }  
  11.     if (enable_spi1) {  
  12.         ...  
  13.         platform_device_register(&at91sam9260_spi1_device);  
  14.     }  
  15. }  

    1、添加 spi 設備信息,這應該是在設備驅動層要說的東西~就是前邊的填充 Board_list 鏈表。

    2、將我們的 master 的設備側 at91sam9260_spi0_device 注冊到 platform_bus_type

    思考:這樣做有什么好處呢?

        這樣可以保證,master driver 與 device 匹配成功調用 probe 函數 scan_boardinfo 時,spi設備已經被添加到board_list中去~!如果先注冊成功了 master 驅動,再添加spi設備信息就無用了。根 i2c 也是一樣的。

    至此,Master 驅動的框架就分析完了,至於 master 的那些設置,我們到下篇文件寫 master 驅動里細究。


4、SPI 設備驅動框架

    設備驅動層,參考韋東山老師的 SPI Flash 驅動來分析,設備驅動層,device driver 都是注冊到spi_bus_type的,因此,我們現在看看 spi_bus_type->match 函數,看看它們如何匹配。

[cpp]  view plain  copy
  1. static int spi_match_device(struct device *dev, struct device_driver *drv)  
  2. {  
  3.     const struct spi_device *spi = to_spi_device(dev);  
  4.     const struct spi_driver *sdrv = to_spi_driver(drv);  
  5.   
  6.     if (sdrv->id_table)  
  7.         return !!spi_match_id(sdrv->id_table, spi);  
  8.   
  9.     return strcmp(spi->modalias, drv->name) == 0;  
  10. }  

    如果,driver里有id_table,則根據id_table進行匹配,沒有就根據 spi->moadlias (設備名),和 driver->name 進行比較了。一樣則配對成功。

  4.1 設備層

    設備層比較簡單,先來分析它吧,目的只有一個分配 spi_board_info 設置 注冊。

[cpp]  view plain  copy
  1. static struct spi_board_info spi_info_jz2440[] = {  
  2.     {  
  3.         .modalias = "100ask_spi_flash",  /* 對應的spi_driver名字也是"oled" */  
  4.         .max_speed_hz = 80000000,   /* max spi clock (SCK) speed in HZ */  
  5.         .bus_num = 1,     /* jz2440里OLED接在SPI CONTROLLER 1 */  
  6.         .mode    = SPI_MODE_0,  
  7.         .chip_select   = S3C2410_GPG(2), /* flash_cs, 它的含義由spi_master確定 */  
  8.     }  
  9. };  

[cpp]  view plain  copy
  1. static int spi_info_jz2440_init(void)  
  2. {  
  3.     return spi_register_board_info(spi_info_jz2440, ARRAY_SIZE(spi_info_jz2440));   // list_add_tail(&bi->list, &board_list);  
  4. }  

    注冊 list_add_tail(&spi_info_jz2440->list, &board_list) 

    前面我們提到,master注冊成功時會掃描 board_list 注冊 spi 設備,現在來看看 spi 設備的注冊過程,雖然沒有啥重要的。

[cpp]  view plain  copy
  1. struct spi_device *spi_new_device(struct spi_master *master,  
  2.                   struct spi_board_info *chip)  
  3. {  
  4.     struct spi_device   *proxy;  
  5.     int         status;  
  6.   
  7.     proxy = spi_alloc_device(master);  
  8.   
  9.     proxy->chip_select = chip->chip_select;  
  10.     proxy->max_speed_hz = chip->max_speed_hz;  
  11.     proxy->mode = chip->mode;  
  12.     proxy->irq = chip->irq;  
  13.     strlcpy(proxy->modalias, chip->modalias, sizeof(proxy->modalias));  
  14.     proxy->dev.platform_data = (void *) chip->platform_data;  
  15.     proxy->controller_data = chip->controller_data;  
  16.     proxy->controller_state = NULL;  
  17.   
  18.     status = spi_add_device(proxy);  
  19.   
  20.     return proxy;  
  21. }  
  22.   
  23. struct spi_device *spi_alloc_device(struct spi_master *master)  
  24. {  
  25.     struct spi_device   *spi;  
  26.     struct device       *dev = master->dev.parent;  
  27.   
  28.     spi = kzalloc(sizeof *spi, GFP_KERNEL);  
  29.   
  30.     spi->master = master;  
  31.     spi->dev.parent = dev;  
  32.     spi->dev.bus = &spi_bus_type;  
  33.     spi->dev.release = spidev_release;  
  34.     device_initialize(&spi->dev);  
  35.     return spi;  
  36. }  
  37.   
  38. int spi_add_device(struct spi_device *spi)  
  39. {  
  40.     static DEFINE_MUTEX(spi_add_lock);  
  41.     struct device *dev = spi->master->dev.parent;  
  42.     int status;  
  43.   
  44.     /* 片選限制 */  
  45.     if (spi->chip_select >= spi->master->num_chipselect) {  
  46.         dev_err(dev, "cs%d >= max %d\n",  
  47.             spi->chip_select,  
  48.             spi->master->num_chipselect);  
  49.         return -EINVAL;  
  50.     }  
  51.   
  52.     /* Set the bus ID string */  
  53.     dev_set_name(&spi->dev, "%s.%u", dev_name(&spi->master->dev),  
  54.             spi->chip_select);  
  55.           
  56.     status = spi_setup(spi);  
  57.     /* 
  58.         if (!spi->bits_per_word) 
  59.             spi->bits_per_word = 8; 
  60.  
  61.         status = spi->master->setup(spi); 
  62.     */  
  63.   
  64.     status = device_add(&spi->dev);  
  65. }  

    有一點,需要留意的吧,我們在寫 master 驅動時,master->num_chipselect 要大於我們將要注冊進來的spi設備的 chip_select 。


  4.2 驅動側

[cpp]  view plain  copy
  1. static struct spi_driver spi_flash_drv = {  
  2.     .driver = {  
  3.         .name   = "100ask_spi_flash",  
  4.         .owner  = THIS_MODULE,  
  5.     },  
  6.     .probe      = spi_flash_probe,  
  7.     .remove     = __devexit_p(spi_flash_remove),  
  8. };  

    分配一個 spi_driver ,沒有 id_table ,要根據名字進行匹配了,顯然跟前面的設備是一樣的。

[cpp]  view plain  copy
  1. static int spi_flash_init(void)  
  2. {  
  3.     return spi_register_driver(&spi_flash_drv);  
  4. }  

    注冊到 spi_bus_type ,匹配成功好調用 probe 函數,韋東山老師是將spi flash 作為一個mtd設備來使用的,因此在probe函數中分配、設置、注冊 mtd_info 

[cpp]  view plain  copy
  1. static int __devinit spi_flash_probe(struct spi_device *spi)  
  2. {  
  3.     int mid, did;  
  4.       
  5.     spi_flash = spi;  
  6.   
  7.     s3c2410_gpio_cfgpin(spi->chip_select, S3C2410_GPIO_OUTPUT);  
  8.     SPIFlashInit();  
  9.     SPIFlashReadID(&mid, &did);  
  10.     printk("SPI Flash ID: %02x %02x\n", mid, did);  
  11.         memset(&spi_flash_dev, 0, sizeof(spi_flash_dev));  
  12.     /* 構造注冊一個mtd_info 
  13.      * mtd_device_register(master, parts, nr_parts) 
  14.      * 
  15.      */  
  16.   
  17.     /* Setup the MTD structure */  
  18.     spi_flash_dev.name = "100ask_spi_flash";  
  19.     spi_flash_dev.type = MTD_NORFLASH;  
  20.     spi_flash_dev.flags = MTD_CAP_NORFLASH;  
  21.     spi_flash_dev.size = 0x200000;  /* 2M */  
  22.     spi_flash_dev.writesize = 1;  
  23.     spi_flash_dev.writebufsize = 4096; /* 沒有用到 */  
  24.     spi_flash_dev.erasesize = 4096;  /* 擦除的最小單位 */  
  25.   
  26.     spi_flash_dev.owner = THIS_MODULE;  
  27.     spi_flash_dev._erase = spi_flash_erase;  
  28.     spi_flash_dev._read  = spi_flash_read;  
  29.     spi_flash_dev._write = spi_flash_write;  
  30.   
  31.     mtd_device_register(&spi_flash_dev, NULL, 0);  
  32.        
  33.     return 0;  
  34. }  

    在 i2c 設備驅動程序中,我們使用 i2c_read 等函數調用 adapter 里的底層收發函數進行與i2c設備通信,spi肯定也有相應的函數,例如 spi_read、spi_write ,下面我們來仔細看看,這個很重要~不然我們怎么寫設備驅動呢,光寫個框架不能收發那不白扯么。
[cpp]  view plain  copy
  1. static inline int spi_write(struct spi_device *spi, const u8 *buf, size_t len)  
  2. {  
  3.     struct spi_transfer t = {  
  4.             .tx_buf     = buf,  
  5.             .len        = len,  
  6.         };  
  7.     struct spi_message  m;  
  8.   
  9.     spi_message_init(&m);  
  10.     spi_message_add_tail(&t, &m);  
  11.     return spi_sync(spi, &m);  
  12. }  
    以 spi_write 為例,看看它是如何調用到底層收發函數的。它構造了一個 spi_message 並由 spi_transfer組成,然后調用 spi_sync(spi, &m)->spi_async(spi,&m)->master->transfer(spi, &m).過程我們了解了,那么如何組織 spi_messgae ,它對應於時序圖怎樣的一個過程我們還不明白。

    還記得,i2c 是通過構造 i2c_msg ,然后傳遞多個 i2c_msg 給底層的發送函數,多個i2c_msg 組成start信號和p信號之間的發送過程,每一個i2c_msg 都有一個start信號。大概 i2c_msg 就類比於spi里的 spi_transfer ,但是通常情況下,整個 spi_messgae 傳輸過程我們只片選一次就夠了。下面看個實例分析。

    上圖是 spi flash 讀取任意字節的時序圖,片選選中之后,我們要先發送一個0x03 命令(1字節),再發送24bit(3字節)地址(先發送高位),然后讀取len,最后取消片選。

[cpp]  view plain  copy
  1. void SPIFlashRead(unsigned int addr, unsigned char *buf, int len)  
  2. {     
  3.     unsigned char tx_buf[4];     
  4.     struct spi_transfer t[] = {  
  5.             {  
  6.                 .tx_buf     = tx_buf,  
  7.                 .len        = 4,  
  8.             },  
  9.             {  
  10.                 .rx_buf     = buf,  
  11.                 .len        = len,  
  12.             },  
  13.         };  
  14.     struct spi_message  m;  
  15.   
  16.     tx_buf[0] = 0x03;  
  17.     tx_buf[1] = addr >> 16;  
  18.     tx_buf[2] = addr >> 8;  
  19.     tx_buf[3] = addr & 0xff;  
  20.   
  21.     spi_message_init(&m);  
  22.     spi_message_add_tail(&t[0], &m);  
  23.     spi_message_add_tail(&t[1], &m);  
  24.     spi_sync(spi_flash, &m);      
  25. }  

    韋老大的程序里,構造一個 struct spi_transfer 類型的數組,兩個成員,第一個有一個tx_buf(表示寫),長度為4,用來發送1字節命令和3字節地址。第二個成員 有一個rx_buf(表示讀),長度由參數決定,用來讀取長度len字節的內容。然后分配一個struct spi_message 並使用 spi_message_init 進行初始化,然后將 spi_transfer 數組成員依次添加到 spi_message 中去,最后 spi_sync(spi_flash, &m)

    紙上談兵一大堆,現在來看看,我們在寫一個spi設備驅動的時候需要做哪些工作。
    設備側:
      1、分配一個 spi_board_info 結構體
      2、設置 spi_board_info 里的名字、最大頻率、控制器編號、模式、片選
      3、注冊 spi_register_board_info 
    驅動側:
      1、分配一個 spi_driver 結構
      2、設置名字、probe等函數
      3、注冊 spi_register_driver
      4、使用spi_write等系統調用,搞明白 spi_transfer spi_message ,會使用它們進行收發

   一個 spi_message 對應於一個不可打斷的spi傳輸過程,可以理解為片選選中直到取消選中的過程(特殊情況下,一個spi_message里面是可以取消片選再選中的),而 spi_message 由 spi_transfer 組成,根據 tx_buf  rx_buf 是否為空來判斷是 寫還是讀 操作。

    至此,整個 spi 的框架分析完畢


5、設備驅動程序實例

[cpp]  view plain  copy
  1. #include <linux/init.h>  
  2. #include <linux/fs.h>  
  3. #include <linux/slab.h>  
  4. #include <linux/module.h>  
  5. #include <linux/kernel.h>  
  6. #include <linux/device.h>  
  7. #include <sound/core.h>  
  8. #include <linux/spi/spi.h>  
  9. #include <asm/uaccess.h>  
  10. #include <linux/timer.h>  
  11.   
  12. #include <mach/hardware.h>  
  13. #include <mach/regs-gpio.h>  
  14.   
  15. #include <linux/gpio.h>  
  16. #include <plat/gpio-cfg.h>  
  17.   
  18. #include <linux/mtd/cfi.h>  
  19. #include <linux/mtd/mtd.h>  
  20. #include <linux/mtd/partitions.h>  
  21.   
  22. /* 參考: 
  23.  * drivers\mtd\devices\mtdram.c 
  24.  * drivers/mtd/devices/m25p80.c 
  25.  */  
  26.   
  27. static struct spi_device *spi_flash;  
  28.   
  29. /*  
  30.  *  
  31.  */  
  32. void SPIFlashReadID(int *pMID, int *pDID)  
  33. {  
  34.     unsigned char tx_buf[4];  
  35.     unsigned char rx_buf[2];  
  36.       
  37.     tx_buf[0] = 0x90;  
  38.     tx_buf[1] = 0;  
  39.     tx_buf[2] = 0;  
  40.     tx_buf[3] = 0;  
  41.   
  42.     spi_write_then_read(spi_flash, tx_buf, 4, rx_buf, 2);  
  43.   
  44.     *pMID = rx_buf[0];  
  45.     *pDID = rx_buf[1];  
  46. }  
  47.   
  48. static void SPIFlashWriteEnable(int enable)  
  49. {  
  50.     unsigned char val = enable ? 0x06 : 0x04;  
  51.     spi_write(spi_flash, &val, 1);      
  52. }  
  53.   
  54. static unsigned char SPIFlashReadStatusReg1(void)  
  55. {  
  56.     unsigned char val;  
  57.     unsigned char cmd = 0x05;  
  58.   
  59.     spi_write_then_read(spi_flash, &cmd, 1, &val, 1);  
  60.       
  61.     return val;  
  62. }  
  63.   
  64. static unsigned char SPIFlashReadStatusReg2(void)  
  65. {  
  66.     unsigned char val;  
  67.     unsigned char cmd = 0x35;  
  68.   
  69.     spi_write_then_read(spi_flash, &cmd, 1, &val, 1);  
  70.       
  71.     return val;  
  72. }  
  73.   
  74. static void SPIFlashWaitWhenBusy(void)  
  75. {  
  76.     while (SPIFlashReadStatusReg1() & 1)  
  77.     {  
  78.         /* 休眠一段時間 */  
  79.         /* Sector erase time : 60ms 
  80.          * Page program time : 0.7ms 
  81.          * Write status reg time : 10ms 
  82.          */  
  83.         set_current_state(TASK_INTERRUPTIBLE);  
  84.         schedule_timeout(HZ/100);  /* 休眠10MS后再次判斷 */  
  85.     }  
  86. }  
  87.   
  88. static void SPIFlashWriteStatusReg(unsigned char reg1, unsigned char reg2)  
  89. {      
  90.     unsigned char tx_buf[4];  
  91.   
  92.     SPIFlashWriteEnable(1);    
  93.       
  94.     tx_buf[0] = 0x01;  
  95.     tx_buf[1] = reg1;  
  96.     tx_buf[2] = reg2;  
  97.   
  98.     spi_write(spi_flash, tx_buf, 3);     
  99.   
  100.     SPIFlashWaitWhenBusy();  
  101. }  
  102.   
  103. static void SPIFlashClearProtectForStatusReg(void)  
  104. {  
  105.     unsigned char reg1, reg2;  
  106.   
  107.     reg1 = SPIFlashReadStatusReg1();  
  108.     reg2 = SPIFlashReadStatusReg2();  
  109.   
  110.     reg1 &= ~(1<<7);  
  111.     reg2 &= ~(1<<0);  
  112.   
  113.     SPIFlashWriteStatusReg(reg1, reg2);  
  114. }  
  115.   
  116. static void SPIFlashClearProtectForData(void)  
  117. {  
  118.     /* cmp=0,bp2,1,0=0b000 */  
  119.     unsigned char reg1, reg2;  
  120.   
  121.     reg1 = SPIFlashReadStatusReg1();  
  122.     reg2 = SPIFlashReadStatusReg2();  
  123.   
  124.     reg1 &= ~(7<<2);  
  125.     reg2 &= ~(1<<6);  
  126.   
  127.     SPIFlashWriteStatusReg(reg1, reg2);  
  128. }  
  129.   
  130. /* erase 4K */  
  131. void SPIFlashEraseSector(unsigned int addr)  
  132. {  
  133.     unsigned char tx_buf[4];  
  134.     tx_buf[0] = 0x20;  
  135.     tx_buf[1] = addr >> 16;  
  136.     tx_buf[2] = addr >> 8;  
  137.     tx_buf[3] = addr & 0xff;  
  138.   
  139.     SPIFlashWriteEnable(1);    
  140.   
  141.     spi_write(spi_flash, tx_buf, 4);  
  142.   
  143.     SPIFlashWaitWhenBusy();  
  144. }  
  145.   
  146. /* program */  
  147. void SPIFlashProgram(unsigned int addr, unsigned char *buf, int len)  
  148. {  
  149.     unsigned char tx_buf[4];     
  150.     struct spi_transfer t[] = {  
  151.             {  
  152.                 .tx_buf     = tx_buf,  
  153.                 .len        = 4,  
  154.             },  
  155.             {  
  156.                 .tx_buf     = buf,  
  157.                 .len        = len,  
  158.             },  
  159.         };  
  160.     struct spi_message  m;  
  161.   
  162.     tx_buf[0] = 0x02;  
  163.     tx_buf[1] = addr >> 16;  
  164.     tx_buf[2] = addr >> 8;  
  165.     tx_buf[3] = addr & 0xff;  
  166.   
  167.     SPIFlashWriteEnable(1);    
  168.   
  169.     spi_message_init(&m);  
  170.     spi_message_add_tail(&t[0], &m);  
  171.     spi_message_add_tail(&t[1], &m);  
  172.     spi_sync(spi_flash, &m);  
  173.   
  174.     SPIFlashWaitWhenBusy();      
  175. }  
  176.   
  177. void SPIFlashRead(unsigned int addr, unsigned char *buf, int len)  
  178. {  
  179.     /* spi_write_then_read規定了tx_cnt+rx_cnt < 32 
  180.      * 所以對於大量數據的讀取,不能使用該函數 
  181.      */  
  182.        
  183.     unsigned char tx_buf[4];     
  184.     struct spi_transfer t[] = {  
  185.             {  
  186.                 .tx_buf     = tx_buf,  
  187.                 .len        = 4,  
  188.             },  
  189.             {  
  190.                 .rx_buf     = buf,  
  191.                 .len        = len,  
  192.             },  
  193.         };  
  194.     struct spi_message  m;  
  195.   
  196.     tx_buf[0] = 0x03;  
  197.     tx_buf[1] = addr >> 16;  
  198.     tx_buf[2] = addr >> 8;  
  199.     tx_buf[3] = addr & 0xff;  
  200.   
  201.     spi_message_init(&m);  
  202.     spi_message_add_tail(&t[0], &m);  
  203.     spi_message_add_tail(&t[1], &m);  
  204.     spi_sync(spi_flash, &m);      
  205. }  
  206.   
  207. static void SPIFlashInit(void)  
  208. {  
  209.     SPIFlashClearProtectForStatusReg();  
  210.     SPIFlashClearProtectForData();  
  211. }  
  212.   
  213. /* 構造注冊一個mtd_info 
  214.  * mtd_device_register(master, parts, nr_parts) 
  215.  * 
  216.  */  
  217.   
  218. /* 首先: 構造注冊spi_driver 
  219.  * 然后: 在spi_driver的probe函數里構造注冊mtd_info 
  220.  */  
  221.   
  222. static struct mtd_info spi_flash_dev;  
  223.   
  224. static int spi_flash_erase(struct mtd_info *mtd, struct erase_info *instr)  
  225. {  
  226.     unsigned int addr = instr->addr;  
  227.     unsigned int len  = 0;  
  228.   
  229.     /* 判斷參數 */  
  230.     if ((addr & (spi_flash_dev.erasesize - 1)) || (instr->len & (spi_flash_dev.erasesize - 1)))  
  231.     {  
  232.         printk("addr/len is not aligned\n");  
  233.         return -EINVAL;  
  234.     }  
  235.   
  236.     for (len = 0; len < instr->len; len += 4096)  
  237.     {  
  238.         SPIFlashEraseSector(addr);  
  239.         addr += 4096;  
  240.     }  
  241.       
  242.     instr->state = MTD_ERASE_DONE;  
  243.     mtd_erase_callback(instr);  
  244.     return 0;  
  245. }  
  246.   
  247. static int spi_flash_read(struct mtd_info *mtd, loff_t from, size_t len,  
  248.         size_t *retlen, u_char *buf)  
  249. {  
  250.     SPIFlashRead(from, buf, len);  
  251.     *retlen = len;  
  252.     return 0;  
  253. }  
  254.   
  255. static int spi_flash_write(struct mtd_info *mtd, loff_t to, size_t len,  
  256.         size_t *retlen, const u_char *buf)  
  257. {  
  258.     unsigned int addr = to;  
  259.     unsigned int wlen  = 0;  
  260.   
  261.     /* 判斷參數 */  
  262.     if ((to & (spi_flash_dev.erasesize - 1)) || (len & (spi_flash_dev.erasesize - 1)))  
  263.     {  
  264.         printk("addr/len is not aligned\n");  
  265.         return -EINVAL;  
  266.     }  
  267.   
  268.     for (wlen = 0; wlen < len; wlen += 256)  
  269.     {  
  270.         SPIFlashProgram(addr, (unsigned char *)buf, 256);  
  271.         addr += 256;  
  272.         buf += 256;  
  273.     }  
  274.   
  275.     *retlen = len;  
  276.     return 0;  
  277. }  
  278.   
  279. static int __devinit spi_flash_probe(struct spi_device *spi)  
  280. {  
  281.     int mid, did;  
  282.       
  283.     spi_flash = spi;  
  284.   
  285.     s3c2410_gpio_cfgpin(spi->chip_select, S3C2410_GPIO_OUTPUT);  
  286.     SPIFlashInit();  
  287.     SPIFlashReadID(&mid, &did);  
  288.     printk("SPI Flash ID: %02x %02x\n", mid, did);  
  289.         memset(&spi_flash_dev, 0, sizeof(spi_flash_dev));  
  290.     /* 構造注冊一個mtd_info 
  291.      * mtd_device_register(master, parts, nr_parts) 
  292.      * 
  293.      */  
  294.   
  295.     /* Setup the MTD structure */  
  296.     spi_flash_dev.name = "100ask_spi_flash";  
  297.     spi_flash_dev.type = MTD_NORFLASH;  
  298.     spi_flash_dev.flags = MTD_CAP_NORFLASH;  
  299.     spi_flash_dev.size = 0x200000;  /* 2M */  
  300.     spi_flash_dev.writesize = 1;  
  301.     spi_flash_dev.writebufsize = 4096; /* 沒有用到 */  
  302.     spi_flash_dev.erasesize = 4096;  /* 擦除的最小單位 */  
  303.   
  304.     spi_flash_dev.owner = THIS_MODULE;  
  305.     spi_flash_dev._erase = spi_flash_erase;  
  306.     spi_flash_dev._read  = spi_flash_read;  
  307.     spi_flash_dev._write = spi_flash_write;  
  308.   
  309.     mtd_device_register(&spi_flash_dev, NULL, 0);  
  310.        
  311.     return 0;  
  312. }  
  313.   
  314. static int __devexit spi_flash_remove(struct spi_device *spi)  
  315. {  
  316.     mtd_device_unregister(&spi_flash_dev);  
  317.     return 0;  
  318. }  
  319.   
  320. static struct spi_driver spi_flash_drv = {  
  321.     .driver = {  
  322.         .name   = "100ask_spi_flash",  
  323.         .owner  = THIS_MODULE,  
  324.     },  
  325.     .probe      = spi_flash_probe,  
  326.     .remove     = __devexit_p(spi_flash_remove),  
  327. };  
  328.   
  329. static int spi_flash_init(void)  
  330. {  
  331.     return spi_register_driver(&spi_flash_drv);  
  332. }  
  333.   
  334. static void spi_flash_exit(void)  
  335. {  
  336.     spi_unregister_driver(&spi_flash_drv);  
  337. }  
  338.   
  339. module_init(spi_flash_init);  
  340. module_exit(spi_flash_exit);  
  341. MODULE_DESCRIPTION("Flash SPI Driver");  
  342. MODULE_AUTHOR("weidongshan@qq.com,www.100ask.net");  
  343. MODULE_LICENSE("GPL");  
  344.   
  345.   
  346. <strong style="color: rgb(255, 0, 0);">  
  347. </strong>  

轉自:http://blog.csdn.net/lizuobin2/article/details/51735963

  



免責聲明!

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



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