RK3588 kernel-6.1/drivers/i2c/i2c-core-acpi.c 功能详细分析

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_ACPIrockchip_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
2
3
4
CONFIG_I2C=y
└── CONFIG_ACPI=y → 编译 i2c-core-acpi.o 并链接进 i2c-core.ko
└── CONFIG_ACPI_I2C_OPREGION=y(默认 y)
→ 启用 Operation Region 代码(文件内 #ifdef 块)

3.2 Kconfig(drivers/i2c/Kconfig

1
2
3
4
config ACPI_I2C_OPREGION
bool "ACPI I2C Operation region support"
depends on I2C=y && ACPI
default y

说明:允许 BIOS/UEFI 固件中的 AML 代码通过 General Serial Bus(GSBUS) 地址空间访问 I2C 从设备(如智能电池)。

3.3 无 ACPI 时的桩函数(i2c-core.h

1
2
3
4
5
6
7
#ifdef CONFIG_ACPI
void i2c_acpi_register_devices(struct i2c_adapter *adap);
int i2c_acpi_get_irq(struct i2c_client *client, bool *wake_capable);
#else
static inline void i2c_acpi_register_devices(struct i2c_adapter *adap) { }
static inline int i2c_acpi_get_irq(...) { return 0; }
#endif

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
2
3
4
struct i2c_acpi_handler_data {
struct acpi_connection_info info;
struct i2c_adapter *adapter;
};

绑定到 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 中筛选:

  1. type == ACPI_RESOURCE_TYPE_SERIAL_BUS
  2. serial_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_MODEI2C_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
2
3
4
5
6
acpi_bus_get_status()           /* 设备必须 present */
acpi_dev_ready_for_enumeration()
过滤 i2c_acpi_ignored_device_ids /* 如 ACPI_VIDEO_HID 误报 I2C */
acpi_dev_get_resources(..., i2c_acpi_fill_info, ...)
→ 填充 info->addr、adapter_handle、speed
acpi_get_handle(device, resource_source) /* 解析 adapter 连接 */

失败返回 -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.ci2c_register_adapter()):

1
2
3
of_i2c_register_devices(adap);
i2c_acpi_install_space_handler(adap);
i2c_acpi_register_devices(adap);

实现逻辑:

1
2
3
4
5
6
7
8
9
10
11
12
13
void i2c_acpi_register_devices(struct i2c_adapter *adap)
{
if (!has_acpi_companion(&adap->dev))
return;

/* 从 ACPI 根向下 walk,深度最多 32 层 */
acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
I2C_ACPI_MAX_SCAN_DEPTH,
i2c_acpi_add_device, NULL, adap, NULL);

/* 清除父设备依赖,避免 adapter 无法 suspend */
acpi_dev_clear_dependencies(ACPI_COMPANION(adap->dev.parent));
}

i2c_acpi_add_device 对每个 handle:

  1. i2c_acpi_get_info() 得到 i2c_board_info
  2. i2c_acpi_register_device()i2c_new_client_device()
  3. 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
2
3
4
5
6
7
PCI/platform I2C adapter probe
→ i2c_add_numbered_adapter()
→ of_i2c_register_devices() [通常为空]
→ i2c_acpi_register_devices()
acpi_walk_namespace → 各 I2C 从设备
i2c_new_client_device()
→ i2c_driver 匹配 → 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
2
3
4
5
} else if (ACPI_COMPANION(dev)) {
irq = i2c_acpi_get_irq(client, &wake_capable);
if (irq > 0 && wake_capable)
client->flags |= I2C_CLIENT_WAKE;
}

解析顺序:

  1. 扫描 ACPI 资源中的中断资源 → i2c_dev_irq_from_resources()
  2. 若无,尝试 acpi_dev_gpio_irq_wake_get()(ACPI GPIO 中断扩展);
  3. 返回 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 推断速率。

算法:

  1. acpi_walk_namespace 扫描所有 I2C 从设备;
  2. 仅统计连接到当前 adapter handle 的设备;
  3. 最慢connection_speed 作为 min_speed
  4. 特殊 quirk:存在 MSSL1680(Silead 触摸)时 强制 400kHzi2c_acpi_force_400khz_device_ids)。

导出符号,供 i2c-designware-common.ci2c-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
2
i2c_acpi_install_space_handler(adapter);   /* adapter 注册时 */
i2c_acpi_remove_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_bytesi2c_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_devicesi2c_acpi_get_irqi2c_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
2
3
4
5
6
7
8
9
10
# 查看 ACPI companion
ls -l /sys/bus/i2c/devices/*/firmware_node

# 查看已枚举 client
ls /sys/bus/i2c/devices/

# 内核日志
dmesg | grep -i "i2c.*acpi\|enumerate I2C slaves"

# 禁用 ACPI I2C 枚举(调试):通常通过不编译 CONFIG_ACPI 或修复 DSDT

17. 结论

i2c-core-acpi.c 是 Linux I2C 子系统在 ACPI/UEFI 固件体系 下的核心适配层,主要完成:

  1. 从 ACPI I2cSerialBus 资源解析地址、adapter 关联、速率、10 位地址;
  2. I2C adapter 注册 时遍历 ACPI 命名空间 自动创建 i2c_client
  3. 支持 热插拔重配置多资源 clientIRQ/GPIO 唤醒
  4. 可选 Operation Region 让固件 AML 经 GSBUS 访问 I2C;
  5. 为 adapter 驱动提供 总线速率推断 等辅助能力。

RK3588:默认 无 ACPI,I2C 完全依赖 Device Tree + i2c-rk3x.c;本文件属于架构对比与 x86 维护知识,而非 RK 产品路径的必读本。


18. 推荐阅读顺序

  1. i2c-core-acpi.ci2c_acpi_register_devicesi2c_acpi_do_lookup
  2. i2c-core-base.ci2c_register_adapteri2c_device_probe
  3. include/linux/i2c.hI2C_DRV_ACPI_WAIVE_D0_PROBE
  4. drivers/i2c/busses/i2c-designware-common.ci2c_acpi_find_bus_speed 使用示例
  5. Documentation/firmware-guide/acpi/ — I2cSerialBus 规范(内核文档)

19. 相关文档


文档版本:基于 rk3588/kernel-6.1 源码树 drivers/i2c/i2c-core-acpi.c 分析。

文章互动

阅读 --

留言

0 条留言

正在加载留言…