Linux 的 I2C、SPI、Regmap 子系统

想深耕嵌入式?这个专辑值得收藏

MCU、FPGA、工控、传感器一站式学习,实战项目直接抄

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_adapterstruct 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]

想深耕嵌入式?这个专辑值得收藏

MCU、FPGA、工控、传感器一站式学习,实战项目直接抄

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值