十九、eMMC驱动框架分析

一、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文件。

文件链接:

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

提取码为: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_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. 分配、设置并添加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. 整体框架:

原文地址:https://www.cnblogs.com/Lioker/p/11258958.html

时间: 2024-10-08 15:26:20

十九、eMMC驱动框架分析的相关文章

Linux USB驱动框架分析 【转】

转自:http://blog.chinaunix.net/uid-11848011-id-96188.html 初次接触与OS相关的设备驱动编写,感觉还挺有意思的,为了不至于忘掉看过的东西,笔记跟总结当然不可缺,更何况我决定为嵌入式卖命了.好,言归正传,我说一说这段时间的收获,跟大家分享一下Linux的驱动开发.但这次只先针对Linux的USB子系统作分析,因为周五研讨老板催货.当然,还会顺带提一下其他的驱动程序写法. 事实上,Linux的设备驱动都遵循一个惯例——表征驱动程序(用driver更

Linux USB驱动框架分析(2)【转】

转自:http://blog.chinaunix.net/uid-23046336-id-3243543.html 看了http://blog.chinaunix.net/uid-11848011-id-96188.html的驱动框架分析,感觉受益匪浅.对于一些内容,我自己查漏补缺. 首先我们按照顺序,看内核模块的注册以及释放函数如下: 点击(此处)折叠或打开 static int __init usb_skel_init(void) { int result; /* register this

QT开发(三十九)——GraphicsView框架

QT开发(三十九)--GraphicsView框架 本文主要翻译自QT 5.6.2GraphicsView官方文档 一.GraphicsView框架简介 QT4.2开始引入了Graphics View框架用来取代QT3中的Canvas模块,并作出了改进,Graphics View框架实现了模型-视图结构的图形管理,能对大量图元进行管理,支持碰撞检测,坐标变换和图元组等多种方便的功能. GraphicsView框架结构主要包含三个主要的类QGraphicsScene(场景).QGraphicsVi

第十九章 Django框架——Admin组件

第十九章 Django框架--Admin组件 一.创建超级用户 二.配置后台管理路由 三.注册admin后台管理页面 四.配置admin后台管理页面 一.创建超级用户 python manage.py createsuperuser 二.配置后台管理路由 url(r'^admin/', include(admin.site.urls)) #默认配置 三.注册admin后台管理页面 admin.py from django.contrib import admin from api import

二十四、V4L2框架分析和虚拟摄像头驱动编写

一.V4L2框架分析 V4L2(video for linux version 2),是内核中视频设备的驱动框架,为上层访问视频设备提供统一接口. V4L2整体框架如下图: 图中主要包括四个部分: 1. 字符设备驱动程序核心:V4L2本身就是一个字符设备,上层连接用户空间 2. V4L2驱动核心:构造通用的视频设备驱动框架,为上层操作提供统一接口 3. 平台V4L2驱动:在V4L2框架下,根据平台自身特性实现与平台相关的V4L2驱动部分,包括注册video_device和v4l2_dev 4.

7 Linux usb驱动框架分析

现象:将USB设备接入PC,PC右下角上会弹出"发现xx新设备",例如"发现andriod phone"若PC上没有安装该设备的驱动程序,则会弹出对话框提示"安装驱动程序". 问1:没有安装设备的驱动程序之前,为什么PC还能发现andriod phone设备呢? 答1:windows系统中已经安装了USB的"总线驱动程序",是"总线驱动程序"发现了新的设备,而提示我们安装的是"设备驱动程序&quo

Linux下USB驱动框架分析【转】

转自:http://blog.csdn.net/brucexu1978/article/details/17583407 版权声明:本文为博主原创文章,未经博主允许不得转载. http://www.cnblogs.com/general001/articles/2319552.html http://blog.csdn.net/uruita/article/details/7263290:MODULE_DEVICE_TABLE http://blog.chinaunix.net/uid-2590

tty初探—uart驱动框架分析

本文参考了大量牛人的博客,对大神的分享表示由衷的感谢. 主要参考: tty驱动分析 :http://www.wowotech.net/linux_kenrel/183.html Linux TTY驱动--Uart_driver底层:http://blog.csdn.net/sharecode/article/details/9196591 Linux TTY驱动--Serial Core层  :http://blog.csdn.net/sharecode/article/details/9197

USB摄像头驱动框架分析(五)

一.USB摄像头驱动框架如下所示:1.构造一个usb_driver2.设置   probe:        2.1. 分配video_device:video_device_alloc        2.2. 设置           .fops           .ioctl_ops (里面需要设置11项)           如果要用内核提供的缓冲区操作函数,还需要构造一个videobuf_queue_ops        2.3. 注册: video_register_device