03、硬件电路与MMC协议介绍
Linux内核用MMC子系统来管理MMC、SD卡、SDIO等设备。具体来说,这些设备包括:
MMC设备:比如手机里的内置存储芯片
SD设备:就是我们日常用的SD存储卡
SDIO设备:这是基于SD卡技术扩展的接口,主要用于连接外设(比如Wi-Fi或蓝牙模块),不需要设备本身是存储卡
1、硬件电路
MMC系统的行为由三部分共同决定:
卡(Card)
遵循预设的通信规则,负责接收并执行主机控制器的指令(如读写数据),同时处理数据存储或传输。它像一个"响应者",根据规则完成指令任务。总线(Bus)
是连接卡和控制器的物理通道,负责数据和命令的传输。它像"信息高速公路",规定了信号传递方式(如电压高低、时钟同步),确保双方能正确解读彼此发送的信息。控制器(Host Controller)
作为系统发起方,负责发送指令并管理整个流程。它像"指挥官",决定何时读写数据、如何与卡交互,并通过总线协调双方的通信节奏。
硬件连线组成:
CLK信号:主机通过这个时钟信号同步数据传输,并驱动设备正常运行。
CMD信号:这是主机和eMMC之间的命令通道。主机用它发送指令给eMMC,eMMC则用它返回回应结果。
DATA数据线(DAT0-7):这是主机和eMMC之间实际传输数据的通道。刚通电或重启后只能使用DAT0传输数据,完成初始化后,可以设置成同时使用DAT0-3(4位模式)或DAT0-7(8位模式)来拓宽数据通道,提升传输速度。
2、硬件抽象
eMMC规范主要规定了接口功能和设备控制器的标准。简单来说,eMMC系统包括设备外部的控制模块和对应的存储芯片,规范详细说明了接口的使用方法,但不会对设备外部的控制器做具体要求。内部的存储颗粒(Flash)由厂商自行设计,只要保证对外提供的接口符合标准的MMC规格即可。
3、MMC通信协议
MMC设备启动或重启后,需要主控芯片按照规则完成初始化。整个通信过程通过总线传输三类信息,流程如下:
命令
控制器通过CMD信号线向eMMC发送指令,告诉它要执行什么操作(比如读数据或写入数据)。响应
eMMC收到命令后,通过同一条CMD线返回结果反馈,告诉控制器操作是否成功或是否准备好下一步。数据
如果需要传输实际内容(比如读取文件或写入新数据),数据会通过DATA信号线在两者之间双向传输。例如:控制器读数据时,eMMC会把数据通过DATA线发给控制器。
完整流程顺序:
每次操作必须按「发命令→等响应→传数据(如果需要)」的步骤执行。
- 比如写入操作时:
控制器先发「写入」命令 → eMMC回复「已准备好」 → 控制器再发送要写入的数据。
- 而读取操作则是:
控制器先发「读取」命令 → eMMC回复「已就绪」 → eMMC再把存储的数据发回控制器。
3.1、命令和响应
3.1.1、命令 ( command) 格式
传输控制是MMC系统的核心部分。具体过程如下:
3.1.2、发送端流程
命令模块会把需要传输的指令整理成固定格式的数据包
数据包包含:
起始标识位
指令内容位
结束标识位
7位错误校验码(CRC7)
整个数据包总长固定为48位
最终将数据包转换为连续的电信号,通过专用指令线发送出去
3.1.3、接收端流程:
接收设备首先解析收到的电信号,还原成原始数据包
对数据包进行CRC7校验(一种7位错误检测方法)
如果校验结果正确:
将有效指令内容提取出来
存储到专门的指令寄存器中等待执行
如果校验失败则丢弃该数据包
整个过程就像寄送带密码的加密包裹:发送方把包裹封好并加上密码,接收方收到后先验证密码是否正确,确认无误后才会拆开使用里面的内容。
如上表所示,指令在传输过程中总是以“01
”作为起始位,标志指令开始传输;总是以“1
”为结束位,标志该条指令发送完毕。指令[45:40
]bit
为所发送指令的索引值,该值会被 SD
卡控制器进行解释。指令[39:8
]bit
为所发送指令附带的参数,通常不同的指令附带参数的意义不同,需根据指令的索引值进行相应的解释。指令在发送过程中需进行 CRC7
校验,以确认在主控制器和存储器之间指令传输是否正确。
3.1.2、响应 ( response)格式
3.2、数据读写操作
读取数据和写入数据操作允许单块或多块的数据传输。与顺序读操作类似,当进行多块数据传输时是由“停止传输”命令来终止操作的。下图描述了单块读操作和多块读操作。在进行单块数据读操作时,主控制器通过 CMD
信号线发送命令,eMMC
器件接收命令之后会做出响应,在 CMD
信号线上发送至主控制器,在命令和响应的传输中都是包含 CRC7
校验以保证传输的正确性的。同时在数据线 DAT0~DAT7
(根据位宽的设置可以变化)上向主控制器发送数据,每根数据线上的数据都会在最后跟随一个 CRC16
校验保证数据传输的正确性,也为主控制器校验数据提供依据。而在多块数据的读操作中,主控制器发送读数据命令之后eMMC
做出相应,并会连续的发送数据块直到主控制器发送停止读取数据的命令之后 eMMC
器件才不向外发送数据。
读取数据和写入数据操作允许单块或多块的数据传输。与顺序读操作类似,当进行多块数据传输时是由“停止传输”命令来终止操作的。下图描述了单块读操作和多块读操作。在进行单块数据读操作时,主控制器通过 CMD
信号线发送命令,eMMC
器件接收命令之后会做出响应,在CMD
信号线上发送至主控制器,在命令和响应的传输中都是包含 CRC7
校验以保证传输的正确性的。同时在数据线 DAT0~DAT7
(根据位宽的设置可以变化)上向主控制器发送数据,每根数据线上的数据都会在最后跟随一个 CRC16
校验保证数据传输的正确性,也为主控制器校验数据提供依据。而在多块数据的读操作中,主控制器发送读数据命令之后 eMMC
做出相应,并会连续的发送数据块直到主控制器发送停止读取数据的命令之后 eMMC
器件才不向外发送数据。
4、MMC寄存器
文件:kernel/include/linux/mmc/card.h``mmc_card
代表了一张 MMC
卡
/*
* MMC device
*/
struct mmc_card {
struct mmc_host *host; /* 隶属的 MMC 控制器 */
struct device dev; /* 当前设备*/
...
unsigned int rca; /* 卡地址*/
unsigned int type; /* 卡的类型: 0=MMC 1=SD 2=SDIO*/
#define MMC_TYPE_MMC 0 /* MMC card */
#define MMC_TYPE_SD 1 /* SD card */
#define MMC_TYPE_SDIO 2 /* SDIO card */
#define MMC_TYPE_SD_COMBO 3 /* SD combo (IO+mem) card */
...
u32 raw_cid[4]; /* 卡 cid,未解码 */
u32 raw_csd[4]; /* 卡 csd,未解码 */
u32 raw_scr[2]; /* 卡 scr,未解码 */
u32 raw_ssr[16];
struct mmc_cid cid; /* 卡 scr,未解码 */
struct mmc_csd csd; /* 卡的特殊数据,已解码*/
struct mmc_ext_csd ext_csd; /* 卡的特殊数据,已解码*/
...
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
4.1、mmc_cid
CID寄存器(器件识别寄存器)是eMMC存储芯片中的一个128位信息存储单元。这个寄存器按照eMMC通信协议设计,记录了该芯片的唯一身份标识信息。每个eMMC芯片都有独立的唯一识别码,同时不同型号的eMMC芯片还会有各自的类型标识码。相关定义位于Linux内核源码的kernel/include/linux/mmc/card.h文件中。
struct mmc_cid {
unsigned int manfid; /* 制造商 id*/
char prod_name[8]; /* 产品名*/
unsigned char prv;
unsigned int serial; /* 产品序列号*/
unsigned short oemid; /* 产品 OID*/
unsigned short year;
unsigned char hwrev;
unsigned char fwrev;
unsigned char month;
};
2
3
4
5
6
7
8
9
10
11
4.2、mmc_csd
CSD寄存器(设备特征数据寄存器)存储了设备操作所需的关键信息。它具体说明了:
数据存储格式
错误检查方法
最大允许读写时间
数据传输速率
是否支持DSR寄存器
这些信息帮助系统正确操作存储设备,具体定义可以在Linux内核的mmc/card.h文件中找到。
struct mmc_csd {
unsigned char structure;
unsigned char mmca_vsn; /* 产品 OID*/
unsigned short cmdclass; /* card 支持的命令子集*/
unsigned short taac_clks; /*用于计算读命令的末位和所读数据的首位间的最大时值 Nac*/
unsigned int taac_ns; /*用于计算读命令的末位和所读数据的首位间的最大时值 Nac*/
unsigned int c_size;
unsigned int r2w_factor;
unsigned int max_dtr; /*最大传输速率*/
unsigned int erase_size;
unsigned int read_blkbits; /*最大的读数据块长度*/
unsigned int write_blkbits; /*最大的写数据块长度*/
unsigned int capacity; /*卡的存储容量*/
unsigned int read_partial:1, /*读小块尺寸数据(最小 1byte)是否允许, SD 卡恒为 1*/
read_misalign:1,
write_partial:1,
write_misalign:1,
dsr_imp:1;
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
4.3、mmc_csr
文件目录: kernel/include/linux/mmc/mmc.h
struct sd_scr {
unsigned char sda_vsn;
unsigned char sda_spec3;
unsigned char bus_widths; /*数据线的宽度*/
#define SD_SCR_BUS_WIDTH_1 (1<<0)
#define SD_SCR_BUS_WIDTH_4 (1<<2)
unsigned char cmds;
#define SD_SCR_CMD20_SUPPORT (1<<0)
#define SD_SCR_CMD23_SUPPORT (1<<1)
};
2
3
4
5
6
7
8
9
10
5、MMC控制命令
SD/MMC存储卡的命令格式存在差异,因此模块不通过硬件直接解析命令类型,而是由软件来指定具体命令。例如:
单次数据读取(CMD17)和写入(CMD24)
连续数据读取(CMD18)和写入(CMD25)
控制器会根据寄存器SD_CMD_INDEX
中存储的命令编号,发送对应的指令并自动调整工作状态。相关代码位于kernel/include/linux/mmc/mmc.h
文件中。
(说明:将技术术语简化为"单次数据"和"连续数据",用"调整工作状态"替代"状态转换",通过分点列出具体命令类型使逻辑更清晰,同时保留关键寄存器名称和文件路径作为技术参考)
文件目录:kernel\drivers\mmc\host\dw_mmc.h
enum dw_mci_state {
STATE_IDLE = 0,
STATE_SENDING_CMD,
STATE_SENDING_DATA,
STATE_DATA_BUSY,
STATE_SENDING_STOP,
STATE_DATA_ERROR,
STATE_SENDING_CMD11,
STATE_WAITING_CMD11_DONE,
};
2
3
4
5
6
7
8
9
10
core/mmc_ops.c中完成cmd的发送
在识别和初始化 SD 卡的过程中,主机控制器会按顺序发送一系列命令(CMD)来配置和准备卡以进行数据传输。以下是典型的 SD 卡识别过程中使用的命令:
6、SD卡枚举 CMD的过程
- CMD0 (GO_IDLE_STATE):
将卡复位到空闲状态。这是识别过程的第一步,用于复位卡的状态机。
- CMD8 (SEND_IF_COND):
检查卡是否支持电压范围,并确认卡是 SD 2.0 及以上版本。主机发送电压和检查模式信息,卡返回相同信息以确认。
- CMD55 (APP_CMD) + ACMD41 (SD_SEND_OP_COND):
组合使用以初始化卡并获取其操作条件。
- CMD55 通知卡下一个命令是应用特定命令。
- CMD41 向卡发送操作条件寄存器(OCR)信息,并请求卡进入就绪状态。主机会循环发送这组命令,直到卡准备就绪。
- CMD2 (ALL_SEND_CID):
请求卡发送其唯一的卡标识符(CID)。所有卡都会回应此命令。
- CMD3 (SEND_RELATIVE_ADDR):
分配一个相对地址(RCA)给卡。卡使用这个地址进行后续的命令通信。
- CMD9 (SEND_CSD):
请求卡发送其卡特定数据(CSD),以获取卡的特性信息,如最大传输速度、块长度等。
- CMD7 (SELECT/DESELECT_CARD):
选择卡以进入传输状态,准备进行数据传输。