一、MMC簡介
eMMC使用BGA封裝了Nand Flash和Flash控制器,向外提供MMC標准接口,其結構圖如下圖所示(圖來自《eMMC5.1官方標准協議》)。eMMC的出現使得手機廠商就能專注於產品開發的其它部分,並縮短向市場推出產品的時間。
對於我們來說,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文件。
文件鏈接:
https://files.cnblogs.com/files/Lioker/19_emmc.zip
首先來看它的入口函數:
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_IRQ 15 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 return ret; 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, struct mmc_host, detect.work); 5 int i; 6 ... 7 mmc_bus_get(host); 8 9 /* 檢測卡是否仍舊存在 */ 10 if (host->bus_ops && host->bus_ops->detect && !host->bus_dead 11 && !(host->caps & MMC_CAP_NONREMOVABLE)) 12 host->bus_ops->detect(host); 13 14 /* If the card was removed the bus will be marked 15 * as dead - extend the wakelock so userspace 16 * 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 that 22 * 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 to 36 * 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 }
probe()函數所做的有以下幾點:
1. 分配、設置並添加mmc_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 return IRQ_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; int err; 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); return err; }
其中,
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. 整體框架:
下一章 20、網卡框架分析和虛擬網卡驅動和DM9621驅動分析