4. DTS设备树配置:RK3588/RK356x平台实战

设备树(Device Tree Source,DTS)是嵌入式Linux驱动开发的基石。说白了,它就是一份硬件描述清单,告诉内核:你有哪些外设、它们挂在哪里、用什么时钟和GPIO。

我在RK3588和RK356x上调试Sensor驱动时,遇到过好几次DTS写错导致系统起不来的情况。嗯,这章我们就来把DTS的配置逻辑彻底理清楚。

4.1 DTS基础结构

一个完整的Sensor DTS节点,通常包含以下几部分:

  • compatible:驱动匹配的关键字符串
  • reg:I2C地址或寄存器基址
  • clocks:时钟资源绑定
  • pinctrl:引脚复用配置
  • power-domains:电源域控制
  • avdd/dovdd/dvdd:电源 regulator 绑定

核心原则:DTS只描述硬件,不描述行为。行为是驱动的事。

4.2 时钟资源绑定实战

Sensor通常需要两个时钟:MCLK(主时钟)和PCLK(外设时钟)。RK3588的时钟树比较复杂,我建议直接使用clk-provider方式绑定。

// RK3588 Sensor时钟节点示例
sensor_mclk: sensor-mclk {
    compatible = "fixed-clock";
    #clock-cells = <0>;
    clock-frequency = <24000000>;  // 24MHz
    clock-output-names = "sensor_mclk";
};

// Sensor节点中引用
i2c2: i2c@fdd40000 {
    status = "okay";
    
    sensor0: sensor@30 {
        compatible = "ov5648";
        reg = <0x30>;
        
        clocks = <&cru CLK_MIPI_CAMERA0>,
                 <&sensor_mclk>;
        clock-names = "xvclk", "mclk";
        
        // 时钟频率配置
        assigned-clocks = <&cru CLK_MIPI_CAMERA0>;
        assigned-clock-rates = <24000000>;
    };
};

我的经验:RK3588的CLK_MIPI_CAMERA0默认是24MHz,但有些Sensor需要27MHz。记得检查Sensor datasheet,别想当然。

4.3 GPIO与pinctrl配置

GPIO配置是DTS里最容易出错的地方。RK平台使用pinctrl子系统管理引脚复用,每个引脚可以配置为GPIO、I2C、SPI等功能。

4.3.1 pinctrl节点定义

// pinctrl配置
&pinctrl {
    sensor_pins: sensor-pins {
        rockchip,pins =
            // 复位引脚:GPIO4_B5,默认输出高
            <4 RK_PB5 RK_FUNC_GPIO &pcfg_output_high>,
            // 电源使能:GPIO4_B6,默认输出低
            <4 RK_PB6 RK_FUNC_GPIO &pcfg_output_low>,
            // 中断引脚:GPIO4_B7,上拉输入
            <4 RK_PB7 RK_FUNC_GPIO &pcfg_pull_up>;
    };
};

4.3.2 Sensor节点引用pinctrl

sensor0: sensor@30 {
    compatible = "ov5648";
    reg = <0x30>;
    
    pinctrl-names = "default", "sleep";
    pinctrl-0 = <&sensor_pins>;
    pinctrl-1 = <&sensor_pins_sleep>;
    
    // GPIO资源
    reset-gpios = <&gpio4 RK_PB5 GPIO_ACTIVE_LOW>;
    power-gpios = <&gpio4 RK_PB6 GPIO_ACTIVE_HIGH>;
    irq-gpios = <&gpio4 RK_PB7 GPIO_ACTIVE_HIGH>;
};

我曾经踩过的坑:RK3588的GPIO bank编号从0开始,但有些文档写的是1开始。我因为搞混了bank号,浪费了整整一天。建议直接看TRM的GPIO章节,别信二手资料。

4.4 电源管理配置

Sensor通常需要三路电源:AVDD(模拟)、DOVDD(数字IO)、DVDD(数字核心)。RK平台推荐使用regulator框架管理。

电源名称 典型电压 regulator绑定 备注
AVDD 2.8V avdd-supply 模拟电源,噪声敏感
DOVDD 1.8V dovdd-supply IO电源,与SoC电平匹配
DVDD 1.2V dvdd-supply 核心电源,电流较大
// regulator节点
vcc_avdd: regulator-avdd {
    compatible = "regulator-fixed";
    regulator-name = "vcc_avdd";
    regulator-min-microvolt = <2800000>;
    regulator-max-microvolt = <2800000>;
    regulator-always-on;
    regulator-boot-on;
    gpio = <&gpio4 RK_PB6 GPIO_ACTIVE_HIGH>;
    enable-active-high;
};

// Sensor节点引用
sensor0: sensor@30 {
    avdd-supply = <&vcc_avdd>;
    dovdd-supply = <&vcc_dovdd>;
    dvdd-supply = <&vcc_dvdd>;
};

4.5 完整DTS示例:RK3588 + OV5648

下面是一个完整的实战配置,我把它整理成了可复用的模板:

// RK3588 OV5648 DTS配置
#include "rk3588.dtsi"

&i2c2 {
    status = "okay";
    pinctrl-names = "default";
    pinctrl-0 = <&i2c2m0_xfer>;
    
    ov5648: camera@36 {
        compatible = "ovti,ov5648";
        reg = <0x36>;
        
        // 时钟配置
        clocks = <&cru CLK_MIPI_CAMERA0>;
        clock-names = "xvclk";
        assigned-clocks = <&cru CLK_MIPI_CAMERA0>;
        assigned-clock-rates = <24000000>;
        
        // pinctrl
        pinctrl-names = "default";
        pinctrl-0 = <&camera_pins>;
        
        // GPIO
        reset-gpios = <&gpio4 RK_PB5 GPIO_ACTIVE_LOW>;
        pwdn-gpios = <&gpio4 RK_PB6 GPIO_ACTIVE_HIGH>;
        
        // 电源
        avdd-supply = <&vcc_avdd>;
        dovdd-supply = <&vcc_dovdd>;
        dvdd-supply = <&vcc_dvdd>;
        
        // MIPI CSI配置
        port {
            ov5648_out: endpoint {
                remote-endpoint = <&mipi_csi2_input>;
                data-lanes = <1 2>;
            };
        };
    };
};

&pinctrl {
    camera_pins: camera-pins {
        rockchip,pins =
            <4 RK_PB5 RK_FUNC_GPIO &pcfg_output_high>,
            <4 RK_PB6 RK_FUNC_GPIO &pcfg_output_low>;
    };
};

关键检查点:编译DTS时,用make dtbs检查语法错误。我习惯在arch/arm64/boot/dts/rockchip/目录下直接编译,方便快速迭代。

4.6 DTS调试技巧

DTS写对了,但系统不认?别急,我总结了几条实用技巧:

  1. 查看设备树是否加载:ls /proc/device-tree/ 检查节点是否存在
  2. 检查GPIO状态:cat /sys/kernel/debug/gpio 查看引脚复用
  3. 时钟调试:cat /sys/kernel/debug/clk/clk_summary 确认时钟频率
  4. I2C探测:i2cdetect -y 2 确认Sensor地址是否正确

我的习惯:每次修改DTS后,先diff对比一下改动,避免手误。我曾经因为多打了一个分号,导致整个I2C总线挂掉,查了半小时才发现。

4.7 知识体系图

下面这张图总结了DTS配置的核心流程和资源绑定关系:

RK3588 Sensor DTS配置流程 Sensor DTS配置核心流程 DTS根节点 I2C控制器节点 时钟资源 clocks / assigned-clocks GPIO & pinctrl reset/pwdn/irq-gpios 电源管理 avdd/dovdd/dvdd-supply Sensor设备节点 编译命令:make dtbs → 生成 .dtb → 烧录到 boot 分区

4.8 常见错误与避坑

错误类型 现象 解决方案
GPIO bank号错误 Sensor无法复位 核对TRM,GPIO0-4对应bank0-4
时钟频率不匹配 MIPI无信号 检查assigned-clock-rates与实际需求
regulator缺失 Sensor上电失败 确保所有supply都绑定到有效regulator
pinctrl冲突 引脚被其他外设占用 检查pinctrl状态,避免复用冲突

我曾经犯过的错:在RK3568上,我把Sensor的复位引脚配置成了GPIO_ACTIVE_HIGH,但Sensor手册要求低电平复位。结果Sensor一直处于复位状态,I2C都探测不到。嗯,从那以后我每次都会仔细检查GPIO的极性。

DTS配置是Sensor驱动开发的第一步,也是最重要的一步。写对了,后面调试就顺风顺水;写错了,可能连系统都起不来。我个人建议:每次修改DTS后,先编译检查,再烧录验证,别偷懒。