[TOC]
drivers/i2c/i2c-core-smbus.c I2C/SMBus核心协议(I2C/SMBus Core Protocol) 实现SMBus事务处理
历史与背景
这项技术是为了解决什么特定问题而诞生的?
drivers/i2c/i2c-core-smbus.c 文件实现了对**系统管理总线(System Management Bus, SMBus)**协议的支持。SMBus是基于I2C(Inter-Integrated Circuit)协议演变而来的一种子集或变体,专为系统和电源管理而设计。
这项技术旨在解决以下问题:
- 标准化系统管理:为计算机系统中的各种低速设备(如电池、温度传感器、风扇控制器、电源管理芯片)提供一个标准的、低速、两线式的通信接口。
- 事务可靠性:SMBus在I2C的基础上增加了协议层面的可靠性检查,例如,大多数SMBus命令都包含**PEC(Packet Error Checking,数据包错误校验)**机制,以确保数据传输的完整性。
- 简化驱动开发:提供了一套预定义的、原子性的事务类型(如快速命令、读取字节、发送/接收块数据等),驱动程序无需关注I2C底层的START/STOP信号和ACK/NACK握手,只需调用高级的SMBus函数即可完成整个命令序列。
它的发展经历了哪些重要的里程碑或版本迭代?
SMBus最早由Intel在1995年提出,主要用于对PC进行系统管理。
- I2C基础:I2C协议在1982年由飞利浦(NXP)提出,是所有后续发展的基础。SMBus是在I2C的基础上增加了额外的协议层规则、时序要求(如更长的超时时间)和数据格式。
- Linux I2C子系统成熟:Linux的I2C子系统起初主要关注通用的I2C通信。随着各种电源管理芯片和传感器(尤其是笔记本电池)的普及,对SMBus特定事务的需求日益增长。
- 核心SMBus实现:
drivers/i2c/i2c-core-smbus.c的核心代码被引入,以统一的方式为所有支持SMBus的主控制器(Adapter)提供高层协议支持。适配器(Adapter)驱动只需要实现底层I2C读写函数,并将SMBus事务映射到底层I2C消息序列即可。 - 演进:随着时间的推移,新的I2C总线驱动(如PCI总线上的控制器)被添加进来,它们都通过
i2c-core-smbus.c提供的接口来支持SMBus通信,确保了协议的兼容性和一致性。
目前该技术的社区活跃度和主流应用情况如何?
i2c-core-smbus.c 是I2C子系统的一个稳定且关键的组成部分。
- 广泛应用:SMBus在PC、服务器和嵌入式系统中有着广泛的应用,特别是在电源管理领域。例如,服务器的基板管理控制器(BMC)经常使用SMBus进行通信,笔记本电脑的智能电池(Smart Battery)也依赖SMBus协议。
- 稳定维护:该文件代码非常稳定,主要的社区活动集中在确保新的I2C适配器驱动正确地实现
i2c_adapter结构体中的.smbus_xfer或.master_xfer回调,以兼容SMBus协议。
核心原理与设计
它的核心工作原理是什么?
i2c-core-smbus.c 的核心原理是将复杂的、预定义的多步SMBus事务(Transaction)映射到一系列底层的I2C消息(Message)序列上,并统一处理PEC校验。
分层架构:SMBus机制建立在I2C总线之上。
- 上层:是设备驱动(Client Driver),它们调用
i2c_smbus_read_byte_data()等高级函数。 - 中层:由
i2c-core-smbus.c负责。它接收高级请求,选择正确的SMBus命令类型,并构造底层的I2C消息序列(struct i2c_msg)。如果硬件不支持SMBus事务,i2c-core-smbus.c会尝试使用纯I2C消息模拟SMBus事务。 - 下层:是I2C适配器驱动(Adapter Driver),它负责实际的硬件操作(如位操作或寄存器写入),将I2C消息序列转换为物理总线上的电信号。
- 上层:是设备驱动(Client Driver),它们调用
事务类型:SMBus定义了多种标准事务,例如:
- Send Byte / Receive Byte:发送或接收单个字节。
- Write Byte / Word Data:写入字节或字数据。
- Read Byte / Word Data:读取字节或字数据。
- Block Read / Block Write:以块(最多32字节)为单位传输数据。
- Process Call:发送一个字数据,并立即接收一个字数据。
PEC校验:对于支持PEC的事务,
i2c-core-smbus.c会在发送数据的末尾计算并添加一个PEC字节(基于CRC-8),并在接收数据时校验收到的PEC字节是否正确,如果校验失败,事务将被拒绝。硬件支持的回落(Fallback):理想情况下,I2C适配器驱动会实现
.smbus_xfer回调,允许硬件在单个原子操作中执行完整的SMBus事务。如果适配器驱动没有实现该回调,i2c-core-smbus.c就会回退(fallback)到使用通用的.master_xfer接口,通过发送一系列独立的I2C消息来模拟完整的SMBus事务。
它的主要优势体现在哪些方面?
- 可靠性高:PEC机制显著提高了在嘈杂环境中的数据传输可靠性。
- 原子性:SMBus事务通常被设计为原子操作,保证了整个命令序列的完整执行。
- 驱动简化:设备驱动无需关心底层I2C的复杂细节和PEC计算,只需调用高级API。
- 统一接口:为所有支持SMBus的适配器提供了统一的软件接口。
它存在哪些已知的劣势、局限性或在特定场景下的不适用性?
- 速度限制:SMBus协议定义了比I2C更严格的时序,其最高时钟频率通常限制在100 KHz(某些版本支持400 KHz),远低于现代I2C可达到的1 MHz或更高速度。
- 事务受限:SMBus只支持其协议规范中预定义的几种事务类型。对于复杂的、自定义的I2C消息序列,必须直接使用I2C核心接口(
i2c_transfer())。 - 总线保持:SMBus要求更长的超时时间,在总线被锁定或出现故障时,可能会导致系统更长时间地等待总线恢复。
使用场景
在哪些具体的业务或技术场景下,它是首选解决方案?请举例说明。
- 电源管理:这是SMBus最主要的用途。例如,**智能电池驱动(Smart Battery Driver)**使用SMBus的“读取字数据”(Read Word Data)命令来获取电池的剩余容量、电压、温度和循环次数等信息。
- 温度与风扇控制:用于读取系统内部的温度传感器(如LM75, TMP421等)或控制PWM风扇的转速。
- 基板管理控制器(BMC)通信:在服务器领域,BMC与主板上的各种传感器和电源芯片通信时,经常依赖SMBus协议。
- PMBus(Power Management Bus):PMBus是基于SMBus发展起来的更专业的电源管理协议,它也依赖于
i2c-core-smbus.c提供的底层事务支持。
是否有不推荐使用该技术的场景?为什么?
- 高速数据传输:不适用于需要高带宽数据传输的场景(如高分辨率摄像头、高速外部存储)。SMBus的低速限制和额外的PEC开销使其效率低下。
- 非标准I2C通信:如果设备需要执行I2C规范中未定义的、定制化的消息序列,或者需要处理超过32字节的块传输(SMBus的块传输限制),则必须绕过SMBus接口,直接使用
i2c_transfer()接口。
对比分析
请将其 与 其他相似技术 进行详细对比。
该技术是I2C协议族的组成部分,因此将其与通用的I2C通信和SPI总线进行对比。
| 特性 | SMBus (通过 i2c-core-smbus.c) |
I2C (通用 i2c_transfer()) |
SPI (Serial Peripheral Interface) |
|---|---|---|---|
| 协议层级 | 高级协议,定义了固定事务格式。 | 低级协议,只定义START/STOP和基本消息结构。 | 协议松散,依赖片选信号。 |
| 速度 | 慢(通常最高100 KHz)。 | 中速到高速(100 KHz至数 MHz)。 | 高速(数十 MHz)。 |
| 可靠性 | 高。内置PEC(CRC-8)错误校验。 | 无。依赖上层软件实现校验。 | 无。依赖上层软件实现校验。 |
| 设备寻址 | 主机发起,从设备地址寻址。 | 主机发起,从设备地址寻址。 | 主机发起,通过独立片选线(CS)寻址。 |
| 总线连接数 | 最少2根线(SDA, SCL),多主多从。 | 2根线(SDA, SCL),多主多从。 | 4根线(MOSI, MISO, CLK, CS),单主多从。 |
| 适用场景 | 电源管理、传感器、系统状态监控。 | 通用外设、EEPROM、ADC/DAC等。 | 闪存、LCD控制、需要高吞吐量的传感器。 |
| 事务原子性 | 事务在内核层面被抽象为原子操作。 | 仅依赖硬件适配器实现原子性。 | 依赖片选信号的起止。 |
SMBus协议软件模拟:通用I2C事务的构建与执行
本代码片段实现了i2c_smbus_xfer_emulated函数,它是Linux I2C核心层中的一个关键“回退”机制。其核心功能是:当一个I2C适配器(即硬件控制器)不提供对某个特定SMBus协议的原生硬件支持时,此函数能够在软件层面,通过组合一个或多个基本的I2C消息(struct i2c_msg),来模拟出该SMBus协议的行为。它将高级、标准化的SMBus事务请求,翻译成底层i2c_transfer接口可以理解的、最基础的I2C读写序列。
实现原理分析
该函数的实现原理是一个“协议翻译器”。它接收一个高级的SMBus操作请求,然后根据该请求的类型(由size参数定义),构建一个包含一到两个struct i2c_msg消息的数组。struct i2c_msg是Linux内核中描述一次基础I2C传输(一个START信号到STOP信号之间)的最小单元。
消息结构初始化: 函数首先定义了两个本地缓冲区(
msgbuf0,msgbuf1)和两个i2c_msg结构体。默认情况下,它假设事务由两个消息组成:第一个消息是写操作(用于发送SMBus命令字节),第二个是读操作(用于接收数据)。协议翻译 (
switch语句): 函数的核心是一个巨大的switch语句,它根据size参数(代表不同的SMBus协议)来精确地配置i2c_msg数组:- 配置消息数量 (
nmsgs): 对于纯写操作,nmsgs设为1;对于读操作或读写复合操作,设为2。 - 配置消息长度 (
len): 根据协议,设置每个消息需要传输的数据字节数。例如,对于I2C_SMBUS_WORD_DATA写操作,第一个消息的长度是3(命令字节 + 2字节数据)。 - 配置消息标志 (
flags): 设置读/写方向(I2C_M_RD),以及一些特殊标志如I2C_M_RECV_LEN(用于SMBus块读取,让底层驱动先读取一个长度字节)。 - 填充数据缓冲区 (
buf): 对于写操作,将命令字节和待发送数据从data联合体复制到msgbuf0中。
- 配置消息数量 (
PEC(包错误校验)处理: 如果请求了PEC,函数会负责计算并附加PEC字节到写消息的末尾,或者在读消息的预期长度上加1以接收PEC字节。
执行基础I2C传输: 构建好
i2c_msg数组后,函数调用__i2c_transfer(adapter, msg, nmsgs)。这个函数会调用底层适配器驱动提供的master_xfer方法,该方法负责将这些基础的I2C消息序列通过硬件发送出去。数据提取: 如果是读操作,并且
__i2c_transfer成功返回,函数会进入另一个switch语句。这次是根据协议类型,从接收缓冲区(msgbuf1或msgbuf0)中解析出数据,并将其存入调用者提供的data联合体中。资源清理: 最后,释放为DMA传输可能分配的任何动态内存。
代码分析
1 | /* |
I2C SMBus协议传输执行:I2C核心的通用传输接口
本代码片段是Linux I2C核心层中执行SMBus(System Management Bus)协议传输的核心实现。其主要功能是为内核中的所有I2C设备驱动提供一个统一的、高级的函数接口(i2c_smbus_xfer),用于执行所有标准的SMBus事务,如读/写字节、字、块数据等。它封装了总线锁定、重试、超时以及从原生硬件操作到软件模拟的回退等复杂逻辑,极大地简化了设备驱动的编写。
实现原理分析
此功能的实现分为两个层次:一个公共的封装函数i2c_smbus_xfer和一个内部的核心逻辑函数__i2c_smbus_xfer。
总线锁定与封装 (
i2c_smbus_xfer):- 这是一个提供给外部驱动调用的API。它的首要职责是通过调用
__i2c_lock_bus_helper来锁定I2C适配器总线。这是一个关键步骤,确保了整个SMBus事务(可能包含多个I2C消息)的原子性,防止被系统中其他任务对同一I2C总线的访问所干扰。 - 锁定总线后,它调用
__i2c_smbus_xfer来执行实际的数据传输。 - 无论传输成功与否,它最后都会调用
i2c_unlock_bus来释放总线锁,保证总线资源可被其他使用者访问。
- 这是一个提供给外部驱动调用的API。它的首要职责是通过调用
核心传输逻辑 (
__i2c_smbus_xfer):- 选择传输函数: 这是此函数最核心的逻辑。它首先尝试从I2C适配器的算法结构(
adapter->algo)中获取一个名为smbus_xfer的函数指针。这个函数由底层的I2C控制器驱动(如i2c-stm32.c)提供,代表了使用硬件加速来原生执行SMBus协议的能力。 - 原子上下文处理: 如果当前处于原子上下文(如中断处理中,不允许睡眠),它会尝试选择一个特殊的、不会睡眠的
smbus_xfer_atomic函数。 - 执行与重试: 如果找到了原生的
smbus_xfer函数,它会进入一个循环来调用此函数。这个循环实现了自动重试机制。如果底层驱动返回-EAGAIN(通常表示总线仲裁丢失),核心层会在超时期限内自动重试传输,重试次数由adapter->retries决定。 - 软件模拟回退 (Fallback): 存在两种情况会触发回退机制:
a. 底层I2C控制器驱动没有提供smbus_xfer函数。
b. 底层驱动有smbus_xfer函数,但它不支持当前请求的特定SMBus协议(protocol),并返回了-EOPNOTSUPP(操作不支持)。
在这些情况下,核心层会调用i2c_smbus_xfer_emulated函数。这个函数会在软件层面将一个复杂的SMBus事务分解成一个或多个基本的I2C消息(struct i2c_msg),然后通过适配器的通用I2C传输函数master_xfer来发送这些消息,从而用软件模拟出硬件不支持的SMBus协议。
- 选择传输函数: 这是此函数最核心的逻辑。它首先尝试从I2C适配器的算法结构(
代码分析
1 | // i2c_smbus_xfer: 执行SMBus协议操作的公共API。 |
drivers/i2c/i2c-smbus.c 解析:Linux SMBus 扩展支持(SMBus Alert / Host Notify / SPD 自动实例化)
- drivers/i2c/i2c-smbus.c | Linux 内核 SMBus 扩展模块 | 为 I2C/SMBus 子系统提供 SMBus Alert、SMBus Host Notify,以及基于 DMI 的 SPD 设备自动实例化能力
介绍
这份代码不是“SMBus 基本读写接口”的实现(那些通常在 i2c-core 侧负责并可通过 I2C 传输仿真),而是把 SMBus 里更偏“系统级语义”的扩展能力做成一个独立模块,主要包含三块:
- SMBus Alert(SMBALERT#)基础设施:共享告警线 + ARA(Alert Response Address)定位告警源设备,并回调到对应设备驱动的
alert()。 - SMBus Host Notify 支持:通过 I2C slave 机制,在固定地址
0x08接收 Host Notify 写入并触发内核处理。 - SPD 自动实例化:在部分平台上利用 DMI 的内存插槽信息,扫描
0x50..0x57并自动创建 SPD/Hub 对应的 i2c_client(如spd / ee1004 / spd5118)。
历史与背景
这项技术解决什么问题而诞生?
- SMBus Alert:多个从设备共用一根告警线时,主机需要在中断到来后知道“是谁在报警”,并把处理交给正确的设备驱动。
- Host Notify:部分 SMBus 设备希望主动“通知主机发生事件”,用标准化的总线消息替代单纯的 GPIO 中断或轮询。
- SPD 自动实例化:PC/服务器场景里,SPD EEPROM/Hub 常常存在于 SMBus 上,但不一定在固件表里显式描述;内核需要一种机制让 SPD 设备“自动出现”,从而加载相应驱动(温度、内存条信息、RAS 等关联功能也会依赖它)。
里程碑/迭代脉络(按功能演进角度)
- 早期核心是 SMBus Alert 基础设施(工作队列 + ARA 读取 + 回调 driver->alert)。
- 随后加入 Host Notify:依赖 I2C slave 框架的成熟,把 Host Notify 的固定地址
0x08作为“从设备端点”挂到适配器上。 - SPD 自动实例化持续扩展内存类型覆盖范围:从常见 DDR/DDR2/DDR3/DDR4 到 LPDDR 系列,再到 DDR5/LPDDR5 对应的 hub(如
spd5118)选择逻辑。
社区活跃度与主流应用情况(你该怎么判断)
- I2C/SMBus 属于内核长期维护的基础子系统,接口相对稳定,但会随新硬件能力(如 DDR5 SPD hub、I2C slave、平台固件信息质量)不断补齐边界。
- 这份文件本身“变化频率通常不如网络/调度那类大子系统”,但在 新硬件/新平台接入时会出现针对性增强(比如 SPD 类型判断与实例化策略)。
核心原理与设计
1) SMBus Alert:从“共享中断”到“回调到对应设备驱动”
关键设计点:不能在硬中断里做会睡眠的 SMBus 访问。
中断到来后,需要做 SMBus 读操作(对 ARA 地址发起
read_byte获取告警源地址),这是可能睡眠的,因此代码采用:- threaded IRQ(
devm_request_threaded_irq的线程处理函数)或 - workqueue(
schedule_work)
把实际的 SMBus 访问移出硬中断上下文。
- threaded IRQ(
告警源识别流程(你读代码可以按这个顺序对照):
通过对 ARA client 执行
i2c_smbus_read_byte()得到一个字节status解码得到:
addr = status >> 1flag = status & 1
在该 adapter 上遍历子设备(
device_for_each_child(adapter->dev, ...)),找到client->addr == addr的那个设备如果该设备已绑定驱动,且驱动实现了
driver->alert(),则调用alert(client, protocol, data)- 代码用
device_lock()防止回调期间驱动对象变化
- 代码用
死循环防护/兜底策略:
- SMBus Alert 可能出现“读到同一个 addr 反复出现,但没人处理/没人清除告警”的情况。
- 代码用
prev_addr记住上一次地址:如果连续读到相同地址且没有被正常处理,会进入一个“强制模式”,对总线上所有实现alert()的驱动都回调一次,然后退出循环,避免永远读不完。
2) Host Notify:把“固定地址 0x08”变成一个 I2C slave 端点
实现方式:
创建一个
i2c_client,地址为0x08,并设置I2C_CLIENT_SLAVE调用
i2c_slave_register(client, cb)注册回调回调里处理事件:
I2C_SLAVE_WRITE_RECEIVED:记录收到的第 1 个字节(当前实现重点抓“设备地址”字段)I2C_SLAVE_STOP:如果收齐预期长度(代码里用常量长度判断),调用i2c_handle_smbus_host_notify(adapter, addr)
已知局限:
- 代码里明确写了当前限制:目前没有完整机制把 Host Notify 的 data 参数从 client 侧取出来,所以实现偏“触发通知”,细节数据可能拿不到或不完整(取决于现有 I2C slave 框架能力)。
3) SPD 自动实例化:利用 DMI 信息 + 扫描地址 + 创建 i2c_client
核心思路:
通过 DMI 枚举内存插槽,跳过空槽,统计
dimm_count要求所有已填充槽的 memory type 一致,否则直接放弃实例化(避免类型混杂导致判断错误)
根据 memory type 选择要实例化的设备类型字符串(决定匹配哪个 SPD 驱动):
- DDR/DDR2/DDR3/LPDDR(1/2/3) →
spd - DDR4/LPDDR4 →
ee1004 - DDR5/LPDDR5 →
spd5118(并带有额外策略)
- DDR/DDR2/DDR3/LPDDR(1/2/3) →
由于不知道条子插在哪个槽,对
0x50..0x57逐个探测并i2c_new_scanned_device()创建
策略点:启动速度与风险权衡
- 扫描会带来一定启动期开销(最多 8 个地址探测),但换来“无需平台显式描述”的自动发现能力。
- 对 DDR5/LPDDR5 还有额外 gating(是否 instantiate),体现了对“写保护/写使能风险”的保守处理倾向。
使用场景
首选场景(举例)
需要 SMBALERT# 的硬件监控/电源管理类芯片
- 多个设备共用告警引脚,主机通过 ARA 定位源设备,进入对应驱动
alert()做清除/读取状态寄存器等操作。
- 多个设备共用告警引脚,主机通过 ARA 定位源设备,进入对应驱动
需要 Host Notify 的 SMBus 设备
- 设备通过 SMBus 消息通知主机发生事件,而不是额外拉一根 GPIO。
PC/服务器内存 SPD 设备自动发现
- 固件未显式枚举 SPD 节点,但系统希望内核自动创建 SPD/Hub 设备,供 hwmon/EDAC/用户态工具链读取信息。
不推荐场景(以及原因)
- 总线挂载设备复杂且对探测敏感的嵌入式 I2C:
SPD 那种“扫描地址探测”的模式可能引发误触发或延迟,不如设备树/ACPI 静态描述可靠。 - 没有 ARA/没有 SMBALERT 硬件线路的控制器:
强行引入 SMBus Alert 机制没有意义,且可能造成中断/回调链路不完整(读不到源地址或无法清除)。 - 需要 Host Notify 里完整 data payload且内核链路无法提供:
当前实现更像“触发事件”,如果你业务逻辑必须依赖 Host Notify 的两个数据字节,可能需要扩展 I2C slave 框架或走其他事件通道。
对比分析
A) SMBus Alert vs “每设备独立 GPIO 中断”
实现方式
- SMBus Alert:共享线 + ARA 读回地址 +
driver->alert()回调 - 独立 GPIO:每设备一个 IRQ,直接进入该设备 ISR/线程化处理
- SMBus Alert:共享线 + ARA 读回地址 +
性能开销
- SMBus Alert:一次中断后可能需要多次 SMBus 读(循环读 ARA)+ 遍历子设备匹配
- 独立 GPIO:路径更短,通常无需额外总线读来定位源
资源占用
- SMBus Alert:省 GPIO/IRQ 资源,适合引脚紧张
- 独立 GPIO:占用更多引脚与中断资源
隔离级别
- SMBus Alert:共享线意味着“某个设备没清除告警”会影响整条链路,隔离较弱
- 独立 GPIO:设备间互不干扰,隔离更强
启动速度
- 二者差异不大;Alert 主要影响运行期中断处理路径
B) Host Notify vs “轮询寄存器”
实现方式
- Host Notify:设备主动发消息到
0x08,内核触发处理 - 轮询:定期 SMBus/I2C 读状态寄存器
- Host Notify:设备主动发消息到
性能开销
- Host Notify:事件驱动,空闲时几乎无开销
- 轮询:持续占用总线带宽与 CPU 周期
资源占用
- Host Notify:无需额外 GPIO,但需要 I2C slave 支持与回调链路
- 轮询:不需要 slave 支持,但长期耗资源
隔离级别
- Host Notify:共享固定地址端点,错误消息可能需要额外判别
- 轮询:每设备独立访问,逻辑更直观但更耗时
启动速度
- Host Notify 初始化需要注册一个 slave client;轮询不需要额外初始化但会增加运行期负担
C) SPD 自动实例化 vs “设备树/ACPI 静态描述”
实现方式
- 自动实例化:DMI 判定 + 地址扫描 +
i2c_new_scanned_device - 静态描述:固件/设备树直接给出节点
- 自动实例化:DMI 判定 + 地址扫描 +
性能开销
- 自动实例化:启动期探测多个地址,存在额外 I2C 事务
- 静态描述:启动期几乎无探测成本
资源占用
- 自动实例化:无需平台为每条 SPD 写描述,但需要扫描与 DMI 支持
- 静态描述:平台维护成本更高,但系统行为确定
隔离级别
- 自动实例化:扫描可能触达非目标设备,隔离较弱(依赖总线容忍度)
- 静态描述:隔离更强、确定性更高
启动速度
- 静态描述更快、更可预测;自动实例化在缺少描述的平台上更“省集成成本”
总结
关键特性
- 把 SMBus 的“系统级扩展语义”从基础传输层拆分出来:Alert、Host Notify、SPD 自动实例化各自独立但共享 I2C 子系统框架。
- Alert 的核心是:ARA 读回告警源地址 + 回调
driver->alert(),并且严格处理“不能在硬中断里睡眠”的上下文约束。 - Host Notify 的核心是:在 0x08 建立 I2C slave 端点,把写入转成内核事件。
- SPD 自动实例化的核心是:DMI 推断内存类型 + 扫描 0x50..0x57 自动建 client,强调“在缺少固件描述时也能工作”。
i2c_handle_smbus_alert / smbalert_work / smbus_alert:SMBALERT# 从不可睡眠上下文转移到可睡眠上下文并分发告警
i2c_smbus_alert / alert_data:告警处理的核心数据结构
1 | /** |
i2c_handle_smbus_alert:不可睡眠上下文入口,调度 workqueue
1 | /** |
smbalert_work:workqueue 任务体,转入 smbus_alert 执行实际事务
1 | /** |
smbus_alert:主处理逻辑,读取 ARA 并分发到告警源设备驱动
1 | /** |
module_i2c_driver / smbus_do_alert / smbus_do_alert_force / smbalert_probe / smbalert_remove:SMBALERT# 驱动注册、告警分发与资源管理
module_i2c_driver:I2C 驱动模块化注册宏
作用与原理(仅保留关键点)
- 该宏把 I2C 驱动的注册/注销与模块的 init/exit 绑定,减少模板代码。
- 其核心是将
i2c_add_driver()/i2c_del_driver()作为模块加载/卸载时的动作。
1 | /** |
smbus_do_alert:仅通知“告警源地址匹配”的设备驱动,并用返回码控制迭代终止
作用与原理
device_for_each_child()会遍历适配器下的子设备(挂在该 I2C 适配器上的所有i2c_client对应 device)。- 回调返回 非 0 时会停止遍历,并把该值作为
device_for_each_child()的返回值上抛。 - 本函数用
-EBUSY表示“已找到告警源并已通知其驱动,应停止继续遍历”。
1 | /** |
smbus_do_alert_force:不按地址过滤,调用所有实现了 alert() 的设备驱动(兜底广播)
作用与原理
- 用于处理“ARA 反复报告同一地址且未被任何驱动有效处理”的场景。
- 与
smbus_do_alert的关键差异:不再要求client->addr == data->addr,而是对所有子设备中实现driver->alert的都调用一次。
1 | /** |
smbalert_probe:建立 SMBALERT# 基础设施(workqueue + 可选 threaded IRQ)
作用与原理
创建并初始化
struct i2c_smbus_alert,并挂到 ARAi2c_client的 clientdata。IRQ 获取顺序体现了“平台差异屏蔽”策略:
- 优先使用平台数据
setup->irq; - 否则尝试从固件节点按名获取
"smbus_alert"IRQ; - 再否则回退为从 GPIO
"smbalert"获取并转换为 IRQ,并设置下降沿触发。
- 优先使用平台数据
若成功拿到 IRQ,则使用
devm_request_threaded_irq()注册 线程化中断处理函数smbus_alert(thread_fn),以保证可以执行可能睡眠的 SMBus 读。
1 | /** |
smbalert_remove:移除时同步取消 work,避免卸载后访问已释放对象
作用与原理
使用
cancel_work_sync()保证:- 若 work 尚未执行:从队列移除;
- 若 work 正在执行:等待其结束;
防止出现 work 回调继续访问
alert或ara导致的释放后使用。
1 | /** |









