首页 > 代码库 > EMMC架构

EMMC架构

现在EMMC盛行,分析总结还是很有必要的。以下以全志a64为实例切入主题。

这里a64有三个sdc0~2,硬件上sdc2是连接EMMC,这里只分析sdc2的代码。


初始化的代码在linux-3.10/drivers/mmc/host/sunxi-mmc.c
以下忽略部分冗余代码:

 1 static const struct of_device_id sunxi_mmc_of_match[] = { 2     ...... 3     ...... 4     { .compatible = "allwinner,sun50i-sdmmc2", }, 5     ...... 6     ...... 7 }; 8 MODULE_DEVICE_TABLE(of, sunxi_mmc_of_match); 9 10 static struct platform_driver sunxi_mmc_driver = {11     .driver = {12         .name    = "sunxi-mmc",13         .of_match_table = of_match_ptr(sunxi_mmc_of_match),14         .pm = sunxi_mmc_pm_ops,15     },16     .probe        = sunxi_mmc_probe,17     .remove        = sunxi_mmc_remove,18     .shutdown   = sunxi_shutdown_mmc,19 };20 module_platform_driver(sunxi_mmc_driver);

 

设备树会初始化deivce,这里有driver,下面直接进sunxi_mmc_probe分析。
以下忽略部分冗余代码:

 1 static int sunxi_mmc_probe(struct platform_device *pdev) 2 { 3     struct sunxi_mmc_host *host;    //全志a64主控硬件相关 4     struct mmc_host *mmc;    //emmc架构相关 5     int ret; 6  7     dev_info(&pdev->dev,"%s\n",DRIVER_VERSION); 8  9     mmc = mmc_alloc_host(sizeof(struct sunxi_mmc_host), &pdev->dev);    //初始化mmc_host,后面会分析10 11     host = mmc_priv(mmc);    //host = mmc->private;12     host->mmc = mmc;        //sunxi_mmc_host和mmc_host建立纽带13 14     //配置硬件相关,如时钟,sdc控制寄存器,IO复用,中断,电源控制15     ret = sunxi_mmc_resource_request(host, pdev);    16 17     //初始化dma     #define     4*PAGE_SIZE18     host->dma_mask = DMA_BIT_MASK(32);19     pdev->dev.dma_mask = &host->dma_mask;20     pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);21     host->sg_cpu = dma_alloc_coherent(&pdev->dev, SUNXI_REQ_PAGE_SIZE,22                       &host->sg_dma, GFP_KERNEL);    23 24     mmc->ops        = &sunxi_mmc_ops;25     mmc->max_blk_count    = 8192;26     mmc->max_blk_size    = 4096;27     mmc->max_segs        = SUNXI_REQ_PAGE_SIZE / sizeof(struct sunxi_idma_des);28     mmc->max_seg_size    = (1 << host->idma_des_size_bits);29     mmc->max_req_size    = mmc->max_seg_size * mmc->max_segs;30     /* 400kHz ~ 50MHz */31     mmc->f_min        =   400000;32     mmc->f_max        = 50000000;33     mmc->caps           |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED | MMC_CAP_ERASE 34                         | MMC_CAP_WAIT_WHILE_BUSY;35     //mmc->caps2      |= MMC_CAP2_HS400_1_8V;36 37 38     mmc_of_parse(mmc);    //解析设备树相关的信息39 40     ret = mmc_add_host(mmc);    //重点,真正初始化emmc并启动,后面会分析41 42     ret = mmc_create_sys_fs(host,pdev);43     44     dev_info(&pdev->dev, "base:0x%p irq:%u\n", host->reg_base, host->irq);45     platform_set_drvdata(pdev, mmc);46     return 0;47 }

到此probe结束,有两个比较关键的函数mmc_alloc_host和mmc_add_host,真正启动emmc的操作都在这里
mmc_alloc_host和mmc_add_host的代码在linux-3.10/drivers/mmc/core/host.c
以下忽略部分冗余代码:

 1 struct mmc_host *mmc_alloc_host(int extra, struct device *dev) 2 { 3     int err; 4     struct mmc_host *host; 5  6     host = kzalloc(sizeof(struct mmc_host) + extra, GFP_KERNEL); 7  8     /* scanning will be enabled when we‘re ready */ 9     host->rescan_disable = 1;10     idr_preload(GFP_KERNEL);    11     12     err = idr_alloc(&mmc_host_idr, host, 0, 0, GFP_NOWAIT);    13     if (err >= 0)14         host->index = err;    //通过整数id管理地址,具体细节本人不太清楚15 16     idr_preload_end();17 18     dev_set_name(&host->class_dev, "mmc%d", host->index);19 20     host->parent = dev;21     host->class_dev.parent = dev;22     host->class_dev.class = &mmc_host_class;23     device_initialize(&host->class_dev);24 25     mmc_host_clk_init(host);    //这里初始化框架相关的clock,不涉及硬件26 27     host->slot.cd_irq = -EINVAL;28 29     init_waitqueue_head(&host->wq);30     wake_lock_init(&host->detect_wake_lock, WAKE_LOCK_SUSPEND,    //初始化唤醒内核的锁31         kasprintf(GFP_KERNEL, "%s_detect", mmc_hostname(host)));32     INIT_DELAYED_WORK(&host->detect, mmc_rescan);        //重要,初始化检查card的队列33 34     host->pm_notify.notifier_call = mmc_pm_notify;35     /*36      * By default, hosts do not support SGIO or large requests.37      * They have to set these according to their abilities.38      */39     host->max_segs = 1;40     host->max_seg_size = PAGE_CACHE_SIZE;    //#define  PAGE_CACHE_SIZE    1UL << 1641 42     host->max_req_size = PAGE_CACHE_SIZE;43     host->max_blk_size = 512;44     host->max_blk_count = PAGE_CACHE_SIZE / 512;45 46     return host;47 }    48 49 int mmc_add_host(struct mmc_host *host)50 {51     int err;52 53     err = device_add(&host->class_dev);54 55     led_trigger_register_simple(dev_name(&host->class_dev), &host->led);    //;类似硬盘的LED闪烁56 57 #ifdef CONFIG_DEBUG_FS58     mmc_add_host_debugfs(host);    //debug用59 #endif60     mmc_host_clk_sysfs_init(host);61 62     mmc_start_host(host);    //重要,真正操作都在这里63 64     return 0;65 }

 

mmc_start_host的代码在linux-3.10/drivers/mmc/core/core.c
以下忽略部分冗余代码:

void mmc_start_host(struct mmc_host *host){    host->f_init = max(freqs[0], host->f_min);    //初始时钟    host->rescan_disable = 0;    //开启rescan    mmc_power_up(host);    mmc_detect_change(host, 0);}static void mmc_power_up(struct mmc_host *host){    int bit;    if (host->ios.power_mode == MMC_POWER_ON)    //已经启动        return;    mmc_host_clk_hold(host);    //没有定义CONFIG_MMC_CLKGATE 所有gateclock相关都是空操作    /* If ocr is set, we use it */    if (host->ocr)    //ocr是电源配置,在sunxi_mmc_resource_request已经设置好        bit = ffs(host->ocr) - 1;    else        bit = fls(host->ocr_avail) - 1;    //io bus设置    host->ios.vdd = bit;    if (mmc_host_is_spi(host))        host->ios.chip_select = MMC_CS_HIGH;    else        host->ios.chip_select = MMC_CS_DONTCARE;    host->ios.bus_mode = MMC_BUSMODE_PUSHPULL;    host->ios.power_mode = MMC_POWER_UP;    //上电状态    host->ios.bus_width = MMC_BUS_WIDTH_1;    host->ios.timing = MMC_TIMING_LEGACY;    mmc_set_ios(host);    //一开始用单线模式初始化,最终调用sunxi_mmc_set_ios,设置线宽,总线,时序    /* Set signal voltage to 3.3V */    __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330);    //最终调用sunxi_mmc_signal_voltage_switch    /*     * This delay should be sufficient to allow the power supply     * to reach the minimum voltage.     */    mmc_delay(10);    host->ios.clock = host->f_init;    host->ios.power_mode = MMC_POWER_ON;    //工作状态    mmc_set_ios(host);    /*     * This delay must be at least 74 clock sizes, or 1 ms, or the     * time required to reach a stable voltage.     */    mmc_delay(10);    mmc_host_clk_release(host);    //没有定义CONFIG_MMC_CLKGATE 所有gateclock相关都是空操作}void mmc_detect_change(struct mmc_host *host, unsigned long delay){    host->detect_change = 1;    //wake_lock(&host->detect_wake_lock);    if(!(host->caps&MMC_CAP_NEEDS_POLL))        wake_lock(&host->detect_wake_lock);    //唤醒内核    mmc_schedule_delayed_work(&host->detect, delay);    //主动执行队列mmc_rescan}

初始化emmc的程序已经完成,如果是sd卡,会中断或者查询方法调用检测sd卡的程序。

mmc_set_ios这个函数很重要,改变emmc的配置都在这里,会调用硬件底层相关函数。

emmc直接主动调用mmc_rescan。

以下忽略部分冗余和大量无关的逻辑判断的代码:

 1 void mmc_rescan(struct work_struct *work) 2 { 3     struct mmc_host *host = container_of(work, struct mmc_host, detect.work); 4     int i; 5     bool extend_wakelock = false; 6     bool present         = false; 7  8     if (host->rescan_disable)    //rescan_disable = 0 9         return;10 11     /* If there is a non-removable card registered, only scan once */12     if ((host->caps & MMC_CAP_NONREMOVABLE) && host->rescan_entered){13         wake_unlock(&host->detect_wake_lock);14         return;15     }16 17     host->rescan_entered = 1;18 19     host->detect_change = 0;20 21 22     mmc_claim_host(host);    23     for (i = 0; i < ARRAY_SIZE(freqs); i++) {24         if (!mmc_rescan_try_freq(host, max(freqs[i], host->f_min))) {25             extend_wakelock = true;    //mmc_rescan_try_freq真正的初始化26             present = true;27             break;        //初始化成功立刻跳出        28         }29         if (freqs[i] <= host->f_min)30             break;31     }32     mmc_release_host(host);33 34     if (extend_wakelock)35         wake_lock_timeout(&host->detect_wake_lock, HZ / 2);36     else37         wake_unlock(&host->detect_wake_lock);38 }

 

核心函数是mmc_rescan_try_freq,里面做了什么?

 1 static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq) 2 { 3     host->f_init = freq; 4  5     mmc_power_up(host);  6  7     /* 8      * Some eMMCs (with VCCQ always on) may not be reset after power up, so 9      * do a hardware reset if possible.10      */11     mmc_hw_reset_for_init(host);    //硬件复位,调用mmc_host_ops.hw_reset = sunxi_mmc_hw_reset12 13     mmc_go_idle(host);    //使mmc空闲14 15     mmc_send_if_cond(host, host->ocr_avail);16 17     /* Order‘s important: probe SDIO, then SD, then MMC */18     if (!mmc_attach_sdio(host))    //不是sdio,不会响应对应命令19         return 0;20     if (!mmc_attach_sd(host))    //不是sd,不会响应对应命令21         return 0;22     if (!mmc_attach_mmc(host))    //zho真正attach对应的emmc23         return 0;24 25     mmc_power_off(host);26     return -EIO;27 }

 

mmc_attach_mmc的代码在linux-3.10/drivers/mmc/core/mmc.c
以下忽略部分冗余和大量无关的逻辑判断的代码:

  1 int mmc_attach_mmc(struct mmc_host *host)  2 {  3     int err;  4     u32 ocr;  5   6     mmc_set_bus_mode(host, MMC_BUSMODE_OPENDRAIN);    //bus开漏  7   8     err = mmc_send_op_cond(host, 0, &ocr);    //配置ocr电源状态  9  10     mmc_attach_bus_ops(host);    //设置bus_ops,mmc_bus_ops = mmc_ops_unsafe 11  12  13     host->ocr = mmc_select_voltage(host, ocr); 14  15     /* 16      * Detect and init the card. 17      */ 18     err = mmc_init_card(host, host->ocr, NULL); 19  20     mmc_release_host(host); 21     err = mmc_add_card(host->card); 22     mmc_claim_host(host); 23     return 0; 24  25 } 26  27 static int mmc_init_card(struct mmc_host *host, u32 ocr, 28     struct mmc_card *oldcard) 29 { 30     struct mmc_card *card; 31     int err, ddr = 0; 32     u32 cid[4]; 33     unsigned int max_dtr; 34     u32 rocr; 35     u8 *ext_csd = NULL; 36  37  38     mmc_set_bus_mode(host, MMC_BUSMODE_OPENDRAIN); 39  40     /* 41      * Since we‘re changing the OCR value, we seem to 42      * need to tell some cards to go back to the idle 43      * state.  We wait 1ms to give cards time to 44      * respond. 45      * mmc_go_idle is needed for eMMC that are asleep 46      */ 47     mmc_go_idle(host); 48  49     /* The extra bit indicates that we support high capacity */ 50     err = mmc_send_op_cond(host, ocr | (1 << 30), &rocr);    //识别大容量 51  52     /* 53      * Fetch CID from card. 54      */ 55     err = mmc_all_send_cid(host, cid);    //识别card id 56  57     /* 58      * Allocate card structure. 59      */ 60     card = mmc_alloc_card(host, &mmc_type);    //重要,后续分析 61  62  63     card->type = MMC_TYPE_MMC; 64     card->rca = 1; 65     memcpy(card->raw_cid, cid, sizeof(card->raw_cid)); 66      67     /* 68      * For native busses:  set card RCA and quit open drain mode. 69      */ 70  71     err = mmc_set_relative_addr(card); 72  73     mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL); 74  75     /* 76      * Fetch CSD from card. 77      */ 78     err = mmc_send_csd(card, card->raw_csd); 79  80     err = mmc_decode_csd(card); 81  82     err = mmc_decode_cid(card); 83      84     /* 85      * Select card, as all following commands rely on that. 86      */ 87  88     err = mmc_select_card(card); 89  90     /* 91      * Fetch and process extended CSD. 92      */ 93  94     err = mmc_get_ext_csd(card, &ext_csd); 95  96     err = mmc_read_ext_csd(card, ext_csd); 97  98     /* If doing byte addressing, check if required to do sector 99      * addressing.  Handle the case of <2GB cards needing sector100      * addressing.  See section 8.1 JEDEC Standard JED84-A441;101      * ocr register has bit 30 set for sector addressing.102      */103     if (!(mmc_card_blockaddr(card)) && (rocr & (1<<30)))104         mmc_card_set_blockaddr(card);105 106     /* Erase size depends on CSD and Extended CSD */107     mmc_set_erase_size(card);108 109     /*110      * Activate high speed (if supported)111      */112     if (card->ext_csd.hs_max_dtr != 0) {113         err = 0;114         if (card->ext_csd.hs_max_dtr > 52000000 &&115             host->caps2 & MMC_CAP2_HS200)116             err = mmc_select_hs200(card);117         else if    (host->caps & MMC_CAP_MMC_HIGHSPEED)118             //err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,119             //         EXT_CSD_HS_TIMING, 1,120             //         card->ext_csd.generic_cmd6_time);121             err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,122                    EXT_CSD_HS_TIMING, 1,123                    card->ext_csd.generic_cmd6_time,124                    true, true, true);125 126         if (card->ext_csd.hs_max_dtr > 52000000 &&127             host->caps2 & MMC_CAP2_HS200) {128             mmc_card_set_hs200(card);129             mmc_set_timing(card->host,130                        MMC_TIMING_MMC_HS200);131         } else {132             mmc_card_set_highspeed(card);133             mmc_set_timing(card->host, MMC_TIMING_MMC_HS);134         }        135     }136 137     /*138      * Compute bus speed.139      */140     max_dtr = (unsigned int)-1;141 142     if (mmc_card_highspeed(card) || (mmc_card_hs200(card)|| mmc_card_hs400(card))) {143         if (max_dtr > card->ext_csd.hs_max_dtr)144             max_dtr = card->ext_csd.hs_max_dtr;145         if (mmc_card_highspeed(card) && (max_dtr > 52000000))146             max_dtr = 52000000;147     } else if (max_dtr > card->csd.max_dtr) {148         max_dtr = card->csd.max_dtr;149     }150 151     mmc_set_clock(host, max_dtr);    152     //mmc_set_ios(host)一开始用单线模式初始化,最终调用sunxi_mmc_set_ios153     //设置线宽,总线,时序154 155     /*156      * Indicate HS200 SDR mode (if supported).157      */158     if (mmc_card_hs200(card)) {159         u32 ext_csd_bits;160         u32 bus_width = card->host->ios.bus_width;161 162 163         if(host->caps2 & MMC_CAP2_HS400){164             //pr_info("************%s: %s,%d************\n",165             //    mmc_hostname(card->host),__FUNCTION__,__LINE__);166             err =  mmc_select_hs400(card);167         }168 169 170         if(mmc_card_hs400(card)){    171             ext_csd_bits = EXT_CSD_DDR_BUS_WIDTH_8;172             err = mmc_select_powerclass(card, ext_csd_bits, ext_csd);    //配置mmc电源173         }else{174             ext_csd_bits = (bus_width == MMC_BUS_WIDTH_8) ?175                     EXT_CSD_BUS_WIDTH_8 : EXT_CSD_BUS_WIDTH_4;176             err = mmc_select_powerclass(card, ext_csd_bits, ext_csd);177         }178     }179 180     /*181      * Enable HPI feature (if supported)182      */183     if (card->ext_csd.hpi) {184         err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,185                 EXT_CSD_HPI_MGMT, 1,186                 card->ext_csd.generic_cmd6_time);187 188     189         card->ext_csd.hpi_en = 1;190     }191 192     host->card = card;193 194     mmc_free_ext_csd(ext_csd);195     return 0;196 }

调用完mmc_attach_mmc,card和host已经准备就绪,怎么通知drv呢?当然需要device!

剩下mmc_alloc_card和mmc_add_card没有分析完,代码都在linux-3.10/drivers/mmc/core/bus.c
以下忽略部分冗余代码:

  1 struct mmc_card *mmc_alloc_card(struct mmc_host *host, struct device_type *type)  2 {  3     struct mmc_card *card;  4   5     card = kzalloc(sizeof(struct mmc_card), GFP_KERNEL);  6     if (!card)  7         return ERR_PTR(-ENOMEM);  8   9     card->host = host; 10  11     device_initialize(&card->dev); 12  13     card->dev.parent = mmc_classdev(host); 14     card->dev.bus = &mmc_bus_type;    //dev.bus,device_attach优先选择bus->probe 15     card->dev.release = mmc_release_card; 16     card->dev.type = type; 17  18     return card; 19 } 20  21 /* 22  * This currently matches any MMC driver to any MMC card - drivers 23  * themselves make the decision whether to drive this card in their 24  * probe method. 25  */ 26 static int mmc_bus_match(struct device *dev, struct device_driver *drv) 27 { 28     return 1;    //只要在这个总线就匹配成功 29 } 30  31 static int mmc_bus_probe(struct device *dev) 32 { 33     struct mmc_driver *drv = to_mmc_driver(dev->driver); 34     struct mmc_card *card = mmc_dev_to_card(dev); 35  36     return drv->probe(card); 37 } 38  39 static struct bus_type mmc_bus_type = { 40     .name        = "mmc", 41     .dev_attrs    = mmc_dev_attrs, 42     .match        = mmc_bus_match, 43     .uevent        = mmc_bus_uevent, 44     .probe        = mmc_bus_probe, 45     .remove        = mmc_bus_remove, 46     .pm        = &mmc_bus_pm_ops, 47 }; 48  49 int mmc_register_bus(void)    //在linux-3.10/drivers/mmc/core/core.c里subsys_initcall(mmc_init)被调用 50 { 51     return bus_register(&mmc_bus_type); 52 } 53  54 int mmc_add_card(struct mmc_card *card) 55 { 56     int ret; 57     const char *type; 58     const char *uhs_bus_speed_mode = ""; 59     static const char *const uhs_speeds[] = { 60         [UHS_SDR12_BUS_SPEED] = "SDR12 ", 61         [UHS_SDR25_BUS_SPEED] = "SDR25 ", 62         [UHS_SDR50_BUS_SPEED] = "SDR50 ", 63         [UHS_SDR104_BUS_SPEED] = "SDR104 ", 64         [UHS_DDR50_BUS_SPEED] = "DDR50 ", 65     }; 66  67     dev_set_name(&card->dev, "%s:%04x", mmc_hostname(card->host), card->rca); 68  69     switch (card->type) { 70     case MMC_TYPE_MMC: 71         type = "MMC"; 72         break; 73     .... 74     .... 75     default: 76         type = "?"; 77         break; 78     } 79  80     if (mmc_sd_card_uhs(card) && 81         (card->sd_bus_speed < ARRAY_SIZE(uhs_speeds))) 82         uhs_bus_speed_mode = uhs_speeds[card->sd_bus_speed]; 83  84  85     pr_info("%s: new %s%s%s%s%s card at address %04x\n", 86         mmc_hostname(card->host), 87         mmc_card_uhs(card) ? "ultra high speed " : 88         (mmc_card_highspeed(card) ? "high speed " : ""), 89         mmc_card_hs400(card) ? "HS400 " : 90         (mmc_card_hs200(card) ? "HS200 " : ""), 91         mmc_card_ddr_mode(card) ? "DDR " : "", 92         uhs_bus_speed_mode, type, card->rca); 93      94     mmc_init_context_info(card->host);    //emmc队列等信息(block设备会用到) 95  96     ret = device_add(&card->dev);    //等待driver 97  98     mmc_card_set_present(card); 99 100     return 0;101 }102 103 /**104  *    mmc_register_driver - register a media driver105  *    @drv: MMC media driver106  */107 int mmc_register_driver(struct mmc_driver *drv)108 {109     drv->drv.bus = &mmc_bus_type;110     return driver_register(&drv->drv);111 }

现在device这边已经准备就绪,就差某个drv调用mmc_register_driver进行match,最后调用某个drv->probe。

其实在linux-3.10/drivers/mmc/card/block.c就调用了mmc_register_driver。
以下忽略部分冗余代码:

 1 static struct mmc_driver mmc_driver = { 2     .drv        = { 3         .name    = "mmcblk", 4     }, 5     .probe        = mmc_blk_probe, 6     .remove        = mmc_blk_remove, 7     .suspend    = mmc_blk_suspend, 8     .resume        = mmc_blk_resume, 9 };10 11 static int __init mmc_blk_init(void)12 {13     int res;14 15     max_devices = 256 / perdev_minors;    //perdev_minors = 816 17     res = register_blkdev(MMC_BLOCK_MAJOR, "mmc");    //注册块设备18 19     res = mmc_register_driver(&mmc_driver);    //我们要找的目标函数20 21     return 0;22 }23 24 static void __exit mmc_blk_exit(void)25 {26     mmc_unregister_driver(&mmc_driver);27     unregister_blkdev(MMC_BLOCK_MAJOR, "mmc");28 }29 30 module_init(mmc_blk_init);31 module_exit(mmc_blk_exit);32 33 static int mmc_blk_probe(struct mmc_card *card)34 {35     struct mmc_blk_data *md, *part_md;36     char cap_str[10];37 38     md = mmc_blk_alloc(card);39 40     string_get_size((u64)get_capacity(md->disk) << 9, STRING_UNITS_2,41             cap_str, sizeof(cap_str));42     pr_info("%s: %s %s %s %s\n",43         md->disk->disk_name, mmc_card_id(card), mmc_cardrd_name(card),44         cap_str, md->read_only ? "(ro)" : "");45 46     if (mmc_blk_alloc_parts(card, md))47         goto out;48 49     mmc_set_drvdata(card, md);50     mmc_fixup_device(card, blk_fixups);51 52     mmc_add_disk(md);53 54     list_for_each_entry(part_md, &md->part, part) {55         mmc_add_disk(part_md);56 57     }58     return 0;59 }

这里暂时没时间分析block.c里面的函数了,是比较复杂。

 

总结emmc初始化流程:

sunxi_mmc_probe---->mmc_alloc_host---->mmc_add_host---->mmc_start_host---->mmc_rescan---->mmc_rescan_try_freq

---->mmc_attach_mmc---->mmc_init_card---->mmc_alloc_card---->mmc_add_card + mmc_blk_init---->mmc_blk_probe......

 

EMMC架构