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\n");
120         dev_info(&pdev->dev, "DMA: RX channel: %d, TX channel: %d, "
121                 "event queue: %d\n", 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\n", 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\n", 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\n");
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\n",
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\n");
 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\n");
 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)\n", 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\n",
 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\n", err);
103     kfree(at25);
104     return err;
105 }

根據代碼就能知道,這個spi_device實際上對應的是一個eeprom,而這里就是它的操作的一些初始化.也就是說,對於分析的這個davinci代碼的實例,是dm365平台的,其開發板上應該是將eeprom通過spi總線掛接在了spi_master上.也就是掛接在了SOC上,因為我們的dm365片子本身就集成了spi_master.之后要訪問eeprom其實回調最終的都是這里提供的一些接口.聽起來有點黏糊,看個圖吧:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

哎,就寫到這里,本人不會用visio,畫個這圖畫了一下午~丟人啊!


免責聲明!

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



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