只读分享解决方案文档原理方案设计📅 2026-10-05🔖 Rev 1.4⏳ 26天有效(至 2026-11-05)
🔧解决方案&应用市场分析 -> 原理方案设计 -> 嵌入式驱动&系统开发 -> Linux_SD_NAND_驱动开发指南

Linux_SD_NAND_驱动开发指南

生成时间 2026-10-04 11:49:31 有效期至 2026-11-05 15:32:34(26天有效(至 2026-11-05))
同系列 · 原理方案设计

Linux SD NAND 驱动开发指南

编制日期 2026-10-05

MMC 块设备路线为主线 · SPI 模式与自研路线作对照 · 面向 SoC BSP 与存储原厂联调 | 2026-10-04 | 版本号 Rev 1.4

100%
片内 FTL 由器件完成
主机不碰坏块、ECC、磨损均衡
0 行
路线 A 需新增的 C 代码
只需设备树与 SoC 侧配置
≤400 kHz
识别阶段时钟上限
识别完成后方可升频到 50 MHz
4-bit
建议的总线宽度
SD NAND 支持 1-bit 与 4-bit
10~20%
必须预留的空闲空间
片内 FTL 靠空闲块做 GC

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 用。

读者路径

PATH
路线 A · 首次点亮
从原理图确认硬件前提,一路走到 mount 成功。板级 BSP 工程师主读 1→8→12 章。
读者:SoC BSP / 板级驱动工程师
PATH
路线 A · 性能与可靠性
跑满 4-bit + High-Speed,做掉电测试与寿命预算。存储产品与测试工程师主读 8、12~14 章。
读者:存储 FAE / 测试工程师
PATH
路线 B · 自研评估
确认主线走不通的原因,评估自研 host 或自研块设备的成本。SDK 团队主读 2、9、10 章。
读者:SoC SDK / IP 团队
PATH
原厂视角 · 联调
站在存储芯片原厂侧看 Linux 侧的接入点,做硅片验证与一致性测试。XTX 内部与 FAE 主读 2、8、13、14 章。
读者:存储原厂 FAE / 产品经理

与同系列文档的关系

文档关注重点本文何时读它
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 章讨论的自研路线属于同一类东西,它不是内核主线能力,向上兼容、内核升级风险都要自己承担。

SD NAND 在 Linux 里的两条归宿:绝大多数项目走上面一条SoC 的 SD/MMC 控制器CLK / CMD / DAT0~3 / VCC / 可选 CD#SD NAND 器件NAND 阵列 + 内部控制器 + 片内 FTL / ECC / BBM路线 A:MMC 子系统 → 块设备(主线能力)推荐 · 量产项目唯一现实选择/dev/mmcblk0 · /dev/mmcblk0p1内核配置:CONFIG_MMC / MMC_BLOCK / MMC_SD设备树:compatible + bus-width + max-frequency文件:mmc/core/sd.c、core/block.c、host/sdhci*.c工作量:写设备树 + 调试 pinmux/电压,0 行 C 代码路线 B:自研驱动挂 MTD / 自己写块设备仅特殊需求 · 需自己承担全部正确性责任自定节点:/dev/sdctl0 或 /dev/mtdX适用:主控无 SD 控制器、需自定义 FTL/寿命策略不适用:绝大多数带 SDHCI/eMMC 控制器的 SoC代价:DMA、超时、掉电、并发全部自己写工作量:800~2500 行 C 代码,且需长期维护关键事实:Linux 主线没有 drivers/mtd/nand/raw/sdnand.c —— 网上多数「SD NAND MTD 驱动」文章是把 SPI NAND 驱动改名套用,不能直接信
图 1 · SD NAND 在 Linux 里的两条归宿

1.2 SD NAND 的封装原理决定了它的接口面

要理解后面的所有结论,只需要理解一张图:SD NAND 内部已经把 NAND 的物理特性全部吃掉了。

SD NAND 分层职责:片内控制器替你做了 70%,剩下 30% 仍要主机负责器件内部(片内控制器 / FTL)对主机表现为:标准 SD 协议的 512 B 块设备没有 Page Read、没有 OOB、没有裸块擦除命令→ 因此内核的 MTD 裸片框架在原理上就不适用主机侧(SoC + Linux 驱动)主机必须做:① 上电时序:tPOR 后才能发 CMD0② 初始化频率 ≤400 kHz,识别后再升频③ 4-bit / High-Speed 协商,失败必须能降级④ 空闲预留 10%~20%,不要把逻辑空间写满⑤ 关键结构写后读回校验(片内 FTL 不等于掉电安全)两者之间的唯一接口:SD 物理层命令CMD0 / CMD8 / ACMD41 / CMD2 / CMD3 / CMD9 / CMD7 / ACMD6 / CMD6 / CMD17 / CMD18 / CMD24 / CMD25 / CMD12 / ACMD23 / CMD13上层挂 ext4 / f2fs / squashfs / vfat 均可 —— 因为它已经是块设备了三个最常见的认知错误错误 1:把它当 SPI NAND,发 Page Read(0x13) 读页SD NAND 的 SPI 模式封装的是 SD 命令(0x13 是 SD 的 READ 变体),不是 NAND 的 0x13+GET_FEATURE 流程。套 SPI NAND 驱动必然读出乱码。错误 2:扫 OOB 找坏块标记、自己算 ECC这些全在器件内部。主机去读 OOB 只会读到未定义内容,写 OOB 可能直接触发器件内部状态机异常。错误 3:以为它能像 eMMC 一样 CMD5 进 SLEEP 省电SD NAND 规范没有定义 SLEEP/AWAKE 指令。停时钟只是 Standby,电流仍在百 µA 量级。要 µA 级待机只能靠 VCC 负载开关门控。
图 2 · SD 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、超时、并发、掉电全自己负责
  • 内核升级要重新适配
两条路线工作量与风险对比:差距不在代码行数,在「谁来保证正确性」对比维度路线 A:MMC → mmcblk路线 B:自研驱动需要写的 C 代码0 行(绝大多数 SoC)800 ~ 2500 行需要改的地方设备树 + 可能的 pinmux/时钟驱动 + 设备树 + Kconfig + Makefile协议正确性内核 sd.c 已验证十余年全部自己写、自己测DMA / 中断 / 缓存一致性host 驱动已实现从零实现,含 ARM D-Cache 一致性掉电 / 并发 / 超时内核统一处理全部自己处理4-bit / HS 协商与降级内核自动完成自己写,且失败无法回退就变砖跟进内核安全更新自动受益每次内核升级都要重新适配空盘挂载到可用半天2 ~ 6 周失败后的现场dmesg 有完整 MMC 栈日志需要自己加调试日志适合的量产项目绝大多数板载存储需求SoC 无 SD 控制器等少数特例结论:先用路线 A 把设备跑通;只有当路线 A 确实走不通时,才评估路线 B
图 3 · 两条路线工作量与风险对比
如果有人让你「在 Linux 下写 SD NAND 驱动」

先问清对方要解决的是哪个问题。八成情况下真正要做的是:改设备树(总线宽度、频率、电压、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 / OCRCMD2 / CMD9 / ACMD41是(绑定用)可用于设备身份绑定、防错料、产线追溯
厂商私有 SMART厂商自定义命令否剩余寿命需厂商私有命令读;需自己写 ioctl 工具

注:ACMD13 / AU_SIZE 的解析在嵌入式自研驱动里常见,但 Linux MMC 核心不读它。若需要,须在 host 驱动或用户态工具里自行发 ACMD13。

Linux 视角下最重要的一个结论

因为 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 kHzCMD8/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 门控方案的目标值待机功耗不达标
不要把 SD NAND 当成有 SLEEP 指令的设备

eMMC 有 CMD5 SLEEP/AWAKE,可以进低功耗状态;SD NAND 规范没有定义这类指令。停掉时钟后器件进入 Standby,电流仍在百 µA 量级。

要达到 µA 级待机,只能靠主控 GPIO 控制负载开关切断 VCC,并严格遵守「先收 IO、再断电」的顺序。这条路线在第 11 章完整展开。

2.3 Linux 侧真正会被用到的命令

内核 MMC 核心发往 SD NAND 的命令集合是固定的、标准的。理解这批命令的用途,既有助于读懂 dmesg 日志,也有助于移植 host 驱动时确认哪些路径必须实现。

命令方向阶段作用驱动关注点
CMD0H→C识别前复位 / 转入 Idlearg = 0x95 时走 SPI 模式锁定;SD 模式下为 0
CMD8H→C识别SD2.0 版本协商(arg=0x1AA)响应非法应走 v1 分支;host 需正确回 R7
ACMD41H→C识别操作条件协商,直到 OCR Busy=1超时上限要放足(约 1 s)
CMD2 / CMD3H→C识别取 CID / 分配 RCARCA 值来自卡,注意 SD NAND 可能是 0x0001
CMD9H→C识别读 CSD(容量、几何)容量解析在此完成
CMD7H→C识别后选卡进入传输态总线宽度切换前后都要保持状态机正确
ACMD51 / SCRH→C配置读 SCR(电压 / 时序能力)决定 1.8 V 与 UHS 能力
ACMD6H→C配置切 4-bit 总线失败必须能回退到 1-bit,否则设备变砖
CMD6H→C配置切 High-Speed(SDR)失败必须能降频继续工作
CMD17 / CMD18H→C读单块读 / 多块读(CMD12 停止)多块读必须正确处理 STOP
CMD24 / CMD25H→C写单块写 / 多块写(CMD12 停止)写后 busy;内核已做 busy 等待
ACMD23H→C写预擦除块数声明大块写前可发,提升写入效率
CMD13H→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 V1 GbXTSDG01GWSIGAWSON-8 8×6 mmSDIO 4-bit / SPI
3.3 V2 GbXTSDG02GWSIGAWSON-8 8×6 mmSDIO 4-bit / SPI
3.3 V4 GbXTSDG04GWSIGAWSON-8 8×6 mmSDIO 4-bit / SPI
3.3 V8 GbXTSDG08GWSIGAWSON-8 8×6 mmSDIO 4-bit / SPI
1.8 V1 GbXTSDQ01GWSIGAWSON-8 8×6 mmSDIO 4-bit(I/O 1.8 V)
1.8 V2 GbXTSDQ02GWSIGAWSON-8 8×6 mmSDIO 4-bit(I/O 1.8 V)
1.8 V4 GbXTSDQ04GWSIGAWSON-8 8×6 mmSDIO 4-bit(I/O 1.8 V)
1.8 V8 GbXTSDQ08GWSIGAWSON-8 8×6 mmSDIO 4-bit(I/O 1.8 V)
1.8 V1 / 2 GbXTSDQ01GLAIGA / XTSDQ02GLAIGALGA-8 6×5 mm小体积版本
料号与封装必须逐一对照 datasheet

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
3VCC 供电3.3 V 或 1.8 V 稳压,限流能力 ≥200 mA,去耦按 datasheet小负载正常、读写时复位或报错
4VCC 门控(可选)负载开关 + 主控 GPIO,允许完全断电无法做低功耗待机
5CMD / DAT 上拉CMD 与 DAT0~3 各有 4.7~10 kΩ 上拉到 I/O 电压总线浮空,总线宽度协商随机失败
6无热插拔需求SD NAND 贴片常驻,设备树用 non-removable反复探测导致启动变慢与额外功耗
最常见的一板走错:DAT1~DAT3 被当成 GPIO 或悬空

这一条造成的现象极具迷惑性: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-arzoodrivers/mmc/host/sdhci*.c支持
DesignWare MMC瑞萨、东芝、海思、博通、安霸dw_mmc + 平台 gluedrivers/mmc/host/dw_mmc*.c支持
dwcmsc(Synopsys)联发科 MT676x/MT678xdwcmsc-dwdrivers/mmc/host/dwcmsc-dw.c支持
sdhci-msm高通 MSM / 骁龙sdhci_msmdrivers/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 NAND 走的是 core/sd.c 这条路

内核的 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 …
第 ⑤ 步的 R7 校验失败最容易被忽略

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、启动中断 / DMAhost 驱动(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 时钟、总线宽度 1host 驱动
.get_ro判断是否只读无写保护引脚时返回 0host 驱动
.pre_request / .post_request请求前后钩子可用于功耗管理、DMA 映射host 驱动或平台 glue
.enable_sdma_irq / .hsq高速路径优化6.x 内核可开 HSQ 提升并发host 驱动
绝大多数 SoC 上这 9 个回调都已经写好了

这就是「不需要写驱动」的含义:这些回调由内核的 sdhci.c 或 dw_mmc.c 通用实现,SoC 厂商只提供寄存器映射、时钟、pinctrl 与电源的 glue 代码。你的板子如果用这些通用 host 驱动,改设备树就够了。

只有在 SoC 用了厂商私有 host 驱动,或 SoC 的控制器与通用实现有偏差时,才需要动这一层。届时本文第 6 章给出适配清单。

4.4 源码位置速查

想搞清楚的问题去看的文件关键符号
SD 协议怎么发的drivers/mmc/core/sd.cmmc_attach_sd、mmc_sd_init_card、mmc_decode_ext_csd
命令队列与超时drivers/mmc/core/core.cmmc_start_request、__mmc_start_timeout、mmc_uhs_...
块设备怎么来的drivers/mmc/core/block.cmmc_blk_alloc、mmc_blk_issue_rq、register_blkdev
设备树属性怎么解析drivers/mmc/core/of.cmmc_of_parse、mmc_of_dwcmsc_probe
SDHCI 通用实现drivers/mmc/host/sdhci.csdhci_send_command、sdhci_send_data
DesignWare 实现drivers/mmc/host/dw_mmc.cdw_mmc_send_cmd、dw_mmc_setup_data
卡状态与错误drivers/mmc/core/block.cmmc_blk_status、MMC_STAT_CARD
日志怎么加drivers/mmc/core/*.cpr_info / mmc_ 前缀的 dev_info
扫码打开本页
微信扫码 · 展会可扫

再分享给同事

同事可直接打开这份资料《Linux_SD_NAND_驱动开发指南》,在线阅读;完整章节请下载芯参谋。