RK3588 kernel-6.1/drivers/i2c/i2c-smbus.c 功能详细分析
1. 文档范围与说明
路径说明: 用户表述为「i2c-smbus.c 目录」,实际为 单个源文件:
rk3588/kernel-6.1/drivers/i2c/i2c-smbus.c(约 461 行)
编译关系: 独立内核模块,由 CONFIG_I2C_SMBUS 控制:
1
| obj-$(CONFIG_I2C_SMBUS) += i2c-smbus.o
|
与 i2c-core-smbus.c 的区别(易混淆):
| 文件 |
编入 |
职责 |
i2c-core-smbus.c |
始终进 i2c-core.ko |
SMBus 协议 API(i2c_smbus_xfer、PEC、I2C 模拟) |
i2c-smbus.c(本文) |
可选 i2c-smbus.ko |
SMBus 扩展协议:Alert、Host Notify 从机接收、DMI SPD 自动枚举 |
文档保存路径: linuxDoc/drivers/i2c/
定位: 实现 SMBus 规范中 Alert Response Address (ARA) 与 Host Notify 的 Linux 侧基础设施,并(在 x86 + DMI 场景)自动创建 SPD/EEPROM client。
2. 功能总览
| 功能块 |
条件编译 |
主要符号 |
作用 |
| SMBus Alert |
始终(模块主体) |
smbalert_driver、smbus_alert() |
SMBALERT# 中断 → 读 ARA → 通知设备驱动 alert() |
| Alert 入口(总线驱动) |
同上 |
i2c_handle_smbus_alert() |
由主机驱动 ISR 调度 work |
| Host Notify 从机 |
CONFIG_I2C_SLAVE |
i2c_new_slave_host_notify_device() |
从地址 0x08 收 Host Notify → 触发虚拟 IRQ |
| DMI SPD 自动实例化 |
CONFIG_DMI |
i2c_register_spd() |
根据 BIOS DMI 在 0x50+n 探测内存 SPD |
3. 架构:SMBus Alert
3.1 协议背景
- 从设备将 SMBALERT# 线拉低表示有事件;
- 主机通过 Alert Response Address (ARA)
0x0c 执行 Receive Byte,得到 待响应设备的 7 位地址(及 1 位标志);
- 多个设备按地址 从低到高 依次响应,直至 SMBALERT# 释放。
3.2 Linux 组件关系
1 2 3 4 5 6 7 8 9 10 11 12 13
| adapter 注册 (i2c-core-base.c) → i2c_setup_smbus_alert(adap) [i2c-core-smbus.c, 需 CONFIG_I2C_SMBUS] → i2c_new_smbus_alert_device() 创建 client "smbus_alert" @0x0c → smbalert_probe() [i2c-smbus.c] → 注册 IRQ "smbus_alert" 或仅保存 ARA client
SMBALERT# 触发: 方式 A: IRQ → smbus_alert() → work → 循环 i2c_smbus_read_byte(ara) 方式 B: 总线驱动 ISR → i2c_handle_smbus_alert(ara) → schedule_work → 同上
对每个 alert 地址: device_for_each_child(adapter, smbus_do_alert) → i2c_driver->alert(client, type, data)
|
3.3 核心数据结构
1 2 3 4 5 6 7 8 9 10
| struct i2c_smbus_alert { struct work_struct alert; struct i2c_client *ara; };
struct alert_data { unsigned short addr; enum i2c_alert_protocol type; unsigned int data; };
|
3.4 smbus_alert() 中断/工作处理
1 2 3 4
| status = i2c_smbus_read_byte(ara); data.addr = status >> 1; data.data = status & 1; device_for_each_child(&ara->adapter->dev, &data, smbus_do_alert);
|
- 循环直到
read_byte 失败(无更多待处理设备);
- 重复地址 且未处理:再
smbus_do_alert_force 广播所有带 alert 的驱动后 break,防止死循环。
3.5 smbus_do_alert() / smbus_do_alert_force()
| 函数 |
行为 |
smbus_do_alert |
匹配 client->addr == data.addr,调用 driver->alert(),返回 -EBUSY 停止迭代 |
smbus_do_alert_force |
对总线上 所有 已绑定且带 alert 的驱动调用(不比较地址) |
驱动需在 struct i2c_driver 中实现:
1
| void (*alert)(struct i2c_client *client, enum i2c_alert_protocol type, unsigned int data);
|
示例:sbs-battery.c、lm90.c、stts751.c 等。
3.6 smbalert_probe() — ARA 设备
- client 名称:
smbus_alert,地址 0x0c(由 i2c_new_smbus_alert_device 创建);
- IRQ 来源:
platform_data → i2c_smbus_alert_setup.irq;或
- 父设备 fwnode:
interrupt-names = "smbus_alert";
devm_request_threaded_irq:thread_fn = smbus_alert,IRQF_SHARED | IRQF_ONESHOT;
- 无 IRQ 时仅创建 ARA client,由 总线驱动轮询 并调用
i2c_handle_smbus_alert()。
3.7 i2c_handle_smbus_alert()
1 2 3 4
| int i2c_handle_smbus_alert(struct i2c_client *ara) { return schedule_work(&alert->alert); }
|
导出符号,供 不能在 IRQ 里睡眠 的主机驱动使用(如 PCI SMBus 控制器)。
3.8 与 i2c-core-smbus.c 的衔接
1 2 3 4 5 6 7 8 9 10 11 12
| struct i2c_client *i2c_new_smbus_alert_device(struct i2c_adapter *adapter, ...) { I2C_BOARD_INFO("smbus_alert", 0x0c); return i2c_new_client_device(adapter, &ara_board_info); }
int i2c_setup_smbus_alert(struct i2c_adapter *adapter) { return i2c_new_smbus_alert_device(adapter, NULL); }
|
i2c_register_adapter() 中调用 i2c_setup_smbus_alert()(见 i2c-core-base.c 文档)。
4. 架构:SMBus Host Notify(CONFIG_I2C_SLAVE)
4.1 协议背景
- 从设备向 固定地址
0x08 写入 3 字节(含通知方地址等),通知主机有事件;
- Linux 主机侧常映射为 per-client 虚拟 IRQ(
i2c-core-base.c 中 host_notify_domain)。
4.2 本文件角色
当控制器 支持 Slave 模式 时,可创建 从机 client 监听 0x08:
1
| struct i2c_client *i2c_new_slave_host_notify_device(struct i2c_adapter *adapter)
|
| 步骤 |
说明 |
| 创建 client |
I2C_BOARD_INFO("smbus_host_notify", 0x08),I2C_CLIENT_SLAVE |
| 从机回调 |
i2c_slave_host_notify_cb |
| STOP 且收到 3 字节 |
i2c_handle_smbus_host_notify(adapter, status->addr) |
i2c_handle_smbus_host_notify() 在 i2c-core-base.c 中实现:根据地址在 host_notify_domain 查找 IRQ 并 generic_handle_irq_safe()。
4.3 从机回调逻辑
1 2 3
| I2C_SLAVE_WRITE_RECEIVED: 记录首字节为 addr,index++ I2C_SLAVE_STOP: 若 index == 3 → i2c_handle_smbus_host_notify() I2C_SLAVE_READ_*: 返回 0xff(占位)
|
注释说明:当前 未解析 Host Notify 的完整数据参数,仅用首字节作地址。
4.4 释放
1 2
| i2c_free_slave_host_notify_device(client);
|
4.5 RK3588
- 需
CONFIG_I2C_SLAVE + i2c-rk3x 实现 reg_slave(当前 无);
- Host Notify 从机路径在 RK3588 上 不可用;
- 若仅作 Master 的 client 使用
I2C_CLIENT_HOST_NOTIFY,则由 i2c-core-base.c 的 host_notify_domain + 总线驱动 i2c_handle_smbus_host_notify(Master 侧收到通知)处理,不依赖 本文件从机段。
5. DMI SPD 自动实例化(CONFIG_DMI)
5.1 目的
在 PC/服务器 上根据 DMI 内存表 自动在 I2C 总线上创建 SPD 或 ee1004 client(DDR2/3/4),便于 ee1004/spd5118 等驱动读取内存条信息。
5.2 i2c_register_spd(struct i2c_adapter *adap)
限制(源码):
- 仅 1~4 个内存槽;
- 所有已插槽 内存类型一致;
- 支持类型:DDR2/DDR3/LPDDR2/3 →
"spd";DDR4/LPDDR4 → "ee1004";
- 在
0x50 + slot_index 上 i2c_new_scanned_device 探测。
5.3 RK3588
- 无 DMI(嵌入式 ARM)→ 该函数通常为 空操作 或不被调用;
- 与 RK3588 产品 无关。
6. 模块与驱动注册
1 2 3 4 5 6 7
| static struct i2c_driver smbalert_driver = { .driver.name = "smbus_alert", .probe = smbalert_probe, .remove = smbalert_remove, .id_table = { { "smbus_alert", 0 }, {} }, }; module_i2c_driver(smbalert_driver);
|
- ARA client 由
i2c_new_smbus_alert_device() 创建后,由 smbalert 驱动 probe 绑定;
- 模块描述:
"SMBus protocol extensions support"。
7. 配置与 RK3588 构建
7.1 Kconfig
1 2 3
| config I2C_SMBUS tristate "SMBus-specific protocols" # 当 I2C_HELPER_AUTO=y 时该选项在 menuconfig 中隐藏
|
- 部分 x86 SMBus 主机驱动
select I2C_SMBUS(如 i2c-i801);
I2C_RK3X 不 select I2C_SMBUS。
7.2 Rockchip defconfig 典型情况
| 配置 |
常见值 |
CONFIG_I2C_RK3X |
y |
CONFIG_I2C_SMBUS |
未显式配置 且 rk3x 不 select → i2c-smbus.ko 可能未构建 |
i2c_setup_smbus_alert() |
无 CONFIG_I2C_SMBUS 时为 inline 空函数,直接 return 0 |
因此 RK3588 产品 通常没有 SMBALERT# 基础设施,除非手动开启 CONFIG_I2C_SMBUS=y/m。
7.3 与 PMIC/传感器的关系
板载 RK806、传感器 等使用 i2c_smbus_read_byte_data() 等 API,这些在 i2c-core-smbus.c 中,不依赖 i2c-smbus.ko。
8. 端到端时序:Alert 示例
1 2 3 4 5 6
| 1. 温度芯片拉低 SMBALERT# 2. 控制器 IRQ → smbus_alert() 或 i2c_handle_smbus_alert(ara) 3. work: i2c_smbus_read_byte(ara@0x0c) → 0x4d (地址 0x26, flag=1) 4. smbus_do_alert → lm90_driver.alert(client@0x26, SMBUS_ALERT, 1) 5. 驱动读取状态寄存器、可能清 alert 6. 重复 read_byte(ara) 直至 < 0
|
9. 导出符号一览
| 符号 |
条件 |
说明 |
i2c_handle_smbus_alert |
模块 |
调度 Alert 处理 work |
i2c_new_slave_host_notify_device |
CONFIG_I2C_SLAVE |
创建 Host Notify 从机 |
i2c_free_slave_host_notify_device |
CONFIG_I2C_SLAVE |
释放 |
i2c_register_spd |
CONFIG_DMI |
DMI SPD 探测 |
(i2c_new_smbus_alert_device 在 i2c-core-smbus.c 中导出。)
10. 调试建议
1 2 3 4 5 6 7 8 9 10 11 12
| zcat /proc/config.gz 2>/dev/null | grep I2C_SMBUS ls /sys/bus/i2c/drivers/smbus_alert/
ls /sys/bus/i2c/devices/*-000c/
grep -r '\.alert\s*=' drivers/hwmon/ drivers/power/
dmesg | grep -i 'SMBALERT\|smbus_alert'
|
11. 源码结构速查(约 461 行)
1 2 3 4 5 6 7 8
| L20-92 smbus_do_alert / smbus_do_alert_force L98-152 smbus_alert (IRQ/thread) L154-162 smbalert_work L165-211 smbalert_probe / remove L240-246 i2c_handle_smbus_alert L248 module_i2c_driver L250-355 Host Notify slave [CONFIG_I2C_SLAVE] L365-455 i2c_register_spd [CONFIG_DMI]
|
12. 结论
i2c-smbus.c 提供 SMBus Alert 与 Host Notify(从机侧) 的 Linux 实现,以及 x86 DMI SPD 便利功能:
- Alert: ARA
0x0c + IRQ/work + i2c_driver.alert() 回调链;
- Host Notify: 可选从机监听
0x08,转交 i2c_handle_smbus_host_notify()(core);
- SPD: 仅 DMI 平台有意义。
RK3588: 日常 I2C 访问依赖 i2c-core-smbus.c;i2c-smbus.ko 通常未启用,板级也少见 SMBALERT#。分析嵌入式 I2C 问题时,应区分 通用 SMBus 读写(core)与 Alert/Notify 扩展(本模块)。
13. 推荐阅读顺序
i2c-smbus.c — smbus_alert、smbalert_probe
rk3588-kernel-6.1-i2c-core-smbus.c功能详细分析_zh.md — i2c_smbus_xfer、i2c_new_smbus_alert_device
rk3588-kernel-6.1-i2c-core-base.c功能详细分析_zh.md — host_notify_domain、i2c_setup_smbus_alert 调用点
- SMBus 规范 Alert / Host Notify 章节
14. 相关文档
文档版本:基于 rk3588/kernel-6.1 源码树 drivers/i2c/i2c-smbus.c 分析。
正在加载留言…