spi(5)參考資料

OMAP3630下的Linux SPI總線驅動分析(2)


4 OMAP3630 SPI 控制器驅動

      在Linux內核中,SPI 控制器驅動位於drivers/spi目錄下,OMAP3630 的spi控制器驅動程序爲omap2_mcspi.c。
      SPI 控制器驅動的註冊採用Platform設備和驅動機制。

4.1 SPI 控制器的Platform device

      Android2.1中Platform device的註冊代碼位於內核的arch/arm/mach-omap2/devices.c中。

4.1.1 Platform device的定義

       在文件arch/arm/mach-omap2/devices.c中,Platform device定義如下:

  1. <span style="font-size:18px;">static struct omap2_mcspi_platform_config omap2_mcspi1_config = {  
  2.     .num_cs     = 4,  
  3. };  
  4.   
  5. static struct resource omap2_mcspi1_resources[] = {  
  6.     {  
  7.         .start      = OMAP2_MCSPI1_BASE,  
  8.         .end        = OMAP2_MCSPI1_BASE + 0xff,  
  9.         .flags      = IORESOURCE_MEM,  
  10.     },  
  11. };  
  12.   
  13. static struct platform_device omap2_mcspi1 = {  
  14.     .name       = "omap2_mcspi",  
  15.     .id     = 1,  
  16.     .num_resources  = ARRAY_SIZE(omap2_mcspi1_resources),  
  17.     .resource   = omap2_mcspi1_resources,  
  18.     .dev        = {  
  19.         .platform_data = &omap2_mcspi1_config,  
  20.     },  
  21. };  
  22.   
  23. static struct omap2_mcspi_platform_config omap2_mcspi2_config = {  
  24.     .num_cs     = 2,  
  25. };  
  26.   
  27. static struct resource omap2_mcspi2_resources[] = {  
  28.     {  
  29.         .start      = OMAP2_MCSPI2_BASE,  
  30.         .end        = OMAP2_MCSPI2_BASE + 0xff,  
  31.         .flags      = IORESOURCE_MEM,  
  32.     },  
  33. };  
  34.   
  35. static struct platform_device omap2_mcspi2 = {  
  36.     .name       = "omap2_mcspi",  
  37.     .id     = 2,  
  38.     .num_resources  = ARRAY_SIZE(omap2_mcspi2_resources),  
  39.     .resource   = omap2_mcspi2_resources,  
  40.     .dev        = {  
  41.         .platform_data = &omap2_mcspi2_config,  
  42.     },  
  43. };  
  44.   
  45. #if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP3)  
  46. static struct omap2_mcspi_platform_config omap2_mcspi3_config = {  
  47.     .num_cs     = 2,  
  48. };  
  49.   
  50. static struct resource omap2_mcspi3_resources[] = {  
  51.     {  
  52.     .start      = OMAP2_MCSPI3_BASE,  
  53.     .end        = OMAP2_MCSPI3_BASE + 0xff,  
  54.     .flags      = IORESOURCE_MEM,  
  55.     },  
  56. };  
  57.   
  58. static struct platform_device omap2_mcspi3 = {  
  59.     .name       = "omap2_mcspi",  
  60.     .id     = 3,  
  61.     .num_resources  = ARRAY_SIZE(omap2_mcspi3_resources),  
  62.     .resource   = omap2_mcspi3_resources,  
  63.     .dev        = {  
  64.         .platform_data = &omap2_mcspi3_config,  
  65.     },  
  66. };  
  67. #endif  
  68.   
  69. #ifdef CONFIG_ARCH_OMAP3  
  70. static struct omap2_mcspi_platform_config omap2_mcspi4_config = {  
  71.     .num_cs     = 1,  
  72. };  
  73.   
  74. static struct resource omap2_mcspi4_resources[] = {  
  75.     {  
  76.         .start      = OMAP2_MCSPI4_BASE,  
  77.         .end        = OMAP2_MCSPI4_BASE + 0xff,  
  78.         .flags      = IORESOURCE_MEM,  
  79.     },  
  80. };  
  81.   
  82. static struct platform_device omap2_mcspi4 = {  
  83.     .name       = "omap2_mcspi",  
  84.     .id     = 4,  
  85.     .num_resources  = ARRAY_SIZE(omap2_mcspi4_resources),  
  86.     .resource   = omap2_mcspi4_resources,  
  87.     .dev        = {  
  88.         .platform_data = &omap2_mcspi4_config,  
  89.     },  
  90. };  
  91. #endif</span>  
        可以看到,這邊定義了4個SPI控制器的Platform device,id分別爲“1,2,3, 4”,name都爲“omap2_mcspi”,變量resource中定義了適配器的寄存器基地址。

4.1.2 Platform device的註冊

        Platform device的註冊是由內核啓動時完成的。在文件arch/arm/mach-omap2/devices.c中,具體的代碼如下:
  1. <span style="font-size:18px;">static int __init omap2_init_devices(void)  
  2. {  
  3.     /* please keep these calls, and their implementations above, 
  4.      * in alphabetical order so they're easier to sort through. 
  5.      */  
  6.     omap_hsmmc_reset();  
  7.     omap_init_camera();  
  8.     omap_init_mbox();  
  9.     omap_init_mcspi();  
  10.     omap_init_sti();  
  11.     omap_init_sha1_md5();  
  12.   
  13.     return 0;  
  14. }  
  15. arch_initcall(omap2_init_devices);</span><span style="font-family:Arial, Verdana, sans-serif;"><span style="white-space: normal;">  
  16. </span></span>  

        內核啓動時會調用函數omap2_init_devices(),函數omap2_init_devices ()再調到spi的初始化函數omap_init_mcspi(),omap_init_mcspi()代碼如下:

  1. <span style="font-size:18px;">static void omap_init_mcspi(void)  
  2. {  
  3.     platform_device_register(&omap2_mcspi1);  
  4.     platform_device_register(&omap2_mcspi2);  
  5. #if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP3)  
  6.     if (cpu_is_omap2430() || cpu_is_omap343x())  
  7.         platform_device_register(&omap2_mcspi3);  
  8. #endif  
  9. #ifdef CONFIG_ARCH_OMAP3  
  10.     if (cpu_is_omap343x())  
  11.         platform_device_register(&omap2_mcspi4);  
  12. #endif  
  13. }</span>  

       函數omap2_init_devices()通過調用platform_device_register()將Platform device註冊到Platform總線上,完成註冊後,寄存器的基地址等信息會在設備樹中描述了,此後只需利用platform_get_resource等標準接口自動獲取即可,實現了驅動和資源的分離。

4.2 SPI 控制器的Platform driver

      Andrord 2.1中Platform driver的註冊代碼位於內核的drivers/spi/omap2_mcspi.c中,該驅動的註冊目的是初始化OMAP3630的SPI 控制器,提供SPI總線數據傳輸的具體實現,並且向spi core層註冊spi 控制器。

4.2.1 Platform driver的定義

       在文件drivers/spi/omap2_mcspi.c中,platform driver定義如下:
  1. <span style="font-size:18px;">static struct platform_driver omap2_mcspi_driver = {  
  2.     .driver = {  
  3.         .name =     "omap2_mcspi",  
  4.         .owner =    THIS_MODULE,  
  5.     },  
  6.     .remove =   __exit_p(omap2_mcspi_remove),  
  7. };</span>  
       可以看到,這裏的name字段與4.1.1中定義的Platform device的name字段相同,用於驅動和設備匹配時使用。

4.2.2 Platform driver的註冊

       在文件drivers/spi/omap2_mcspi.c中,platform driver註冊如下:
  1. <span style="font-size:18px;">static int __init omap2_mcspi_init(void)  
  2. {  
  3.     omap2_mcspi_wq = create_singlethread_workqueue(  
  4.                 omap2_mcspi_driver.driver.name);  
  5.     if (omap2_mcspi_wq == NULL)  
  6.         return -1;  
  7.     return platform_driver_probe(&omap2_mcspi_driver, omap2_mcspi_probe);  
  8. }  
  9. subsys_initcall(omap2_mcspi_init); </span>  
        在內核啓動時會調用函數omap2_mcspi_init(),該函數先創建一個名字爲"omap2_mcspi"的workqueue,第二章中提到過spi的數據傳輸是基於workqueue的,所以這邊的初始化函數會首先創建一個workqueue。
        Workqueue創建成功後纔會用函數platform_driver_probe()來註冊Platform driver,該函數是帶有probe函數的平臺驅動註冊函數,用於非hot-plug設備。
        註冊時,會掃描platform bus上的所有設備,由於匹配因子name爲" omap2_mcspi ",因此與4.1.1節中註冊的Platform device匹配成功,於是函數omap2_mcspi_probe()將被調用,控制器device和driver將被綁定起來。
        在文件drivers/spi/omap2_mcspi.c中會涉及到一個數據結構omap2_mcspi,這個結構定義了專門針對omap3630的SPI控制器結構,代碼如下:

  1. <span style="font-size:18px;">struct omap2_mcspi {  
  2.     struct work_struct  work;  
  3.     /* lock protects queue and registers */  
  4.     spinlock_t      lock;  
  5.     struct list_head    msg_queue;  
  6.     struct spi_master   *master;  
  7.     struct clk      *ick;  
  8.     struct clk      *fck;  
  9.     /* Virtual base address of the controller */  
  10.     void __iomem        *base;  
  11.     unsigned long       phys;  
  12.     /* SPI1 has 4 channels, while SPI2 has 2 */  
  13.     struct omap2_mcspi_dma  *dma_channels;  
  14. };</span>  
       msg_queue對應消息隊列。
       master對應通用的spi控制器結構。
       ick和fck分別對應接口時鐘和功能時鐘。
       base對應SPI控制器寄存器的虛擬地址。
       phys對應SPI控制器寄存器的物理地址。
       dma_channels對應SPI控制器的DMA通道。  
      函數omap2_mcspi_probe()的執行流程如下圖:

圖4.1 omap2_mcspi_probe()的執行流程
       函數omap2_mcspi_probe()的簡要代碼如下:
  1. <span style="font-size:18px;">static int __init omap2_mcspi_probe(struct platform_device *pdev)  
  2. {  
  3.     struct spi_master   *master;  
  4.     struct omap2_mcspi  *mcspi;  
  5.     ……  
  6.   
  7.     switch (pdev->id) {  
  8.     case 1:  
  9.         rxdma_id = spi1_rxdma_id;  
  10.         txdma_id = spi1_txdma_id;  
  11.         num_chipselect = 4;  
  12.         break;  
  13.     case 2:  
  14.         ……  
  15. #if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP3)  
  16.     case 3:  
  17.         ……  
  18. #endif  
  19. #ifdef CONFIG_ARCH_OMAP3  
  20.     case 4:  
  21.         ……  
  22. #endif  
  23.     default:  
  24.         return -EINVAL;  
  25.     }  
  26.   
  27.     master = spi_alloc_master(&pdev->dev, sizeof *mcspi);  
  28.     ……  
  29.     if (pdev->id != -1)  
  30.         master->bus_num = pdev->id;  
  31.   
  32.     master->setup = omap2_mcspi_setup;  
  33.     master->transfer = omap2_mcspi_transfer;  
  34.     master->cleanup = omap2_mcspi_cleanup;  
  35.     master->num_chipselect = num_chipselect;  
  36.   
  37.     dev_set_drvdata(&pdev->dev, master);  
  38.   
  39.     mcspi = spi_master_get_devdata(master);  
  40.     mcspi->master = master;  
  41.   
  42.     r = platform_get_resource(pdev, IORESOURCE_MEM, 0);  
  43.     if (r == NULL) {  
  44.         status = -ENODEV;  
  45.         goto err1;  
  46.     }  
  47.     if (!request_mem_region(r->start, (r->end - r->start) + 1,  
  48.             pdev->dev.bus_id)) {  
  49.         status = -EBUSY;  
  50.         goto err1;  
  51.     }  
  52.   
  53.     mcspi->phys = r->start;  
  54.     mcspi->base = ioremap(r->start, r->end - r->start + 1);  
  55.     ……    
  56.     INIT_WORK(&mcspi->work, omap2_mcspi_work);  
  57.     spin_lock_init(&mcspi->lock);  
  58.     INIT_LIST_HEAD(&mcspi->msg_queue);  
  59.   
  60.     mcspi->ick = clk_get(&pdev->dev, "mcspi_ick");  
  61.    ……  
  62.     mcspi->fck = clk_get(&pdev->dev, "mcspi_fck");  
  63.     ……  
  64.     mcspi->dma_channels = kcalloc(master->num_chipselect,  
  65.             sizeof(struct omap2_mcspi_dma),  
  66.             GFP_KERNEL);  
  67.     ……  
  68.     for (i = 0; i < num_chipselect; i++) {  
  69.         mcspi->dma_channels[i].dma_rx_channel = -1;  
  70.         mcspi->dma_channels[i].dma_rx_sync_dev = rxdma_id[i];  
  71.         mcspi->dma_channels[i].dma_tx_channel = -1;  
  72.         mcspi->dma_channels[i].dma_tx_sync_dev = txdma_id[i];  
  73.     }  
  74.     ……  
  75.     status = spi_register_master(master);  
  76.     ……  
  77.     return status;  
  78.     ……  
  79. }</span>  
        可以看到,omap2_mcspi_probe()函數會給spi_master的賦setup,transfer等初始化以及數據傳輸函數,爲了能提高spi的傳輸速度,一般會採用DMA模式,最後使用了spi_register_master ()將spi控制器註冊到spi core層,在4.1.1節中定義了4個SPI總線設備,所以這裏會建立4個spi控制器,總線號分別爲1,2,3,4。

5 OMAP3630 SPI 設備驅動

       在Linux內核中,SPI 設備可以是各種SPI接口的設備,設備可以有不同的用途,這裏以Ti的觸摸屏控制器芯片tsc2005爲例來介紹SPI設備驅動的註冊過程,對應的設備驅動程序爲tsc2005.c,位於目錄drivers/input/touchscreen下。

5.1 Tsc2005設備註冊

       Tsc2005設備的創建以及註冊分爲兩步。

5.1.1 將tsc2005設備信息加入到設備鏈表

       在板級初始化時將tsc2005的名稱,地址和相關的信息加入到鏈表board_list中,該鏈表定義在driver/spi/spi.c中,記錄了具體開發板上的SPI設備信息。
  1. <span style="font-size:18px;">static LIST_HEAD(board_list);</span>  
        在board-xxxx.c中,tsc2005設備信息定義如下:
  1. <span style="font-size:18px;">static struct spi_board_info xxxx_spi_board_info[] __initdata = {  
  2. ……  
  3.     {  
  4.      .modalias = "tsc2005",  
  5.      .platform_data = &tsc2005_config,  
  6.      .controller_data = &touchscreen_spi_cfg,  
  7.      .mode = SPI_MODE_1,  
  8.      .irq = OMAP_GPIO_IRQ(TS_GPIO),  
  9.      .max_speed_hz = 700000,  
  10.      .bus_num   = 0,  
  11.      .chip_select = 2,  
  12.      },  
  13. };</span>  
       Tsc2005設備信息通過函數spi_register_board_info()加入到鏈表board_list中,代碼如下:
  1. <span style="font-size:18px;">static void __init omap_xxxx_init(void)  
  2. {  
  3.     omap_i2c_init();  
  4.     ……  
  5.     spi_register_board_info(xxxx_spi_board_info,  
  6.                 ARRAY_SIZE(xxxx_spi_board_info));  
  7.     ……  
  8. }</span>  
       Tsc2005加入到設備鏈表board_list的流程圖如下圖:
圖5.1 tsc2005加入到SPI設備鏈表的過程
        spi_register_board_info()函數的定義在driver/spi/spi.c中,如下
  1. <span style="font-size:18px;">int __init spi_register_board_info(struct spi_board_info const *info, unsigned n)  
  2. {  
  3.     struct boardinfo    *bi;  
  4.   
  5.     bi = kmalloc(sizeof(*bi) + n * sizeof *info, GFP_KERNEL);  
  6.     if (!bi)  
  7.         return -ENOMEM;  
  8.     bi->n_board_info = n;  
  9.     memcpy(bi->board_info, info, n * sizeof *info);  
  10.   
  11.     mutex_lock(&board_lock);  
  12.     list_add_tail(&bi->list, &board_list);  
  13.     mutex_unlock(&board_lock);  
  14.     return 0;  
  15. }</span>  

5.1.2 Tsc2005 spi_device的創建並添加

        Tsc2005 spi_device的創建並添加在SPI控制器驅動註冊過程中完成,SPI 控制器驅動的註冊可以參考4.2.2節,spi_register_master()函數在註冊SPI控制器驅動的過程會掃描5.1.1中提到的SPI設備鏈表board_list,如果該總線上有對應的SPI設備,則創建相應的 spi_device,並將其添加到SPI的設備鏈表中。流程圖如下所示:
圖5.2創建並添加spi_device
        相應的代碼位於driver/spi/spi.c,如下:
  1. <span style="font-size:18px;">int spi_register_master(struct spi_master *master)  
  2. {  
  3.     static atomic_t     dyn_bus_id = ATOMIC_INIT((1<<15) - 1);  
  4.     struct device       *dev = master->dev.parent;  
  5.     int         status = -ENODEV;  
  6.     int         dynamic = 0;  
  7.     ……  
  8.     status = device_add(&master->dev);  
  9.     ……  
  10.     scan_boardinfo(master);  
  11.     ……  
  12. }</span>  
       函數spi_register_master()調用scan_boardinfo()函數掃描板級的設備。
  1. <span style="font-size:18px;">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.   
  10.         for (n = bi->n_board_info; n > 0; n--, chip++) {  
  11.             if (chip->bus_num != master->bus_num)  
  12.                 continue;  
  13.             /* NOTE: this relies on spi_new_device to 
  14.              * issue diagnostics when given bogus inputs 
  15.              */  
  16.             (void) spi_new_device(master, chip);  
  17.         }  
  18.     }  
  19.     mutex_unlock(&board_lock);  
  20. }</span>  
        在scan_boardinfo()函數中遍歷SPI設備鏈表board_list,設備的總線號和控制器的總線號相等,則使用函數spi_new_device()創建該設備。
  1. <span style="font-size:18px;">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.     if (status < 0) {  
  20.         spi_dev_put(proxy);  
  21.         return NULL;  
  22.     }  
  23.   
  24.     return proxy;  
  25. }</span>  
       在函數spi_new_device ()中使用函數spi_alloc_device()創建一個spi_device,spi_alloc_device()代碼如下:
  1. <span style="font-size:18px;">struct spi_device *spi_alloc_device(struct spi_master *master)  
  2. {  
  3.     struct spi_device   *spi;  
  4.     struct device       *dev = master->dev.parent;  
  5.     if (!spi_master_get(master))  
  6.         return NULL;  
  7.     spi = kzalloc(sizeof *spi, GFP_KERNEL);  
  8.     ……  
  9.     spi->master = master;  
  10.     spi->dev.parent = dev;  
  11.     spi->dev.bus = &spi_bus_type;  
  12.     spi->dev.release = spidev_release;  
  13.     device_initialize(&spi->dev);  
  14.     return spi;  
  15. }</span>  
       首先爲spi_device分配內存空間,接着初始化該結構體,將該設備的bus初始化爲spi_bus_type,說明該設備將被掛接到SPI總線上,方便與spi driver匹配,初始化master爲當前的SPI控制器。
       從spi_alloc_device()函數返回後繼續初始化spi_device結構體的chip_select,mode,modalias, controller_data等變量,這裏的proxy->modalias被初始化爲chip->modalias,在5.1.1中,chip->modalias初始化爲“tsc2005”,proxy->modalias後面會用於spi device和spi driver匹配時使用,最後調用spi_add_device ()將該SPI設備proxy添加到SPI設備鏈表中。

  1. <span style="font-size:18px;">int spi_add_device(struct spi_device *spi)  
  2. {  
  3.     static DEFINE_MUTEX(spi_add_lock);  
  4.     struct device *dev = spi->master->dev.parent;  
  5.     int status;  
  6.     ……  
  7.     if (bus_find_device_by_name(&spi_bus_type, NULL, dev_name(&spi->dev))  
  8.             != NULL) {  
  9.         dev_err(dev, "chipselect %d already in use\n",  
  10.                 spi->chip_select);  
  11.         status = -EBUSY;  
  12.         goto done;  
  13.     }  
  14.     ……  
  15.     status = spi->master->setup(spi);  
  16.     ……  
  17.     /* Device may be bound to an active driver when this returns */  
  18.     status = device_add(&spi->dev);  
  19.     …….  
  20.  }</span>  
        函數spi_add_device ()進一步初始化SPI設備proxy,驗證在spi_bus_type是否已存在該設備,如果沒有則初始化該設備所在的spi控制器,最後使用device_add(&spi->dev)添加該SPI設備proxy到SPI設備鏈表中。

5.2 Tsc2005設備驅動註冊

      在driver/input/touchscreen/tsc2005.c中,定義了tsc2005的設備驅動,代碼如下:
  1. <span style="font-size:18px;">static struct spi_driver tsc2005_driver = {  
  2.     .driver = {  
  3.         .name = "tsc2005",  
  4.         .owner = THIS_MODULE,  
  5.     },  
  6. #ifdef CONFIG_PM  
  7.     .suspend = tsc2005_suspend,  
  8.     .resume = tsc2005_resume,  
  9. #endif  
  10.     .probe = tsc2005_probe,  
  11.     .remove = __devexit_p(tsc2005_remove),  
  12. };</span>  
        註冊的簡要示意圖如下:
圖5.3 tsc2005設備驅動的註冊
       相應的代碼位於driver/input/touchscreen/tsc2005.c和driver/spi/spi.c。
  1. <span style="font-size:18px;">static int __init tsc2005_init(void)  
  2. {  
  3.     printk(KERN_INFO "TSC2005 driver initializing\n");  
  4.   
  5.     return spi_register_driver(&tsc2005_driver);  
  6. }  
  7. module_init(tsc2005_init);</span>  
      在模塊加載的時候首先調用tsc2005_init(),然後tsc2005_init()調用函數spi_register_driver()註冊tsc2005_driver結構體。
  1. <span style="font-size:18px;">int spi_register_driver(struct spi_driver *sdrv)  
  2. {  
  3.     sdrv->driver.bus = &spi_bus_type;  
  4.     if (sdrv->probe)  
  5.         sdrv->driver.probe = spi_drv_probe;  
  6.     if (sdrv->remove)  
  7.         sdrv->driver.remove = spi_drv_remove;  
  8.     if (sdrv->shutdown)  
  9.         sdrv->driver.shutdown = spi_drv_shutdown;  
  10.     return driver_register(&sdrv->driver);  
  11. </span>  
       函數spi_register_driver()初始化該驅動的總線爲spi_bus_type,然後使用函數driver_register(&sdrv->driver)註冊該驅動,因此內核會在SPI總線上遍歷所有SPI設備,由於該tsc2005 設備驅動的匹配因子name變量爲“tsc2005”,因此正好和在5.1.2裏創建的chip->modalias爲“tsc2005”的SPI 設備匹配。因此SPI設備驅動的probe函數tsc2005_probe()將會被調用,具體代碼如下:
  1. <span style="font-size:18px;">static int __devinit tsc2005_probe(struct spi_device *spi)  
  2. {  
  3.     struct tsc2005          *tsc;  
  4.     struct tsc2005_platform_data    *pdata = spi->dev.platform_data;  
  5.     int r;  
  6.     ……  
  7.     tsc = kzalloc(sizeof(*tsc), GFP_KERNEL);  
  8.     ……  
  9.     dev_set_drvdata(&spi->dev, tsc);  
  10.     tsc->spi = spi;  
  11.     spi->dev.power.power_state = PMSG_ON;  
  12.     spi->mode = SPI_MODE_0;  
  13.     spi->bits_per_word = 8;  
  14.     ……  
  15.     spi_setup(spi);  
  16.     r = tsc2005_ts_init(tsc, pdata);  
  17.     ……  
  18. }</span>  

       在tsc2005_probe()函數中,完成一些具體tsc2005設備相關的初始化等操作,這邊就不再詳述。

6 用戶空間的支持

       和I2C一樣,SPI也具有一個通用的設備的動程序,通過一個帶有操作集file_operations的標準字符設備驅動爲用戶空間提供了訪問接口。
       此驅動模型是針對SPI設備的,只有註冊board info時modalias是”spidev”的才能由此驅動訪問。訪問各個slave的基本框架是一致的,具體的差異將由從設備號來體現,代碼部分位於drivers/spi/spidev.c。

6.1 Spidev的註冊

       在drivers/spi/spidev.c中,首先定義了SPI設備驅動spidev_spi:
  1. <span style="font-size:18px;">static struct spi_driver spidev_spi = {  
  2.     .driver = {  
  3.         .name =     "spidev",  
  4.         .owner =    THIS_MODULE,  
  5.     },  
  6.     .probe =    spidev_probe,  
  7.     .remove =   __devexit_p(spidev_remove),  
  8. };</span>  
      spidev_spi註冊代碼如下:
  1. <span style="font-size:18px;">static int __init spidev_init(void)  
  2. {  
  3.     int status;  
  4.     ……  
  5.     status = register_chrdev(SPIDEV_MAJOR, "spi", &spidev_fops);  
  6.     if (status < 0)  
  7.         return status;  
  8.   
  9.     spidev_class = class_create(THIS_MODULE, "spidev");  
  10.     ……  
  11.     status = spi_register_driver(&spidev_spi);  
  12.     ……  
  13. }  
  14. module_init(spidev_init);</span>  
       首先註冊了一個主設備號爲SPIDEV_MAJOR,操作集爲spidev_fops,名字爲“spi”的字符設備。在文件drivers/spi/spidev.c中,SPI_MAJOR被定義爲153。在spidev.c中spidev_fops的定義如下:
  1. <span style="font-size:18px;">static struct file_operations spidev_fops = {  
  2.     .owner =    THIS_MODULE,  
  3.     /* REVISIT switch to aio primitives, so that userspace  
  4.      * gets more complete API coverage.  It'll simplify things  
  5.      * too, except for the locking.  
  6.      */  
  7.     .write =    spidev_write,  
  8.     .read =     spidev_read,  
  9.     .unlocked_ioctl = spidev_ioctl,  
  10.     .open =     spidev_open,  
  11.     .release =  spidev_release,  
  12. };</span>  
        該操作集是用戶空間訪問該字符設備的接口。
        然後調用函數spi_register_driver(&spidev_spi)將spidev_spi 驅動註冊到SPI 核心層中,spidev_spi驅動註冊過程中會掃描SPI總線上的所有SPI設備,一旦掃描到關鍵字modalias爲”spidev”的SPI設備時,則與該SPI驅動匹配,匹配函數spidev_probe()被調用,具體的註冊流程圖如下:

圖5.1 SPIdev_driver註冊過程
       spidev_probe ()函數的代碼如下:
  1. <span style="font-size:18px;">static int spidev_probe(struct spi_device *spi)  
  2. {  
  3.     struct spidev_data  *spidev;  
  4.     int         status;  
  5.     unsigned long       minor;  
  6.   
  7.     /* Allocate driver data */  
  8.     spidev = kzalloc(sizeof(*spidev), GFP_KERNEL);  
  9.     if (!spidev)  
  10.         return -ENOMEM;  
  11.   
  12.     /* Initialize the driver data */  
  13.     spidev->spi = spi;  
  14.     ……  
  15.     minor = find_first_zero_bit(minors, N_SPI_MINORS);  
  16.     if (minor < N_SPI_MINORS) {  
  17.         struct device *dev;  
  18.   
  19.         spidev->devt = MKDEV(SPIDEV_MAJOR, minor);  
  20.         dev = device_create(spidev_class, &spi->dev, spidev->devt,  
  21.                     spidev, "spidev%d.%d",  
  22.                     spi->master->bus_num, spi->chip_select);  
  23.         status = IS_ERR(dev) ? PTR_ERR(dev) : 0;  
  24.     } else {  
  25.         dev_dbg(&spi->dev, "no minor number available!\n");  
  26.         status = -ENODEV;  
  27.     }  
  28.     if (status == 0) {  
  29.         set_bit(minor, minors);  
  30.         list_add(&spidev->device_entry, &device_list);  
  31.     }  
  32.     …….  
  33. }</span>  
        在匹配函數spidev_probe()中,會首先爲spidev_data結構體分配內存空間,該結構體定義如下:
  1. <span style="font-size:18px;">struct spidev_data {  
  2.     dev_t           devt;  
  3.     spinlock_t      spi_lock;  
  4.     struct spi_device   *spi;  
  5.     struct list_head    device_entry;  
  6.     /* buffer is NULL unless this device is open (users > 0) */  
  7.     struct mutex        buf_lock;  
  8.     unsigned        users;  
  9.     u8          *buffer;  
  10. };</span>  
       結構體中包含了spi_device結構體,device_entry鏈表頭等成員變量。
       分配完spidev_data結構體後對其進行初始化,然後創建spi設備,成功後將該spidev_data添加到device_list鏈表中,該鏈表維護這些modalias爲”spidev”的SPI設備。device_list鏈表定義在spidev.c中如下:

  1. <span style="font-size:18px;">static LIST_HEAD(device_list);</span>  
       這些SPI設備以SPIDEV_MAJOR爲主設備號,以函數find_first_zero_bit()的返回值爲從設備號創建並註冊設備節點。如果系統有udev或者是hotplug,那麼就會在/dev下自動創建相關的設備節點了,其名稱命名方式爲spidevB.C,B爲總線編號,C爲片選序號。

6.2 Spidev的打開

      Spidev的open函數如下:
  1. <span style="font-size:18px;">static int spidev_open(struct inode *inode, struct file *filp)  
  2. {  
  3.     struct spidev_data  *spidev;  
  4.     int         status = -ENXIO;  
  5.   
  6.     lock_kernel();  
  7.     mutex_lock(&device_list_lock);  
  8.   
  9.     list_for_each_entry(spidev, &device_list, device_entry) {  
  10.         if (spidev->devt == inode->i_rdev) {  
  11.             status = 0;  
  12.             break;  
  13.         }  
  14.     }  
  15.     if (status == 0) {  
  16.         if (!spidev->buffer) {  
  17.             spidev->buffer = kmalloc(bufsiz, GFP_KERNEL);  
  18.             if (!spidev->buffer) {  
  19.                 dev_dbg(&spidev->spi->dev, "open/ENOMEM\n");  
  20.                 status = -ENOMEM;  
  21.             }  
  22.         }  
  23.         if (status == 0) {  
  24.             spidev->users++;  
  25.             filp->private_data = spidev;  
  26.             nonseekable_open(inode, filp);  
  27.         }  
  28.     } else  
  29.         pr_debug("spidev: nothing for minor %d\n", iminor(inode));  
  30.   
  31.     mutex_unlock(&device_list_lock);  
  32.     unlock_kernel();  
  33.     return status;  
  34. }  
  35. </span>  
       Open操作是用戶空間程序和內核驅動交互的第一步,首先會遍歷device_list鏈表,然後根據設備節點號進行匹配查找對應的spidev_data節點從而找到相應的從設備,然後給spidev分配buffer空間,最後filp的private_data被賦值爲spidev,而最終返回給用戶空間的就是這個struct file結構體。對於SPI 驅動來說,用戶空間所獲得的就是spidev這個關鍵信息,在其中可以找到所有有關的信息如spidev的SPI設備。
       用open函數將spidev設備打開以後,就可以通過ioctl函數的各種命令來對modalias爲”spidev”的SPI設備進行讀寫等操作,也可以通過 read和write函數完成對SPI設備的讀寫。
       對SPI設備的具體操作在這裏不再具體闡述,可以參看spidev.c源代碼。

7 SPI數據收發

       第二章的時候的時候已經講到過,SPI的通信是通過消息隊列機制也就是workqueue,核心思想就是要構造一個spi_message。

7.1 SPI數據收發數據結構

             SPI數據收發用到的結構體主要有兩個,spi_message和spi_transfer,兩者都定義在include/linux/spi/spi.h中。
spi_message定義如下:

  1. <span style="font-size:18px;">/** 
  2.  * struct spi_message - one multi-segment SPI transaction 
  3.  * @transfers: list of transfer segments in this transaction 
  4.  * @spi: SPI device to which the transaction is queued 
  5.  * @is_dma_mapped: if true, the caller provided both dma and cpu virtual 
  6.  *  addresses for each transfer buffer 
  7.  * @complete: called to report transaction completions 
  8.  * @context: the argument to complete() when it's called 
  9.  * @actual_length: the total number of bytes that were transferred in all 
  10.  *  successful segments 
  11.  * @status: zero for success, else negative errno 
  12.  * @queue: for use by whichever driver currently owns the message 
  13.  * @state: for use by whichever driver currently owns the message 
  14.  * 
  15.  * A @spi_message is used to execute an atomic sequence of data transfers, 
  16.  * each represented by a struct spi_transfer.  The sequence is "atomic" 
  17.  * in the sense that no other spi_message may use that SPI bus until that 
  18.  * sequence completes.  On some systems, many such sequences can execute as 
  19.  * as single programmed DMA transfer.  On all systems, these messages are 
  20.  * queued, and might complete after transactions to other devices.  Messages 
  21.  * sent to a given spi_device are alway executed in FIFO order. 
  22.  * 
  23.  * The code that submits an spi_message (and its spi_transfers) 
  24.  * to the lower layers is responsible for managing its memory. 
  25.  * Zero-initialize every field you don't set up explicitly, to 
  26.  * insulate against future API updates.  After you submit a message 
  27.  * and its transfers, ignore them until its completion callback. 
  28.  */  
  29. struct spi_message {  
  30.     struct list_head    transfers;  
  31.     struct spi_device   *spi;  
  32.     unsigned        is_dma_mapped:1;  
  33.     /* REVISIT:  we might want a flag affecting the behavior of the 
  34.      * last transfer ... allowing things like "read 16 bit length L" 
  35.      * immediately followed by "read L bytes".  Basically imposing 
  36.      * a specific message scheduling algorithm. 
  37.      * 
  38.      * Some controller drivers (message-at-a-time queue processing) 
  39.      * could provide that as their default scheduling algorithm.  But 
  40.      * others (with multi-message pipelines) could need a flag to 
  41.      * tell them about such special cases. 
  42.      */  
  43.     /* completion is reported through a callback */  
  44.     void            (*complete)(void *context);  
  45.     void            *context;  
  46.     unsigned        actual_length;  
  47.     int         status;  
  48.     /* for optional use by whatever driver currently owns the 
  49.      * spi_message ...  between calls to spi_async and then later 
  50.      * complete(), that's the spi_master controller driver. 
  51.      */  
  52.     struct list_head    queue;  
  53.     void            *state;  
  54. };</span><span style="font-size:18px;">  
  55. </span>  
        spi_message 包含了一系列spi_transfer,在所有的spi_transfer發送完畢前,SPI總線一直被佔據,其間不會出現總線衝突,因此一個spi_message是一個原子系列,這種特性非常利於複雜的SPI通信協議,如先發送命令字然後才能讀取數據。所有的spi_message都按照FIFO順序執行。
       還定義了需要發送的SPI設備,上下文信息context和傳輸完成後需要的回調函數complete等。
       spi_transfer結構體的定義如下:

  1. <span style="font-size:18px;">/** 
  2.  * struct spi_transfer - a read/write buffer pair 
  3.  * @tx_buf: data to be written (dma-safe memory), or NULL 
  4.  * @rx_buf: data to be read (dma-safe memory), or NULL 
  5.  * @tx_dma: DMA address of tx_buf, if @spi_message.is_dma_mapped 
  6.  * @rx_dma: DMA address of rx_buf, if @spi_message.is_dma_mapped 
  7.  * @len: size of rx and tx buffers (in bytes) 
  8.  * @speed_hz: Select a speed other than the device default for this 
  9.  *      transfer. If 0 the default (from @spi_device) is used. 
  10.  * @bits_per_word: select a bits_per_word other than the device default 
  11.  *      for this transfer. If 0 the default (from @spi_device) is used. 
  12.  * @cs_change: affects chipselect after this transfer completes 
  13.  * @delay_usecs: microseconds to delay after this transfer before 
  14.  *  (optionally) changing the chipselect status, then starting 
  15.  *  the next transfer or completing this @spi_message. 
  16.  * @transfer_list: transfers are sequenced through @spi_message.transfers 
  17.  * 
  18.  * SPI transfers always write the same number of bytes as they read. 
  19.  * Protocol drivers should always provide @rx_buf and/or @tx_buf. 
  20.  * In some cases, they may also want to provide DMA addresses for 
  21.  * the data being transferred; that may reduce overhead, when the 
  22.  * underlying driver uses dma. 
  23.  * 
  24.  * If the transmit buffer is null, zeroes will be shifted out 
  25.  * while filling @rx_buf.  If the receive buffer is null, the data 
  26.  * shifted in will be discarded.  Only "len" bytes shift out (or in). 
  27.  * It's an error to try to shift out a partial word.  (For example, by 
  28.  * shifting out three bytes with word size of sixteen or twenty bits; 
  29.  * the former uses two bytes per word, the latter uses four bytes.) 
  30.  * 
  31.  * In-memory data values are always in native CPU byte order, translated 
  32.  * from the wire byte order (big-endian except with SPI_LSB_FIRST).  So 
  33.  * for example when bits_per_word is sixteen, buffers are 2N bytes long 
  34.  * (@len = 2N) and hold N sixteen bit words in CPU byte order. 
  35.  * 
  36.  * When the word size of the SPI transfer is not a power-of-two multiple 
  37.  * of eight bits, those in-memory words include extra bits.  In-memory 
  38.  * words are always seen by protocol drivers as right-justified, so the 
  39.  * undefined (rx) or unused (tx) bits are always the most significant bits. 
  40.  * 
  41.  * All SPI transfers start with the relevant chipselect active.  Normally 
  42.  * it stays selected until after the last transfer in a message.  Drivers 
  43.  * can affect the chipselect signal using cs_change. 
  44.  * 
  45.  * (i) If the transfer isn't the last one in the message, this flag is 
  46.  * used to make the chipselect briefly go inactive in the middle of the 
  47.  * message.  Toggling chipselect in this way may be needed to terminate 
  48.  * a chip command, letting a single spi_message perform all of group of 
  49.  * chip transactions together. 
  50.  * 
  51.  * (ii) When the transfer is the last one in the message, the chip may 
  52.  * stay selected until the next transfer.  On multi-device SPI busses 
  53.  * with nothing blocking messages going to other devices, this is just 
  54.  * a performance hint; starting a message to another device deselects 
  55.  * this one.  But in other cases, this can be used to ensure correctness. 
  56.  * Some devices need protocol transactions to be built from a series of 
  57.  * spi_message submissions, where the content of one message is determined 
  58.  * by the results of previous messages and where the whole transaction 
  59.  * ends when the chipselect goes intactive. 
  60.  * 
  61.  * The code that submits an spi_message (and its spi_transfers) 
  62.  * to the lower layers is responsible for managing its memory. 
  63.  * Zero-initialize every field you don't set up explicitly, to 
  64.  * insulate against future API updates.  After you submit a message 
  65.  * and its transfers, ignore them until its completion callback. 
  66.  */  
  67. struct spi_transfer {  
  68.     /* it's ok if tx_buf == rx_buf (right?) 
  69.      * for MicroWire, one buffer must be null 
  70.      * buffers must work with dma_*map_single() calls, unless 
  71.      *   spi_message.is_dma_mapped reports a pre-existing mapping 
  72.      */  
  73.     const void  *tx_buf;  
  74.     void        *rx_buf;  
  75.     unsigned    len;  
  76.   
  77.     dma_addr_t  tx_dma;  
  78.     dma_addr_t  rx_dma;  
  79.   
  80.     unsigned    cs_change:1;  
  81.     u8      bits_per_word;  
  82.     u16     delay_usecs;  
  83.     u32     speed_hz;  
  84.   
  85.     struct list_head transfer_list;  
  86. };</span><span style="font-size:18px;">  
  87. </span>  
        一個spi_transfer是以某種特性如速率、延時及字長連續傳輸的最小單位。因爲SPI是全雙工總線,只要總線上有數據傳輸,則MOSI和MISO上同時有數據採樣,對於SPI 控制器來說,其收發緩衝區中都有數據,但是對於SPI設備來說,其可以靈活的選擇tx_buf和rx_buf的設置。當發送數據時,tx_buf必須設置爲待發送的數據,rx_buf可以爲null或者指向無用的緩衝區,即不關心MISO的數據。而當接收數據時,rx_buf必須指向接收緩衝區,而tx_buf可以爲Null,則MOSI上爲全0,或者tx_buf指向不會引起從設備異常相應的數據,len爲待發送或者接收的數據長度,當然當採用DMAf方式傳輸時,必須初始化相應的DMA地址。當一系列spi_transfer構成一個spi_message時,cs_change的設置非常講究。當cs_change爲0即不變時,則可以連續對同一個設備進行數據收發;當cs_change爲1時通常用來停止command的傳輸而隨後進行數據接收。因此cs_change可以利用SPI總線構造複雜的通信協議。
        對於SPI總線協議來說,傳輸單位可以是4-32之間的任意bits,但對於SPI控制器來說,bits_per_word只能是8/16/32,即需要取整,待收發的數據在內存裏都是以主機字節序右對齊存儲,SPI控制器會自動根據傳輸單位及大小端進行轉換。

7.2 SPI數據收發的函數

       SPI數據收發的函數主要有spi_async()和spi_sync()。
       函數spi_async()定義在include/linux/spi/spi.h中,

  1. <span style="font-size:18px;">/** 
  2.  * spi_async - asynchronous SPI transfer 
  3.  * @spi: device with which data will be exchanged 
  4.  * @message: describes the data transfers, including completion callback 
  5.  * Context: any (irqs may be blocked, etc) 
  6.  * 
  7.  * This call may be used in_irq and other contexts which can't sleep, 
  8.  * as well as from task contexts which can sleep. 
  9.  * 
  10.  * The completion callback is invoked in a context which can't sleep. 
  11.  * Before that invocation, the value of message->status is undefined. 
  12.  * When the callback is issued, message->status holds either zero (to 
  13.  * indicate complete success) or a negative error code.  After that 
  14.  * callback returns, the driver which issued the transfer request may 
  15.  * deallocate the associated memory; it's no longer in use by any SPI 
  16.  * core or controller driver code. 
  17.  * 
  18.  * Note that although all messages to a spi_device are handled in 
  19.  * FIFO order, messages may go to different devices in other orders. 
  20.  * Some device might be higher priority, or have various "hard" access 
  21.  * time requirements, for example. 
  22.  * 
  23.  * On detection of any fault during the transfer, processing of 
  24.  * the entire message is aborted, and the device is deselected. 
  25.  * Until returning from the associated message completion callback, 
  26.  * no other spi_message queued to that device will be processed. 
  27.  * (This rule applies equally to all the synchronous transfer calls, 
  28.  * which are wrappers around this core asynchronous primitive.) 
  29.  */  
  30. static inline int  
  31. spi_async(struct spi_device *spi, struct spi_message *message)  
  32. {  
  33.     message->spi = spi;  
  34.     return spi->master->transfer(spi, message);  
  35. }</span><span style="font-family:Arial, Verdana, sans-serif;"><span style="white-space: normal;font-size:18px;">  
  36. </span></span>  

        該函數是異步的SPI傳輸函數,可以用於在中端或者某些不能睡眠的上下文場合,當然也可以用於睡眠的場合。

        對於發送給同一個SPI設備的message遵循FIFO的順序,發送給不同的SPI設備的message可以有不同的順序。

        直到從回調函數返回之前,都不能處理該設備的其他spi_message。
        從源代碼可以看到,該函數最後調用了發送給相應設備的相應控制器的transfer函數。
        spi_sync()函數定義在driver/spi/spi.c中,

  1. <span style="font-size:18px;">/** 
  2.  * spi_sync - blocking/synchronous SPI data transfers 
  3.  * @spi: device with which data will be exchanged 
  4.  * @message: describes the data transfers 
  5.  * Context: can sleep 
  6.  * 
  7.  * This call may only be used from a context that may sleep.  The sleep 
  8.  * is non-interruptible, and has no timeout.  Low-overhead controller 
  9.  * drivers may DMA directly into and out of the message buffers. 
  10.  * 
  11.  * Note that the SPI device's chip select is active during the message, 
  12.  * and then is normally disabled between messages.  Drivers for some 
  13.  * frequently-used devices may want to minimize costs of selecting a chip, 
  14.  * by leaving it selected in anticipation that the next message will go 
  15.  * to the same chip.  (That may increase power usage.) 
  16.  * 
  17.  * Also, the caller is guaranteeing that the memory associated with the 
  18.  * message will not be freed before this call returns. 
  19.  * 
  20.  * It returns zero on success, else a negative error code. 
  21.  */  
  22. int spi_sync(struct spi_device *spi, struct spi_message *message)  
  23. {  
  24.     DECLARE_COMPLETION_ONSTACK(done);  
  25.     int status;  
  26.   
  27.     message->complete = spi_complete;  
  28.     message->context = &done;  
  29.     status = spi_async(spi, message);  
  30.     if (status == 0) {  
  31.         wait_for_completion(&done);  
  32.         status = message->status;  
  33.     }  
  34.     message->context = NULL;  
  35.     return status;  
  36. }</span><span style="font-size:18px;">  
  37. </span>  
        該函數是同步的SPI傳輸函數,只能用於可以睡眠的場合。

7.3 SPI數據傳輸example

        下面是tsc2005進行SPI數據傳輸的例子,
  1. <span style="font-size:18px;">static void tsc2005_write(struct tsc2005 *ts, u8 reg, u16 value)  
  2. {  
  3.     u32 tx;  
  4.     struct spi_message msg;  
  5.     struct spi_transfer xfer = { 0 };  
  6.     tx = (TSC2005_REG | reg | TSC2005_REG_PND0 |  
  7.            TSC2005_REG_WRITE) << 16;  
  8.     tx |= value;  
  9.   
  10.     xfer.tx_buf = &tx;  
  11.     xfer.rx_buf = NULL;  
  12.     xfer.len = 4;  
  13.     xfer.bits_per_word = 24;  
  14.   
  15.     spi_message_init(&msg);  
  16.     spi_message_add_tail(&xfer, &msg);  
  17.     spi_sync(ts->spi, &msg);  
  18. }</span><span style="font-size:18px;">  
  19. </span>  

        該函數首先定義SPI 傳輸所需的spi_message和spi_transfer數據結構,初始化他們,然後將spi_transfer添加到spi_message的transfer_list鏈表中,最後調用spi_sync()將SPI數據發送出去


429人閱讀 評論(0) 收藏 舉報
在移植內核的時候,通常會遇到引腳複用(MUX)的配置問題。在現在的Linux內核中,對於TI的ARM芯片,早已經有了比較通用的MUX配置框架。這對於許多TI的芯片都是通用的,這次看AM335X的代碼順手寫一下分析,以備後用。
一、硬件
    對於許多TI的芯片來說,引腳複用的配置是在Control Module(配置模塊)的寄存器裏配置的,(這個和三星的CPU有點不同,三星的一般在GPIO的寄存器中配置)。所以當你需要配置這些寄存器的時候,請到數據手冊的Control Module的Pad Control Registers查找。

  1. TI的CPU芯片手冊有兩種:
  2. 一種是datasheet(DS:數據手冊),較小,只是大概介紹下芯片的結構;
  3. 另一種是Technical Reference Manual(TRM:技術參考手冊),較大,詳細介紹芯片的各部分功能原理和寄存器定義。
  4. 在開發過程中,這兩個手冊都需要參考,是互補的。

對於AM335X,關於引腳複用的列表及模式號與功能的對應可以在數據手冊中找到:
2 Terminal Description:
2.2 Ball Characteristics

關於引腳複用寄存器定義及各引腳相應寄存器的偏移可以在TRM中找到:
9 Control Module
9.1 Control Module
9.1.3 Functional Description
9.1.3.2 Pad Control Registers (包含引腳複用寄存器定義)
9.1.5 Registers
9.1.5.1 CONTROL_MODULE Registers (包含引腳相應寄存器的偏移)

二、軟件
    由於TI的芯片構架類似,對於Linux內核來說,早就已經爲這個做好了一個軟件上的框架,無論是在啓動的初始化階段還是在系統運行時,都可以通過這個框架提供的接口函數配置芯片的MUX。下面就來簡要的分析一下。
以AM335X爲例,相關代碼位置:arch/arm/mach-omap2
  1. mux.h
  2. mux.c
  3. mux33xx.h
  4. mux33xx.c
  5. board-am335xevm.c

  6. (還有一些用到了:arch/arm/plat-omap/include/plat/omap_hwmod.h)

其中他們的層次關係是:


(1)重要的數據結構
  1. /**
  2.  * struct mux_partition - 包含分區相關信息
  3.  * @name: 當前分區名
  4.  * @flags: 本分區的特定標誌
  5.  * @phys: 物理地址
  6.  * @size: 分區大小
  7.  * @base: ioremap 映射過的虛擬地址
  8.  * @muxmodes: 本分區mux節點鏈表頭
  9.  * @node: 分區鏈表頭
  10.  */
  11. struct omap_mux_partition {
  12.     const char        *name;
  13.     u32            flags;
  14.     u32            phys;
  15.     u32            size;
  16.     void __iomem        *base;
  17.     struct list_head    muxmodes;
  18.     struct list_head    node;
  19. };

    這個數據結構中包含了芯片中幾乎所有定義好的mux的數據,它在mux數據初始化函數omap_mux_init中初始化,並添加到全局 mux_partitions鏈表中(通過node成員)。而其中的muxmodes是所有mux信息節點的鏈表頭,用來鏈接以下數據結構:
  1. /**
  2.  * struct omap_mux_entry - mux信息節點
  3.  * @mux: omap_mux結構體
  4.  * @node: 鏈表節點
  5.  */
  6. struct omap_mux_entry {
  7.     struct omap_mux        mux;
  8.     struct list_head    node;
  9. };

而在以上數據結構中,struct omap_mux是記錄單個mux節點數據的結構體:
  1. /**
  2.  * struct omap_mux - omap mux 寄存器偏移和值的數據
  3.  * @reg_offset:    從Control Module寄存器基地址算起的mux寄存器偏移
  4.  * @gpio:    GPIO 編號
  5.  * @muxnames:    引腳可用的信號模式字符串指針數組
  6.  * @balls:    封裝中可用的引腳
  7.  */
  8. struct omap_mux {
  9.     u16    reg_offset;
  10.     u16    gpio;
  11. #ifdef CONFIG_OMAP_MUX
  12.     char    *muxnames[OMAP_MUX_NR_MODES];
  13. #ifdef CONFIG_DEBUG_FS
  14.     char    *balls[OMAP_MUX_NR_SIDES];
  15. #endif
  16. #endif
  17. };

     而struct mux_partition中muxmodes鏈表及其節點數據的初始化都是在omap_mux_init初始化函數中 (omap_mux_init_list(partition, superset);),而struct omap_mux節點數據中信息是由mux33xx.h和mux33xx.c中提供的。你可以在mux33xx.c中看到一個巨大的struct omap_mux結構體數組初始化代碼,這個代碼一看就明瞭。不同的芯片只需要根據芯片資料修改這個結構體就好了,但是am33xx的這個結構體(當前) 還不完善,gpio的數據還都是0。值得一提的是其中用到了一個宏:
  1. #define _AM33XX_MUXENTRY(M0, g, m0, m1, m2, m3, m4, m5, m6, m7)        \
  2. {                                    \
  3.     .reg_offset    = (AM33XX_CONTROL_PADCONF_##M0##_OFFSET),    \
  4.     .gpio        = (g),                        \
  5.     .muxnames    = { m0, m1, m2, m3, m4, m5, m6, m7 },        \
  6. }
這個宏使得這個結構體數組的初始化變得清晰明瞭。

以 上的數據結構是在系統初始化的時候使用的,在struct omap_mux_partition完成初始化後,omap_mux_init初始化函數最後會根據不同的板子初始化部分mux寄存器 (omap_mux_init_signals(partition, board_mux);),其中牽涉到了以下結構體:
  1. /**
  2.  * struct omap_board_mux - 初始化mux寄存器的數據
  3.  * @reg_offset:    從Control Module寄存器基地址算起的mux寄存器偏移
  4.  * @mux_value:    希望設置的mux寄存器值
  5.  */
  6. struct omap_board_mux {
  7.     u16    reg_offset;
  8.     u16    value;
  9. };

    在最上層的板級初始化文件(board-am335xevm.c)中會定義一個這樣的結構體數組,確定所要初始化的引腳複用寄存器,交由omap_mux_init_signals(partition, board_mux);使用。例如:

  1. #ifdef CONFIG_OMAP_MUX
  2. static struct omap_board_mux board_mux[] __initdata = {
  3.     AM33XX_MUX(I2C0_SDA, OMAP_MUX_MODE0 | AM33XX_SLEWCTRL_SLOW |
  4.             AM33XX_INPUT_EN | AM33XX_PIN_OUTPUT),
  5.     AM33XX_MUX(I2C0_SCL, OMAP_MUX_MODE0 | AM33XX_SLEWCTRL_SLOW |
  6.             AM33XX_INPUT_EN | AM33XX_PIN_OUTPUT),
  7.     { .reg_offset = OMAP_MUX_TERMINATOR },
  8. };
  9. #else
  10. #define    board_mux    NULL
  11. #endif
其中用到了一個宏:
  1. /* 如果引腳沒有定義爲輸入,拉動電阻將會被禁用
  2.  * 如果定義爲輸入,所提供的標誌位將確定拉動電阻的配置
  3.  */
  4. #define AM33XX_MUX(mode0, mux_value)                    \
  5. {                                    \
  6.     .reg_offset    = (AM33XX_CONTROL_PADCONF_##mode0##_OFFSET),    \
  7.     .value        = (((mux_value) & AM33XX_INPUT_EN) ? (mux_value)\
  8.                 : ((mux_value) | AM33XX_PULL_DISA)),    \
  9. }

注意_AM33XX_MUXENTRYAM33XX_MUX這兩個宏,前者是用於struct omap_mux的;後者是用於struct omap_board_mux的。

(2)重要的接口函數

  1. /**
  2.  * omap_mux_init - MUX初始化的私有函數,請勿使用
  3.  * 由各板級特定的MUX初始化函數調用
  4.  */
  5. int omap_mux_init(const char *name, u32 flags,
  6.          u32 mux_pbase, u32 mux_size,
  7.          struct omap_mux *superset,
  8.          struct omap_mux *package_subset,
  9.          struct omap_board_mux *board_mux,
  10.          struct omap_ball *package_balls);
這個函數是內部用於初始化struct mux_partition的最總要的函數,但是這個函數並不作爲接口函數使用,而是供各芯片初始化函數“*_mux_init”所使用的。比如AM33XX芯片:
  1. /**
  2.  * am33xx_mux_init() - 用板級特定的設置來初始化MUX系統
  3.  * @board_mux:        板級特定的MUX配置表
  4.  */
  5. int __init am33xx_mux_init(struct omap_board_mux *board_subset)
  6. {
  7.     return omap_mux_init("core", 0, AM33XX_CONTROL_PADCONF_MUX_PBASE,
  8.             AM33XX_CONTROL_PADCONF_MUX_SIZE, am33xx_muxmodes,
  9.             NULL, board_subset, NULL);
  10. }

    有了已經初始化好的struct mux_partition結構體,我們可以利用mux.h提供的許多函數方便的初始化各mux寄存器:
  1. /**
  2.  * omap_mux_init_signal - 根據信號名字符串初始化一個引腳的mux
  3.  * @muxname:        mode0_name.signal_name的格式的Mux名稱
  4.  * @val:        mux寄存器值
  5.  */
  6. int omap_mux_init_signal(const char *muxname, int val);

  7. /**
  8.  * omap_mux_get() - 通過名字返回一個mux分區
  9.  * @name:        mux分區名
  10.  *
  11.  */
  12. struct omap_mux_partition *omap_mux_get(const char *name);

  13. /**
  14.  * omap_mux_read() - 讀取mux寄存器(通過分區結構體指針和寄存器偏移值)
  15.  * @partition:        Mux分區
  16.  * @mux_offset:        mux寄存器偏移
  17.  *
  18.  */
  19. u16 omap_mux_read(struct omap_mux_partition *p, u16 mux_offset);

  20. /**
  21.  * omap_mux_write() - 寫mux寄存器(通過分區結構體指針和寄存器偏移值)
  22.  * @partition:        Mux分區
  23.  * @val:        新的mux寄存器值
  24.  * @mux_offset:        mux寄存器偏移
  25.  *
  26.  * 這個函數僅有在非GPIO信號的動態複用需要
  27.  */
  28. void omap_mux_write(struct omap_mux_partition *p, u16 val, u16 mux_offset);

  29. /**
  30.  * omap_mux_write_array() - 寫mux寄存器陣列
  31.  * @partition:        Mux分區
  32.  * @board_mux:        mux寄存器陣列 (用MAP_MUX_TERMINATOR結尾)
  33.  *
  34.  * 這個函數僅有在非GPIO信號的動態複用需要
  35.  */
  36. void omap_mux_write_array(struct omap_mux_partition *p,
  37.              struct omap_board_mux *board_mux);
在代碼比較完備的芯片中,struct omap_mux中的gpio成員有被初始化過,這樣就可以使用以下接口函數:
  1. /**
  2.  * omap_mux_init_gpio - 根據GPIO編號初始化一個信號引腳
  3.  * @gpio:        GPIO編號
  4.  * @val:        mux寄存器值
  5.  */
  6. int omap_mux_init_gpio(int gpio, int val);

  7. /**
  8.  * omap_mux_get_gpio() - 根據GPIO編號獲取一個mux寄存器值
  9.  * @gpio:        GPIO編號
  10.  *
  11.  */
  12. u16 omap_mux_get_gpio(int gpio);

  13. /**
  14.  * omap_mux_set_gpio() - 根據GPIO編號設定一個mux寄存器值
  15.  * @val:        新的mux寄存器值
  16.  * @gpio:        GPIO編號
  17.  *
  18.  */
  19. void omap_mux_set_gpio(u16 val, int gpio);

     但是am33xx的gpio成員(當前)還都是0,所有這些函數沒法使用。

     此外,在mux.h中還導出了其他的軟件接口和數據結構,這些在am33xx中沒有使用,有需要的時候再看。

     在板級初始化代碼(比如board-am335xevm.c)運行完芯片特定的MUX初始化函數 (am33xx_mux_init(board_mux);)之後,也可以在各子系統初始化時通過上面的接口函數修改配置MUX,比如在am33xx中使 用了自己封裝的一個函數和結構體:

  1. /* 模塊引腳複用結構體 */
  2. struct pinmux_config {
  3.     const char *string_name; /* 信號名格式化字符串,“模式0字符串.目標模式字符串“ */
  4.     int val; /* 其他mux寄存器可選配置值 */
  5. };

  6. /*
  7. * @pin_mux - 單個模塊引腳複用結構體
  8. *            其中定義了本模塊所有引腳複用細節.
  9. */
  10. static void setup_pin_mux(struct pinmux_config *pin_mux)
  11. {
  12.     int i;

  13.     for (= 0; pin_mux->string_name != NULL; pin_mux++)
  14.         omap_mux_init_signal(pin_mux->string_name, pin_mux->val);

  15. }
你可以在board-am335xevm.c中看到如下的代碼:
  1. static struct pinmux_config d_can_ia_pin_mux[] = {
  2.     {"uart0_rxd.d_can0_tx", OMAP_MUX_MODE2 | AM33XX_PULL_ENBL},
  3.     {"uart0_txd.d_can0_rx", OMAP_MUX_MODE2 | AM33XX_PIN_INPUT_PULLUP},
  4.     {NULL, 0},
  5. };

  6. ......
  7. static void d_can_init(int evm_id, int profile)
  8. {
  9.     switch (evm_id) {
  10.     case IND_AUT_MTR_EVM:
  11.         if ((profile == PROFILE_0) || (profile == PROFILE_1)) {
  12.             setup_pin_mux(d_can_ia_pin_mux);
  13.             /* Instance Zero */
  14.             am33xx_d_can_init(0);
  15.         }
  16.         break;
  17.     case GEN_PURP_EVM:
  18.         if (profile == PROFILE_1) {
  19.             setup_pin_mux(d_can_gp_pin_mux);
  20.             /* Instance One */
  21.             am33xx_d_can_init(1);
  22.         }
  23.         break;
  24.     default:
  25.         break;
  26.     }
  27. }
三、使用注意

     上面初始化過的結構體和接口函數的定義都是帶有"__init"和“__initdata”的,所以這些都只能在內核初始化代碼中使用,一旦系統初始化結束並進入了文件系統,這些定義都會被free。所有它們不能在內核模塊(.ok)中被調用,否則你就等着Oops吧。因爲一個芯片的引腳複用一般是硬件設計的時候定死的,一般不可能在啓動後更改。如果你是在要在模塊中改變引腳複用配置,你只能通過自己ioremap相關寄存器再修改它們來實現

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章