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

Linux_PPI_NAND_驱动开发指南

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

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

编制日期 2026-10-05

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 集成。
扫码打开本页
微信扫码 · 展会可扫

再分享给同事

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