只读分享解决方案文档原理方案设计📅 2026-10-14🔖 Rev 1.0⏳ 还有3日到期
🔧解决方案&应用市场分析 -> 原理方案设计 -> 电子设计&技术探讨 -> Linux_MTD层与设备树配置_技术探讨

Linux_MTD层与设备树配置_技术探讨

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

Linux MTD 层与设备树配置

MTD 子系统、SPI MEM 框架、设备树描述与分区设计 —— 一份以「裸器件到挂载点」为主线的软件设计参考 | 编制日期 2026-10-14 | 版本号 Rev 1.0

2 条路径
Linux 看待存储器件的方式
块设备(FTL 在器件内)/ MTD(FTL 在软件侧)
raw / SPI NAND
MTD 子系统的主要服务对象
SPI NOR 同走 MTD,但无 ECC 与坏块负担
设备树描述
从器件到分区的唯一配置入口
compatible / reg / partition 子节点
裸器件到挂载点
本文主线
驱动 → 分区 → UBI/UBIFS → mount
报告总结

把一颗裸 Flash(raw NAND / SPI NAND / SPI NOR)接进 Linux,让它在开机后变成一个能读写、能掉电安全、能被 OTA 更新的文件系统,中间要经过一条很长的软件链:设备树描述器件、控制器驱动接管总线、MTD 核心层统一抽象、分区表切分地址空间、UBI 承担磨损与坏块、UBIFS 或 SquashFS 提供文件接口,最后才是 mount 与启动脚本。这条链上任何一环写错,症状都出现在最后一环——「根文件系统挂不上」。

这份文档只讲这条链的软件侧配置与设计:MTD 核心抽象是什么、控制器驱动怎么分层、spi-mem 框架怎么描述一条总线操作、设备树里每个属性对应驱动的哪个参数、分区表按什么原则切、UBI/UBIFS 分别在替谁干活、启动链路三处配置怎么保持一致。文中只写机制、属性名、结构名与配置顺序,所有具体数值一律以正式 datasheet / 内核绑定文档 / 器件手册为准。

结论: Linux 用两条路径对待存储器件——FTL 在器件内的走块设备(eMMC / UFS / SD),FTL 在软件侧的走 MTD 子系统(raw / SPI NAND); MTD 配置的正确形态是三件事同时成立:设备树把器件与分区描述清楚、控制器驱动把 ECC / 坏块 / 时序管起来、UBI 与 UBIFS 把磨损均衡和文件级掉电安全接住。 这条链的设计动作可以压缩成一句话:按绑定文档写设备树、按擦除块切分区、按最坏情况调 ECC 与坏块策略、按掉电场景选文件系统。 所有属性名与参数值以内核绑定文档与器件 datasheet 的正式版本为准。

读者路径

  • 刚接手裸 Flash 适配,不知道从哪下手:第 1 章(两条路径怎么分)+ 第 5 章(设备树怎么写)+ 第 9 章(启动链路怎么配)。
  • 设备树写了但驱动不认、分区不出来:第 4 章(spi-mem 与存储驱动)+ 第 5 章(属性与驱动参数的对应)+ 第 10 章(调试接口)。
  • 要定分区表、支持 OTA:第 6 章(分区表设计)+ 附录 B(分区布局模板)。
  • 关心掉电安全与可写数据:第 7 章(UBI 与 UBIFS)+ 第 8 章(挂载参数)+ 第 10 章(掉电测试)。
  • 从厂商 BSP 往新内核移植:第 11 章(版本差异与移植检查单)。
  • 只想要结论:第 12 章(要点汇总与配置-验证闭环)。

前言:关于这份参考文档

本文的读者是做 BSP、驱动与系统集成的工程师:需要把一颗存储器件接进 Linux、把分区表定下来、让系统稳定启动并可在现场升级。阅读前不需要你是 MTD 专家,但需要你大概知道设备树与内核驱动的基本关系。

三条约定先说清楚。其一,只写机制不写死数值:全文不出现寄存器地址、位域位置、属性字节默认值与具体 opcode 数值;出现的属性名(如 compatible、reg、partition 子节点)与结构名(如 mtd_info、spi_mem)都是可以照写的配置接口,但它们的取值一律以正式 datasheet / 绑定文档为准,量级性描述仅用于建立判断。其二,不含商业数据:不涉及任何单价、份额、产能与厂商排名,选型讨论只围绕技术机制。其三,引图用章号:正文引用插图一律写「第 N 章」,不依赖图号跳转,图号仅作检索。

本文是存储技术系列中的「软件配置」一篇:器件侧的电气与架构在系列其他篇中讨论,本篇聚焦从设备树一个属性到 mount 成功一次之间的全部软件决策,并在第 12.3 节说明与系列其他篇的衔接关系。

1. 问题本质:Linux 怎么看待一块存储器件

这一章不涉及任何具体配置,只把问题还原成机制:Linux 面对一颗存储器件时到底有几种用法、MTD 子系统在软件栈里站在什么位置、裸器件与块设备的边界划在哪里。后面所有章节的判断——设备树怎么写、ECC 怎么配、分区怎么切——都是这一章三条边界的具体投影。

1.1 从块设备到裸器件的两条路径

同一颗存储介质,接进 Linux 的方式由「谁来做 FTL」决定。FTL(Flash Translation Layer)是 Flash 与应用之间那一层映射与管理者:把「写前必须擦除的块」伪装成「可以覆写的扇区」、处理坏块替换、做磨损均衡。这层工作由谁承担,就决定了内核怎么看待这颗器件:

  • 路径一:当块设备用。器件内部(eMMC / UFS 的控制器,SD 卡内部)已经内置了完整的 FTL,主机看到的就是一个规整的块设备——512B 或 4KB 扇区、可随机覆写、坏块被内部替换。Linux 侧只需要块设备驱动(mmc / scsi / mmcblk),文件系统直接架在上面。
  • 路径二:走 MTD 子系统当裸器件用。器件是裸片(raw NAND / SPI NAND)或只做了最少管理的介质(SPI NOR),没有内部 FTL。Linux 侧必须自己完成映射、ECC、坏块与磨损管理,入口就是 MTD(Memory Technology Device)子系统。

两条路径没有优劣,只有分工不同:块设备路径把复杂度卖给器件供应商,MTD 路径把控制权留给软件。需要深度参与掉电策略、需要现场可预测的擦写行为、或者器件本身就是裸片时,MTD 是唯一选择;反之,如果器件已经内置控制器且容量成本合适,没有必要把 FTL 的活接到软件侧。

维度块设备路径MTD 路径
FTL 位置器件内部(eMMC / UFS / SD 控制器)软件侧(控制器驱动 + UBI / 文件系统)
访问单位逻辑扇区(512B / 4KB,可覆写)擦除块与页,写前必须擦除
坏块管理器件内部替换,对主机透明驱动维护坏块表(BBT),软件侧跳过
ECC器件内部或对主机隐藏驱动显式配置(片上 / 控制器 / 软件)
磨损均衡器件内部UBI 或文件系统层(JFFS2 一类)
典型器件eMMC / UFS / SD 卡raw NAND(并行 / SPI)/ SPI NAND / SPI NOR
内核子系统block 层 + mmc / scsi 驱动mtd 子系统(+ ubi 上层)
典型文件系统ext4 / f2fsSquashFS(只读)/ UBIFS / JFFS2(可写)
掉电语义依赖器件内部兜底,主机不可见软件侧必须显式设计(日志、原子提交)
一个容易混淆的中间态

有些器件「看起来是块设备,其实是 MTD」:例如 SPI NAND 在内核里可以挂 mtdblock 生成一个块设备视图,或者用 ubiblock 把 UBI 卷伪装成只读块设备。它们解决的是「让 ext4 这类只认块设备的文件系统有地方挂」,并没有改变 FTL 在软件侧这个事实——坏块、磨损、掉电语义仍然由 MTD / UBI 兜着。看到 /dev/mtdblockX 时,不要按 eMMC 的经验推断它的行为。

Linux 存储子系统的两条路径:分界点在 FTL 由谁承担应用与 VFSopen / read / write / mmap块设备路径FTL 在器件内 · eMMC / UFS / SDMTD 路径FTL 在软件侧 · raw / SPI NAND块设备层request 队列 · 512B / 4KB 扇区MTD 子系统mtd_info · 擦除块与页粒度块设备驱动eMMC / UFS / SD 控制器驱动存储驱动NAND 控制器 / spi-mem 驱动存储器件eMMC / UFS / SD 卡裸存储器件raw NAND / SPI NAND / SPI NOR文件系统ext4 / f2fs文件系统UBIFS / JFFS2 / SquashFS分界在 FTL 的位置:FTL 在器件内走块设备路径,FTL 在软件侧走 MTD 路径本文主讲右半侧——从裸器件到挂载点的整条软件链;块设备路径仅作对照
图 1 · Linux 存储子系统的两条路径

图 1 是全文的总图:两条路径共享顶层的应用与 VFS,分叉发生在「谁把块介质管理掉」这一层。右半侧就是本文的主线——MTD 子系统把裸器件抽象成「擦除块 + 页 + OOB」的统一视图,控制器驱动与 spi-mem 框架负责把一条条操作送到器件,文件系统(UBIFS / JFFS2 / SquashFS)在 MTD 之上提供文件语义。这条链上的每一层,后面都有一章专门拆解。

1.2 MTD 子系统的定位

MTD 子系统的定位可以用一句话概括:它是裸 Flash 在 Linux 里的「设备类」,就像网络子系统是网卡的设备类、输入子系统是键鼠的设备类。它不提供文件语义,也不做磨损均衡;它只负责把「一块会坏块、要擦除、带 OOB、要 ECC」的介质,抽象成一个接口稳定的字符设备与一套操作回调,让上层的文件系统和下层的控制器驱动都能各自演进。

这个定位带来三个直接后果。其一,文件系统必须懂 Flash:架在 MTD 上的是 UBIFS / JFFS2 / SquashFS 这类原生支持裸器件的文件系统,而不是 ext4——ext4 假设底层是可覆写块设备,直接架在裸 NAND 上会反复触发读-改-写,把磨损与坏块问题放大。其二,驱动必须显式管理缺陷:ECC 强度、坏块策略、时序参数都要在驱动与设备树里显式声明,没有「默认值兜底」这回事。其三,工具链独立:mtd-utils(mtdinfo / flash_erase / nandwrite / ubiformat)是 MTD 世界的标准工具,调试与量产烧录都围绕它们组织。

MTD 提供的能力说明深入章节
统一器件抽象mtd_info 描述容量、擦除块大小、页大小与 OOB 布局第 2 章
操作回调read / write / erase / OOB 访问的驱动实现入口第 2 章
分区机制把一个 mtd 器件切成多个 mtdX,边界按擦除块对齐第 2 章 / 第 6 章
字符设备与 sysfs/dev/mtdX、ioctl 与 sysfs 属性,供用户态查询与操作第 2 章
器件驱动框架raw NAND 核心层、spi-mem 抽象与 spi-nor / spi-nand 驱动第 3 章 / 第 4 章
UBI 上层卷、逻辑-物理块映射、磨损均衡、坏块透明化第 7 章
MTD 在软件栈中的位置:从应用到裸器件的分层应用与工具层mtd-utils:flash_erase / nandwrite / ubiformat / mtdinfo文件系统层SquashFS(只读)/ UBIFS、JFFS2(可写)/ ext4-on-ubiblockMTD 核心层mtd_info、分区(ofpart)、字符设备 /dev/mtdX、sysfs器件抽象层nand_chip(raw NAND 核心)/ spi_nor / spinand(spi-mem 之上)总线与控制器驱动SPI 控制器驱动、NAND 控制器驱动(时序 / ECC 引擎 / DMA)裸存储器件raw NAND(并行 / SPI)/ SPI NAND / SPI NORMTD 的边界:核心层只认识「擦除块 + 页 + OOB」,不认识文件;文件系统只认识卷与目录,不认识坏块两层之间的契约就是 mtd_info 的操作回调;器件差异被吸收在「器件抽象层」以下,由控制器驱动各自实现
图 2 · MTD 在软件栈中的位置

图 2 是 MTD 世界的分层地图。记住两个边界:MTD 核心层与文件系统之间的契约是「块设备语义的裸器件」——文件系统拿到的是「可读可写可擦的块」,不需要知道坏块在哪;MTD 核心层与驱动之间的契约是「操作回调」——核心层说「读这一页」,驱动负责把这条命令连同时序、ECC、坏块检查一起送到器件。中间三层的差异(NAND 还是 SPI、什么线宽、什么 ECC 引擎)全部被吸收在分层内部,这就是 MTD 抽象的价值,也是第 2 章要逐层展开的内容。

1.3 raw 器件与块设备的边界

「裸器件」和「块设备」的差别不是速度、不是容量,而是访问契约。块设备的契约是:给你一个逻辑扇区号,你可以随时读、随时覆写,原子性由器件保证。裸器件的契约是:读以页为单位,写只能把位从 1 改成 0,把 0 改回 1 必须整块擦除,坏块可能出现且必须避开,位错误可能出现且必须纠正。下表把两条契约的差异逐条列出——这些差异就是后面每一章要处理的问题清单。

对比项raw 器件(MTD 视图)块设备(FTL 之后)
写入前操作必须先擦除整个擦除块直接覆写目标扇区
最小写单位页;页内只能顺序写、不能回头改扇区(512B / 4KB)
坏块出厂与使用中都会出现,软件标记并跳过内部替换,主机不可见
读干扰 / 电荷保持需要软件策略关注(读阈值、定期巡检)器件内部处理
掉电中断可能使整块进入不确定状态,需软件恢复由器件内部兜底
ECC驱动显式配置强度与步长对主机隐藏
容量口径按擦除块报告,坏块不计入可用空间按扇区报告,容量固定
raw 器件与块设备的边界:访问契约的差异裸器件视图 · MTD 的擦除块与页访问单位:擦除块(EB)与页(Page)EB0页页页页EB1页页页页EB2页页页页· 读写以页为单位,擦除以整块为单位· 坏块与 ECC 由软件侧负责· 上电后器件状态由驱动重建(坏块表)块设备视图 · FTL 之后的 LBA访问单位:逻辑扇区(LBA)LBA扇区扇区扇区扇区LBA扇区扇区扇区扇区LBA扇区扇区扇区扇区· 读写以逻辑扇区为单位,扇区可覆写· 映射、磨损、坏块全部藏进器件· 掉电时未完成的写入由器件内部兜底FTL边界判据只有一句:应用看到的是「可覆写扇区」还是「要先擦除的块」前者走块设备路径,后者走 MTD 路径;把 raw NAND 当块设备直贴会绕开坏块与磨损管理
图 3 · raw 器件与块设备的边界

图 3 把两条契约并排画开:左边每一条「EB」里装着若干页,擦除的动作作用于整条;右边每一条「LBA」里装着若干扇区,覆写直接落在扇区上。中间那一箭就是 FTL——它存在的全部意义就是把左边的契约翻译成右边的契约。MTD 路径把这层翻译工作接回了软件侧,于是第 2 章的核心抽象、第 3 章的 ECC 配置、第 6 章的分区对齐、第 7 章的 UBI 映射,本质上都是在回答「没有 FTL 之后,谁来干这些活」。

最常见的误用:把 MTD 器件当块设备用

看到 /dev/mtdblockX 就按 eMMC 的经验往上架 ext4,是 MTD 世界最高频的误用。mtdblock 是简陋的可写块设备仿真:写入靠读-改-擦-写缓存完成,不管坏块新增、不做磨损均衡、掉电易损坏,只适合只读挂载(SquashFS 一类),任何可写场景一律走第 7、8 章的 UBI / UBIFS;ubiblock 只用于给只读镜像(SquashFS / 只读 ext4)提供块设备视图。

1.4 路径选择的三个输入

「用块器件还是 MTD」这个问题,在项目里由三个输入共同决定,缺一个都会选错。器件类型是硬约束:裸片没有内置 FTL,除非外加控制器,否则只能走 MTD;eMMC / UFS 内置控制器,走块设备是天经地义。系统需求是软约束:需要现场可预测的擦写行为、需要掉电安全由自己掌握、需要固件级的数据恢复能力时,MTD + UBI 给的控制权更大。生态与成本是现实约束:MTD 侧的驱动、分区、UBI、文件系统、烧录工具都要自己搭,团队没有相关积累时,选内置 FTL 的器件更稳。

输入要回答的问题导向
器件类型器件是否内置控制器与 FTL内置 → 块设备路径;裸片 → MTD 路径
系统需求掉电安全、磨损可预测性、数据恢复是否要自主可控要求越高 → 越倾向 MTD + UBI 的显式设计
生态与成本驱动、工具、烧录、人力的投入是否可承受投入有限 → 优先内置 FTL 的块器件方案
容量与成本结构大容量、高吞吐、随机写密集块器件内置 FTL 通常更划算
寿命与更换策略是否要现场长期运行、不可开盖MTD + UBI 的磨损策略可显式调优

1.5 本文主线与读法

本文的主线只有一条:从一颗裸器件出发,沿着软件链一路走到挂载点。读法建议按链走,不要按章节跳:

  1. 第 1~2 章建立模型:两条路径的边界、MTD 核心抽象(mtd_info / 分区 / 回调 / 坏块与 ECC 的位置)。
  2. 第 3~4 章进入驱动:NAND 控制器驱动的分层与接口、spi-mem 抽象与 SPI NOR / SPI NAND 驱动结构。
  3. 第 5~6 章落到配置:设备树里存储节点与分区子节点的写法、分区表设计的原则与 OTA 配合。
  4. 第 7~8 章架文件系统:UBI / UBIFS 分别替谁干活、只读与可写方案怎么选、挂载参数与掉电安全。
  5. 第 9~10 章管启动与验证:启动链路三处配置如何保持一致、调试接口、掉电测试与回归。
  6. 第 11~12 章收口:内核版本差异与移植、配置-验证闭环与要点汇总。

每一章的写法相同:先讲机制与判据,再给可直接对照的表格,最后用提示框把「最容易踩的坑」单独标出。所有表格都可以当核对底稿用——把具体型号、具体内核版本填进去,评审时就是证据。

2. MTD 核心抽象

这一章讲 MTD 子系统的「内功」:一块裸器件怎么注册进内核、怎么被切成分区、驱动通过什么回调被调用、ECC 与坏块的责任落在哪一层、用户态又能看到哪些接口。这一章的概念是后面所有配置的地基——设备树里的每个属性,最终都落到本章的某个结构体字段或某个回调上。

2.1 器件注册与核心结构

一颗裸器件进入 MTD 世界要过三关:总线探测(控制器驱动在总线上发现器件)、结构填充(驱动填好描述器件几何与能力的结构体)、注册(调注册接口把器件挂进 MTD 核心层,此后分区、字符设备、sysfs 才陆续出现)。这三步的顺序不能颠倒——分区出现在注册之后,用户态工具能查到 mtdX 在分区创建之后。

核心结构只需要认识五个,全部是「名字级」的了解:mtd_info 是每块 MTD 器件的身份证,记录容量、擦除块大小、页大小、OOB 大小与操作回调表;mtd_partition 描述一个分区,记录偏移、大小与名字;nand_chip 是 raw NAND 器件的私有状态,挂着控制器钩子、坏块表与 ECC 配置;spi_device / spi_mem 是 SPI 总线侧的器件与「一条总线操作」的描述结构;ubi_volume 是 UBI 层的卷。第 3、4 章会展开 nand_chip 与 spi_mem,本章只建立地图。

结构 / 概念回答的问题关键字段(名字级)
mtd_info这块裸器件是什么、能做什么size / erasesize / writesize / oobsize / 操作回调表
mtd_partition一个分区切在哪里、叫什么offset / size / name / mask_flags
nand_chipNAND 器件的私有状态控制器挂接点、坏块表、ECC 配置入口
spi_device / spi_memSPI 总线上的器件与操作片选、速率、线宽、op 描述结构
ubi_volumeUBI 层的一个卷vol_id / vol_type / usable_leb_size
nand_controller一颗控制器管几颗片挂接多颗 nand_chip、锁与调度

2.2 分区机制

分区的本质是在一个 mtd 器件的地址空间上划出若干段,每段生成一个新的 mtdX。对上层来说,每个分区就是一块独立的 MTD 器件:有自己的容量、自己的文件系统、自己的挂载点;对驱动来说,分区只是偏移与长度的记录,坏块表与 ECC 配置仍然归整个物理器件所有。

分区从哪来?主线内核的默认来源是设备树里的 partitions 子节点(解析器名为 ofpart),此外还有命令行 mtdparts、镜像里残留的 RedBoot 表等。选择来源的原则只有一条:分区表要能被 BootLoader 与内核同时、一致地解读——否则内核看到一个分区布局,U-Boot 看到另一个,烧写与启动就会错位(第 6.5 节与第 9 章展开)。

分区来源配置位置特点适用场景
ofpart设备树 partitions 子节点静态、随 DTB 一起发布,主线默认正式产品
cmdlinepart内核 cmdline 的 mtdparts不改 DTB 即可调整,便于试产调参调试 / 试产
RedBoot 表等Flash 上的残留分区表格式兼容旧镜像,仅迁移期使用旧平台迁移
用户态创建ioctl / 工具动态创建运行期生效,重启即失效特殊场景

2.3 操作回调与并发

MTD 核心层与驱动之间靠一张回调表对话:读(read)、写(write)、擦除(erase)三大件,加上 OOB 读写、坏块查询、加锁等辅助入口。每条回调都有 MTD 核心层定好的语义:读要返回实际读出量与位错误信息;写以页为单位、页内顺序;擦除以块为单位、耗时最长且期间器件不可访问。驱动实现这些回调时,负责把语义翻译成具体的总线命令序列。

并发的规则比 API 表面看起来严格。同一颗 MTD 器件上的并发访问,由驱动层的锁串行化——两个进程同时 nandwrite 同一分区,落到器件上也排队执行;擦除进行中器件不可访问,此时对该分区的读写会等待。真正要小心的不是「并发」,而是「掉电打断」:擦除擦到一半掉电、写写了一半掉电,这块的状态由器件的物理性质决定,恢复策略属于文件系统与 UBI 的职责(第 7、8 章)。

回调语义单位实现要点
read读一页到缓冲区,返回实际读出量页位错误按阈值上报,可纠正的静默处理
write写一页,只能把位从 1 改 0页页内顺序写,不支持回头改
erase擦除一个块,耗时最长擦除块期间器件不可访问,状态需等待就绪
OOB 读写读写页外区域页坏块标记与 ECC 校验位的家
坏块查询报告某块是否坏块擦除块读坏块表,不直接访问器件
sync / lock刷新与加锁器件掉电前的统一刷新点
写与擦的边界:不要绕过回调直接操作器件

用户态拿到 /dev/mtdX 之后,ioctl 与读写接口仍然受同一套锁与语义保护;但绕过 MTD、直接用 SPI 用户态接口怼器件的做法,会把坏块检查、ECC、锁全部废掉——写进去的数据当时是对的,下一次读就可能因为坏块或位错误而损坏。量产烧录也要走 mtd-utils 或内核接口,不要自建总线直写。

2.4 ECC 与坏块在 MTD 中的位置

ECC 与坏块是 MTD 路径的两个「必修课」,它们的责任划分必须心里有数:ECC 的正确性与强度由驱动配置(用片上 ECC、控制器 ECC 引擎还是软件 ECC),坏块的标记与跳过由驱动维护的坏块表(BBT)负责,而「坏块之后怎么继续提供服务」是 UBI 与文件系统的事。三者串起来才是完整的答案:驱动保证「这一页读对了、这块跳过了」,UBI 保证「跳过坏块之后容量与磨损仍然均衡」,文件系统保证「掉电时正在写的元数据能恢复」。

配置层面的判断顺序:先看器件是否带片上 ECC(SPI NAND 多数带),能用片上就用片上;不能用再看 SoC 控制器的 ECC 引擎支持什么强度与步长;最后才考虑软件 ECC(只适用于小容量、低速场景)。强度与步长不是越大越好——步长决定每页要几次校验,强度决定纠错能力,两者都要与页大小、OOB 大小匹配,取值以器件与 SoC 手册的推荐组合为准。

职责谁负责配置入口配错的典型表现
ECC 计算与校验驱动 / 硬件引擎ECC 模式、强度、步长的选择位错误累积成读失败,日志刷 UBE
坏块标记与查询驱动(坏块表 BBT)OOB 标记或片上坏块表坏块被当成数据区,读写出错
坏块跳过策略MTD 核心层 / 驱动坏块是否允许被分区使用分区在坏块上建立,挂载即失败
坏块重映射与磨损UBI(若启用)卷表与 PEB 分配策略某几块被写穿,寿命远低于预期
掉电一致性文件系统 / UBI日志模式、原子提交设计掉电后文件系统需要全盘重建

2.5 字符设备与 sysfs 接口

注册成功之后,用户态能看到三样东西:/proc/mtd(分区总览,启动早期就能读,是排查「分区怎么切的」第一现场)、sysfs 里的 mtd 类属性(器件的 size / erasesize / writesize、位错误纠正与坏块的统计阈值等)、/dev/mtdX 与 /dev/mtdXro(字符设备,配合 ioctl 完成查询、擦除、读写 OOB;只读节点 ro 是刻意保留的安全阀,防止手滑)。此外还有一个 /dev/mtdblockX 块设备视图——第 1.3 节说过,它是简陋的可写块设备仿真,只适合只读挂载,不要当普通块设备用。

调试与量产绕不开 mtd-utils 这一套工具:mtdinfo 看器件与分区几何;flash_erase 擦除;nandwrite 烧写并处理 OOB 与坏块跳过;nanddump 连 OOB 一起导出;UBI 侧还有 ubiattach / ubiformat / ubinfo。把「dmesg 里的 mtd 注册日志 + mtdinfo -a 的输出」当成裸器件调试的起点,90% 的「分区不对、容量不对」在这两处就能定位(第 10 章展开)。

调试起手式:三行命令

拿到一块新板,裸器件侧先跑三行:dmesg | grep -i mtd 看器件是否注册、几何是否合理;cat /proc/mtd 看分区是否按设备树切出来;mtdinfo -a 看每个分区的擦除块、页与 OOB 细节。三行输出对不上设备树,问题就在设备树或绑定匹配上,不用往下走。

本章小结:MTD 核心层用 mtd_info 描述器件、用分区切分地址、用回调表与驱动对话;ECC 与坏块的责任在驱动,磨损与重映射的责任在 UBI,掉电一致性的责任在文件系统。下一章进入驱动的第一层——NAND 控制器驱动怎么把「读一页」这条语义变成总线上的动作。

3. NAND 控制器驱动

这一章进入驱动的第一层:一颗 NAND 接进 SoC,控制器驱动到底要干哪些活、raw NAND 核心层与控制器层之间的接口长什么样、ECC 引擎与时序怎么配、器件怎么被认出来。主线是:核心层定策略,控制器驱动定执行——移植一颗新 NAND,大部分工作发生在下层两格,上三层一般不用动。

从控制器驱动到器件注册与分区结构控制器驱动 probe时序、ECC 引擎、DMA片选与就绪检测器件识别READ ID / ONFI 参数页或 SFDP 参数表mtd_info 注册size / erasesizewritesize / OOB分区解析ofpart / cmdlinepart生成 mtdX设备节点/dev/mtdX 字符设备/dev/mtdblockXMTD 核心层看到的器件:一个地址空间,按擦除块对齐切分Flash 地址空间(偏移与大小都以擦除块为单位)u-bootmtd0envmtd1dtbmtd2kernelmtd3rootfsmtd4datamtd5每个分区是一段独立的 mtdX;坏块表与 ECC 配置仍归整个物理器件所有注册顺序不可逆:先有 mtd_info,才有分区;分区边界以擦除块为单位,重叠即注册失败分区来源默认取设备树(ofpart);命令行 mtdparts 可临时覆盖(第 6 章)
图 4 · 器件注册与分区结构

图 4 把「从 probe 到 /dev/mtdX」的注册链画成了一条流水线,下半部分是 MTD 核心层最终看到的景象:一个完整的 Flash 地址空间,按擦除块对齐切成若干分区,每段生成一个 mtdX。这条链有三个工程含义:其一,注册失败(probe 阶段就报错)时用户态什么都看不到,排查只能看 dmesg;其二,分区切得对不对,在 /proc/mtd 里一眼能验证;其三,坏块表与 ECC 配置属于整个物理器件,不随分区划分而变化。

注册阶段触发条件产物失败时的现象
控制器驱动 probe设备树 compatible 匹配到控制器nand_controller 就绪dmesg 无控制器日志,器件完全不出现
器件识别片选上有器件响应容量、页大小、厂商信息识别失败,probe 直接退出
mtd_info 注册几何与回调填充完成mtdX 出现在 /proc/mtd内核 panic 或注册日志缺失
分区解析设备树 partitions 节点存在每个分区成为独立 mtdX只有一个整片 mtd0,无子分区
设备节点注册成功/dev/mtdX 与 /dev/mtdblockX用户态工具报找不到设备

3.1 控制器驱动的职责划分

一颗 SoC 上的 NAND 控制器,驱动要同时伺候三样东西:总线(怎么把命令、地址、数据送到器件)、引擎(ECC 与 DMA 这两个硬件块怎么用)、时序(跑多快器件才受得了)。这三样在代码里分成两个层次:raw NAND 核心层(所有 NAND 通用)负责命令序列的编排、坏块表与器件模型;控制器驱动(SoC 厂商提供)负责把每条指令翻译成对控制器寄存器的操作、配置 ECC 引擎、发起 DMA。

划分的好处是可移植性:换一颗 NAND 器件,改的只是核心层的器件参数;换一个 SoC,改的只是控制器驱动。坏味道是反过来写的驱动——把命令编排也塞进控制器驱动,换器件就要动驱动。判断一份驱动是否「分层干净」,看它是否实现了核心层定义的执行入口(exec_op 或旧式的 cmdfunc),而不是自己另起炉灶。

职责归属层具体内容配置入口
命令序列编排raw NAND 核心层读页 / 写页 / 擦块的指令顺序器件参数(页大小、时序档)
指令执行控制器驱动命令 / 地址 / 数据的锁存与搬运exec_op 或 cmdfunc 实现
片选与就绪控制器驱动选择器件、等待就绪引脚设备树片选号与就绪属性
ECC 计算校验控制器驱动 + 硬件引擎纠错码计算、校验、错误上报ECC 模式、强度、步长
坏块表raw NAND 核心层扫描、缓存、查询、标记坏块表存放位置的选择
DMA控制器驱动大数据量搬运,卸载 CPU缓冲区对齐与长度约束
时序协商控制器驱动速率档位与参数下发ONFI 时序模式或等效配置
NAND 控制器驱动分层:核心层定策略,控制器驱动定执行MTD 核心层mtd_info 操作回调、分区、字符设备raw NAND 核心层nand_chip / nand_controller、exec_op 指令序列、坏块表控制器驱动层exec_op 实现、片选、就绪引脚、命令锁存硬件引擎层ECC 引擎(BCH / RS / 片上 ECC)、DMA、时序寄存器NAND 器件ONFI / Toggle 并行 NAND,或 SPI NANDECC 引擎on-die ECC 或控制器 ECC强度与步长随页大小配置以器件与 SoC 手册为准时序参数ONFI 时序模式或等效配置经 setup_data_interface 下发随速率档位协商确定坏块管理坏块表存于 OOB 或片上nand-on-flash-bbt 可加速坏块跳过由核心层统一处理分层的意义:控制器驱动只回答「怎么把一条指令送到器件」,ECC 与坏块的策略在核心层与驱动配置里移植一颗新 NAND,多数工作是重写下层两格;上三格一般不用动
图 5 · NAND 控制器驱动分层

图 5 给出分层的全貌与三条横向配置线。ECC 引擎的选择顺序是:器件带片上 ECC 优先用片上;否则用 SoC 控制器的硬件引擎;软件 ECC 只留给小容量低速场景。时序参数经核心层统一下发,速率档位是协商结果而不是单方面设定。坏块管理里最值得调的一个开关是「坏块表是否存放在器件上」(nand-on-flash-bbt)——打开后上电不必全片扫描坏块,启动更快,代价是表本身也要维护。

经验法则:先确认分层,再谈移植

拿到一份厂商 NAND 驱动,先问三个问题:它实现了 exec_op 还是老式 cmdfunc?ECC 是走硬件引擎还是软件循环?坏块表放内存还是放器件?三个答案决定了移植工作量:exec_op + 硬件 ECC + 片上坏块表的驱动最接近主线结构,往新内核移植最省事;三者都是老式实现的,基本按重写估算。

3.2 raw NAND 层与控制器层的接口

两层之间的接口经历过一次大改:老式接口是一堆「一条命令一个函数」的 cmdfunc——发命令、发地址、读数据各一个入口,灵活但难以描述一次操作里的时序细节;新式接口是 exec_op:把一次完整操作(命令 + 地址 + 等待 + 数据 + 方向)打包成一个指令序列,一次性交给控制器执行。新接口让控制器有机会做优化(合并等待、流水 DMA),也让「哪些线数 / 速率组合支持」变得可声明。

对配置的影响很直接:用 exec_op 的驱动,设备树里声明线宽、速率时,控制器能回答「支持 / 不支持」;用 cmdfunc 的老驱动,这些声明常常被静默忽略,跑出问题才发现根本没按四线在跑。移植时如果设备树声明了四线但实测是单线速度,第一个要怀疑的就是驱动还在用老接口。

对比项老式 cmdfunc 接口exec_op 接口
操作描述拆成多个函数调用,逐步下发一条指令序列一次提交
等待处理驱动自己轮询或中断指令内声明等待周期与就绪检测
线宽 / 速率难以声明,常被忽略可通过 supports_op 声明
DMA 协同逐个函数手工触发序列级优化,整段搬运
可移植性控制器驱动各自为政核心层统一,驱动只实现执行
主线状态逐步废弃,仅存量驱动当前推荐结构

3.3 ECC 引擎的配置

ECC 配置要同时定三件事:用哪个引擎(片上 / 控制器 / 软件)、多强的纠错(强度)、多大的校验单元(步长)。步长与页大小、OOB 大小强相关:页越大、步长越大,校验次数越少,但单次校验覆盖的数据越多、出错后损失越大;强度决定能纠几个位,选小了位错误累积成不可纠,选大了 OOB 放不下、延迟还高。取值以器件与 SoC 手册的推荐组合为准,不建议自创组合。

一个常被忽略的细节是 OOB 布局的分配权:坏块标记、ECC 校验位、文件系统元数据都住在 OOB 里,布局由驱动或引擎配置决定,用户态工具(nandwrite 的 OOB 选项)只是按同一布局读写。换了 ECC 配置再读旧镜像,往往不是数据坏了,而是校验位位置对不上——量产烧录镜像与驱动 ECC 配置必须成对管理。

ECC 模式适用场景优点代价与限制
片上 ECCSPI NAND 与部分并行 NAND无需控制器引擎,配置简单强度固定,按器件档位选
控制器硬件引擎多数 SoC 的并行 NAND强度步长可配,CPU 占用低受引擎能力上限约束
软件 ECC小容量、低速、无引擎场景不依赖硬件CPU 占用高,强度有限
无 ECC仅 SLC 小容量调试场景最简位错误不可纠,不可量产
易错点:镜像与 ECC 配置不成对

量产最常见的返工之一是「镜像烧进去能启动,升级后再读数据全错」——根因是两次烧录用了不同的 ECC 配置,校验位在 OOB 里的位置变了。把 ECC 配置(引擎、强度、步长、OOB 布局)与镜像版本一起归档,烧录脚本从同一份配置生成,不允许现场手改。

3.4 时序参数的下发

并行 NAND 的速率协商走 ONFI 或 Toggle 一类的标准机制:器件在上电时报告自己支持哪些时序档位,控制器按档位把一组时序参数(建立时间、保持时间、访问周期一类)写进自己的寄存器。档位越高越快,但对信号完整性的要求也越严——PCB 布线差的板子,高档位表现为偶发读错误,且往往查不出原理问题。拿不准时先降一档验证,把「时序问题」与「器件问题」分开。

SPI 侧的「时序」概念不同:没有 ONFI 档位,速率是控制器与器件按 spi-max-frequency 一类属性协商出的一个上限,真正限制常常来自信号质量而不是协议。设备树里写了高速率、器件手册支持、控制器也支持,实测仍掉码,优先查布线(第 4 章)。

机制谁报告能力谁下发参数配置位置常见问题
ONFI 时序档器件参数页控制器 setup_data_interface速率档位选择档位过高偶发读错
Toggle DDR器件 READ ID 后的特性控制器等效配置控制器驱动选项布线不满足时误码
SPI 速率器件手册上限spi-mem 协商设备树速率属性声明速率超器件能力
就绪检测方式器件 / 控制器能力控制器驱动就绪引脚或轮询轮询方式拖慢擦除等待

3.5 芯片探测与识别

器件识别的流程各总线大同小异:上电后控制器向片选发读 ID 命令,拿回厂商 ID 与器件 ID,核心层拿这组 ID 去查内置的器件表,命中就填好容量、页大小、OOB 布局等参数。并行 NAND 还可以进一步读 ONFI 参数页,把容量、时序档、ECC 要求全部从器件侧拿到;SPI NAND 与 SPI NOR 各有自己的参数表(SFDP 或厂商参数页)作为补充。表里没有的型号不是不能用——多数驱动支持按「容量 + 页大小 + OOB」手工声明一个兼容项,但新器件优先推动厂商把 ID 补进器件表,手工声明只作试产应急。

探测步骤动作拿到的信息失败的处理
片选使能选中目标器件器件在线与否重试与超时,最终 probe 失败
读 ID发读 ID 命令厂商 ID、器件 ID换片选 / 查硬件连线
查器件表ID 匹配内置表容量、页大小、OOB手工声明兼容项
读参数页ONFI / SFDP / 厂商页时序档、ECC 要求、特性位回退到器件表默认值
坏块扫描建坏块表(可选放器件)坏块清单扫描失败按保守策略处理
经验法则:识别失败的排查顺序

识别失败的排查顺序固定为:供电与时钟 → 总线连接(片选、极性)→ 读 ID 命令本身(线宽、速率)→ 器件表匹配。前两项是硬件问题,占实际案例的大多数;后两项才是软件。拿万用表量过供电、示波器看过片选波形再怀疑驱动,能省下大量时间。

本章小结:NAND 控制器驱动的分层是「核心层定策略、控制器定执行」;exec_op 是新式接口的标志;ECC 三要素(引擎、强度、步长)与 OOB 布局必须和镜像成对管理;时序是协商不是单方面声明;识别失败先查硬件再查软件。下一章把这套思路搬到 SPI 总线,看 spi-mem 抽象怎么统一 SPI NOR 与 SPI NAND 的驱动结构。

4. SPI MEM 框架与 SPI 存储驱动

SPI 总线上的存储(SPI NOR / SPI NAND)走的是与并行 NAND 不同的框架:spi-mem 抽象层把「一条总线操作」结构体化,存储驱动(spi-nor / spi-nand)在它之上实现 MTD 接口。这一章的核心是理解 spi-mem 的抽象边界:它管总线操作,不管存储语义;存储语义归 spi-nor / spi-nand,最终仍汇入 MTD。

spi-mem 抽象与 SPI 存储驱动:一条操作的结构体化描述spi_mem_op:一条操作的描述opcode命令码(可含扩展)addr地址宽度与线数dummy等待周期数data方向 / 线数 / 长度opcode 与 addr 各自可指定线数:四线 / 八线协议由此拼装(4.4 节)spi-mem 之上的驱动栈MTD 核心层(mtd_info)spi-nor / spi-nand 驱动spi-mem 核心(spi_mem)SPI 控制器驱动(adapter)控制器驱动实现 supports_op 与 exec_op:不支持的线数 / 速率组合由上层协商降级exec_op同步执行一条 spi_mem_op读 / 写 / 擦除的公共入口dirmap内存映射读写通道XIP 与大数据量读的加速路径厂商操作表spi-nand 的厂级差异以 manufacture_ops 形式挂接spi-mem 把「一条总线操作」变成结构体,存储驱动不再直接拼 SPI 消息控制器能力通过 supports_op 声明,上层按能力协商;这是 SPI 存储驱动可移植的根源
图 6 · spi-mem 抽象与 SPI 存储驱动

图 6 左右对照:左边是一次操作的四个成分——opcode(命令码)、addr(地址与线宽)、dummy(等待周期)、data(方向 / 线数 / 长度),四线、八线协议就是这四个成分的线数组合;右边是 spi-mem 之上的驱动栈,最下面一格是 SPI 控制器驱动,它通过 supports_op 声明能力、通过 exec_op 执行操作。理解这张图,就理解了「为什么 SPI 存储驱动可以不管控制器差异」——差异全被 supports_op 挡住了。

spi-mem 概念回答的问题说明
spi_mem_op一次操作怎么描述命令、地址、等待、数据四成分的集合
spi_mem_adapter控制器能做什么supports_op 声明支持的操作组合
exec_op怎么执行同步提交一条操作,等待完成
dirmap怎么加速大读内存映射读写通道,XIP 的基础
adjust_op怎么适配硬件控制器对操作的微调钩子

4.1 spi-mem 抽象层

spi-mem 存在的理由是SPI 存储操作越来越复杂,老接口描述不了:一条读操作可能是「单线发命令、单线发地址、四线读数据、中间插若干等待周期」,这在老式「消息 + 传输」模型里要拼多个传输段,控制器还无法判断整条操作是否支持。spi-mem 把整条操作变成一个结构体,控制器拿到完整信息才能回答「支持 / 不支持」,也才能做真正的优化。

对驱动开发者的实际影响:写 SPI 存储驱动时不再直接碰 SPI 消息,而是构造 spi_mem_op 调 exec_op;写控制器驱动时不再只实现「传一个字节」,而是实现「执行一条操作」并声明能力。这个抽象也带来了一个调试福利——启动日志里能直接看到协商后的操作参数(线数、速率),排查「为什么没跑上四线」时比老框架容易得多。

4.2 SPI NOR 驱动结构

spi-nor 驱动的结构可以概括为「一个核心 + 一张参数表 + 若干后端的组合」:核心(spi_nor)负责把 MTD 的读 / 写 / erase 翻译成 spi_mem_op;参数表按器件 ID 与 SFDP 描述容量、页大小、擦除块大小、四线使能方式;后端处理厂商特有的使能序列(四线使能、八线使能、软件保护)。SPI NOR 没有坏块、没有片上 ECC,它在 MTD 世界里是最「干净」的器件——配置项少,主要工作量在设备树与分区,而不是 ECC。

两个值得记住的能力。其一是 XIP(就地执行):NOR 支持内存映射读,代码可以直接在 Flash 上运行,省掉把根文件系统搬进内存的开销——对启动时间敏感的小系统很有价值。其二是 SFDP(串行 Flash 可发现参数):新器件把自己的能力标准化报告,驱动不必为每个新型号加表,设备树里的 compatible 可以尽量写通用值,让 SFDP 说话。

spi-nor 组成职责与设备树的关联
核心(spi_nor)MTD 操作到 spi_mem_op 的翻译决定 /dev/mtdX 的行为
器件参数表容量、擦除块、页大小、使能序列compatible 匹配到具体型号
SFDP 解析从器件读标准化能力减少对具体型号的依赖
厂商后端四线 / 八线使能、软件保护影响线宽协商结果
dirmap / XIP内存映射读通道影响启动性能与代码就地执行

4.3 SPI NAND 驱动结构

spi-nand 的结构与 spi-nor 平行,但多两样东西:ECC 与坏块。spi-nand 核心把 MTD 的读 / 写 / erase 翻译成 SPI 命令序列,ECC 默认用片上 ECC(多数 SPI NAND 内置),强度按器件档位选择;坏块表沿用 raw NAND 核心层那一套。厂商差异比 NOR 大——不同厂家的使能、状态寄存器、缓存操作各有花样,核心层用一张「厂商操作表」把这些差异挂接进来,驱动移植时主要工作就是填这张表。

与 SPI NOR 的选择判断很简单:要容量、要可写、能接受坏块管理 → SPI NAND;要 XIP、要简单、容量需求不大 → SPI NOR。两者的设备树结构几乎一样(都是 SPI 总线上的子节点),差别在 compatible 与 ECC / 坏块相关属性——这也是为什么第 5 章讲设备树时用一套结构覆盖两者。

对比项SPI NORSPI NAND
MTD 语义读 / 写 / 擦除,无坏块读 / 写 / 擦除,有坏块
ECC不需要默认片上 ECC,强度按档位
访问单位页 + 大擦除块页 + 擦除块,与并行 NAND 同构
XIP支持(内存映射)通常不支持
设备树差异compatible 与线宽属性增加 ECC 与坏块相关属性
典型用途启动代码、小根文件系统大容量数据、可写文件系统

4.4 四线与八线模式协商

线宽不是「写了属性就生效」的单方面声明,而是器件能力、设备树请求、控制器能力三者的交集。设备树用 spi-rx-bus-width / spi-tx-bus-width 声明收 / 发方向各用几根线;控制器通过 supports_op 声明能执行哪些线数组合;器件通过 SFDP 或参数页声明自己支持哪些模式。三者取交集,驱动按结果拼装操作——请求超出能力时不报错,而是静默降级,只在启动日志里留下记录。这个「静默降级」是排查线宽问题必须知道的第一个知识点。

八线(Octal)是更激进的模式:命令、地址、数据都可能走八线,还需要器件特定的使能序列与配置段(常涉及非易失配置寄存器的修改,改错会让器件「消失」)。八线的使能顺序必须严格按器件手册执行,且一旦在非易失配置里写错,恢复手段有限——试产阶段先在样板上验证完整流程,再往量产配置里固化。

四线与八线模式协商:器件能力、设备树请求与控制器能力的交集器件能力SFDP / 参数页声明的可寻址线数与协议设备树请求spi-rx-bus-widthspi-tx-bus-width控制器能力adapter 的 supports_op能跑哪些线数组合模式协商取三者交集,能力不足逐档降级单线 1-1-1兼容兜底档四线 1-1-4最常见配置四线 1-4-4地址也走四线八线 1-1-8需设备树声明支持协商是逐项能力求交集:设备树声明的是「请求」,控制器与器件声明的是「能力」请求超出能力时驱动降级并在启动日志留记录;八线通常还需额外使能序列,以器件手册为准
图 7 · 四线与八线模式协商

图 7 把协商画成了一次「求交」:三张卡片分别是器件能力、设备树请求与控制器能力,汇入模式协商后输出一档具体模式。常用档位从单线兜底到八线,中间是四线的两个变体(地址单线 / 地址四线)。选档的原则是「够用就好」:线数越多,对信号完整性的要求越高,PCB 上并行走线越长越容易出问题;容量不大、速率要求不高的场景,四线通常是性价比最优解。

排查「线宽没生效」的三个提问

现象是「设备树写了四线,速度却是单线」。按顺序问三个问题:一、启动日志里协商后的线宽是几?(日志会记录实际使用的操作参数)二、控制器驱动实现了 supports_op 吗、支持四线组合吗?(老驱动常常不支持)三、器件的四线使能序列执行了吗?(有些器件要发一条使能命令才进四线,漏了就一直单线)。三个问题定位不到再怀疑硬件。

4.5 与旧式框架的差异

很多在产项目跑的是几年前的厂商内核,SPI 存储那一层还是旧式结构:一个 m25p80 驱动包打天下、直接拼 SPI 消息、四线靠全局开关。往新内核迁移时,设备树侧最大的变化是 compatible 的取值与线宽属性的改名,驱动侧则是把「拼消息」改成「构造 spi_mem_op」。迁移前先确认目标内核的绑定文档版本——同一个属性名在不同内核里含义可能不同(第 11 章展开)。

维度旧式框架spi-mem 框架
驱动组织m25p80 一类大驱动spi-nor / spi-nand 分离
操作描述拼 SPI 消息与传输spi_mem_op 结构体
线宽声明全局开关或驱动内写死设备树属性 + 能力协商
控制器适配每驱动各自实现adapter 统一接口
设备树兼容值厂商型号字符串通用 compatible + SFDP
大读优化无统一通道dirmap 内存映射
迁移判断:改设备树还是改驱动

从旧内核迁到新内核,先做一次「绑定文档对表」:把旧设备树里每个属性在新绑定文档里查一遍。属性还在、含义没变 → 只改驱动;属性被改名或废弃 → 先改设备树再验驱动;兼容值需要换 → 改设备树 compatible。顺序反了会同时面对两个变量,问题无法定位。

本章小结:spi-mem 把一条总线操作结构体化,控制器用 supports_op 声明能力、exec_op 执行操作;spi-nor 干净(无 ECC 无坏块、支持 XIP),spi-nand 与并行 NAND 同构(有 ECC 有坏块);线宽是三方协商的结果,不生效时按「日志 → 控制器能力 → 使能序列」顺序排查。下一章回到设备树,把这些驱动参数一个个对应到树上的属性。

相关报告 · 交叉引用 4 器件全部展开收起 ▾
点开类目即可展开:具体文档 / 章节(点章直接跳转到该章)
SPI NOR
电路设计指南
软件设计规范
Linux 驱动开发
样机问题排查
量产问题排查2
系统设计指南
SPI NAND
电路设计指南
软件设计规范
Linux 驱动开发
样机问题排查
量产问题排查4
全志平台 SPI NAND NOR
章节(点章跳转,共 11 章)
海思平台 SPI NAND 量产常见失败
章节(点章跳转,共 11 章)
烧录成功但不开机 SPI NAND
章节(点章跳转,共 12 章)
瑞芯微平台 SPI NAND eMMC
章节(点章跳转,共 11 章)
系统设计指南3
eMMC
电路设计指南
软件设计规范
Linux 驱动开发
样机问题排查
量产问题排查