当前位置:   article > 正文

mmc子系统框架_linux内核mmc 流程

linux内核mmc 流程

Linux kernel把mmc,sd以及sdio三者的驱动代码整合在一起,俗称mmc子系统。源码位于drivers/mmc下。其下有三个子目录,分别是:card、core、host,其中,card用于构建一个块设备作为上层与mmc子系统沟通的桥梁;core抽象了mmc,sd,sdio三者的通用操作;host则是各类平台上的host驱动代码,包括如TI Omap的omap_hsmmc,三星的s3cmci等。具体的SOC,会根据实际情况再次封装(比如rockchip采用新思designwave的IP),就重新封装为struct dw_mci。DesignWare是SoC/ASIC设计者最钟爱的设计IP库和验证IP库。它包括一个独立于工艺的、经验证的、可综合的虚拟微架构的元件集合,包括逻辑、算术、存储和专用元件系列,超过140个模块。

一、Mmc子系统涉及总线(SD为例)

1、Host驱动相应的driver和device挂载在Linux内核内置的虚拟抽象总线platform_bus_type。设备树指定compatible属性与源码匹配后调用对应的probe函数来初始化对应平台的host硬件。

2、Card驱动相应的driver和device挂载在mmc自己创建的虚拟总线mmc_bus_type下。该总线的结构体定义在driver/mmc/core/bus.c;因为是统一的协议,mmc_driver在内核已经写好并已注册driver/mmc/card/block.c-->mmc_blk_init()-->mmc_register_driver(&mmc_driver); SD卡中extended CSD寄存器中存储分区信息,上电检测到了SD卡,或者上电之后的热拔插操作,触发mmc_rescan查找检测到SD卡插入后,会通过读取该寄存器的值,获取mmc card的分区信息等,然后注册对应的mmc_card device,因mmc driver仅有一个,因此针对mmc bus而言其match函数直接返回1,表示匹配成功(因为是统一的协议),而不需要像spi/i2c总线的match接口那样对设备和驱动进行匹配检测;随后调用mmc_driver的probe函数(拔出卡则反之操作)。

3、mmc_driver的probe函数针对该mmc card的每一个分区,均执行如下操作:
①、为每一个分区,创建对应的通用磁盘设备gendisk;
②、设置gendisk的fops为mmc_bdops;
③、为每一个通用磁盘设备,均创建request_queue及其处理方法;
④、为该通用磁盘设备创建mmc block块i/o请求机制;
⑤、将该分区的通用磁盘设备注册至块设备i/o,以便应用层可通过块设备访问该mmc card 分区。

设备-总线-驱动模型:

在分析MMC子系统的设备-总线-驱动模型时,可以借助已经分析的i2c驱动模型、spi驱动模型的实现,来学习mmc子系统的驱动模型,通过与i2c驱动模型、spi驱动模型的对比,也可以加深我们对mmc子系统驱动模型的理解(从已知去学习未知,可提供我们的学习效率)。我们知道针对复杂的设备,则需要借助控制器进行通信,而针对i2c、spi、mmc而言,均需要对应的控制器实现与该类设备的通信。而针对这三类控制器,其模块都进行了抽象,其中i2c控制器抽象为i2c adapter、spi控制器抽象为spi master,而针对mmc控制器,则抽象为mmc host;而针对这三种类型设备的抽象,i2c设备抽象为i2c client、spi设备抽象为spi device、mmc device抽象为mmc card。 

下面分析下这三类驱动模型的异同:

1、均为控制器创建了对应的class,用于将对应的控制器设备链接至对应的class上

2、针对i2c adapter、spi master这两类而言,其驱动模型中均创建了对应的链表,将所有注册的控制器设备链接在一起,而mmc子系统并没有为mmc host创建类似的链表;

3、I2c/spi设备不属于热插拔设备,因此在具体系统的板级文件或者设备树中,需要针对系统中存在的i2c/spi设备定义注册信息,从而在LINUX系统初始化时完成i2c/spi设备的注册;而针对mmc子系统其属于热插拔的,因此在系统初始化时不需要单独进行设备的注册;

4、针对mmc子系统而言,针对mmc card的注册,mmc子系统提供了rescan接口,而该接口作为延迟工作队列的回调函数。针对mmc card而发起rescan扫描的时机如下:

①在mmc host添加的时候,执行一次mmc card的扫描操作

②热拔插过程执行扫描

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

SOC带卡检测引脚,即mmc控制器部分带专门的中断寄存器位(MCI中断)

②SOC不带卡检测引脚,即用普通中断引脚做检测(即sdmmc_cd引脚)

③没有专门的寄存器,也没有用cd的gpio中断,采用轮询POLL

 二、分析mmc子系统框架函数调用流程(以SD卡为例)

  1. 关于SD卡的HOST最初始设备树节点在rk3399.dtsi
  2. sdmmc: dwmmc@fe320000 {
  3. compatible = "rockchip,rk3399-dw-mshc",
  4. "rockchip,rk3288-dw-mshc";
  5. ............................
  6. }

Host驱动相应的driver和device挂载在Linux内核内置的虚拟抽象总线platform_bus_type。我们设备树host device与driver匹配后调用对应的probe函数来初始化对应平台的host硬件。 

  1. drivers\mmc\host\dw_mmc-rockchip.c
  2. dw_mci_rockchip_probe(struct platform_device *pdev)
  3. --> dw_mci_pltfm_register(pdev, drv_data); //解析设备树信息,用来构造一个dw_mci *host;
  4. --> dw_mci_probe(dw_mci *host); //将上面构造好的dw_mci *host结构体传参进来;
  5. --> setup_timer(&host->cmd11_timer,dw_mci_cmd11_timer, (unsigned long)host); //设置一些定时器,定时发送一些命令
  6. --> host->dma_ops = host->pdata->dma_ops;
  7. --> ret = devm_request_irq(host->dev, host->irq, dw_mci_interrupt, host->irq_flags, "dw-mci", host);
  8. //这个dw_mci_interrupt中断函数会被注册两次,一次是emmc(irq=25),一次是sd卡(irq=26),所以源码虽然只注册了一次,但是实际上注册了两次,用的都是同一个中断处理函数
  9. //这个是mci寄存器的中断,我们的这款soc可以根据这个寄存器来判断发送,接收,SD卡热拔插的行为,都会触发这个中断,在这个中断处理函数里面再细分是什么操作触发的中断,进行处理,比如热拔插:
  10. --> dw_mci_init_slot(dw_mci *host, i); // 定义了mmc_host *mmc;,用我们封装了一层的dw_mci *host去填充注册他
  11. --> mmc = mmc_alloc_host(sizeof(struct dw_mci_slot), host->dev);
  12. --> INIT_DELAYED_WORK(&host->detect, mmc_rescan); //初始化工作队列,里面有扫描SD卡的函数
  13. --> mmc->ops = &dw_mci_ops; //mmc_host_ops类型的变量,用于定义本mmc_host的操作接口(包括与mmc card通信的接口request、卡检测相关的接口等)
  14. --> mmc_add_host(mmc);
  15. --> mmc_start_host(mmc_host *host);
  16. --> mmc_schedule_delayed_work(&host->detect, delay); //上电添加mmc_host的时候,先主动执行一次mmc_rescan函数
  17. --> dw_mci_enable_cd(host); //cd = card detect ≠ sdmmc_cd 引脚中断,主要看函数内容,像这款soc就是在里面开启mci寄存器的SD卡热拔插中断
  18. --> temp = mci_readl(host, INTMASK); temp |= SDMMC_INT_CD; mci_writel(host, INTMASK, temp); //开启mci寄存器的SD卡热拔插中断

假设热拔插SD卡引起了中断: 

  1. dw_mci_interrupt()
  2. if (pending & SDMMC_INT_CD)
  3. dw_mci_handle_cd(dw_mci *host);
  4. --> mmc_detect_change(slot->mmc,msecs_to_jiffies(host->pdata->detect_delay_ms));
  5. --> _mmc_detect_change(host, delay, true);
  6. --> mmc_schedule_delayed_work(&host->detect, delay); //开启工作队列
  7. --> mmc_rescan() //最终会调用此函数
  8. 重点看看mmc_rescan检测SD卡热拔插的函数,我们在设备树中的SD卡定义了 supports-sd 所以:
  9. drivers\mmc\core\host.c
  10. mmc_of_parse
  11. if (of_property_read_bool(np, "supports-sd"))
  12. host->restrict_caps |= RESTRICT_CARD_TYPE_SD;
  13. mmc_rescan()
  14. if (host->bus_ops && !host->bus_dead && !(host->caps & MMC_CAP_NONREMOVABLE)) //上电第一次调用这个函数时候,还没有设置host->bus_ops,所以会执行后面的语句来设置host->bus_ops
  15. host->bus_ops->detect(host);
  16. --> mmc_rescan_try_freq(host, max(freqs[i], host->f_min)) //以各种频率给host发,选择最合适的频率
  17. if ((host->restrict_caps & RESTRICT_CARD_TYPE_SD) && !mmc_attach_sd(host)) return 0; 所以就会调用mmc_attach_sd
  18. --> mmc_attach_sd(host)
  19. --> mmc_attach_bus(host, &mmc_sd_ops);
  20. --> host->bus_ops = ops; //这里的ops就是上面的mmc_ops参数
  21. --> mmc_sd_init_card(host, rocr, NULL);
  22. --> card = mmc_alloc_card(host, &sd_type);
  23. --> mmc_add_card(host->card);
  24. --> device_add(&card->dev);
  25. --> bus_add_device(dev); //Add the device to its bus's list of devices, 会给mmc_bus注册一个devices,会和系统自动注册的mmc_driver匹配,调用它的probe函数
  26. ......
  27. --> mmc_driver->probe
  28. --> mmc_blk_probe() //如上分析的,会读取SD卡的寄存器获取分区信息,根据分区信息注册block等。

放一张图中好看些:

三、mmc子系统的数据结构 (以emmc的为例)

mmc_host数据结构:

Mmc card: 

①host完成mmc_card与mmc_host的绑定;
②dev完成将该mmc_card与系统中的设备、总线、驱动的关联(即完成与系统设备驱动总线的关联与绑定操作);
③卡类型与卡状态记录
④卡相关的寄存器信息记录(主要从mmc card中读取),包括cid、csd、scr、ssr等内容
⑤Mmc card的分区信息,主要完成块设备的创建(由mmc driver实现)若为sdio设备,则有cccr、cis等(关于sdio的部分,请参考sdio协议相关的文档)

 mmc_ios:

该结构体主要定义mmc总线相关的参数,主要包括时钟频率、电源、总线模式、电源状态、总线带宽、支持的信号电压值等等这些参数的设置可通过mmc_host_ops->set_ios实现。

mmc_host_ops:

该数据结构定义了mmc_host的操作方法

mmc_bus_ops:

该接口主要是针对mmc card的sleep/awake的操作,因mmc card也需要进行sleep/awake等接口,这些接口类似于设备驱动的电源管理(suspend/resume)相关的接口。该结构体中的成员,不需要我们进行实现,mmc子系统已经完成,mmc子系统根据mmc各协议版本针对sleep/awake的支持情况,实现对应的操作。

 参考这位博主:LINUX MMC子系统分析之一 概述_jerry_chg的博客-CSDN博客_linux mmc子系统

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/煮酒与君饮/article/detail/896124
推荐阅读
相关标签
  

闽ICP备14008679号