• SPI子系统分析之四:驱动模块


    内核版本:3.9.5

    SPI控制器层(平台相关)

    上一节讲了SPI核心层的注册和匹配函数,它是平台无关的.正是在核心层抽象了SPI控制器层的相同部分然后提供了统一的API给SPI设备层来使用.我们这一节就能看到,一个SPI控制器以platform_device的形式注册进内核,并且调用spi_register_board_info函数注册了spi_board_info结构.我们前面说过,struct spi_board_info结构是对spi_device的描述,其中的内从最终是要用来初始化struct spi_device实例的.

    哎!闲话少说了,越说越糊涂.我们以davinci的dm365平台为例,来看看SPI控制器的相关内容.在arch/arm/mach-davinci/board-dm365-evm.c中有:

     1 static struct spi_board_info dm365_evm_spi_info[] __initconst = {
     2     {
     3         .modalias    = "at25",
     4         .platform_data    = &at25640,
     5         .max_speed_hz    = 10 * 1000 * 1000,
     6         .bus_num    = 0,
     7         .chip_select    = 0,
     8         .mode        = SPI_MODE_0,
     9     },
    10 };
    11 
    12 static __init void dm365_evm_init(void)
    13 {
    14     ……
    15     dm365_init_spi0(BIT(0), dm365_evm_spi_info,
    16             ARRAY_SIZE(dm365_evm_spi_info));
    17 }

    dm365_evm_init这个函数是dm365平台初始化函数,我略去了和SPI无关的部分.可以看到其中调用了dm365_init_spi0函数,并且将一个struct spi_board_info这个结构类型的数组作为参数传了进去.那么来看看dm365_init_spi0函数,在arch/arm/mach-davinci/dm365.c中:

     1 static u64 dm365_spi0_dma_mask = DMA_BIT_MASK(32);
     2 
     3 static struct davinci_spi_platform_data dm365_spi0_pdata = {
     4     .version     = SPI_VERSION_1,
     5     .num_chipselect = 2,
     6     .dma_event_q    = EVENTQ_3,
     7 };
     8 
     9 static struct resource dm365_spi0_resources[] = {
    10     {
    11         .start = 0x01c66000,
    12         .end   = 0x01c667ff,
    13         .flags = IORESOURCE_MEM,
    14     },
    15     {
    16         .start = IRQ_DM365_SPIINT0_0,
    17         .flags = IORESOURCE_IRQ,
    18     },
    19     {
    20         .start = 17,
    21         .flags = IORESOURCE_DMA,
    22     },
    23     {
    24         .start = 16,
    25         .flags = IORESOURCE_DMA,
    26     },
    27 };
    28 
    29 static struct platform_device dm365_spi0_device = {
    30     .name = "spi_davinci",/*这个是和platform_driver匹配的依据,具体到davinci的板子就是davinci_spi_driver*/
    31     .id = 0,/*对于SPI,这个值最后会在初始化spi_master的时候用来初始化master->bus_num*/
    32     .dev = {
    33         .dma_mask = &dm365_spi0_dma_mask,
    34         .coherent_dma_mask = DMA_BIT_MASK(32),
    35         .platform_data = &dm365_spi0_pdata,
    36     },
    37     .num_resources = ARRAY_SIZE(dm365_spi0_resources),
    38     .resource = dm365_spi0_resources,
    39 };
    40 
    41 void __init dm365_init_spi0(unsigned chipselect_mask,
    42         const struct spi_board_info *info, unsigned len)
    43 {
    44     davinci_cfg_reg(DM365_SPI0_SCLK);
    45     davinci_cfg_reg(DM365_SPI0_SDI);
    46     davinci_cfg_reg(DM365_SPI0_SDO);
    47 
    48     /* not all slaves will be wired up */
    49     if (chipselect_mask & BIT(0))
    50         davinci_cfg_reg(DM365_SPI0_SDENA0);
    51     if (chipselect_mask & BIT(1))
    52         davinci_cfg_reg(DM365_SPI0_SDENA1);
    53 
    54     spi_register_board_info(info, len);
    55 
    56     platform_device_register(&dm365_spi0_device);
    57 }

    第54行注册了struct spi_board_info实例,就是我们传进来的dm365_evm_spi_info.在设备移植时填充结构体spi_board_info是移植的重要工作.我们来看看这个函数的实现,在drivers/spi/spi.c中:

     1 int spi_register_board_info(struct spi_board_info const *info, unsigned n)
     2 {
     3     struct boardinfo *bi;
     4     int i;
     5 
     6     bi = kzalloc(n * sizeof(*bi), GFP_KERNEL);/*为结构体boardinfo分配内存空间*/
     7     if (!bi)
     8         return -ENOMEM;
     9 
    10     for (i = 0; i < n; i++, bi++, info++) {
    11         struct spi_master *master;
    12 
    13         memcpy(&bi->board_info, info, sizeof(*info));
    14         mutex_lock(&board_lock);
    15         list_add_tail(&bi->list, &board_list);/*添加到板级描述符链表*/
    16         list_for_each_entry(master, &spi_master_list, list)/*将SPI主机控制类链表所有的节点匹配板级信息的设备初始化*/
    17             spi_match_master_to_boardinfo(master, &bi->board_info);
    18         mutex_unlock(&board_lock);
    19     }
    20 
    21     return 0;
    22 }

    这里又看到了一个结构struct boardinfo,其实它简单的就像没穿裤子的女人,我们来看,在同文件中:

     1 struct boardinfo {
     2     struct list_head    list;
     3     struct spi_board_info    board_info;
     4 };
     5 
     6 static LIST_HEAD(board_list);
     7 static LIST_HEAD(spi_master_list);
     8 
     9 /*
    10  * Used to protect add/del opertion for board_info list and
    11  * spi_master list, and their matching process
    12  */
    13 /*boardinfo链表操作锁*/
    14 static DEFINE_MUTEX(board_lock);

    这个结构是一个板级相关信息链表,就是说它是一些描述spi_device的信息的集合.结构体boardinfo管理多个结构体spi_board_info,结构体spi_board_info中挂在SPI总线上的设备的平台信息.一个结构体spi_board_info对应着一个SPI设备spi_device.
    同时我们也看到了,函数中出现的board_list和spi_master_list都是全局的链表,她们分别记录了系统中所有的boardinfo和所有的spi_master.至于spi_match_master_to_boardinfo函数是什么意思,我们后面还会遇到,到时候再讲.
    dm365_init_spi0函数中第56行注册平台设备.我们看到这个platform_device的的name是"spi_davinci",那么就必然还存在一个名为"spi_davinci"的platform_driver.那好办了,搜一下发现在drivers/spi/spi_davinci.c中:

     1 static struct platform_driver davinci_spi_driver = {
     2     .driver = {
     3         .name = "spi_davinci",
     4         .owner = THIS_MODULE,
     5         .of_match_table = davinci_spi_of_match,
     6     },
     7     .probe = davinci_spi_probe,
     8     .remove = davinci_spi_remove,
     9 };
    10 module_platform_driver(davinci_spi_driver);

    Linux设备模型常识告诉我们,当系统中注册了一个名为"spi_davinci"的platform_device时,同时又住了一个名为"spi_davinci"的platform_driver.那么就会执行这里的probe回调.这里我们来看davinci_spi_probe函数.

      1 static int davinci_spi_probe(struct platform_device *pdev)
      2 {
      3     struct spi_master *master;
      4     struct davinci_spi *dspi;/*davinci_spi这个结构用来描述具体的davinci平台上的spi控制器,等于说是对spi_master的一个封装*/
      5     struct davinci_spi_platform_data *pdata;
      6     struct resource *r, *mem;
      7     resource_size_t dma_rx_chan = SPI_NO_RESOURCE;
      8     resource_size_t    dma_tx_chan = SPI_NO_RESOURCE;
      9     int i = 0, ret = 0;
     10     u32 spipc0;
     11 
     12     /*分配master结构体,其中包括davinci_spi结构的内存空间,使用master.dev.driver_data指向它*/
     13     master = spi_alloc_master(&pdev->dev, sizeof(struct davinci_spi));
     14     if (master == NULL) {
     15         ret = -ENOMEM;
     16         goto err;
     17     }
     18 
     19     dev_set_drvdata(&pdev->dev, master);/*pdev->dev.device_private->driver_data = master*/
     20 
     21     dspi = spi_master_get_devdata(master);/*就是获取上文master.dev.driver_data指向的对象地址,其实就是davinci_spi结构对象的空间地址,将
     22     其赋给dspi*/
     23     if (dspi == NULL) {/*dspi不能为空哦*/
     24         ret = -ENOENT;
     25         goto free_master;
     26     }
     27 
     28     /*下面这几行就是填充dspi的pdata字段*/
     29     if (pdev->dev.platform_data) {
     30         pdata = pdev->dev.platform_data;/*具体到对于dm365来说就是dm365_spi0_pdata*/
     31         dspi->pdata = *pdata;
     32     } else {
     33         /* update dspi pdata with that from the DT */
     34         ret = spi_davinci_get_pdata(pdev, dspi);
     35         if (ret < 0)
     36             goto free_master;
     37     }
     38 
     39     /* pdata in dspi is now updated and point pdata to that */
     40     pdata = &dspi->pdata;/*pdata指针再指向dspi->pdata*/
     41 
     42     r = platform_get_resource(pdev, IORESOURCE_MEM, 0);/*获取IO资源*/
     43     if (r == NULL) {
     44         ret = -ENOENT;
     45         goto free_master;
     46     }
     47 
     48     dspi->pbase = r->start;
     49 
     50     mem = request_mem_region(r->start, resource_size(r), pdev->name);/*申请IO内存*/
     51     if (mem == NULL) {
     52         ret = -EBUSY;
     53         goto free_master;
     54     }
     55 
     56     dspi->base = ioremap(r->start, resource_size(r));/*建立内存映射*/
     57     if (dspi->base == NULL) {
     58         ret = -ENOMEM;
     59         goto release_region;
     60     }
     61 
     62     dspi->irq = platform_get_irq(pdev, 0);/*获取irq号*/
     63     if (dspi->irq <= 0) {
     64         ret = -EINVAL;
     65         goto unmap_io;
     66     }
     67 
     68     ret = request_threaded_irq(dspi->irq, davinci_spi_irq, dummy_thread_fn,
     69                  0, dev_name(&pdev->dev), dspi);/*申请spi中断,中断处理函数为davinci_spi_irq*/
     70     if (ret)
     71         goto unmap_io;
     72 
     73     /*设置bitbang的所属master*/
     74     dspi->bitbang.master = spi_master_get(master);
     75     if (dspi->bitbang.master == NULL) {
     76         ret = -ENODEV;
     77         goto irq_free;
     78     }
     79 
     80     dspi->clk = clk_get(&pdev->dev, NULL);/*获取spi时钟*/
     81     if (IS_ERR(dspi->clk)) {
     82         ret = -ENODEV;
     83         goto put_master;
     84     }
     85     clk_prepare_enable(dspi->clk);
     86 
     87     master->dev.of_node = pdev->dev.of_node;
     88     master->bus_num = pdev->id;/*bus_num*/
     89     master->num_chipselect = pdata->num_chipselect;/*保存SPI主机控制器支持的片选数量.具体到dm365可以看到dm365_spi0_pdata中将其定义为2*/
     90     master->setup = davinci_spi_setup;
     91 
     92     /*设置bitbang控制传输的相关函数*/
     93     dspi->bitbang.chipselect = davinci_spi_chipselect;
     94     dspi->bitbang.setup_transfer = davinci_spi_setup_transfer;
     95 
     96     dspi->version = pdata->version;/*具体到dm365可以看到dm365_spi0_pdata中将其定义为0*/
     97 
     98     dspi->bitbang.flags = SPI_NO_CS | SPI_LSB_FIRST | SPI_LOOP;
     99     if (dspi->version == SPI_VERSION_2)
    100         dspi->bitbang.flags |= SPI_READY;
    101 
    102     r = platform_get_resource(pdev, IORESOURCE_DMA, 0);/*获取DMA资源,这作为输入缓冲*/
    103     if (r)
    104         dma_rx_chan = r->start;
    105     r = platform_get_resource(pdev, IORESOURCE_DMA, 1);/*由参数就能知道davinci的DMA资源定义了两个,这里就获取第二个.这作为输出缓冲*/
    106     if (r)
    107         dma_tx_chan = r->start;
    108 
    109     dspi->bitbang.txrx_bufs = davinci_spi_bufs;/*传输数据最终要调用的函数*/
    110     if (dma_rx_chan != SPI_NO_RESOURCE &&
    111         dma_tx_chan != SPI_NO_RESOURCE) {
    112         dspi->dma_rx_chnum = dma_rx_chan;
    113         dspi->dma_tx_chnum = dma_tx_chan;
    114 
    115         ret = davinci_spi_request_dma(dspi);
    116         if (ret)
    117             goto free_clk;
    118 
    119         dev_info(&pdev->dev, "DMA: supported
    ");
    120         dev_info(&pdev->dev, "DMA: RX channel: %d, TX channel: %d, "
    121                 "event queue: %d
    ", dma_rx_chan, dma_tx_chan,
    122                 pdata->dma_event_q);
    123     }
    124 
    125     dspi->get_rx = davinci_spi_rx_buf_u8;
    126     dspi->get_tx = davinci_spi_tx_buf_u8;
    127 
    128     init_completion(&dspi->done);/*初始化completion,用于实现同步I/O*/
    129 
    130     /* Reset In/OUT SPI module */
    131     iowrite32(0, dspi->base + SPIGCR0);
    132     udelay(100);
    133     iowrite32(1, dspi->base + SPIGCR0);
    134 
    135     /* Set up SPIPC0.  CS and ENA init is done in davinci_spi_setup */
    136     spipc0 = SPIPC0_DIFUN_MASK | SPIPC0_DOFUN_MASK | SPIPC0_CLKFUN_MASK;
    137     iowrite32(spipc0, dspi->base + SPIPC0);
    138 
    139     /* initialize chip selects */
    140     if (pdata->chip_sel) {/*davinci的pdata中chip_sel字段并没有设置,这里为空,因此不会进来*/
    141         for (i = 0; i < pdata->num_chipselect; i++) {
    142             if (pdata->chip_sel[i] != SPI_INTERN_CS)
    143                 gpio_direction_output(pdata->chip_sel[i], 1);
    144         }
    145     }
    146 
    147     if (pdata->intr_line)/*dm365这个字段为空*/
    148         iowrite32(SPI_INTLVL_1, dspi->base + SPILVL);
    149     else
    150         iowrite32(SPI_INTLVL_0, dspi->base + SPILVL);
    151 
    152     iowrite32(CS_DEFAULT, dspi->base + SPIDEF);
    153 
    154     /* master mode default */
    155     set_io_bits(dspi->base + SPIGCR1, SPIGCR1_CLKMOD_MASK);
    156     set_io_bits(dspi->base + SPIGCR1, SPIGCR1_MASTER_MASK);/*默认设置SPI主控制器工作在master方式*/
    157     set_io_bits(dspi->base + SPIGCR1, SPIGCR1_POWERDOWN_MASK);
    158 
    159     ret = spi_bitbang_start(&dspi->bitbang);/*注册我们的主机SPI控制器*/
    160     if (ret)
    161         goto free_dma;
    162 
    163     dev_info(&pdev->dev, "Controller at 0x%p
    ", dspi->base);
    164 
    165     return ret;
    166 
    167 free_dma:
    168     dma_release_channel(dspi->dma_rx);
    169     dma_release_channel(dspi->dma_tx);
    170 free_clk:
    171     clk_disable_unprepare(dspi->clk);
    172     clk_put(dspi->clk);
    173 put_master:
    174     spi_master_put(master);/*减少引用计数*/
    175 irq_free:
    176     free_irq(dspi->irq, dspi);
    177 unmap_io:
    178     iounmap(dspi->base);
    179 release_region:
    180     release_mem_region(dspi->pbase, resource_size(r));
    181 free_master:
    182     kfree(master);
    183 err:
    184     return ret;
    185 }

    该函数首先为spi_master结构体以及davinci_spi结构体分配了空间,同时,spi_master.dev.driver_data指向了davinci_spi.接着执行了该条语句:

    pdata = pdev->dev.platform_data;/*具体到对于dm365来说就是dm365_spi0_pdata*/

    dspi->pdata = *pdata;

    NOTE:在这里获取platform_device.dev.platform_data,也就是平台设备的相关数据,这是平台设备移植最需要关注的地方.

    随后,为master定义了setup方法,为bitbang定义了3个方法.之后获取了一系列的资源,同时注册了中断服务程序.接着再初始化了completion,这个东东将用于实现同步I/O,他的伟大之处后面会体现出来的.最后调用spi_bitbang_start注册主机控制器.我们来看这个函数,在drivers/spi/spi_bitbang.c中:

     1 int spi_bitbang_start(struct spi_bitbang *bitbang)
     2 {
     3     struct spi_master *master = bitbang->master;
     4     int status;
     5 
     6     if (!master || !bitbang->chipselect)
     7         return -EINVAL;
     8 
     9     INIT_WORK(&bitbang->work, bitbang_work);/*初始化一个struct work,处理函数为bitbang_work*/
    10     spin_lock_init(&bitbang->lock);/*初始化自旋锁*/
    11     INIT_LIST_HEAD(&bitbang->queue);/*初始化链表头,链表为双向循环链表*/
    12 
    13     if (!master->mode_bits)
    14         master->mode_bits = SPI_CPOL | SPI_CPHA | bitbang->flags;
    15 
    16     /*检测bitbang中的函数是否都定义了,如果没定义,则默认使用spi_bitbang_xxx*/
    17     if (!master->transfer)/*master的transfer方法没有定义过*/
    18         master->transfer = spi_bitbang_transfer;/*使用默认的spi_bitbang_transfe方法*/
    19     if (!bitbang->txrx_bufs) {/*如果bitbang没有txrx_bufs方法,其实对于davinci在davinci_spi_probe函数中定义过该方法*/
    20         bitbang->use_dma = 0;
    21         bitbang->txrx_bufs = spi_bitbang_bufs;
    22         if (!master->setup) {
    23             if (!bitbang->setup_transfer)
    24                 bitbang->setup_transfer =
    25                      spi_bitbang_setup_transfer;
    26             master->setup = spi_bitbang_setup;
    27             master->cleanup = spi_bitbang_cleanup;
    28         }
    29     } else if (!master->setup)/*对于davinci在davinci_spi_probe函数中定义过该方法*/
    30         return -EINVAL;
    31     if (master->transfer == spi_bitbang_transfer &&
    32             !bitbang->setup_transfer)
    33         return -EINVAL;
    34 
    35     /* this task is the only thing to touch the SPI bits */
    36     bitbang->busy = 0;
    37     bitbang->workqueue = create_singlethread_workqueue(
    38             dev_name(master->dev.parent));/*创建bitbang的工作队列*/
    39     if (bitbang->workqueue == NULL) {
    40         status = -EBUSY;
    41         goto err1;
    42     }
    43 
    44     /* driver may get busy before register() returns, especially
    45      * if someone registered boardinfo for devices
    46      */
    47     status = spi_register_master(master);/*注册spi控制器*/
    48     if (status < 0)
    49         goto err2;
    50 
    51     return status;
    52 
    53 err2:
    54     destroy_workqueue(bitbang->workqueue);
    55 err1:
    56     return status;
    57 }
    58 EXPORT_SYMBOL_GPL(spi_bitbang_start);

    定义了控制器的transfer方法为spi_bitbang_transfer.创建了一个工作队列和一个工作bitbang_work,同时创建了一个链表.这些东东后面都会看到.最后,调用了spi_register_master函数,该函数将完成SPI控制器的注册,其中还牵涉到spi_device的注册.我们来看看这个函数.下列函数位于drivers/spi/spi.c:

     1 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     struct boardinfo    *bi;
     6     int            status = -ENODEV;
     7     int            dynamic = 0;
     8 
     9     if (!dev)
    10         return -ENODEV;
    11 
    12     status = of_spi_register_master(master);
    13     if (status)
    14         return status;
    15 
    16     /* even if it's just one always-selected device, there must
    17      * be at least one chipselect
    18      */
    19     if (master->num_chipselect == 0)/*SPI主控制器支持的片选数当然不能为0,否则还怎么挂接从设备啊.一个接口对应一个master,一个master对应
    20     一条SPI总线,一条总线上可能挂有多个设备,num_chipselect就表示该总线上的设备数*/
    21         return -EINVAL;
    22 
    23     if ((master->bus_num < 0) && master->dev.of_node)
    24         master->bus_num = of_alias_get_id(master->dev.of_node, "spi");
    25 
    26     /* convention:  dynamically assigned bus IDs count down from the max */
    27     if (master->bus_num < 0) {/*总线号从最大开始减*/
    28         /* FIXME switch to an IDR based scheme, something like
    29          * I2C now uses, so we can't run out of "dynamic" IDs
    30          */
    31         master->bus_num = atomic_dec_return(&dyn_bus_id);
    32         dynamic = 1;
    33     }
    34 
    35     spin_lock_init(&master->bus_lock_spinlock);
    36     mutex_init(&master->bus_lock_mutex);
    37     master->bus_lock_flag = 0;/*这个标志指示SPI总线是否被锁*/
    38 
    39     /* register the device, then userspace will see it.
    40      * registration fails if the bus ID is in use.
    41      */
    42     dev_set_name(&master->dev, "spi%u", master->bus_num);
    43     status = device_add(&master->dev);/*向内核注册设备*/
    44     if (status < 0)
    45         goto done;
    46     dev_dbg(dev, "registered master %s%s
    ", dev_name(&master->dev),
    47             dynamic ? " (dynamic)" : "");
    48 
    49     /* If we're using a queued driver, start the queue */
    50     if (master->transfer)/*对于具体到davinci,此字段在spi_bitbang_start中被初始化为spi_bitbang_transfer*/
    51         dev_info(dev, "master is unqueued, this is deprecated
    ");
    52     else {
    53         status = spi_master_initialize_queue(master);
    54         if (status) {
    55             device_unregister(&master->dev);
    56             goto done;
    57         }
    58     }
    59 
    60     mutex_lock(&board_lock);
    61     list_add_tail(&master->list, &spi_master_list);/*把这个SPI主机控制器添加进全局的spi_master_list链表*/
    62     list_for_each_entry(bi, &board_list, list)/*遍历全局的board_list链表,为每一个boardinfo结构节点查找其中的指向的spi_board_info结构,通过
    63     对spi_board_info的bus_bum和SPI主机控制器(spi_master)的bus_num进行匹配,来确定SPI从设备是否由此SPI主机控制器来控制.如果匹配,则通
    64     过调用spi_new_device函数创建spi_device从设备,并且将其注册进内核*/
    65         spi_match_master_to_boardinfo(master, &bi->board_info);
    66     mutex_unlock(&board_lock);
    67 
    68     /* Register devices from the device tree and ACPI */
    69     of_register_spi_devices(master);
    70     acpi_register_spi_devices(master);
    71 done:
    72     return status;
    73 }
    74 EXPORT_SYMBOL_GPL(spi_register_master);

    该函数注释一目了然,我们来看看spi_match_master_to_boardinfo这个函数吧.在同文件中有:

     1 /*使用SPI主控制类和板级信息匹配则添加一个新设备*/
     2 static void spi_match_master_to_boardinfo(struct spi_master *master,
     3                 struct spi_board_info *bi)
     4 {
     5     struct spi_device *dev;
     6 
     7     if (master->bus_num != bi->bus_num)/*通过bus_num对spi设备和master进行匹配*/
     8         return;
     9 
    10     dev = spi_new_device(master, bi);/*执行到此,表示匹配完成,SPI设备由该SPI接口来控制,开始创建spi_device*/
    11     if (!dev)
    12         dev_err(master->dev.parent, "can't create new device for %s
    ",
    13             bi->modalias);
    14 }

    地球人都知道这段代码什么意思,好了继续看spi_new_device函数.在同文件中:

     1 struct spi_device *spi_new_device(struct spi_master *master,
     2                   struct spi_board_info *chip)
     3 {
     4     struct spi_device    *proxy;
     5     int            status;
     6 
     7     /* NOTE:  caller did any chip->bus_num checks necessary.
     8      *
     9      * Also, unless we change the return value convention to use
    10      * error-or-pointer (not NULL-or-pointer), troubleshootability
    11      * suggests syslogged diagnostics are best here (ugh).
    12      */
    13 
    14     proxy = spi_alloc_device(master);/*分配spi_device结构,并初始化一些字段*/
    15     if (!proxy)
    16         return NULL;
    17 
    18     WARN_ON(strlen(chip->modalias) >= sizeof(proxy->modalias));
    19 
    20     /*从spi_board_info获取SPI从设备的参数*/
    21     proxy->chip_select = chip->chip_select;
    22     proxy->max_speed_hz = chip->max_speed_hz;
    23     proxy->mode = chip->mode;
    24     proxy->irq = chip->irq;
    25     strlcpy(proxy->modalias, chip->modalias, sizeof(proxy->modalias));
    26     proxy->dev.platform_data = (void *) chip->platform_data;
    27     proxy->controller_data = chip->controller_data;
    28     proxy->controller_state = NULL;
    29 
    30     status = spi_add_device(proxy);/*将新设备添加进内核*/
    31     if (status < 0) {
    32         spi_dev_put(proxy);/*从内核模块中撤销这个SPI(从)设备,但是这貌似并没有释放spi_alloc_device开辟的内存.实质上这个函数只是减少
    33     了SPI(从)设备的引用计数*/
    34         return NULL;
    35     }
    36 
    37     return proxy;
    38 }
    39 EXPORT_SYMBOL_GPL(spi_new_device);

    这个函数首先创建了spi_device结构,让后通过板级信息spi_board_info将SPI从设备的相关信息复制给spi_device结构,从而完成了spi_device结构的定义,最后调用spi_add_device,完成spi_device的注册.其中struct spi_board_info *chip这就是我们当初arch/arm/mach-davinci/board-dm365-evm.c中定义的dm365_evm_spi_info数组中的结构实例.

    第25行我们就知道,这里注册的spi_device的modalias字段就被初始化为"at25".那么与其对应的spi_driver的device_driver中的name字段肯定为"at25".只有这样才能在SPI核心层的spi_match_device函数中匹配.搜了一遍内核,看到在drivers/msic/eeprom/at25.c中有:

     1 static struct spi_driver at25_driver = {
     2     .driver = {
     3         .name        = "at25",
     4         .owner        = THIS_MODULE,
     5     },
     6     .probe        = at25_probe,/*与相应的SPI(从)设备spi_device匹配成功后,则调用这里的probe函数*/
     7     .remove        = at25_remove,
     8 };
     9 
    10 module_spi_driver(at25_driver);

    第10行这个宏是SPI架构专门定义的,在include/linux/spi/spi.h中,我们来看:

    1 #define module_spi_driver(__spi_driver) 
    2     module_driver(__spi_driver, spi_register_driver, 
    3             spi_unregister_driver)

    让暴风雨来的更猛烈些吧,研究内核的孩纸都伤不起啊,我们只有硬着头皮往下看,在include/linux/device.h中:

     1 #define module_driver(__driver, __register, __unregister, ...) 
     2 static int __init __driver##_init(void) 
     3 { 
     4     return __register(&(__driver) , ##__VA_ARGS__); 
     5 } 
     6 module_init(__driver##_init); 
     7 static void __exit __driver##_exit(void) 
     8 { 
     9     __unregister(&(__driver) , ##__VA_ARGS__); 
    10 } 
    11 module_exit(__driver##_exit);

    山重水复疑无路,柳暗花明又一村.这东东你让我说啥~没说的.相信大家也不想在听我唠叨.但是这里为什么多此一举写了这么一个叫宏,而不是像其他的linux模块那样直接写两个module_xxx呢?这是因为这个设备eeprom本身是不可插拔的,也就不需要什么加载卸载的过程,系统上电运行直接就注册了.
    那么我们知道了,现在因为SPI子系统核心层我们已经注册了一条SPI总线,就是spi_bus_type.它里面的match回调函数我们已经看过了,就是spi_match_device.就是在这个函数中将完成这个spi_device和spi_driver的匹配,匹配成功就会去执行spi_driver的probe回调了.我们来看,at25_probe函数在drivers/msic/eeprom/at25.c中:

      1 static int at25_probe(struct spi_device *spi)
      2 {
      3     struct at25_data    *at25 = NULL;/*这个结构其实就是对spi_device的封装,我们可以像理解面向对象那样将这个结构理解为对spi_device的实例*/
      4     struct spi_eeprom    chip;/*此结构用来作为记录一个SPI EEPROMS的句柄,它保存了platform_data的数据*/
      5     struct device_node    *np = spi->dev.of_node;
      6     int            err;
      7     int            sr;
      8     int            addrlen;
      9 
     10     /* Chip description */
     11     if (!spi->dev.platform_data) {/*具体到dm365平台,此platform_data就是arch/arm/mach-davinci/board-ddm365-evm.c中定义的的at25640*/
     12         if (np) {
     13             err = at25_np_to_chip(&spi->dev, np, &chip);
     14             if (err)
     15                 goto fail;
     16         } else {
     17             dev_err(&spi->dev, "Error: no chip description
    ");
     18             err = -ENODEV;
     19             goto fail;
     20         }
     21     } else
     22         chip = *(struct spi_eeprom *)spi->dev.platform_data;
     23 
     24     /* For now we only support 8/16/24 bit addressing */
     25     if (chip.flags & EE_ADDR1)/*flags用来标志eeprom的位宽和读写模式,具体到dm365平台此flags为EE_ADDR2*/
     26         addrlen = 1;
     27     else if (chip.flags & EE_ADDR2)
     28         addrlen = 2;
     29     else if (chip.flags & EE_ADDR3)
     30         addrlen = 3;
     31     else {
     32         dev_dbg(&spi->dev, "unsupported address type
    ");
     33         err = -EINVAL;
     34         goto fail;
     35     }
     36 
     37     /* Ping the chip ... the status register is pretty portable,
     38      * unlike probing manufacturer IDs.  We do expect that system
     39      * firmware didn't write it in the past few milliseconds!
     40      */
     41     /*ping一下芯片,状态寄存器是很容易被检测的,不像制造商ID那样麻烦.我们期待系统固件之前没有写入它.*/
     42     sr = spi_w8r8(spi, AT25_RDSR);/*同步的读取状态寄存器的值,返回的八位数据保存在sr中.spi_w8r8这个函数有可能会睡眠*/
     43     if (sr < 0 || sr & AT25_SR_nRDY) {
     44         dev_dbg(&spi->dev, "rdsr --> %d (%02x)
    ", sr, sr);
     45         err = -ENXIO;
     46         goto fail;
     47     }
     48 
     49     if (!(at25 = kzalloc(sizeof *at25, GFP_KERNEL))) {/*以kmalloc分配内存,并清0*/
     50         err = -ENOMEM;
     51         goto fail;
     52     }
     53 
     54     mutex_init(&at25->lock);/*初始化互斥体*/
     55     at25->chip = chip;/*记录下spi_eeprom*/
     56     at25->spi = spi_dev_get(spi);/*记录下这个片子对应的spi_device*/
     57     dev_set_drvdata(&spi->dev, at25);/*spi->dev.device_private->driver_data = at25*/
     58     at25->addrlen = addrlen;/*我觉得应该可以理解为这个片子使用的位宽是多少个字节.那根据上文分析,此处值为2*/
     59 
     60     /* Export the EEPROM bytes through sysfs, since that's convenient.
     61      * And maybe to other kernel code; it might hold a board's Ethernet
     62      * address, or board-specific calibration data generated on the
     63      * manufacturing floor.
     64      *
     65      * Default to root-only access to the data; EEPROMs often hold data
     66      * that's sensitive for read and/or write, like ethernet addresses,
     67      * security codes, board-specific manufacturing calibrations, etc.
     68      */
     69     /*通过sysfs文件系统导出EEPROM的字节,因为这是很方便的.也许其他内核代码也是这样做的:比如保存板子的以太网地址,或者是生产商的特定板的校验数据.
     70     默认只有root用户能够访问的数据.EEPROMs经常保存一些敏感的读或写的数据,像是以太网地址,安全码,特定板的校准数据等*/
     71     sysfs_bin_attr_init(&at25->bin);/*初始化一个动态分配的bin_attribute属性*/
     72     at25->bin.attr.name = "eeprom";/*属性的名字*/
     73     at25->bin.attr.mode = S_IRUSR;/*属性的模式(用户可读)*/
     74     at25->bin.read = at25_bin_read;/*属性的读方法*/
     75     at25->mem.read = at25_mem_read;/*片子的内存读函数*/
     76 
     77     at25->bin.size = at25->chip.byte_len;
     78     if (!(chip.flags & EE_READONLY)) {/*flags用来标志eeprom的位宽和读写模式,具体到dm365平台此flags为EE_ADDR2*/
     79         at25->bin.write = at25_bin_write;/*如果eeprom片子不是只读的话,那么就设置属性的写方法*/
     80         at25->bin.attr.mode |= S_IWUSR;/*增加属性的模式(用户可写)*/
     81         at25->mem.write = at25_mem_write;/*片子的内存写函数*/
     82     }
     83 
     84     err = sysfs_create_bin_file(&spi->dev.kobj, &at25->bin);/*创建一个二进制的属性文件*/
     85     if (err)
     86         goto fail;
     87 
     88     if (chip.setup)/*如果片子定义了setup函数,具体到dm365平台,此platform_data就是arch/arm/mach-davinci/board-ddm365-evm.c中定义的的at25640里并没有
     89     定义这个函数,因此为空*/
     90         chip.setup(&at25->mem, chip.context);/*使用片子的setup函数做一些初始化*/
     91 
     92     dev_info(&spi->dev, "%Zd %s %s eeprom%s, pagesize %u
    ",
     93         (at25->bin.size < 1024)
     94             ? at25->bin.size
     95             : (at25->bin.size / 1024),
     96         (at25->bin.size < 1024) ? "Byte" : "KByte",
     97         at25->chip.name,
     98         (chip.flags & EE_READONLY) ? " (readonly)" : "",
     99         at25->chip.page_size);
    100     return 0;
    101 fail:
    102     dev_dbg(&spi->dev, "probe err %d
    ", err);
    103     kfree(at25);
    104     return err;
    105 }

    根据代码就能知道,这个spi_device实际上对应的是一个eeprom,而这里就是它的操作的一些初始化.也就是说,对于分析的这个davinci代码的实例,是dm365平台的,其开发板上应该是将eeprom通过spi总线挂接在了spi_master上.也就是挂接在了SOC上,因为我们的dm365片子本身就集成了spi_master.之后要访问eeprom其实回调最终的都是这里提供的一些接口.听起来有点黏糊,看个图吧:

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

    哎,就写到这里,本人不会用visio,画个这图画了一下午~丢人啊!

  • 相关阅读:
    响应式开发
    css3的2D和3D的转换
    前端CSS3笔记
    DOM精简版笔记
    JS进阶
    linux 修改时间同步到BIOS
    linux-设置代理和取消代理
    linux-rpm强制安装跳过依赖包
    [linux] VNC the connection was refused by the computer
    Linux vim 常用命令(不定时update)
  • 原文地址:https://www.cnblogs.com/jason-lu/p/3165327.html
Copyright © 2020-2023  润新知