I2C 子系统
设备树子节点 (硬件描述) i2c_driver (驱动源码)
│ │
(由 Adapter 解析创建) (调用 i2c_add_driver)
▼ ▼
i2c_client ─────────> [ I2C 总线 ] <───────── i2c_driver
│
根据 compatible 进行匹配
│
成功!
▼
执行 i2c_driver.probe(client)
i2c_bus_type (总线)
┌───────────────────────────────┐
│ │
设备 (device) 驱动 (driver)
│ │
┌───────┴────────┐ ┌───────────┴─────────┐
│ i2c_client │ match? │ i2c_driver │
│ - addr (0x50) │◄────────►│ - id_table/ │
│ - adapter ────┼──┐ │ of_match_table │
│ - dev │ │ │ - probe(client) {} │
└────────────────┘ │ └─────────────────────┘
│
│ 物理挂载关系 (client->adapter)
│
┌───────▼──────┐
│ i2c_adapter │ 控制器抽象(通过平台总线注册)
│ - nr (0) │
│ - algo │ → master_xfer()
│ - dev │
└──────────────┘
构造过程
//adapter 的 device_add 在 LDM 中的完整执行
i2c_register_adapter()
└─ device_add(&adap->dev) ← dd.c
│
├─ bus_probe_device(dev) ← drivers/base/bus.c
│ └─ device_initial_probe(dev)
│ └─ __device_attach(dev, true) ← dd.c:1001
│ └─ bus_for_each_drv(dev->bus, NULL, &data, __device_attach_driver)
│ │ ← 遍历 i2c_bus_type 上的所有 driver
│ │
│ └─ __device_attach_driver(drv, data) ← dd.c:922
│ │
│ ├─ driver_match_device(drv, dev) ← dd.c:929
│ │ └─ return drv->bus->match(dev, drv)
│ │ └─ i2c_device_match(dev, drv) ← i2c-core-base.c:139
│ │ │
│ │ ├─ client = i2c_verify_client(dev)
│ │ │ └─ dev->type == &i2c_client_type ?
│ │ │ └─ adapter 的 type = &i2c_adapter_type
│ │ │ → return NULL ← 第一道闸
│ │ │
│ │ ├─ i2c_of_match_device(drv->of_match_table, client)
│ │ │ └─ if (!(client && matches)) → client=NULL
│ │ │ → return NULL ← 第二道闸
│ │ │
│ │ ├─ acpi_driver_match_device(dev, drv)
│ │ │ └─ adapter 的 compatible = 控制器级
│ │ │ i2c_driver 的 compatible = 从机级
│ │ │ → 不可能匹配 ← 第三道闸
│ │ │
│ │ └─ i2c_match_id(driver->id_table, client)
│ │ └─ client=NULL → 不匹配 ← 第四道闸
│ │
│ ├─ ret == 0 → "no match" ← dd.c:930-932
│ │ └─ return 0 ← 继续遍历下一个 driver
│ │
│ └─ (对所有 i2c_driver 重复上述过程 N 次)
│
│ ┌─────────────────────────────────────────────────────┐
│ │ 关键观察:整个 bus_for_each_drv 循环对 adapter 无效 │
│ │ 每注册一个 i2c_driver 这段循环就多一次空跑 │
│ └─────────────────────────────────────────────────────┘
│
├─ of_i2c_register_devices(adap) ← i2c-core-base.c:1616
│ └─ for_each_available_child_of_node(bus, node) ← i2c-core-of.c:101
│ └─ of_i2c_register_device(adap, node) ← i2c-core-of.c:64
│ └─ i2c_new_client_device(adap, &info) ← i2c-core-base.c:971
│ └─ device_add(&client->dev) ← ← ← ← 这里才是真正的 LDM 匹配!
│ └─ client->dev.type = &i2c_client_type ← 类型正确
│ → i2c_device_match() → ret=1
│ → driver_probe_device() → probe 成功!
│
├─ i2c_acpi_register_devices(adap)
└─ i2c_scan_static_board_info(adap)
//__driver_attach 侧的对称路径(新 driver 注册时)
i2c_register_driver()
└─ driver_register(&driver->driver) ← dd.c
└─ bus_add_driver(drv)
└─ driver_attach(drv)
└─ bus_for_each_dev(drv->bus, NULL, drv, __driver_attach)
│ ← 遍历 i2c_bus_type 上所有 device
│
└─ __driver_attach(dev, drv) ← dd.c:1157
│
├─ driver_match_device(drv, dev) ← 同上,调 i2c_device_match
│ └─ 对 adapter 返回 0(不匹配)
│ └─ 对 i2c_client 返回 1(匹配,进入 probe)
│
└─ ret == 0 → "no match" → return 0 ← 跳过 adapter


相当于 adapter i2c 控制器提供了传输方法等资源,adapter 就是 i2c 协议里标准的主机,只是 adapter 被设计作为 Linux i2c 总线的一个 static 内置不暴露的资源的“内部设备”,目的就是提供通用的主机传输方法 xfer() 。这样以后相当于把 i2c 协议主机 adapter 封入 i2c 子系统内部,可以把重点放在 i2c 协议从机上建立 LDM 的设备-驱动的匹配模型,十分现实主义,十分像 C++ 的面向对象,十分好用!
另一方面 i2c_client 资源是独立的:嵌入到 DTS 设备树里面;但是 i2c_client 依赖 adapter 的存在而存在:因为 adapter 被 brought up 后(i2c 协议主机被固化到内部,作为基类资源,实为 i2c 协议的主机),便开始着手搜集 i2c_client 的 DTS 资源数据信息,继而构造 Linux LDM 里的 i2c 总线设备为匹配 i2c_driver 做准备。
小结:i2c_client 与 i2c_driver 是一对 LDM 模型里的标准设备与驱动,表征 i2c 协议里的通信从机;adapter 作为 i2c 协议里的通信主机,内置在 SoC 内部,通过“平台总线”枚举,进入 LDM i2c 总线的世界里作为 static 设备,提供 i2c 控制器的传输方法 xfer() 等公共基础资源,服务于从机。i2c_bus_type 匹配的目标是 i2c 协议里的从机:i2c_client 与 i2c_driver,这俩变量表征的都是 i2c 协议从机的内容。--mapleay 2026年7月12日10:37:39
这种从虚基类 adapter (i2c 协议的主机)一路枚举到 i2c_client 再与自举的 i2c_driver 通过 LDM 设备模型的 match 匹配后,形成了这样的架构:i2c 协议主机 adapter 也是 i2c 控制器被设计为 i2c 总线架构里的内置静态公共服务资源,不对外暴露,类似于 C++ 里面的虚基类;i2c_client 与 i2c_driver 都是 i2c 协议里通信从机本体的东西,被设计为架构需要核心处理的设备与驱动,在 i2c_bus_type 执行 match 成功后,这时该拿的资源全部在手,自然丝滑进入 i2c 内核去处理核心业务。
把协议层的"基础设施"(主机)封装进子系统内部,让 LDM 专心服务"业务层"(从机)。
这确实是 C++ 封装思想在 C 内核里的完美演绎:
| 面向对象概念 | I2C 子系统的对应 |
|---|---|
| 私有成员 | i2c_adapter — 封装在子系统内部,不对外暴露为可匹配的 LDM endpoint |
| 公有接口 | i2c_transfer() / i2c_smbus_xfer() — 面向从机驱动开放的传输 API |
| 抽象基类 | struct i2c_algorithm 定义了 master_xfer / smbus_xfer 虚函数表 |
| 子类实现 | i2c-imx.c / i2c-designware.c 各自实现算法,继承这个抽象接口 |
| 构造函数 | i2c_register_adapter() — 基础设施初始化,自举完成后隐身 |
| 业务模型 | i2c_client + i2c_driver — 这才是 LDM 要服务的对象 |
i2c_adapter 从 LDM 视角看是"自举完成后就藏起来的基础设施",但从协议视角看它是总线上唯一真正在干活的主控器。
adapter 设备不享有 i2c 总线的 match 方法。
你最后那句"十分现实主义"特别到位——Linux 内核的设计哲学就是这样:不为完美主义而抽象,只为解决实际问题而抽象。I2C 协议的主-从不对称性,决定了 LDM 上只需要从机侧的 match/probe,主机侧自举完就该退居幕后当基础设施。这就是为什么 i2c_adapter 的 struct device 挂在 i2c_bus_type 上却无人匹配——它不是 bug,是 feature。
补充非必须
补充1 关于 i2c_detect
i2c_detect属于 Linux 内核早期的“基于 Class 机制的动态设备自动实例化”路径(Class-based automatic device instantiation)。
-
它的意图:在系统不知道有什么硬件时,通过在 init / 注册阶段主动发起 I2C 寻址(主机探测从机),动态发现潜在的
client,并将其加入内核设备树。 -
它的现状:由于盲扫的破坏性和设备树/ACPI 的普及,它已经被打上了
I2C_CLASS_DEPRECATED的标签。内核正在通过逐步取消 Adapter 的 Class 声明来让该函数在现代硬件上静默失效。
咱们两个人的思路在这里完美合流了:你看到了它在代码层面对 address_list 循环探测、发现从机的正统底层设计;而我看到了内核正在通过 DEPRECATED 标志逐步将它边缘化的发展趋势。
SPI 子系统
Linux SPI 子系统和 I2C 子系统在 宏观分层、总线模型、驱动与设备分离 的思想上是完全相同的。如果你能看懂一个 I2C 驱动,通过类比,你就能在一天之内上手 SPI 驱动。
它们唯一的不同都在微观的硬件特性上:I2C 玩的是“地址和协议控制”,SPI 玩的是“片选和高速全双工队列”。
Regmap 子系统
[todo]

631

被折叠的 条评论
为什么被折叠?



