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

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 协议 APIi2c_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_driversmbus_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; /* ARA @ 0x0c */
};

struct alert_data {
unsigned short addr; /* 报警设备 7 位地址 << 1 解码后 */
enum i2c_alert_protocol type; /* I2C_PROTOCOL_SMBUS_ALERT */
unsigned int data; /* status 低 1 位 */
};

3.4 smbus_alert() 中断/工作处理

1
2
3
4
status = i2c_smbus_read_byte(ara);   /* 从 0x0c 读一字节 */
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.clm90.cstts751.c 等。

3.6 smbalert_probe() — ARA 设备

  • client 名称: smbus_alert,地址 0x0c(由 i2c_new_smbus_alert_device 创建);
  • IRQ 来源:
    • platform_datai2c_smbus_alert_setup.irq;或
    • 父设备 fwnode:interrupt-names = "smbus_alert"
  • devm_request_threaded_irqthread_fn = smbus_alertIRQF_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); /* 最终进入 smbus_alert(0, alert) */
}

导出符号,供 不能在 IRQ 里睡眠 的主机驱动使用(如 PCI SMBus 控制器)。

3.8 与 i2c-core-smbus.c 的衔接

1
2
3
4
5
6
7
8
9
10
11
12
// i2c-core-smbus.c
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)
{
// 父设备 interrupt-names 含 "smbus_alert" 时创建 ARA
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 虚拟 IRQi2c-core-base.chost_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);
// i2c_slave_unregister + kfree(platform_data) + i2c_unregister_device

4.5 RK3588

  • CONFIG_I2C_SLAVE + i2c-rk3x 实现 reg_slave(当前 );
  • Host Notify 从机路径在 RK3588 上 不可用
  • 若仅作 Master 的 client 使用 I2C_CLIENT_HOST_NOTIFY,则由 i2c-core-base.chost_notify_domain + 总线驱动 i2c_handle_smbus_host_notify(Master 侧收到通知)处理,不依赖 本文件从机段。

5. DMI SPD 自动实例化(CONFIG_DMI

5.1 目的

PC/服务器 上根据 DMI 内存表 自动在 I2C 总线上创建 SPDee1004 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_indexi2c_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 clienti2c_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_devicei2c-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/

# ARA 设备是否存在
ls /sys/bus/i2c/devices/*-000c/ # 地址 0x0c

# 某驱动是否实现 alert
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 AlertHost Notify(从机侧) 的 Linux 实现,以及 x86 DMI SPD 便利功能:

  1. Alert: ARA 0x0c + IRQ/work + i2c_driver.alert() 回调链;
  2. Host Notify: 可选从机监听 0x08,转交 i2c_handle_smbus_host_notify()(core);
  3. SPD: 仅 DMI 平台有意义。

RK3588: 日常 I2C 访问依赖 i2c-core-smbus.ci2c-smbus.ko 通常未启用,板级也少见 SMBALERT#。分析嵌入式 I2C 问题时,应区分 通用 SMBus 读写(core)与 Alert/Notify 扩展(本模块)。


13. 推荐阅读顺序

  1. i2c-smbus.csmbus_alertsmbalert_probe
  2. rk3588-kernel-6.1-i2c-core-smbus.c功能详细分析_zh.mdi2c_smbus_xferi2c_new_smbus_alert_device
  3. rk3588-kernel-6.1-i2c-core-base.c功能详细分析_zh.mdhost_notify_domaini2c_setup_smbus_alert 调用点
  4. SMBus 规范 Alert / Host Notify 章节

14. 相关文档


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

文章互动

阅读 --

留言

0 条留言

正在加载留言…