Linux SD NAND 驱动开发指南
编制日期 2026-10-05
MMC 块设备路线为主线 · SPI 模式与自研路线作对照 · 面向 SoC BSP 与存储原厂联调 | 2026-10-04 | 版本号 Rev 1.4
SD NAND 在 Linux 里不需要你写驱动 —— 它是标准 SD 卡,接在 SoC 的 SD/MMC 控制器上,内核 MMC 子系统会自动把它认成 /dev/mmcblk0。真正的工作量在设备树、pinmux、供电时序和掉电安全策略上。本文档给出「先走主线 MMC、确有必要再自研」的完整落地路径与全部踩坑点。
这份文档解决什么
- 先破除最大的误解。网上大量「SD NAND Linux MTD 驱动」文章其实是把 SPI NAND 驱动改名套用。本文第 1 章用内核目录事实说明:主线没有
drivers/mtd/nand/raw/sdnand.c,SD NAND 在 Linux 里的正确归宿是 MMC 子系统 + 块设备。 - 把主线路线写细。第 3~8 章按「硬件前提 → MMC 内核栈 → 设备树 → host 驱动适配 → 编译烧写 → 枚举排障 → 性能调优 → 挂载与文件系统」的顺序,把路线 A 每一步的命令、日志、配置项都写出来,可以照着做。
- 把自研路线讲透边界。第 9~10 章讲清楚什么情况下才需要自研 MMC host 或自研块设备,以及 SPI 模式在 Linux 里的真实位置(不进 MTD,只能自己实现 SD-over-SPI 栈)。
- 低功耗与掉电是主线。第 11 章把 VCC 负载开关门控、内核
runtime PM、suspend/resume三者的配合写清楚 —— 这是 SD NAND 相对 TF 卡最大的差异化收益。 - 速查表和核查清单。第 12~14 章是故障现象速查表、交付核查清单与附录命令表,可以直接当 review checklist 用。
读者路径
与同系列文档的关系
| 文档 | 关注重点 | 本文何时读它 |
|---|---|---|
| SD_NAND_Flash 电路设计指南 | SPI/SDIO 双模式、VCC 负载开关门控、PCB Layout、ESD | 本文第 3 章确认硬件前提时 |
| SD_NAND_Flash 软件设计规范 | 命令流、忙等双确认、AU 对齐、错误码、CID 绑定 | 本文第 5 章 host 驱动移植时 |
| RT-Thread / FreeRTOS / AliOS 驱动开发指南 | RTOS 侧设备模型与驱动开发方法论 | 同一器件在 RTOS 侧落地时 |
1 · 先把事实说清楚:SD NAND 在 Linux 里到底是什么
这一章解决一个根子上的认知问题:SD NAND 是不是「一种 NAND」?如果你按 NAND 的思路去找驱动、找文件系统、找坏块表,会一路走错。本章给出内核目录层面的事实、SD NAND 的封装原理,以及两条技术路线的取舍依据。
1.1 一个必须先接受的事实:主线没有 SD NAND 的 MTD 驱动
Linux 内核 drivers/mtd/nand/raw/ 目录下共有 70 余个文件,覆盖从早期的 plat_nand.c、diskonchip.c 到现代的 qcom_nandc.c、denali.c、cadence-nand-controller.c 等各类 NAND 控制器,以及 nand_*.c 系列芯片厂商表。其中没有任何一个文件叫 sdnand.c,也没有任何一个文件的 compatible 是 SD NAND。
MTD 子系统处理的是裸 Flash:它暴露 Page 读写、OOB 读写、块擦除、坏块标记,由主机负责 ECC、坏块重映射、磨损均衡(UBI/UBIFS 就是干这个的)。
而 SD NAND 是NAND 阵列 + 内部 SD 控制器 + 片内 FTL 的封装。片内控制器已经完成了坏块管理、ECC、磨损均衡,对主机只暴露 512 字节逻辑块(LBA)。它没有 OOB 可读写,没有裸块擦除命令,主机也无从得知物理坏块位置。
换句话说:SD NAND 抛给主机的接口面就是块设备接口,MTD 抽象对它没有意义。这和 eMMC、SD 卡、U 盘完全同理 —— 它们都不在 /proc/mtd 里,都不用 mtd-utils。
与之相对,Linux 里确实存在「把 SD 卡当 MTD 用」的第三方实现(例如某些 MCU 厂商 SDK 里的 sdmtd),它们做的是把 SD 卡的块映射成一个带 erase 语义的伪 MTD 设备,供 UBIFS 或 FTL 使用。这类实现与本文第 9 章讨论的自研路线属于同一类东西,它不是内核主线能力,向上兼容、内核升级风险都要自己承担。
1.2 SD NAND 的封装原理决定了它的接口面
要理解后面的所有结论,只需要理解一张图:SD NAND 内部已经把 NAND 的物理特性全部吃掉了。
1.3 两条技术路线
把「SD NAND 怎么接进 Linux」这个问题拆开,只有两种答案。
路线 A:MMC 子系统 → 块设备(主线)
- SoC 已有 SD/MMC 控制器(SDHCI、dwcmsc、dw_mmc、sdhci-msm 等绝大多数 SoC 都有)
- 设备树里描述控制器 + 子节点,内核 MMC 核心走标准 SD 协议枚举
- 识别后注册
/dev/mmcblk0,直接mkfs+mount - 你可能一行 C 代码都不写
路线 B:自研驱动
- 触发条件很少:主控确实没有 SD 控制器(如某些 MCU、DSP、FPGA 软核),或者产品需要内核块设备之外的自定义语义(自定义寿命管理、端到端加密、分区密钥)
- 可以自研 MMC host(复用内核 MMC 核心),也可以自研裸块设备(绕过 MMC 核心)
- 工作量 800~2500 行,DMA、超时、并发、掉电全自己负责
- 内核升级要重新适配
先问清对方要解决的是哪个问题。八成情况下真正要做的是:改设备树(总线宽度、频率、电压、regulator)、补 pinmux、确认供电时序、做掉电保护策略。这几件事做好,设备就跑起来了。
只有当对方明确说「我们主控没有 SD 控制器」或「我们需要块设备之外的自定义语义」,才进入路线 B 的讨论。
2 · SD NAND 器件与 Linux 侧的接口面
这一章把「器件提供了什么」和「Linux 关心什么」对齐,列出 host 驱动与文件系统真正依赖的字段,为第 5 章移植代码做准备。
2.1 SD NAND 的关键几何与能力参数
SD NAND 遵循 SD Physical Layer 规范,容量不做分档,但几何、擦除粒度、AU 大小写在 CSD/SCR 里,Linux 内核并不解析这些字段(它只按 512 B 扇区看待)。对 Linux 开发者来说,只有下面几组参数真正影响软件行为:
| 参数 | 来源 | Linux 是否使用 | 对软件的实际影响 |
|---|---|---|---|
| 容量(C_SIZE / OCR) | CSD / OCR | 是 | 决定 /dev/mmcblk0 大小与分区大小 |
| 块长度 512 B | 固定 | 是 | 块设备扇区大小恒为 512 |
| SCR(SD 配置寄存器) | ACMD51 | 部分 | 决定是否支持 1.8 V 切换、SDR50/DDR50 能力 |
| AU_SIZE(分配单元) | ACMD13 | 否 | 内核不感知。写放大由器件内部 FTL 承担,但应用写对齐仍能显著提升寿命 |
| 擦除块 / 页几何 | CSD 或器件私有 | 否 | 主控无法发裸块擦除;只影响 fio/flashbench 类工具的实测结果 |
| CID / CSD / OCR | CMD2 / CMD9 / ACMD41 | 是(绑定用) | 可用于设备身份绑定、防错料、产线追溯 |
| 厂商私有 SMART | 厂商自定义命令 | 否 | 剩余寿命需厂商私有命令读;需自己写 ioctl 工具 |
注:ACMD13 / AU_SIZE 的解析在嵌入式自研驱动里常见,但 Linux MMC 核心不读它。若需要,须在 host 驱动或用户态工具里自行发 ACMD13。
因为 AU_SIZE、擦除块大小对 Linux 不可见,「让应用写对齐」这件事在 Linux 里只能靠应用与文件系统侧自觉,无法由驱动强制。Linux 侧真正能做的优化是:写合并(block 层 blk-mq 与调度器)、减少 fsync、选择合适的文件系统(f2fs 比 ext4 更适合闪存)。
2.2 电气与时序前提(驱动前必须对表)
| 项目 | 典型值 | 驱动侧约束 | 违反后的现象 |
|---|---|---|---|
| 供电电压 | 3.3 V(2.7~3.6 V)或 1.8 V(1.7~1.9 V) | regulator 配对;I/O 电压与 SoC bank 匹配 | CMD 无响应 / 读到全 0xFF / 枚举超时 |
| tPOR(上电到首条命令) | ≥1 ms(典型 5 ms) | regulator 使能后留足延时 | 偶发首条 CMD 无响应,重试后正常 |
| 识别时钟 | 100~400 kHz | 设备树 max-frequency 在识别阶段由内核自动降到 400 kHz | CMD8/ACMD41 失败,能耗异常 |
| 工作时钟 | 50 MHz(HS);部分器件支持更高 | 识别成功后由内核切 High-Speed | 数据 CRC 错、随机读失败 |
| 总线宽度 | 4-bit(HS 前提) | bus-width = <4> | 日志显示 1-bit,带宽降到 1/4 |
| 峰值电流 | 读 15~30 mA;写 / 擦 30~70 mA(峰值 100 mA) | regulator 限流留足裕量;去耦按 datasheet | 枚举成功但负载下读写报错、复位 |
| Standby 电流 | 80~350 µA | 停时钟只到这个量级 | 误以为能省电,实际待机电流不降 |
| 断电后漏电流 | <1 µA(取决于负载开关) | VCC 门控方案的目标值 | 待机功耗不达标 |
eMMC 有 CMD5 SLEEP/AWAKE,可以进低功耗状态;SD NAND 规范没有定义这类指令。停掉时钟后器件进入 Standby,电流仍在百 µA 量级。
要达到 µA 级待机,只能靠主控 GPIO 控制负载开关切断 VCC,并严格遵守「先收 IO、再断电」的顺序。这条路线在第 11 章完整展开。
2.3 Linux 侧真正会被用到的命令
内核 MMC 核心发往 SD NAND 的命令集合是固定的、标准的。理解这批命令的用途,既有助于读懂 dmesg 日志,也有助于移植 host 驱动时确认哪些路径必须实现。
| 命令 | 方向 | 阶段 | 作用 | 驱动关注点 |
|---|---|---|---|---|
| CMD0 | H→C | 识别前 | 复位 / 转入 Idle | arg = 0x95 时走 SPI 模式锁定;SD 模式下为 0 |
| CMD8 | H→C | 识别 | SD2.0 版本协商(arg=0x1AA) | 响应非法应走 v1 分支;host 需正确回 R7 |
| ACMD41 | H→C | 识别 | 操作条件协商,直到 OCR Busy=1 | 超时上限要放足(约 1 s) |
| CMD2 / CMD3 | H→C | 识别 | 取 CID / 分配 RCA | RCA 值来自卡,注意 SD NAND 可能是 0x0001 |
| CMD9 | H→C | 识别 | 读 CSD(容量、几何) | 容量解析在此完成 |
| CMD7 | H→C | 识别后 | 选卡进入传输态 | 总线宽度切换前后都要保持状态机正确 |
| ACMD51 / SCR | H→C | 配置 | 读 SCR(电压 / 时序能力) | 决定 1.8 V 与 UHS 能力 |
| ACMD6 | H→C | 配置 | 切 4-bit 总线 | 失败必须能回退到 1-bit,否则设备变砖 |
| CMD6 | H→C | 配置 | 切 High-Speed(SDR) | 失败必须能降频继续工作 |
| CMD17 / CMD18 | H→C | 读 | 单块读 / 多块读(CMD12 停止) | 多块读必须正确处理 STOP |
| CMD24 / CMD25 | H→C | 写 | 单块写 / 多块写(CMD12 停止) | 写后 busy;内核已做 busy 等待 |
| ACMD23 | H→C | 写 | 预擦除块数声明 | 大块写前可发,提升写入效率 |
| CMD13 | H→C | 任意 | 读卡状态(R2 含错误位) | 内核用于错误诊断 |
| CMD0(复位后重枚举) | H→C | 错误恢复 | 卡挂死后重新走完整识别流 | 内核 mmc_rescan 路径 |
说明:SD NAND 的 SPI 模式命令是 SD 命令的 SPI 封装(如 0x13 读、0x14 写、0x15 擦),与 SPI NAND 的 0x13/0x0F/0x03 三段式缓存读流程不是一回事,详见第 10 章。
2.4 芯天下 XTX SD NAND 型号对照(供设备树与验证参考)
| 电压域 | 容量 | 料号 | 封装 | 接口形态 |
|---|---|---|---|---|
| 3.3 V | 1 Gb | XTSDG01GWSIGA | WSON-8 8×6 mm | SDIO 4-bit / SPI |
| 3.3 V | 2 Gb | XTSDG02GWSIGA | WSON-8 8×6 mm | SDIO 4-bit / SPI |
| 3.3 V | 4 Gb | XTSDG04GWSIGA | WSON-8 8×6 mm | SDIO 4-bit / SPI |
| 3.3 V | 8 Gb | XTSDG08GWSIGA | WSON-8 8×6 mm | SDIO 4-bit / SPI |
| 1.8 V | 1 Gb | XTSDQ01GWSIGA | WSON-8 8×6 mm | SDIO 4-bit(I/O 1.8 V) |
| 1.8 V | 2 Gb | XTSDQ02GWSIGA | WSON-8 8×6 mm | SDIO 4-bit(I/O 1.8 V) |
| 1.8 V | 4 Gb | XTSDQ04GWSIGA | WSON-8 8×6 mm | SDIO 4-bit(I/O 1.8 V) |
| 1.8 V | 8 Gb | XTSDQ08GWSIGA | WSON-8 8×6 mm | SDIO 4-bit(I/O 1.8 V) |
| 1.8 V | 1 / 2 Gb | XTSDQ01GLAIGA / XTSDQ02GLAIGA | LGA-8 6×5 mm | 小体积版本 |
WSON-8 与 LGA-8 的 pin order 不通用,不同容量批次也可能不同。建库时不要套用其他家的封装符号,务必按具体料号 datasheet 的引脚图录入。具体电气参数(时序、电流、时钟上限)同样以正式 datasheet 为准。
3 · 硬件前提核查:软件之前先把板子确认一遍
这一章解决「代码还没写就注定失败」的那部分问题。SD NAND 在 Linux 下不需要写驱动,但硬件上有 6 个前提必须逐条确认,任何一条不满足都会表现为「枚举失败」,而根因在板子上。
3.1 原理图检查清单
拿到一块板子,先按下面 6 条逐项确认。这 6 条对应 SD NAND 的最小系统,缺一条都不会枚举成功。
| # | 检查项 | 合格标准 | 不满足时的现象 |
|---|---|---|---|
| 1 | 信号线完整性 | CLK、CMD、DAT0~DAT3 六根线全部接到 SoC 对应引脚,无错接 | 完全无响应;或 1-bit 能识别但 4-bit 失败 |
| 2 | 总线方向引脚复用 | DAT1~DAT3 配为 MMC 功能,不是 GPIO 或测试点 | 内核只能枚举到 1-bit,bus-width 声明 4 但实际 1 |
| 3 | VCC 供电 | 3.3 V 或 1.8 V 稳压,限流能力 ≥200 mA,去耦按 datasheet | 小负载正常、读写时复位或报错 |
| 4 | VCC 门控(可选) | 负载开关 + 主控 GPIO,允许完全断电 | 无法做低功耗待机 |
| 5 | CMD / DAT 上拉 | CMD 与 DAT0~3 各有 4.7~10 kΩ 上拉到 I/O 电压 | 总线浮空,总线宽度协商随机失败 |
| 6 | 无热插拔需求 | SD NAND 贴片常驻,设备树用 non-removable | 反复探测导致启动变慢与额外功耗 |
这一条造成的现象极具迷惑性:dmesg 里能看到 bus width 4 已经设置,但 cat /sys/block/mmcblk0/device/.. 下的实际宽度是 1,或者吞吐只有理论值的四分之一。很多人会去怀疑驱动、去改时钟、去换器件,绕一大圈。
正确的排查顺序是:先量信号。用示波器看 DAT2/DAT3 在 ACMD6 之后有没有翻转为低,再查 pinmux 配置表里这几根线是否被复用了。
3.2 SoC 控制器能力确认
设备树写之前,先在 SoC 手册或内核源码里确认控制器类型与能力。这决定了后面用哪条 host 驱动路径。
| 控制器类型 | 典型 SoC | 内核 host 驱动 | 驱动文件 | 支持 4-bit / HS |
|---|---|---|---|---|
| SDHCI | 高通、联发科部分、瑞昱、英伟达部分 | sdhci-pci / sdhci-of-dwcmshc / sdhci-of-arzoo | drivers/mmc/host/sdhci*.c | 支持 |
| DesignWare MMC | 瑞萨、东芝、海思、博通、安霸 | dw_mmc + 平台 glue | drivers/mmc/host/dw_mmc*.c | 支持 |
| dwcmsc(Synopsys) | 联发科 MT676x/MT678x | dwcmsc-dw | drivers/mmc/host/dwcmsc-dw.c | 支持 |
| sdhci-msm | 高通 MSM / 骁龙 | sdhci_msm | drivers/mmc/host/sdhci-msm.c | 支持 |
| 无 SD 控制器 | 部分 MCU、DSP、FPGA | — | — | 需路线 B(见第 10 章) |
两条路都可以:
① 查 SoC 内核源码里的设备树:grep -r "mmc@" arch/arm64/boot/dts/,看节点的 compatible 字符串;
② 启动后看 dmesg | grep -i mmc,日志里会出现驱动名(如 mmcblk0: mmc0:0001 ... 之前那几行里的控制器标识)。
3.3 供电与电压域:三个容易出错的地方
- 数量级不够。SD NAND 写 / 擦瞬间电流可达 70 mA(峰值 100 mA)。若 regulator 限流设得偏低(如默认 100 mA 且压降明显),会出现「读正常、写几秒后失败」。建议留到 200 mA 以上裕量,去耦按 datasheet 的高频电容。
- I/O 电压不匹配。3.3 V 版本(XTSDG 系列)不能接在 1.8 V 域的 MMC bank 上;1.8 V 版本(XTSDQ 系列)同理。SoC 的 MMC 控制器通常有
vqmmc-supply(I/O 电压)与vmmc-supply(核心电压)两个 regulator,1.8 V 版本需要额外电平或直接落在 1.8 V bank。 - 上电时序未留延时。regulator 使能后需要等 tPOR(≥1 ms)。内核 MMC 核心在探测时会发 74 个时钟并轮询 ACMD41,天然覆盖了大部分情况;但如果用
mmc-pwrseq自定义上电序列,必须显式加延时。
3.4 布线与信号完整性
| 信号 | 要求 | 不满足的后果 |
|---|---|---|
| CLK | 连续参考平面,跨层换参考要加过孔对;避免与 DAT 并行走长距离 | 边沿畸变,High-Speed 下数据 CRC 错、随机读失败 |
| CMD | 开漏信号,必须有上拉(4.7~10 kΩ 到 I/O 电压);不能加 RC 滤波 | 上升沿过慢,识别阶段就可能超时 |
| DAT0~3 | 并行走线等长(组内 5 mm 内);跨层同时换参考 | 总线宽度切换失败,或高负载下偶发 CRC 错 |
| VCC 去耦 | 近端 0.1 µF + 1~10 µF 多级 | 写负载瞬时压降,器件复位 |
这类现象的判定方法很明确:把 max-frequency 降到 25 MHz 甚至 12.5 MHz,如果故障消失,就是信号完整性问题;如果降频后依然错,才往协议层与驱动层查。降频法是这类问题最快的二分手段,应作为标准排查步骤写进团队的调试手册。
4 · MMC 子系统解剖
这一章解决「内核到底替我做了什么」。理解 MMC 内核的三层结构与枚举时序,才能读懂 dmesg、才能判断失败发生在哪一层、才能知道 host 驱动必须实现什么。
4.1 三层结构与调用链
Linux MMC 子系统分为三层:核心层(core)实现 SD 协议;主机层(host)实现具体控制器的寄存器与中断;卡层(block)提供块设备与分区。
用户态 mount / fdisk / mkfs.ext4 /dev/mmcblk0p1
│ VFS 路径
卡层 drivers/mmc/core/block.c → /dev/mmcblk0
│ bio / request_queue
核心层 drivers/mmc/core/core.c 协议状态机、命令队列、超时
drivers/mmc/core/sd.c SD 专用:CMD0/8/ACMD41/2/3/9/7 + CMD17/18/24/25
drivers/mmc/core/block.c 卡状态(present / readonly / write-protected)
│ struct mmc_host_ops
主机层 drivers/mmc/host/sdhci.c SDHCI 通用
drivers/mmc/host/dw_mmc*.c DesignWare MMC
drivers/mmc/host/sdhci-msm.c 高通私有
│ 寄存器 + 中断 + DMA
硬件 SoC SD/MMC 控制器 → CLK/CMD/DAT0~3 → SD NAND内核的 SD 协议实现写在 drivers/mmc/core/sd.c,SD/eMMC/SDIO 的区分体现在 struct mmc_card 的类型与 mmc_attach_sd() 流程。SD NAND 对内核来说就是一张焊死在板上的 SD 卡,没有任何一行内核代码知道它是 NAND。
4.2 枚举时序逐拍
理解这段时序有两个用处:一是能对照 dmesg 判断卡在哪一步;二是移植 host 驱动或用 GPIO 模拟时知道每一步的预期响应。
内核 MMC 核心的 SD 枚举流程(简化):
① mmc_of_parse() 读设备树:bus-width / max-frequency / pwrseq
② 供电与 pwrseq 使能 vmmc / vqmmc,按 pwrseq 拉 GPIO,等 tPOR
③ 送 74+ 个时钟 清总线残留状态(总线宽度初始为 1)
④ CMD0 → 期望 R1 = 0x01(Idle)
⑤ CMD8 (arg=0x1AA) → 期望 R7 回显 0x1AA(v2.00)
失败则走 v1.x 分支(无 CMD8)
⑥ CMD55 + ACMD41 循环 直到 OCR[31] = 1(Busy)
超时上限约 1 s,器件实际通常几十 ms 完成
⑦ CMD2 → 取 128 bit CID
⑧ CMD3 → 卡分配 RCA(SD NAND 常为 0x0001)
⑨ CMD9 → 取 128 bit CSD(容量在 CSD[69:48] 22 bit)
⑩ 解析 CSD → 算出容量(块数 × 512 B)
⑪ CMD7 (rca) → 选卡,进 Transfer 状态
⑫ ACMD51 → 读 SCR,决定电压切换与时序能力
⑬ ACMD6 = 4 → 切 4-bit;失败回退 1-bit
⑭ CMD6 (SDR50/HS) → 切 High-Speed;失败留在默认速
⑮ 分配 mmcblk → register_blkdev(),生成 /dev/mmcblk0
⑯ 扫描分区表 → msdos / GPT,挂载为 mmcblk0p1 …CMD8 的响应里必须包含与参数相同的 0x1AA。host 驱动若没有正确填充 R7 寄存器回显,内核会判定 v2.00 协商失败并回退到 v1.x 分支 —— v1.x 分支下不会走 ACMD41,也不会做 4-bit 与 High-Speed 协商,最终表现为「能识别但很慢」。
因此:如果你的设备是 1-bit 且 25 MHz,那就是 R7 回显或 CMD8 通路的问题,而不是器件限制。
4.3 关键结构体与移植接口
host 驱动要实现的,是 struct mmc_host_ops 里的几个回调。理解这些回调的语义,就理解了 host 驱动的全部工作量。
| 回调 | 作用 | SD NAND 场景下的实现要点 | 谁帮你写好了 |
|---|---|---|---|
.request | 提交一个 MMC 请求(命令 + 数据) | 把命令写进控制器 FIFO、启动中断 / DMA | host 驱动(SDHCI / dw_mmc) |
.send_data | 发送命令的数据段 | 配置 DMA 描述符与目标缓冲区 | host 驱动 |
.send_scr / .get_ro / .get_csd / .get_cid | 读 SCR / CSD / CID | 从控制器 FIFO 读即可 | host 驱动 |
.card_busy | 查询卡是否忙(写 / 擦后) | 读 DAT0 线电平;SD 模式为低电平表示忙 | host 驱动 |
.set_ios | 切换时钟 / 总线宽度 / 时序 | 在 CMD6 / ACMD6 之后被调用 | host 驱动 |
.init_host | 控制器上电初始化 | 设置初始 400 kHz 时钟、总线宽度 1 | host 驱动 |
.get_ro | 判断是否只读 | 无写保护引脚时返回 0 | host 驱动 |
.pre_request / .post_request | 请求前后钩子 | 可用于功耗管理、DMA 映射 | host 驱动或平台 glue |
.enable_sdma_irq / .hsq | 高速路径优化 | 6.x 内核可开 HSQ 提升并发 | host 驱动 |
这就是「不需要写驱动」的含义:这些回调由内核的 sdhci.c 或 dw_mmc.c 通用实现,SoC 厂商只提供寄存器映射、时钟、pinctrl 与电源的 glue 代码。你的板子如果用这些通用 host 驱动,改设备树就够了。
只有在 SoC 用了厂商私有 host 驱动,或 SoC 的控制器与通用实现有偏差时,才需要动这一层。届时本文第 6 章给出适配清单。
4.4 源码位置速查
| 想搞清楚的问题 | 去看的文件 | 关键符号 |
|---|---|---|
| SD 协议怎么发的 | drivers/mmc/core/sd.c | mmc_attach_sd、mmc_sd_init_card、mmc_decode_ext_csd |
| 命令队列与超时 | drivers/mmc/core/core.c | mmc_start_request、__mmc_start_timeout、mmc_uhs_... |
| 块设备怎么来的 | drivers/mmc/core/block.c | mmc_blk_alloc、mmc_blk_issue_rq、register_blkdev |
| 设备树属性怎么解析 | drivers/mmc/core/of.c | mmc_of_parse、mmc_of_dwcmsc_probe |
| SDHCI 通用实现 | drivers/mmc/host/sdhci.c | sdhci_send_command、sdhci_send_data |
| DesignWare 实现 | drivers/mmc/host/dw_mmc.c | dw_mmc_send_cmd、dw_mmc_setup_data |
| 卡状态与错误 | drivers/mmc/core/block.c | mmc_blk_status、MMC_STAT_CARD |
| 日志怎么加 | drivers/mmc/core/*.c | pr_info / mmc_ 前缀的 dev_info |