Linux EMMC子系统分析-初始化流程(转载)

最近在解EMMC的一个bug,发现Linux EMMC有点小复杂,先整理个文档出来吧

用的是TI 平台,仅分析MMC,不分析SD和SDIO


mmc_init

[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. 2769 static int __init mmc_init(void)  
  2. 2770 {  
  3. 2774   
  4. 2775     workqueue = alloc_ordered_workqueue("kmmcd", 0);  
  5. 2776     if (!workqueue)  
  6. 2777         return -ENOMEM;  
  7. 2778   
  8. 2779     ret = mmc_register_bus();  
  9. 2780     if (ret)  
  10. 2781         goto destroy_workqueue;  
  11. 2782   
  12. 2783     ret = mmc_register_host_class();  
  13. 2784     if (ret)  
  14. 2785         goto unregister_bus;;  
  15. 2793   
  16.   
  17. 2802 }  

2775分配一个workqueue,这个workqueue是专门用来处理card detect的,EMMC因为是unremovable的,所以不需要关注它

2779 注册mmc_bus_type,实现如下:

[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. int mmc_register_bus(void)  
  2. {  
  3.     return bus_register(&mmc_bus_type);  
  4. }  
把mmc_bus_type注册到bus系统,后面调用device_add函数时,则会辗转调用到mmc_bus_type中的probe函数,不必太纠结在这个代码


mmc_init调用时间比较早,会在host驱动初始化之前执行完。

[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. subsys_initcall(mmc_init);  

host驱动初始化

kernel/drivers/mmc/host/omap_hsmmc.c

host驱动位置是kernel/drivers/mmc/host/,一般来说都是platform_driver,以omap hsmmc host驱动为例

[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1.     <strong>.probe      = omap_hsmmc_probe,</strong>  
  2.     .remove     = omap_hsmmc_remove,  
  3.     .driver     = {  
  4.         .name = DRIVER_NAME,  
  5.         .owner = THIS_MODULE,  
  6.         .pm = &omap_hsmmc_dev_pm_ops,  
  7.         .of_match_table = of_match_ptr(omap_mmc_of_match),  
  8.     },  
  9. };  
  10.   
  11. module_platform_driver(omap_hsmmc_driver);  

只需要关注omap_hsmmc_probe即可,当系统匹配到platform_device时,会调用omap_hsmmc_probe,好长的一个函数

[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. 2627     base = devm_ioremap_resource(&pdev->dev, res);  
  2. 2628     if (IS_ERR(base))  
  3. 2629         return PTR_ERR(base);  
  4. 2630   
  5. 2631     ret = omap_hsmmc_gpio_init(pdata);  
  6. 2632     if (ret)  
  7. 2633         goto err;  
devm_ioremap_resource,实质就是ioremap,设备特定io资源到内核地址的映射,自行脑补一下ioremap。

omap_hsmmc_gpio_init是card detect和write protect gpio的初始化,Ignore it!!!

[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. 2635     mmc = mmc_alloc_host(sizeof(struct omap_hsmmc_host), &pdev->dev);  
  2. 2636     if (!mmc) {  
  3. 2637         ret = -ENOMEM;  
  4. 2638         goto err_alloc;  
  5. 2639     }  
  6. ...............  
mmc_alloc_host,分配一个omap_hsmmc_host结构(包含struct mmc_host),以供后面玩耍

[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. 2661     mmc->ops    = &omap_hsmmc_ops;  
  2.   
  3. 2410 static const struct mmc_host_ops omap_hsmmc_ops = {  
  4. 2411     .enable = omap_hsmmc_enable_fclk,  
  5. 2412     .disable = omap_hsmmc_disable_fclk,  
  6. 2413     .post_req = omap_hsmmc_post_req,  
  7. 2414     .pre_req = omap_hsmmc_pre_req,  
  8. 2415     .request = omap_hsmmc_request,  
  9. 2416     .set_ios = omap_hsmmc_set_ios,  
  10. 2417     .get_cd = omap_hsmmc_get_cd,  
  11. 2418     .get_ro = omap_hsmmc_get_ro,  
  12. 2419     .init_card = omap_hsmmc_init_card,  
  13. 2420     .start_signal_voltage_switch = omap_start_signal_voltage_switch,  
  14. 2421     .card_busy = omap_hsmmc_card_busy,  
  15. 2422     .execute_tuning = omap_execute_tuning,  
  16. 2423     /* NYET -- enable_sdio_irq */  
  17. 2424 };  
 omap hsmmc host驱动和mmc子系统的所有接口都在这里了。

[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. 2663     mmc->f_min = OMAP_MMC_MIN_CLOCK;  
  2.   
  3. 2675     if (pdata->max_freq > 0) {  
  4. 2676         mmc->f_max = pdata->max_freq;  
  5. 2677         ret = clk_set_rate(host->fclk, pdata->max_freq);  
  6. 2678         if (ret) {  
  7. 2679             dev_err(dev, "failed to set clock to %d\n",  
  8. 2680                 pdata->max_freq);  
  9. 2681             goto err1;  
  10. 2682         }  
  11. 2683     } else {  
  12. 2684         mmc->f_max = OMAP_MMC_MAX_CLOCK;  
  13. 2685     }  
定义了最小时钟400K,貌似后面发送命令都会用这个时钟。最大时钟为52MHZ,后面还有机会再修改这个时钟频率。

[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. 2724     mmc->max_segs = 1024;  
  2. 2725   
  3. 2726     mmc->max_blk_size = 512;       /* Block Length at max can be 1024 */  
  4. 2727     mmc->max_blk_count = 0xFFFF;    /* No. of Blocks is 16 bits */  
  5. 2728     mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;  
  6. 2729     mmc->max_seg_size = mmc->max_req_size;  

mmc作为快设备,最主要的操作就是数据sectors读写,max_segs定义了block设备的request所支持的最大segment

max_blk_count 一个request中所包含的最大block数

其实多少都不重要,反正就是和request相关的一些上限。

[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. 2731     mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED |  
  2. 2732              MMC_CAP_WAIT_WHILE_BUSY | MMC_CAP_ERASE;  
  3. 2733   
  4. 2734     mmc->caps |= mmc_slot(host).caps;  
  5. 2735     if (mmc->caps & MMC_CAP_8_BIT_DATA)  
  6. 2736         mmc->caps |= MMC_CAP_4_BIT_DATA;  
  7. 2737     if (mmc_slot(host).nonremovable)  
  8. 2738         mmc->caps |= MMC_CAP_NONREMOVABLE;  
  9.   
  10. 2741   
  11. 2742     if (of_property_read_bool(np, "sd-uhs-sdr12"))  
  12. 2743         mmc->caps |= MMC_CAP_UHS_SDR12;  
  13. 2744     if (of_property_read_bool(np, "sd-uhs-sdr25"))  
  14. 2745         mmc->caps |= MMC_CAP_UHS_SDR25;  
  15. 2746     if (of_property_read_bool(np, "sd-uhs-sdr50"))  
  16. 2747         mmc->caps |= MMC_CAP_UHS_SDR50;  
  17. 2748     if (of_property_read_bool(np, "sd-uhs-sdr104"))  
  18. 2749         mmc->caps |= MMC_CAP_UHS_SDR104;  
  19. 2750     if (of_property_read_bool(np, "sd-uhs-ddr50"))  
  20. 2751         mmc->caps |= MMC_CAP_UHS_DDR50;  
  21. 2752     if (of_property_read_bool(np, "mmc-ddr-1_8v"))  
  22. 2753         mmc->caps |= MMC_CAP_1_8V_DDR;  
  23. 2754     if (of_property_read_bool(np, "mmc-hs200-1_8v"))  
  24. 2755         mmc->caps2 |= MMC_CAP2_HS200_1_8V_SDR;  
  25.   
  26.    
mmc->caps 表示host能力的标志,mmc->caps2 表示host更多的能力,也许有一天会添加mmc->caps3这个字段

MMCCAP_MMC_HIGHSPEED, MMC_CAP_SD_HIGHSPEED MMC_CAP_WAIT_WHILE_BUSY MMC_CAP_ERASE是最基本的能力

MMC_CAP_8_BIT_DATA表示数据总线是多少,EMMC 数据线支持8线,4线和1线。

2742 ~ 2755 通过of系统定义的一些特定能力,当前MMC_CAP_1_8V_DDR,MMC_CAP2_HS200_1_8V_SDR都很常见了。DDR是Double Date Rate的缩写,SDR是Single Data Rate的缩写

自行脑补百度资料DDR SDRAM, EMMC的DDR SDR与之类似。

DDR=Double Data Rate双倍速率同步动态随机存储器。严格的说DDR应该叫DDR SDRAM,人们习惯称为DDR,其中,SDRAM 是Synchronous Dynamic Random Access Memory的缩写,
即同步动态随机存取存储器。而DDR SDRAM是Double Data Rate SDRAM的缩写,是双倍速率同步动态随机存储器的意思。DDR内存是在SDRAM内存基础上发展而来的,仍然沿用SDRAM生产体系,
一个时钟周期内进行两次读/写操作,即在时钟的上升沿和下降沿分别执行一次读/写操作。

因此对于内存厂商而言,只需对制造普通SDRAM的设备稍加改进,即可实现DDR内存的生产,可有效的降低成本。Double Data Rate:与传统的单数据速率相比,DDR技术实现了一个时钟周期内进行两次读/写操作,即在时钟的上升沿和下降沿分别执行一次读/写操作。

注意对于EMMC CMD线来说,是没有DDR SDR说法的,CMD和response只在clock的上升沿传输,参见EMMC spec5.0  section 6.15.1


 

[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. 2773     omap_hsmmc_conf_bus_power(host);  
设置EMMC总线电压,EMMC芯片支持3.3V, 1.8V和1.2V总线信号电压,host需要和EMMC芯片的电压匹配(就是相同了)。

[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. 2775     if (!pdev->dev.of_node) {  
  2. 2776         res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx");  
  3. 2777         if (!res) {  
  4. 2778             dev_err(mmc_dev(host->mmc), "cannot get DMA TX channel\n");  
  5. 2779             ret = -ENXIO;  
  6. 2780             goto err_irq;  
  7. 2781         }  
  8. 2782         tx_req = res->start;  
  9. 2783   
  10. 2784         res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx");  
  11. 2785         if (!res) {  
  12. 2786             dev_err(mmc_dev(host->mmc), "cannot get DMA RX channel\n");  
  13. 2787             ret = -ENXIO;  
  14. 2788             goto err_irq;  
  15. 2789         }  
  16. 2790         rx_req = res->start;  
  17. 2791     }  
  18. 2792   
  19. 2793     dma_cap_zero(mask);  
  20. 2794     dma_cap_set(DMA_SLAVE, mask);  
  21. 2795   
  22. 2796     host->rx_chan =  
  23. 2797         dma_request_slave_channel_compat(mask, omap_dma_filter_fn,  
  24. 2798                          &rx_req, &pdev->dev, "rx");  
  25. 2799   
  26. 2800     if (!host->rx_chan) {  
  27. 2801         dev_err(mmc_dev(host->mmc), "unable to obtain RX DMA engine channel %u\n", rx_req);  
  28. 2802         ret = -ENXIO;  
  29. 2803         goto err_irq;  
  30. 2804     }  
  31. 2805   
  32. 2806     host->tx_chan =  
  33. 2807         dma_request_slave_channel_compat(mask, omap_dma_filter_fn,  
  34. 2808                          &tx_req, &pdev->dev, "tx");  

DMA相关的代码,内核中EMMC驱动必然要使用DMA方式读写数据,

dma_request_slave_channel_compat分配一个DMA channel

[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. 2816     /* Request IRQ for MMC operations */  
  2. 2817     ret = devm_request_irq(&pdev->dev, host->irq, omap_hsmmc_irq, 0,  
  3. 2818             mmc_hostname(mmc), host);  
  4. 2819     if (ret) {  
  5. 2820         dev_err(mmc_dev(host->mmc), "Unable to grab HSMMC IRQ\n");  
  6. 2821         goto err_irq;  
  7. 2822     }  
设置中断处理函数,omap_hsmmc_irq负责处理EMMC 命令和EMMC DMA数据传输。

[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. 2864     mmc_add_host(mmc);  
  2.   
  3. drivers/mmc/core/host.c  
  4. 538 int mmc_add_host(struct mmc_host *host)  
  5. 539 {  
  6.   
  7. 557     mmc_start_host(host);  
  8.    
  9. 562 }  
初始换mmc host硬件,mmc_start_host会负责扫描mmc设备

mmc_start_host->_mmc_detect_change -> mmc_rescan ->mmc_rescan_try_freq,对于REMOVALBE设备,rescan可以进行多次,而对于mmc这样的NON-REMOVABLE设备,只需扫描一次即可。

host会使用不同的频率发送命令到device,因此这个函数也是尝试设置f_init的过程

[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. 2362 static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)  
  2. 2363 {  
  3. 2364     host->f_init = freq;  
  4. 2365   
  5. 2371     mmc_power_up(host, host->ocr_avail);  
  6. 2372   
  7. 2389     /* Order's important: probe SDIO, then SD, then MMC */  
  8. 2390     if (!mmc_attach_sdio(host))  
  9. 2391         return 0;  
  10. 2392     if (!mmc_attach_sd(host))  
  11. 2393         return 0;  
  12. 2394     if (!mmc_attach_mmc(host)) {  
  13. 2395         printk(KERN_ERR "%s: end, host->index=%d\n", __func__, host->index);  
  14. 2396         return 0;  
  15. 2397     }  
  16.   
  17. 2401 }  
分别用400K 300K  200K 100K 频率尝试和device通信,不过400K就会成功

mmc_power_up Power_up流程,可参见MMC spec5.0 A6.1,首先设置总线操作电压,然后设置clock

mmc_attach_mmc: mmc 卡初始化函数

[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. 1785 int mmc_attach_mmc(struct mmc_host *host)  
  2. 1786 {  
  3. 1792      
  4. 1794     /* Set correct bus mode for MMC before attempting attach */  
  5. 1795     if (!mmc_host_is_spi(host))  
  6. 1796         mmc_set_bus_mode(host, MMC_BUSMODE_OPENDRAIN);  
  7. 1797   
  8. 1798     err = mmc_send_op_cond(host, 0, &ocr);  
  9. 1799     if (err)  
  10. 1800         return err;  
  11. 1804   
  12. 1805     mmc_attach_bus_ops(host);  
  13. 1806     if (host->ocr_avail_mmc)  
  14. 1807         host->ocr_avail = host->ocr_avail_mmc;  
  15. 1808   
  16. 1817   
  17. 1818     rocr = mmc_select_voltage(host, ocr);  
  18. 1819   
  19. 1827   
  20. 1828     /*  
  21. 1829      * Detect and init the card.  
  22. 1830      */  
  23. 1831     err = mmc_init_card(host, rocr, NULL);  
  24. 1832     if (err)  
  25. 1833         goto err;  
  26. 1834   
  27. 1835     mmc_release_host(host);  
  28. 1836     err = mmc_add_card(host->card);  
  29. 1837     mmc_claim_host(host);  
  30. 1838     if (err)  
  31. 1839         goto remove_card;  
  32. 1840   
  33. 1842     return 0;  
  34. 1856 }  

mmc_send_op_cond等待device完成power up过程

mmc_select_voltage检查前面获得的ocr,判断ocr中标称的电压,返回的ocr为处理过的ocr

mmc_init_card 会读取cid csd ext_csd,并根据这些寄存器的值做一些初始化操作。

[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. 133 int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)  
  2. 134 {  
  3. 135     struct mmc_command cmd = {0};  
  4. 136     int i, err = 0;  
  5. 139   
  6. 140     cmd.opcode = MMC_SEND_OP_COND;  
  7. 141     cmd.arg = mmc_host_is_spi(host) ? 0 : ocr;  
  8. 142     cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R3 | MMC_CMD_BCR;  
  9. 143   
  10. 144     for (i = 100; i; i--) {  
  11. 145         err = mmc_wait_for_cmd(host, &cmd, 0);  
  12. 146         if (err)  
  13. 147             break;  
  14. 148   
  15. 149         /* if we're just probing, do a single pass */  
  16. 150         if (ocr == 0)  
  17. 151             break;  
  18. 152   
  19. 153         /* otherwise wait until reset completes */  
  20. 154         if (mmc_host_is_spi(host)) {  
  21. 155             if (!(cmd.resp[0] & R1_SPI_IDLE))  
  22. 156                 break;  
  23. 157         } else {  
  24. 158             if (cmd.resp[0] & MMC_CARD_BUSY)  
  25. 159                 break;  
  26. 160         }  
  27. 161   
  28. 162         err = -ETIMEDOUT;  
  29. 163   
  30. 164         mmc_delay(10);  
  31. 165     }  
  32. 166   
  33. 167     if (rocr && !mmc_host_is_spi(host))  
  34. 168         *rocr = cmd.resp[0];  
  35. 169   
  36. 170     return err;  
  37. 171 }  
145 发送CMD1并等待R3

158 如果发现ocr的最高位为1,表示mmc device已经完成power up,否则循环发送CMD1,直到mmc device 完成power up


[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1.  969 static int mmc_init_card(struct mmc_host *host, u32 ocr,  
  2.  970     struct mmc_card *oldcard)  
  3.  971 {  
  4.  995   
  5.  996     /* The extra bit indicates that we support high capacity */  
  6.  997     err = mmc_send_op_cond(host, ocr | (1 << 30), &rocr);  
  7.  998     if (err)  
  8.  999         goto err;  
  9. 1000   
  10. 1010     /*   
  11. 1011      * Fetch CID from card.  
  12. 1012      */          
  13. 1013     if (mmc_host_is_spi(host))  
  14. 1014         err = mmc_send_cid(host, cid);  
  15. 1015     else  
  16. 1016         err = mmc_all_send_cid(host, cid);  
  17. 1017     if (err)  
  18. 1018         goto err;  
997 ocr | (1 << 30) 表示驱动支持的是sector模式,Linux EMMC 子系统强制使用sector模式。

1016 mmc_all_send_cid 发送CMD10,并等待device 返回R2。CMD10请求设备发送CID给host

[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. 1047     /*  
  2. 1048      * For native busses:  set card RCA and quit open drain mode.  
  3. 1049      */  
  4. 1050     if (!mmc_host_is_spi(host)) {  
  5. 1051         err = mmc_set_relative_addr(card);  
  6. 1052         if (err)  
  7. 1053             goto free_card;  
  8. 1054   
  9. 1055         mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);  
  10. 1056     }  
对于本地总线,也就是相对于spi emmc总线,需要设置emmc chip地址,并且设置总线模式为PUSHPULL


[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. 1058     if (!oldcard) {  
  2. 1059         /*  
  3. 1060          * Fetch CSD from card.  
  4. 1061          */  
  5. 1062         err = mmc_send_csd(card, card->raw_csd);  
  6. 1063         if (err)  
  7. 1064             goto free_card;  
  8. 1065   
  9. 1066         err = mmc_decode_csd(card);  
  10. 1067         if (err)  
  11. 1068             goto free_card;  
  12. 1069         err = mmc_decode_cid(card);  
  13. 1070         if (err)  
  14. 1071             goto free_card;  
  15. 1072     }  
1062 发送MMC_SEND_CSD CMD9获取设备csd寄存器

[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. 1077     if (!mmc_host_is_spi(host)) {  
  2. 1078         err = mmc_select_card(card);  
  3. 1079         if (err)  
  4. 1080             goto free_card;  
  5. 1081     }  
 1078 发送MMC_SELECT_CARD CMD7选择设备,device的状态会从standby状态转化为transfer状态。
[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. 1083     if (!oldcard) {  
  2. 1084         /*  
  3. 1085          * Fetch and process extended CSD.  
  4. 1086          */  
  5. 1087   
  6. 1088         err = mmc_get_ext_csd(card, &ext_csd);  
  7. 1089         if (err)  
  8. 1090             goto free_card;  
  9. 1091         err = mmc_read_ext_csd(card, ext_csd);  
  10. 1092         if (err)  
  11. 1093             goto free_card;  
  12. 1094   
  13. 1095         /* If doing byte addressing, check if required to do sector  
  14. 1096          * addressing.  Handle the case of <2GB cards needing sector  
  15. 1097          * addressing.  See section 8.1 JEDEC Standard JED84-A441;  
  16. 1098          * ocr register has bit 30 set for sector addressing.  
  17. 1099          */  
  18. 1100         if (!(mmc_card_blockaddr(card)) && (rocr & (1<<30)))  
  19. 1101             mmc_card_set_blockaddr(card);  
  20. 1102   
  21. 1103         /* Erase size depends on CSD and Extended CSD */  
  22. 1104         mmc_set_erase_size(card);  
  23. 1105     }  

1088 获取ext_csd,ext_csd的获取,需要发送MMC_SEND_EXT_CSD,并从data线上获取device发送回ext_csd

1091 解析获取的ext_csd

[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. 1174     if (card->ext_csd.hs_max_dtr != 0) {  
  2. 1175         err = 0;  
  3. 1176         if (card->ext_csd.hs_max_dtr > 52000000 &&  
  4. 1177             host->caps2 & MMC_CAP2_HS200)  
  5. 1178             err = mmc_select_hs200(card);  
  6. 1179         else if (host->caps & MMC_CAP_MMC_HIGHSPEED)  
  7. 1180             err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,  
  8. 1181                      EXT_CSD_HS_TIMING, 1,  
  9. 1182                      card->ext_csd.generic_cmd6_time);  
  10. 1183   
  11. 1184         if (err && err != -EBADMSG)  
  12. 1185             goto free_card;  
  13. 1186   
  14. 1187         if (err) {  
  15. 1188             pr_warning("%s: switch to highspeed failed\n",  
  16. 1189                    mmc_hostname(card->host));  
  17. 1190             err = 0;  
  18. 1191         } else {  
  19. 1192             if (card->ext_csd.hs_max_dtr > 52000000 &&  
  20. 1193                 host->caps2 & MMC_CAP2_HS200) {  
  21. 1194                 mmc_card_set_hs200(card);  
  22. 1195                 mmc_set_timing(card->host,  
  23. 1196                            MMC_TIMING_MMC_HS200);  
  24. 1198             } else {  
  25. 1199                 mmc_card_set_highspeed(card);  
  26. 1200                 mmc_set_timing(card->host, MMC_TIMING_MMC_HS);  
  27. 1202             }  
  28. 1203         }  
  29. 1204     }  
ext_csd.hs_max_dtr已经在mmc_select_card_type中设置为可支持的最大速度,这个值是由ext_csd[CARD_TYPE]以及host-caps2来决定的。比如host支持HS200,device 也支持HS200

那么理论上总线应该能跑到HS200,此时就设置ext_csd.hs_max_dtr为MMC_HS200_MAX_DTR

mmc_select_hs200设置device的timing为HS200

mmc_set_timing则设置host的timing 为HS200

[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. 1211     if (mmc_card_highspeed(card) || mmc_card_hs200(card)) {  
  2. 1212         if (max_dtr > card->ext_csd.hs_max_dtr)  
  3. 1213             max_dtr = card->ext_csd.hs_max_dtr;  
  4. 1214         if (mmc_card_highspeed(card) && (max_dtr > 52000000))  
  5. 1215             max_dtr = 52000000;  
  6. 1216     } else if (max_dtr > card->csd.max_dtr) {  
  7. 1217         max_dtr = card->csd.max_dtr;  
  8. 1218     }  
  9. 1219   
  10. 1220     printk(KERN_ERR "%s: mmc_set_clock() max_dtr=%d\n", __func__, max_dtr);  
  11. 1221     mmc_set_clock(host, max_dtr);  

设置host的最大clock

[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. 1267         ext_csd_bits = (bus_width == MMC_BUS_WIDTH_8) ?  
  2. 1268                 EXT_CSD_BUS_WIDTH_8 : EXT_CSD_BUS_WIDTH_4;  
  3. 1269         err = mmc_select_powerclass(card, ext_csd_bits);  
1269 设置device 的power class。关于power class, EMMC spec为了让host端控制EMMC芯片的耗电范围,允许host设置ext_csd的POWER_CLASS[187]来控制device的耗电行为。

但是低耗电带来的副作用就是性能的损失,所以linux emmc core 驱动都是性能优先。


mmc_add_card

[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. 309 int mmc_add_card(struct mmc_card *card)  
  2. 310 {  
  3. 351   
  4. 352     if (mmc_card_uhs(card) &&  
  5. 353         (card->sd_bus_speed < ARRAY_SIZE(uhs_speeds)))  
  6. 354         uhs_bus_speed_mode = uhs_speeds[card->sd_bus_speed];  
  7. 355   
  8. 356     if (mmc_host_is_spi(card->host)) {  
  9. 357         pr_info("%s: new %s%s%s card on SPI\n",  
  10. 358             mmc_hostname(card->host),  
  11. 359             mmc_card_highspeed(card) ? "high speed " : "",  
  12. 360             mmc_card_ddr_mode(card) ? "DDR " : "",  
  13. 361             type);  
  14. 362     } else {  
  15. 363         pr_info("%s: new %s%s%s%s%s card at address %04x\n",  
  16. 364             mmc_hostname(card->host),  
  17. 365             mmc_card_uhs(card) ? "ultra high speed " :  
  18. 366             (mmc_card_highspeed(card) ? "high speed " : ""),  
  19. 367             (mmc_card_hs200(card) ? "HS200 " : ""),  
  20. 368             mmc_card_ddr_mode(card) ? "DDR " : "",  
  21. 369             uhs_bus_speed_mode, type, card->rca);  
  22. 370     }  
  23. 379     ret = device_add(&card->dev);  
  24.   
  25.    

352~370会输出mmc card的一些信息,比如high speed, HS200;DDR等

379会调用设备模型,增加设备,因为card属于mmc_bus,会调用mmc_bus_probe

[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. 110 static int mmc_bus_probe(struct device *dev)  
  2. 111 {  
  3. 112     int ret;  
  4. 113     struct mmc_driver *drv = to_mmc_driver(dev->driver);  
  5. 114     struct mmc_card *card = mmc_dev_to_card(dev);  
  6. 115   
  7. 117     ret = drv->probe(card);  
  8. 118   
  9. 120     return ret;  
  10. 121 }  
drv->probe对应的是mmc/card/block.c中的mmc_driver-.probe

这样理解,每个mmc设备也是一个block设备,因此这个mmc设备,也要对应一个mmc block驱动。mmc block驱动需要调用驱动本身的probe函数,探测这个block设备

[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. 2429 static int mmc_blk_probe(struct mmc_card *card)  
  2. 2430 {  
  3. 2431     struct mmc_blk_data *md, *part_md;  
  4. 2432     char cap_str[10];  
  5. 2439   
  6. 2440     md = mmc_blk_alloc(card);  
  7. 2441     if (IS_ERR(md))  
  8. 2442         return PTR_ERR(md);  
  9. 2443   
  10. 2444     string_get_size((u64)get_capacity(md->disk) << 9, STRING_UNITS_2,  
  11. 2445             cap_str, sizeof(cap_str));  
  12. 2446     pr_info("%s: %s %s %s %s\n",  
  13. 2447         md->disk->disk_name, mmc_card_id(card), mmc_card_name(card),  
  14. 2448         cap_str, md->read_only ? "(ro)" : "");  
  15. 2449   
  16. 2450     if (mmc_blk_alloc_parts(card, md))  
  17. 2451         goto out;  
  18. 2452   
  19. 2453     mmc_set_drvdata(card, md);  
  20. 2454     mmc_fixup_device(card, blk_fixups);  
  21. 2455   
  22. 2456     if (mmc_add_disk(md))  
  23. 2457         goto out;  
  24. 2458   
  25. 2459     list_for_each_entry(part_md, &md->part, part) {  
  26. 2460         if (mmc_add_disk(part_md))  
  27. 2461             goto out;  
  28. 2462     }  
  29. 2467     /*  
  30. 2468      * Don't enable runtime PM for SD-combo cards here. Leave that  
  31. 2469      * decision to be taken during the SDIO init sequence instead.  
  32. 2470      */  
  33. 2471     if (card->type != MMC_TYPE_SD_COMBO) {  
  34. 2472         pm_runtime_set_active(&card->dev);  
  35. 2473         pm_runtime_enable(&card->dev);  
  36. 2474     }  
  37. 2475   
  38. 2476     return 0;  
  39. 2477   
  40. 2478  out:  
  41. 2479     mmc_blk_remove_parts(card, md);  
  42. 2480     mmc_blk_remove_req(md);  
  43. 2481     return 0;  
  44. 2482 }  

2440 每一个mmc_card都对应一个mmc_blk_data,mmc_blk_data管理快设备相关的数据。

2450 mmc设备内部可能预先分配了boot partitions,以及最多四个general purpose partitions,需要为这些内置分区分配part。注意这些分区在系统中是以通用磁盘的形式存在的,并不是传统意义上的分区。

2456 mmc_add_disk增加mmc 设备到系统中,mmc_add_disk会调用add_disk向系统内增加通用硬盘,这个函数比较复杂,会单独开一个帖子分析add_disk的流程。

mmc_add_disk结束后,mmc设备的分区信息也已经添加到系统中。

mmc_blk_data主要实现函数是mmc_blk_alloc_req

[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. 2074 static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card,  
  2. 2075                           struct device *parent,  
  3. 2076                           sector_t size,  
  4. 2077                           bool default_ro,  
  5. 2078                           const char *subname,  
  6. 2079                           int area_type)  
  7. 2080 {  
  8.   
  9. 2116     md->disk = alloc_disk(perdev_minors);  
  10. 2117     if (md->disk == NULL) {  
  11. 2118         ret = -ENOMEM;  
  12. 2119         goto err_kfree;  
  13. 2120     }  
  14.   
  15. 2126     ret = mmc_init_queue(&md->queue, card, &md->lock, subname);  
  16. 2127     if (ret)  
  17. 2128         goto err_putdisk;  

2116 alloc_disk分配一个gendisk结构,在linux内核中gendisk用来表示一个磁盘或者分区

2126 初始化该块设备的queue,从这里我们可以看出不管mmc_blk_data对应是磁盘还是分区,都会为它分配queue。这是合理的,比如我们可以通过/dev/mmcblk0访问设备,也可以通过/dev/mmcblk0p1访问设备。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值