RK3506 Linux SMBus 协议与 Alert 机制(中文)

RK3506 Linux SMBus 协议与 Alert 机制(中文)

本文档说明 SMBus Alert、ARA 及 Linux 内核实现,并结合 RK3506 MPU 板级设备树(rk3506b-mpu.dtsi)举例。
内核源码路径以当前工程 kernel/ 为准。

术语

缩写 英文全称 中文
ARA Alert Response Address 告警响应地址
SMBALERT# SMBus Alert (active low) SMBus 告警信号(低有效)

1. SMBus 与 I2C 的关系

项目 说明
物理层 与 I2C 相同(SCL/SDA,开漏)
协议 在 I2C 之上定义超时、ACK 规则、部分命令格式
Linux CONFIG_I2C_SMBUS 启用后提供 i2c_smbus_*() API;Alert 由 i2c-smbus 模块处理

RK3506 的 I2C 控制器由 drivers/i2c/busses/i2c-rk3x.c 驱动,仅作 主机(Master),不实现 I2C 从机模式。


2. SMBus Alert 概述

2.1 信号:SMBALERT#

  • SMBALERT#:可选的开漏、线与信号(类似 SCL/SDA)。
  • 从设备有事件需主机处理时,将 SMBALERT# 拉低
  • 多条从设备可 同时 拉低同一根线;主机只看到一个低电平 → 触发 一次 GPIO 中断。

2.2 专用地址:ARA(0x0C)

ARA 全称:Alert Response Address(告警响应地址)

项目
缩写 ARA
英文全称 Alert Response Address
中文 告警响应地址
7 位地址 0x0C
用途 主机在 SMBALERT# 中断后访问,查询 当前正在告警 的从设备身份

「从设备才会响应 ARA」 的含义:

  • 只有 自己把 SMBALERT# 拉低 的从机,才会对地址 0x0C 应答并在读阶段送出 自己的 7 位地址 + 1 位标志
  • 未拉 SMBALERT# 的从机(例如总线上有 0x59 节点但未告警)不参与 此次 ARA 访问,等价于不存在。

2.3 与正常访问从设备地址的区别

操作 主机寻址 谁应答
读 PSU 寄存器 0x58 reg = <0x58> 的芯片
处理 SMBus Alert 0x0C (ARA) 正在拉 SMBALERT# 的芯片

3. 多从设备同时告警时的仲裁与读取顺序

3.1 规范行为(SMBus 1.1)

  1. 主机对 0x0C 执行 Receive Byte(读 1 字节)。
  2. 多个拉低 SMBALERT# 的从机同时参与;在 从机发送地址 阶段使用 标准 I2C 仲裁
    • SCL 为高时,SDA 上 发 0 胜、发 1 败
    • 7 位地址从高位比较 → 地址数值更小(优先级更高)的设备先胜出
  3. 胜出从机在应答 ARA 后应 释放 SMBALERT# 下拉
  4. 若传输结束后 SMBALERT# 仍为低,主机 再次读 ARA,处理下一个仍挂起的告警。

3.2 示例:0x58、0x59、0x5a,仅 0x58 与 0x5a 拉线

地址 SMBALERT# 第 1 次 ARA 第 2 次 ARA
0x58 拉低 读出 0x58
0x59 未拉低 不参与 不参与
0x5a 拉低 与 0x58 仲裁失败 读出 0x5a

实际读取顺序:0x58 → 0x5a(0x59 不会出现)。

若三者 拉低 SMBALERT#,顺序为:0x58 → 0x59 → 0x5a(严格按地址从小到大,每轮 ARA 读出一个)。

3.3 读回字节的格式

1
2
3
4
status = i2c_smbus_read_byte(ara);   /* ara->addr == 0x0c */

data.addr = status >> 1; /* 7 位从机地址 */
data.data = status & 1; /* 告警类型标志位 */

4. Linux 内核实现

4.1 相关文件

文件 作用
drivers/i2c/i2c-core-smbus.c i2c_new_smbus_alert_device():创建 ARA 客户端(0x0c);i2c_setup_smbus_alert()
drivers/i2c/i2c-smbus.c smbus_alert 驱动:GPIO 中断、for(;;) 循环读 ARA、调用 driver->alert()
drivers/i2c/i2c-core-base.c 适配器注册时调用 i2c_setup_smbus_alert(adap)
drivers/i2c/busses/i2c-rk3x.c RK3506 I2C 主机驱动( 硬件 SMBALERT, I2C Slave)

4.2 适配器注册时自动挂载 Alert

1
2
/* i2c-core-base.c */
res = i2c_setup_smbus_alert(adap);
1
2
3
4
5
6
7
8
/* i2c-core-smbus.c */
int i2c_setup_smbus_alert(struct i2c_adapter *adapter)
{
irq = device_property_match_string(parent, "interrupt-names", "smbus_alert");
if (irq == -EINVAL || irq == -ENODATA)
return 0;
return PTR_ERR_OR_ZERO(i2c_new_smbus_alert_device(adapter, NULL));
}

4.3 ARA 虚拟设备

1
2
3
struct i2c_board_info ara_board_info = {
I2C_BOARD_INFO("smbus_alert", 0x0c),
};

4.4 中断处理与循环读 ARA

流程:

1
2
3
4
5
6
7
8
SMBALERT# GPIO 中断
→ smbus_alert() [threaded IRQ]
→ schedule work → smbalert_work()
→ for (;;) {
i2c_smbus_read_byte(ara); /* 读 0x0C */
smbus_do_alert(); /* 匹配 client->addr,调 driver->alert() */
若同一地址连续两次且未处理 → smbus_do_alert_force() 后 break;
}

内核注释(仲裁与顺序):

1
2
3
4
5
6
/*
* Devices with pending alerts reply in address order, low
* to high, because of slave transmit arbitration. After
* responding, an SMBus device stops asserting SMBALERT#.
*/
status = i2c_smbus_read_byte(ara);

4.5 从设备驱动要求

  • 实现 struct i2c_driver.alert 回调。
  • alert()清除芯片内部告警状态,并确保释放 SMBALERT#(硬件规范要求)。
  • 若已处理完毕,应使 alert() 的语义能让上层不再死循环;未实现 alert() 时会打印 no driver alert()

5. 设备树配置(RK3506)

5.1 每条 I2C 总线仅一路 smbus_alert

Linux I2C 核心 每条 adapter 只支持一个 名为 smbus_alert 的中断,不能 在同一条 &i2cN 上配置多根独立 SMBALERT# GPIO。

多路 SMBALERT# 的推荐做法:

场景 做法
BMC、PSU 各有一条 SMBALERT# 分到 不同 I2C 总线i2c0 / i2c2 等),各配 1 个 smbus_alert
硬件多根线接同一总线 硬件 线与 成一根 SMBALERT#,软件仍只配 1 个 GPIO
插拔/存在检测 子节点 present-gpios,由各驱动处理,不是 SMBus Alert

5.2 板级示例(rk3506b-mpu.dtsi

i2c2(PSU 共用 SMBALERT#,GPIO1_B6):

1
2
3
4
5
6
7
8
9
10
&i2c2 {
/delete-property/ interrupts;
interrupts-extended = <&gic GIC_SPI 42 IRQ_TYPE_LEVEL_HIGH>,
<&gpio1 RK_PB6 IRQ_TYPE_LEVEL_LOW>;
interrupt-names = "irq", "smbus_alert";

aspower_d2000_0: power-supply@58 { reg = <0x58>; /* ... */ };
aspower_d2000_1: power-supply@59 { reg = <0x59>; /* ... */ };
aspower_d2000_2: power-supply@5a { reg = <0x5a>; /* ... */ };
};
  • irq:I2C 控制器完成传输等中断。
  • smbus_alert:SMBALERT# 外部 GPIO 中断。

i2c0(BMC SMBALERT#,示例中当前为注释状态):

1
2
/* interrupts-extended = <&gic ...>, <&gpio1 RK_PD1 IRQ_TYPE_LEVEL_LOW>; */
/* interrupt-names = "irq", "smbus_alert"; */

5.3 内核配置

  • CONFIG_I2C=y
  • CONFIG_I2C_SMBUS=y
  • CONFIG_I2C_RK3X=y(或对应 Rockchip I2C 选项)

6. RK3506 限制说明

能力 RK3506 / i2c-rk3x 说明
I2C Master 支持 常规 i2c0/1/2
SMBus Alert(GPIO + 软件 ARA) 支持 依赖 DTS smbus_alert + CONFIG_I2C_SMBUS
I2C Slave / SMBus Host Notify 不支持 i2c-rk3xI2C_FUNC_SLAVE,无法让 SoC 作总线从机

7. SMBus Host Notify(扩展,本板不适用)

  • 从设备向地址 0x08 写通知,主机需 I2C 从模式 接收。
  • 实现见 i2c-smbus.ci2c_new_slave_host_notify_device()依赖适配器 Slave 能力
  • RK3506 当前 i2c-rk3x 不支持,不能通过 DTS 单独启用。

8. 时序示意(0x58 + 0x5a 同时告警)

1
2
3
4
5
6
7
8
9
10
11
12
13
Dev@0x58          Dev@0x5a          SMBALERT#        Host
| | | |
|---- pull low ---|-----------------| |
| |---- pull low ---| |
| | |-- GPIO -->|
| | | | Read ARA 0x0C
|<--- win arb ---->| | | → 0x58
|-- release # ----| | | alert(0x58)
| | | (仍低) |
| | | | Read ARA 0x0C
| |<--- only 0x5a -| | → 0x5a
| |-- release # ----| | alert(0x5a)
| | | (高) | 结束

9. 调试建议

  1. 确认 dmesg 中有 supports SMBALERT#(适配器注册成功后 i2c-smbus 打印)。
  2. 触发告警后抓 I2C 波形:应对 0x0C 出现读事务,数据字节含从机地址。
  3. 若同一地址反复出现:检查从设备 alert() 是否清除告警 及是否释放 SMBALERT#。
  4. 使用 present-gpios 的插拔事件 不会 走 ARA,勿与 SMBALERT# 混淆。

10. 参考

资料 路径 / 说明
SMBus Alert 内核注释 kernel/drivers/i2c/i2c-smbus.c
ARA 设备创建 kernel/drivers/i2c/i2c-core-smbus.c
I2C 绑定 interrupt-names kernel/Documentation/devicetree/bindings/i2c/i2c.txt
SMBus 协议说明 kernel/Documentation/i2c/smbus-protocol.rst
RK3506 板级 DTS kernel/arch/arm/boot/dts/rk3506b-mpu.dtsi
SMBus 规范 SMBus 1.1+,Appendix / SMBALERT# 章节

文档版本:基于 RK3506 MPU 工程内核与 rk3506b-mpu.dtsi 整理。

文章互动

阅读 --

留言

0 条留言

正在加载留言…