ARM架构MMC块设备驱动参数配置与性能调优工程 - 第1节
1. MMC子系统概述
MMC(MultiMediaCard)协议自 1997 年由西门子和 SanDisk 推出以来,经历了从机械卡到嵌入式存储芯片的完整演进。理解协议代际差异是配置驱动参数和调优性能的前提。
| 协议版本 | 推出年份 | 总线宽度 | 最高时钟频率 | 理论峰值速率 | 关键特性 |
|---|---|---|---|---|---|
| MMC 3.x | 1997 | 1-bit | 20 MHz | 2.5 MB/s | SPI 模式、串行通信 |
| MMC 4.x | 2002 | 1/4/8-bit | 52 MHz | 52 MB/s | 多块传输、分区、Boot 分区 |
| SD 1.0 / 2.0 | 2001 | 1/4-bit | 25 / 50 MHz | 12.5 / 25 MB/s | CPRM 版权保护、SDHC |
| SD 3.0 (UHS-I) | 2010 | 4-bit | 208 MHz (DDR) | 104 MB/s | SDR104/DDR50 模式 |
| eMMC 4.4 / 4.5 | 2009 | 1/4/8-bit | 52 / 200 MHz (HS200) | 200 MB/s | HS200、Cache、Reliable Write |
| eMMC 5.0 / 5.1 | 2013 | 1/4/8-bit | 200 MHz (HS400) | 400 MB/s | HS400、Command Queue、FFU |
| UFS 2.x / 3.x | 2014 | 全双工差分 | 2.9 / 5.8 Gbps/lane | 1.45 / 2.9 GB/s | SCSI 命令集、多队列 |
调优要点: eMMC 5.0 引入的 HS400 模式需要双沿采样和 CRC 校验,ARM 平台的 MMC 控制器必须正确配置 DLL 延迟线,否则高频下会出现 CRC 错误。这是嵌入式调优中最常见的硬件-软件协同问题。
1.2 Linux MMC 子系统架构
Linux MMC 子系统采用分层设计,从上到下依次为:块设备层、MMC 核心层、MMC 主机控制器驱动层。每一层都定义了清晰的接口,使得驱动开发与协议细节解耦。
1.2.1 三层架构职责
| 层次 | 内核路径 | 核心职责 | 关键文件 |
|---|---|---|---|
| 块设备层 | block/ |
提供通用块 I/O 请求队列、I/O 调度、bio 处理 | block/blk-core.c, block/elevator.c |
| MMC 核心层 | drivers/mmc/core/ |
协议状态机、命令/数据传输、卡识别、电源管理 | mmc.c, sd.c, mmc_ops.c |
| 主机控制器层 | drivers/mmc/host/ |
硬件寄存器操作、DMA 配置、中断处理、时钟调优 | sdhci.c, dw_mmc.c, omap_hsmmc.c |
1.2.2 核心层关键组件
MMC 核心层是子系统的中枢,负责管理卡的生命周期和协议交互。以下为核心层的主要模块:
- mmc_alloc_card / mmc_add_card:卡识别阶段,通过 CMD2/CMD3 获取 CID 并分配卡结构体。
- mmc_init_card:卡初始化,包括电压匹配、总线宽度协商、高速模式使能。
- mmc_set_ios:设置 I/O 参数(时钟、总线宽度、电压、驱动强度),是调优的核心入口。
- mmc_start_request:将块设备层的请求转换为 MMC 命令序列,支持单块/多块/预擦除。
- mmc_wait_for_req:同步等待命令完成,处理超时和 CRC 错误重试。
关键数据结构:
struct mmc_card 是核心层最重要的结构体,包含卡类型、时序模式、最大频率、块长度、分区信息等。驱动调优时,通过 mmc_card->ext_csd 访问 eMMC 扩展寄存器,获取 HS400 支持、Cache 大小、擦除分组等信息。
1.3 块设备层与 MMC 核心层的关系
块设备层与 MMC 核心层通过 请求队列 (request queue) 和 块设备操作函数 (block_device_operations) 进行交互。MMC 核心层向块设备层注册为一个块设备,并实现 mmc_blk_ops 回调。
1.3.1 交互流程
- bio 提交:文件系统通过
submit_bio将 I/O 请求提交到块设备层的请求队列。 - I/O 调度:块设备层的 I/O 调度器(如 CFQ、deadline、mq-deadline)对 bio 进行合并和排序。
- 请求分发:MMC 块设备驱动 (
mmc_block.c) 从请求队列中取出请求,调用mmc_blk_issue_rq。 - 命令组装:核心层将请求转换为 MMC 命令(如 CMD18/CMD25 多块读写),填充
struct mmc_request。 - 硬件执行:主机控制器驱动通过 DMA 或 PIO 执行数据传输,完成后触发中断。
- 完成回调:核心层调用
mmc_blk_rw_rq_prep中的完成函数,通知块设备层 I/O 结束。
1.3.2 关键数据结构关系
块设备层与 MMC 核心层之间的数据传递通过以下核心结构体完成:
| 结构体 | 所属层次 | 作用 | 关键字段 |
|---|---|---|---|
struct request |
块设备层 | 表示一个 I/O 请求,包含 bio 链表、扇区号、方向 | cmd_flags, __sector, bio |
struct mmc_blk_request |
MMC 块驱动 | 将块请求转换为 MMC 命令的中间结构 | mrq, cmd, data, stop |
struct mmc_request |
MMC 核心层 | 核心层使用的命令/数据/停止三元组 | cmd, data, sbc, done |
struct mmc_command |
MMC 核心层 | 单条 MMC 命令,包含命令号、参数、标志 | opcode, arg, flags, resp |
struct mmc_data |
MMC 核心层 | 数据传输描述,包含 SG 表、块数、块大小 | sg, blocks, blksz, flags |
常见误区: 初学者常混淆
struct mmc_blk_request 和 struct mmc_request。前者是块驱动层的封装,包含多个 MMC 命令(如预擦除 + 写命令);后者是核心层的单一命令/数据单元。调优时,如果需要在块驱动层插入自定义命令(如 Cache Flush),必须操作 mmc_blk_request 中的 mrq 字段。
1.3.3 性能关键路径
从块设备层到 MMC 核心层的完整路径中,以下环节直接影响 I/O 性能:
- I/O 调度器选择:eMMC 设备建议使用
mq-deadline或none,避免 CFQ 带来的额外延迟。 - 请求合并:块设备层会合并相邻扇区的 bio,减少 MMC 命令数量。通过
/sys/block/mmcblk0/queue/max_sectors_kb控制最大合并大小。 - DMA 映射:MMC 核心层通过
dma_map_sg将 SG 表映射到 DMA 地址,ARM 平台需注意 cache 一致性维护。 - 命令排队:eMMC 5.0 支持命令排队 (CQ),块设备层通过
blk-mq多队列框架与核心层交互,减少锁竞争。
// 块设备层请求队列参数示例 (sysfs 调优)
// 查看当前队列参数
cat /sys/block/mmcblk0/queue/max_sectors_kb
cat /sys/block/mmcblk0/queue/max_segments
cat /sys/block/mmcblk0/queue/io_poll
// 设置最大扇区数为 1024 KB (适合大块顺序读写)
echo 1024 > /sys/block/mmcblk0/queue/max_sectors_kb
// 设置 I/O 调度器为 mq-deadline
echo mq-deadline > /sys/block/mmcblk0/queue/scheduler
ARM 平台特别提示: 在 ARM 架构中,MMC 主机控制器通常挂在 AXI 总线上。块设备层与 MMC 核心层之间的数据缓冲区必须进行 cache 刷新操作(
dma_map_sg 自动处理),否则会出现数据不一致导致的 CRC 错误。调试时可通过 CONFIG_DMA_API_DEBUG 检测 DMA 映射问题。