RK3588 kernel-6.1/drivers/i2c/i2c-core-smbus.c 功能详细分析
1. 文档范围与说明
路径说明: 用户表述为「i2c-core-smbus.c 目录」,实际为 单个源文件:
rk3588/kernel-6.1/drivers/i2c/i2c-core-smbus.c(约 723 行)
编译关系: 作为 i2c-core 复合模块的固定组成部分,始终参与链接(CONFIG_I2C=y 时):
1 | i2c-core-objs := i2c-core-base.o i2c-core-smbus.o |
与 i2c-smbus.c(独立模块 CONFIG_I2C_SMBUS)分工不同:本文件实现 SMBus 协议栈与 I2C 模拟;i2c-smbus.ko 实现 SMBus Alert / Host Notify 等扩展协议的处理逻辑。
文档保存路径: linuxDoc/drivers/i2c/
定位: Linux I2C 核心的 SMBus 层:PEC/CRC、各类 SMBus 读写 API、统一入口 i2c_smbus_xfer、在 adapter 无原生 SMBus 时通过 i2c_transfer 模拟,以及 adapter 注册时的 SMBus Alert 设备创建入口。
与 RK3588 的关系: i2c-rk3x.c 仅声明 I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL,无 algo->smbus_xfer,故 RK3588 上几乎所有 SMBus 访问均走 i2c_smbus_xfer_emulated() → __i2c_transfer() → rk3x_i2c_xfer()。PMIC(如 rk808)、部分传感器驱动大量使用 i2c_smbus_read_byte_data() 等 API。
2. 功能总览
| 模块 | 行号(约) | 主要符号 | 作用 |
|---|---|---|---|
| PEC/CRC8 | 28–93 | i2c_smbus_pec, i2c_smbus_add_pec |
SMBus 分组错误校验 |
| 便捷读写 API | 95–300 | i2c_smbus_read/write_* |
Byte/Word/Block/I2C-Block 协议封装 |
| I2C 模拟 | 322–520 | i2c_smbus_xfer_emulated |
将 SMBus 转为 1~2 条 i2c_msg |
| 统一传输 | 535–619 | i2c_smbus_xfer, __i2c_smbus_xfer |
锁总线、选原生/模拟、重试、trace |
| 块读回退 | 640–676 | i2c_smbus_read_i2c_block_data_or_emulated |
无 I2C block 时用 byte/word 拼读 |
| SMBus Alert 入口 | 693–722 | i2c_new_smbus_alert_device, i2c_setup_smbus_alert |
创建 ARA(0x0c) client(需 CONFIG_I2C_SMBUS) |
3. 在 I2C 栈中的位置
1 | 驱动 (rk808, 传感器, hwmon...) |
文件头注释:
SMBus specific extensions (e.g. smbalert) are handled in a separate i2c-smbus module.
4. SMBus 协议类型与便捷 API
所有便捷函数最终调用 i2c_smbus_xfer(adapter, addr, flags, read_write, command, protocol, data)。
protocol 常量 |
便捷 API | 协议说明 |
|---|---|---|
I2C_SMBUS_QUICK |
(内部/探测) | Quick Command,无数据字节 |
I2C_SMBUS_BYTE |
read_byte / write_byte |
Receive/Send Byte |
I2C_SMBUS_BYTE_DATA |
read_byte_data / write_byte_data |
命令字节 + 1 数据字节 |
I2C_SMBUS_WORD_DATA |
read_word_data / write_word_data |
命令 + 16 位字(小端) |
I2C_SMBUS_BLOCK_DATA |
read_block_data / write_block_data |
首字节为长度,最多 32 字节 |
I2C_SMBUS_I2C_BLOCK_DATA |
read/write_i2c_block_data |
I2C 风格块传输(长度由主机指定) |
I2C_SMBUS_PROC_CALL |
(经 xfer) | 写 word 再读 word |
I2C_SMBUS_BLOCK_PROC_CALL |
(经 xfer) | 写 block 再读 block |
数据容器: union i2c_smbus_data(include/linux/i2c.h),block 数据存放在 data.block[],block[0] 为长度。
5. PEC(Packet Error Code)
5.1 i2c_smbus_pec()
对缓冲区做 增量 CRC-8(多项式 0x1070<<3),用于 SMBus 2.0 PEC。
5.2 写路径 i2c_smbus_add_pec()
在 msg->buf 末尾追加一字节 PEC(msg->len++)。
5.3 读路径 i2c_smbus_check_pec()
读完成后校验最后一字节;失败返回 -EBADMSG,并缩短 msg->len 隐藏 PEC 字节。
5.4 启用条件
client->flags & I2C_CLIENT_PEC 且在模拟路径中 wants_pec 为真(Quick 与 I2C_BLOCK_DATA 的 I2C-block 类型除外)。
6. i2c_smbus_xfer_emulated() — I2C 模拟核心
当 adapter 没有 可用的 smbus_xfer(或返回 -EOPNOTSUPP 且无 master_xfer)时,由 __i2c_smbus_xfer 调用本函数。
6.1 基本思路
将 SMBus 操作拆成 1 或 2 条 struct i2c_msg:
| 操作类型 | 典型 msg 布局 |
|---|---|
| Quick | 1 条,无数据或仅 R/W 位 |
| Send/Receive Byte | 1 条读或写 |
| Byte/Word Data | 写:1 条(cmd[+data]);读:写 cmd + 读 data |
| Block Data 读 | 写 cmd + 读(I2C_M_RECV_LEN,首字节为长度) |
| I2C Block | 写 cmd+data 或 写 cmd + 读固定长度 |
最后调用 __i2c_transfer(adapter, msg, nmsgs)(不再重复加 bus 锁,由上层 i2c_smbus_xfer 已加锁)。
6.2 DMA 安全缓冲
i2c_smbus_try_get_dmabuf():对 block 类传输分配 I2C_M_DMA_SAFE 缓冲区,避免栈上 msgbuf 无法 DMA;结束后 kfree。
6.3 I2C_M_RECV_LEN
SMBus Block Read 模拟时在读消息上设置 I2C_M_RECV_LEN,由 主机驱动 根据从机返回的第一个字节扩展读长度。若驱动不支持(RK3x 未专门处理该标志),Block Read 模拟可能失败;RK3588 上常见驱动多用 Byte/Word Data,一般不受影响。
7. i2c_smbus_xfer() 与 __i2c_smbus_xfer()
7.1 i2c_smbus_xfer()
1 | res = __i2c_lock_bus_helper(adapter); |
对调用者提供 已加总线锁 的 SMBus 传输(与 i2c_transfer 锁语义一致)。
7.2 __i2c_smbus_xfer() 决策流程
1 | __i2c_check_suspended() |
RK3588: rk3x 无 smbus_xfer → 始终进入 i2c_smbus_xfer_emulated。
7.3 Tracepoint
trace/events/smbus.h:smbus_write、smbus_read、smbus_reply、smbus_result。
8. i2c_smbus_read_i2c_block_data_or_emulated()
当 adapter 不支持 I2C_FUNC_SMBUS_READ_I2C_BLOCK 时:
- 若支持 I2C block,直接
i2c_smbus_read_i2c_block_data; - 否则用 word data(每次 2 字节)和 byte data(逐字节)从
command + i线性地址读取。
前提: 从设备寄存器布局必须 线性映射(文档已警告)。用于兼容老 adapter/简化硬件。
9. SMBus Alert 相关(与 i2c-smbus.ko 分工)
9.1 i2c_new_smbus_alert_device()
创建 Alert Response Address (ARA) 设备:
1 | I2C_BOARD_INFO("smbus_alert", 0x0c) |
返回 i2c_client *,供 i2c_handle_smbus_alert() 等使用(实现在 i2c-smbus.c 或 core 其它处)。
9.2 i2c_setup_smbus_alert()(#if IS_ENABLED(CONFIG_I2C_SMBUS))
在 i2c_register_adapter() 中调用(i2c-core-base.c):
- 检查 adapter 父设备是否有
interrupt-names = "smbus_alert"; - 若有则
i2c_new_smbus_alert_device(adapter, NULL)。
未配置 CONFIG_I2C_SMBUS 时,i2c-core.h 提供 空 inline,直接返回 0。
9.3 Host Notify
i2c-core-base.c 中 I2C_CLIENT_HOST_NOTIFY 与 i2c_smbus_host_notify_to_irq();从机侧 Host Notify 在 CONFIG_I2C_SLAVE 时由 i2c-core-smbus.c 后部或 i2c-smbus.c 协作(本树 i2c-core-smbus.c 在 705 行后仅保留 alert setup,Host Notify 从机代码在 #if CONFIG_I2C_SLAVE 块,需对照当前文件——我读到的文件在 705 行结束是 #endif,Host Notify 可能在之前被移走)
让我再确认 i2c-core-smbus.c 是否有 host notify - from first read, lines 250-350 in i2c-smbus.c had host notify with CONFIG_I2C_SLAVE. The grep showed i2c-core-smbus.c had host notify at 250+ but my read of offset 575+ ended at 724 with only smbus alert. The initial grep was:
1 | rk3588/kernel-6.1/drivers/i2c/i2c-smbus.c |
So Host Notify slave is in i2c-smbus.c not i2c-core-smbus.c. Good.
10. 与 i2c-core-base.c 的集成
| 调用点 | 使用的 SMBus API |
|---|---|
i2c_register_adapter() |
i2c_setup_smbus_alert(adap) |
i2c_device_probe() |
Host Notify → i2c_smbus_host_notify_to_irq(base.c,非 smbus.c 核心) |
i2c_default_probe() |
i2c_smbus_xfer Quick/Byte Read |
i2c_get_device_id() |
SMBus Read Block 设备 ID |
i2c_probe_func_quick_read() |
Quick read |
11. 与 i2c-rk3x / RK3588
11.1 能力位
1 | // i2c-rk3x.c |
| 标志 | 含义 |
|---|---|
I2C_FUNC_I2C |
支持 i2c_transfer |
I2C_FUNC_SMBUS_EMUL |
SMBus 可通过 I2C 消息模拟(本文件路径) |
无 I2C_FUNC_SMBUS_READ_BLOCK_DATA 等 |
不宣称原生 SMBus block |
11.2 典型驱动调用链(PMIC)
1 | rk808_probe() |
11.3 使用建议(RK3588)
- Byte/Word Data:完全支持,无需担心。
- Block Data 读:依赖
I2C_M_RECV_LEN;RK3x 未明确实现时,优先在驱动中用 I2C block 或 多次 byte read;或使用i2c_smbus_read_i2c_block_data_or_emulated()。 - PEC:若外设要求 PEC,设置
I2C_CLIENT_PEC(DT 或 board info)。
12. i2c-core-smbus.c vs i2c-smbus.ko
| 项目 | i2c-core-smbus.c |
drivers/i2c/i2c-smbus.c |
|---|---|---|
| 链接 | 始终进 i2c-core.ko |
可选 CONFIG_I2C_SMBUS 模块 |
| 内容 | 协议、模拟、PEC、便捷 API | SMBus Alert IRQ/workqueue、i2c_handle_smbus_alert |
| RK3588 | 必用(凡用 SMBus API) | 仅当需要 Alert 线且配置 Kconfig |
13. 导出符号分类
PEC: i2c_smbus_pec
便捷 API: i2c_smbus_read_byte, write_byte, read_byte_data, write_byte_data, read_word_data, write_word_data, read_block_data, write_block_data, read_i2c_block_data, write_i2c_block_data, read_i2c_block_data_or_emulated
核心: i2c_smbus_xfer, __i2c_smbus_xfer
Alert: i2c_new_smbus_alert_device;i2c_setup_smbus_alert(非 export,core 内用)
14. 调试
1 | # 查看某总线是否注册 smbus_alert |
| 错误码 | 常见原因 |
|---|---|
-EBADMSG |
PEC 校验失败 |
-EPROTO |
Block 长度非法 |
-EOPNOTSUPP |
模拟路径不支持的 protocol |
-EIO |
__i2c_transfer 返回 msg 数不足 |
15. 源码结构速查(约 723 行)
1 | L28-93 CRC8 / PEC |
16. 结论
i2c-core-smbus.c 是 Linux I2C 子系统中 SMBus 与 I2C 之间的桥梁:
- 为驱动提供 标准 SMBus 读写 API;
- 通过
i2c_smbus_xfer_emulated使仅实现master_xfer的控制器(含 RK3588 rk3x)也能运行 SMBus 客户端; - 支持 PEC、trace、总线锁 与 重试;
- 在 adapter 注册时可选挂载 SMBus Alert 客户端(配合
i2c-smbus.ko)。
RK3588 平台上 PMIC、传感器等驱动的寄存器访问大量依赖本文件;理解 i2c_smbus_xfer → 模拟 → rk3x_i2c_xfer 是分析 I2C 寄存器读写问题的关键路径之一。
17. 推荐阅读顺序
i2c-core-smbus.c—__i2c_smbus_xfer、i2c_smbus_xfer_emulatedinclude/linux/i2c.h—I2C_SMBUS_*、i2c_smbus_databusses/i2c-rk3x.c—functionality、rk3x_i2c_xferdrivers/mfd/rk808.c— SMBus 读芯片 ID 示例i2c-smbus.c— Alert 协议(若板级有 smbus_alert 中断)Documentation/i2c/smbus-protocol.rst(若存在)或 SMBus 规范
18. 相关文档
rk3588-kernel-6.1-i2c-core-base.c功能详细分析_zh.mdrk3588-kernel-6.1-i2c-core-of.c功能详细分析_zh.mdrk3588-kernel-6.1-drivers-i2c-busses目录功能详细分析_zh.mdrk3588-kernel-6.1-drivers-i2c目录功能详细分析_zh.md
文档版本:基于 rk3588/kernel-6.1 源码树 drivers/i2c/i2c-core-smbus.c 分析。
正在加载留言…