现在EMMC盛行,分析总结还是很有必要的。以下以全志a64为实例切入主题。
这里a64有三个sdc0~2,硬件上sdc2是连接EMMC,这里只分析sdc2的代码。
初始化的代码在linux-3.10/drivers/mmc/host/sunxi-mmc.c
以下忽略部分冗余代码:
static const struct of_device_id sunxi_mmc_of_match[] = {
......
......
{ .compatible = "allwinner,sun50i-sdmmc2", },
......
......
};
MODULE_DEVICE_TABLE(of, sunxi_mmc_of_match); static struct platform_driver sunxi_mmc_driver = {
.driver = {
.name = "sunxi-mmc",
.of_match_table = of_match_ptr(sunxi_mmc_of_match),
.pm = sunxi_mmc_pm_ops,
},
.probe = sunxi_mmc_probe,
.remove = sunxi_mmc_remove,
.shutdown = sunxi_shutdown_mmc,
};
module_platform_driver(sunxi_mmc_driver);
设备树会初始化deivce,这里有driver,下面直接进sunxi_mmc_probe分析。
以下忽略部分冗余代码:
static int sunxi_mmc_probe(struct platform_device *pdev)
{
struct sunxi_mmc_host *host; //全志a64主控硬件相关
struct mmc_host *mmc; //emmc架构相关
int ret; dev_info(&pdev->dev,"%s\n",DRIVER_VERSION); mmc = mmc_alloc_host(sizeof(struct sunxi_mmc_host), &pdev->dev); //初始化mmc_host,后面会分析 host = mmc_priv(mmc); //host = mmc->private;
host->mmc = mmc; //sunxi_mmc_host和mmc_host建立纽带 //配置硬件相关,如时钟,sdc控制寄存器,IO复用,中断,电源控制
ret = sunxi_mmc_resource_request(host, pdev); //初始化dma #define 4*PAGE_SIZE
host->dma_mask = DMA_BIT_MASK();
pdev->dev.dma_mask = &host->dma_mask;
pdev->dev.coherent_dma_mask = DMA_BIT_MASK();
host->sg_cpu = dma_alloc_coherent(&pdev->dev, SUNXI_REQ_PAGE_SIZE,
&host->sg_dma, GFP_KERNEL); mmc->ops = &sunxi_mmc_ops;
mmc->max_blk_count = ;
mmc->max_blk_size = ;
mmc->max_segs = SUNXI_REQ_PAGE_SIZE / sizeof(struct sunxi_idma_des);
mmc->max_seg_size = ( << host->idma_des_size_bits);
mmc->max_req_size = mmc->max_seg_size * mmc->max_segs;
/* 400kHz ~ 50MHz */
mmc->f_min = ;
mmc->f_max = ;
mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED | MMC_CAP_ERASE \
| MMC_CAP_WAIT_WHILE_BUSY;
//mmc->caps2 |= MMC_CAP2_HS400_1_8V; mmc_of_parse(mmc); //解析设备树相关的信息 ret = mmc_add_host(mmc); //重点,真正初始化emmc并启动,后面会分析 ret = mmc_create_sys_fs(host,pdev); dev_info(&pdev->dev, "base:0x%p irq:%u\n", host->reg_base, host->irq);
platform_set_drvdata(pdev, mmc);
return ;
}
到此probe结束,有两个比较关键的函数mmc_alloc_host和mmc_add_host,真正启动emmc的操作都在这里
mmc_alloc_host和mmc_add_host的代码在linux-3.10/drivers/mmc/core/host.c
以下忽略部分冗余代码:
struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
{
int err;
struct mmc_host *host; host = kzalloc(sizeof(struct mmc_host) + extra, GFP_KERNEL); /* scanning will be enabled when we're ready */
host->rescan_disable = ;
idr_preload(GFP_KERNEL); err = idr_alloc(&mmc_host_idr, host, , , GFP_NOWAIT);
if (err >= )
host->index = err; //通过整数id管理地址,具体细节本人不太清楚 idr_preload_end(); dev_set_name(&host->class_dev, "mmc%d", host->index); host->parent = dev;
host->class_dev.parent = dev;
host->class_dev.class = &mmc_host_class;
device_initialize(&host->class_dev); mmc_host_clk_init(host); //这里初始化框架相关的clock,不涉及硬件 host->slot.cd_irq = -EINVAL; init_waitqueue_head(&host->wq);
wake_lock_init(&host->detect_wake_lock, WAKE_LOCK_SUSPEND, //初始化唤醒内核的锁
kasprintf(GFP_KERNEL, "%s_detect", mmc_hostname(host)));
INIT_DELAYED_WORK(&host->detect, mmc_rescan); //重要,初始化检查card的队列 host->pm_notify.notifier_call = mmc_pm_notify;
/*
* By default, hosts do not support SGIO or large requests.
* They have to set these according to their abilities.
*/
host->max_segs = ;
host->max_seg_size = PAGE_CACHE_SIZE; //#define PAGE_CACHE_SIZE 1UL << 16 host->max_req_size = PAGE_CACHE_SIZE;
host->max_blk_size = ;
host->max_blk_count = PAGE_CACHE_SIZE / ; return host;
} int mmc_add_host(struct mmc_host *host)
{
int err; err = device_add(&host->class_dev); led_trigger_register_simple(dev_name(&host->class_dev), &host->led); //;类似硬盘的LED闪烁 #ifdef CONFIG_DEBUG_FS
mmc_add_host_debugfs(host); //debug用
#endif
mmc_host_clk_sysfs_init(host); mmc_start_host(host); //重要,真正操作都在这里 return ;
}
mmc_start_host的代码在linux-3.10/drivers/mmc/core/core.c
以下忽略部分冗余代码:
void mmc_start_host(struct mmc_host *host)
{
host->f_init = max(freqs[], host->f_min); //初始时钟
host->rescan_disable = ; //开启rescan
mmc_power_up(host);
mmc_detect_change(host, );
} 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) - ;
else
bit = fls(host->ocr_avail) - ;
//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(); 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(); mmc_host_clk_release(host); //没有定义CONFIG_MMC_CLKGATE 所有gateclock相关都是空操作
} void mmc_detect_change(struct mmc_host *host, unsigned long delay)
{
host->detect_change = ; //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。
以下忽略部分冗余和大量无关的逻辑判断的代码:
void mmc_rescan(struct work_struct *work)
{
struct mmc_host *host = container_of(work, struct mmc_host, detect.work);
int i;
bool extend_wakelock = false;
bool present = false; if (host->rescan_disable) //rescan_disable = 0
return; /* If there is a non-removable card registered, only scan once */
if ((host->caps & MMC_CAP_NONREMOVABLE) && host->rescan_entered){
wake_unlock(&host->detect_wake_lock);
return;
} host->rescan_entered = ; host->detect_change = ; mmc_claim_host(host);
for (i = ; i < ARRAY_SIZE(freqs); i++) {
if (!mmc_rescan_try_freq(host, max(freqs[i], host->f_min))) {
extend_wakelock = true; //mmc_rescan_try_freq真正的初始化
present = true;
break; //初始化成功立刻跳出
}
if (freqs[i] <= host->f_min)
break;
}
mmc_release_host(host); if (extend_wakelock)
wake_lock_timeout(&host->detect_wake_lock, HZ / );
else
wake_unlock(&host->detect_wake_lock);
}
核心函数是mmc_rescan_try_freq,里面做了什么?
static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
{
host->f_init = freq; mmc_power_up(host); /*
* Some eMMCs (with VCCQ always on) may not be reset after power up, so
* do a hardware reset if possible.
*/
mmc_hw_reset_for_init(host); //硬件复位,调用mmc_host_ops.hw_reset = sunxi_mmc_hw_reset mmc_go_idle(host); //使mmc空闲 mmc_send_if_cond(host, host->ocr_avail); /* Order's important: probe SDIO, then SD, then MMC */
if (!mmc_attach_sdio(host)) //不是sdio,不会响应对应命令
return ;
if (!mmc_attach_sd(host)) //不是sd,不会响应对应命令
return ;
if (!mmc_attach_mmc(host)) //zho真正attach对应的emmc
return ; mmc_power_off(host);
return -EIO;
}
mmc_attach_mmc的代码在linux-3.10/drivers/mmc/core/mmc.c
以下忽略部分冗余和大量无关的逻辑判断的代码:
int mmc_attach_mmc(struct mmc_host *host)
{
int err;
u32 ocr; mmc_set_bus_mode(host, MMC_BUSMODE_OPENDRAIN); //bus开漏 err = mmc_send_op_cond(host, , &ocr); //配置ocr电源状态 mmc_attach_bus_ops(host); //设置bus_ops,mmc_bus_ops = mmc_ops_unsafe host->ocr = mmc_select_voltage(host, ocr); /*
* Detect and init the card.
*/
err = mmc_init_card(host, host->ocr, NULL); mmc_release_host(host);
err = mmc_add_card(host->card);
mmc_claim_host(host);
return ; } static int mmc_init_card(struct mmc_host *host, u32 ocr,
struct mmc_card *oldcard)
{
struct mmc_card *card;
int err, ddr = ;
u32 cid[];
unsigned int max_dtr;
u32 rocr;
u8 *ext_csd = NULL; mmc_set_bus_mode(host, MMC_BUSMODE_OPENDRAIN); /*
* Since we're changing the OCR value, we seem to
* need to tell some cards to go back to the idle
* state. We wait 1ms to give cards time to
* respond.
* mmc_go_idle is needed for eMMC that are asleep
*/
mmc_go_idle(host); /* The extra bit indicates that we support high capacity */
err = mmc_send_op_cond(host, ocr | ( << ), &rocr); //识别大容量 /*
* Fetch CID from card.
*/
err = mmc_all_send_cid(host, cid); //识别card id /*
* Allocate card structure.
*/
card = mmc_alloc_card(host, &mmc_type); //重要,后续分析 card->type = MMC_TYPE_MMC;
card->rca = ;
memcpy(card->raw_cid, cid, sizeof(card->raw_cid)); /*
* For native busses: set card RCA and quit open drain mode.
*/ err = mmc_set_relative_addr(card); mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL); /*
* Fetch CSD from card.
*/
err = mmc_send_csd(card, card->raw_csd); err = mmc_decode_csd(card); err = mmc_decode_cid(card); /*
* Select card, as all following commands rely on that.
*/ err = mmc_select_card(card); /*
* Fetch and process extended CSD.
*/ err = mmc_get_ext_csd(card, &ext_csd); err = mmc_read_ext_csd(card, ext_csd); /* If doing byte addressing, check if required to do sector
* addressing. Handle the case of <2GB cards needing sector
* addressing. See section 8.1 JEDEC Standard JED84-A441;
* ocr register has bit 30 set for sector addressing.
*/
if (!(mmc_card_blockaddr(card)) && (rocr & (<<)))
mmc_card_set_blockaddr(card); /* Erase size depends on CSD and Extended CSD */
mmc_set_erase_size(card); /*
* Activate high speed (if supported)
*/
if (card->ext_csd.hs_max_dtr != ) {
err = ;
if (card->ext_csd.hs_max_dtr > &&
host->caps2 & MMC_CAP2_HS200)
err = mmc_select_hs200(card);
else if (host->caps & MMC_CAP_MMC_HIGHSPEED)
//err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
// EXT_CSD_HS_TIMING, 1,
// card->ext_csd.generic_cmd6_time);
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_HS_TIMING, ,
card->ext_csd.generic_cmd6_time,
true, true, true); if (card->ext_csd.hs_max_dtr > &&
host->caps2 & MMC_CAP2_HS200) {
mmc_card_set_hs200(card);
mmc_set_timing(card->host,
MMC_TIMING_MMC_HS200);
} else {
mmc_card_set_highspeed(card);
mmc_set_timing(card->host, MMC_TIMING_MMC_HS);
}
} /*
* Compute bus speed.
*/
max_dtr = (unsigned int)-; if (mmc_card_highspeed(card) || (mmc_card_hs200(card)|| mmc_card_hs400(card))) {
if (max_dtr > card->ext_csd.hs_max_dtr)
max_dtr = card->ext_csd.hs_max_dtr;
if (mmc_card_highspeed(card) && (max_dtr > ))
max_dtr = ;
} else if (max_dtr > card->csd.max_dtr) {
max_dtr = card->csd.max_dtr;
} mmc_set_clock(host, max_dtr);
//mmc_set_ios(host)一开始用单线模式初始化,最终调用sunxi_mmc_set_ios
//设置线宽,总线,时序 /*
* Indicate HS200 SDR mode (if supported).
*/
if (mmc_card_hs200(card)) {
u32 ext_csd_bits;
u32 bus_width = card->host->ios.bus_width; if(host->caps2 & MMC_CAP2_HS400){
//pr_info("************%s: %s,%d************\n",
// mmc_hostname(card->host),__FUNCTION__,__LINE__);
err = mmc_select_hs400(card);
} if(mmc_card_hs400(card)){
ext_csd_bits = EXT_CSD_DDR_BUS_WIDTH_8;
err = mmc_select_powerclass(card, ext_csd_bits, ext_csd); //配置mmc电源
}else{
ext_csd_bits = (bus_width == MMC_BUS_WIDTH_8) ?
EXT_CSD_BUS_WIDTH_8 : EXT_CSD_BUS_WIDTH_4;
err = mmc_select_powerclass(card, ext_csd_bits, ext_csd);
}
} /*
* Enable HPI feature (if supported)
*/
if (card->ext_csd.hpi) {
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_HPI_MGMT, ,
card->ext_csd.generic_cmd6_time); card->ext_csd.hpi_en = ;
} host->card = card; mmc_free_ext_csd(ext_csd);
return ;
}
调用完mmc_attach_mmc,card和host已经准备就绪,怎么通知drv呢?当然需要device!
剩下mmc_alloc_card和mmc_add_card没有分析完,代码都在linux-3.10/drivers/mmc/core/bus.c
以下忽略部分冗余代码:
struct mmc_card *mmc_alloc_card(struct mmc_host *host, struct device_type *type)
{
struct mmc_card *card; card = kzalloc(sizeof(struct mmc_card), GFP_KERNEL);
if (!card)
return ERR_PTR(-ENOMEM); card->host = host; device_initialize(&card->dev); card->dev.parent = mmc_classdev(host);
card->dev.bus = &mmc_bus_type; //dev.bus,device_attach优先选择bus->probe
card->dev.release = mmc_release_card;
card->dev.type = type; return card;
} /*
* This currently matches any MMC driver to any MMC card - drivers
* themselves make the decision whether to drive this card in their
* probe method.
*/
static int mmc_bus_match(struct device *dev, struct device_driver *drv)
{
return ; //只要在这个总线就匹配成功
} static int mmc_bus_probe(struct device *dev)
{
struct mmc_driver *drv = to_mmc_driver(dev->driver);
struct mmc_card *card = mmc_dev_to_card(dev); return drv->probe(card);
} static struct bus_type mmc_bus_type = {
.name = "mmc",
.dev_attrs = mmc_dev_attrs,
.match = mmc_bus_match,
.uevent = mmc_bus_uevent,
.probe = mmc_bus_probe,
.remove = mmc_bus_remove,
.pm = &mmc_bus_pm_ops,
}; int mmc_register_bus(void) //在linux-3.10/drivers/mmc/core/core.c里subsys_initcall(mmc_init)被调用
{
return bus_register(&mmc_bus_type);
} int mmc_add_card(struct mmc_card *card)
{
int ret;
const char *type;
const char *uhs_bus_speed_mode = "";
static const char *const uhs_speeds[] = {
[UHS_SDR12_BUS_SPEED] = "SDR12 ",
[UHS_SDR25_BUS_SPEED] = "SDR25 ",
[UHS_SDR50_BUS_SPEED] = "SDR50 ",
[UHS_SDR104_BUS_SPEED] = "SDR104 ",
[UHS_DDR50_BUS_SPEED] = "DDR50 ",
}; dev_set_name(&card->dev, "%s:%04x", mmc_hostname(card->host), card->rca); switch (card->type) {
case MMC_TYPE_MMC:
type = "MMC";
break;
....
....
default:
type = "?";
break;
} if (mmc_sd_card_uhs(card) &&
(card->sd_bus_speed < ARRAY_SIZE(uhs_speeds)))
uhs_bus_speed_mode = uhs_speeds[card->sd_bus_speed]; pr_info("%s: new %s%s%s%s%s card at address %04x\n",
mmc_hostname(card->host),
mmc_card_uhs(card) ? "ultra high speed " :
(mmc_card_highspeed(card) ? "high speed " : ""),
mmc_card_hs400(card) ? "HS400 " :
(mmc_card_hs200(card) ? "HS200 " : ""),
mmc_card_ddr_mode(card) ? "DDR " : "",
uhs_bus_speed_mode, type, card->rca); mmc_init_context_info(card->host); //emmc队列等信息(block设备会用到) ret = device_add(&card->dev); //等待driver mmc_card_set_present(card); return ;
} /**
* mmc_register_driver - register a media driver
* @drv: MMC media driver
*/
int mmc_register_driver(struct mmc_driver *drv)
{
drv->drv.bus = &mmc_bus_type;
return driver_register(&drv->drv);
}
现在device这边已经准备就绪,就差某个drv调用mmc_register_driver进行match,最后调用某个drv->probe。
其实在linux-3.10/drivers/mmc/card/block.c就调用了mmc_register_driver。
以下忽略部分冗余代码:
static struct mmc_driver mmc_driver = {
.drv = {
.name = "mmcblk",
},
.probe = mmc_blk_probe,
.remove = mmc_blk_remove,
.suspend = mmc_blk_suspend,
.resume = mmc_blk_resume,
}; static int __init mmc_blk_init(void)
{
int res; max_devices = / perdev_minors; //perdev_minors = 8 res = register_blkdev(MMC_BLOCK_MAJOR, "mmc"); //注册块设备 res = mmc_register_driver(&mmc_driver); //我们要找的目标函数 return ;
} static void __exit mmc_blk_exit(void)
{
mmc_unregister_driver(&mmc_driver);
unregister_blkdev(MMC_BLOCK_MAJOR, "mmc");
} module_init(mmc_blk_init);
module_exit(mmc_blk_exit); static int mmc_blk_probe(struct mmc_card *card)
{
struct mmc_blk_data *md, *part_md;
char cap_str[]; md = mmc_blk_alloc(card); string_get_size((u64)get_capacity(md->disk) << , STRING_UNITS_2,
cap_str, sizeof(cap_str));
pr_info("%s: %s %s %s %s\n",
md->disk->disk_name, mmc_card_id(card), mmc_cardrd_name(card),
cap_str, md->read_only ? "(ro)" : ""); if (mmc_blk_alloc_parts(card, md))
goto out; mmc_set_drvdata(card, md);
mmc_fixup_device(card, blk_fixups); mmc_add_disk(md); list_for_each_entry(part_md, &md->part, part) {
mmc_add_disk(part_md); }
return ;
}
这里暂时没时间分析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......