4. Sensor驱动初始化流程实战

Sensor 驱动能否被系统正确识别,几乎全部取决于 probe 函数这一段初始化流程是否严谨。联咏(Novatek)平台上的 Sensor 驱动通常挂在 I2C 总线上,由设备树或板级配置提供电源、时钟、复位和 PWDN 引脚信息。probe 函数被调用时,意味着内核已经匹配到设备节点,接下来要做的事情就是:把 Sensor 从“断电、复位、无时钟”的冷状态,一步步带到“I2C 可通信、ID 可读取”的可工作状态。本节围绕这条初始化链路展开,重点讲清楚每一步做什么、为什么这个顺序不能乱、以及如何验证。

一、probe 函数的整体职责与执行顺序

probe 函数不是简单地“打开电源然后读 ID”,它是一条有严格先后依赖的初始化链。典型顺序是:获取 GPIO 与 regulator 资源 → 使能电源(AVDD/DOVDD/DVDD)→ 使能 MCLK → 拉低复位、释放 PWDN → 等待稳定时间 → 建立 I2C 通信 → 读取并校验 Sensor ID → 注册 subdev 与回调。顺序错乱最常见的后果是 I2C 读不到 ACK,或者读回的 ID 是 0x00/0xFF。

下面这张图展示了联咏平台上 Sensor 从冷态到可通信状态的初始化信号链,包含电源、时钟、GPIO 与 I2C 之间的依赖关系:

Sensor 初始化信号链(冷态 → 可通信) 电源轨 AVDD 2.8V DOVDD 1.8V DVDD 1.2V MCLK 24MHz 主时钟 clk_prepare_enable GPIO 控制 RESET 复位脚 PWDN 掉电脚 I2C 总线 SCL / SDA 7bit 地址 0x36 t0 t1 t2 t3 t4 t5 上电 MCLK 稳定 复位拉低 PWDN 释放 I2C 探测 ID 校验 关键等待与依赖 • 电源全部稳定后再使能 MCLK,避免时钟先于电源导致锁存异常 • 复位保持低电平 ≥ 1ms,PWDN 拉低后需等待 ≥ 5ms 再访问 I2C • I2C 地址需与 Sensor 数据手册一致,常见 0x36 / 0x30 / 0x21 • ID 寄存器通常为 0x0000 或 0x0002,读回值需与预期高字节匹配 • 任一环节失败应回滚已使能资源,避免电源/时钟泄漏

二、电源与时钟管理

联咏平台一般通过 regulator 框架管理 Sensor 的多路电源。以常见的 2.8V/1.8V/1.2V 三路供电为例,probe 中需要先获取 regulator 句柄,再按顺序使能。典型代码结构如下:

static int sensor_probe(struct i2c_client *client,
                        const struct i2c_device_id *id)
{
    struct sensor_priv *priv;
    int ret;

    priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
    if (!priv)
        return -ENOMEM;

    priv->avdd  = devm_regulator_get(&client->dev, "avdd");
    priv->dovdd = devm_regulator_get(&client->dev, "dovdd");
    priv->dvdd  = devm_regulator_get(&client->dev, "dvdd");

    ret = regulator_enable(priv->avdd);
    if (ret) return ret;
    ret = regulator_enable(priv->dovdd);
    if (ret) goto err_avdd;
    ret = regulator_enable(priv->dvdd);
    if (ret) goto err_dovdd;

    priv->mclk = devm_clk_get(&client->dev, "mclk");
    ret = clk_prepare_enable(priv->mclk);
    if (ret) goto err_dvdd;

    msleep(5);   /* 等待电源与时钟稳定 */
    ...
}

这里有两个容易踩坑的点。第一,regulator 使能顺序建议先 AVDD 再 DOVDD 最后 DVDD,部分 Sensor 对 DVDD 先于 AVDD 上电敏感,可能触发内部 ESD 保护。第二,MCLK 必须在电源稳定后再使能,且频率要与 Sensor 规格一致,常见为 24MHz。若时钟频率偏差过大,I2C 虽然可能通信成功,但后续出图会出现花屏或帧率异常。

三、GPIO 复位与 PWDN 控制

复位(RESET)和掉电(PWDN)是两个独立引脚,但常常被混淆。RESET 用于让 Sensor 内部寄存器回到默认值,PWDN 用于让 Sensor 进入低功耗待机。probe 中的标准动作是:先将 RESET 拉低、PWDN 拉低(或按手册要求拉高),保持一段时间后释放 RESET,再释放 PWDN。

priv->reset_gpio = devm_gpiod_get(&client->dev, "reset", GPIOD_OUT_LOW);
priv->pwdn_gpio  = devm_gpiod_get(&client->dev, "pwdn",  GPIOD_OUT_LOW);

gpiod_set_value(priv->reset_gpio, 0);
gpiod_set_value(priv->pwdn_gpio,  0);
msleep(2);

gpiod_set_value(priv->reset_gpio, 1);   /* 释放复位 */
msleep(1);
gpiod_set_value(priv->pwdn_gpio,  1);   /* 退出掉电 */
msleep(5);

注意 PWDN 的有效电平因 Sensor 而异,有的高有效、有的低有效,必须查数据手册确认。如果 PWDN 一直处于有效状态,I2C 从机会被关闭,表现为读地址无 ACK。复位保持时间一般要求 ≥ 1ms,释放后到第一次 I2C 访问之间建议再等 1~5ms,给内部 PLL 锁定留出时间。

四、I2C 通信验证与 Sensor ID 读取

完成电源、时钟、GPIO 后,就可以验证 I2C 通信了。联咏平台常用 i2c_transfer 或 regmap 封装读写。最直接的验证方式是读取 Sensor 的 ID 寄存器。以某款常见 Sensor 为例,ID 寄存器地址为 0x0000,高字节应为 0x56,低字节为 0x40。读取代码如下:

static int sensor_read_id(struct sensor_priv *priv, u16 *id)
{
    u8 buf[2];
    struct i2c_msg msgs[2];
    u8 reg_addr[2] = {0x00, 0x00};

    msgs[0].addr  = priv->client->addr;
    msgs[0].flags = 0;
    msgs[0].len   = 2;
    msgs[0].buf   = reg_addr;

    msgs[1].addr  = priv->client->addr;
    msgs[1].flags = I2C_M_RD;
    msgs[1].len   = 2;
    msgs[1].buf   = buf;

    if (i2c_transfer(priv->client->adapter, msgs, 2) != 2)
        return -EIO;

    *id = (buf[0] << 8) | buf[1];
    return 0;
}

读回 ID 后必须做校验,而不是打印一下就算完成。校验逻辑通常是:先判断读操作是否成功,再判断 ID 是否等于预期值。如果 ID 为 0x0000 或 0xFFFF,说明 I2C 没有真正通信成功,需要回头检查地址、上拉电阻、电源和 PWDN 状态。如果 ID 是其他值,可能是地址冲突或读到了别的器件。

调试小技巧:在 probe 中临时把读到的 ID 通过 dev_info 打印出来,配合示波器观察 SCL/SDA 波形,可以快速区分“没通信”和“通信了但 ID 不对”两类问题。

五、失败回滚与资源释放

probe 中任何一步失败,都必须回滚前面已经使能的资源,否则会造成电源常开、时钟泄漏,甚至影响其他模块。推荐使用 goto 标签逐级回滚,顺序与使能顺序相反:先关 MCLK,再依次关 DVDD、DOVDD、AVDD。GPIO 如果使用了 devm 管理,通常无需手动释放,但显式拉回复位状态更稳妥。

常见错误:只在成功路径释放资源,失败路径直接 return,导致 regulator 引用计数不平衡。多次 insmod/rmmod 后会出现“regulator 无法关闭”的告警。

六、probe 完成后的注册动作

ID 校验通过后,probe 还需要完成 v4l2 subdev 的注册、设置 sensor 的操作函数集(s_stream、s_fmt、s_power 等),并把 priv 挂到 client 的 drvdata 上。至此,Sensor 才真正对上层 ISP 可见。后续的曝光、增益、帧率配置,都建立在这一步成功的基础之上。

把整条链路串起来看,probe 的本质是“按依赖顺序把硬件唤醒,并用 ID 读回作为最终验收”。电源、时钟、GPIO、I2C 四者缺一不可,顺序和等待时间是最容易被忽视却最影响成功率的细节。实际调试时,建议先确保 ID 能稳定读回,再去调图像效果,否则会在错误的方向上浪费大量时间。