首页 > 代码库 > 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架构