资讯详情

资讯详情

设备树其实只描述三件事

前两天调一块 imx6ull 的板子外挂了一颗 I2C 的温湿度传感器驱动是从别处拷过来的按理说改个地址就能跑。结果 probe 死活不进来dmesg里干干净净连个报错都没有。我盯着arch/arm/boot/dts/imx6ull-myboard.dts看了半天越看越迷糊——这文件里一堆i2c1、pinctrl-0、status okay到底哪些是必须的哪些是我抄多了后来我把设备树这块重新捋了一遍发现其实没那么玄。设备树说白了就描述三件事有什么设备、设备挂在哪、设备用什么引脚和参数。想通这一点再看那些 dts 文件就顺眼多了。一、背景一个不 probe 的传感器板子是 imx6ull跑的 Linux 4.1.15NXP 那套 BSP。传感器是 SHT30I2C 地址 0x44挂在 I2C1 上SDA 用的是 UART4_TX_DATA 那个复用脚SCL 是 UART4_RX_DATA。我一开始是这么写的从别的板子的 dts 里抄了一段i2c1 { status okay; clock-frequency 100000; sht3044 { compatible sensirion,sht30; reg 0x44; }; };编译、烧录、重启i2cdetect -y 1能看到 0x44 有响应说明硬件通了。但ls /sys/bus/i2c/devices/里就是没有1-0044这个目录驱动的 probe 函数一次都没进。二、相关知识点设备树到底在描述什么先别急着改代码把设备树的作用想清楚。内核启动的时候CPU 只知道自己是 ARM 核、内存多大板子上焊了什么芯片、芯片接在哪根总线上、用哪个引脚内核是不知道的。以前这些信息全塞在arch/arm/mach-xxx/的 C 代码里板子一多就成一锅粥。设备树就是把这些板级信息抽出来变成一份给内核看的硬件清单。所以一份 dts 里你写的每一段本质上都在回答三个问题1、有什么设备—— 靠compatible字符串。这是设备和驱动配对的唯一钥匙驱动里of_match_table写什么dts 里就得写什么一个字符都不能差。2、设备挂在哪—— 靠设备树的层级关系。sht3044写在i2c1里面内核就知道它是 I2C1 总线上的设备reg 0x44就是它的从机地址。写在spi1里面那就是 SPI 设备reg变成片选号。位置决定总线类型这是设备树最省事的地方。3、设备用什么引脚、什么参数—— 靠pinctrl和自定义属性。引脚复用要单独定义 pinctrl 节点然后在设备节点里用pinctrl-0引用频率、中断脚、复位脚这些就写成自定义 property驱动里用of_property_read_u32()去读。至于status okay那只是这个节点要不要启用的开关跟设备本身没关系。clock-frequency是 I2C 控制器的总线速率也是控制器的事不是传感器的事。想明白这三层再回头看我的 dts——设备有了compatible 写了位置有了挂在 i2c1 下但引脚复用压根没配。imx6ull 的 I2C1 默认引脚和 UART4 那两个脚不是同一组不配 pinctrlI2C 控制器根本出不来波形。三、解决过程1、先确认 compatible 有没有对上别笑这一步最容易翻车。我grep了一下驱动源码grep -rn sht30 drivers/hwmon/驱动里写的是static const struct of_device_id sht30_of_match[] { { .compatible sensirion,sht30 }, { } };跟我 dts 里写的一致这条没问题。顺手提醒一句compatible建议用厂商,型号的格式全小写别自己造。2、确认 I2C 控制器真的使能了ls /sys/bus/i2c/devices/输出里有i2c-0、i2c-1说明控制器起来了。再看一眼cat /sys/bus/i2c/devices/i2c-1/name输出21a0000.i2c对得上。控制器 OK。3、补 pinctrl这是真正的坑去arch/arm/boot/dts/imx6ul-pinfunc.h里查引脚宏找到#define MX6UL_PAD_UART4_TX_DATA__I2C1_SCL 0x01b4 0x0440 0x0000 0x2 0x0 #define MX6UL_PAD_UART4_RX_DATA__I2C1_SDA 0x01b8 0x0444 0x0000 0x2 0x0在 dts 里加 pinctrl 节点iomuxc { pinctrl_i2c1_sht30: i2c1sht30grp { fsl,pins MX6UL_PAD_UART4_TX_DATA__I2C1_SCL 0x4001b8b0 MX6UL_PAD_UART4_RX_DATA__I2C1_SDA 0x4001b8b0 ; }; }; i2c1 { pinctrl-names default; pinctrl-0 pinctrl_i2c1_sht30; status okay; clock-frequency 100000; sht3044 { compatible sensirion,sht30; reg 0x44; }; };那个0x4001b8b0是 pad 配置值具体含义查参考手册的 IOMUXC 章节我一般直接抄同板子其他 I2C 的配置改一下电气属性。4、编译、烧录、重启make ARCHarm CROSS_COMPILEarm-linux-gnueabihf- imx6ull-myboard.dtb把 dtb 拷到板子/boot/下重启。实际测试还是不行呀/sys/bus/i2c/devices/1-0044还是没有probe 依然不进。我当时的第一反应是 pinctrl 又写错了把 dts 反编译回来对了一遍dtc -I dtb -O dts /boot/imx6ull-myboard.dtb | grep -A5 sht30节点确实在。又cat /proc/device-tree/soc/aips-bus02100000/i2c021a0000/status输出okay。设备树这边看着都对。折腾了大半天才发现是内核里的 SHT30 驱动压根没编进去。make menuconfig里那个选项是M但我烧的是最小根文件系统modules目录是空的insmod也没得 insmod。改成*编进内核重新烧 zImage重启1-0044目录终于出现了。cat /sys/bus/i2c/devices/1-0044/name输出sht30。再读一下温度cat /sys/class/hwmon/hwmon0/temp1_input输出2530025.3 摄氏度通了。四、结果和一点遗留现在回头看设备树那三个问题我一开始只答对了前两个——写了 compatible挂对了位置但引脚复用没配。等这个补上又栽在驱动没编进内核这种跟设备树八竿子打不着的地方。所以说设备树配对了不等于驱动能跑probe 不进的时候dts、驱动、内核配置三边都得查别一上来就怀疑设备树。遗留一个小问题clock-frequency我写的 100kHz但实测波形上升沿有点软估计是上拉电阻偏大。这个跟设备树没关系了得改板子先放着。参考资料内核文档Documentation/devicetree/bindings/i2c/i2c.txtNXP《i.MX 6ULL Applications Processor Reference Manual》第 32 章 IOMUXC感谢网上那些把 pinfunc 宏整理成表格的网友省了我不少事我这方面也就学了点皮毛上面有说错的地方欢迎留言指正一起交流。如果这篇对你有用请扫码关注。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →