拓冰建站拓冰建站
首页 / 资讯中心 / 正文

STM32MP257 SPI3从机NSS引脚claim失败排查与设备树配置

这块STM32MP257F_EV1评估板本身素质不错双核Cortex-A35加Cortex-M33跑OpenSTLinux做工业网关绰绰有余。但当我开始验证SPI3的从机模式时一个不到1厘米宽的NSS引脚差点让我怀疑人生PB1在设备树里配置为SPI3从机的片选输入结果系统起来后内核日志直接告诉我这个NSS引脚claim失败。这个问题非常典型凡是把SPI配成从机、而且是硬件NSS场景的工程师几乎都会在某个阶段撞上。这篇帖子我会把从复现、定位到解决的全过程拆开讲包括我踩过的坑和最后真正生效的配置方式。如果你是第一次在MP2/MP13系列上做从机通信这篇应该能帮你省下一到两个通宵。1. 现象复盘从错误日志判断问题边界1.1 我这边的复现条件先说清楚测试环境这样下面的日志和结论才有意义。我手上这块STM32MP257F_EV1板子软件用的是OpenSTLinux的SDK版本内核是标准主线加ST补丁的设备树。测试目标是把SPI3配置成从机模式外部用一个主机控制器我手头临时用的是一块F746 Nucleo来发起通信。SPI3的SCK、MISO、MOSI都接好了唯独NSS这个脚我按常规理解直接选择了PB1想着让外部主机的CS信号直接拉低PB1来选中从机。结果系统启动后内核日志里SPI3的驱动确实检测到了从机模式但紧接着就报了一串错误核心信息就是标题里那句SPI3 Slave mode下NSS PINPB1Fails to claim。整段日志我放在下面[ 2.520805] spi-stm32 48003000.spi: spi3 probed as slave [ 2.521014] spi-stm32 48003000.spi: using hardware NSS, pin PB1 [ 2.521262] spi-stm32 48003000.spi: failed to claim NSS pin PB1: -16 [ 2.521443] spi-stm32 48003000.spi: spi3 initialization failed, probe error [ 2.521595] spi-stm32 48003000.spi: probe with driver spi-stm32 failed with error -16注意日志里的地址前缀48003000.spi在不同BSP版本里可能不一样但核心的报错文本是一致的SPI驱动已经确认自己工作在从机模式也找到了NSS引脚是PB1但在claim这个引脚时被拒绝错误码是-16。也就是说驱动的整体思路是对的卡点非常具体地落在“引脚申请”这一步整个问题边界一开始就可以缩小到GPIO资源和引脚复用上。1.2 错误码-16到底在说什么在Linux内核里-16对应的errno是EBUSY翻译成人话就是“设备或资源忙”。SPI驱动在probe过程中会尝试把NSS引脚从GPIO子系统申请过来这个申请动作本质上是GPIO的gpiod_get或类似接口。如果内核里已经有一个驱动提前占用了PB1后到的SPI驱动就会收到-16。还有一种情况虽然不常见但确实存在即便没有其他驱动占用如果SPI从机的设备树节点里写了cs-gpiosLinux SPI核心会把PB1当成常规的片选GPIO去申请而这个GPIO同时在pinctrl里又被配置成了外设复用功能比如AF5的SPI3_NSS两边说法不一致GPIO子系统内部校验不过去也会给你返回-EBUSY。搞清楚错误码的含义之后我并没有急着去改设备树而是先建了一张排查清单设备树里PB1到底怎么描述、运行时这个引脚被谁占用、SPI3外设本身有没有被安全侧或者M33侧锁住、以及物理接线是不是真的把主机的CS连到了PB1上。下面几章按这个顺序展开。1.3 先别慌这种错误不算罕见说实话第一次看到这个报错时我也以为是什么高深的外设配置问题甚至在SPI寄存器层面翻了好一阵子。后来回头一想在STM32MP这种异构多核SoC上做外设验证十个里至少六七个都卡在GPIO占用和外设所有权上。这类问题最怕的就是怀疑驱动、怀疑硬件、甚至怀疑人生其实绝大多数时候只是设备树里同一个引脚被描述成了两种身份。2. 从机片选背后的机制和原理2.1 SPI从机为什么离不开NSS很多人学SPI时重点关注SCK、MOSI、MISO却把NSS当成一个可有可无的“使能脚”。这话在主机模式下勉强说得过去因为主机可以自己控制CS输出但在从机模式下NSS就是命脉。用一个生活化的类比SPI主机是工地上的队长SCK是他吹的哨子MOSI是他下达的指令MISO是工人的回应。而NSS这个脚就是队长在喊“老张这活儿归你”。从机芯片只有听到“老张”这两个字也就是NSS被拉低之后才会开始听哨子声干活。如果NSS从来没有被拉低哪怕SCK时钟跑得飞起MOSI上的数据也和你完全没关系整个从机外设就会一直待在空闲态接收FIFO一个字节都不会进。所以我在调试SPI3从机时最先确认的就是PB1外部电平。这个在物理上极其简单但极容易被忽略主机端的CS如果没接对或者CS极性配置反了从机端的NSS永远保持高电平从机自然“装死”。2.2 硬件NSS和软件NSS是两条完全不同的路STM32的SPI外设H7及之后代的IP在从机模式下NSS有两种玩法。第一种是硬件NSS也是我们想用的方式。PB1复用为SPI3_NSS这个alternate function外部主机通过拉低PB1来选中从机外设硬件直接对这个引脚的电平做仲裁再触发内部的收发状态机。这种方式实时性好、抗不过度占用CPU适合对时序敏感的场景同时也是多从机总线上的标准做法。第二种是软件NSS也叫内部从机选择。此时外部不需要真正的CS信号输入通过设置SPI_CFG1相关的SSI位让内部片选一直处于有效状态SPI从机相当于永远被选中。好处是可以节省一个引脚很多简单点对点通信就这么干坏处是没法在一条总线上挂多个从机因为所有从机都会同时监听这根总线。“claim”这个词在驱动层面的意思就是驱动去把NSS这个资源纳入自己控制。硬件NSS模式下它要拿到GPIO的控制权并配置成输入软件NSS模式下不需要GPIO输入只需要在控制器内部把片选信号强制置有效。两者配置方式完全不同如果设备树里描述的NSS模式跟实际想要的不一致报告出来的错误就会五花八门。2.3 “claim”在驱动栈里的实际位置我再往下挖一层。Linux SPI核心在注册一个从机设备时会做不少准备工作读取设备树节点里的compatible、中断号、DMA通道以及片选描述。对从机控制器来说如果配置要求NSS参与控制器驱动会在自己的probe回调里通过GPIO子系统请求这个引脚。这一步失败就是我们在日志里看到的claim失败。在SPI驱动栈的语境中claim一旦失败控制器驱动可能直接放弃初始化SPI3整个节点都不会注册成功。所以从日志看到probe error -16时基本可以断定问题就出在GPIO请求之前或请求的瞬间而不是SPI外设时钟、分频、极性这些更深层的配置上。这个判断帮我把排查范围缩小了一大截后面所有动作都围绕“为什么PB1申请不下来”去展开。3. 从设备树、GPIO占用到硬件权限逐级排查3.1 第一件事确认设备树里PB1的真实身份在改任何文件之前我先去看了运行时的设备树实际状态。在OpenSTLinux的debugfs挂载的情况下可以通过下面几行命令快速确认引脚当前的复用和占用情况mount -t debugfs none /sys/kernel/debug cat /sys/kernel/debug/pinctrl/*/pinmux-pins | grep -i pb1 cat /sys/kernel/debug/gpio | grep -i pb1pinmux-pins会告诉你PB1当前被复用成哪个功能是GPIO还是某个外设的AFgpio文件则会列出当前所有被声明过的GPIO请求者。如果PB1出现在某个不相关的设备名下那它就是在你之前已被占用。我还用了libgpiod的工具补了一刀gpioinfo gpiobgpioinfo会直接列出BANK0到BANK8所有引脚状态并且标注哪些被内核占用。看到PB1旁边如果跟着一个陌生的label例如led或者usb那基本就是它了。3.2 典型反面教材把从机NSS写成了cs-gpios在查看设备树源文件时我第一版写的是下面这种样子spi3 { status okay; pinctrl-names default; pinctrl-0 spi3_slave_pins; cs-gpios gpiob 1 GPIO_ACTIVE_LOW; };这里就埋下了一个非常隐蔽的雷cs-gpios这个属性在Linux SPI框架里是给SPI主机控制器用的它是一个输出信号由主机主动拉高拉低去控制外部从设备。可我这边恰恰是要把SPI3配置成从机让外部主机来控制我的NSS方向上就反了。当驱动尝试把这个GPIO当成输出来claim时GPIO子系统发现这个引脚在pinctrl里被配置成了SPI3_NSS的复用功能方向和模式都对不上于是返回-EBUSY。正确的做法是不把PB1当作常规GPIO输出而是让PB1复用成SPI3_NSS的外设功能在pinctrl里声明并且不要在SPI3节点中使用cs-gpios。下面是我后来调整过的pinmux片段spi3_slave_pins: spi3-slave-pins { pins1 { pinmux STM32MP_PINMUX(B, 1, AF5), STM32MP_PINMUX(B, 4, AF5), STM32MP_PINMUX(B, 5, AF5); bias-disable; }; };注意这里的AF编号在不同芯片上可能不一样以实际数据手册为准我这边是AF5。PB1走SPI3_NSSPB4和PB5走SPI3_SCK和SPI3_MOSI具体引脚分配要对照EV1板原理图。3.3 第二步查GPIO是否被其他模块占用有时候你的设备树写得完全正确但PB1仍然claim失败这时候就要怀疑板子上别的驱动把你的引脚抢走了。嵌入式开发板上最常用的几个GPIO占用大户包括板载LED、用户按键、SD卡检测脚、模组复位脚。以EV1这种评估板来说默认设备树里已经把很多GPIO分配给了板载外设PB1不一定是“空闲可用的”。我在调试时把debugfs的gpio列表翻了个底朝天很快就看到PB1旁边挂着一个板载功能的名字。这种时候通常是两种处理方案第一种是换一个确认真空的引脚来做NSS改设备树和接线第二种是直接在板级DTS里把占用PB1的那个节点status改成disabled把引脚让出来。从工程角度如果SPI3的引脚位置已经根据EV1的Arduino接口或者排针固定好了那我建议优先用第二种方案把冲突节点关掉。一旦关了冲突源重新编译dtb重启后再看gpio信息PB1就是干净状态claim自然就通过了。3.4 第三步确认SPI3和GPIOB没有被安全侧或M33侧锁住STM32MP系列和普通MCU很大的不同在于同一个外设可能在系统启动早期就被分配给了安全世界TrustZone或Cortex-M33内核。如果SPI3外设被资源配置给了安全侧那么Linux跑在A35的非安全世界访问这个外设时会在总线层面直接失败表现出来也不一定是清晰的中文错误可能是访问超时、寄存器写入无效、甚至probe defer。检查方法很简单看板级设备树里的etzpc节点配置。OpenSTLinux的设备树里往往有一段类似下面这样的宏定义区域etzpc { st,decprot DECPROT(STM32MP1_ETZPC_SPI3_ID, DECPROT_NS_RW) DECPROT(STM32MP1_ETZPC_GPIOB_ID, DECPROT_NS_RW) ; };如果SPI3的ID被配置成了DECPROT_S_RW或者DECPROT_MCU那Linux是没有权限碰它的。这时候需要在U-Boot阶段改环境变量或者修改设备树里etzpc的配置把外设权限放开重新烧录启动。我这次遇到的情况一开始也怀疑过是不是M33固件把SPI3占了但在U-Boot启动日志里翻了半天发现SPI3和GPIOB都还是非安全侧资源所以暂时排除了这条路径。如果你发现自己排查下来GPIO、设备树都没有明显问题那一定不要漏掉这一步。3.5 第四步物理层别想当然软件排查完之后我回到硬件上又验证了一遍。EV1板子的排针很多但PB1未必直接引到排针上它可能和板载外设连在一起。我要确保外部主机的CS信号确实物理上连通到了PB1。我用万用表做了通断测试然后在主机侧写了一个最简单的GPIO翻转程序反复拉低PB1从机端用示波器看PB1波形。如果波形能看到一个干净的拉低硬件通路就通了。如果波形始终是高电平甚至根本看不到翻转那就要回去查主机端的CS配置和接线。一个很容易踩的坑是主机端GPIO的推挽/开漏配置。推挽输出能正常拉低到地但如果主机端CS配置成了开漏又忘了外接上拉电阻或下拉电阻电平可能会飘忽不定从机端的NSS读取也会跟着出错。4. 最终修改方案与完整验证流程4.1 删除cs-gpios用硬件NSS的正规配置我经过完整排查后最终把设备树改成了下面这套结构整套配置也通过了验证。spi3 { status okay; compatible st,stm32mp13-spi-slave; pinctrl-names default; pinctrl-0 spi3_slave_pins; };注意里面没有任何cs-gpios属性也没有多余的GPIO请求。PB1纯粹通过pinctrl的引脚复用配置来承担SPI3_NSS功能。如果你用的是比较新的OpenSTLinux版本compatible字段名可能有一点差异最稳妥的方式是把st,stm32mp13-spi-slave换成你实际BSP里源码中spi-stm32驱动声明的从机兼容字符串。这一点必须和内核源码对齐不能靠猜。如果你确实需要一个外部来的CS信号同时还想让这个CS在GPIO层面可见、可以读取状态你可以考虑用普通GPIO输入的方式绕开SPI硬件NSS逻辑但那是另一种玩法了和这次的问题不是一回事。4.2 一个临时兜底方案软件NSS对照实验在最终定位到根因之前我做了一个非常有效的对照实验临时把SPI3从机切到软件NSS方式。软件NSS模式下不需要外部片选也不需要PB1做硬件输入SPI3从机永远处于被选中状态。具体做法是在设备树中去掉PB1的复用配置并在SPI控制器内部通过寄存器把内部选择信号置为有效。切到软件NSS之后SPI3的probe就再也没有报过claim失败的错误dmesg显示spi3正常注册成了从机控制器主机端发过来的数据也能在从机端收到。这个结论一下子就把问题锁定在了“硬件NSS相关描述”上而不再怀疑SPI外设本身配置有问题。软件NSS只能作为验证手段实际项目中如果要从机可靠地和多个主机设备在总线上共存还是得用硬件NSS。因为软件NSS意味着所有从机都在听总线一旦总线上有两个从机用了同一个协议头冲突就是灾难性的。4.3 重新编译设备树并部署到板子OpenSTLinux SDK环境下重新编译单板设备树不需要构建整个内核通常只需要在Linux源码目录下执行make stm32mp257f-ev1.dtb如果你用的是独立SDK环境也可以通过dtc手动把dts编译成dtb但需要注意头文件的包含路径不然很多宏定义找不到。编译好之后把新的dtb文件拷贝到开发板的/boot分区覆盖同名文件前先做备份cp stm32mp257f-ev1.dtb /boot/stm32mp257f-ev1.dtb.bak cp stm32mp257f-ev1.dtb /boot/ sync reboot重启之后我第一时间就去看了dmesg和gpio状态。这次日志干净利落SPI3正常以从机模式初始化PB1也如愿出现在spi-stm32驱动的名下。4.4 从机模式的验证手段确认初始化只是第一步通信是否能真正工作还要实测。我在从机端没有直接使用现成的spidev工具因为spidev更多面向主机模式。这里分享一个有效的验证思路。先确保主机端能正常发送一个自定义的帧比如5个字节0x5A。从机端我先在设备树里注册了一个简单的自定义从机设备驱动或者直接用内核提供的spi-slave测试接口把接收到的数据打印出来。如果从机端能在主机每次拉低CS之后收到对应字节就证明NSS工作了收发链路也通了。如果不想写驱动还有一个更快的验证方式用逻辑分析仪同时抓SCK、MOSI和PB1三根线。只要看到主机拉低PB1、然后SCK上出现时钟、同时MOSI上有数据就说明从机NSS被正确claim硬件通路完全正常。我在验证时还用了一个很笨但有效的土办法把从机的MISO引脚通过一根杜邦线直接连到逻辑分析仪主机发送一帧数据观察从机MISO上有没有正确的响应数据。这样能从物理层确认从机确实在NSS选中后参与了通信。5. 问题速查表与这几天的经验笔记5.1 一张表总结常见claim失败原因错误码可能原因处理方式-16 EBUSY引脚被其他驱动占用查debugfs/gpioinfo释放冲突GPIO-16 EBUSY同时用了cs-gpios和AF复用删除cs-gpios走pinctrl复用-6 ENXIO设备树引用了不存在的GPIO控制器检查gpiob节点是否使能-22 EINVAL引脚号、极性、偏移配置错误核对数据手册和原理图probe defer依赖的时钟或pinctrl还没就绪查看完整启动日志确认依赖链这张表虽然没有穷尽所有内核版本的情况但对于“NSS claim失败”这个具体报文覆盖了绝大部分实际问题。遇到类似的报错直接套这张表排一遍效率会高很多。还有一个值得记录的细节内核日志里的probe失败不一定是永久失败。如果你看到的是probe defer或-EPROBE_DEFER那说明驱动只是暂时没等到资源后面还有其他驱动释放资源时可能再次触发probe。这种情况下不要急着判断失败先确认整个启动过程是否完整。5.2 我这次最终定位到的根因我这边最终锁定的原因是默认板级设备树中已经把PB1定义成了一个板载状态指示用途并且占用了这个GPIO而我的SPI3从机配置里又通过cs-gpios重复请求了同一个引脚。GPIO子系统拒绝了一个引脚同时被两个驱动以不同方向申请的情况于是SPI驱动在claim PB1时收到了-EBUSY。把设备树改为PB1走SPI3_NSS的复用功能删掉cs-gpios并在pinctrl里明确声明PB1为SPI3_NSS之后问题彻底消失。整个过程让我再次确认了一个道理在异构多核SoC上外设功能的实现往往不是靠多写代码而是靠把系统资源描述的“所有权”理清楚。GPIO资源尤其如此它不像时钟可以有共享计数器同一个引脚同一时刻只能属于一个方向、一个使用者。5.3 如果你也想做类似调试我给几条建议第一不要在probe失败的第一时间就去翻SPI外设寄存器。先看GPIO资源因为GPIO层面的错误往往在日志最前面就写得很清楚。第二设备树里不要同时写pinctrl复用和cs-gpios这两者是互斥的描述方式同时出现会把驱动带偏。第三准备一个逻辑分析仪在SPI从机调试中的价值远超大部分仿真工具它能把真实信号给你看省去大量猜谜时间。我实际使用中发现真正花在“改设备树、编译、重启”上的时间只占三成剩下七成都耗在了“为什么这个引脚不能申请”的排查上。如果一开始就按GPIO占用、权限、硬件通路的顺序来可能半天就能定位到问题不至于像我这样折腾到半夜。希望这篇复盘能让你少走这一圈弯路。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门