一、MMC简介
eMMC在封装中集成了一个控制器,提供标准接口并管理Nand Flash,使得手机厂商就能专注于产品开发的其它部分,并缩短向市场推出产品的时间。
对于我们来说,eMMC就是在Nand Flash上添加负责ECC、管理坏块等功能的控制器。
在内核中,使用MMC子系统统一管理MMC、SD、SDIO等设备。从MMC规范发布至今,基于不同的考量(物理尺寸、数据位宽和clock频率等),进化出了MMC、SD、microSD、SDIO、eMMC等不同的规范。其本质是一样的,这也是内核将它们统称为MMC的原因。
和MTD相同,MMC驱动也有一个单独的文件夹,位于drivers/mmc目录下,目录下的三个目录card、core、host对应MMC驱动的三个层次。
1. card:区块层,用于实现卡的块设备驱动。
2. core:核心层,抽象了卡的设备驱动的函数。
3. host:主机控制器层,依赖于不同平台的控制器操作函数。
二、MMC框架分析
为了方便分析框架,我们需要分析host目录,读者可在此目录下任意选择一个单板驱动文件进行分析,我选择的是s3cmci.c文件。
文件链接:
提取码为:gt93
首先来看它的入口函数:
1 static int __init s3cmci_init(void)2 {3 return platform_driver_register(&s3cmci_driver);4 }
我们进入platform_driver的probe函数中,看看它如何初始化。
1 static int __devinit s3cmci_probe(struct platform_device *pdev)2 {3 struct s3cmci_host *host;4 struct mmc_host *mmc;5 ...6 /*分配mmc_host*/
7 mmc = mmc_alloc_host(sizeof(struct s3cmci_host), &pdev->dev);8 ... /*省略阶段做的是设置s3cmci_host成员和gpio管脚*/
9 request_irq(host->irq_cd, s3cmci_irq_cd, IRQF_TRIGGER_RISING |IRQF_TRIGGER_FALLING, DRIVER_NAME, host));10 ...11 /*设置mmc_host*/
12 mmc->ops = &s3cmci_ops;13 mmc->ocr_avail = MMC_VDD_32_33 |MMC_VDD_33_34;14 #ifdef CONFIG_MMC_S3C_HW_SDIO_IRQ15 mmc->caps = MMC_CAP_4_BIT_DATA |MMC_CAP_SDIO_IRQ;16 #else
17 mmc->caps =MMC_CAP_4_BIT_DATA;18 #endif
19 mmc->f_min = host->clk_rate / (host->clk_div * 256);20 mmc->f_max = host->clk_rate / host->clk_div;21
22 if (host->pdata->ocr_avail)23 mmc->ocr_avail = host->pdata->ocr_avail;24
25 mmc->max_blk_count = 4095;26 mmc->max_blk_size = 4095;27 mmc->max_req_size = 4095 * 512;28 mmc->max_seg_size = mmc->max_req_size;29
30 mmc->max_segs = 128;31 ...32 /*添加mmc_host*/
33 ret =mmc_add_host(mmc);34 ...35 platform_set_drvdata(pdev, mmc);36 ...37 returnret;38 }
其中,
1. mmc_alloc_host()函数调用关系如下:
mmc = mmc_alloc_host(sizeof(struct s3cmci_host), &pdev->dev);-> host = kzalloc(sizeof(struct mmc_host) +extra, GFP_KERNEL);/*初始化工作队列*/
-> INIT_DELAYED_WORK(&host->detect, mmc_rescan);
2. mmc_add_host()函数调用关系如下:
mmc_add_host(mmc);-> device_add(&host->class_dev);->mmc_start_host(host);-> mmc_power_off(host); /*掉电刷新*/
-> mmc_detect_change(host, 0);-> mmc_schedule_delayed_work(&host->detect, delay);/*在工作队列中添加一个延迟的工作任务host->detect*/
-> return queue_delayed_work(workqueue, work, delay);
mmc_add_host()函数最终会调用mmc_alloc_host()初始化工作队列的mmc_rescan()函数。此函数用于检测是否有卡插入了卡控制器。
1 void mmc_rescan(struct work_struct *work)2 {3 static const unsigned freqs[] = { 400000, 300000, 200000, 100000};4 struct mmc_host *host = container_of(work, structmmc_host, detect.work);5 inti;6 ...7 mmc_bus_get(host);8
9 /*检测卡是否仍旧存在*/
10 if (host->bus_ops && host->bus_ops->detect && !host->bus_dead11 && !(host->caps &MMC_CAP_NONREMOVABLE))12 host->bus_ops->detect(host);13
14 /*If the card was removed the bus will be marked15 * as dead - extend the wakelock so userspace16 * can respond*/
17 if (host->bus_dead)18 extend_wakelock = 1;19
20 /*
21 * Let mmc_bus_put() free the bus/bus_ops if we've found that22 * the card is no longer present.23 */
24 mmc_bus_put(host);25 mmc_bus_get(host);26
27 /*如果卡仍存在, stop here*/
28 if (host->bus_ops !=NULL) {29 mmc_bus_put(host);30 mmc_set_drv_state(e_inserted,host);//ly
31 goto out;32 }33
34 /*
35 * Only we can add a new handler, so it's safe to36 * release the lock here.37 */
38 mmc_bus_put(host);39
40 /*卡不存在,释放*/
41 if (host->ops->get_cd && host->ops->get_cd(host) == 0){42 mmc_set_drv_state(e_removed,host);43 goto out;44 }45 mmc_claim_host(host);46 for (i = 0; i < ARRAY_SIZE(freqs); i++) {47 if (!mmc_rescan_try_freq(host, max(freqs[i], host->f_min))) {48 extend_wakelock = true;49 break;50 }51 if (freqs[i] <= host->f_min)52 break;53 }54 mmc_release_host(host);55
56 out:57 if(extend_wakelock)58 wake_lock_timeout(&host->detect_wake_lock, HZ / 2);59 else
60 wake_unlock(&host->detect_wake_lock);61 if (host->caps &MMC_CAP_NEEDS_POLL) {62 wake_lock(&host->detect_wake_lock);63 mmc_schedule_delayed_work(&host->detect, HZ);64 }65 }
View Code
probe()函数所做的有以下几点:
1. 分配、设置并添加mc_host
2. 检测卡是否插入了卡控制器
如果在probe()函数执行时,卡并没有插入呢?也就是除了probe()函数,一定会有其他函数最终调用了mmc_rescan()函数。现在我们需要重新看一遍probe()函数,它注册了一个中断函数s3cmci_irq_cd()。
1 static irqreturn_t s3cmci_irq_cd(int irq, void *dev_id)2 {3 struct s3cmci_host *host = (struct s3cmci_host *)dev_id;4 dbg(host, dbg_irq, "card detect\n");5
6 mmc_detect_change(host->mmc, msecs_to_jiffies(500));7
8 returnIRQ_HANDLED;9 }
之前分析过,mmc_detect_change(host->mmc, msecs_to_jiffies(500));函数最终会调用mmc_rescan()函数。
此时如果有卡插入了,会调用到mmc_rescan()函数,此函数调用关系如下:
mmc_rescan(struct work_struct *work)-> mmc_rescan_try_freq(host, max(freqs[i], host->f_min))-> mmc_attach_sdio(host) /*检测卡的类型*/
->mmc_attach_sd(host)->mmc_attach_mmc(host)-> mmc_send_op_cond(host, 0, &ocr); /*发送卡的ID*/
-> mmc_init_card(host, host->ocr, NULL); /*初始化mmc_card*/
-> card = mmc_alloc_card(host, &mmc_type);-> device_initialize(&card->dev);-> card->dev.bus = &mmc_bus_type; /*设置总线为mmc_bus_type*/
-> card->type = MMC_TYPE_MMC; /*设置card结构体*/
->mmc_release_host(host);-> mmc_add_card(host->card); /*添加卡mmc_card*/
-> device_add(&card->dev);-> mmc_claim_host(host); /*使能host*/
在添加mmc_card调用device_add()函数时,mmc_bus_type总线会调用match()函数匹配设备驱动,如果匹配成功会调用总线的probe()函数或设备驱动的probe()函数。
1 static int mmc_bus_match(struct device *dev, struct device_driver *drv)2 {3 return 1; /*匹配永远成功*/
4 }
probe()函数最终会调用mmc_driver的probe()函数。
1 static int mmc_bus_probe(struct device *dev)2 {3 struct mmc_driver *drv = to_mmc_driver(dev->driver);4 struct mmc_card *card =mmc_dev_to_card(dev);5
6 return drv->probe(card);7 }
在SI4的Project中搜索struct mmc_driver,发现只有block.c文件有对此结构体的定义。
现在我们来查看mmc_driver的probe()函数。
static int mmc_blk_probe(struct mmc_card *card)
{struct mmc_blk_data *md, *part_md;interr;char cap_str[10];
...
md=mmc_blk_alloc(card);
err=mmc_blk_set_blksize(md, card);
...
mmc_set_drvdata(card, md);
mmc_fixup_device(card, blk_fixups);
...if(mmc_add_disk(md))goto out;
list_for_each_entry(part_md,&md->part, part) {if(mmc_add_disk(part_md))goto out;
}return 0;out:
mmc_blk_remove_parts(card, md);
mmc_blk_remove_req(md);returnerr;
}
其中,
1. mmc_blk_alloc()函数调用关系如下:
md =mmc_blk_alloc(card);-> md = mmc_blk_alloc_req(card, &card->dev, size, false, NULL);-> md->disk =alloc_disk(perdev_minors);-> ret = mmc_init_queue(&md->queue, card, &md->lock, subname);-> mq->queue = blk_init_queue(mmc_request, lock);-> set_capacity(md->disk, size);
2. mmc_add_disk()函数调用关系如下:
mmc_add_disk(md)-> add_disk(md->disk);-> device_create_file(disk_to_dev(md->disk), &md->force_ro);
这个mmc_driver底层做的与块设备驱动相同:
1. 分配、初始化请求队列,绑定请求队列和请求函数
2. 分配、设置并添加gendisk
3. 注册块设备驱动
队列函数为mmc_blk_issue_rq(),其调用关系如下:
mmc_blk_issue_rq->mmc_blk_issue_secdiscard_rq(mq, req);->mmc_blk_issue_discard_rq(mq, req);->mmc_blk_issue_flush(mq, req);-> mmc_blk_issue_rw_rq(mq, req); /*上面四个函数选一个执行*/
-> mmc_wait_for_req(card->host, &brq.mrq);->mmc_start_request(host, mrq);-> host->ops->request(host, mrq); /*s3cmci.c中host->requset = s3cmci_request*/
三、MMC驱动框架总结
1. 各个结构体作用:
struct mmc_card用于描述卡,struct mmc_driver用于描述卡驱动,sutrct mmc_host用于描述卡控制器,struct mmc_host_ops用于描述卡控制器操作函数。
2. 整体框架: