驱动初始化流程:设备树解析、电源上电序列、时钟配置、复位与初始化序列、ID读取验证

各位做Camera驱动的朋友,大家好。今天我们来聊聊驱动初始化流程。说实话,这部分是驱动稳定性的基石。很多莫名其妙的bug,追根溯源,都是初始化阶段埋下的坑。

我个人习惯把初始化拆成五个关键步骤:设备树解析、电源上电、时钟配置、复位与初始化、ID验证。每一步都有讲究,我们一个一个来看。

1. 设备树解析:你的硬件到底长什么样?

设备树(DT)是驱动和硬件之间的“翻译官”。驱动通过它知道:Sensor挂在哪个I2C总线上?用哪路MCLK?电源怎么控?

我建议你养成一个习惯:所有硬件参数都从设备树读,不要硬编码。为什么?因为同一个驱动可能要适配多个项目,硬编码会让你改到崩溃。

举个例子,一个典型的Camera Sensor设备树节点长这样:

sensor0: camera-sensor@10 {
    compatible = "sony,imx307";
    reg = <0x10>;
    clocks = <&cam_clk 0>;
    clock-names = "xvclk";
    clock-frequency = <24000000>;
    avdd-supply = <&cam_avdd>;
    iovdd-supply = <&cam_iovdd>;
    dvdd-supply = <&cam_dvdd>;
    reset-gpios = <&gpio1 15 GPIO_ACTIVE_LOW>;
    powerdown-gpios = <&gpio1 16 GPIO_ACTIVE_HIGH>;
    status = "okay";
};

解析时,我一般按这个顺序来:

  • 获取基础资源:I2C地址、中断号、GPIO
  • 获取时钟信息:时钟源、频率
  • 获取电源信息:各路供电的regulator
  • 获取私有属性:比如lane数、翻转方向等
我的小技巧:解析完设备树后,建议把关键参数打印出来。我在项目中遇到过设备树写错I2C地址,结果驱动死活找不到设备。加个打印,一眼就能发现问题。

2. 电源上电序列:顺序错了,Sensor就挂了

电源上电,说白了就是给Sensor“喂电”。但喂电的顺序和时间间隔,每个Sensor都有自己的脾气。

我见过最典型的坑:先上IO电压,再上模拟电压。有些Sensor要求先上模拟电压再上IO,顺序反了,轻则初始化失败,重则烧坏芯片。

一般上电序列是这样的:

  1. 使能主电源(AVDD、DVDD)
  2. 等待稳定(通常1-5ms)
  3. 使能IO电源(IOVDD)
  4. 等待稳定(1ms左右)
  5. 拉高复位引脚(释放复位)
  6. 等待Sensor内部初始化完成(通常10-20ms)

代码实现上,我习惯用regulator框架:

static int sensor_power_on(struct sensor_dev *sdev)
{
    int ret;

    // 先上模拟电源
    ret = regulator_enable(sdev->avdd);
    if (ret) goto err_avdd;
    usleep_range(2000, 3000);

    // 再上IO电源
    ret = regulator_enable(sdev->iovdd);
    if (ret) goto err_iovdd;
    usleep_range(1000, 2000);

    // 释放复位
    gpiod_set_value(sdev->reset_gpio, 1);
    usleep_range(10000, 15000);

    return 0;

err_iovdd:
    regulator_disable(sdev->avdd);
err_avdd:
    return ret;
}
注意:上电时序的延时不能太短。我曾经为了赶性能,把延时从10ms减到5ms,结果部分Sensor在低温下初始化失败。后来老老实实按datasheet来,再没出过问题。

3. 时钟配置:频率不准,图像就花

时钟是Sensor的“心跳”。MCLK频率不对,PLL就锁不住,出来的图像要么花屏,要么帧率不对。

我建议你这样做:

  • 从设备树读取目标频率
  • 调用clk_set_rate设置时钟
  • 用clk_get_rate确认实际频率

为什么还要确认?因为有些时钟源可能不支持你想要的频率,它会自动选一个最接近的。你不检查,就埋下了隐患。

unsigned long target_rate = 24000000; // 24MHz
unsigned long actual_rate;

clk_set_rate(sdev->xvclk, target_rate);
actual_rate = clk_get_rate(sdev->xvclk);

if (abs(actual_rate - target_rate) > 100000) {
    dev_warn(sdev->dev, "MCLK频率偏差过大: %lu vs %lu\n",
             actual_rate, target_rate);
}
避坑指南:我曾经遇到一个案子,Sensor在常温下工作正常,高温下图像抖动。查了两天才发现,是时钟源的热漂移导致频率偏移。后来加了温度补偿,问题解决。

4. 复位与初始化序列:让Sensor“醒过来”

复位和初始化,说白了就是让Sensor从“沉睡”状态进入“工作”状态。这个过程,每个Sensor都有自己的“暗号”。

一般流程是:

  1. 拉低复位引脚(进入复位状态)
  2. 等待至少1ms
  3. 拉高复位引脚(释放复位)
  4. 等待Sensor内部初始化(通常10-30ms)
  5. 通过I2C写入初始化寄存器序列

初始化寄存器序列,我建议你这样做:

  • 先写软件复位寄存器(让Sensor内部复位)
  • 等待复位完成(轮询状态寄存器)
  • 配置模式寄存器(分辨率、帧率、输出格式)
  • 配置模拟增益、曝光等参数
  • 启动输出(设置Stream On)
static int sensor_init_sequence(struct sensor_dev *sdev)
{
    int ret;

    // 软件复位
    ret = sensor_write_reg(sdev, 0x0103, 0x01);
    if (ret) return ret;
    msleep(10);

    // 等待复位完成
    ret = sensor_poll_reg(sdev, 0x0100, 0x00, 100);
    if (ret) {
        dev_err(sdev->dev, "Sensor复位超时\n");
        return -ETIMEDOUT;
    }

    // 配置输出模式
    sensor_write_reg(sdev, 0x0340, 0x04);  // 高度高字节
    sensor_write_reg(sdev, 0x0341, 0x38);  // 高度低字节
    // ... 更多配置

    return 0;
}
注意:初始化序列中,每个寄存器写入后是否需要延时,要看datasheet。有些寄存器写入后需要等待几个帧周期才能生效。别问我怎么知道的,问就是吃过亏。

5. ID读取验证:确认你找对了“人”

ID验证,是初始化流程的最后一道防线。说白了就是读Sensor的型号寄存器,确认你操作的是正确的设备。

为什么需要这一步?因为I2C总线上可能挂多个设备,或者焊接有问题导致地址冲突。不验证,你后面写的所有寄存器都可能写到了错误的设备上。

一般做法是:

  • 读取芯片ID寄存器(通常是0x0000或0x0001)
  • 与datasheet中的预期值比较
  • 匹配则继续,不匹配则返回错误
static int sensor_check_id(struct sensor_dev *sdev)
{
    u16 chip_id;
    int ret;

    ret = sensor_read_reg(sdev, 0x0000, &chip_id);
    if (ret) {
        dev_err(sdev->dev, "读取芯片ID失败\n");
        return ret;
    }

    if (chip_id != 0x0307) {  // IMX307的ID
        dev_err(sdev->dev, "芯片ID不匹配: 0x%04x, 期望0x0307\n",
                chip_id);
        return -ENODEV;
    }

    dev_info(sdev->dev, "芯片ID验证通过: 0x%04x\n", chip_id);
    return 0;
}
核心要点:ID验证失败时,不要急着怀疑Sensor坏了。先检查I2C通信是否正常,电源是否到位,时钟是否稳定。我遇到过好几次,都是电源没上稳导致ID读不出来。

总结一下

驱动初始化流程,说白了就是“对暗号”的过程。设备树告诉驱动硬件长什么样,电源和时钟让Sensor“活过来”,复位和初始化让Sensor“准备好”,ID验证确认“没找错人”。

每一步都马虎不得。我见过太多驱动不稳定,最后发现是初始化阶段某个延时不够,或者某个寄存器没配对。嗯,细节决定成败,这话在驱动开发里一点不假。

下一章,我们会深入聊聊I2C通信的稳定性问题。到时候见。