Nineteen, eMMC drive Frame Analysis

A, MMC Profile

eMMC integrated in the package a controller that provides a standard interface and management Nand Flash, so that mobile phone manufacturers will be able to focus on other parts of product development and shorten time to market product launch.

For us, eMMC is responsible for adding ECC on Nand Flash, bad block management controller functions and so on.

 

In the kernel, using the MMC subsystem unified management MMC, SD, SDIO and other equipment. From the MMC specification was released to date, based on different considerations (physical size, data width and clock frequency, etc.), evolved MMC, SD, microSD, SDIO, eMMC, such as different specifications. Its essence is the same, which is the core reason they are collectively referred to as the MMC.

 

And the same as the MTD, MMC also has a separate drive folder, located in drivers / mmc directory, three directories under the directory card, core, host corresponding to the three levels of MMC driven.

1. card: block layer, for implementing the block driver card.

2. core: core layer abstracts the card device driver functions.

3. host: the host controller layer, the controller depends on the operating functions of different platforms.

 

 

Two, MMC Frame Analysis

For analytical framework, we need to analyze the host catalog, readers can select a file for analysis at any veneer drive in this directory, I chose s3cmci.c file.

File link:

https://pan.baidu.com/s/1ALh5Msnh91kt5K1358xfLg

Extraction code is: gt93

 

First look at its entry function:

1 static int __init s3cmci_init(void)
2 {
3     return platform_driver_register(&s3cmci_driver);
4 }

We enter the probe function platform_driver's see how it is initialized.

. 1  static  int __devinit s3cmci_probe ( struct platform_device * PDEV)
 2  {
 . 3      struct s3cmci_host * Host;
 . 4      struct mmc_host * MMC;
 . 5  ...
 . 6      / * assign mmc_host * / 
. 7      MMC = mmc_alloc_host ( the sizeof ( struct s3cmci_host), & pdev-> dev);
 8 ...     / * omitted to do is set the stage s3cmci_host members and gpio pin * / 
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 }

among them,

1. mmc_alloc_host () function call relationship is as follows:

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 () function call relationship is as follows:

mmc_add_host (MMC);
   -> device_add (& host-> class_dev);
   -> mmc_start_host (Host);
     -> mmc_power_off (Host);             / * down refresh * / 
    -> mmc_detect_change (Host, 0 );
       -> mmc_schedule_delayed_work (& Host -> Detect, delay);
         / * add a delay in the work queue task host-> Detect * / 
        -> return queue_delayed_work (workQueue, work, delay);

 

mmc_add_host () function will eventually call mmc_alloc_host () to initialize the work queue mmc_rescan () function. This function is used to detect whether a card is inserted into the card controller.

 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 }
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     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. 整体框架:

 

Guess you like

Origin www.cnblogs.com/Lioker/p/11258958.html