RK3588 kernel-6.1/drivers/i2c/i2c-slave-eeprom.c 功能详细分析
1. 文档范围与说明
路径说明: 用户表述为「i2c-slave-eeprom.c 目录」,实际为 单个源文件:
rk3588/kernel-6.1/drivers/i2c/i2c-slave-eeprom.c(约 218 行)
编译关系:
1 | obj-$(CONFIG_I2C_SLAVE_EEPROM) += i2c-slave-eeprom.o |
依赖链:
1 | CONFIG_I2C_SLAVE_EEPROM → 依赖 CONFIG_I2C_SLAVE |
文档保存路径: linuxDoc/drivers/i2c/
定位: I2C 从机模式后端驱动,在软件中模拟 I2C EEPROM(24Cxx 系列) 行为:外部 I2C 主机通过总线读写“EEPROM”,Linux 侧通过 sysfs 二进制文件 读写同一块内存,实现 双端口存储(总线 + 用户空间)。
与 RK3588 的关系:
| 项目 | RK3588 典型情况 |
|---|---|
CONFIG_I2C_SLAVE / I2C_SLAVE_EEPROM |
rockchip_linux_defconfig 通常未启用 |
i2c-rk3x.c |
无 reg_slave,即使编译也无法 i2c_slave_register() |
| 产品用途 | SoC 作 I2C Master;本驱动用于 开发/测试/特殊从机场景,非 RK3588 量产主路径 |
2. 功能总览
| 功能 | 实现位置 | 说明 |
|---|---|---|
| EEPROM 协议模拟 | i2c_slave_eeprom_slave_cb() |
响应 I2C 从机事件,维护地址指针与数据区 |
| 用户空间访问 | sysfs slave-eeprom |
bin_attribute 读写模拟存储 |
| 启动预载 | firmware-name 属性 |
从固件文件填充 buffer |
| 多种容量/型号 | i2c_slave_eeprom_id[] |
24c02 / 24c32 / 24c64 / 24c512 及 ro 变体 |
| 从机注册 | i2c_slave_register() |
依赖 i2c-core-slave.c + 主机 reg_slave |
3. 架构与数据流
1 | 外部 I2C Master (MCU/另一 SoC) Linux (本驱动) |
内核文档:Documentation/i2c/slave-eeprom-backend.rst、slave-interface.rst。
要点: 本文件 不操作硬件寄存器;仅实现 EEPROM 协议状态机 与 RAM 缓冲区;硬件从机能力由 I2C 控制器驱动 + i2c-core-slave.c 提供。
4. 核心数据结构
4.1 struct eeprom_data
1 | struct eeprom_data { |
4.2 驱动私有数据编码(id->driver_data)
1 |
probe 中:
size = FIELD_GET(I2C_SLAVE_BYTELEN, id->driver_data) + 1;num_address_bytes = 2若ADDR16,否则1;address_mask = size - 1。
5. 支持的模拟型号
i2c_device_id.name |
容量(字节) | 地址宽度 | 只读 |
|---|---|---|---|
slave-24c02 |
256 (2048/8) | 8 位 | 否 |
slave-24c02ro |
256 | 8 位 | 是 |
slave-24c32 |
4096 | 16 位 | 否 |
slave-24c32ro |
4096 | 16 位 | 是 |
slave-24c64 |
8192 | 16 位 | 否 |
slave-24c64ro |
8192 | 16 位 | 是 |
slave-24c512 |
65536 | 16 位 | 否 |
slave-24c512ro |
65536 | 16 位 | 是 |
限制(文件头注释): 多数从机 IP 仅支持 一个从地址,故 不支持 需要多个 I2C 地址的大容量 EEPROM 型号拆分。
6. i2c_slave_eeprom_slave_cb() — 协议状态机
由主机控制器驱动在总线活动时调用 i2c_slave_event(),最终进入本回调。
6.1 写事务(外部 Master 写 EEPROM)
| 事件 | 行为 |
|---|---|
I2C_SLAVE_WRITE_REQUESTED |
idx_write_cnt = 0,准备接收地址 |
I2C_SLAVE_WRITE_RECEIVED |
若 idx_write_cnt < num_address_bytes:将字节拼入 buffer_idx(先低后高移位);否则在 非只读 时写入 buffer[buffer_idx++ & mask] |
I2C_SLAVE_STOP |
idx_write_cnt = 0 |
地址字节顺序: 第一个地址字节在 idx_write_cnt==0 时清零 buffer_idx,随后 buffer_idx = *val | (buffer_idx << 8),与常见 24Cxx 大端地址 行为一致。
FIXME(源码注释): 16 位地址只收到 8 位时,当前仍将 8 位当作有效地址(与 ST M24C64 行为有关,其它芯片待验证)。
6.2 读事务(外部 Master 读 EEPROM)
| 事件 | 行为 |
|---|---|
I2C_SLAVE_READ_REQUESTED |
从 buffer[buffer_idx & mask] 取字节到 *val,不递增 buffer_idx(因该字节可能被 NACK) |
I2C_SLAVE_READ_PROCESSED |
上一字节已发出,buffer_idx++,再取下一字节到 *val |
I2C_SLAVE_WRITE_REQUESTED / STOP |
重置 idx_write_cnt |
遵循 Documentation/i2c/slave-interface.rst 对 READ_REQUESTED / READ_PROCESSED 的语义。
6.3 并发
buffer 访问使用 spin_lock(&buffer_lock)(I2C 回调可能在中断上下文);sysfs 读写使用 spin_lock_irqsave。
7. Sysfs 接口:slave-eeprom
路径:
1 | /sys/bus/i2c/devices/<client>/slave-eeprom |
| 属性 | 说明 |
|---|---|
| 文件名 | slave-eeprom |
| 权限 | S_IRUSR | S_IWUSR(0600,属主可读写) |
| 大小 | bin.size = EEPROM 容量 |
read |
i2c_slave_eeprom_bin_read — memcpy 出 buffer[off..] |
write |
i2c_slave_eeprom_bin_write — 写入 buffer(只读型号仍可通过 sysfs 写,I2C 侧写被忽略) |
注意: 文档说明 Linux 不支持 对 bin sysfs 的 poll;外部 Master 修改内容后,用户空间 不会自动得到通知。
8. Probe / Remove 流程
8.1 i2c_slave_eeprom_probe()
1 | 1. devm_kzalloc(sizeof(eeprom_data) + size) |
8.2 i2c_slave_eeprom_remove()
1 | i2c_slave_unregister(client) |
8.3 模块注册
1 | module_i2c_driver(i2c_slave_eeprom_driver); |
标准 i2c_driver,通过 id_table 字符串 匹配(无 of_match_table),通常由 sysfs new_device 实例化,而非 DT compatible。
9. 实例化方式(用户空间)
9.1 通过 adapter sysfs new_device
地址需加 0x1000 偏移(与 I2C_OWN_SLAVE_ADDRESS / 从机实例化约定一致,见 slave-interface.rst):
1 | # 24c02,7 位地址 0x64,挂在 i2c-1 |
9.2 启动预载内容(DT/ACPI)
设备属性 firmware-name 指向固件文件,probe 时 request_firmware_into_buf() 载入 buffer。
示例(概念性 DT,实际需配合支持从机的主机与 new_device 或专用绑定):
1 | eeprom_slave: slave-eeprom@64 { |
10. 与 i2c-core-slave.c 的关系
| 步骤 | 模块 |
|---|---|
i2c_slave_register() |
i2c-core-slave.c → adap->algo->reg_slave(client) |
| 总线事件 | 主机驱动 ISR → i2c_slave_event() → i2c_slave_eeprom_slave_cb |
i2c_slave_unregister() |
remove 时对称注销 |
若 reg_slave 不存在(rk3x),i2c_slave_register() 返回 -EOPNOTSUPP,本驱动 probe 失败。
详见:rk3588-kernel-6.1-i2c-core-slave.c功能详细分析_zh.md。
11. RK3588 平台说明
11.1 默认内核
CONFIG_I2C_SLAVE、CONFIG_I2C_SLAVE_EEPROM在rockchip_linux_defconfig中 通常未配置 → 驱动 默认不编译。i2c-rk3x仅 Master 模式,无法实现 本驱动所需的从机硬件路径。
11.2 若要在 RK3588 上实验
需同时满足:
- 确认 I2C IP 是否支持 Slave(查阅 TRM);
- 在
i2c-rk3x.c(或另一控制器)实现reg_slave/unreg_slave及i2c_slave_event调用链; CONFIG_I2C_SLAVE=y、CONFIG_I2C_SLAVE_EEPROM=m;- 通过
new_device在目标总线上创建slave-24c02等; - 外部设备或另一主控作为 I2C Master 访问。
量产 RK3588 产品 一般 不使用 本驱动;常见为 SoC Master 访问真实 EEPROM/PMIC/传感器。
11.3 与真实 EEPROM 驱动对比
| 维度 | i2c-slave-eeprom(本驱动) |
普通 at24/eeprom 等 |
|---|---|---|
| SoC 角色 | I2C Slave(被访问) | I2C Master(访问外设) |
| 存储 | 内核 RAM 模拟 | 物理 EEPROM 芯片 |
| 典型用途 | 测试、协处理器仿真 | 板级配置/数据存储 |
12. 调试与验证
1 | # 确认模块 |
配合外部 Master 或另一开发板用 i2cget/i2cset 访问同一从地址,对比 sysfs 内容是否一致。
13. 源码结构速查(约 218 行)
1 | L29-38 struct eeprom_data |
14. 结论
i2c-slave-eeprom.c 是 I2C 从机子系统的一个后端示例/实用模块:
- 模拟 24C02~24C512 EEPROM 的地址与读写时序;
- 通过
i2c_slave_register挂接到支持从机模式的 I2C 控制器; - 通过
slave-eepromsysfs 向用户空间暴露同一块存储; - 支持 只读型号 与
firmware-name预载; - 依赖
CONFIG_I2C_SLAVE与主机reg_slave,与 RK3588 默认 rk3x Master-only 栈正交。
用于理解 Linux 作 I2C 从设备 时的软件模型;RK3588 常规 bring-up 可跳过,仅在从机/双主测试场景需要关注。
15. 推荐阅读顺序
i2c-slave-eeprom.c—slave_cb、probeDocumentation/i2c/slave-interface.rst— 从机事件语义Documentation/i2c/slave-eeprom-backend.rst— 用户操作说明i2c-core-slave.c—i2c_slave_registeri2c-slave-testunit.c— 另一从机后端(测试用)
16. 相关文档
rk3588-kernel-6.1-i2c-core-slave.c功能详细分析_zh.mdrk3588-kernel-6.1-i2c-dev.c功能详细分析_zh.mdrk3588-kernel-6.1-i2c-core-base.c功能详细分析_zh.mdrk3588-kernel-6.1-drivers-i2c-busses目录功能详细分析_zh.md
文档版本:基于 rk3588/kernel-6.1 源码树 drivers/i2c/i2c-slave-eeprom.c 分析。