4. DTS设备树配置:sensor节点编写、clk/power/gpio资源绑定、pinctrl配置
设备树(Device Tree)是RK平台驱动开发的基石。说白了,它就是一块“硬件描述板”,告诉内核你的Sensor接在哪个I2C上、用什么时钟、怎么上电、中断脚是哪个。我见过不少新手,代码写得挺溜,结果DTS配错了,驱动死活不 probe。嗯,这一节咱们就把DTS这块啃透。
4.1 Sensor节点基本结构
一个标准的Camera Sensor节点,通常挂在I2C总线下。我个人习惯先写好骨架,再往里填资源。来看一个典型的OV5645节点:
&i2c4 {
status = "okay";
clock-frequency = <400000>;
ov5645: camera-sensor@3c {
compatible = "ovti,ov5645";
reg = <0x3c>;
status = "okay";
clocks = <&cru SCLK_CIF_OUT>;
clock-names = "xvclk";
pinctrl-names = "default";
pinctrl-0 = <&cif_clkout_m0>;
power-domains = <&power RK3588_PD_VI>;
avdd-supply = <&vcc_camera>;
dvdd-supply = <&vcc_camera_dvdd>;
dovdd-supply = <&vcc_camera_dovdd>;
reset-gpios = <&gpio1 RK_PB0 GPIO_ACTIVE_LOW>;
pwdn-gpios = <&gpio1 RK_PB1 GPIO_ACTIVE_HIGH>;
rotation = <180>;
orientation = <0>;
port {
ov5645_ep: endpoint {
remote-endpoint = <&mipi_csi2_input>;
data-lanes = <1 2>;
};
};
};
};
关键字段速查表
| 字段 | 含义 | 常见坑 |
|---|---|---|
| compatible | 驱动匹配字符串 | 必须与驱动中of_match_table一致 |
| reg | I2C设备地址 | 7位地址,左对齐还是右对齐?RK默认7位 |
| clocks | 时钟句柄 | 别忘了clock-names要对应 |
| pinctrl-0 | 引脚复用配置 | 不配的话,CLK可能出不来波形 |
| reset-gpios | 复位引脚 | 注意极性:ACTIVE_LOW还是HIGH |
4.2 时钟资源绑定
Sensor的MCLK(主时钟)是命脉。没时钟,Sensor就是一坨硅。RK平台通常通过CRU(Clock & Reset Unit)提供时钟。
我曾经在一个项目里,Sensor死活不出图,示波器一量MCLK——没波形。查了半天,发现DTS里clock-names写成了"xvclk",驱动里却用"xvclk"去clk_get,但CRU输出的实际名字是"clk_cif_out"。对不上,时钟没开。
正确的做法是:
clocks = <&cru SCLK_CIF_OUT>;
clock-names = "xvclk";
驱动里这样拿:
sensor->xvclk = devm_clk_get(dev, "xvclk");
if (IS_ERR(sensor->xvclk)) {
dev_err(dev, "failed to get xvclk\n");
return PTR_ERR(sensor->xvclk);
}
小技巧: 调试时钟时,可以在/sys/kernel/debug/clk/clk_summary里看时钟有没有被enable。如果没开,检查clk_prepare_enable有没有被调用。
4.3 Power资源绑定
Sensor一般需要三路供电:AVDD(模拟)、DVDD(数字核心)、DOVDD(IO电压)。RK平台用regulator框架管理。
DTS里这样配:
avdd-supply = <&vcc_camera>;
dvdd-supply = <&vcc_camera_dvdd>;
dovdd-supply = <&vcc_camera_dovdd>;
驱动里获取:
sensor->avdd = devm_regulator_get(dev, "avdd");
if (IS_ERR(sensor->avdd))
return PTR_ERR(sensor->avdd);
ret = regulator_set_voltage(sensor->avdd, 2800000, 2800000);
if (ret)
dev_warn(dev, "failed to set avdd voltage\n");
注意: 上电时序很重要!有些Sensor要求先上DOVDD,再上AVDD,最后上DVDD。如果顺序错了,Sensor可能无法正常初始化。我遇到过一款GC系列Sensor,上电顺序反了,I2C通信都建立不起来。
4.4 GPIO资源绑定
GPIO主要用于复位(RESET)和掉电(PWDN)。RK平台用gpio descriptor API。
DTS配置:
reset-gpios = <&gpio1 RK_PB0 GPIO_ACTIVE_LOW>;
pwdn-gpios = <&gpio1 RK_PB1 GPIO_ACTIVE_HIGH>;
驱动里操作:
sensor->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
if (IS_ERR(sensor->reset_gpio))
return PTR_ERR(sensor->reset_gpio);
// 复位:拉低再拉高
gpiod_set_value(sensor->reset_gpio, 0);
usleep_range(1000, 2000);
gpiod_set_value(sensor->reset_gpio, 1);
GPIO极性对照表
| GPIO标志 | 含义 | 驱动中初始值 |
|---|---|---|
| GPIO_ACTIVE_LOW | 低电平有效 | GPIOD_OUT_HIGH(默认非复位状态) |
| GPIO_ACTIVE_HIGH | 高电平有效 | GPIOD_OUT_LOW(默认非复位状态) |
4.5 pinctrl配置
pinctrl决定了芯片引脚的功能复用。比如某个引脚是当GPIO用,还是当I2C用,还是当CLK输出用。
RK平台通常在pinctrl节点里定义好function和group:
&pinctrl {
cif_clkout_m0: cif-clkout-m0 {
rockchip,pins =
<1 RK_PB2 2 &pcfg_pull_none>;
};
};
然后在Sensor节点里引用:
pinctrl-names = "default";
pinctrl-0 = <&cif_clkout_m0>;
为什么需要pinctrl?你想想看,如果不配pinctrl,内核不知道这个引脚要当CLK用,可能默认就是GPIO模式,CLK信号根本出不来。
调试方法: 可以在/sys/kernel/debug/pinctrl/pinctrl-handles里查看当前引脚复用状态。如果发现引脚模式不对,检查pinctrl节点是否写对了。
4.6 知识体系总览
下面这张图总结了DTS配置的核心流程和资源关系:
4.7 常见问题与避坑
- compatible不匹配: 驱动里of_match_table写的是"ovti,ov5645",DTS里写成了"ovti,ov5645-v1",对不上,驱动不probe。我曾经花了一下午查这个问题,最后发现就是多了一个"-v1"。
- I2C地址错误: Sensor datasheet给的地址通常是8位(含读写位),DTS里要填7位地址。比如8位地址0x78,7位就是0x3c。
- 时钟频率不对: 有些Sensor对MCLK频率很敏感,比如OV5645要求24MHz,你配成了27MHz,Sensor可能无法正常输出图像。
- 上电时序: 如果Sensor初始化失败,先检查电源有没有按datasheet要求的顺序上电。用示波器抓一下波形,一目了然。
- pinctrl冲突: 同一个引脚被两个设备用了,会导致pinctrl申请失败。检查dmesg里有没有"pin already requested"的报错。
重要提醒: 修改DTS后,一定要重新编译并烧录dtb文件。我见过有人改了DTS源码,但没重新编译,结果还是老配置在跑。用fdtdump或者dtc -I dtb -O dts可以反编译dtb,确认修改是否生效。