RK eMMC命令与数据传输机制:CMD/ADMA描述符链、DMA传输模式、PIO模式、命令超时处理

好,咱们进入第四章。这一章讲的是eMMC通信的核心——命令怎么发,数据怎么传。说白了,就是Host和eMMC设备之间怎么“对话”。很多驱动问题,比如卡死在某个命令上、数据传输到一半断了、或者速度上不去,根子都在这里。

我个人习惯,拿到一个新平台,先把命令传输和数据传输这两条路径理清楚。路径通了,剩下的就是调参和修修补补。

4.1 命令传输:从Host到Device的“指令通道”

eMMC协议里,命令是通过CMD线发送的。Host发一个命令,Device回一个响应。看起来简单,但实际工程里,命令超时是最常见的故障之一。

命令传输的基本流程:

  • Host构造命令结构体(包含命令索引、参数、响应类型)
  • 写入命令寄存器,触发发送
  • 等待设备响应(硬件自动接收,或中断通知)
  • 检查响应状态,判断命令是否成功

我曾经遇到过一个案子,板子启动时总是在初始化阶段卡死。抓日志发现是发送CMD1(查询电压范围)后没收到响应。查了一圈,发现是命令超时时间设得太短,设备上电慢,还没来得及回。嗯,这里要注意:命令超时时间不能一刀切,不同阶段、不同设备,超时值要区别对待。

4.1.1 命令超时处理机制

为什么会超时?无非三种情况:

  1. 设备没响应——设备挂了、没上电、或者CMD线断了
  2. 响应被噪声干扰——信号完整性差,CRC校验失败
  3. Host没等到中断——中断配置错误,或者DMA没完成

我建议在驱动里实现一个分级超时策略:

命令类型 典型超时值 说明
CMD0(复位) 100ms 设备复位需要时间
CMD1(初始化) 1s 设备上电初始化可能较慢
CMD6(切换模式) 500ms 模式切换涉及内部操作
CMD18(多块读) 根据块数动态计算 数据量大时超时要放宽

避坑指南:我曾经在调试一个eMMC读写异常时,发现CMD6(切换高速模式)总是超时。后来用示波器抓CMD线,发现设备确实回了响应,但Host的中断没触发。原因是中断控制器里该中断被屏蔽了。所以,超时不一定就是设备的问题,先检查中断路径。

4.2 数据传输模式:DMA vs PIO

数据传输是eMMC性能的关键。RK平台支持两种模式:DMA(直接内存访问)和PIO(程序控制输入输出)。你想想看,PIO模式下CPU要亲自搬数据,DMA模式下CPU只需要告诉硬件“你去搬”,然后就可以干别的了。

4.2.1 PIO模式

PIO模式,说白了就是CPU一个一个字节地读写数据寄存器。优点是实现简单,不需要复杂的DMA描述符链。缺点是CPU占用率高,速度上不去。

  • 适用于:小数据量传输(比如读取设备CID、CSD寄存器)
  • 缺点:大数据量时CPU被拖死,系统响应变慢
  • 我建议:只在初始化阶段或调试时用PIO,正常读写用DMA

4.2.2 DMA传输模式

DMA模式是主流。RK平台的DMA控制器支持ADMA(高级DMA),它使用描述符链来管理数据传输。描述符链是什么?就是一个链表,每个节点描述一段内存的地址、长度和下一个节点的指针。

个人经验:我在项目中遇到过DMA传输到一半卡死的情况。查到最后发现是描述符链的最后一个节点没有设置“结束标志”。硬件不知道传输结束,就一直等。所以,描述符链的结尾一定要正确标记。

4.3 ADMA描述符链详解

ADMA描述符链是RK eMMC控制器实现高效数据传输的核心。每个描述符是一个16字节的结构体,包含:

偏移 字段 说明
0x00 属性(Valid/End/Int/Act) 控制位:有效、结束、中断、操作类型
0x04 数据长度 本次传输的字节数
0x08 数据地址 内存缓冲区的物理地址
0x0C 下一个描述符地址 指向链中下一个描述符

描述符链的工作流程:

  1. Host在内存中构建描述符链
  2. 将链首地址写入控制器的ADMA寄存器
  3. 启动DMA传输
  4. 硬件自动遍历描述符链,完成所有数据传输
  5. 遇到End标志或错误时停止,触发中断

关键点:描述符链的地址必须是物理地址,不能是虚拟地址。因为DMA控制器直接访问内存,不经过MMU。很多新手在这里翻车,传了个虚拟地址进去,DMA直接访问了错误的内存区域。

4.4 核心逻辑流程图

下面这张图展示了从命令发送到数据传输完成的完整流程。我把它画出来,方便你对照代码理解。

eMMC命令与数据传输流程 构造命令结构体 写入命令寄存器 等待设备响应 超时 超时处理 判断是否有数据传输 无数据 命令完成 有数据 选择传输模式 DMA PIO ADMA描述符链传输 PIO逐字节传输 传输完成,触发中断 说明 DMA模式适合大数据量 PIO模式适合小数据量

4.5 实际调试中的常见问题

最后,我总结几个我在RK平台上实际踩过的坑:

  • 描述符链地址不对齐:ADMA要求描述符链首地址按32字节对齐。我遇到过因为结构体成员对齐问题,导致地址没对齐,DMA直接报错。
  • Cache一致性问题:DMA传输的数据在内存中,CPU可能通过Cache访问同一块内存。如果不做Cache刷新或使用DMA一致性内存,数据会乱掉。我建议在分配DMA缓冲区时使用dma_alloc_coherent。
  • 中断丢失:如果中断处理函数里没有清中断标志位,或者中断优先级太低被其他中断淹没了,会导致传输完成但Host不知道。我曾经在调试时发现中断状态寄存器里已经置位了,但CPU没进中断处理函数——查到最后是中断控制器里该中断被屏蔽了。

调试小技巧:当怀疑DMA传输有问题时,先切到PIO模式试试。如果PIO模式能正常工作,那问题大概率出在DMA描述符链或DMA控制器配置上。这个排除法我屡试不爽。