精华内容
下载资源
问答
  • mmc驱动

    2012-07-11 10:30:36
    CORE部分:这是整个MMC的核心存,这部分完成了不同协议和规范的实现,并为HOST层的驱动提供了接口函数。 CARD部分:因为这些记忆卡都是块设备,当然需要提供块设备的驱动程序,这部分就是实现了将你的SD卡如何实

    MMC子系统的代码在kernel/driver/MMC下.MMC子系统范围三个部分:
    HOST部分是针对不同主机的驱动程序,这一部是驱动程序工程师需要根据自己的特点平台来完成的。
    CORE部分:这是整个MMC的核心存,这部分完成了不同协议和规范的实现,并为HOST层的驱动提供了接口函数。
    CARD部分:因为这些记忆卡都是块设备,当然需要提供块设备的驱动程序,这部分就是实现了将你的SD卡如何实现为块设备的。

    1.HOST层分析:
    HOST层实现的就是我们针对特定主机的驱动程序,我们先采用platform_driver_register(&&atxx_sdhc_drv)注册了一个平台设备,接下来重点关注probe函数。在这个函数中,我们与CORE的联系是通过下面三句实现的。

     

    首先分配一个mmc_host结构体,

    mmc = mmc_alloc_host(sizeof(atxx_host_t), &dev->dev);注意sizeof(atxx_host_t),这样就能在mmc_host中找到了atxx_host。

     

    接下来为mmc->ops赋值,mmc->ops = &atxx_ops;atxx_ops结构实现了几个很重要的函数。中间还对mmc结构的很多成员进行了赋值,最后将mmc结构加入到MMC子系统,mmc_alloc_host,以及mmc_add_host的具体做了什么事情,我们在下节再分析,这三句是些MMC层驱动必须包含的。



    static struct mmc_host_ops atxx_ops =

    {

        .enable            = atxx_enable,

        .disable       = atxx_disable,

        .request       = atxx_request,

        .set_ios       = atxx_set_ios,

        .get_ro            = atxx_get_ro,

        .get_cd            = atxx_get_cd,

        .enable_sdio_irq  = atxx_enable_sdio_irq,

    };

    atxx_get_ro:判断我们的卡是否是写保护的
    atxx_get_cd:判断卡是否存在
    atxx_set_ios:atxx_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
    依据核心层传递过来的ios,来设置硬件IO,包括引脚配置,使能时钟,和配置总线带宽。
    atxx_request:这个函数是最主要,也最复杂的函数,实现了命令和数据的发送和接收,
    当CORE部分需要发送命令或者传输数据时,都会调用这个函数,并传递mrq请求。

    static void atxx_request(struct mmc_host *mmc, struct mmc_request *req)

    {

        atxx_host_t *host;

        host = mmc_priv(mmc);

        host->mrq = req;

     

        PDEBUG("atxx_request:cmd %d,arg 0x%x,resp 0x%x.\n",req->cmd->opcode,

                req->cmd->arg,req->cmd->resp[0]);

     

        if (card_present(host) == 0) {

            PDEBUG("card is out\n");

            req->cmd->error = -ENOMEDIUM;

            host->mrq = NULL;

            mmc_request_done(host->mmc,req); //如果卡不存在就终止请求

            return;

        }

        if (atxx_setup_cmd(host,req->cmd) != 0) {

            PERROR("atxx_setup_cmd failed\n");

            req->cmd->error = -EIO;

            host->mrq = NULL;

            mmc_request_done(host->mmc,req);

            return;

        }

     

        return;

    }

     

    Atxx_host.c分析:

    static int __init atxx_sdhc_init(void)

    {

        return platform_driver_register(&atxx_sdhc_drv);

    }

    注册平台驱动

    static struct platform_driver atxx_sdhc_drv =

    {

        .driver = {

            .name  = "sdhc",

            .owner = THIS_MODULE,

            .pm = &atxx_sdhc_pm_ops,

        },

        .probe     = atxx_sdhc_probe,

        .remove        = atxx_sdhc_remove,

        .shutdown  = atxx_sdhc_shutdown,

    };

    .probe     = atxx_sdhc_probe,是核心,由于host与硬件是直接相关的,probe接下来将做部分关于硬件初始化的工作

    进入函数atxx_sdhc_probe

        r = platform_get_resource(dev, IORESOURCE_MEM, 0);

        irq = platform_get_resource(dev, IORESOURCE_IRQ, 0)->start;

    获取平台资源

    mmc = mmc_alloc_host(sizeof(atxx_host_t), &dev->dev);分配一个mmc的控制器

    host = mmc_priv(mmc);

    host->sdhc.controller_id = dev->id;

    host->pdev = dev;

     

    ret=request_threaded_irq(irq,atxx_sdhc_irq_handle,atxx_sdhc_irq_thread,     IRQF_DISABLED, host_names[dev->id], host);//中断申请

     

    host->mmc = mmc;

    host->irq = irq;

    host->sdhc.ctx = host

    对atxx_host_t这个私有结构的配置

     

    if (host->sdhc.controller_id == SYNOP_MEM_CARD) {

            host->clk_gate = clk_get(&dev->dev,"a_sd");

        } else {

            host->clk_gate = clk_get(&dev->dev,"a_sdio");

        }

    clk_enable(host->clk_gate);获取时钟并使能        ????时钟从哪里获取,怎么获取的

     

    mmc->ops = &atxx_ops;向core层提供的接口函数集

     

    mmc->ocr_avail = MMC_VDD_27_28 | MMC_VDD_28_29 | MMC_VDD_30_31

            | MMC_VDD_32_33 | MMC_VDD_33_34;

    #ifdef CONFIG_ATXX_SDHC_4BITS

        PINFO("SD bus width is set to 4bits\n");

        mmc->caps = MMC_CAP_SDIO_IRQ | MMC_CAP_4_BIT_DATA;

    #else

        mmc->caps = MMC_CAP_SDIO_IRQ;

    #endif

        mmc->f_min = 125000;

        mmc->f_max = 26000000;

     

    #if defined(CONFIG_MACH_T3C)

            mmc->caps |= MMC_CAP_SD_HIGHSPEED;

            mmc->f_max = 52000000;

    #endif

     

        mmc->max_hw_segs = MAX_DESCRIPTOR_NUM;

        mmc->max_phys_segs = MAX_DESCRIPTOR_NUM;

       

     

        mmc->max_req_size = MAX_DESCRIPTOR_NUM * PAGE_SIZE;

       

     

        mmc->max_seg_size = PAGE_SIZE;

     

        mmc->max_blk_size = 65535;

        mmc->max_blk_count = 65535;

        PDEBUG("max_segs %d, max_size %d,max_blk_size %d\n",mmc->max_hw_segs,mmc->max_seg_size,mmc->max_blk_size);

    向mmc_host结构体赋值

     

    host->dma_desc = dma_alloc_coherent(NULL,

                sizeof(struct DES) * MAX_DESCRIPTOR_NUM,

                &host->dma_desc_phy, GFP_KERNEL);

    Dma映射

     

        platform_set_drvdata(dev, mmc);

     

        mmc_add_host(mmc);增加设备类并启动host

     

    mmc_add_host分析:

    int mmc_add_host(struct mmc_host *host)

    {

        int err;

     

        WARN_ON((host->caps & MMC_CAP_SDIO_IRQ) &&

            !host->ops->enable_sdio_irq);

     

        led_trigger_register_simple(dev_name(&host->class_dev), &host->led);

     

        err = device_add(&host->class_dev);

        if (err)

            return err;

     

    #ifdef CONFIG_DEBUG_FS

        mmc_add_host_debugfs(host);

    #endif

     

        mmc_start_host(host);  //开启主机控制器

     

        return 0;

    }

     

    void mmc_start_host(struct mmc_host *host)

    {

        mmc_power_off(host);

        mmc_detect_change(host, 0);

    }

    Mmc_delect_change函数中调用mmc_schedule_delayed_work(&host->detect, delay);作用是延时delay后就会调用mmc_rescan函数,由于delay=0,即相当于直接调用mmc_rescan函数

     

    进入mmc_rescan函数

     

        if (host->ops->get_cd && host->ops->get_cd(host) == 0)

            goto out;

    检测是否有sd卡存在

    host->ops->get_cd(host)

    Get_cd对应atxx_ops结构中的.get_cd         = atxx_get_cd,

     

    mmc_claim_host(host);获取主机控制权

    mmc_power_up(host);设置并开启主机控制器     

    mmc_go_idle(host);是sd卡进入idle状态

    err = mmc_send_app_op_cond(host, 0, &ocr);对sd卡的设置

     

    if (mmc_attach_sd(host, ocr))

    函数mmc_attach_sd开始对sd的初始化

    其中mmc_sd_attach_bus_ops(host);完成bus_ops与sd的绑定

    host->ocr = mmc_select_voltage(host, ocr);这是根据host的电压范围和读取到的SD卡工作电压,来判断工作电压是否匹配。

    err = mmc_sd_init_card(host, host->ocr, NULL);这个函数完成了整个SD卡初始化的全动作

    函数mmc_sd_init_card中在调用mmc_all_send_cid函数

          memcpy(cid, cmd.resp, sizeof(u32) * 4);拷贝身份信息

    函数mmc_sd_init_card继续调用card = mmc_alloc_card(host, &sd_type);

    card->dev.parent = mmc_classdev(host);

    card->dev.bus = &mmc_bus_type;

    card->dev.release = mmc_release_card;

    card->dev.type = type;

    sd卡devices信息与core层的driver相匹配

    展开全文
  • mmc驱动代码分析

    2021-04-05 20:34:52
    阅读mmc驱动源码的笔记

    mmc驱动代码分析

    参考资料

    先附上参考资料,也方便自己查询。

    原理性的知识就不做分析了,参考资料里的大佬分析得都很好。

    蜗窝科技网站的mmcemmc相关的文章,Linux Kernel Internals

    Hacker_Albert博客mmc相关的文章。

    Linux设备驱动开发详解-基于最新的Linux 4.0内核》–宋宝华 编著

    mmc驱动代码分析

    本文章的分析基于linux-4.9.259版本内核,可通过163镜像站下载linux-4.9.259.tar.xz

    MMC/SD存储卡的驱动位于内核源码的目录drivers/mmc下,下面又分为card、core、host3个子目录。card层实际上跟Linux的块设备子系统对接,实现块设备驱动以及完成请求,但是具体的协议经过core层的接口,最终通过host完成传输,因此整个MMC子系统的框架如下图所示。card目录除实现标准的MMC/SD存储卡以外,还包含一些SDIO外设和驱动。core目录除了给card提供接口外,也定义好了host驱动的框架。

    在这里插入图片描述

    上图MMC子系统的框架是通过Fedora33系统的LibreOffice Draw软件画的,谁说Linux只有字符界面,画图,视频,什么都行。

    card/block.c 文件 的mmc_blk_init初始化块设备,通过

    res = register_blkdev(MMC_BLOCK_MAJOR, "mmc");
    

    注册块设备。再通过

    res = mmc_register_driver(&mmc_driver);
    

    向core层注册mmc驱动。

    其中mmc_driver结构体有2个重要成员函数,mmc_blk_probe函数是在检测到mmc卡插入时执行,mmc_blk_remove函数是在mmc卡移除时执行。

    static struct mmc_driver mmc_driver = {
        .drv        = {  
            .name   = "mmcblk",
            .pm = &mmc_blk_pm_ops,
        },   
        .probe      = mmc_blk_probe,
        .remove     = mmc_blk_remove,
        .shutdown   = mmc_blk_shutdown,
    };
    

    core/bus.c文件下的mmc_bus_probe调用mmc_blk_probe

    static struct bus_type mmc_bus_type = {
        .name       = "mmc",
        .dev_groups = mmc_dev_groups,
        .match      = mmc_bus_match,
        .uevent     = mmc_bus_uevent,
        .probe      = mmc_bus_probe,
        .remove     = mmc_bus_remove,
        .shutdown   = mmc_bus_shutdown,
        .pm     = &mmc_bus_pm_ops,
    }; 
    

    mmc_bus_type的分析 TODO

    函数调用顺序为:mmc_blk_probe-> mmc_blk_alloc-> mmc_blk_alloc_req-> mmc_init_queue

    mmc_blk_alloc_req函数中,通过

    md->disk->fops = &mmc_bdops;
    

    指定块设备操作的函数,当应用层执行open、close、ioctl等操作时调用相应的接口:

    static const struct block_device_operations mmc_bdops = {
        .open           = mmc_blk_open,
        .release        = mmc_blk_release,
        .getgeo         = mmc_blk_getgeo,
        .owner          = THIS_MODULE,
        .ioctl          = mmc_blk_ioctl,
    #ifdef CONFIG_COMPAT
        .compat_ioctl       = mmc_blk_compat_ioctl,
    #endif
    };
    
    

    mmc_init_queue中通过

    mq->queue = blk_init_queue(mmc_request_fn, lock);
    

    绑定了请求处理函数mmc_request_fn。再通过

    mq->thread = kthread_run(mmc_queue_thread, mq, "mmcqd/%d%s", host->index, subname ? subname : "");
    

    运行mmc对应的内核线程mmc_queue_thread

    当应用层调用read,write等接口时,mmc_request_fn函数会唤醒mmc对应的内核线程mmc_queue_thread来处理请求。mmc对应的内核线程mmc_queue_thread执行mmc_blk_issue_rq函数,mmc_blk_issue_rq函数再调用mmc_blk_issue_rw_rqmmc_blk_issue_rw_rq函数最终会调用core层的mmc_start_req函数。

    mmc_start_req函数里调用host驱动的mmc_host_ops成员函数(位于文件host/sdhci.c):

    static const struct mmc_host_ops sdhci_ops = {
        .request    = sdhci_request,
        .post_req   = sdhci_post_req,
        .pre_req    = sdhci_pre_req,
        .set_ios    = sdhci_set_ios,
        .get_cd     = sdhci_get_cd,
        .get_ro     = sdhci_get_ro,
        .hw_reset   = sdhci_hw_reset,
        .enable_sdio_irq = sdhci_enable_sdio_irq,
        .start_signal_voltage_switch    = sdhci_start_signal_voltage_switch,
        .prepare_hs400_tuning       = sdhci_prepare_hs400_tuning,
        .execute_tuning         = sdhci_execute_tuning,
        .select_drive_strength      = sdhci_select_drive_strength,
        .card_event         = sdhci_card_event,
        .card_busy  = sdhci_card_busy,
    };
    

    接下来以Samsung SoC平台为例子分析host driver(文件host/sdhci-s3c.c):

    注册platfom驱动:

    static struct platform_driver sdhci_s3c_driver = { 
        .probe      = sdhci_s3c_probe,
        .remove     = sdhci_s3c_remove,
        .id_table   = sdhci_s3c_driver_ids,
        .driver     = { 
            .name   = "s3c-sdhci",
            .of_match_table = of_match_ptr(sdhci_s3c_dt_match),
            .pm = &sdhci_s3c_pmops,
        },  
    };
    
    module_platform_driver(sdhci_s3c_driver);
    

    当设备和驱动匹配时调用sdhci_s3c_driver中的成员函数sdhci_s3c_probe

    sdhci_s3c_probe函数先初始化参数,其中sdhci_s3c_opssdhci_ops类型,是host driver要实现的核心内容,由于各个host的硬件有所差异,所以实际和硬件交互的驱动部分还是在host driver中实现:

    static struct sdhci_ops sdhci_s3c_ops = {
        .get_max_clock      = sdhci_s3c_get_max_clk,
        .set_clock      = sdhci_s3c_set_clock,
        .get_min_clock      = sdhci_s3c_get_min_clock,
        .set_bus_width      = sdhci_s3c_set_bus_width,
        .reset          = sdhci_reset,
        .set_uhs_signaling  = sdhci_set_uhs_signaling,
    };
    

    sdhci_s3c_probe函数最后调用sdhci_add_host注册sdhci_host,在注册之前已经设置的信息有:sdhci的寄存器的映射过后的基地址、quirks、quirks2、中断号、提供给sdhci core用来操作硬件的操作函数 等。

    上面讲到的mmc_start_req函数调用Samsung SoC平台的sdhci_s3c_ops成员函数(通过struct mmc_host_ops sdhci_ops里的成员函数调用)。

    未完待续。。。

    展开全文
  • uboot mmc驱动移植

    2020-04-06 22:57:18
    uboot mmc驱动移植uboot mmc驱动移植1,相关文件2,修改对应文件夹下的Makefile3,浏览对应的.c源文件,并进行修改4,移植cmd_mmc.c5,编译时报错:mmc_write.c文件中有些函数没有定义 uboot mmc驱动移植 将三星官方...

    uboot mmc驱动移植

    将三星官方uboot的mmc驱动移植到uboot官方的uboot中。

    1,相关文件

    需要一致的c源文件有如下几个相关文件

    2,三星uboot的mmc初始化流程

    1,调用mmc_initialize()函数,这个函数在mmc.c中
    在这里插入图片描述
    2,在mmc_initialize()函数中调用cpu_mmc_init()函数,这个函数在cpu.c中
    在这里插入图片描述
    3,在cpu_mmc_init()函数中初始化了mmc的gpio,时钟,并调用了smdk_s3c_hsmmc_init()这个函数,在这些函数中通过USE_MMC0,USE_MMC1等宏来决定初始化哪个mmc。(对应的函数在setup_hsmmc.c中)
    在这里插入图片描述
    4,而smdk_s3c_hsmmc_init()这个函数,通过调用s3c_hsmmc_initialize()这个函数来将对应mmc的参数和初始化函数赋值给对应的mmc结构体中的变量,并将这个mmc的结构体注册到mmc设备列表中。
    在这里插入图片描述
    在这里插入图片描述
    5,调用mmc_initialize()函数,还调用了mmc_init()函数初始化具体的mmc设备,这个函数就是通调用在s3c_hsmmc_initialize()函数中注册的初始化函数来初始化mmc。
    在这里插入图片描述

    2,修改对应文件夹下的Makefile

    修改对应文件夹下的Makefile,使得uboot编译时编译对应的文件

    3,浏览对应的.c源文件,并进行修改

    从三星移植过来的那些.c源文件中一些没有被定义的函数和宏,需要将这些函数和宏移植过来(其中包括将对应的.h文件拷贝过来)
    在这里插入图片描述
    修改的内容:
    1,在配置头文件中添加#define USE_MMC0,#define USE_MMC2这两个宏
    2,uboot官方没有提供get_MPLL_CLK()这个函数,用get_pll_clk(MPLL)来替代。
    3,uboot的官方的代码中没有reg.h这个头文件
    4,在需要添加clk.h的地方添加这个头文件
    在这里插入图片描述

    4,移植cmd_mmc.c

    uboot的cmd_mmc.c文件中定义了和mmc相关的命令,这些命令是通过调用mmc驱动文件中的函数实现的。
    所以,移植完三星的mmc驱动后,还需要用替换成三星uboot的cmd_mmc.c替换uboot官方原有的cmd_mmc.c文件,这样才能调用对应的mmc驱动程序。

    5,编译时报错:mmc_write.c文件中有些函数没有定义

    mmc_write.c文件依赖于cmd_mmc.c等文件,所以会报错。
    我们移植的三星uboot的mmc驱动中没有对应的文件,所以我们不需要编译这个文件,直接把该文件从对应的Makefile中去掉。

    展开全文
  • mmc驱动框架基础介绍

    2020-10-12 14:54:26
    mmc驱动框架基础介绍 本文主要介绍一下Linux内核的mmc子系统驱动的整体框架。 (作者对SDIO设备不熟悉,所以不过多描述;鄙人才疏学浅,有不当之处,还请指教。) 大概包括以下几个部分: mmc子系统驱动简介 mmc设备...

    mmc驱动框架基础介绍

    本文主要介绍一下Linux内核的mmc子系统驱动的整体框架。
    (作者对SDIO设备不熟悉,所以不过多描述;鄙人才疏学浅,有不当之处,还请指教。)

    大概包括以下几个部分:

    • mmc子系统驱动简介
    • mmc设备的识别、初始化流程的大概介绍
    • mmc设备的读写流程大概介绍

    mmc子系统驱动简介

    mmc子系统驱动分为三层,分别为:

    • Block层:主要作用是对接通用块层,创建块设备及上层请求处理等工作。
    • Core层:主要提供协议层的内容,为Block层、Host层提供相应接口。
    • Host层:主要是对接SOC的MMC控制器,是比较底层的寄存器操作及中断操作。

    上下层之间的交互大概如下图所示:
    在这里插入图片描述

    mmc子系统实际包含三种设备的驱动:

    • mmc设备
    • SD设备
    • SDIO设备

    MMC、SD、SDIO的技术本质是一样的(使用相同的总线规范,等等),都是从MMC规范演化而来;MMC强调的是多媒体存储(MM,MultiMedia),SD强调的是安全和数据保护(S,Secure);SDIO是从SD演化出来的,强调的是接口(IO,Input/Output),不再关注另一端的具体形态(可以
    是WIFI设备、Bluetooth设备、GPS等等)。

    虽然这三种设备共用这一套子系统,但对应到实际的设备时,这三种不同的设备有不同的物理层规范协议:

    eMMC:

    https://www.jedec.org/

    eMMC物理层协议V5.1版本:

    JESD84-B51.pdf

    SD:

    https://www.sdcard.org/

    SD物理层协议V3.1版本:

    Part_1_Physical_Layer_Simplified_Specification_Ver_3.01.pdf

    SDIO:

    https://www.sdcard.org/

    SDIO物理层协议V2.0版本:

    Part_E1_SDIO_Simplified_Specification_Ver2.00.pdf

    从下面这个图中也可以看到SDIO其实是SD的扩展:
    在这里插入图片描述

    或者直接从官网中可以看到更多的关于SD及其扩展的协议:
    在这里插入图片描述

    mmc设备的初始化、识别流程的大概介绍

    这里主要介绍代码中的初始化及识别流程,主要以hi3559av100的代码为例。

    初始化流程

    在设备启动过程中,会执行sdhci_hisi_init函数注册平台驱动设备,注册完后,系统总线会执行probe进行探测设备,这里的probe函数注册为sdhci_hisi_probe,通过跟代码,可以看到其实这个的调用流程如下所示:
    在这里插入图片描述

    可以看到这里是初始化了一个延迟工作队列,任务是调用mmc_rescan函数进行识别设备流程。

    识别流程

    mmc_rescan函数就是用来扫描识别eMMCSDSDIO设备的,设备识别时会一个较低的频率去与设备交互,当设备识别成功后,可以将工作频率提高。这里会先以400KHz~100KHz这四种频率去与设备交互(注1),每次都调用mmc_rescan_try_freq去扫描设备。

    注1:虽然代码上会以这四个频率去识别设备,但实际频率往往取决于芯片本身提供的频率。比如如果芯片的MMC控制器模块在初始化阶段只能提供300KHz的频率,那么实际工作就是一直以300KHz的频率去识别设备。

    实际扫描时会先尝试识别SDIO设备,如果成功则返回;否则,继续尝试识别SD设备,如果成功则返回;否则,继续尝试识别MMC设备,如果成功则返回;否则返回错误。当然,识别前会先对设备上电,硬件复位等,如果都没有识别到设备,就对设备下电。比如海思有专门的寄存器(PWR_CTRL_R)来控制eMMC或者SD的电源引脚,来控制设备的上下电。
    在这里插入图片描述

    正常eMMC设备或者SOC芯片上电时已经在卡槽的SD设备,这里已经被识别成功了。而热插拔的设备还需要另外的工作。

    热插拔识别流程

    这里大概分三个情况来描述热插拔识别流程:

    • SOC带卡检测引脚,且mmc控制器部分带专门的中断寄存器位
    • SOC不带卡检测引脚,用普通中断引脚做检测
    • 不使用卡检测引脚

    控制器带专门检测引脚的热插拔识别流程

    海思的SOC大部分目前都支持有专门的引脚用来卡插入拔出检测,通过读取PASTATE_R寄存器或者配置中断的方式进行。这里是以中断检测的方式进行卡插入拔出检测:
    在这里插入图片描述

    可以看到,接收到插入或移除的中断后,中断处理中重新启动detect工作任务去扫描设备。

    中断处理的代码如下:

    static irqreturn_t sdhci_thread_irq(int irq, void *dev_id)
    {
    	...
    	if (isr & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) {
    		struct mmc_host *mmc = host->mmc;
    
    		mmc->ops->card_event(mmc);
    		mmc_detect_change(mmc, msecs_to_jiffies(200));
    	}
    	...
    
    	return isr ? IRQ_HANDLED : IRQ_NONE;
    }

    控制器无专门检测引脚的热插拔识别流程

    有的芯片没有专门的检测引脚,需要用普通的GPIO引脚作为检测引脚。这样的话可以在设备树的节点里配置cd的引脚号,并配置触发电平,比如cd-gpios = <&gpio6 27 GPIO_ACTIVE_LOW>。而mmc驱动中会解析此节点的值,并申请gpio中断。

    int mmc_of_parse(struct mmc_host *host)
    {
        ...
        ret = mmc_gpiod_request_cd(host, "cd", 0, true,
    					   0, &cd_gpio_invert); 
        ...
    }

    无检测引脚的热插拔识别流程

    若卡槽没有专门的检测引脚或者SOC上的没有预留检测引脚,可以用轮询的方式来检测设备的热插拔。
    在上面的mmc_rescan函数中,可以看到执行到最后有一个条件判断:根据是否有MMC_CAP_NEEDS_POLL能力,来再次调度host->detect任务。而这个能力可以通过设备树节点赋值broken-cd能力获得,或者直接在host初始化时对他的caps能力赋值。

    void mmc_rescan(struct work_struct *work)
    {
        ...
     out:
    	if (host->caps & MMC_CAP_NEEDS_POLL)
    		mmc_schedule_delayed_work(&host->detect, HZ);
    }

    mmc设备的读写流程大概介绍

    从开始的子系统简介里面可以看到上下层之间的交互大概流程,无论用户空间是通过文件系统操作mmc设备,都会经过通用块层。这里就对上面的交互流程进行稍微的细化,主要以读写数据为主。

    在这里插入图片描述

    从上图可以看到,读写数据的流程主要经过以下几层次:(下面的内容也是围绕上面的图来进行)

    • 通用块层
    • mmc block层
    • mmc core层
    • mmc host层
    • soc 控制器 && mmc设备

    通用块层

    无论是经过文件系统读写,还是直接用open(block_dev)的方式读写mmc设备,最后都是会调用submit_bio函数提交IO请求,最后由内核提交到具体的驱动,这里是mmc驱动:

    在这里插入图片描述

    如果submit_bio是提交的读操作,那么此时的空间已经由内核分配好;如果提交的是写操作,那么用户空间的数据已经拷贝到内核空间。

    图中绿色函数blk_queue_bio,是通用块层提供的接口,最终会调用到红色的函数mmc_request_fn,即到了具体的驱动:mmc驱动层了。

    这个mmc_request_fn函数是在mmd驱动的block层初始化时调用通用块层的接口初始化时传入:
    在这里插入图片描述

    mmc block层

    在通用块层调用mmc_request_fn之后,接着便唤醒mmc的读写线程mmcqd/%d%s,线程执行mmc_queue_thread函数,从request_queue请求队列中获取单个请求request,并将请求通过mmc_blk_issue_rq发送出去,最后经过mmc_blk_issue_rw_rq发送到mmc core层。

    在这里,会将上层传下来的request请求解析:确定请求的读写方向、单块读写还是多块读写、读写命令的起始地址(在mmc设备上的地址)和数据长度、确定DMA搬移地址、设置超时等工作。
    在这里插入图片描述

    mmc core层

    mmc core层主要工作还是为协议服务。

    mmc_start_req函数发起一个非阻塞的请求,此时的请求已经不是上层传下来的request请求,而是一个只在mmc驱动范围内作用的、异步的请求mmc_async_req。如果发送成功这将这次发送的请求返回,否则返回空。
    在这里插入图片描述

    mmc host层 && soc 控制器 && mmc设备

    到了mmc host层,则主要是soc控制器的寄存器相以及中断等操作。在这里,DMA搬移的数据目的地址或者源地址都以及经过dma_map_sg等相关函数映射到scatterlist结构体里,DMA可以直接操作映射后的地址,并且可以避免申请连续大块内存的需求。
    在这里插入图片描述

    eMMC/SD协议中,发送读写数据时会预先发送命令告诉eMMC/SD设备,命令中会带有将要读写数据的地址,如果设备响应OK,在进行数据交互。读写数据的量可以由两种方式决定,一个是通过CMD23命令并带上要读写的数据量,提前告诉eMMC/SD设备;第二个是通过CMD12命令来直接告诉eMMC/SD设备停止发送/接收数据。

    控制器发送/接受完后会产生数据中断,在中断处理函数里,会调用mmc core层的接口,通知上层完成数据的传输。

    读写数据返回,通知上层

    前面mmc block层提到,mmc_queue_thread函数,从request_queue请求队列中获取单个请求request并发送。如果request为空,但前一个请求没有发送完,这时候会发送一个空的request请求。当数据传输完成后,mmc host层调用mmc core层的mmc_request_done接口,标志数据传输结束,此时函数返回到mmc_blk_issue_rw_rq,并调用通用块层的blk_end_request接口,表示数据以及从设备返回,这时候就代表着一整个读写流程的结束。

    读写过程中主要涉及的数据结构

    在通用块层到mmc驱动层再到硬件DMA,经过了许多个数据结构:
    通用块层使用submit_bio函数提交IO请求时,使用的结构体是struct bio,然后经过blk_queue_bio函数的处理,将bio里面IO请求合并到struct request_queue请求队列里面。到mmc驱动层时,会从request_queue请求队列里面将单个的struct request请求通过blk_fetch_request函数取出,然后request指针会存放到struct mmc_request,同时,在mmc的block层准备数据时,会将request映射到struct scatterlistscatterlist指针存放到struct mmc_data中。在准备DMA传输时,再将scatterlist使用dma_map_sg进行映射,交给DMA传输。

    在这里插入图片描述

    数据结构的关联关系大概如下图所示,在这里就不过多描述了。
    在这里插入图片描述

    补充

    如何确定读写的位置的

    每个struct request都会有一个叫__sector的变量,用来记录读写磁盘的位置的。

    struct request同时记录有struct hd_struct *partstruct hd_struct结构体里面也有一个关于位置的变量sector_t start_sect,而这个位置是分区在磁盘上的起始位置。

    在这里插入图片描述

    mmc设备磁盘数据的加解密过程

    在前面数据传输过程的了解以及相关数据结构体的大概关系理清楚后,可以做一个对磁盘数据解密的需求。

    大概过程如下:

    1. 打包时将只读分区的数据通过AES算法进行加密,烧到eMMC中。
    2. 设备启动读取只读加密分区时,使用密钥对只读加密分区数据进行解密。

    关键点:

    1. 如何判断分区是否需要解密。
    2. 如何正确解密。
    3. 在哪里解密。

    做法:

    1. 在上面描述读写位置时,可以知道每一个request都会记录到所属的分区hd_struct,这个结构体中的partition_meta_info里面记录了当前分区的名字,可以根据名字来判断是否需要解密。
    2. AES算法加密分区数据时,是与烧录在磁盘上的位置无关的,如果解密算法把相对位置作为IV去解密,需要将请求的绝对位置换算成相对位置,使用sector_t start_sectrequest的请求位置即可换算出来。
    3. 在mmc驱动层中,只有block层合适做解密。原因有两个:core层提供的data_done之类的函数会在中断上下文执行,解密数据需要时间,不合适;host层没有办法处理发出的重复请求且不知道数据是否有效,直接解密会浪费时间。所在在block层中,在合适的blk_end_request之前执行解密即可。

    识别过程的更具体描述

    mmc_rescan_try_freq扫描过程就是一个上电->复位->发送命令尝试->成功后按照协议继续发命令的一个流程;如果失败了就执行下电,再次尝试。

    在这里插入图片描述

    扫描SD设备如下图所示:
    在这里插入图片描述

    从下面的流程图可以看到,代码就是协议的具体实现:
    在这里插入图片描述

    SD卡锁定功能的实现

    SD卡支持锁定功能,但目前的内核代码是没有支持的,可以通过修改内核代码来实现SD卡锁定功能。

    • 优点:有效保护卡内数据,不输入密码卡无法识别
    • 缺点:需要额外的代码支持,且密码需要在识别过程中输入,交互性不好

    更多细节在协议上的4.3.7 Card Lock/Unlock Operation有描述,过程不复杂,但用户密码管理不友好。

    当然,还有另外的SD卡安全协议内容:Part_3_Security_Specification_Ver3.00不在本篇讨论范围内。
    的实现

    SD卡支持锁定功能,但目前的内核代码是没有支持的,可以通过修改内核代码来实现SD卡锁定功能。

    • 优点:有效保护卡内数据,不输入密码卡无法识别
    • 缺点:需要额外的代码支持,且密码需要在识别过程中输入,交互性不好

    更多细节在协议上的4.3.7 Card Lock/Unlock Operation有描述,过程不复杂,但用户密码管理不友好。

    当然,还有另外的SD卡安全协议内容:Part_3_Security_Specification_Ver3.00不在本篇讨论范围内。

    展开全文
  • 情景:由于出现开机加载SD卡失败导致主机重启的情况,现对加载mmc驱动时间做一下调整,等主机完全准备好再加载mmc驱动,之前mmc驱动是放在内核里编译的,现调整为开机完成后应用层的APP启动之前手动加载mmc驱动。...
  • linux-2.6.2x的mmc驱动与linux-2.6.1x的mmc驱动的区别 在linux-2.6.2x中,mmc驱动用到的block_device_operations结构已重新定义,请看: linux-2.6.1x: struct block_device_operations { int (*open) (struct...
  • MMC驱动程序,可以应用到mx21嵌入式开发板上的驱动程序!
  • mmc驱动框架分析1

    2015-06-17 10:15:26
    mmc驱动框架分为core层,host层和card层。
  • MMC驱动之mmc host

    千次阅读 2017-08-16 23:30:01
    mmc host驱动的核心的数据结构是struct mmc_host,而对具体的mmc host驱动,是要实现host相关的操作函数集struct mmc_host_ops,比如卡的检测,写保护,发送命令请求等,然后创建一个mmc_host并添加进系统
  • mmc驱动框架分析2

    千次阅读 2015-06-22 22:52:08
    mmc驱动框架分析1中,分析了sd卡的初始化流程,本文主要分析sd卡的读写过程。先贴一张图,网上网友画的,非常清晰,表示感谢。
  • Linux mmc驱动框架Host驱动设备树Host驱动platform_driver数据结构初始化第一阶段 Host驱动设备树   host驱动设备树用于匹配host驱动,host驱动匹配上设备树,初始化流程才能开始。本文举例全志H3设备树以及mmc...
  • UBOOT——MMC驱动分析

    2017-02-17 11:51:00
    1:MMC驱动初始化是在start_armboot函数中 #if defined(CONFIG_X210) #if defined(CONFIG_GENERIC_MMC) puts ("SD/MMC: "); ///* //lqm masked mmc_exist = mmc_initialize(gd->bd); ...
  • LPC23xx mmc驱动程序, 包含测试程序
  • mmc驱动工作流程

    2012-03-31 16:42:00
    mmc驱动主要文件包括 drivers/mmc/card/block.c drivers/mmc/card/queue.c drivers/mmc/core/core.c drivers/mmc/core/host.c drivers/mmc/core/ 内核启动时,首先执行core/core.c的mmc_init,注册mmc、sd总线...
  • Linux mmc驱动框架SD/MMC/SDIO基础概念的介绍 SD/MMC/SDIO基础概念的介绍   SD/MMC/SDIO基础概念的介绍网上可以找到很多,就不重复造轮子了,推荐一篇我觉得写的比较好的介绍文章。 链接: SD/MMC/SDIO基础概念的...
  • 在linux-2.6.2x中,mmc驱动用到的block_device_operations结构已重新定义,请看: linux-2.6.1x:struct block_device_operations { int (*open) (struct inode *, struct file *); int (*...
  • Linux mmc驱动框架卡检测函数`mmc_rescan``mmc_rescan->mmc_rescan_try_freq`SDIO卡`mmc_rescan->mmc_rescan_try_freq->mmc_attach_sdio`SD卡`mmc_rescan->mmc_rescan_try_freq->mmc_attach_sd`MMC卡...
  • 三星24XX平台SD/MMC驱动移植指南。
  • MMC驱动移植

    2016-06-29 15:43:36
    1.cd drivers/mmc vim s3c6410_sdhci.c 添加下面代码 #include #include #include #include #ifdef CONFIG_MMC_CHANNEL #define MMC_CHANNEL CONFIG_MMC_CHANNEL #else #define MMC_CHANN
  • 基于ARM处理器的嵌入式Linux操作系统MMC驱动的开发与应用,王梓睿,刘元安,嵌入式Linux操作系统是以Linux为基础的嵌入式作业系统,已被广泛应地应用在个人移动电话、超级电视、车载电子产品以及航空航天等领��
  • linux mmc驱动子系统

    千次阅读 2015-12-04 15:22:50
    MMC驱动分析》  最近花时间研究了一下 MMC驱动程序,开始在网上找了很多关于 MMC驱动的分析文章,但大都是在描述各个层,这对于初学者来讲帮助并不大,所以我就打算把自己的理解写下来,希望对大家...
  • 上期通过三星Internal ROM Application Note...本期完成MMC驱动的移植。 1. 代码修改 1.1 修改include/configs/tiny6410.h,增加 /* MMC */ #define CONFIG_GENERIC_MMC 1 #define CONFIG_MMC 1 #define CONFI...
  • linux MMC驱动子系统

    2017-04-06 15:38:41
    MMC驱动分析》  最近花时间研究了一下 MMC驱动程序,开始在网上找了很多关于 MMC驱动的分析文章,但大都是在描述各个层,这对于初学者来讲帮助并不大,所以我就打算把自己的理解写下来,希望对大家...
  • MMC 驱动分析

    千次阅读 2013-06-24 07:39:10
    分析 //driver/mmc/host/davinci_mmc.c static int __init davinci_mmcsd_init... //注册一个平台驱动  return platform_driver_probe(&davinci_mmcsd_driver, davinci_mmcsd_probe);  /* temporary section vi
  • MMC驱动之SD卡

    2017-08-27 10:23:36
    SD卡作为一种块设备,其驱动是挂在gendisk下实现的,对应代码在drivers/mmc/core/block.c 两个主要的函数是mmc_blk_alloc,mmc_add_disk mmc_blk_alloc里调用alloc_disk分配了一个gendisk,调用blk_init_queue分配...
  • 三星平台SD/MMC驱动分析

    千次阅读 2013-11-06 13:02:06
    三星平台SD/MMC驱动主要有两个文件sdhci.c和sdhci-s3c.c,核心驱动在后者里面。 我们一步一步分析,先从平台驱动注册看起 sdhci-s3c.c: static int __init sdhci_s3c_init(void) { return platform_driver_...
  • 一、uboot与linux驱动 1、uboot是裸机程序 狭义的驱动的概念:操作系统中用来具体操控硬件的那部分代码叫驱动。裸机中没有驱动的概念,因为没有操作系统。裸机程序是直接操控硬件的,而操作系统中则是通过驱动来操控...
  • uboot 下MMC驱动

    千次阅读 2017-03-16 08:30:47
    drivers\mmc   static const struct blk_ops mmc_blk_ops = {  .read = mmc_bread, #ifndef CONFIG_SPL_BUILD  .write = mmc_bwrite,  .erase = mmc_berase, #endif  .select_hwpart = mmc_select_hwpart,
  • dw_mmc 驱动 s5p4418安卓5.1系统 u-boot dwmmc驱动导致不能启动系统的解决 1. 条件和要求: 某个客户定制的安卓5.1系统,基于三星的s5p4418 cpu,系统是nexell官方的android 5.1系统(当然我们改写了不少),我在...
  • SD/MMC驱动2(SD检测)

    千次阅读 2010-12-11 02:28:00
    SD/MMC驱动2(SD检测)

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 16,188
精华内容 6,475
关键字:

mmc驱动