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

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
驱动 (rk808, 传感器, hwmon...)
│ i2c_smbus_read_byte_data() 等

i2c-core-smbus.c ← 本文
│ i2c_smbus_xfer()
│ ├─ algo->smbus_xfer (若有)
│ └─ i2c_smbus_xfer_emulated()
│ └─ __i2c_transfer()

i2c-core-base.c
│ i2c_transfer() → rk3x_i2c_xfer

i2c-rk3x.c (硬件)

可选并行路径:
CONFIG_I2C_SMBUS → i2c-smbus.ko (Alert IRQ、Host Notify 从机等)

文件头注释:

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_datainclude/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
2
3
res = __i2c_lock_bus_helper(adapter);
res = __i2c_smbus_xfer(...);
i2c_unlock_bus(adapter, I2C_LOCK_SEGMENT);

对调用者提供 已加总线锁 的 SMBus 传输(与 i2c_transfer 锁语义一致)。

7.2 __i2c_smbus_xfer() 决策流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
__i2c_check_suspended()
trace_smbus_write / trace_smbus_read
flags &= I2C_M_TEN | I2C_CLIENT_PEC | I2C_CLIENT_SCCB

xfer_func = adapter->algo->smbus_xfer
[原子上下文: 优先 smbus_xfer_atomic,否则可能 NULL → 模拟]

若 xfer_func 存在:
重试循环 (EAGAIN + timeout)
若 res != -EOPNOTSUPP 或 无 master_xfer → 返回

否则:
i2c_smbus_xfer_emulated()

trace_smbus_reply / trace_smbus_result

RK3588: rk3xsmbus_xfer始终进入 i2c_smbus_xfer_emulated

7.3 Tracepoint

trace/events/smbus.hsmbus_writesmbus_readsmbus_replysmbus_result


8. i2c_smbus_read_i2c_block_data_or_emulated()

当 adapter 不支持 I2C_FUNC_SMBUS_READ_I2C_BLOCK 时:

  1. 若支持 I2C block,直接 i2c_smbus_read_i2c_block_data
  2. 否则用 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.cI2C_CLIENT_HOST_NOTIFYi2c_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
2
rk3588/kernel-6.1/drivers/i2c/i2c-smbus.c
250:#if IS_ENABLED(CONFIG_I2C_SLAVE)

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
2
// i2c-rk3x.c
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_PROTOCOL_MANGLING;
标志 含义
I2C_FUNC_I2C 支持 i2c_transfer
I2C_FUNC_SMBUS_EMUL SMBus 可通过 I2C 消息模拟(本文件路径)
I2C_FUNC_SMBUS_READ_BLOCK_DATA 不宣称原生 SMBus block

11.2 典型驱动调用链(PMIC)

1
2
3
4
5
6
rk808_probe()
→ i2c_smbus_read_byte_data(client, RK808_CHIPID1)
→ i2c_smbus_xfer()
→ i2c_smbus_xfer_emulated() // 2 msg: 写 reg + 读 1 字节
→ __i2c_transfer()
→ rk3x_i2c_xfer()

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_devicei2c_setup_smbus_alert(非 export,core 内用)


14. 调试

1
2
3
4
5
6
7
8
9
# 查看某总线是否注册 smbus_alert
ls /sys/bus/i2c/devices/i2c-*/ | grep smbus

# trace(需 CONFIG_EVENT_TRACING)
echo 1 > /sys/kernel/debug/tracing/events/smbus/enable
cat /sys/kernel/debug/tracing/trace

# 功能位(debugfs 或 驱动打印)
# adapter 应含 SMBUS_EMUL
错误码 常见原因
-EBADMSG PEC 校验失败
-EPROTO Block 长度非法
-EOPNOTSUPP 模拟路径不支持的 protocol
-EIO __i2c_transfer 返回 msg 数不足

15. 源码结构速查(约 723 行)

1
2
3
4
5
6
7
8
L28-93    CRC8 / PEC
L95-300 便捷 SMBus API
L302-316 DMA 缓冲辅助
L322-520 i2c_smbus_xfer_emulated
L535-551 i2c_smbus_xfer
L553-619 __i2c_smbus_xfer
L640-676 read_i2c_block_data_or_emulated
L693-722 smbus alert 设备创建 / i2c_setup_smbus_alert

16. 结论

i2c-core-smbus.c 是 Linux I2C 子系统中 SMBus 与 I2C 之间的桥梁

  1. 为驱动提供 标准 SMBus 读写 API
  2. 通过 i2c_smbus_xfer_emulated 使仅实现 master_xfer 的控制器(含 RK3588 rk3x)也能运行 SMBus 客户端;
  3. 支持 PECtrace总线锁重试
  4. 在 adapter 注册时可选挂载 SMBus Alert 客户端(配合 i2c-smbus.ko)。

RK3588 平台上 PMIC、传感器等驱动的寄存器访问大量依赖本文件;理解 i2c_smbus_xfer → 模拟 → rk3x_i2c_xfer 是分析 I2C 寄存器读写问题的关键路径之一。


17. 推荐阅读顺序

  1. i2c-core-smbus.c__i2c_smbus_xferi2c_smbus_xfer_emulated
  2. include/linux/i2c.hI2C_SMBUS_*i2c_smbus_data
  3. busses/i2c-rk3x.cfunctionalityrk3x_i2c_xfer
  4. drivers/mfd/rk808.c — SMBus 读芯片 ID 示例
  5. i2c-smbus.c — Alert 协议(若板级有 smbus_alert 中断)
  6. Documentation/i2c/smbus-protocol.rst(若存在)或 SMBus 规范

18. 相关文档


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

文章互动

阅读 --

留言

0 条留言

正在加载留言…