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

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
2
3
CONFIG_I2C_SLAVE_EEPROM  →  依赖 CONFIG_I2C_SLAVE
CONFIG_I2C_SLAVE → 链接 i2c-core-slave.o
主机驱动 → 须实现 algo->reg_slave / unreg_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
2
3
4
5
6
7
8
9
10
11
12
外部 I2C Master (MCU/另一 SoC)          Linux (本驱动)
│ │
│ START + 从机地址 + 写/读 │
▼ │
┌──────────────┐ i2c_slave_event() ┌────▼─────────────┐
│ 物理 I2C 总线 │ ──────────────────────►│ slave_cb │
└──────────────┘ (主机驱动调用) │ 更新 buffer[] │
▲ └────┬─────────────┘
│ │ 同一 buffer
│ ▼
│ sysfs slave-eeprom
│ (应用/脚本读写)

内核文档:Documentation/i2c/slave-eeprom-backend.rstslave-interface.rst

要点: 本文件 不操作硬件寄存器;仅实现 EEPROM 协议状态机RAM 缓冲区;硬件从机能力由 I2C 控制器驱动 + i2c-core-slave.c 提供。


4. 核心数据结构

4.1 struct eeprom_data

1
2
3
4
5
6
7
8
9
10
struct eeprom_data {
struct bin_attribute bin; /* sysfs: slave-eeprom */
spinlock_t buffer_lock; /* 保护 buffer 与 I2C 并发访问 */
u16 buffer_idx; /* 当前 EEPROM 地址指针 */
u16 address_mask; /* size - 1,地址回绕 */
u8 num_address_bytes; /* 1 或 2(24c02 vs 24c32+) */
u8 idx_write_cnt; /* 写事务中已收地址字节数 */
bool read_only; /* 只读型号 */
u8 buffer[]; /* 柔性数组,模拟 EEPROM 内容 */
};

4.2 驱动私有数据编码(id->driver_data

1
2
3
4
#define I2C_SLAVE_BYTELEN      GENMASK(15, 0)   /* 容量 - 1 编码在低位 */
#define I2C_SLAVE_FLAG_ADDR16 BIT(16) /* 16 位地址 */
#define I2C_SLAVE_FLAG_RO BIT(17) /* 只读 */
#define I2C_SLAVE_DEVICE_MAGIC(_len, _flags) ((_flags) | ((_len) - 1))

probe 中:

  • size = FIELD_GET(I2C_SLAVE_BYTELEN, id->driver_data) + 1
  • num_address_bytes = 2ADDR16,否则 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_readmemcpybuffer[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
2
3
4
5
6
7
8
1. devm_kzalloc(sizeof(eeprom_data) + size)
2. 根据 id_table 设置 num_address_bytes / address_mask / read_only
3. i2c_slave_init_eeprom_data()
- 若有 DT/ACPI 属性 firmware-name → request_firmware_into_buf()
- 否则 memset(buffer, 0xff, size) // 空 EEPROM 典型为全 1
4. sysfs_create_bin_file(slave-eeprom)
5. i2c_slave_register(client, i2c_slave_eeprom_slave_cb)
- 失败则删除 sysfs

8.2 i2c_slave_eeprom_remove()

1
2
i2c_slave_unregister(client)
sysfs_remove_bin_file()

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
2
3
4
5
# 24c02,7 位地址 0x64,挂在 i2c-1
echo slave-24c02 0x1064 > /sys/bus/i2c/devices/i2c-1/new_device

# 24c512 只读,地址 0x42
echo slave-24c512ro 0x1042 > /sys/bus/i2c/devices/i2c-1/new_device

9.2 启动预载内容(DT/ACPI)

设备属性 firmware-name 指向固件文件,probe 时 request_firmware_into_buf() 载入 buffer

示例(概念性 DT,实际需配合支持从机的主机与 new_device 或专用绑定):

1
2
3
4
eeprom_slave: slave-eeprom@64 {
/* 通常仍通过 new_device 按 id 名实例化 */
firmware-name = "eeprom_init.bin";
};

10. 与 i2c-core-slave.c 的关系

步骤 模块
i2c_slave_register() i2c-core-slave.cadap->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_SLAVECONFIG_I2C_SLAVE_EEPROMrockchip_linux_defconfig通常未配置 → 驱动 默认不编译
  • i2c-rk3x 仅 Master 模式,无法实现 本驱动所需的从机硬件路径。

11.2 若要在 RK3588 上实验

需同时满足:

  1. 确认 I2C IP 是否支持 Slave(查阅 TRM);
  2. i2c-rk3x.c(或另一控制器)实现 reg_slave / unreg_slavei2c_slave_event 调用链;
  3. CONFIG_I2C_SLAVE=yCONFIG_I2C_SLAVE_EEPROM=m
  4. 通过 new_device 在目标总线上创建 slave-24c02 等;
  5. 外部设备或另一主控作为 I2C Master 访问。

量产 RK3588 产品 一般 不使用 本驱动;常见为 SoC Master 访问真实 EEPROM/PMIC/传感器。

11.3 与真实 EEPROM 驱动对比

维度 i2c-slave-eeprom(本驱动) 普通 at24/eeprom
SoC 角色 I2C Slave(被访问) I2C Master(访问外设)
存储 内核 RAM 模拟 物理 EEPROM 芯片
典型用途 测试、协处理器仿真 板级配置/数据存储

12. 调试与验证

1
2
3
4
5
6
7
8
9
10
# 确认模块
lsmod | grep i2c_slave
zcat /proc/config.gz | grep I2C_SLAVE # 若可用

# 创建模拟 EEPROM 后
ls /sys/bus/i2c/devices/*/
hexdump -C /sys/bus/i2c/devices/1-0064/slave-eeprom | head

# 删除实例
echo 0x1064 > /sys/bus/i2c/devices/i2c-1/delete_device

配合外部 Master 或另一开发板用 i2cget/i2cset 访问同一从地址,对比 sysfs 内容是否一致。


13. 源码结构速查(约 218 行)

1
2
3
4
5
6
7
L29-38   struct eeprom_data
L45-91 i2c_slave_eeprom_slave_cb()
L93-121 sysfs bin read/write
L123-141 i2c_slave_init_eeprom_data()
L143-182 probe
L184-190 remove
L192-213 id_table + i2c_driver + module_i2c_driver

14. 结论

i2c-slave-eeprom.cI2C 从机子系统的一个后端示例/实用模块

  1. 模拟 24C02~24C512 EEPROM 的地址与读写时序;
  2. 通过 i2c_slave_register 挂接到支持从机模式的 I2C 控制器;
  3. 通过 slave-eeprom sysfs 向用户空间暴露同一块存储;
  4. 支持 只读型号firmware-name 预载
  5. 依赖 CONFIG_I2C_SLAVE 与主机 reg_slave,与 RK3588 默认 rk3x Master-only 栈正交

用于理解 Linux 作 I2C 从设备 时的软件模型;RK3588 常规 bring-up 可跳过,仅在从机/双主测试场景需要关注。


15. 推荐阅读顺序

  1. i2c-slave-eeprom.cslave_cbprobe
  2. Documentation/i2c/slave-interface.rst — 从机事件语义
  3. Documentation/i2c/slave-eeprom-backend.rst — 用户操作说明
  4. i2c-core-slave.ci2c_slave_register
  5. i2c-slave-testunit.c — 另一从机后端(测试用)

16. 相关文档


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

文章互动

阅读 --

留言

0 条留言

正在加载留言…