RK3588 kernel-6.1/drivers/i2c/i2c-core-acpi.c 功能详细分析
1. 文档范围与说明
路径说明: 用户表述为「i2c-core-acpi.c 目录」,实际为 单个源文件:
rk3588/kernel-6.1/drivers/i2c/i2c-core-acpi.c(约 825 行)
该文件在 Makefile 中作为 i2c-core 复合模块 的可选组成部分编译:
1 | i2c-core-$(CONFIG_ACPI) += i2c-core-acpi.o |
文档保存路径: linuxDoc/drivers/i2c/
定位: 为 x86/ACPI 固件平台 提供 I2C 子系统的 ACPI 支持:从 ACPI 命名空间解析 I2cSerialBus 资源、枚举从设备、解析 IRQ/总线速率,并可选实现 ACPI Operation Region(固件通过 AML 访问 I2C)。
与 RK3588 的关系: Rockchip ARM64 产品内核通常 不启用 CONFIG_ACPI(rockchip_linux_defconfig 中无 ACPI 配置),因此 本文件在 RK3588 默认构建中通常不参与链接;设备枚举走 i2c-core-of.c。本文仍完整分析其机制,便于对比 x86 与 ARM/DT 路径。
2. 功能总览
| 功能模块 | 主要符号 | 作用 |
|---|---|---|
| ACPI 资源解析 | i2c_acpi_get_i2c_resource() |
从 ACPI 资源中识别 I2cSerialBus |
| 设备枚举 | i2c_acpi_register_devices() |
adapter 注册后扫描 ACPI 命名空间创建 i2c_client |
| 热插拔/重配置 | i2c_acpi_notifier |
响应 ACPI 设备增删 |
| IRQ 解析 | i2c_acpi_get_irq() |
从 ACPI/GPIO 获取 client 中断号 |
| 总线速率 | i2c_acpi_find_bus_speed() |
按最慢从设备约束 adapter 时钟 |
| 适配器查找 | i2c_acpi_find_adapter_by_handle() |
按 ACPI handle 找 i2c_adapter |
| 多资源 client | i2c_acpi_new_device_by_fwnode() |
同一 ACPI 设备多个 I2cSerialBus 资源 |
| D0 探测豁免 | i2c_acpi_waive_d0_probe() |
特定驱动跳过 D0 电源域探测 |
| Operation Region | i2c_acpi_install_space_handler() |
固件 AML 经 GSBUS 访问 I2C(CONFIG_ACPI_I2C_OPREGION) |
3. 编译与配置依赖
3.1 依赖链
1 | CONFIG_I2C=y |
3.2 Kconfig(drivers/i2c/Kconfig)
1 | config ACPI_I2C_OPREGION |
说明:允许 BIOS/UEFI 固件中的 AML 代码通过 General Serial Bus(GSBUS) 地址空间访问 I2C 从设备(如智能电池)。
3.3 无 ACPI 时的桩函数(i2c-core.h)
1 |
|
RK3588 无 ACPI 时,上述调用在编译期被优化为空操作。
4. 核心数据结构
4.1 struct i2c_acpi_lookup
查找/填充 i2c_board_info 时的临时上下文:
| 字段 | 含义 |
|---|---|
info |
输出的 i2c_board_info |
adapter_handle |
I2cSerialBus 资源中 resource_source 指向的 adapter ACPI handle |
device_handle |
当前 ACPI 设备 handle |
index / n |
多资源时选择第 N 个 I2cSerialBus |
speed / min_speed / force_speed |
总线速率扫描用 |
4.2 struct i2c_acpi_handler_data(Operation Region)
1 | struct i2c_acpi_handler_data { |
绑定到 adapter 父设备的 ACPI handle,供 i2c_acpi_space_handler 使用。
4.3 struct gsb_buffer
ACPI GSBUS 访问协议缓冲区布局(status/len/data),与 AML _REG 读写格式对应。
5. ACPI 资源模型:I2cSerialBus
5.1 i2c_acpi_get_i2c_resource()
从 struct acpi_resource 中筛选:
type == ACPI_RESOURCE_TYPE_SERIAL_BUSserial_bus.type == ACPI_RESOURCE_SERIAL_TYPE_I2C
成功则得到 struct acpi_resource_i2c_serialbus *,包含:
| 字段 | 映射到 Linux |
|---|---|
slave_address |
i2c_board_info.addr |
connection_speed |
速率(Hz) |
access_mode |
ACPI_I2C_10BIT_MODE → I2C_CLIENT_TEN |
resource_source |
指向 host adapter 的 ACPI 路径 |
导出符号,供其他驱动(如 DesignWare I2C、平台驱动)解析 ACPI 资源。
5.2 i2c_acpi_client_count()
统计某 acpi_device 的 CRS(Current Resource Settings)中有多少个 I2cSerialBus 资源,用于需要多 client 的驱动。
6. 设备查找与注册流程
6.1 核心查找:i2c_acpi_do_lookup()
对单个 acpi_device 执行:
1 | acpi_bus_get_status() /* 设备必须 present */ |
失败返回 -EINVAL / -ENODEV。
6.2 i2c_acpi_get_info()
在 i2c_acpi_do_lookup 基础上:
- 若指定
adapter,校验ACPI_HANDLE(&adapter->dev) == lookup.adapter_handle; - 否则确认 adapter ACPI 设备存在且
status.present; - 设置
info->fwnode = acpi_fwnode_handle(adev); acpi_set_modalias()填充info->type(用于驱动匹配)。
6.3 Adapter 注册时批量枚举:i2c_acpi_register_devices()
调用点(i2c-core-base.c → i2c_register_adapter()):
1 | of_i2c_register_devices(adap); |
实现逻辑:
1 | void i2c_acpi_register_devices(struct i2c_adapter *adap) |
i2c_acpi_add_device 对每个 handle:
i2c_acpi_get_info()得到i2c_board_info;i2c_acpi_register_device()→i2c_new_client_device();acpi_device_set_enumerated()标记已枚举,避免重复。
6.4 与 OF 枚举的并行关系
同一 adapter 注册时 同时 执行:
| 顺序 | 函数 | 平台 |
|---|---|---|
| 1 | of_i2c_register_devices() |
ARM/DT(RK3588) |
| 2 | i2c_acpi_install_space_handler() |
x86 + OPREGION |
| 3 | i2c_acpi_register_devices() |
x86/ACPI |
三者互不排斥,但同一地址不应在 DT 与 ACPI 中重复声明。
6.5 时序图(x86)
1 | PCI/platform I2C adapter probe |
7. 运行时热插拔:i2c_acpi_notifier
在 i2c_init() 注册:
1 | acpi_reconfig_notifier_register(&i2c_acpi_notifier); |
| 事件 | 行为 |
|---|---|
ACPI_RECONFIG_DEVICE_ADD |
查找 adapter → i2c_acpi_register_device() |
ACPI_RECONFIG_DEVICE_REMOVE |
找 client → i2c_unregister_device();unbind adapter |
用于 ACPI 动态重配置(较少见,多用于服务器/特殊平台)。
8. IRQ 与电源相关
8.1 i2c_acpi_get_irq()
调用点: i2c_device_probe()(i2c-core-base.c),当 client 无 DT irq 且存在 ACPI companion 时:
1 | } else if (ACPI_COMPANION(dev)) { |
解析顺序:
- 扫描 ACPI 资源中的中断资源 →
i2c_dev_irq_from_resources(); - 若无,尝试
acpi_dev_gpio_irq_wake_get()(ACPI GPIO 中断扩展); - 返回 IRQ 号或负错误码。
8.2 i2c_acpi_waive_d0_probe()
当 i2c_driver 设置 I2C_DRV_ACPI_WAIVE_D0_PROBE 且 ACPI 设备电源状态允许时,跳过 dev_pm_domain_attach() 的 D0 探测,避免某些固件枚举顺序导致的 probe 失败。
9. 总线速率:i2c_acpi_find_bus_speed()
用途: 部分 I2C adapter 驱动(如 i2c-designware)在 probe 时若 DT 未给 clock-frequency,则调用此函数从 ACPI 推断速率。
算法:
acpi_walk_namespace扫描所有 I2C 从设备;- 仅统计连接到当前 adapter handle 的设备;
- 取 最慢 的
connection_speed作为min_speed; - 特殊 quirk:存在
MSSL1680(Silead 触摸)时 强制 400kHz(i2c_acpi_force_400khz_device_ids)。
导出符号,供 i2c-designware-common.c、i2c-amd-mp2-plat.c 等使用。
10. 辅助 API
10.1 i2c_acpi_find_adapter_by_handle()
通过 bus_find_device(..., device_match_acpi_handle) 在 i2c_bus_type 上查找 adapter。用于 ACPI 设备需绑定到特定 adapter 的场景(如 i2c-mux-gpio ACPI 路径)。
10.2 i2c_acpi_new_device_by_fwnode()
当单个 ACPI 设备含有 多个 I2cSerialBus 资源时,默认枚举只创建第一个;本 API 按 index 创建第 N 个资源的 client。
使用示例: Intel Cherry Trail 平台 intel/chtwc_int33fe.c(电池、FUSB302 等)、serial-multi-instantiate 驱动。
10.3 忽略列表 i2c_acpi_ignored_device_ids
1 | { ACPI_VIDEO_HID, 0 }, /* 显卡 ACPI 设备误带 I2C 资源,忽略 */ |
11. ACPI Operation Region(CONFIG_ACPI_I2C_OPREGION)
11.1 目的
UEFI/BIOS 中的 AML 字节码 可通过 Operation Region 在运行时访问硬件。本驱动在 adapter 父设备 的 ACPI handle 上安装 GSBUS(General Serial Bus) 地址空间处理器,使固件能经 I2C adapter 读写从设备(典型:智能电池 SMBus)。
11.2 安装与拆除
1 | i2c_acpi_install_space_handler(adapter); /* adapter 注册时 */ |
- 父设备必须有 ACPI companion;
acpi_install_address_space_handler(..., ACPI_ADR_SPACE_GSBUS, i2c_acpi_space_handler, ...)。
11.3 i2c_acpi_space_handler()
根据 AML 访问类型转发到 Linux I2C/SMBus API:
| ACPI GSB 访问类型 | Linux 调用 |
|---|---|
ACPI_GSB_ACCESS_ATTRIB_SEND_RCV |
i2c_smbus_read_byte / write_byte |
ACPI_GSB_ACCESS_ATTRIB_BYTE |
read_byte_data / write_byte_data |
ACPI_GSB_ACCESS_ATTRIB_WORD |
read_word_data / write_word_data |
ACPI_GSB_ACCESS_ATTRIB_BLOCK |
read_block_data / write_block_data |
ACPI_GSB_ACCESS_ATTRIB_MULTIBYTE |
acpi_gsb_i2c_read_bytes / write_bytes(i2c_transfer) |
每次访问临时构造 i2c_client(仅 addr/flags/adapter),不注册到驱动模型。
11.4 与 core 文档的对应
顶层 Kconfig 说明:
Operation Regions allow firmware (BIOS) code to access I2C slave devices, such as smart batteries through an I2C host controller driver.
12. 与 i2c-core-base.c 的集成点汇总
| 时机 | ACPI 相关调用 |
|---|---|
i2c_init() |
acpi_reconfig_notifier_register(&i2c_acpi_notifier) |
i2c_register_adapter() |
i2c_acpi_install_space_handler() + i2c_acpi_register_devices() |
i2c_del_adapter() |
i2c_acpi_remove_space_handler() |
i2c_device_probe() |
i2c_acpi_get_irq()、i2c_acpi_waive_d0_probe() |
| 驱动匹配 | acpi_driver_match_device() 可作为无 id_table 时的匹配依据 |
i2c_exit() |
注销 notifier |
13. 导出符号一览
| 符号 | 说明 |
|---|---|
i2c_acpi_get_i2c_resource |
解析 I2cSerialBus 资源 |
i2c_acpi_client_count |
统计 I2cSerialBus 数量 |
i2c_acpi_find_bus_speed |
推断 adapter 速率 |
i2c_acpi_find_adapter_by_handle |
按 handle 找 adapter |
i2c_acpi_new_device_by_fwnode |
按资源索引创建 client |
i2c_acpi_waive_d0_probe |
D0 探测豁免判断 |
i2c_acpi_notifier |
重配置通知块(非 export,core 内用) |
内部函数(i2c-core.h 声明给 core):i2c_acpi_register_devices、i2c_acpi_get_irq、i2c_acpi_install/remove_space_handler。
14. RK3588 平台说明
14.1 默认是否编译
| 配置项 | RK3588 典型值 |
|---|---|
CONFIG_ACPI |
未启用(无 ACPI 表) |
i2c-core-acpi.o |
不链接 |
| 设备枚举 | of_i2c_register_devices()(i2c-core-of.c) |
| IRQ | of_irq_get / of_irq_get_byname |
| 总线速率 | DT clock-frequency(如 100k/400k) |
14.2 何时需要了解本文件
- 维护 跨平台 驱动(同一驱动同时支持 ACPI 与 OF);
- 阅读 Intel/AMD x86 上 I2C 问题(枚举缺失、速率错误、电池 OP region);
- 内核配置误开 ACPI 时的行为分析(ARM 上一般应关闭)。
14.3 与 RK3588 相关的唯一间接引用
i2c-rk3x.c 中含 rk3x_i2c_acpi_get_bus_id(),用于 ACPI 平台 下从 ACPI UID 获取 bus number;在纯 DT 的 RK3588 上使用 of_alias_get_id(),不经过 i2c-core-acpi.c 的枚举逻辑。
15. 三种设备声明方式对比(复习)
| 方式 | 实现文件 | RK3588 |
|---|---|---|
| Device Tree | i2c-core-of.c |
使用 |
| Boardinfo 静态表 | i2c-boardinfo.c |
不使用 |
| ACPI 命名空间 | i2c-core-acpi.c |
不使用(无 ACPI) |
16. 调试建议(x86/ACPI 平台)
1 | # 查看 ACPI companion |
17. 结论
i2c-core-acpi.c 是 Linux I2C 子系统在 ACPI/UEFI 固件体系 下的核心适配层,主要完成:
- 从 ACPI I2cSerialBus 资源解析地址、adapter 关联、速率、10 位地址;
- 在 I2C adapter 注册 时遍历 ACPI 命名空间 自动创建
i2c_client; - 支持 热插拔重配置、多资源 client、IRQ/GPIO 唤醒;
- 可选 Operation Region 让固件 AML 经 GSBUS 访问 I2C;
- 为 adapter 驱动提供 总线速率推断 等辅助能力。
对 RK3588:默认 无 ACPI,I2C 完全依赖 Device Tree + i2c-rk3x.c;本文件属于架构对比与 x86 维护知识,而非 RK 产品路径的必读本。
18. 推荐阅读顺序
i2c-core-acpi.c—i2c_acpi_register_devices、i2c_acpi_do_lookupi2c-core-base.c—i2c_register_adapter、i2c_device_probeinclude/linux/i2c.h—I2C_DRV_ACPI_WAIVE_D0_PROBEdrivers/i2c/busses/i2c-designware-common.c—i2c_acpi_find_bus_speed使用示例Documentation/firmware-guide/acpi/— I2cSerialBus 规范(内核文档)
19. 相关文档
rk3588-kernel-6.1-drivers-i2c目录功能详细分析_zh.mdrk3588-kernel-6.1-i2c-boardinfo.c功能详细分析_zh.mdrk3588-kernel-6.1-drivers-i2c-busses目录功能详细分析_zh.md
文档版本:基于 rk3588/kernel-6.1 源码树 drivers/i2c/i2c-core-acpi.c 分析。
正在加载留言…