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相关寄存器再修改它们来实现

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