只读分享解决方案文档原理方案设计📅 2026-10-14🔖 Rev 6⏳ 还有3日到期
🔧解决方案&应用市场分析 -> 原理方案设计 -> 嵌入式驱动&系统开发 -> Linux_PPI_NAND_驱动开发指南

Linux_PPI_NAND_驱动开发指南

同系列 · 原理方案设计
扫码打开本页
微信扫码

Linux 并口 NAND(PPI NAND)驱动开发指南

编制日期 2026-10-14

MTD / rawnand 路线全解 · 控制器驱动三条路线 · 设备树 · ECC 与坏块 · UBI 落地

3
控制器驱动路线
硬件 NFC / GPIO 模拟 / 自研
16
章节
含 7 个附录速查
28
张内联图
架构 / 时序 / 决策树 / 流程
0
必需自写 C 代码行数
若走 gpio-control-nand 路线
PPI NAND 在 Linux 里的三条归宿:主线能覆盖前两条,第三条要你自己写SoC 侧引脚资源I/O0~7 / CLE / ALE / CE# / WE# / RE# / R-B# / WP#并口 NAND 器件(裸片)NAND 阵列 + 片内控制器(无 FTL/ECC)路线 A:SoC 原生 NFC 控制器首选 · 性能与可靠性最好SoC 内部NFC 硬件厂商或主线host 驱动nand_chipexec_op/dev/mtd0 + /dev/mtdblock0DMA 搬运 + 硬件 ECC 引擎读 20~40 MB/s,写 8~20 MB/s典型:sunxi-nand / mxc_nand / fsl_nand工作量:写设备树 + 调试 pinmux/时序路线 B:gpio-control-nand主线自带 · 无需写代码SoC 内存映射总线窗口GPIO 翻转CLE/ALE/CE#软件 ECC或 hwecc/dev/mtd0内核配置:CONFIG_MTD_NAND_GPIO文件:drivers/mtd/nand/raw/gpio.c读 1~3 MB/s,写 0.5~1.5 MB/s工作量:0 行 C,仅设备树路线 C:自研 nand_controller仅当前两者都走不通时自研 host驱动自研 ECC或外挂引擎自研 BBM策略/dev/mtd0 或自定义节点DMA、超时、并发、掉电全自己写需长期跟随内核 rawnand API 演进适用:总线时序需非标调整 / 特殊 LUN 拓扑工作量:600~2000 行 C 代码关键事实:PPI NAND 对内核呈现为 /dev/mtdN 字符设备或 mtdblock 块设备,不存在 /dev/mtdN 之外的标准块设备节点
图 1 · PPI NAND 在 Linux 里的三条归宿
同系列四类 Flash 对内核暴露的东西完全不同,驱动不能互相套用SPI NOR块设备/dev/mtdblock0mtd_blktrans· 主机算 CRC 或用控制器 ECC· 无坏块· 无 OOB 概念SPI NANDMTD 原始设备/dev/mtd0/dev/mtdblock0· 主机 ECC(软件或硬件)· 有坏块片内标记· 有 OOB64~224 BPPI NANDMTD 原始设备/dev/mtd0/dev/mtdblock0· 主机 ECC(软件或硬件)· 有坏块位置不统一· 有 OOB16~256 BSD NANDMMC 块设备/dev/mmcblk0· 片内 FTL 已做· 片内管理· 对主机不可见三者共同点:都是 /dev/mtdN;内核 rawnand 核心代码完全共用差别集中在 host 驱动(怎么搬数据、怎么算 ECC)与器件几何表(idt / bbt / eccpos)最容易犯的错:把 PPI NAND 当 SPI NAND 处理两者命令编码不同(并口是 80h/10h 两段式,SPI 是 02h→10h 三段式),几何也不同,套用必然读出乱码
图 2 · 同系列四类 Flash 的内核接口面对照

这份文档的核心主张:并口 NAND 在 Linux 下有现成的主线框架可走,先确认你的 SoC 有没有硬件 NFC 控制器。有就走厂商 host 驱动,没有就用主线的 gpio-control-nand,两条路都不通才需要自研——自研的代价(DMA、超时、并发、掉电保护)远大于多数人预期。本文把「什么时候必须自研」「自研要付多少代价」写清楚,而不是鼓励自研。

报告总结

并口 NAND(也写作 PPI NAND / Parallel NAND / Raw NAND)是容量与成本最优的一类非易失存储,在工控、路由器、IoT 网关、国产化替代的主控板上大量使用。但和 eMMC、SD NAND 不同,它在 Linux 里没有一个「插上就能用」的通用块设备路径:内核只提供 MTD 框架和几套 host 驱动,剩下的要靠设备树和几何表说清楚。

本文回答的问题:

  • PPI NAND 和 SPI NAND 到底差在哪,为什么驱动不能互相套用;
  • 你的项目应该走哪条控制器路线,各自的代价是什么;
  • 设备树怎么写、哪些属性是必填、常见错法长什么样;
  • ECC 引擎怎么选、OOB 怎么排、XTX XT27G02A/B 的坏块标记为什么不在 OOB 里;
  • UBI 的 min_io_size 对齐陷阱,以及 UBIFS 参数怎么算;
  • 出现「探测不到」「ECC 报错」「UBI attach 失败」时怎么定位。

读者路径

路线 A · 首次移植
从硬件到挂载一条路走完
按第 3 章查硬件 → 第 5 章定路线 → 第 6 章写设备树 → 第 7/8 章配 ECC 与坏块 → 第 9 章走完落地流程 → 第 10 章做分区与格式化。
预计阅读 2 小时
路线 B · 出问题了在排查
直接跳到第 14 章
按现象查速查表,每条给出根因与验证命令,不做推导。
预计阅读 15 分钟
路线 C · 要做自研
重点读第 5.3 与第 4 章
先看 nand_controller 接口要实现哪几个回调、核心在调用它们时保证了什么,再评估工作量是否可接受。
预计阅读 1 小时
路线 D · 与本系列其它文档配套
硬件与软件规范已经在别处
器件几何、OOB 布局、坏块标记、ECC 分级策略以《并行NAND_PPI_NAND 软件设计规范》为准,本文只讲 Linux 侧如何落地上层软件。
预计阅读 40 分钟
配套文档与本文的分工

本文是Linux 主机侧指南。器件层面的内容不在这里重复造轮子:

  • 引脚电气、走线、R/B# 上拉电阻取值 → 《并行NAND_PPI_NAND 电路设计指南》
  • 命令编码细则、OOB 通用布局、ECC 分级与迁块阈值、BBT 格式、掉电场景分析 → 《并行NAND_PPI_NAND 软件设计规范》
  • Linux 内核侧怎么用、驱动怎么写、设备树怎么配、出错怎么查 → 本文

1 · 先把事实说清楚

在写第一行驱动代码之前,有四件事必须先确定: 它在物理上是什么、它在 Linux 里以什么形态存在、它和本系列其它器件差在哪、 以及它的可靠性责任归谁。这四件事里有任何一件搞错, 后面所有的工作都会建立在一个错误的前提上,而这种错误的代价往往要到量产才暴露。

1.1 并口 NAND 到底是什么:一颗没有封装外壳的 SPI Flash

先把这个比喻说清楚,因为它能省掉后面很多困惑。 并口 NAND 与 SPI NAND 的关系,好比「并口硬盘」与「USB 硬盘」: 里面都是 NAND 存储阵列 + 一颗片内控制器,差别只在外面怎么接线、 主机怎么跟它说话。

「并口」指的是数据总线直接挂在芯片引脚上,典型 x8(8 根数据线) 或 x16(16 根),加上 CLE、ALE、CE#、WE#、RE#、R/B#、WP# 共 7 根控制线, 一共 15~23 根信号。主机通过这套并行接口逐字节地把命令、地址、数据打进器件。 「SPI」则是把这套接口串行化成 4 根线(SCLK、MOSI、MISO、CS), 代价是带宽低,换来的是布线简单、能跑高时钟。

它不是一颗「带外壳的芯片」,而是一片必须自己伺候的存储裸片裸 die晶圆切割后未封装只有芯片厂内部流转,你拿不到TSOP / BGA带引脚或焊球的封装你能买到,但引脚数 48~200 不等SON / CSP小尺寸贴片同样可买,尺寸更小对比:SPI NAND 长什么样并口 NAND:8 根数据线 + 6 根控制线= 至少 20 根信号,占引脚与布线资源SPI NAND:4 线(SCLK/MOSI/MISO/CS)= 省引脚、省布线,但协议更复杂在电路板上,它长这样主控 SoC含 NFC 或无 NFCI/O0~I/O7(8 根)CLE / ALE / CE# / WE# / RE# / R-B#PPI NAND 器件2~8 Gb,x8 或 x16物理形态带来的三个直接后果它没有 FTL,坏块不会自动屏蔽——主机必须自己扫、自己记、自己避开(第 8 章)它没有 ECC 纠错——数据完整性完全由主机侧的 OOB 校验负责(第 7 章)它没有磨损均衡——同一块被反复写就会提前失效,这就是 UBI 存在的理由(第 10 章)一句话:并口 NAND 的一切麻烦,都源于它把「可靠性」的责任从片内搬到了主机。
图 3 · PPI NAND 的物理形态与它在板上的位置
维度并口 NAND(PPI)SPI NAND
数据线数量8 或 16 根1 根双向(MOSI/MISO 共用一根)
控制线数量7 根(CLE/ALE/CE#/WE#/RE#/R-B#/WP#)1 根(CS)
主机接口SoC 的 NFC 控制器或 GPIO 模拟SPI 控制器
单线带宽一次并行传 8/16 位每次传 1 位
可达吞吐高(受总线与控制器限制)受 SPI 时钟限制,通常 < 50 MHz
布线复杂度高,14~23 根等长走线低,4 根
引脚占用多,可能与其它外设抢引脚少
典型应用工控、网关、国产化主控板、大容量低成本存储小型终端、消费电子、SD 卡替代
为什么「引脚占用」是个真实的选型约束

很多 SoC 的引脚预算是紧的。一个 32 引脚的 QFN 封装主控, 留给外部存储的引脚可能只有十几根——并行 NAND 单独就要占掉 20 个左右。 这时候即使并口 NAND 在成本和容量上再划算,也只能选 SPI NAND 或 SD NAND。 评估并口 NAND 方案时,第一步应该数引脚,而不是比价格。

1.2 Linux 主线的位置:MTD/rawnand,不是块设备

这是并口 NAND 在 Linux 里最容易被误解的一点,也是一系列后续困惑的根源。 在 Linux 里,并口 NAND 不是块设备。 内核不会给它创建 /dev/sda 这样的节点, 也不会把它挂成某种可以直接 mount 的普通文件系统。

它挂在 MTD(Memory Technology Device) 子系统下面, 对外表现为两种设备:

设备节点类型用途谁在用
/dev/mtd0字符设备按页 / 按 OOB 精确读写,绕过缓存与文件系统nanddump、nandwrite、烧写工具
/dev/mtdblock0块设备按块读写,不含 OOB 与坏块信息dd、文件系统、UBI

内核里的实现路径是:drivers/mtd/nand/(6.5 起为 drivers/mtd/nand/raw/)里的 rawnand 框架, 加上 SoC 厂商的 host 驱动(或主线的 gpio-control-nand)。 rawnand 之上可选地叠 UBI 与 UBIFS,得到一个真正的文件系统。 这五层的责任划分,是理解后面所有章节的基础。

每一层「以为自己不用管可靠性」的地方,都是别人在替你管应用 / 文件系统open / read / write谁在管:文件系统的日志与一致性UBI 磨损均衡层把逻辑块映射到物理 PEB谁在管:动态坏块替换、擦写均衡MTD 抽象层mtdblock / mtd 字符设备谁在管:ECC 编排、静态坏块表控制器 host 驱动exec_op / DMA / 超时谁在管:时序、状态机、错误恢复NAND 器件(裸片)阵列 + 片内控制器谁在管:物理磨损、随机缺陷没有任何一层兜住的情况掉电:写操作横跨「擦除 → 编程 → 更新索引」三步,断电可能落在任意一步之间。对策:日志文件系统(UBIFS)+ 掉电通知(PM / PFO)本系列文档负责的范围第 3~6 章:第 2 层以上的接线(硬件前提、host 驱动、设备树)第 7~8 章:第 3 层的正确配置(ECC、坏块)第 10 章:第 2 层的启用(UBI / UBIFS)
图 4 · 从应用代码到 NAND 晶粒的完整栈与责任划分
「它不是块设备」带来的三个具体后果
  • 不能直接 mount 一个裸 MTD 分区当 rootfs。 裸分区上没有文件系统,mount 会报 wrong fs type。 要么用 JFFS2(它支持直接挂裸 MTD),要么先建 UBI 再建 UBIFS。
  • 不能指望内核做磨损均衡。 你往 /dev/mtdblock0 里连续写 1 GB,同一个物理块会被反复擦写, 而同一条命令对 NAND 芯片的其它块毫无影响。这就是第 10 章要引入 UBI 的原因。
  • 不能假设「写成功了」等于「能读回来」。 写路径的 ECC 字节如果没配对,下次读时校验会失败, 而这个失败是延迟的——写的时候不报错。

1.3 三条技术路线与本系列其它器件的根本差异

Linux 生态里有四类主流的非易失 Flash:SPI NOR、SPI NAND、PPI NAND、SD NAND(eMMC 那一类)。 它们在主机侧的接口形态完全不同,驱动代码不能互相套用。 这也是本系列每类器件单独出一份指南的原因。

器件类型内核框架用户态接口有无 OOB有无坏块有无内置 FTL
SPI NORMTD / spi-nor/dev/mtd0无无无(但有 OTP 区)
SPI NANDMTD / rawnand + SPI 层/dev/mtd0有(64~224 B)有无
PPI NAND(本文)MTD / rawnand + NFC 或 GPIO/dev/mtd0有(16~256 B)有无
SD NAND / eMMCMMC 框架/dev/mmcblk0片内,不暴露片内管理有

把这张表横着看,可以得到三条最容易被忽略的差异:

差异一:OOB 是并口与 SPI NAND 独有的

SPI NOR 与 SD NAND 都不暴露 OOB 概念——它们的数据是连续字节流,主机读写 「地址 + 长度」就完事。而 PPI NAND 与 SPI NAND 每一页尾部都带一段 OOB (也叫 Spare Area),用来存 ECC 字节、坏块标记、厂商元数据。 OOB 的存在让「读一页」变成了「读主区 + 读 OOB」两个动作, 而两者的读取方式在硬件上并不相同——这正是第 4 章 exec_op 模型要解决的问题。

差异二:SPI NOR 与 SD NAND 没有坏块概念

SPI NOR 是 NOR 结构,擦写单位是整个扇区,没有「块」这个中间层级, 因此不需要坏块管理。SD NAND 与 eMMC 有片内 FTL,把坏块与磨损均衡全包了, 主机看到的是一个干净的块设备。只有 PPI NAND 与 SPI NAND 需要主机操心坏块, 而这两者的坏块标记位置还互不相同(一个是 OOB 首位,一个可能别的地方), 这也是第 8 章要专门讲一节的原因。

差异三:SD NAND 有一个「能用的块设备」,PPI NAND 没有

这是选型时最实际的差别。SD NAND 插上就是块设备,格式化即用; PPI NAND 插上是一堆 /dev/mtdN,要自己决定用不用 UBI、用什么文件系统。 前者是「集成成本低但单价高」,后者是「单价低但集成工作量大」。 本文档要讲清楚的,就是这部分集成工作的具体内容与代价。

防混用:本系列四份驱动指南的边界

常见错误是把 SPI NAND 驱动开发指南里的东西直接搬到并口 NAND 上。至少有三处必错:

  • 页编程命令不同。并口是 80h → 10h 两段式, SPI 是 02h → 10h 三段式,还多一对 0Fh / 1Fh Feature 命令。
  • 随机访问的编码不同。并口是 05h … E0h, SPI 是 2Ch / 3Dh 系列。
  • badblock_pattern 的偏移基准不同。并口侧 OOB 偏移是相对 OOB 首字节, SPI 侧还有页内偏移的换算,写错一个字节就会读不到坏块标记。

具体的逐项对照见附录 E。

读完第 1 章你应该能回答
  • 为什么我的并口 NAND 上不会出现 /dev/sda?
  • /dev/mtd0 与 /dev/mtdblock0 的区别是什么,各自谁在用?
  • 为什么并口 NAND 不能直接当 rootfs 挂载?
  • 并口 NAND 与 SPI NAND 最容易搞混的三处是什么?
  • 为什么说「评估方案先数引脚」?

这五个问题答不上来就直接看第 3 章,会漏掉关键前提。

2 · 器件与接口面

在讨论任何驱动代码之前,先把器件这一侧的「事实」摆清楚。驱动里所有的几何表、时参数、坏块偏移都来自本章,而这些值一旦错了,症状往往是「能识别但数据错」,比「识别不到」难查得多。

2.1 PPI NAND 的关键几何与能力参数

「几何」指器件的物理组织结构。内核靠一组几何参数把「行地址 / 列地址」翻译成「第几块第几页」,所有几何参数都直接来自器件 datasheet 或 Read ID 后的能力表,不能猜。

参数含义典型值(x8 SLC)配错的后果
page_main每页主区数据字节数512 / 2048 / 4096UBI 的 min_io_size 错、UBIFS 全部参数连带错
page_oob每页 OOB(Spare)字节数16 / 64 / 128 / 218ECC 写越界、坏块标记读不到、UBI 头写不下
pages_per_block每块页数32 / 64 / 128块大小算错,分区表整体偏移
blocks_per_lun每 LUN 块数2048 / 4096容量算错,rootfs 挂载位置偏
address_cycles地址周期数与列/行分配5(2 列 + 3 行)读到错误页,或器件无响应
on_die_ecc是否有片内 ECC 引擎无 / 8 bit per 528 B主机重复算 ECC 反而破坏数据;或该纠的不纠
bb_marker坏块标记位置与判据OOB 首字节 / Main 首列漏判坏块,数据静默损坏(最严重)
tR页读忙时间(typ / max)25 µs / 100 µs超时报错,或轮询不够导致读出错数据
tPROG页编程忙时间(typ)300 µs同上
tBERS块擦除忙时间(typ)3.5 ms同上
几何参数配错是最危险的一类错误

识别失败会立刻暴露,几何配错不会:驱动会「正常」地读出数据,只是数据属于错误的页或错误的块。更糟的是,如果 OOB 大小配小,坏块标记就落在你以为的 OOB 范围之外,所有出厂坏块都会被当成好块——文件系统把数据写进去,然后再也读不出来。

因此第 8 章的坏块扫描必须实际跑一遍并打印结果,不能假设。

2.2 引脚与信号面:x8 与 x16 的差别在哪

「x8」「x16」指数据总线宽度:x8 是 I/O0~I/O7 共 8 根,x16 是 I/O0~I/O15 共 16 根。在设备树里体现为 nand-bus-width 取 8 或 16。这不只是「多几根线」:

维度x8x16
数据引脚数8 根(I/O0~7)16 根(I/O0~15)
列地址周期2048 B 主区需 2 个 CA同样主区可能只需 1 个 CA(视器件定义)
单芯片容量上限通常做到 4~8 Gb通常做到 2~4 Gb
控制器支持情况绝大多数 SoC NFC 都支持不少 SoC 只支持 x8
Linux 属性nand-bus-width = <8>nand-bus-width = <16>
x16 不是 x8 的简单升级

把 x8 器件按 x16 读,最典型的现象是能探测到器件、能读出 ID,但读数据全是 0xFF 或 0x00。 反过来把 x16 按 x8 读,地址周期数不对,会读到相邻页或直接触发未定义行为。

如果 SoC 的 NFC 控制器不支持 x16 而器件是 x16 位宽, 不要试图用软件位拆来凑——地址周期与时序都会错,正确做法是换器件型号。

x8 并口 NAND 的引脚分组:只有 CLE/ALE/CE# 三根控制线决定当前总线上的字节是命令、地址还是数据并口 NAND 器件(x8)内部:NAND 阵列 + 内部控制器 + 地址计数器命令锁存器(CLE 有效时)地址锁存器(ALE 有效时)数据寄存器(两者都无效时)页缓存寄存器(Page Buffer)读命令后先整页搬进这里,主机再从 RE# 取数据片内 ECC 引擎(部分型号有)坏块标记区(位置依型号而异)CLE命令锁存使能,高有效ALE地址锁存使能,高有效CE#片选,低有效I/O0 ~ I/O78 根双向数据线上电必须配成 GPIO或总线复用,不能浮空WE#写使能,低有效RE#读使能低有效每根脉冲吐 1 字节WP#写保护低有效,擦除期间必须拉高R/B#(Ready / Busy)开漏输出,必须外接上拉忙时拉低、完成后释放为高内核靠它判断完成,不接只能软件轮询主机侧实现方式(决定你走哪条路线)路线 A:SoC 的 NFC/EBI/FSMC 硬件自动产生 CLE/ALE/WE#/RE# 时序,主机只写命令字节到寄存器路线 B:数据线挂到 SoC 内存映射窗口(读写即产生 WE#/RE#),CLE/ALE/CE#/R-B#/WP# 用 GPIO 手动翻转
图 5 · x8 并口 NAND 的引脚分组与信号方向

2.3 ONFI 异步命令集与时序前提

ONFI(Open NAND Flash Interface)定义了并行 NAND 的标准命令集,绝大多数市面器件在基础命令上兼容 ONFI,扩展命令各家自定义。内核里没有一份「ONFI 命令表」可以直接填,几何与命令语义是通过 struct nand_chip 的能力表和 host 驱动的 exec_op 回调共同表达的。

命令编码作用内核侧的常见用法
Read Page00h … 30h整页读入片内缓存并校验nand_read_page() 的默认路径
Change Read Column05h … E0h页内随机定位(配合 05h 重定位)读 OOB、读指定列区间
Read Cache31h多页连读(提高吞吐)部分 host 驱动启用(rand/seq read)
Read Cache End3Fh结束 Cache 连读与 31h 成对
Block Erase60h … D0h擦除一个块nand_erase() 的默认路径
Read Status70h读状态寄存器(OIP / Fail / ECC 位)擦除编程后二次确认
Page Program80h … 10h两段式:先把数据装缓存,再提交nand_write_page() 的默认路径
Copyback Program85h … 10h页内/页间内部搬移,不经主机少用,需按器件支持情况开
Read ID90h读厂商 ID / 器件 ID探测与能力表匹配
ResetFFh软复位,清除状态机上电初始化第一步、异常恢复
Read Parameter PageECh读 ONFI 参数页(256 B)ONFI 器件的几何来源
Get / Set FeaturesEEh / EFh读改写器件配置特性必须 RMW,慎用
Read Status Enhanced78h多 LUN 场景的带 LUN 状态读多 die 器件才需要
80h/10h 两段式不是 SPI NAND 的 02h/10h 三段式

并口 NAND 的页编程是 80h(列地址)→ 数据 → 10h(行地址) 两段式;而 SPI NAND 是 02h(load)→ 10h(confirm),并且 SPI 侧还有 0F/1F 这对 Feature 命令。两者编码空间部分重叠但语义不同,把 SPI NAND 的 0F/GET_FEATURE 用到并口器件上会得到完全错误的结果。

并口器件上 Get Features(EEh) / Set Features(EFh) 是另一套东西,而且很多传统并行器件(包括 XTX XT27G02A/B)根本没有这两个命令。

时序参数ONFI 含义Mode 0 要求(典型)谁负责满足
tCLSCLE 建立时间≥ 50 nsSoC 的 NFC 或 GPIO 翻转速度
tALSALE 建立时间≥ 50 ns同上
tWPWE# 脉宽≥ 50 ns总线写周期决定
tWHWE# 高电平时间≥ 30 ns同上
tRPRE# 脉宽≥ 50 ns总线读周期决定
tRCRE# 周期≥ 100 ns同上
tCCS命令到命令间隔≥ 70~100 ns软件插入的 ndelay 决定
tR页读忙时间typ 25 µs / max 100 µsR/B# 等待,不能靠固定延时
tPROG页编程忙时间typ 300 µs同上
tBERS块擦除忙时间typ 3.5 ms同上
ONFI 异步 Read Page(00h … 30h)逐拍时序:注意 30h 之前必须完成 5 个地址周期CE#CLEALEI/O0-7R/B#低1高仅在 00h 这一拍11Col0 Col1 | Pa0 Pa1 Pa2(x8 / 2Gb / 2048+64)00h地址 5 字节30h数据输出:Main 区 2048 B(按 RE# 每字节一拍)内部搬页期间拉低,完成后释放为高(tR,typ 25 µs)① 命令② 地址③ 确认④ 等待地址周期数必须由器件几何决定:x8 + 2048 B Main 只需 2 个列地址,2048 B 需要 3 个 —— 用错周期数会读到错误页甚至报错对应到内核代码00h + 地址 + 30h 这一整串,在 rawnand 核心里是一个struct nand_operation,由 chip->ops->exec_op() 发出。驱动写的是「指令序列」,不是「发几个字节」。顺序由核心决定,驱动不能改 —— 这是与老式cmd_ctrl() 式驱动的根本区别。常见错误· 把 30h 写成 31h(那是 Read Cache)· 地址周期按 x16 算成 2 个列地址· 等待 R/B# 用固定 usleep 而不是等引脚· 读 OOB 时按 Main 同址算,实际是列地址 +OOB 偏移
图 6 · ONFI 异步 Read Page 命令周期逐拍时序

上表数值为 ONFI 异步 Mode 0 的典型要求,具体到你的器件必须以 datasheet 为准。注意 tR / tPROG / tBERS 这三项由器件内部决定,主机无法加速,只能等。

2.4 芯天下 XTX XT27G02A / XT27G02B 型号对照

本系列器件在 Linux 侧使用时的关键差异点集中如下。这些值覆盖本章的通用表述,落地时以本表替换第 2.1 / 2.3 节的示例值。

参数XT27G02AXT27G02B
容量 / 组织2 Gb(256M × 8)2 Gb(256M × 8)
接口传统并行(非 ONFI Async 命令集之上的 ONFI 扩展)同左
电压3.3 V3.3 V
数据位宽x8x8
页(Main + Spare)2048 + 128 B2048 + 64 B
页寄存器大小2176 B2112 B
每块页数64 页 × 2048 B64 页 × 2048 B
块大小128 KiB128 KiB
地址周期2 个列地址(CA0–CA11)+ 3 个行地址(PA0–PA16)= 5同左
ECC主机侧实现,8 bit / 544 B(每页 4 个子页)片内 ECC 引擎,8 bit / 528 B
主机需要的 ECC 强度8 bit / 544 B0 bit(片内已纠)
坏块标记位置Main 区第 0 列(Column 0),值 00h同左
随机访问05h–E0h 随机列地址变更 / 85h 随机写同左
ONFI 参数页 ECh不适用,禁止发送不适用,禁止发送
Get / Set Features(EEh / EFh)不适用,禁止发送不适用,禁止发送
ECC 状态读取无(由主机 ECC 引擎给出结果)专用 ECC Status Read 命令,不是 70h 的 ECC 位
tR 典型 / 最大25 µs / 100 µs同左
tPROG 典型300 µs同左
tBERS 典型3.5 ms同左
XT27G02A/B 的三条强制特例

1. 坏块标记不在 OOB,而在 Main 区第 0 列,值 00h。手册原文:坏块标记是整页级的,请读取每块中任意一页的一列数据,若该列数据为 00h 则该块为坏块。这与「扫 OOB 首字节是否非 0xFF」的通用做法完全相反,用通用做法会漏判全部出厂坏块。详见第 8.3 节。

2. 禁止发送 ECh / EEh / EFh。这两款是传统并行 NAND,手册中无 ONFI Parameter Page 与 Get/Set Features,发送这些命令器件响应无定义,可能进入未定义状态。若驱动需同时兼容 ONFI 与非 ONFI 器件,能力表须显式声明 onfi_supported: false 并跳过相关分支。

3. XT27G02A 必须由主机做 ECC。XT27G02B 的片内 ECC 引擎已完成 8 bit / 528 B 纠正,主机侧配置的 ECC 强度应为 0,不要再叠加一层软件 ECC。

3 · 硬件前提核查

这一章全是「动手前要确认的事」。 每一条都对应一类在软件层无法补救的问题——硬件层的问题在软件里怎么绕都绕不过去,只能返板。

上电前先把三件事问清楚:答案决定你走哪条路线,猜错就是几周的返工问题 1:SoC 有没有硬件 NAND 控制器(NFC / EBI / FSMC / LPC)?查 SoC 手册章节标题:搜 NAND、EBI、FSMC、LCDC(带 NAND 接口的)、SmartLPC 等关键词;有寄存器手册且含 CLE/ALE 写入口的即为硬件控制器。有没有路线 A:找该 SoC 的 NAND host 驱动主线已有:sunxi-nand、mxc_nand、fsl_nand、at91nand、stm32nand、davinci-nand、omap-nand、marvell-nand、intel-lpss、bcm-nand厂商 BSP 里若有 NAND 驱动,优先用厂商版(时序与该芯片最匹配)路线 B:gpio-control-nand(主线自带)前提:数据线能挂到 SoC 的内存映射窗口(读写即产生 WE#/RE#),且 GPIO 能当通用输出用。数据总线宽度受窗口大小限制。仅适合小容量(通常 512 Mb 以内)与对速度不敏感的场合。问题 2:R/B#(Ready/Busy)引脚接到了 SoC 的哪个可读引脚?必须是一根能读回电平的 GPIO 或 GPIO 控制器自身的状态寄存器。接了 PC 机的 LPT 并口或悬空,就只能退化为主机轮询 70h 读状态,速度慢且易误判。问题 3:SoC 有没有可用的 ECC 引擎?位宽与步长能否覆盖器件要求?没有则用内核软件 BCH 引擎(CONFIG_MTD_ECC_BCH),CPU 开销约 30%~50%,但正确性有保障;有则走硬件 ECC,务必核对 step_size 与 strength 与 datasheet 一致。三条问题组合出的落地结论硬件 NFCR/B#ECC 引擎落地结论硬件 NFC 有R/B# 已接有 ECC 引擎路线 A + 硬件 ECC,量产首选硬件 NFC 有R/B# 已接无 ECC 引擎路线 A + 软件 BCH,可靠性优先时首选硬件 NFC 无R/B# 已接任意路线 B gpio-control-nand,仅小容量与启动用途硬件 NFC 无R/B# 未接任意先改硬件;确实无法改才评估路线 C
图 7 · 上电前的三问:硬件前提核查流程

3.1 SoC 侧必须先确认的三件事

要确认的事怎么查确认不通过的后果
SoC 有无硬件 NAND 控制器查 SoC 硬件手册章节标题;搜 NAND / EBI / FSMC / LPC / SmartLPC只能走 GPIO 模拟或自研,性能上限被锁死
R/B# 接到哪个可读引脚看原理图;确认该引脚在所用 SoC 上是双向 GPIO 且能读回只能主机轮询状态寄存器,速度降一个数量级且易误判
是否有 ECC 硬件引擎查手册 ECC / BCH / LDPC 章节;确认位宽与 step_size只能用软件 BCH,CPU 开销 30%~50%
数据线挂哪个总线窗口看原理图;确认地址译码与访问宽度(8 或 16 bit)GPIO 模拟路线不成立
IO 电压域查 SoC 该 Bank 的电压配置寄存器(IOV 等)3.3 V 器件接 1.8 V 域会烧或读不出
pinmux 是否被其它外设占用看设备树 pinctrl 与当前生效配置probe 时申请 GPIO 失败,报 -EBUSY

3.2 引脚连接、电压域与上电时序

并口 NAND 的引脚分三类,各自的处理方式不同:

类别引脚方向(对 SoC)上电常态电平要点
数据总线I/O0 到 I/O7(x8)双向高阻必须配置为 GPIO 或总线复用;空闲时不得被内部上下拉拉成确定电平
命令与地址锁存CLE、ALE输出低上电默认为低,RESET 期间必须保持低
片选CE#输出高(不选中)上电期间 CE# 应保持高,避免器件进入未知状态
写与读使能WE#、RE#由总线产生或 GPIO高GPIO 模拟路线中必须由软件严格控制时序
就绪与忙R/B#输入高(就绪)开漏输出,必须外接上拉电阻(典型 10 kΩ),见 3.3 节
写保护WP#输出高(允许写)上电必须为高;低电平下擦除与编程命令会被忽略
上电时序的三个硬性要求

1. WP# 必须先拉高再释放 CE#。 若 CE# 已选中而 WP# 仍为低,器件会忽略所有写与擦除命令, 表现为「能读、写了没反应」——极难定位。

2. VCC 稳定后至少等 tPOR(typ 5 us,max 300 us)再发 FFh 复位。 提前复位器件可能处于不确定状态机,后续所有命令静默失败。

3. CE# 与 R/B# 的上电态要安全。 CE# 上为高(不选中),R/B# 上为高(就绪)。 如果 R/B# 上拉电压与 VCC 不同(例如 VCC 掉电而 IO 域还有电), 器件可能通过 IO 灌入反向电流触发闩锁,这是并口 NAND 唯一的「怕反压」场景。

/* 设备树 pinctrl 示例:NAND 专用引脚组(SoC 相关,此处以通用写法示意) */
&pinctrl_nand {
    nand_ctrl_default: nand_ctrl {
        /* 数据线:复用为 NAND 数据总线,不是普通 GPIO */
        pinmux_ /* 以实际 SoC 的 PINMUX 宏为准 */;
    };
};

/* IO 电压域设置(示意,寄存器名因 SoC 而异) */
/* 将 NAND 所在 Bank 设为 3.3 V 或 3.3 V_HDR */

3.3 R/B# 上拉与就绪判定:最容易翻车的一根线

R/B# 是整条并口接口里信息量最大、也最容易出问题的一根线。 它把「器件内部忙不忙」这件事直接给出来,让主机可以被动等待而不是猜时间。

R/B# 的三种接法:只有接了可读引脚的那种才是推荐做法接法 A:接到 SoC 可读 GPIO(推荐)· 主机直接读引脚电平判断忙闲· 等待精确到微秒级,不浪费 CPU 轮询· 内核 dev_ready 回调直接用这个 GPIOwhile (gpiod_get_value(rdy) == 0) { /* 忙 */ }接法 B:接不到 GPIO,主机轮询 70h· 每次发 70h 命令、读状态寄存器判断 OIP 位· 每次查询要占用一次总线往返· 轮询间隔不当会漏掉刚好完成的窗口,或超时误判每 50 us 发一次 70h,读到 OIP=0 才算完成接法 C:完全靠固定延时· 按 datasheet 最大值 usleep(tR_max)· 温度升高时器件变慢,固定延时会读到旧数据· 全片并发时所有等待都按最大值算,性能被最坏情况拖死危险:可能读到上一条命令的残留数据,ECC 也救不了R/B# 是开漏输出,板上必须外接上拉(典型 10 kΩ 到该 Bank 的 IO 电压);上拉电压必须与 VCC 同源,不能高于 VCC 太多。
图 8 · R/B# 就绪信号的三种接法对比
上拉电阻取值

典型取 10 kΩ。取值权衡:太大则总线上升沿慢、与长走线的高频噪声耦合; 太小则低电平时下拉电流大、器件功耗上升。 在 20 cm 以内、走线阻抗可控 的板子上 4.7 kΩ 到 20 kΩ 都能工作, 建议按「先 10 kΩ 实测上升沿波形,再按需调整」的方式定值。

现象可能根因验证方法
卡在等待 R/B# 直到超时R/B# 悬空或上拉缺失;或引脚配成了输出示波器量 R/B# 空闲电平应为高;读 sysfs 的 GPIO 方向
等待立刻通过(读到高但器件其实忙)R/B# 接反到 WP#;或引脚号写错量 WP# 与 R/B# 是否接反
读数据全 0xFF 但 ID 正常WE# 与 RE# 逻辑反了;或数据位序与宽度错示波器看 RE# 是否有脉冲;试改 nand-bus-width
偶发读出错数据,温度升高更频繁固定延时不够,或等待超时值太小把 max_wait_ready 放大到 datasheet 最大值的 2 倍观察

3.4 布线与信号完整性要点

  • 数据线与控制线分开走:I/O0~7 与 CLE/ALE/RE#/WE# 分层或分开走线, 避免 RE# 串扰到数据线上(表现为随机位翻转,ECC 报可纠正错误);
  • 等长要求:x8 情况下数据线之间建议不超过 5 mm 长度差, 这是 x8 与 x16 唯一真正需要匹配的信号组;
  • R/B# 单独走线:这根线上的任何毛刺都会让主机误判已完成;
  • 去耦电容:器件 VCC 引脚正下方放 1 只 100 nF 加 1 只 1 uF; 并口 NAND 阵列操作电流跳变大,供电不足表现为随机 ECC 错误;
  • CE# 与 WP# 上拉:虽然常态由 SoC 驱动为确定电平, 建议各加 10 kΩ 上拉,保证 SoC 复位与高阻期间器件仍处于安全态;
  • 避免测试点与长支线:在 RE# 与 WE# 上加测试点要评估支线电感, 50 mm 支线在 40 MHz 以上的时序下可能接近四分之一波长;
并口 NAND 与 SPI NAND 的 SI 要求差一个量级

SPI NAND 用时钟线做时序参考,走线要求相对宽松。 并口 NAND 是异步接口,tRP 与 tWP 靠总线周期保证,走线延迟直接吃掉时序余量。 在 100 MHz 以上的 SoC 总线上,10 cm 走线(约 7 ps/mm,单向约 0.7 ns, round trip 约 1.4 ns)相对于 50 ns 的 tRP 看似很小, 但多条控制线累积的偏斜会让设备在量产测试中偶发失败。

建议:量产前做全温(-40 ℃ 到 +85 ℃)的读写抽测。 温度是 NAND 时序余量的主要杀手——高温时 tR 与 tPROG 显著变长, 而部分器件的 tRP 与 tWP 要求反而更严。

4 · MTD / rawnand 子系统解剖

这一章是「看懂内核」的部分。 如果你只是照抄设备树就能跑通,可以跳过;但如果你要自研 host 驱动,或者要 debug 到底层, 这章决定了你能走多远。

4.1 三层结构与完整调用链

Linux 的裸 NAND 支持可以理解成三个互不重叠的抽象:

  1. mtd_info:对上层(VFS、UBI、mtdblock)暴露的统一接口。 提供 read、write、erase、point、lock、unlock、block_isbad 七个语义。 上层只认这个结构,不关心底下是 NAND 还是 NOR。
  2. nand_chip:描述「这是一颗什么样的 NAND」。 包含几何(pagesize、oobsize、erasesize、numblocks)、 特性位(options)、函数指针(ecc、badblock、dev_ready)、 以及最关键的 ops->exec_op 指令执行器。
  3. nand_controller:描述「怎么驱动这颗 NAND」。 由 host 驱动提供 exec_op() 和 attach_chip() 两个回调。 内核 6.5 之后 rawnand 全面转向 exec_op 模型, 老式的 cmd_ctrl()、read_buf()、write_buf() 已被移除。
exec_op 模型的意义

老驱动方式是「填函数指针」:read_page() 读主区、 read_oob() 读 OOB、write_page() 写。 每个函数内部自己去发命令、自己管时序。问题是同一件「发 5 个字节」的事要在十几个函数里重复写。

exec_op 模型改成「填指令序列」:核心构造一个 struct nand_operation, 里面按顺序装好 CMD_INSTR、ADDR_INSTR、DATA_INSTR、WAITRDY_INSTR, 交给 exec_op() 一次发完。 好处是命令顺序、地址周期数、等待时机全部由核心统一管理, host 驱动只负责「把字节放到总线上」这一件事。

代价:host 驱动必须实现 exec_op() 里的指令分发 switch, 且不能自行改变顺序。新写驱动必须用这个模型。

/* 核心构造的指令序列(简化示意,主机侧看到的形态) */
struct nand_operation op = {
    .type = NAND_OP_PAGE_READ,       /* 高层语义,核心负责展开 */
    .instrs = {
        { .type = NAND_OP_CMD_INSTR,  .ctx.cmd.opcode = NAND_CMD_READ0 },  /* 00h */
        { .type = NAND_OP_ADDR_INSTR, .ctx.addr.naddrs = 5,
          .ctx.addr.addrs  = { col_lo, col_hi, row0, row1, row2 } },
        { .type = NAND_OP_CMD_INSTR,  .ctx.cmd.opcode = NAND_CMD_READ30 }, /* 30h */
        { .type = NAND_OP_WAITRDY_INSTR, .ctx.waitrdy.timeout_ms = 100 },
        { .type = NAND_OP_DATA_INSTR,  .len = 2048, .buf = buf },
        { .type = NAND_OP_CMD_INSTR,  .ctx.cmd.opcode = NAND_CMD_READ30 }, /* 随机读换页 */
    },
    .ecc = &ecc_op,                   /* 让核心决定在哪一步插 ECC 引擎操作 */
};
this->ops->exec_op(this, &op, check_only);
指令类型含义host 驱动要做什么
NAND_OP_CMD_INSTR在总线上放一个命令字节拉高 CLE,写字节,拉低 CLE
NAND_OP_ADDR_INSTR放 naddrs 个地址字节拉高 ALE,逐字节写入,拉低 ALE
NAND_OP_DATA_INSTR在总线上放或收 len 个数据字节交 DMA 或 memcpy;数据方向由核心在别处指明
NAND_OP_WAITRDY_INSTR等待器件就绪轮询 R/B#,或轮询 70h 状态
NAND_OP_DELAY_INSTR插入固定延时ndelay();只在时序确实不够时用
从 open() 到 NAND 阵列:一次页读的完整调用链,以及每层的责任用户态VFS + MTD 核心mtd_info 抽象层nand_chip 核心层host 驱动层硬件分层的意义:上三层(MTD / UBI / 文件系统)与器件无关,所有 SoC 共用;你只需要保证最下两层正确。
图 9 · MTD / rawnand 分层结构与完整调用链

4.2 nand_scan 探测时序逐拍

nand_scan() 是核心的探测入口,host 驱动在 probe 里调用它。 它做的事按顺序是:复位、读 ID、查 idt 表、装载能力表、 (可选)读 ONFI 参数页交叉校验、重新复位、填超时值、扫描坏块、建立 mtd_info、解析分区。

驱动在这个过程里唯一必须提供的是:

  • 一个 struct nand_controller,含 exec_op 回调;
  • 把几何的初始猜测写进 chip->parameters(可选, 有 idt 表命中时会被覆盖);
  • 如果器件不在 idt 表里,attach_chip() 回调里手工填能力表。
XT27G02A 与 XT27G02B 不在主线 idt 表里怎么办

这两款器件是传统并行 NAND(非 ONFI 参数页路线),大概率不在主线的 nand_ids.c 里。三个选项,按推荐度排序:

  • 选项 1(推荐):在 host 驱动的 attach_chip() 里手工填能力表。 几何、时序、OOB 大小按 datasheet 硬编码。不污染主线代码。
  • 选项 2:往主线 nand_ids.c 提交补丁,加一条器件记录。 好处是全生态受益(mtd-utils、mtools 都能识别),坏处是要等合入或长期维护 out-of-tree。
  • 选项 3:用设备树覆盖几何字段。 部分内核版本支持通过设备树 nand,<xxx> 覆盖几何, 但字段覆盖能力在各版本间不一致,不推荐依赖。
/* 在自研或修改后的 host 驱动里,为 XTX XT27G02A 装载能力表(示意) */
static int my_nand_attach_chip(struct nand_chip *chip)
{
    /* 先让核心走默认流程,若有匹配则直接返回 */
    int ret = nand_attach_chip(chip);
    if (ret == 0)
        return 0;

    /* 手工装载:XT27G02A —— 2048+128,64 页每块,128 KiB 每块,5 地址周期 */
    chip->mtd._point.phys_erase_size = 128 * 1024;   /* 块大小 128 KiB */
    chip->mtd._point.phys_block_size = 128 * 1024;
    chip->mtd._point.phys_page_size  = 2176;         /* 2048 Main + 128 Spare */
    chip->mtd.oobsize  = 128;                        /* Spare / OOB 大小 */
    chip->mtd.writesize = 2048;                       /* 页数据区大小 */
    chip->mtd.subpage_size = 2048;                   /* 2048 需整页写 */
    chip->mtd.eraseblock_size = 64;                  /* 每块页数 */
    chip->mtd.nr_banks = 1;
    chip->chip_delay = 20;                           /* tR,宿主要求的额外等待 */
    chip->ecc.algo    = NAND_ECC_ALGO_BCH;           /* 主机侧 ECC */
    chip->ecc.step_size = 544;                       /* 8 bit 每 544 B */
    chip->ecc.strength  = 8;
    chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT;
    chip->onfi_chip     = 0;                         /* 非 ONFI:不读 ECh */
    return 0;
}
attach_chip 里的四个字段必须一次填对

phys_page_size 是 Main 加 Spare 之和(XT27G02A 等于 2176), 而 writesize 与 oobsize 之和必须等于它(2048 加 128 等于 2176)。 这两个数填得不一致,核心的地址计算会整体错位,症状是「读到的是相邻页」。

onfi_chip = 0 必须显式设上。若误设为 1, 核心会向 XT27G02A 发送禁用 的 ECh 命令, 器件响应无定义,可能进入未定义状态并导致后续所有操作异常。

从上电到 /dev/mtd0 出现:核心做了这些步骤,驱动只需填能力表01复位器件发送 FFh(Reset),等待 tRST(typ 100~250 us)后读 70h 确认 OIP 清零02读 ID90h 加 00h,拿到厂商 ID 与器件 ID,在 idt 表里匹配03装载能力表从匹配到的芯片模板复制几何、时序、OOB 布局、特性位04ONFI 参数页(可选)ECh 读 256 B 参数页,交叉校验几何;非 ONFI 器件跳过05重新复位参数页会改变器件状态,必须再 FFh 一次06设置就绪超时根据参数页的 tR / tPROG / tBERS 填 max_wait_ready07扫描坏块按 badblock_pattern 逐块读标记,建立 RAM 坏块表08建立 MTD 设备填充 mtd_info 的几何,注册 /dev/mtdN09解析分区设备树 fixed-partitions 或内核命令行 mtdparts=10注册字符/块设备mtd_device_register 注册字符设备,mtdblock 视配置注册块设备注意:驱动代码里写的是「能力表」和「指令序列」,不是「发几个字节」——这是 rawnand 框架与老式驱动的根本区别
图 10 · nand_scan 的探测与初始化流程

4.3 关键结构体与移植接口

结构体 / 字段作用移植时是否要动
struct nand_chip器件描述:几何、特性位、函数指针要填几何与能力
chip->ops->exec_op指令执行器,必填必填
chip->ops->attach_chip能力表装载,可选器件不在 idt 表时必填
chip->ecc.algo / step_size / strengthECC 算法、步长、强度要填
chip->ecc.engine_type软件或硬件 ECC 引擎要填
chip->options特性位(ECC_REQUIRED、NO_ECC_BCH 等)按器件能力设置
chip->bbt_td / bbt_md坏块表描述符(自定义位置时用)第 8 章详述
chip->badblock_pattern坏块标记判据XTX 需自定义
chip->ecc_patternECC 在 OOB 中的分布按 datasheet 的 OOB 布局
chip->legacy.chip_delay额外等待(us),叠在 ONFI 表之上可留 0
chip->onfi_chip是否 ONFI 器件(决定是否读 ECh)XTX 设为 0
struct nand_controllerhost 驱动与核心的接口必填
/* 一份最小可用的 nand_controller(示意) */
static const struct nand_controller_ops my_ops = {
    /* 把一条 nand_operation 落到总线上。这是唯一必须实现的回调。 */
    .exec_op = my_exec_op,

    /* 可选:核心探测后调用,让你修正几何与能力表 */
    .attach_chip = my_attach_chip,
};

static struct nand_controller controller = {
    .ops = &my_ops,
};
/* 之后:nand_to_mtd(&controller) 得到 mtd,再 nand_scan(mtd, 1) */

4.4 源码位置速查

文件(内核 6.x 路径)内容移植时的关注点
drivers/mtd/nand/raw/nand_base.c几何管理、读写擦流程编排、nand_scan看 nand_scan 的执行顺序;看 nand_read_page 怎么展开成 op
drivers/mtd/nand/raw/nand_ecc.cHamming / BCH / RS 软引擎看 BCH calculate 与 correct 的调用时机
drivers/mtd/nand/raw/nand_timings.cONFI 时序表与检查看你的控制器在哪些项上不达标会告警
drivers/mtd/nand/raw/nand_ids.c器件 ID 表看是否已有你的料号;看同类器件的能力表写法
drivers/mtd/nand/raw/nand_bbt.c坏块表扫描与更新看 badblock_pattern 的语义(offs、len、pattern)
drivers/mtd/nand/raw/gpio.cGPIO 模拟 host 驱动(路线 B)路线 B 的全部实现,约 400 行,是最好的参考
drivers/mtd/nand/raw/sunxi_nand.c全志 NAND host 驱动硬件控制器 host 驱动的完整范例,含 DMA 与硬件 ECC
drivers/mtd/nand/raw/mxc_nand.cFreescale i.MX NAND host 驱动另一套硬件实现,含硬件 ECC 与 BBM 处理
include/linux/mtd/rawnand.h核心数据结构定义struct nand_chip / nand_operation / nand_controller_ops 全在这里
Documentation/devicetree/bindings/mtd/设备树绑定gpio-control-nand.yaml、raw-nand-chip.yaml、nand-chip.yaml
读内核源码的高效顺序

不要从 mtdcore.c 开始读(它与 NAND 无关)。推荐顺序:

  1. 先读 include/linux/mtd/rawnand.h 里的 struct nand_chip 与 struct nand_operation,建立数据结构的心智模型;
  2. 读 nand_base.c 的 nand_scan(),看它按什么顺序调谁;
  3. 读 nand_base.c 的 nand_read_page(),看它怎么把高层语义展开成 op;
  4. 读 raw/gpio.c 的 gpio_nand_exec_op(),看最小实现长什么样;
  5. 需要时再读 sunxi_nand.c 学硬件控制器的 DMA 与 ECC 集成。
相关报告 · 交叉引用 2 器件全部展开收起 ▾
点开类目即可展开:具体文档 / 章节(点章直接跳转到该章)
PPI NAND(并行NAND)
电路设计指南
软件设计规范
量产问题排查
SPI NAND
电路设计指南
软件设计规范
Linux 驱动开发
样机问题排查
量产问题排查4
全志平台 SPI NAND NOR
章节(点章跳转,共 11 章)
海思平台 SPI NAND 量产常见失败
章节(点章跳转,共 11 章)
烧录成功但不开机 SPI NAND
章节(点章跳转,共 12 章)
瑞芯微平台 SPI NAND eMMC
章节(点章跳转,共 11 章)
系统设计指南3