关键词:i2c_adapter、i2c_client、i2c_driver、i2c-core、platform_driver、设备树匹配、驱动模型
本文目标:通过实际代码一步步讲清楚 I2C 子系统的结构与运行机制,让你不再混淆 platform_driver 与 i2c_driver 的职责。
🧩 一、本文问题导入
很多人在学 Linux 驱动开发时经常会问:
"I2C 子系统到底是 platform_driver 模型,还是总线设备驱动模型?"
这个问题本质在于:
- 你看到的 SoC 上的 I2C 控制器(比如 i2c@30a20000)是通过 platform_driver 注册的。
- 而 I2C 外设(如 EEPROM、PMIC)却通过 i2c_driver 注册。
所以本文只做一件事:
基于真实源码,把整个 I2C 子系统的结构和逻辑梳理清楚。
🧭 二、I2C 子系统架构概览(核心数据结构)
I2C 子系统的本质,是内核为 I2C 总线提供的一个子系统框架,基于 Linux 的总线设备驱动模型,主要涉及这三类角色:
类型 | 对应结构体 | 描述 |
---|---|---|
主控制器 | struct i2c_adapter |
SoC 的 I2C 控制器,通常由平台驱动注册 |
外设设备 | struct i2c_client |
掛载在 I2C 总线上的从设备 |
外设驱动 | struct i2c_driver |
针对某类设备的驱动,如 at24、wm8960 |
🚩控制器(adapter)负责提供访问总线的能力,
🚩驱动(driver)通过匹配
i2c_client
来完成初始化。
🔩 三、从代码看:如何注册一个 I2C 控制器(i2c_adapter)
控制器驱动一般是 platform_driver ,其 probe()
函数中会注册 i2c_adapter
。
🔍 示例:i.MX8MP 的 I2C 控制器驱动 drivers/i2c/busses/i2c-imx.c
c
static int i2c_imx_probe(struct platform_device *pdev)
{
struct i2c_adapter *adapter;
struct imx_i2c_struct *i2c_imx;
// 1. 分配结构体,映射寄存器、时钟等
i2c_imx = devm_kzalloc(...);
i2c_imx->base = devm_ioremap_resource(...);
// 2. 初始化适配器(i2c_adapter)
adapter = &i2c_imx->adapter;
adapter->owner = THIS_MODULE;
adapter->algo = &i2c_imx_algo;
adapter->dev.parent = &pdev->dev;
adapter->nr = pdev->id;
strlcpy(adapter->name, "IMX I2C", sizeof(adapter->name));
// 3. 注册 i2c_adapter
i2c_add_numbered_adapter(adapter);
return 0;
}
✅ 一旦
i2c_adapter
注册成功,I2C 核心就认为系统具备一条 I2C 总线。
🔌 四、从代码看:设备是怎么挂在 I2C 总线上的?
这是由设备树决定的,I2C 从设备(如 EEPROM)一般长这样:
dts
i2c@30a20000 {
status = "okay";
eeprom@50 {
compatible = "atmel,24c02";
reg = <0x50>; // I2C 地址
pagesize = <16>;
};
};
关键机制:
- 内核在识别到
i2c@xxx
是一个已注册的i2c_adapter
后; - 会自动为子节点(eeprom@50)创建一个
i2c_client
; - 调用
of_i2c_register_devices()
完成挂载。
✅ 每个子节点都被封装为
struct i2c_client
,表示总线上挂的一个 I2C 从设备。
🧠 五、设备驱动是怎么匹配设备的?(i2c_driver)
我们看一个典型的 I2C 驱动注册过程:drivers/misc/eeprom/at24.c
c
static const struct i2c_device_id at24_ids[] = {
{ "24c02", 0 },
...
};
static const struct of_device_id at24_of_match[] = {
{ .compatible = "atmel,24c02" },
...
};
static struct i2c_driver at24_driver = {
.driver = {
.name = "at24",
.of_match_table = of_match_ptr(at24_of_match),
},
.probe = at24_probe,
.remove = at24_remove,
.id_table = at24_ids,
};
module_i2c_driver(at24_driver); // 注册驱动
🧷 关键点:
- 注册的是
i2c_driver
,不是platform_driver
。 - 匹配的依据是
compatible
+of_device_id
。 - 匹配成功后调用
at24_probe()
,拿到i2c_client
,从而可以进行i2c_transfer()
通信。
⚙ 六、驱动内如何访问设备?(client + transfer)
以 at24.c
驱动为例:
c
static int at24_read(void *priv, unsigned int offset, void *buf, size_t count)
{
struct i2c_client *client = priv;
struct i2c_msg msgs[2];
// 1. 写 offset 地址
msgs[0].addr = client->addr;
msgs[0].flags = 0;
msgs[0].buf = &offset;
msgs[0].len = 1;
// 2. 读数据
msgs[1].addr = client->addr;
msgs[1].flags = I2C_M_RD;
msgs[1].buf = buf;
msgs[1].len = count;
return i2c_transfer(client->adapter, msgs, 2);
}
✅
i2c_transfer()
就是 I2C 核心提供的 API,适配器驱动通过.master_xfer()
回调实现底层通信。
🔚 七、小结与答疑
问题 | 回答 |
---|---|
I2C 子系统属于哪种驱动模型? | 总线设备驱动模型(不是 platform_driver) |
为什么控制器驱动用的是 platform_driver? | 控制器是 SoC 集成外设,依赖平台结构注册 |
一个 I2C 总线控制器驱动和设备驱动的区别? | 控制器注册 i2c_adapter,驱动匹配 i2c_client |
I2C 子系统的核心 API 是? | i2c_transfer() ,用于驱动层通信 |
📌 附图:I2C 子系统结构图
设备树 驱动注册
┌────────────┐ ┌─────────────────────┐
│ i2c@xxxx │──┐ │ i2c_driver: at24 │
│ └──@50 │ │ │ └─ probe() │
└────────────┘ │ └─────────────────────┘
↓
of_i2c_register_devices()
↓
i2c_new_device / client
↓
驱动匹配并调用 probe

✅ 总结一句话
控制器驱动用 platform_driver,挂载设备驱动用 i2c_driver,I2C 子系统的核心在于 client 与 driver 的匹配过程。
如果你需要配合 at24 驱动的完整实战样例 ,或者想了解 regmap
如何结合 I2C 使用,也可以继续深入。如果需要,我可以出一个《I2C 子系统实战篇》。
是否需要我继续整理一篇基于 i2c-tools 测试或 regmap+i2c 的完整实战篇?