sdhci.c
是 Linux 内核中针对 SD Host Controller Interface(简称 SDHCI)控制器的通用驱动核心模块。它支持绝大多数符合 SDHCI 标准的硬件控制器,实现了对 SD、eMMC、SDIO 等设备的读写控制。在本文中,我们将结合源码,从硬件规范角度深入剖析其核心机制。
一、SDHCI 是什么?
SDHCI(Secure Digital Host Controller Interface)是一种主机控制器接口标准,由 SD 协会定义,旨在统一 SD 卡控制器与系统主机之间的软件接口。它通常部署在 SoC 或主板芯片组中,负责与 SD 卡之间的通信,支持:
- 标准 SD 和 SDHC/SDXC 卡
- eMMC 存储芯片(部分兼容)
- SDIO 外设(如无线模块)
二、寄存器布局与硬件抽象
SDHCI 控制器通过一组统一寄存器与 CPU 通信。常见的寄存器包括:
寄存器名称 | 功能描述 |
---|---|
SDHCI_ARGUMENT | 命令参数 |
SDHCI_COMMAND | 命令与响应类型控制 |
SDHCI_RESPONSE | 命令响应寄存器 |
SDHCI_TRANSFER_MODE | 传输模式设置(DMA、多块等) |
SDHCI_PRESENT_STATE | 当前状态(总线忙、卡插入等) |
SDHCI_CLOCK_CONTROL | 时钟启用与分频 |
SDHCI_HOST_CONTROL | 主控功能控制,如数据宽度、HighSpeed |
SDHCI_POWER_CONTROL | 电压设定与电源开关 |
驱动通过 sdhci_readl()
和 sdhci_writel()
等封装函数与寄存器交互,实现硬件无关性。
三、初始化与卡检测
驱动初始化时,sdhci_init()
会进行控制器复位,并设置中断、清除旧状态等:
sdhci_reset(host, SDHCI_RESET_ALL);
sdhci_clear_set_irqs(...);
sdhci_set_ios(...); // 设置电压、时钟、数据宽度
随后调用 sdhci_enable_card_detection()
启用卡插拔检测逻辑。检测方式通常有两种:
- 电平检测(读取
PRESENT_STATE
) - 中断检测(依赖
CARD_INSERT
、CARD_REMOVE
中断)
部分控制器存在卡检测问题(如 SDHCI_QUIRK_BROKEN_CARD_DETECTION
),驱动中也通过 quirk 机制予以规避。
四、命令发送机制(Command Path)
MMC 协议中,每次传输前都需发出命令(CMDx)。命令发送通过 sdhci_send_command()
实现:
- 等待控制器空闲(检查
CMD_INHIBIT
、DATA_INHIBIT
) - 写入命令参数:
SDHCI_ARGUMENT
- 设置传输模式(Transfer Mode):读/写、多块、DMA
- 写入命令寄存器:
SDHCI_COMMAND
- 启动定时器防止卡死
中断响应由 sdhci_cmd_irq()
处理,解析响应并填充 mmc_command
的 resp[]
字段。
五、数据传输机制:PIO 与 DMA
SDHCI 控制器支持三种数据传输方式:
-
PIO 模式:CPU 轮询或中断,读写
SDHCI_BUFFER
。sdhci_read_block_pio()
sdhci_write_block_pio()
-
SDMA(Simple DMA):
- 支持一次性 DMA 传输,使用
SDHCI_DMA_ADDRESS
- 配合 SG 列表,驱动仅支持单个段,限制大
- 支持一次性 DMA 传输,使用
-
ADMA(Advanced DMA):
- 使用 ADMA descriptor table:
sdhci_adma_table_pre/post()
- 可自动遍历 SG 列表,性能高
- 使用 ADMA descriptor table:
驱动通过 host->flags
记录当前使用的传输方式,若 DMA 条件不满足(如未对齐、长度限制),则自动回退至 PIO。
六、时钟控制机制
时钟控制由 sdhci_set_clock()
完成,其核心思路是:
- 按目标频率计算分频值(
div
) - 写入
SDHCI_CLOCK_CONTROL
,使能内部时钟 - 等待
INT_CLOCK_STABLE
标志 - 启用卡时钟输出
在 UHS 模式或 Preset Value 启用场景下,还需配合 HOST_CONTROL2
寄存器更新。
七、电压控制与信号电平切换
控制器可支持 3.3V / 1.8V 的 IO 电压切换,流程如下:
- 关闭 SDCLK 输出
- 修改
HOST_CONTROL2
中的 VDD 电压位 - 延迟等待电平稳定
- 重新启用 SDCLK
- 检查数据线电平(DAT[3:0])
若切换失败,需重新上电(power cycle)卡片。
八、中断处理
中断处理函数为 sdhci_irq()
,主要分为:
- 命令中断:
sdhci_cmd_irq()
- 数据中断:
sdhci_data_irq()
- 卡插拔中断:通过
CARD_INSERT/REMOVE
标志调度 tasklet - SDIO 中断:通过
CARD_INT
标志,回调mmc_signal_sdio_irq()
对于 DMA、PIO、卡状态变化等情况,均通过中断+tasklet 模式完成异步处理,提升效率并避免死锁。
九、调试与寄存器分析
驱动提供 sdhci_dumpregs()
函数用于输出关键寄存器值,便于调试卡识别、数据异常等问题。典型输出如:
sdhci: =========== REGISTER DUMP ===========
Sys addr: 0x00000000 | Version: 0x00003000
Blk size: 0x00000200 | Blk cnt: 0x00000008
...
建议在调试中插入关键路径调用以观察寄存器状态变化。
十、总结:SDHCI 驱动的关键价值
SDHCI 作为标准控制器驱动,其核心价值在于封装控制器访问规范、屏蔽平台差异性、优化传输性能。通过 quirks、ops 等机制,它既支持通用性,又能灵活适配特定硬件。
对嵌入式开发者而言,理解 SDHCI 驱动的硬件控制机制有助于:
- 定位卡识别失败、挂载问题
- 优化高速模式下的数据传输性能
- 进行平台适配或自研控制器移植
如果你还希望扩展博文内容至移植过程分析或DMA 调试技巧,我可以继续完善。如果需要插图,我也可配合绘制 SDHCI 控制器结构或时序图。需要我继续扩展某部分吗?