Linux MMC原理及框架详解

目录

1. 背景

1.1基本概念

1.2 规范简介

1.2.1 卡的规范

1.2.2 总线规范

2. eMMC原理介绍

2.1 eMMC内部结构简介

2.1.2 Flash Controller

2.1.3 Host Interface

2.2分区管理

2.3总线协议

3. Linux MMC软件框架流程

3.1软件架构

3.2工作流程

参考文档


本文参考资料主要来源于网络,原文网址会在文末附上。整理本文的原因,一是为了自己学习记录;二也是为了分享出来帮助别人。如有打扰,尽请谅解。

1. 背景

1.1基本概念

MMC是MultiMediaCard的简称,从本质上看,它是一种用于固态非易失性存储的内存卡(memory card)规范,定义了诸如卡的形态、尺寸、容量、电气信号、和主机之间的通信协议等方方面面的内容。

从1997年MMC规范发布至今,基于不同的考量(物理尺寸、电压范围、管脚数量、最大容量、数据位宽、clock频率、安全特性、是否支持SPI mode、是否支持DDR mode、等等),进化出了MMC、SD、microSD、SDIO、eMMC等不同的规范(如下面图片1所示)。虽然乱花迷人,其本质终究还是一样的,丝毫未变,这就是Linux kernel将它们统称为MMC的原因。

图1.MMC/SD/SDIO演进图

 

关于该图片,这里强调几点(其它的,大家可参考[1][2],不再详细介绍):

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

1.2 规范简介

MMC分别从卡(Card Concept)、总线(Bus Concept)以及控制器(Host Controller)三个方面,定义MMC system的行为,如下所示:

图2.MMC/SD/SDIO硬件框图

1.2.1 卡的规范

卡的规范主要规定卡的形状、物理尺寸、管脚,内部block组成、寄存器等等,以eMMC为例[3]:

图3.Card Concept(eMMC)

1)有关形状、尺寸的内容,这里不再介绍,感兴趣的同学可参考[1]。

2)卡的内部由如下几个block组成:

  • Memory core,存储介质,一般是NAND flash、NOR flash等;
  • Memory core interface,管理存储介质的接口,用于访问(读、写、擦除等操作)存储介质;
  • Card interface(CMD、CLK、DATA),总线接口,外界访问卡内部存储介质的接口,和具体的管脚相连;
  • Card interface controller,将总线接口上的协议转换为Memory core interface的形式,用于访问内部存储介质;
  • Power模块,提供reset、上电检测等功能;
  • 寄存器(图片3中位于Card interface controller的左侧,那些小矩形),用于提供卡的信息、参数、访问控制等功能。

3)卡的管脚有VDD、GND、RST、CLK、CMD和DATA等,VDD和GND提供power,RST用于复位,CLK、CMD和DATA为MMC总线协议(具体可参考下一小节)的物理通道:

  • CLK有一条,提供同步时钟,可以在CLK的上升沿(或者下降沿,或者上升沿和下降沿)采集数据;
  • CMD有一条,用于传输命令(CMD)及响应(Response)。
  • DATA用于传输双向的数据,根据MMC的类型,可以有一条(1-bit)、四条(4-bit)或者八条(8-bit)。

4)以eMMC为例,规范定义了OCR, CID, CSD, EXT_CSD, RCA 以及DSR 6组寄存器,具体含义请参考手册[3]。

1.2.2 总线规范

前面我们提到过,MMC的本质是提供一套可以访问固态非易失性存储介质的通信协议,从产业化的角度看,这些存储介质一般集成在一个独立的外部模块中(卡、WIFI模组等),通过物理总线和CPU连接。对任何有线的通信协议来说,总线规范都是非常重要的。关于MMC总线规范,简单总结如下:

1)物理信号有CLK、CMD和DATA三类。

2)电压范围为1.65V和3.6V(参考上面图片2),根据工作电压的不同,MMC卡可以分为两类:

  • High Voltage MultiMediaCard,工作电压为2.7V~3.6V。
  • Dual Voltage MultiMediaCard,工作电压有两种,1.70V~1.95V和2.7V~3.6V,CPU可以根据需要切换。

3)数据传输的位宽(称作data bus width mode)是允许动态配置的,包括1-bit (默认)模式、4-bit模式和8-bit模式。

注:不使用的数据线,需要保持上拉状态,这就是图片2中的DATA中标出上拉的原因。另外,由于数据线宽度是动态可配的,这要求CPU可以动态的enable/disable数据线的那些上拉电阻

4)MMC规范定义了CLK的频率范围,包括0-20MHz、0-26MHz、0-52MHz等几种,结合数据线宽度,基本决定了MMC的访问速度。

5)总线规范定义了一种简单的、主从式的总线协议,MMC卡为从机(slave),CPU为主机(Host)。

6)协议规定了三种可以在总线上传输的信标(token):

  • Command,Host通过CMD线发送给Slave的,用于启动(或结束)一个操作(后面介绍);
  • Response,Slave通过CMD线发送给Host,用于回应Host发送的Command;
  • Data,Host和Slave之间通过数据线传输的数据。方向可以是Host到Slave,也可以是Slave到Host。数据线的根数可以是1、4或者8。在每一个时钟周期,每根数据线上可以传输1bit或者2bits的数据。

7)一次数据传输过程,需要涉及所有的3个信标。一次数据传输的过程也称作Bus Operation,根据场景的不同,MMC协议规定了很多类型的Bus Operation(具体可参考相应的规范)。

2. eMMC原理介绍

2.1 eMMC内部结构简介

eMMC 是 embedded MultiMediaCard 的简称。MultiMediaCard,即 MMC, 是一种闪存卡(Flash Memory Card)标准,它定义了 MMC 的架构以及访问 Flash Memory 的接口和协议。而 eMMC 则是对 MMC 的一个拓展,以满足更高标准的性能、成本、体积、稳定、易用等的需求。

eMMC 的整体架构如下图片所示:

图4. eMMC 整体框图

eMMC 内部主要可以分为 Flash Memory、Flash Controller 以及 Host Interface 三大部分。

2.1.1 Flash Memory

Flash Memory 是一种非易失性的存储器,通常在嵌入式系统中用于存放系统、应用和数据等,类似与 PC 系统中的硬盘。

目前,绝大部分手机和平板等移动设备中所使用的 eMMC 内部的 Flash Memory 都属于 NAND Flash。eMMC 标准中,将内部的 Flash Memory 划分为 4 类区域,最多可以支持 8 个硬件分区,如下图所示:

图5.eMMC分区图

  • BOOT Area Partition 1 & 2
    此分区主要是为了支持从 eMMC 启动系统而设计的。
    该分区的数据,在 eMMC 上电后,可以通过很简单的协议就可以读取出来。同时,大部分的 SOC 都可以通过 GPIO 或者 FUSE 的配置,让 ROM 代码在上电后,将 eMMC BOOT 分区的内容加载到 SOC 内部的 SRAM 中执行。
  • RPMB Partition
    RPMB 是 Replay Protected Memory Block 的简称,它通过 HMAC SHA-256 和 Write Counter 来保证保存在 RPMB 内部的数据不被非法篡改。
    在实际应用中,RPMB 分区通常用来保存安全相关的数据,例如指纹数据、安全支付相关的密钥等。
  • General Purpose Partition 1~4
    此区域则主要用于存储系统或者用户数据。 General Purpose Partition 在芯片出厂时,通常是不存在的,需要主动进行配置后,才会存在。
  • User Data Area
    此区域则主要用于存储系统和用户数据。
    User Data Area 通常会进行再分区,例如 Android 系统中,通常在此区域分出 boot、system、userdata 等分区。

2.1.2 Flash Controller

NAND Flash 直接接入 Host 时,Host 端通常需要有 NAND Flash Translation Layer,即 NFTL 或者 NAND Flash 文件系统来做坏块管理、ECC等的功能。

eMMC 则在其内部集成了 Flash Controller,用于完成擦写均衡、坏块管理、ECC校验等功能。相比于直接将 NAND Flash 接入到 Host 端,eMMC 屏蔽了 NAND Flash 的物理特性,可以减少 Host 端软件的复杂度,让 Host 端专注于上层业务,省去对 NAND Flash 进行特殊的处理。同时,eMMC 通过使用 Cache、Memory Array 等技术,在读写性能上也比 NAND Flash 要好很多。

图6. Flash Controller图

2.1.3 Host Interface

eMMC 与 Host 之间的连接如下图所示:

图7. eMMC Interface

 

各个信号的用途如下所示:

  • CLK
    用于同步的时钟信号
  • Data Strobe
    此信号是从 Device 端输出的时钟信号,频率和 CLK 信号相同,用于同步从 Device 端输出的数据。该信号在 eMMC 5.0 中引入。
  • CMD
    此信号用于传送 Host 的 command 和 Device 的 response。
  • DAT0-7
    用于传输数据的 8 bit 总线。

Host 与 eMMC 之间的通信都是 Host 以一个 Command 开始发起的。针对不同的 Command,Device 会做出不同的响应。详细的通信协议相关内容,请参考 2.3总线协议 章节。

2.2分区管理

参考“eMMC分区管理”一文。

2.3总线协议

参考“eMMC总线协议简析”一文。

3. Linux MMC软件框架流程

3.1软件架构

Linux kernel的驱动框架有两个要点:

1)抽象硬件(硬件架构是什么样子,驱动框架就应该是什么样子)。

2)向“客户”提供使用该硬件的API(之前我们提到最多的客户是“用户空间的Application”,不过也有其它“客户”,例如内核空间的其它driver、其它framework)。

MMC framework的软件架构如下图8所示:

图8.Linux MMC软件框架图

MMC framework分别有“从左到右”和“从下到上”两种层次结构。

1) 从左到右

MMC协议是一个总线协议,因此包括Host controller、Bus、Card三类实体(从左到右)。相应的,MMC framework抽象出了host、bus、card三个软件实体,以便和硬件一一对应:

  • host,负责驱动Host controller,提供诸如访问card的寄存器、检测card的插拔、读写card等操作方法。从设备模型的角度看,host会检测卡的插入,并向bus注册MMC card设备;
  • bus,是MMC bus的虚拟抽象,以标准设备模型的方式,收纳MMC card(device)以及对应的MMC driver(driver);
  • card,抽象具体的MMC卡,由对应的MMC driver驱动(从这个角度看,可以忽略MMC的技术细节,只需关心一个个具有特定功能的卡设备,如存储卡、WIFI卡、GPS卡等等)。

2)从下到上

MMC framework从下到上也有3个层次(老生常谈了):

MMC core位于中间,是MMC framework的核心实现,负责抽象host、bus、card等软件实体,负责向底层提供统一、便利的编写Host controller driver的API;

MMC host controller driver位于底层,基于MMC core提供的框架,驱动具体的硬件(MMC controller);

MMC card driver位于最上面,负责驱动MMC core抽象出来的虚拟的card设备,并对接内核其它的framework(例如块设备、TTY、wireless等),实现具体的功能。

3.2工作流程

基于上图8的软件架构,Linux MMC framework的初始化流程如下图9:

图9. MMC初始化流程图

其实,互联网上对Linux mmc软件框架的介绍已经非常多。本小节主要再以mmc的rescan初始化流程及request流程进行说明。参考Linux版本为4.17,mmc host驱动为瑞萨的rcar-M3。Block层的相关处理流程及驱动的具体实现流程本小节不涉及。

如下图16所示,在瑞萨mmc驱动在执行probe流程时,首先会通过mmc_alloc_host函数分配一个struct mmc_host结构体,并初始化相关成员。同时,还会初始化一个卡检测任务——INIT_DELAYED_WORK(&host->detect, mmc_rescan);当有mmc卡插入时,会触发mmc_rescan流程。另外如下图10右面部分,当mmc的probe驱动初始化完成,调用mmc_add_host流程中也会触发mmc_rescan流程,下面我们就来详细分析下这个流程(忽略部分次要步骤)。

  1. mmc_rescan首先会用一些比较低的频率来尝试扫描mmc设备,尝试的频率值分别为400KHz、300KHz、200KHz和100KHz
  2. 用每个尝试的频率值(如果一个尝试成功则不进行后续更低频率的尝试)通过mmc_rescan_try_freq函数来具体执行rescan流程
  3. 在mmc_rescan_try_freq函数中会依次调用host驱动接口执行POWER_UP、hw_reset以及发送CMD0命令让卡进入idle状态
  4. 接下来会依次尝试attach SDIO、SD和MMC三种子协议的卡,本小节以MMC子协议为例讲解具体的attach流程
  5. 发送CMD1命令,获取OCR寄存器值。通过mmc_select_voltage函数设置OCR中引脚电平为3.3V
  6. 通过mmc_init_card函数执行mmc卡的初始化流程,如下
  7. 再次发送CMD0命令,保证前面OCR修改后卡的状态再次回到idle态
  8. 再次发送CMD1命令,设置卡的sector模式(>2G的高容量模式,默认是byte模式)
  9. 发送CMD2命令,获取CID寄存器值
  10. 通过CMD3命令设置RCA(relative card address),如果MMC总线上只有一张卡则是0001
  11. 通过CMD9命令获取CSD寄存器值
  12. 解析CSD值,获取获取容量、版本等信息
  13. 解析CID值,获取卡的序列号、年份信息、oemid等
  14. 发送CMD7命令,选中该卡,准备进行升频模式操作
  15. 通过CMD8命令,读取ext CSD寄存器值,获取更多卡支持的模式、时序等信息
  16. mmc_switch,通过CMD6/CMD13命令使能ERASE_GRP_DEF、power_off_notification等
  17. mmc_select_timing则开始选择并设置高速timing interface,具体操作有如下18~21步
  18. mmc_set_signal_voltage,高速模式首先设置信号电平为1.2V(如果支持)或者1.8Vgao
  19. mmc_select_bus_width,通过CMD6/CMD13设置总线位宽为8bit或4bit
  20. __mmc_switch,CMD6命令切换到HS200模式,并设置时钟频率为200MHz
  21. __mmc_switch_status,CMD13获取卡设置状态信息
  22. 接下来,如果HS200模式切换成功,则进行tuning操作(这里会根据mmc host硬件寄存器值进行多次tuning,根据结果择优选择采样点)
  23. 上面tuning成功则会尝试切换到更高速的HS400模式,具体操作有如下24~29步
  24. __mmc_switch,CMD6命令切换到普通HS模式,并通过mmc_set_clock将时钟频率降低到52MHz
  25. mmc_switch_status,发送CMD13获取卡切换状态信息
  26. mmc_switch,CMD6/CMD13切换至DDR模式
  27. 通过CMD6切换至HS400模式
  28. mmc_set_timing,设置时钟频率回200MHz
  29. mmc_switch_status,通过CMD13命令获取模式设置状态信息
  30. 最后通过mmc_select_powerclass选择合适的power class,完成MMC子协议的attach流程
  31. 完成attach之后通过mmc_add_card函数向mmc bus添加卡设备,触发block层的probe、分区管理以及文件系统初始化等后续工作

上述流程为了方便主线的描述,忽略了一些特殊场景的处理及特性的支持,大家可以结合具体代码和下图来进行理解。

图10. Linux MMC协议框架probe流程图

Request流程其实相对简单,block层的bio转化后封装成request,层层调用传递到mmc core层的mmc_start_request,并最终调用host驱动程序注册的request方法执行硬件操作。这中间的流程在linux的不同版本之间变动非常大,不过万变不离其中,大的流程框架还是没有变。

图11. request调用流程图

参考文档

[1] https://en.wikipedia.org/wiki/MultiMediaCard

[2] https://en.wikipedia.org/wiki/Secure_Digital

[3] eMMC spec:http://www.jedec.org/standards-documents/results/jesd84-b51(注册后可免费下载)。

[4] http://www.wowotech.net/basic_tech/mmc_sd_sdio_intro.html

[5] http://www.wowotech.net/basic_tech/emmc_intro.html

[6] http://www.wowotech.net/basic_tech/emmc_partitions.html

[7] http://www.wowotech.net/basic_tech/emmc_bus_protocol.html

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章