芯参谋(86):Linux_SD_NAND_驱动开发指南

发布时间:2026/11/5 1:57:30
芯参谋(86):Linux_SD_NAND_驱动开发指南 Linux SD NAND 驱动开发指南编制日期 2026-10-28MMC 块设备路线为主线 · SPI 模式与自研路线作对照 · 面向 SoC BSP 与存储原厂联调 2026-10-04 版本号 Rev 1.0SD NAND 在 Linux 里不需要你写驱动 —— 它是标准 SD 卡接在 SoC 的 SD/MMC 控制器上内核 MMC 子系统会自动把它认成/dev/mmcblk0。真正的工作量在设备树、pinmux、供电时序和掉电安全策略上。本文档给出「先走主线 MMC、确有必要再自研」的完整落地路径与全部踩坑点。报告总结先破除最大的误解。网上大量「SD NAND Linux MTD 驱动」文章其实是把 SPI NAND 驱动改名套用。本文第 1 章用内核目录事实说明主线没有drivers/mtd/nand/raw/sdnand.cSD 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路线 AMMC 子系统 → 块设备主线能力推荐 · 量产项目唯一现实选择/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 的 0x13GET_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」这个问题拆开只有两种答案。路线 AMMC 子系统 → 块设备主线SoC 已有 SD/MMC 控制器SDHCI、dwcmsc、dw_mmc、sdhci-msm 等绝大多数 SoC 都有设备树里描述控制器 子节点内核 MMC 核心走标准 SD 协议枚举识别后注册/dev/mmcblk0直接mkfsmount你可能一行 C 代码都不写路线 B自研驱动触发条件很少主控确实没有 SD 控制器如某些 MCU、DSP、FPGA 软核或者产品需要内核块设备之外的自定义语义自定义寿命管理、端到端加密、分区密钥可以自研 MMC host复用内核 MMC 核心也可以自研裸块设备绕过 MMC 核心工作量 800~2500 行DMA、超时、并发、掉电全自己负责内核升级要重新适配两条路线工作量与风险对比差距不在代码行数在「谁来保证正确性」对比维度路线 AMMC → mmcblk路线 B自研驱动需要写的 C 代码0 行绝大多数 SoC800 ~ 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 / OCRCSD / OCR是决定/dev/mmcblk0大小与分区大小块长度 512 B固定是块设备扇区大小恒为 512SCRSD 配置寄存器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 V2.7~3.6 V或 1.8 V1.7~1.9 Vregulator 配对I/O 电压与 SoC bank 匹配CMD 无响应 / 读到全 0xFF / 枚举超时tPOR上电到首条命令≥1 ms典型 5 msregulator 使能后留足延时偶发首条 CMD 无响应重试后正常识别时钟100~400 kHz设备树max-frequency在识别阶段由内核自动降到 400 kHzCMD8/ACMD41 失败能耗异常工作时钟50 MHzHS部分器件支持更高识别成功后由内核切 High-Speed数据 CRC 错、随机读失败总线宽度4-bitHS 前提bus-width 4日志显示 1-bit带宽降到 1/4峰值电流读 15~30 mA写 / 擦 30~70 mA峰值 100 mAregulator 限流留足裕量去耦按 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 模式下为 0CMD8H→C识别SD2.0 版本协商arg0x1AA响应非法应走 v1 分支host 需正确回 R7ACMD41H→C识别操作条件协商直到 OCR Busy1超时上限要放足约 1 sCMD2 / CMD3H→C识别取 CID / 分配 RCARCA 值来自卡注意 SD NAND 可能是 0x0001CMD9H→C识别读 CSD容量、几何容量解析在此完成CMD7H→C识别后选卡进入传输态总线宽度切换前后都要保持状态机正确ACMD51 / SCRH→C配置读 SCR电压 / 时序能力决定 1.8 V 与 UHS 能力ACMD6H→C配置切 4-bit 总线失败必须能回退到 1-bit否则设备变砖CMD6H→C配置切 High-SpeedSDR失败必须能降频继续工作CMD17 / CMD18H→C读单块读 / 多块读CMD12 停止多块读必须正确处理 STOPCMD24 / 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 / SPI3.3 V2 GbXTSDG02GWSIGAWSON-8 8×6 mmSDIO 4-bit / SPI3.3 V4 GbXTSDG04GWSIGAWSON-8 8×6 mmSDIO 4-bit / SPI3.3 V8 GbXTSDG08GWSIGAWSON-8 8×6 mmSDIO 4-bit / SPI1.8 V1 GbXTSDQ01GWSIGAWSON-8 8×6 mmSDIO 4-bitI/O 1.8 V1.8 V2 GbXTSDQ02GWSIGAWSON-8 8×6 mmSDIO 4-bitI/O 1.8 V1.8 V4 GbXTSDQ04GWSIGAWSON-8 8×6 mmSDIO 4-bitI/O 1.8 V1.8 V8 GbXTSDQ08GWSIGAWSON-8 8×6 mmSDIO 4-bitI/O 1.8 V1.8 V1 / 2 GbXTSDQ01GLAIGA / XTSDQ02GLAIGALGA-8 6×5 mm小体积版本料号与封装必须逐一对照 datasheetWSON-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-bitbus-width声明 4 但实际 13VCC 供电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 / HSSDHCI高通、联发科部分、瑞昱、英伟达部分sdhci-pci/sdhci-of-dwcmshc/sdhci-of-arzoodrivers/mmc/host/sdhci*.c支持DesignWare MMC瑞萨、东芝、海思、博通、安霸dw_mmc 平台 gluedrivers/mmc/host/dw_mmc*.c支持dwcmscSynopsys联发科 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-supplyI/O 电压与vmmc-supply核心电压两个 regulator1.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 NANDSD NAND 走的是 core/sd.c 这条路内核的 SD 协议实现写在drivers/mmc/core/sd.cSD/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 0x01Idle ⑤ CMD8 (arg0x1AA) → 期望 R7 回显 0x1AAv2.00 失败则走 v1.x 分支无 CMD8 ⑥ CMD55 ACMD41 循环 直到 OCR[31] 1Busy 超时上限约 1 s器件实际通常几十 ms 完成 ⑦ CMD2 → 取 128 bit CID ⑧ CMD3 → 卡分配 RCASD 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_probeSDHCI 通用实现drivers/mmc/host/sdhci.csdhci_send_command、sdhci_send_dataDesignWare 实现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_info5 · 设备树完全指南这一章是路线 A 的主战场。设备树写对了设备就通了写错了表现为「没反应」「只有 1-bit」「一 mount 就崩」。本章给出可直接使用的完整示例与错误对照表。5.1 控制器节点必填属性属性作用SD NAND 推荐取值写错的后果status节点使能okaydisabled则完全不存在bus-width数据总线宽度4写 1 则吞吐掉到 1/4max-frequency最大工作时钟Hz50000000HS超规格则数据 CRC 错 / 枚举失败non-removable标记常驻设备存在不加则反复探测卡检测引脚启动变慢、功耗上升cap-sd-highspeed声明支持 SD High-Speed存在不加则停在默认速 25 MHzvmmc-supply核心供电 regulator指向板级稳压节点不配则用默认电源可能限流不足vqmmc-supplyI/O 供电 regulator1.8 V 器件必配I/O 电平不对则识别失败disable-wp禁用写保护引脚检测存在SD NAND 无 WP 脚无此引脚时不配会有告警cd-gpios卡检测引脚不配用 non-removable配了常有效电平也能工作但不如 non-removable 干净pinctrl-0/-names引脚复用指向 4-bit MMC 引脚组DAT1~3 未复用则只跑 1-bitclocks/clock-names控制器时钟指向 SoC 的 MMC 时钟源时钟错误则控制器工作异常5.2 SD NAND 相关能力属性详解non-removableSD NAND 贴片常驻没有插入拔出事件。加这个属性后内核不再监听卡检测脚枚举一次就结束。cap-sd-highspeed声明控制器与卡都支持 SD High-Speed50 MHz。SD NAND 支持此模式。不加则停在默认速 25 MHz。cap-sd-uhs-sdr50等 UHS 属性SD NAND 一般不支持 UHS 模式不要加。加了内核会去尝试协商 SDR50失败后回退虽不至于不通但会留下误报日志。no-1-8-v如果 SoC 支持 1.8 V 切换而板上是 3.3 V 器件加这个属性避免内核做无用的电压切换尝试。sdhci-caps-mask限速神器。内核默认会尝试从最高速率往下协商把不需要的模式掩掉可以让启动更干净。例如只跑 SD HSsdhci-caps-mask 0x00000007 0x00000000。cap-power-off-card声明支持断电。配合 VCC 门控电路时内核可以在 suspend 时真正断电第 11 章展开。5.3 可直接使用的完整示例5.3.1 3.3 V SD NANDWSON-8完整节点soc { sdhci0: mmc0: mmcfe000000 { compatible arm,pl011-dw-apb-uart; /* 占位按 SoC 实际改 */ /* 上面这行必须是该 SoC 对应的 host 驱动 compatible */ reg 0x0 0xfe000000 0x0 0x1000; interrupts 0 12 IRQ_TYPE_LEVEL_HIGH; clocks clk_mmc; clock-names ahb; /* 4-bit 总线建议 50 MHzHigh-Speed */ bus-width 4; max-frequency 50000000; /* SD NAND 贴片常驻 */ non-removable; /* 支持 SD High-Speed */ cap-sd-highspeed; /* 不做 1.8 V 切换3.3 V 器件 */ no-1-8-v; /* 声明支持断电配合 VCC 门控 */ cap-power-off-card; /* SD NAND 无 WP 脚 */ disable-wp; /* 电源 */ vmmc-supply vdd_sd; vqmmc-supply vdd_sd_io; /* 限速到 SD HS避免内核尝试更高速率 */ sdhci-caps-mask 0x00000007 0x00000000; /* 引脚复用 */ pinctrl-names default; pinctrl-0 sd0_mmc_pins; status okay; }; /* 供电能力要留足峰值 100 mA建议 ≥200 mA */ vdd_sd: vdd-sd { compatible regulator-fixed; regulator-name vdd-sd; regulator-min-microvolt 3300000; regulator-max-microvolt 3300000; regulator-enable-gpios gpio1 5 GPIO_ACTIVE_HIGH; }; }; pinctrl { /* 必须是 4-bit 组CLK CMD DAT0~3 */ sd0_mmc_pins: sd0-mmc { pinmux PINMUX_MMC0_CLK PINMUX_FUNC_SDMMC0 7, PINMUX_MMC0_CMD PINMUX_FUNC_SDMMC0 7, PINMUX_MMC0_DAT0 PINMUX_FUNC_SDMMC0 7, PINMUX_MMC0_DAT1 PINMUX_FUNC_SDMMC0 7, PINMUX_MMC0_DAT2 PINMUX_FUNC_SDMMC0 7, PINMUX_MMC0_DAT3 PINMUX_FUNC_SDMMC0 7; }; };说明compatible、reg、interrupts、clocks必须按 SoC 手册改。pinmux 宏名各 SoC 不同。这段是模板不是可直接编译的代码。5.3.2 用 GPIO 控制负载开关VCC 门控的写法soc { mmc0: mmcfe000000 { /* ... 同 5.3.1 ... */ vmmc-supply vdd_sd_gated; cap-power-off-card; non-removable; }; /* 带使能脚的负载开关 regulator */ vdd_sd_gated: vdd-sd-gated { compatible regulator-gpio; regulator-name vdd-sd-gated; regulator-min-microvolt 3300000; regulator-max-microvolt 3300000; enable-gpios gpio1 5 GPIO_ACTIVE_HIGH; enable-active-high; startup-delay-us 1000; /* 关键等待 tPOR */ }; }; /* 用 mmc-pwrseq 显式控制上电顺序配合 SD NAND 的 VCC 严格时序 */ mmc0 { mmc-pwrseq sd_nand_pwrseq; }; sd_nand_pwrseq { compatible mmc-poweroff-sequence; pwrseq-tokens reset-off, vdd-sd-power-on, reset-on; reset-off-ms 10; vdd-sd-power-on-delay-ms 2; /* 必须 tPOR */ }; vdd_sd_gated { vdd-sd-power-on-supply vdd_3v3; /* 上游 3.3 V */ }; /* 顺序标签定义部分内核版本需要命名以 SDK 为准 */ pwrseq_reset_off { reset-off-gpios gpio1 6 GPIO_ACTIVE_LOW; };mmc-pwrseq 的语法各版本有差异设备树里的mmc-pwrseq属性在 Linux 4.x~6.x 之间经历过多轮调整早期用pwrseq-tokens*-delay-us较新版本改为reset-gpios、vmmc-supply-delay-us等更细的字段。写之前先看内核源码drivers/mmc/core/of.c里的mmc_of_parse_mmc_pwrseq()按你手上这版内核实际支持的字段名写。多数情况其实不需要写 pwrseq—— regulator 的startup-delay-us加上内核自带的 74 时钟已足够。5.4 设备树常见错误对照表症状最可能的原因检查点修复完全没有 mmc 相关日志节点statusdisabled或 host 驱动未编译dtb 是否真的被加载cat /proc/device-tree/...改 status确认CONFIG_MMC_xxxmmc0: Timeout waiting for hardware interruptCMD 线浮空 / 电压不对 / tPOR 不足示波器看 CMD 是否有响应量 VCC 实际电压加上拉改 regulator 电压加 startup-delaymmc0: error -84 while trying to send r/w commandCRC 错通常是信号完整性降max-frequency到 25 MHz 试由低到高逐级提速定位临界频率查布线能识别但只有 1-bitDAT1~3 pinmux 未配 / ACMD6 失败cat /sys/kernel/debug/mmc0/ios配 4-bit pinmux 组量 DAT2/DAT3 翻转能识别但只有 25 MHz缺cap-sd-highspeed或 CMD6 切换失败cat /sys/kernel/debug/mmc0/ios看 clock加cap-sd-highspeed查 R7 回显Got data interrupt 0x... even though no data operation was in progress残留中断状态host 驱动未清干净看这条之前是否有 CRC 错先解决真正的 CRC 错这条是后果不是原因mmcblk0: error -84写入时随机失败供电不足 / 峰值电流超限流量 VCC 在写负载下的跌落波形提高 regulator 限流加强去耦写入时报readonly内核判定卡只读看设备树有无wp-gpios而该引脚悬空改用non-removable 去掉 wp-gpiosDevice or resource busy挂载失败分区表被占用 / 已有分区lsblk/cat /proc/partitions确认设备名与分区号改了 dtb 但行为没变加载的不是你改的那份 dtbls -l /boot/dtb/启动日志里的 dtb 路径改对文件某些平台需重新打包 boot.img/sys/kernel/debug/mmc*/ios 是最有用的一行命令它直接打印内核当前认为的时钟、总线宽度与时序模式cat /sys/kernel/debug/mmc0/ios clock: 50000000 Hz actual clock: 49631200 Hz vdd: 18 19 20 21 22 23 24 25 26 27 28 29 30 31 bus mode: 2 (4-bit) timing spec: 3 (sd high-speed) signal voltage: 0 (3.30 V) driver type: 0 (driver type B)需要先mount -t debugfs none /sys/kernel/debug。排查「为什么只有 1-bit」「为什么没提速」时先看这四行bus mode、timing spec、actual clock。后续章节请下载「芯参谋」查看……篇幅原因, 还有10章· 详见芯参谋→方案设计打开路径解决方案应用市场分析 - 原理方案设计 - 嵌入式驱动系统开发 - Linux_SD_NAND_驱动开发指南 下载芯参谋免费查看完整报告网址www.myxincm.com相关报告 · 1雾化器(Nebulizer)_市场分析市场应用分析信创硬件(XC Hardware)_市场分析市场应用分析分区错位_坏块跳过不一致_量产排查调试解决方案FreeRTOS_系统开发指南原理方案设计炬芯(Actions)平台_SPI_NOR_量产排查调试解决方案相关报告 · 2博流(BouffaloLab)平台_SPI_NOR_QSPI_量产排查调试解决方案存储接口信号采样与时序要求_技术探讨原理方案设计锂电打草机(Cordless Grass Trimmer)_市场分析市场应用分析兆易创新(GigaDevice)平台_SPI_NOR_QSPI_样机排查调试解决方案翻译笔(Translation Pen)_市场分析市场应用分析