1. SDIO -110 错误的本质与系统定位
调 WiFi 模块的时候,如果你在内核日志里翻到-110这个返回值,基本意味着 SDIO 总线上的某一次命令或者数据传输已经超过内核规定的等待上限,被强制判定为超时了。这个错误码在不同子系统里有不同含义,而在 Linux 的 MMC/SDIO 子系统里,-110对应的是-ETIMEDOUT,也就是"等待对方回应,等了规定时间还没等到"。WiFi 类模块走 SDIO 接口的非常多,从常见的 SDIO WiFi 芯片到各种模组,只要以 SDIO 方式挂到 SoC 的 SDMMC 控制器上,就有机会撞见这个报错。
我写这篇东西,是想把 sdio -110 从日志表象一直扒到硬件和驱动的根子上,因为大多数遇到这个错的人,第一反应是去改 wifi 驱动源码,结果改了半天发现根本没碰对地方。这篇文章适合几类人看:正在做嵌入式 WiFi 移植的驱动工程师、调试 SDIO 外设的硬件工程师、以及跑 Linux 板子时被mmc0: timeout刷屏的同学。我会把 SDIO 报错的常见几个阶段拆开讲,告诉你每一类 -110 分别指向什么病灶,再给一套从软件到硬件的排查路径,尽量让你能直接抄着用。
先说结论:-110从来不是一个孤立的错误,它几乎总是成串出现的,而且报出来的位置往往不是真正的故障点。真正要做的,是看它在哪个命令上超的时、超时前后还输出了什么,再顺着信号链一路往上摸。
2. 从枚举到传数据:搞清 -110 卡在哪一环
2.1 SDIO 通信的完整链路长什么样
要定位 -110,得先知道一次 SDIO 交互到底经过了哪些环节。SDIO 是建立在 SD 总线基础上的,物理上就是那几根线:CLK、CMD、DAT0~DAT3(四线模式)或者只用DAT0(一线模式)。SoC 这一侧是 SDMMC host controller,负责产生时钟、发送命令、搬运数据;模块那一侧是 SDIO device,负责响应。中间没有握手协议里的重传机制,一次命令发出去,等应答,等不到就是等不到,硬件控制器到点就报超时。
命令层面分成两类:CMD52用于读写单个字节的寄存器,CMD53用于块读写,WiFi 模块传数据主要靠CMD53。枚举阶段会依次发CMD5(询问 IO 能力)、CMD3(分配相对地址 RCA)、CMD7(选中卡)、CMD52去配置功能寄存器,然后启用中断、初始化 WiFi 固件,最后进入数据收发。每个节点都可能超时,但含义完全不同。
内存和总线这一侧同样关键。SoC 侧的数据通常走 DMA,DMA 要正确配置描述符和地址对齐;时钟则由分频器从 PLL 分出来,max-frequency决定上限。任何一环没对齐,命令就会石沉大海,最后以 -110 的形式爆出来。
2.2 不同阶段的 -110 指向不同的病灶
枚举阶段的 -110,比如mmc0: error -110 whilst initialising SDIO card或者mmc_sdio_init_card里的超时,问题多半在物理层最底层:供电没稳住、时钟太乱、上拉电阻缺失、复位时序不对,或者模块压根没上电。这个阶段驱动还没跑起来,软件参数基本没机会使坏,先怀疑硬件绝对不亏。
固件下载阶段的 -110,典型的是CMD53 write timeout刷屏,模块已经枚举成功了,但往它里面灌固件或者写配置的时候卡住。这种情况我遇到最多的是两个原因:一是 SDIO 时钟频率给高了,模块在低电压或者长走线下跑不到那么快;二是供电在突发大电流下塌下去,模块瞬间掉线,命令自然没响应。
运行阶段的零星 -110,跑了几个小时才偶尔冒一条,往往是信号完整性或者电源管理的问题。比如runtime PM把模块挂起了但你还在发数据,或者时钟在省电模式被关掉还没恢复,又或者走线串扰在高温下变严重。这类最难缠,得靠长时间压测加示波器组合拳去抓。
3. 硬件层排查:供电、时钟与信号完整性
3.1 供电能力直接决定能不能跑圆
SDIO WiFi 模块有个很坑的特点:平均电流看起来不大,但发射瞬间的峰值电流能到几百毫安。文档标称的典型值常常是"工作电流 80mA"这种,但你真按这个去选 LDO,一发包就翻车。我建议按峰值留够余量,起码给到模块数据手册里瞬态电流的两倍。
排查的时候,用万用表静态量是量不出问题的,得上示波器,而且探头要带足够带宽,接在模块的供电脚上,最好用短地线。看两样东西:纹波和跌落。发射瞬间电压掉超过 5% 就要警惕,如果掉到模块的最低工作电压以下,SDIO 通信必然出问题。常见补救手段有换更大电流的 LDO、把走线加粗缩短、在模块电源脚旁边补一个 10uF 加 0.1uF 的组合电容,靠近引脚放,别嫌占地方。
注意:很多人习惯只焊一个 0.1uF 靠近模块,这在静态下没问题,但对付瞬态电流远远不够,一定要有大电容兜底。
还有一种隐蔽故障是上电时序。有些模块要求 IO 电压先于核心电压建立,或者反过来,如果电源轨同时上电导致 IO 域有异常电平,模块可能直接进入一个半死不活的状态,枚举就读不到,报 -110。检查模块手册的 power sequence 章节,用示波器同时抓几路电源看时序对不对。
3.2 时钟频率和信号质量是重灾区
SDIO 时钟是从 SoC 的 PLL 分频出来的,max-frequency设成 50MHz,实际波形不一定真的干净。我一般先从降频验证做起:把设备树里的频率砍到 25MHz,甚至降到 400kHz 这个枚举标准频率,看 -110 还出不出。如果降频后一切正常,那基本可以锁定是信号完整性或模块速率能力的问题。
波形怎么看?示波器抓CLK和CMD、DAT0,重点看几个指标:上升沿是不是太慢(太慢会导致采样点错过)、有没有过冲和振铃(过冲会误触发或损伤 IO)、占空比是不是接近 50%。如果时钟线走得太长、没有做好阻抗控制,或者旁边有高速信号在干扰,波形会很脏。改善办法包括缩短走线、时钟线两边包地、必要时串联一个小电阻做匹配。
实操心得:我遇到过一批板子,时钟线和其他高速信号并排走了很长一段,常温下勉强能跑,一到夏天高温就零星报 -110,后来把时钟线改道、加了串阻就稳了。信号完整性问题最喜欢在温度和电压的边界上露头。
3.3 上拉电阻和走线那些容易忽略的细节
SDIO 的CMD和DAT线本质上需要上拉。规范里要求这些线在空闲时保持高电平,靠的就是上拉电阻。有些 SoC 内部带上拉,有些必须外部加,如果漏了,总线空闲电平飘忽,命令刚发出去就被干扰成别的电平,模块没法解析,自然超时。典型取值在 10k 到 50k 之间,具体看总线的负载和速率,高速模式下往往要更小一点保证边沿。
走线上还有几个坑:DAT几根线最好等长,别一根长一根短,否则四线模式下各个位的到达时间差太大,采样错位;CLK尽量远离易干扰的线;模块和 SoC 之间的地要连好,不能出现地电位差,否则整个参考电平都在漂。这些在快速打样阶段很容易被忽略,但恰恰是 -110 的常客。
4. 软件与驱动层的定位手法
4.1 设备树里的关键参数怎么配
Linux 平台下 SDIO WiFi 能不能稳,设备树配置占了很大比重。几个必看的节点参数:bus-width决定用几线,WiFi 用的模块大多数是四线,写错成一线要么跑不快要么报错;max-frequency是上限,前面说了,调它是最好的验证手段;cap-sdio-irq表示支持 SDIO 中断;non-removable表示模块是焊死的,告诉内核别去做插拔检测;keep-power-in-suspend关系到休眠时供电是否保持,如果模块休眠后被断电,唤醒时又没重新枚举,就会报超时。
&sdmmc1 { status = "okay"; bus-width = <4>; max-frequency = <50000000>; cap-sdio-irq; non-removable; keep-power-in-suspend; no-1-8-v; /* 如果模块 IO 只支持 3.3V 就加上 */ broken-cd; /* 无插拔检测引脚时用轮询 */ };no-1-8-v这个参数坑过不少人。有些 SoC 会尝试把信号电平切到 1.8V 来跑高速模式,但你的模块 IO 只支持 3.3V,一切换通信立刻乱套,表现为各种 -110。如果模块明确不支持 1.8V 信号,就把这个属性加上,禁止内核切换电压域。反过来,如果模块要求 1.8V 而你电路没做电压切换,同样会出问题。
4.2 用 debugfs 和动态调试把线索挖出来
内核提供了一堆现成的观测点,别上来就改代码。/sys/kernel/debug/mmc0/下面有几个文件特别有用:ios能看到当前实际协商出来的时钟频率、总线宽度、时序模式,这是判断"我配的和实际跑的是否一致"的第一手资料;err_stats能看各类错误的计数。如果ios里显示的时钟和你设备树配的差很远,说明协商过程本身就有问题,先解决这个。
打开 MMC 子系统的动态调试能拿到更细的日志:
echo 'file mmc*.c +p' > /sys/kernel/debug/dynamic_debug/control echo 'file sdio*.c +p' > /sys/kernel/debug/dynamic_debug/control打开之后再触发一次故障,日志里会明确告诉你哪个命令超时、超时前的交互序列是什么。这个方法我强烈推荐,因为它能把"某处超时"细化到"第几步的哪个命令超时",排查效率直接翻倍。
4.3 降频和分步验证是最省钱的办法
我最常用的一招:二分法降频。先从标称频率往下砍一半,不行再砍一半,直到降到 400kHz 这个枚举频率。如果 400kHz 下枚举和基本通信都稳定,那模块和 SoC 的连通性没问题,问题在速率相关的部分——可能是走线、匹配,也可能是模块对高时钟的容忍度。这时候再慢慢往上加,找到能稳定工作的临界点,往往离故障点就很近了。
另一个思路是拆分阶段验证。只做枚举不做数据传输,看会不会报错;枚举过了只写固件,看会不会超;固件过了再跑数据收发。把链路一段段隔开,哪一段开始报 -110,病灶就锁定在那一段涉及的硬件和配置上。别一上来就整体压测,那样只会得到一堆互相干扰的信息。
5. 常见问题速查与实战避坑清单
5.1 一张表对着症状找原因
| 报错场景 | 最可能的原因 | 优先验证手段 |
|---|---|---|
| 枚举阶段就 -110 | 供电异常、复位时序、模块未上电 | 示波器量供电和复位脚 |
| CMD53 写固件超时 | 时钟过高、供电瞬态跌落 | 降频验证 + 测峰值电压 |
| 运行中零星 -110 | 信号完整性、电源管理挂起冲突 | 长时压测 + 关 runtime PM |
| 特定板子必现、其他正常 | 走线、上拉、接地差异 | 对比两块板的总线波形 |
| 高温或低压下复现 | 器件裕量不足 | 环境箱加温/降压复现 |
| 切 1.8V 后报错 | 电压域不匹配 | 设备树加 no-1-8-v |
这张表是我这些年踩坑攒下来的,对照着看能省很多瞎试的时间。注意一点,很多情况是几个原因叠加的,比如供电本身就临界,再叠上时钟偏高,单看哪个都不算致命,凑一起就必现 -110。
5.2 几个我真心建议大家记住的坑
第一个坑,不要迷信模块文档的电流标称值。那些"典型工作电流"是给选型估算用的,不是给你设计电源用的。真正决定成败的是瞬态特性,一定要看模块手册里的峰值电流,或者干脆自己上示波器抓。
第二个坑,降频和加余量永远比死磕高频划算。有些项目非要跑 50MHz,为了那点速率折腾几周,其实降到 40MHz 稳定性就上一个大台阶,实际吞吐未必差多少。WiFi 的瓶颈经常不在 SDIO 时钟,而在射频和协议栈。
第三个坑,-110 出现的位置经常是假象。日志说某个命令超时,不代表那个命令有问题,可能是前一步的副作用,比如电源刚被切、时钟刚切换、模块刚复位。看日志要连着上下文一起看,别只盯报错那一行。
第四个坑,改驱动之前先确认硬件和配置。我见过太多人一看到 -110 就去改驱动里的超时时间,把超时从默认的几百毫秒改成几秒。这治标不治本,只是把报错延后,真到了生产环境还是会翻车。超时时间适当加长可以做临时验证,但不能当解决方案。
提醒:如果加长超时后错误消失了,恰恰说明链路里有"慢"或"丢"的问题,而不是没问题,一定要继续往下查。
第五个坑,同一块板子在别人机器上好的,在你这里不行,先比电源和时钟。不同批次、不同环境温度、不同负载都会改变裕量。做 WiFi SDIO 移植,最好手里有一台能看眼图的示波器,能省下大量试错成本。
6. 把问题收敛:我的实际排查顺序
真正上手排查 sdio -110,我习惯按固定顺序走一遍,这样不容易漏。第一步看日志定位阶段,确认是枚举、固件下载还是运行时超时,这一步决定了后面往哪个方向使劲。第二步量供电,静态加瞬态都看,尤其是发射相关的瞬间跌落。第三步降频验证,快速判断是速率相关还是连通性相关。第四步看波形,主要盯时钟质量和总线空闲电平。第五步查设备树和电压域配置,确认参数和模块能力匹配。第六步才是动驱动,而且优先用动态调试看细节,而不是直接改代码。
这个顺序的逻辑是"由外到内、由廉价到昂贵"。量电和降频几乎不花钱,改驱动最费时间还容易引入新问题。多数 -110 在第三步之前就能定位个七七八八,剩下的细活才交给波形和内核调试。
我个人在几个项目里最大的体会是:SDIO -110 里,纯软件问题占的比例远没有大家想象的高,供电和信号完整性才是大头。尤其是那些"偶发""高温才出""某块板子才出"的 -110,几乎都能在电源和走线上找到答案。所以下次再看到这个错误,先别急着怀疑驱动,拿起探头量一量,往往比盯着代码看一天管用。