第一章 RK平台存储接口全景概览
做RK平台存储驱动开发,第一件事不是翻芯片手册,而是搞清楚整个存储子系统长什么样。我见过不少工程师上来就啃eMMC协议,结果连DDR配置都没摸对,折腾两周发现是时钟树配错了。所以这一章,我们先搭个全局框架。
1.1 RK存储子系统架构
RK的存储子系统,说白了就是一条数据通路:CPU发指令,经过总线,到达存储控制器,再跟物理介质打交道。中间每一层都可能成为瓶颈。
核心分层(从上到下):
- VFS层 — 虚拟文件系统,屏蔽底层差异
- 块设备层 — 处理IO调度、合并、排队
- MTD/NAND层 — 专为NAND Flash设计的中间件
- 主机控制器驱动 — 直接操作寄存器,发命令
- 物理接口 — eMMC/SD/NAND/SPI Nor的电气协议
我个人习惯把控制器驱动看作"翻译官"——它把上层标准命令翻译成物理接口能懂的时序。RK的存储控制器集成在SoC内部,不同系列(RK33xx、RK35xx)的寄存器布局差异不小,但设计思想一脉相承。
1.2 主流存储接口对比
RK平台常见的存储介质有四种:eMMC、SD卡、NAND Flash、SPI Nor Flash。它们各有各的脾气。我整理了一张对比表,方便你快速选型。
| 特性 | eMMC | SD卡 | NAND Flash | SPI Nor Flash |
|---|---|---|---|---|
| 接口协议 | MMC 5.1 / HS400 | SD 3.0 / UHS-I | ONFI / Toggle | SPI / QSPI / OSPI |
| 最大速率 | 400 MB/s (HS400) | 104 MB/s (SDR104) | ~400 MT/s (DDR) | ~200 MB/s (OSPI DDR) |
| 坏块管理 | 内置 | 内置 | 驱动层处理 | 无(需应用层处理) |
| 寿命管理 | 内置磨损均衡 | 内置 | 驱动层实现 | 无 |
| 典型容量 | 4GB ~ 256GB | 2GB ~ 1TB | 128MB ~ 8GB | 1MB ~ 256MB |
| 启动支持 | 支持(Boot分区) | 不支持 | 支持(需Boot ROM配合) | 支持(XIP执行) |
| 驱动复杂度 | 中等 | 中等 | 高 | 低 |
选型建议:
- 量产产品首选eMMC,省心,坏块和磨损均衡都帮你管好了
- 低成本方案用NAND,但驱动工作量翻倍,我踩过不少坑
- SPI Nor只适合存固件或配置文件,别拿它当主存储
- SD卡适合调试阶段或消费类产品,工业场景慎用(接触可靠性问题)
1.3 RK芯片存储控制器特性
RK的存储控制器不是自己从头造的,而是集成了DesignWare IP。嗯,这里要注意:不同系列用的IP版本不一样,寄存器偏移和功能位有差异。
1.3.1 eMMC/SD控制器 (DW_mmc)
RK33xx和RK35xx都用的Synopsys DesignWare移动存储主机控制器。我最早在RK3288上调试eMMC时,被它的FIFO阈值配置坑过一次——默认值在HS400模式下会溢出。
- 时钟范围: 100kHz ~ 200MHz(取决于分频配置)
- 数据宽度: 1/4/8位,支持DDR模式
- DMA支持: IDMAC(内部DMA)或GDMA(通用DMA)
- 命令队列: 支持CMD Queue(eMMC 5.1)
- 电压: 1.8V / 3.3V 双电压
避坑指南: 我曾经在RK3399上遇到eMMC初始化失败,查了三天发现是时钟相位没调好。DW_mmc的时钟延迟链(Delay Chain)需要根据PCB走线长度做校准,别偷懒用默认值。
1.3.2 NAND控制器 (NANDC)
RK的NAND控制器相对"原始",不像eMMC那么智能。它只负责发命令、传数据,坏块管理和ECC都得驱动层自己干。
- 支持类型: SLC/MLC/TLC,ONFI 2.0/3.0
- ECC: 硬件BCH引擎(最多72bit/1KB)
- 页大小: 2KB/4KB/8KB/16KB
- 随机读: 约25μs(典型值)
- 编程时间: 约500μs ~ 2ms(取决于工艺)
说实话,NAND驱动是RK平台里最考验功力的部分。我见过不少团队直接拿公版驱动改,结果量产三个月后坏块率飙升——因为没做动态坏块映射。
1.3.3 SPI控制器
SPI Nor的控制器相对简单,但RK在RK35xx系列里加入了QSPI和OSPI支持,速率提升明显。
| 模式 | 数据线 | 最大时钟 | 理论带宽 |
|---|---|---|---|
| SPI | 1条 | 50MHz | 6.25 MB/s |
| Dual SPI | 2条 | 50MHz | 12.5 MB/s |
| Quad SPI (QSPI) | 4条 | 50MHz | 25 MB/s |
| Octal SPI (OSPI) | 8条 | 100MHz (DDR) | 200 MB/s |
关键寄存器概览(以DW_mmc为例):
// 控制器版本寄存器
#define DW_MMC_CTRL 0x000 // 控制寄存器
#define DW_MMC_CLKDIV 0x00C // 时钟分频
#define DW_MMC_CMD 0x010 // 命令寄存器
#define DW_MMC_RESP0 0x014 // 响应寄存器0
#define DW_MMC_RESP1 0x018 // 响应寄存器1
#define DW_MMC_RINTSTS 0x01C // 原始中断状态
#define DW_MMC_INTMASK 0x020 // 中断掩码
#define DW_MMC_FIFOTH 0x028 // FIFO阈值
#define DW_MMC_BLKSIZ 0x04C // 块大小
#define DW_MMC_BYTCNT 0x050 // 字节计数
#define DW_MMC_DBADDR 0x058 // DMA描述符地址
你想想看,这些寄存器就是你和硬件对话的"电话"。每个bit都对应一个硬件行为。我调试时习惯先把RINTSTS寄存器打印出来,看中断状态位就知道卡在哪一步了。
1.4 小结
这一章我们搭好了RK存储接口的知识框架。记住三点:
- 存储子系统是分层结构,问题定位要逐层排查
- 四种接口各有适用场景,eMMC最省心,NAND最折腾
- 控制器寄存器是调试的入口,先读状态寄存器再动手