SDIO -110排查:Linux内核超时与嵌入式WiFi初始化
新板子第一次上电串口 log 滚到最后停在一行字上mmc1: error -110 whilst initialising SDIO card。另一台已经跑起来的产品用户反馈 WiFi 偶尔掉线抓内核日志能看到brcmf_sdio_bus_txctl: dongle is not responding: err-110。这两个场景看起来八竿子打不着但报出来的错码是同一个SDIO -110。这个数字不是硬件手册里的寄存器值也不是什么厂商私有码它是 Linux 内核里ETIMEDOUT取负之后的样子翻译成人话就是我等得太久对面一直没理我。做嵌入式 WiFi 的人迟早会碰到它。它可能出现在板子刚回来第一次 bring-up 的时候也可能出现在量产几百台之后某一台的偶发故障里。麻烦的地方在于-110 是个症状而不是病因从供电、上电时序、走线、上拉、设备树配置到固件文件缺失都能让它冒出来。这篇就把我这些年拆 -110 的思路完整摊开先讲清楚这个错误码在内核协议栈里是怎么产生的再给一套从日志到示波器的排查流程然后按硬件、软件两条线把常见元凶逐个点名最后用一次真实的排查复盘把整套方法串起来。1. 从 -110 这个数字反推它到底是谁报的错很多人第一次看到error -110会下意识去翻 WiFi 芯片的 datasheet想找出 110 号寄存器或者 110 号状态位。方向就错了。搞清楚这个数字的来源是后面所有排查的前提。1.1 内核错误码的约定负数就是 errno 取负Linux 内核里有个很统一的约定函数返回int成功返回 0失败返回负的 errno。errno 表里第 110 号是ETIMEDOUT所以-110就是超时。跟它经常一起出现的还有几个兄弟错误码errno含义通常指向-110ETIMEDOUT等响应等到超时供电、时钟、复位、上拉、没枚举上-84EILSEQCRC 校验失败信号完整性、时钟太快、走线太长-5EIO通用 IO 错误驱动逻辑、固件、buffer 问题-19ENODEV设备不存在枚举失败、卡没上电-22EINVAL参数非法设备树属性写错、时序参数不匹配这张表很有用。-110 和 -84 的区别基本就是没人应答和应答了但听不清的区别。前者优先怀疑供电、时钟、复位、上拉后者优先怀疑信号质量和时钟频率。我在实际排查里第一步就是看这两个码哪个出现能砍掉一半的排查方向。1.2 一次 CMD53 要穿过几层才落到引脚上要理解超时是怎么发生的得知道一条命令要过几道手。以一次典型的 SDIO 数据读写CMD53为例从上到下大致是这么几层WiFi 驱动层比如 brcmfmac、rtl8xxxu 这类。驱动组好数据调用 SDIO core 提供的读写接口然后阻塞等待完成。MMC/SDIO core 层。把请求翻译成 SDIO 命令挂到 host 的请求队列上启动一个超时定时器通常是几百毫秒到秒级。Host 控制器驱动层sdhci、dw_mmc 等。把命令写进控制器的寄存器等控制器给出命令完成的中断。硬件控制器。按协议在 CLK 上打节拍在 CMD 线上发命令在 DAT0~DAT3 上收数据。SDIO 卡WiFi 芯片。芯片内部的状态机响应或者不响应。超时可能发生在任何一层。如果是第 3 层就超时了日志里会看到mmc1: Timeout waiting for hardware interrupt或者Timeout waiting for hardware cmd interrupt这说明连控制器自己都没等到中断卡根本没在 CMD 线上回应。如果是第 2 层的请求超时日志通常是mmc1: error -110 whilst initialising SDIO card或者mmc1: card never left busy state。而如果是第 1 层的固件邮箱超时日志会带驱动前缀比如brcmf_sdio_bus_txctl: dongle is not responding: err-110。这条特别容易被误读因为它意味着SDIO 总线本身是通的固件也下进去了但芯片在固件层面没回邮箱消息。1.3 为什么日志里的 -110 几乎只出现在两个位置把上面几层对上现实-110 高频出现的位置其实就两个枚举阶段。也就是whilst initialising SDIO card这一段。此时芯片刚上电正在走 CMD0 → CMD5 → CMD3 → CMD7 的流程读 CIS、协商电压和时钟。这个阶段超时九成是硬件问题电没给对、复位没放开、LPO 时钟没来、上拉缺失。固件下载完成后的通信阶段。典型日志是dongle is not responding。这个阶段超时往往是芯片中途复位了、电源在发射瞬间塌了、或者固件/NVRAM 文件不匹配导致芯片跑飞。我个人的经验是枚举阶段的 -110 先查板子通信阶段的 -110 先查电源和固件版本。方向对了能省下好几天。2. 别急着返修用三段式流程把范围缩小到板子或代码拿到一个 -110最忌讳的就是立刻改板或者立刻换驱动版本。我一般会走一个固定的三段式流程目的只有一个用最低成本确定问题在硬件还是软件再决定往哪个方向深挖。2.1 第一步把 dmesg 的时间线按毫秒级对齐第一件事是拿到完整日志而且必须带时间戳。命令很简单dmesg -T | grep -Ei mmc|sdio|brcm|wlan|wifi|regulator如果内核开了 dynamic debug还能把 mmc 子系统的调试信息打开看到每条命令的细节mount -t debugfs none /sys/kernel/debug echo file mmc* p /sys/kernel/debug/dynamic_debug/control echo file core.c p /sys/kernel/debug/dynamic_debug/control这时候再复现一次日志会密很多。重点看三件事从 regulator 上电到第一条 mmc 命令之间隔了多久枚举过程中第几条命令开始超时超时之后控制器有没有重新尝试降速枚举。很多板子的问题一看时间线就露馅了比如 regulator 使能日志比 mmc 枚举还晚那就是典型的上下电顺序写反了。同时看一下当前总线状态cat /sys/kernel/debug/mmc1/ios这个文件会告诉你当前的时钟频率、总线宽度、时序模式legacy / high speed / SDR104、信号电压。如果显示的是 SDR104 且时钟 208MHz而你手上是块刚打样的板子那基本可以确定是这个速度跑不动先降下来试试。2.2 第二步降频与降位宽最低成本的变量隔离这是我最推荐的一招在设备树里把max-frequency从 50MHz 改成 25MHz 甚至更低把bus-width从 4 降到 1然后重新编译烧录。如果改完就正常了说明问题大概率在信号完整性或者供电余量上而不是驱动逻辑。这一步不需要动硬件半小时就能出结论。举个我碰到的真实例子一块用 SDIO 接 WiFi 模组的板子4 线 50MHz 下必现 -110改成 1 线 25MHz 后就稳了。这种情况下问题不在能不能通而在高速下眼图闭了。后面查出来是 CLK 走线绕了远路还跟一路 PWM 背靠背走了很长一段。降频只是验证手段不是解决方案但它能帮你把问题定性。注意有些模组在低频率下反而不工作因为芯片内部的初始化固件有一套默认的时钟假设。所以降频如果没效果别急着否定信号完整性方向试着换成相邻的几档频率各跑一次看是不是某个频段特别差这本身就是信号完整性的典型特征。2.3 第三步分清没人应答和应答了但校验不过把日志里的错误码和超时阶段做个交叉能画出一张很实用的分诊表日志表现阶段优先怀疑Timeout waiting for hardware interrupt -110枚举早期供电、复位、LPO 时钟error -110 whilst initialising SDIO card枚举中段上拉、VDDIO 电平、bus-widthcard never left busy state枚举后段芯片没正常启动、固件区异常dongle is not responding: err-110固件已下载电源塌陷、固件不匹配、芯片过热大量 -84 夹杂少量 -110高速数据走线、串阻、时钟频率这张表我几乎每块新板子都会过一遍。它不能直接给答案但能满足排除法的需要——能明确排除掉的方向本身就是进展。3. 硬件侧翻车重灾区供电、时序、走线、上拉如果前面的流程把问题指向硬件那接下来就是具体的四个重灾区。这四个地方占了我见过的硬件类 -110 的绝大部分。3.1 供电WiFi 发射瞬间的电流坑和去耦电容的真实作用很多人看 WiFi 模组的平均功耗觉得 3.3V、一两百毫安用个普通的 LDO 完全够。然后板子一跑就 -110。问题出在峰值电流上。WiFi 在发射瞬间的瞬时电流可以冲到 300mA 甚至更高而且上升沿很陡持续几百微秒。这段时间里如果电源路径上的等效串联电阻和等效串联电感偏大模组端的电压就会瞬间掉下去芯片内部的状态机被复位SDIO 总线上的表现就是突然不应答了。去耦电容的意义就在这里而且有个很容易被忽略的点电容要靠近模组的电源引脚放而不是靠近稳压器放。我见过好几块板子10uF 和 0.1uF 都规规矩矩地放在 LDO 输出旁边离模组有 20mm 远中间还隔着一段细走线。这种情况下电容对高频瞬态的响应基本无效因为它和负载之间那段走线的电感已经把高频阻抗抬起来了。实际操作上的建议是模组的 VBAT/VDD 引脚旁边放一颗 10uF 的陶瓷或钽电容再并一两颗 0.1uF 和 1nF越小越近。电源走线尽量粗能走平面就走平面不要用细线串过去。如果 LDO 的瞬态响应一般考虑换成响应更快的型号或者在 LDO 输出加一颗大容量电容做能量缓冲。有条件的用电流探头配示波器直接看发射瞬间模组端的电压波形比任何推理都直观。3.2 上电时序WL_REG_ON 与 32.768kHz LPO 的配合WiFi 模组基本上都有两个关键的外围信号一个是使能脚常见叫法有 WL_REG_ON、WL_EN、CHIP_EN一个是低功耗时钟输入通常是 32.768kHz简称 LPO。这两个信号的时序错了枚举阶段的 -110 会稳定复现。先说要命的顺序问题。正确顺序是先给 VBAT/VDDIO 上电稳定之后把使能脚拉高再等一段时间一般 50~200ms具体看手册然后 mmc 控制器才开始枚举。如果设备树里的 regulator 和 pwrseq 顺序写反或者 pwrseq 的延时给得太短芯片还没准备好host 就开始打 CMD0那必然超时。这里的延时不能拍脑袋要去翻模组的 datasheet找Power-up Sequence那一节的时序图里面有明确的t1、t2参数。再说 LPO。有些芯片尤其是带低功耗模式的方案在固件下载和休眠唤醒阶段依赖 32.768kHz 时钟。这个时钟通常来自板上的 RTC 或者专门的晶振。如果没接、接错、或者信号幅度不够表现就是枚举过了、固件也下了但通信阶段时不时dongle is not responding。这个坑很隐蔽因为完全不影响它看起来能起来只是稳定性很差。排查手段是用示波器直接量 LPO 引脚确认频率、峰峰值和波形干净程度。3.3 走线与串阻为什么 CLK 线不能随便拉长SDIO 里最容易出问题的信号是 CLK。它是唯一一个由 host 单向驱动、频率最高的信号其他信号都要跟着它的节拍走。CLK 走线一旦拉长、绕远、或者旁边有强干扰源边沿就会出现振铃和过冲采样窗口被吃掉最终表现为 -84 或者 -110。几条我踩出来经验CLK 走线尽量短尽量直尽量和同组的 CMD、DAT0~DAT3 保持等长长度差控制在几毫米以内。在 CLK 源端串一颗 22Ω 到 33Ω 的电阻用来压振铃。这个电阻的位置很讲究要串在驱动端附近不是接收端。CMD 和 DAT 线上也可以视情况串小电阻但别一次全加上容易把边沿压得太慢导致新的超时。不要在 SDIO 信号线下面铺大面积的开关电源或者 DC-DC 的走线耦合噪声很难处理。有一点要提醒串阻不是万能的它是用来修整边沿的不是用来救走线布局的。如果已经拉到 208MHz 跑 SDR104布局又很糟糕串阻只能让你从必现变成偶尔不能根治。3.4 上拉电阻、VDDIO 电平与 DAT1 中断线SDIO 总线上拉的问题经常被忽略因为很多 SoC 内部已经带了可配置的上拉电阻。但内部有不等于参数合适。上拉太弱信号上升沿被拉慢高速下就是超时上拉太强又会让驱动端灌电流变大功耗和边沿都会变差。还有两个点值得单独拎出来VDDIO 电平必须匹配。现在很多 WiFi 模组支持 1.8V 和 3.3V 两种 IO 电压靠寄存器或者配置决定。如果设备树里声明了 1.8V 时序比如开了 SDR104但硬件实际供的是 3.3V或者反过来就会出现各种奇怪的超时。这类问题特别容易在换了一批模组之后出现因为不同批次默认状态可能不一样。DAT1 中断线。SDIO 卡中断是通过 DAT1 上报的很多板子忘记在这根线上加上拉导致中断收不到驱动只能靠轮询等待表现就是超时变成常态。设备树里对应的是cap-sdio-irq这个属性用了它就必须保证 DAT1 的硬件条件到位。4. 软件侧隐形坑设备树、caps、固件文件三件事排完硬件剩下的就是软件配置。这一块的特点是配置写错不会报错只会安静地让总线工作在错误的状态下然后等到某次通信量大了才突然 -110。4.1 mmc 控制器节点里那几个必须写对的属性一块板子上 mmc 控制器的设备树节点看起来平平无奇但每个属性都在改硬件行为。以常见的嵌入式 SoC 为例一个 SDIO 接 WiFi 的节点大概是这个结构sdmmc1 { bus-width 4; non-removable; cap-sdio-irq; keep-power-in-suspend; disable-wp; max-frequency 50000000; mmc-pwrseq sdio_pwrseq; vmmc-supply vcc_wifi; vqmmc-supply vcc_wifi_io; status okay; };逐个说清楚为什么这么写bus-width 4走 4 线。如果硬件只连了 DAT0这里写 4 就会在枚举后段超时。non-removable表示卡是焊死的不会被拔掉。不写这个控制器可能会去做卡检测某些平台会因为检测不到 CD 信号而放弃初始化。cap-sdio-irq让驱动使用 DAT1 中断减少轮询开销。前提是 DAT1 硬件上拉到位。keep-power-in-suspend保证系统休眠时 WiFi 不掉电不然唤醒后重新枚举很容易撞上时序问题。max-frequency是上限。我通常先写一个比较保守的值把功能跑通再逐步往上加每加一档做一轮长时间压测。4.2 pwrseq 和 regulator 的启用顺序mmc-pwrseq-simple这个节点看起来很不起眼但它决定了使能脚什么时候拉高sdio_pwrseq: sdio-pwrseq { compatible mmc-pwrseq-simple; reset-gpios gpio1 12 GPIO_ACTIVE_LOW; post-power-on-delay-ms 200; };这里GPIO_ACTIVE_LOW的写法和很多人直觉相反它表示低电平是复位状态所以 pwrseq 在供电时会把这个引脚拉高也就是释放复位。如果你写成了GPIO_ACTIVE_HIGH那结果就是使能脚一直是低芯片永远不上电枚举必超时。这个错误我在别人的板子上至少见过三次。post-power-on-delay-ms就是前面说的等芯片准备好的延时别嫌它大200ms 对启动时间影响微乎其微但能挡掉一大批偶发问题。至于 regulator重点是保证vmmc 和 vqmmc 两个电源的顺序和上电时间满足模组手册。有的模组要求 VDDIO 先于 VBAT有的反过来看手册。设备树里可以通过regulator-ramp-delay和regulator-enable-ramp-delay来微调。4.3 固件、NVRAM 与校准文件缺失时的表现还有一个特别容易被当成硬件问题的软件坑固件文件缺失或者路径不对。很多 WiFi 方案尤其是 Broadcom/Cypress 系的 brcmfmac需要从文件系统加载三个东西固件.bin、NVRAM 配置.txt、以及某些方案的校准数据。如果文件缺失或者名字对不上由于固件根本下不进去芯片会一直卡在等待状态表现就是brcmf_sdio_download_firmware: dongle is not responding: err-110。这种情况你换板子、换电源、降频都没用因为它压根不是硬件问题。排查方法很简单把驱动的调试等级拉高看它到哪个路径去找文件dmesg | grep -i firmware ls -l /lib/firmware/brcm/确认文件名和芯片型号完全对应注意大小写和连字符。同一个芯片不同封装、不同批次固件名可能就差一个后缀这个我吃过亏。5. 一次真实的 -110 复盘从复现到定位到修复前面讲的是方法论这里用一个完整案例把流程走一遍。5.1 现象与初始日志一块基于某国产 SoC 的板子SDIO 4 线接 WiFi 模组。第一批 10 块样板其中 3 块在冷启动时必现 -110另外 7 块偶尔出现热启动之后基本都能起来。日志长这样[ 3.214560] mmc1: Timeout waiting for hardware interrupt. [ 3.215031] mmc1: error -110 whilst initialising SDIO card [ 4.302118] mmc1: Timeout waiting for hardware interrupt. [ 4.302601] mmc1: error -110 whilst initialising SDIO card注意卡在了枚举阶段的早期而且两次重试都失败。5.2 排查动作与每步的结论我按顺序做了这么几件事每一步都记录结论看 regulator 与 mmc 的时间线。日志里 regulator 使能比第一条 mmc 命令早了 40ms看起来对但模组手册要求的是最少 150ms。结论延时不够先改。改 pwrseq 延时到 200ms。3 块必现的板子里有 1 块变正常了另外 2 块和 7 块偶发的依旧。结论延时是个问题但不是唯一问题说明还有别的因素。降频到 25MHz、单线。10 块板子全部正常启动。结论问题跟信号质量或供电余量强相关。恢复 4 线降到 25MHz。10 块里 8 块正常2 块仍偶发。结论进一步指向信号完整性。示波器量 CLK 波形。发现边沿有明显振铃过冲接近 0.8V而且 3.3V 的上升沿有明显台阶。结论走线阻抗不连续且上拉偏强。量模组端电源。在启动瞬间能看到约 200mV 的跌落。结论供电去耦不够。5.3 根因与改法综合下来是三个因素叠加pwrseq 延时太短150ms 是下限实际量产件一致性差直接给到 250ms 更稳。CLK 走线过长且有一段绕行导致 50MHz 下边沿振铃。硬件上飞线缩短走线、在 CLK 源端加 27Ω 串阻后振铃明显收窄。模组 VBAT 引脚旁只有一颗 1uF 电容去耦不足。补了一颗 10uF 和一颗 0.1uF 后启动瞬间的电压跌落从 200mV 降到 60mV 以内。软件侧的最终配置sdmmc1 { bus-width 4; non-removable; cap-sdio-irq; keep-power-in-suspend; max-frequency 25000000; mmc-pwrseq sdio_pwrseq; vmmc-supply vcc_wifi; status okay; }; sdio_pwrseq: sdio-pwrseq { compatible mmc-pwrseq-simple; reset-gpios gpio1 12 GPIO_ACTIVE_LOW; post-power-on-delay-ms 250; };5.4 回归验证要看的几个点改完之后不能只看能起来要按这几个维度压一轮冷启动 200 次每次断电至少 10 秒统计失败率。改之前是 30% 必现加 70% 偶发改之后 200 次全过。高低温。低温下晶振和电容特性都会变很多信号完整性问题只在低温或高温暴露。做 -20℃ 和 70℃ 各 50 次冷启动。WiFi 大流量传输时的电源纹波。用 iperf 之类的工具跑满吞吐同时示波器盯着模组电源确认没有超过阈值的跌落。反复上下电。模拟用户开关 WiFi看 pwrseq 释放和复位之间有没有残留状态。6. 长期维护视角量产阶段怎么把 -110 挡在出厂之前研发阶段把 -110 修掉只是第一步真正难的是量产几百上千台之后它不会以低频偶发的形式冒出来然后变成一堆无法复现的客诉。6.1 把 WiFi 上下电做成压力测试项出厂测试里加一项循环开关 WiFi 电源 100 次每次都要走完枚举、固件下载、扫描、关联的完整流程。任何一次失败就判 NG。这个测试能筛掉大量只是时序余量不够的板子。实现上就是循环操作使能脚对应的 GPIO然后等驱动把网口拉起来再检查dmesg里有没有出现 -110 或 -84。for i in $(seq 1 100); do echo 0 /sys/class/net/wlan0/device/power_control 2/dev/null ifconfig wlan0 down 2/dev/null sleep 2 ifconfig wlan0 up 2/dev/null || echo FAIL at round $i dmesg | tail -50 | grep -E \-110|\-84 echo SDIO ERROR at round $i done具体路径因平台而异核心思路是让 SDIO 总线完整地经历一次断-连循环。6.2 老化测试与不同温度下的表现差异我见过太多这样的案例常温下跑几个小时没问题放到 60℃ 的环境里跑两小时就开始掉线日志里全是 -110。原因通常是电源芯片在高温下效率下降、电容容值变化、或者模组本身发热叠加导致的余量不足。所以老化测试一定要覆盖温度区间不能只在空调房里跑。另外一个容易被忽略的是长时间大流量下的热累积。WiFi 芯片持续发射本身就会发热如果模组周围散热不好芯片温度上去之后内部某些模拟电路的工作点会漂移表现就是跑一段时间后开始超时。这种情况在结构设计阶段就要考虑比如模组下面加散热焊盘、开散热孔或者调整射频发射的占空比。6.3 一份可落地的自检清单最后给一份我平时用的清单从软到硬按排查成本从低到高排列确认固件、NVRAM 文件存在且名字匹配路径正确。确认设备树里bus-width和实际连线一致non-removable已加。确认 pwrseq 的 GPIO 极性和post-power-on-delay-ms满足模组手册。确认 regulator 上电顺序和延时满足手册VDDIO 电平一致。用max-frequency降频测试确认问题是否与速率相关。用示波器量 CLK 边沿、LPO 频率、模组端电源跌落。在 CLK 源端加串阻检查走线长度和等长情况。检查模组电源引脚旁的去耦电容布局确认是靠近负载而不是靠近稳压器。检查 DAT1 上拉确认cap-sdio-irq的前提条件成立。做冷启动、高低温、大流量三类压测统计失败率。我自己在实际操作中的体会是-110 这类问题几乎从来不是单一原因造成的而是几个刚好都差一点的因素叠加。所以排查时不要指望找到某一个神奇的错误点而是要把每个环节的余量都补足。反过来一旦你在某个项目上吃过一次亏就把上面这份清单固化进硬件设计规范和出厂测试脚本里后面同样的坑就不太会再踩第二次。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →