Linux EMMC子系統分析-初始化流程

最近在解EMMC的一個bug,發現Linux EMMC有點小複雜,先整理個文檔出來吧

用的是TI 平臺,僅分析MMC,不分析SD和SDIO


mmc_init

2769 static int __init mmc_init(void)
2770 {
2774 
2775     workqueue = alloc_ordered_workqueue("kmmcd", 0);
2776     if (!workqueue)
2777         return -ENOMEM;
2778 
2779     ret = mmc_register_bus();
2780     if (ret)
2781         goto destroy_workqueue;
2782 
2783     ret = mmc_register_host_class();
2784     if (ret)
2785         goto unregister_bus;;
2793 

2802 }

2775分配一個workqueue,這個workqueue是專門用來處理card detect的,EMMC因爲是unremovable的,所以不需要關注它

2779 註冊mmc_bus_type,實現如下:

int mmc_register_bus(void)
{
    return bus_register(&mmc_bus_type);
}
把mmc_bus_type註冊到bus系統,後面調用device_add函數時,則會輾轉調用到mmc_bus_type中的probe函數,不必太糾結在這個代碼


mmc_init調用時間比較早,會在host驅動初始化之前執行完。

subsys_initcall(mmc_init);

host驅動初始化

kernel/drivers/mmc/host/omap_hsmmc.c

host驅動位置是kernel/drivers/mmc/host/,一般來說都是platform_driver,以omap hsmmc host驅動爲例

    <strong>.probe      = omap_hsmmc_probe,</strong>
    .remove     = omap_hsmmc_remove,
    .driver     = {
        .name = DRIVER_NAME,
        .owner = THIS_MODULE,
        .pm = &omap_hsmmc_dev_pm_ops,
        .of_match_table = of_match_ptr(omap_mmc_of_match),
    },
};

module_platform_driver(omap_hsmmc_driver);

只需要關注omap_hsmmc_probe即可,當系統匹配到platform_device時,會調用omap_hsmmc_probe,好長的一個函數

2627     base = devm_ioremap_resource(&pdev->dev, res);
2628     if (IS_ERR(base))
2629         return PTR_ERR(base);
2630 
2631     ret = omap_hsmmc_gpio_init(pdata);
2632     if (ret)
2633         goto err;
devm_ioremap_resource,實質就是ioremap,設備特定io資源到內核地址的映射,自行腦補一下ioremap。

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

2635     mmc = mmc_alloc_host(sizeof(struct omap_hsmmc_host), &pdev->dev);
2636     if (!mmc) {
2637         ret = -ENOMEM;
2638         goto err_alloc;
2639     }
...............
mmc_alloc_host,分配一個omap_hsmmc_host結構(包含struct mmc_host),以供後面玩耍

2661     mmc->ops    = &omap_hsmmc_ops;

2410 static const struct mmc_host_ops omap_hsmmc_ops = {
2411     .enable = omap_hsmmc_enable_fclk,
2412     .disable = omap_hsmmc_disable_fclk,
2413     .post_req = omap_hsmmc_post_req,
2414     .pre_req = omap_hsmmc_pre_req,
2415     .request = omap_hsmmc_request,
2416     .set_ios = omap_hsmmc_set_ios,
2417     .get_cd = omap_hsmmc_get_cd,
2418     .get_ro = omap_hsmmc_get_ro,
2419     .init_card = omap_hsmmc_init_card,
2420     .start_signal_voltage_switch = omap_start_signal_voltage_switch,
2421     .card_busy = omap_hsmmc_card_busy,
2422     .execute_tuning = omap_execute_tuning,
2423     /* NYET -- enable_sdio_irq */
2424 };
 omap hsmmc host驅動和mmc子系統的所有接口都在這裏了。

2663     mmc->f_min = OMAP_MMC_MIN_CLOCK;

2675     if (pdata->max_freq > 0) {
2676         mmc->f_max = pdata->max_freq;
2677         ret = clk_set_rate(host->fclk, pdata->max_freq);
2678         if (ret) {
2679             dev_err(dev, "failed to set clock to %d\n",
2680                 pdata->max_freq);
2681             goto err1;
2682         }
2683     } else {
2684         mmc->f_max = OMAP_MMC_MAX_CLOCK;
2685     }


定義了最小時鐘400K,在系統初始化階段爲了兼容的原因會使用這個時鐘。最大時鐘爲52MHZ,後面還有機會再修改這個時鐘頻率。

2724     mmc->max_segs = 1024;
2725 
2726     mmc->max_blk_size = 512;       /* Block Length at max can be 1024 */
2727     mmc->max_blk_count = 0xFFFF;    /* No. of Blocks is 16 bits */
2728     mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
2729     mmc->max_seg_size = mmc->max_req_size;

mmc作爲快設備,最主要的操作就是數據sectors讀寫,max_segs定義了block設備的request所支持的最大segment

max_blk_count 一個request中所包含的最大block數

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

2731     mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED |
2732              MMC_CAP_WAIT_WHILE_BUSY | MMC_CAP_ERASE;
2733 
2734     mmc->caps |= mmc_slot(host).caps;
2735     if (mmc->caps & MMC_CAP_8_BIT_DATA)
2736         mmc->caps |= MMC_CAP_4_BIT_DATA;
2737     if (mmc_slot(host).nonremovable)
2738         mmc->caps |= MMC_CAP_NONREMOVABLE;

2741 
2742     if (of_property_read_bool(np, "sd-uhs-sdr12"))
2743         mmc->caps |= MMC_CAP_UHS_SDR12;
2744     if (of_property_read_bool(np, "sd-uhs-sdr25"))
2745         mmc->caps |= MMC_CAP_UHS_SDR25;
2746     if (of_property_read_bool(np, "sd-uhs-sdr50"))
2747         mmc->caps |= MMC_CAP_UHS_SDR50;
2748     if (of_property_read_bool(np, "sd-uhs-sdr104"))
2749         mmc->caps |= MMC_CAP_UHS_SDR104;
2750     if (of_property_read_bool(np, "sd-uhs-ddr50"))
2751         mmc->caps |= MMC_CAP_UHS_DDR50;
2752     if (of_property_read_bool(np, "mmc-ddr-1_8v"))
2753         mmc->caps |= MMC_CAP_1_8V_DDR;
2754     if (of_property_read_bool(np, "mmc-hs200-1_8v"))
2755         mmc->caps2 |= MMC_CAP2_HS200_1_8V_SDR;

 
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



2773     omap_hsmmc_conf_bus_power(host);
設置EMMC總線電壓,EMMC芯片支持3.3V, 1.8V和1.2V總線信號電壓,host需要和EMMC芯片的電壓匹配(就是相同了)。

2775     if (!pdev->dev.of_node) {
2776         res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx");
2777         if (!res) {
2778             dev_err(mmc_dev(host->mmc), "cannot get DMA TX channel\n");
2779             ret = -ENXIO;
2780             goto err_irq;
2781         }
2782         tx_req = res->start;
2783 
2784         res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx");
2785         if (!res) {
2786             dev_err(mmc_dev(host->mmc), "cannot get DMA RX channel\n");
2787             ret = -ENXIO;
2788             goto err_irq;
2789         }
2790         rx_req = res->start;
2791     }
2792 
2793     dma_cap_zero(mask);
2794     dma_cap_set(DMA_SLAVE, mask);
2795 
2796     host->rx_chan =
2797         dma_request_slave_channel_compat(mask, omap_dma_filter_fn,
2798                          &rx_req, &pdev->dev, "rx");
2799 
2800     if (!host->rx_chan) {
2801         dev_err(mmc_dev(host->mmc), "unable to obtain RX DMA engine channel %u\n", rx_req);
2802         ret = -ENXIO;
2803         goto err_irq;
2804     }
2805 
2806     host->tx_chan =
2807         dma_request_slave_channel_compat(mask, omap_dma_filter_fn,
2808                          &tx_req, &pdev->dev, "tx");

DMA相關的代碼,內核中EMMC驅動必然要使用DMA方式讀寫數據,

dma_request_slave_channel_compat分配一個DMA channel

2816     /* Request IRQ for MMC operations */
2817     ret = devm_request_irq(&pdev->dev, host->irq, omap_hsmmc_irq, 0,
2818             mmc_hostname(mmc), host);
2819     if (ret) {
2820         dev_err(mmc_dev(host->mmc), "Unable to grab HSMMC IRQ\n");
2821         goto err_irq;
2822     }
設置中斷處理函數,omap_hsmmc_irq負責處理EMMC 命令和EMMC DMA數據傳輸。

2864     mmc_add_host(mmc);

drivers/mmc/core/host.c
538 int mmc_add_host(struct mmc_host *host)
539 {

557     mmc_start_host(host);
 
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的過程

2362 static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
2363 {
2364     host->f_init = freq;
2365 
2371     mmc_power_up(host, host->ocr_avail);
2372 
2389     /* Order's important: probe SDIO, then SD, then MMC */
2390     if (!mmc_attach_sdio(host))
2391         return 0;
2392     if (!mmc_attach_sd(host))
2393         return 0;
2394     if (!mmc_attach_mmc(host)) {
2395         printk(KERN_ERR "%s: end, host->index=%d\n", __func__, host->index);
2396         return 0;
2397     }

2401 }
分別用400K 300K  200K 100K 頻率嘗試和device通信,不過400K就會成功

mmc_power_up Power_up流程,可參見MMC spec5.0 A6.1,首先設置總線操作電壓,然後設置clock

mmc_attach_mmc: mmc 卡初始化函數

1785 int mmc_attach_mmc(struct mmc_host *host)
1786 {
1792    
1794     /* Set correct bus mode for MMC before attempting attach */
1795     if (!mmc_host_is_spi(host))
1796         mmc_set_bus_mode(host, MMC_BUSMODE_OPENDRAIN);
1797 
1798     err = mmc_send_op_cond(host, 0, &ocr);
1799     if (err)
1800         return err;
1804 
1805     mmc_attach_bus_ops(host);
1806     if (host->ocr_avail_mmc)
1807         host->ocr_avail = host->ocr_avail_mmc;
1808 
1817 
1818     rocr = mmc_select_voltage(host, ocr);
1819 
1827 
1828     /*
1829      * Detect and init the card.
1830      */
1831     err = mmc_init_card(host, rocr, NULL);
1832     if (err)
1833         goto err;
1834 
1835     mmc_release_host(host);
1836     err = mmc_add_card(host->card);
1837     mmc_claim_host(host);
1838     if (err)
1839         goto remove_card;
1840 
1842     return 0;
1856 }

mmc_send_op_cond等待device完成power up過程

mmc_select_voltage檢查前面獲得的ocr,判斷ocr中標稱的電壓,返回的ocr爲處理過的ocr

mmc_init_card 會讀取cid csd ext_csd,並根據這些寄存器的值做一些初始化操作。

133 int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
134 {
135     struct mmc_command cmd = {0};
136     int i, err = 0;
139 
140     cmd.opcode = MMC_SEND_OP_COND;
141     cmd.arg = mmc_host_is_spi(host) ? 0 : ocr;
142     cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R3 | MMC_CMD_BCR;
143 
144     for (i = 100; i; i--) {
145         err = mmc_wait_for_cmd(host, &cmd, 0);
146         if (err)
147             break;
148 
149         /* if we're just probing, do a single pass */
150         if (ocr == 0)
151             break;
152 
153         /* otherwise wait until reset completes */
154         if (mmc_host_is_spi(host)) {
155             if (!(cmd.resp[0] & R1_SPI_IDLE))
156                 break;
157         } else {
158             if (cmd.resp[0] & MMC_CARD_BUSY)
159                 break;
160         }
161 
162         err = -ETIMEDOUT;
163 
164         mmc_delay(10);
165     }
166 
167     if (rocr && !mmc_host_is_spi(host))
168         *rocr = cmd.resp[0];
169 
170     return err;
171 }
145 發送CMD1並等待R3

158 如果發現ocr的最高位爲1,表示mmc device已經完成power up,否則循環發送CMD1,直到mmc device 完成power up


 969 static int mmc_init_card(struct mmc_host *host, u32 ocr,
 970     struct mmc_card *oldcard)
 971 {
 995 
 996     /* The extra bit indicates that we support high capacity */
 997     err = mmc_send_op_cond(host, ocr | (1 << 30), &rocr);
 998     if (err)
 999         goto err;
1000 
1010     /* 
1011      * Fetch CID from card.
1012      */        
1013     if (mmc_host_is_spi(host))
1014         err = mmc_send_cid(host, cid);
1015     else
1016         err = mmc_all_send_cid(host, cid);
1017     if (err)
1018         goto err;


997 ocr | (1 << 30) 表示驅動支持的是sector模式,Linux EMMC 子系統強制使用sector模式。

1016 mmc_all_send_cid 發送CMD10,並等待device 返回R2。CMD10請求設備發送CID給host

1047     /*
1048      * For native busses:  set card RCA and quit open drain mode.
1049      */
1050     if (!mmc_host_is_spi(host)) {
1051         err = mmc_set_relative_addr(card);
1052         if (err)
1053             goto free_card;
1054 
1055         mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
1056     }
對於本地總線,也就是相對於spi emmc總線,需要設置emmc chip地址,並且設置總線模式爲PUSHPULL


1058     if (!oldcard) {
1059         /*
1060          * Fetch CSD from card.
1061          */
1062         err = mmc_send_csd(card, card->raw_csd);
1063         if (err)
1064             goto free_card;
1065 
1066         err = mmc_decode_csd(card);
1067         if (err)
1068             goto free_card;
1069         err = mmc_decode_cid(card);
1070         if (err)
1071             goto free_card;
1072     }
1062 發送MMC_SEND_CSD CMD9獲取設備csd寄存器

1077     if (!mmc_host_is_spi(host)) {
1078         err = mmc_select_card(card);
1079         if (err)
1080             goto free_card;
1081     }
 1078 發送MMC_SELECT_CARD CMD7選擇設備,device的狀態會從standby狀態轉化爲transfer狀態。
1083     if (!oldcard) {
1084         /*
1085          * Fetch and process extended CSD.
1086          */
1087 
1088         err = mmc_get_ext_csd(card, &ext_csd);
1089         if (err)
1090             goto free_card;
1091         err = mmc_read_ext_csd(card, ext_csd);
1092         if (err)
1093             goto free_card;
1094 
1095         /* If doing byte addressing, check if required to do sector
1096          * addressing.  Handle the case of <2GB cards needing sector
1097          * addressing.  See section 8.1 JEDEC Standard JED84-A441;
1098          * ocr register has bit 30 set for sector addressing.
1099          */
1100         if (!(mmc_card_blockaddr(card)) && (rocr & (1<<30)))
1101             mmc_card_set_blockaddr(card);
1102 
1103         /* Erase size depends on CSD and Extended CSD */
1104         mmc_set_erase_size(card);
1105     }

1088 獲取ext_csd,ext_csd的獲取,需要發送MMC_SEND_EXT_CSD,並從data線上獲取device發送回ext_csd

1091 解析獲取的ext_csd

1174     if (card->ext_csd.hs_max_dtr != 0) {
1175         err = 0;
1176         if (card->ext_csd.hs_max_dtr > 52000000 &&
1177             host->caps2 & MMC_CAP2_HS200)
1178             err = mmc_select_hs200(card);
1179         else if (host->caps & MMC_CAP_MMC_HIGHSPEED)
1180             err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
1181                      EXT_CSD_HS_TIMING, 1,
1182                      card->ext_csd.generic_cmd6_time);
1183 
1184         if (err && err != -EBADMSG)
1185             goto free_card;
1186 
1187         if (err) {
1188             pr_warning("%s: switch to highspeed failed\n",
1189                    mmc_hostname(card->host));
1190             err = 0;
1191         } else {
1192             if (card->ext_csd.hs_max_dtr > 52000000 &&
1193                 host->caps2 & MMC_CAP2_HS200) {
1194                 mmc_card_set_hs200(card);
1195                 mmc_set_timing(card->host,
1196                            MMC_TIMING_MMC_HS200);
1198             } else {
1199                 mmc_card_set_highspeed(card);
1200                 mmc_set_timing(card->host, MMC_TIMING_MMC_HS);
1202             }
1203         }
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

1211     if (mmc_card_highspeed(card) || mmc_card_hs200(card)) {
1212         if (max_dtr > card->ext_csd.hs_max_dtr)
1213             max_dtr = card->ext_csd.hs_max_dtr;
1214         if (mmc_card_highspeed(card) && (max_dtr > 52000000))
1215             max_dtr = 52000000;
1216     } else if (max_dtr > card->csd.max_dtr) {
1217         max_dtr = card->csd.max_dtr;
1218     }
1219 
1220     printk(KERN_ERR "%s: mmc_set_clock() max_dtr=%d\n", __func__, max_dtr);
1221     mmc_set_clock(host, max_dtr);

設置host的最大clock

1267         ext_csd_bits = (bus_width == MMC_BUS_WIDTH_8) ?
1268                 EXT_CSD_BUS_WIDTH_8 : EXT_CSD_BUS_WIDTH_4;
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

309 int mmc_add_card(struct mmc_card *card)
310 {
351 
352     if (mmc_card_uhs(card) &&
353         (card->sd_bus_speed < ARRAY_SIZE(uhs_speeds)))
354         uhs_bus_speed_mode = uhs_speeds[card->sd_bus_speed];
355 
356     if (mmc_host_is_spi(card->host)) {
357         pr_info("%s: new %s%s%s card on SPI\n",
358             mmc_hostname(card->host),
359             mmc_card_highspeed(card) ? "high speed " : "",
360             mmc_card_ddr_mode(card) ? "DDR " : "",
361             type);
362     } else {
363         pr_info("%s: new %s%s%s%s%s card at address %04x\n",
364             mmc_hostname(card->host),
365             mmc_card_uhs(card) ? "ultra high speed " :
366             (mmc_card_highspeed(card) ? "high speed " : ""),
367             (mmc_card_hs200(card) ? "HS200 " : ""),
368             mmc_card_ddr_mode(card) ? "DDR " : "",
369             uhs_bus_speed_mode, type, card->rca);
370     }
379     ret = device_add(&card->dev);

 

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

379會調用設備模型,增加設備,因爲card屬於mmc_bus,會調用mmc_bus_probe

110 static int mmc_bus_probe(struct device *dev)
111 {
112     int ret;
113     struct mmc_driver *drv = to_mmc_driver(dev->driver);
114     struct mmc_card *card = mmc_dev_to_card(dev);
115 
117     ret = drv->probe(card);
118 
120     return ret;
121 }
drv->probe對應的是mmc/card/block.c中的mmc_driver-.probe

這樣理解,每個mmc設備也是一個block設備,因此這個mmc設備,也要對應一個mmc block驅動。mmc block驅動需要調用驅動本身的probe函數,探測這個block設備

2429 static int mmc_blk_probe(struct mmc_card *card)
2430 {
2431     struct mmc_blk_data *md, *part_md;
2432     char cap_str[10];
2439 
2440     md = mmc_blk_alloc(card);
2441     if (IS_ERR(md))
2442         return PTR_ERR(md);
2443 
2444     string_get_size((u64)get_capacity(md->disk) << 9, STRING_UNITS_2,
2445             cap_str, sizeof(cap_str));
2446     pr_info("%s: %s %s %s %s\n",
2447         md->disk->disk_name, mmc_card_id(card), mmc_card_name(card),
2448         cap_str, md->read_only ? "(ro)" : "");
2449 
2450     if (mmc_blk_alloc_parts(card, md))
2451         goto out;
2452 
2453     mmc_set_drvdata(card, md);
2454     mmc_fixup_device(card, blk_fixups);
2455 
2456     if (mmc_add_disk(md))
2457         goto out;
2458 
2459     list_for_each_entry(part_md, &md->part, part) {
2460         if (mmc_add_disk(part_md))
2461             goto out;
2462     }
2467     /*
2468      * Don't enable runtime PM for SD-combo cards here. Leave that
2469      * decision to be taken during the SDIO init sequence instead.
2470      */
2471     if (card->type != MMC_TYPE_SD_COMBO) {
2472         pm_runtime_set_active(&card->dev);
2473         pm_runtime_enable(&card->dev);
2474     }
2475 
2476     return 0;
2477 
2478  out:
2479     mmc_blk_remove_parts(card, md);
2480     mmc_blk_remove_req(md);
2481     return 0;
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

2074 static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card,
2075                           struct device *parent,
2076                           sector_t size,
2077                           bool default_ro,
2078                           const char *subname,
2079                           int area_type)
2080 {

2116     md->disk = alloc_disk(perdev_minors);
2117     if (md->disk == NULL) {
2118         ret = -ENOMEM;
2119         goto err_kfree;
2120     }

2126     ret = mmc_init_queue(&md->queue, card, &md->lock, subname);
2127     if (ret)
2128         goto err_putdisk;


2116 alloc_disk分配一個gendisk結構,在linux內核中gendisk用來表示一個磁盤或者分區

2126 初始化該塊設備的queue,從這裏我們可以看出不管mmc_blk_data對應是磁盤還是分區,都會爲它分配queue。這是合理的,比如我們可以通過/dev/mmcblk0訪問設備,也可以通過/dev/mmcblk0p1訪問設備。











發佈了183 篇原創文章 · 獲贊 42 · 訪問量 142萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章