3. Linux I2C 子系统架构:三层模型与数据结构关系

Linux I2C 子系统采用经典的三层软件架构设计,将硬件控制、总线管理与设备驱动解耦。理解这三层模型及其核心数据结构之间的关联,是进行 I2C 设备树配置与驱动开发的基础。

3.1 三层模型概述

I2C 子系统从上到下分为:I2C 设备驱动层、I2C 核心层、I2C 总线驱动层。每一层职责明确,通过标准接口交互。

层次 职责 核心数据结构 典型文件
I2C 设备驱动 实现具体外设(如触摸屏、传感器、音频编解码器)的驱动逻辑,通过 I2C 核心层提供的 API 与硬件通信。 i2c_driver、i2c_client drivers/input/touchscreen/
I2C 核心层 提供统一的设备注册/注销、总线管理、地址解析、适配器算法调用框架。不依赖具体硬件。 i2c_adapter、i2c_algorithm、i2c_bus_type drivers/i2c/i2c-core.c
I2C 总线驱动 直接操作 I2C 控制器硬件寄存器,实现时序产生、起始/停止条件、ACK/NACK 检测等底层操作。 i2c_adapter(实例化)、i2c_algorithm(实现) drivers/i2c/busses/i2c-imx.c
车载场景提示: 在车载 SoC(如 i.MX8、R-Car、J6)中,I2C 总线驱动通常由芯片厂商提供,开发者主要工作集中在 I2C 设备驱动层和设备树配置。理解三层边界有助于快速定位问题归属。

3.2 核心数据结构关系图

下图展示了 I2C 子系统中关键数据结构之间的关联。图中使用不同颜色区分三层归属,箭头表示指针引用或注册关系。

Linux I2C 子系统三层数据结构关系图 Linux I2C 子系统三层数据结构关系图 设备驱动层 核心层 总线驱动层 struct i2c_driver probe() / remove() / id_table struct i2c_client addr / adapter / name driver->client struct i2c_adapter nr / name / algo / clients struct i2c_algorithm master_xfer / smbus_xfer i2c_bus_type 总线匹配 / 热插拔 adapter->algo 注册到总线 i2c_imx / i2c_rcar 等 硬件寄存器操作 / 中断处理 Device Tree Node compatible / reg / clock 解析生成 注册到 adapter client->adapter adapter 实例化 算法实现 图例: ■ 设备驱动层 ■ 核心层 ■ 总线驱动层 ■ 设备树 ■ 总线类型 → 指针/注册关系 - - → 跨层关联

3.3 关键数据结构详解

3.3.1 struct i2c_driver(设备驱动层)

每个 I2C 外设驱动必须定义一个 i2c_driver 实例,并通过 i2c_add_driver() 注册到核心层。核心成员包括:

  • probe():设备匹配成功后调用,完成硬件初始化、注册字符设备/输入设备等。
  • remove():设备移除时调用,释放资源。
  • id_table:struct of_device_id 数组,用于和设备树 compatible 属性匹配。
  • driver:内嵌的 struct device_driver,由核心层统一管理。

3.3.2 struct i2c_client(设备驱动层)

代表一个挂载在 I2C 总线上的物理设备。由核心层在设备匹配成功后自动创建,驱动通过 client 指针访问设备:

  • addr:7 位或 10 位 I2C 从设备地址。
  • adapter:指向所属 i2c_adapter 的指针。
  • name:设备名称,用于调试和匹配。
  • dev:内嵌的 struct device,用于设备模型框架。

3.3.3 struct i2c_adapter(核心层)

代表一个 I2C 控制器(总线适配器)。每个 I2C 控制器硬件对应一个 i2c_adapter 实例:

  • nr:总线编号,如 /dev/i2c-1 中的 1。
  • algo:指向 i2c_algorithm 的指针,定义了实际的数据传输方法。
  • clients:挂载在此适配器上的设备链表头。
  • dev:内嵌设备结构,用于设备模型。

3.3.4 struct i2c_algorithm(核心层)

定义 I2C 控制器与从设备通信的底层方法。总线驱动必须实现以下回调:

回调函数 功能 参数说明
master_xfer() 标准 I2C 传输(支持多消息组合) adapter, msgs, num
smbus_xfer() SMBus 协议传输(可选) adapter, addr, flags, command, protocol, data
functionality() 返回适配器支持的功能掩码 adapter

3.4 设备注册与匹配流程

从设备树解析到驱动 probe 的完整链路如下:

  1. 设备树解析:内核启动时,of_platform_populate() 遍历设备树,为每个 compatible 匹配的 I2C 控制器节点创建 platform_device。
  2. 总线驱动 probe:I2C 总线驱动(如 i2c_imx_probe())被调用,初始化硬件寄存器,调用 i2c_add_numbered_adapter() 注册 i2c_adapter。
  3. 子节点扫描:核心层在适配器注册后,扫描设备树中该 I2C 控制器下的子节点,为每个子节点创建 i2c_client。
  4. 驱动匹配:核心层遍历已注册的 i2c_driver,通过 of_match_table 或 id_table 进行匹配。
  5. probe 调用:匹配成功后,调用驱动的 probe() 函数,传入 i2c_client 指针,驱动开始工作。
调试要点: 在车载调试中,若 I2C 设备无法挂载,应依次检查:① 设备树节点 compatible 是否与驱动 id_table 一致;② 总线驱动是否成功注册 adapter;③ 子节点 reg 属性中的地址是否与硬件一致;④ 内核是否编译了对应的驱动模块。

3.5 三层模型与设备树的映射关系

设备树中的 I2C 控制器节点和子节点,最终映射到内核中的三层数据结构:

设备树到三层模型的映射关系 设备树 → 三层数据结构映射 设备树 (Device Tree) i2c@30a20000 compatible = "fsl,imx8-i2c" touch@1a reg = <0x1a> codec@2c reg = <0x2c> sensor@48 reg = <0x48> 解析 内核三层数据结构 总线驱动层:i2c_imx_driver probe → i2c_add_adapter → 注册 i2c_adapter 核心层:i2c_adapter + i2c_algorithm 扫描子节点 → 创建 i2c_client → 匹配驱动 设备驱动层:多个 i2c_driver touch_driver (probe) | codec_driver (probe) | sensor_driver (probe) 每个 driver 对应一个 i2c_client

3.6 车载开发中的常见问题

常见陷阱:
  • 地址冲突: 同一 I2C 总线上两个设备使用相同地址(如 0x1a),导致 probe 失败。车载多传感器场景需仔细核对硬件原理图。
  • 适配器未注册: 总线驱动 probe 失败(如时钟未使能、复位未释放),导致子节点无法扫描。检查 /sys/bus/i2c/devices/ 确认 adapter 是否存在。
  • id_table 遗漏: 驱动中 of_match_table 未包含设备树中的 compatible 字符串,导致匹配失败。
  • reg 属性错误: 设备树中 reg 值必须与硬件从设备地址一致,且不能包含读写位(7 位地址左移 1 位是常见误解)。