Linux I2C 从设备测试单元后端

由 Wolfram Sang <wsa@sang-engineering.com> 于 2020 年编写

此后端可用于触发 I2C 总线主控的测试用例,这些测试用例需要具有某些功能的远程设备(通常不容易获得)。例如,多主控测试和 SMBus 主机通知测试。对于某些测试,I2C 从控制器必须能够在主控模式和从属模式之间切换,因为它也需要发送数据。

请注意,这是一个用于测试和调试的设备。不应在生产版本中启用它。虽然有一些版本控制,并且我们尽力保持向后兼容性,但不能保证稳定的 ABI!

实例化设备是常规的。总线 0,地址 0x30 的示例

# echo "slave-testunit 0x1030" > /sys/bus/i2c/devices/i2c-0/new_device

或使用固件节点。这是一个设备树示例(请注意,这只是一个调试设备,因此没有官方的 DT 绑定)

&i2c0 {
      ...

      testunit@30 {
              compatible = "slave-testunit";
              reg = <(0x30 | I2C_OWN_SLAVE_ADDRESS)>;
      };
};

之后,您将拥有正在侦听的设备。读取将返回单个字节。如果测试单元处于空闲状态,则其值为 0,否则为当前正在运行的命令的命令号。

写入时,该设备由 4 个 8 位寄存器组成,并且除了某些“部分”命令外,必须写入所有寄存器才能启动测试用例,即通常将 4 个字节写入设备。寄存器是

偏移量

名称

描述

0x00

CMD

要触发哪个测试

0x01

DATAL

测试的配置字节 1

0x02

DATAH

测试的配置字节 2

0x03

DELAY

延迟 n * 10 毫秒,直到测试开始

使用 i2c-tools 包中的 'i2cset',通用命令如下所示

# i2cset -y <bus_num> <testunit_address> <CMD> <DATAL> <DATAH> <DELAY> i

DELAY 是一个通用参数,它将延迟 CMD 中测试的执行。当一个命令正在运行时(包括延迟),新的命令将不会被确认。您需要等待直到旧命令完成。

命令在以下部分中描述。无效命令将导致传输未被确认。

命令

0x00 NOOP

保留供将来使用。

0x01 READ_BYTES

CMD

DATAL

DATAH

DELAY

0x01

从中读取数据的地址(低 7 位,最高位当前未使用)

要读取的字节数

n * 10 毫秒

还需要主控模式。这对于测试您的总线主控驱动程序是否正确处理多主控非常有用。您可以触发测试单元从总线上的另一个设备读取字节。如果正在测试的总线主控也想同时访问总线,则总线将处于忙碌状态。示例:延迟 50 毫秒后从设备 0x50 读取 128 个字节

# i2cset -y 0 0x30 1 0x50 0x80 5 i

0x02 SMBUS_HOST_NOTIFY

CMD

DATAL

DATAH

DELAY

0x02

要发送的状态字的低字节

要发送的状态字的高字节

n * 10 毫秒

还需要主控模式。此测试将向主机发送 SMBUS_HOST_NOTIFY 消息。请注意,状态字目前在 Linux 内核中被忽略。示例:延迟 10 毫秒后发送状态字 0x6442 的通知

# i2cset -y 0 0x30 2 0x42 0x64 1 i

如果主机控制器支持 HostNotify,则应显示带有调试级别的此消息(Linux 6.11 及更高版本)

Detected HostNotify from address 0x30

0x03 SMBUS_BLOCK_PROC_CALL

CMD

DATAL

DATAH

DELAY

0x03

0x01(即,将写入另一个字节)

要发送回的字节数

省略,部分命令!

部分命令。此测试将响应 SMBus 规范定义的块进程调用。写入的一个数据字节指定在后续读取传输中将发送回多少字节。请注意,在此读取传输中,测试单元将在要跟随的字节前加上长度。因此,如果您的主机总线驱动程序像大多数驱动程序一样模拟 SMBus 调用,则它需要支持 i2c_msg 的 I2C_M_RECV_LEN 标志。这是一个很好的测试用例。返回的数据首先由长度组成,然后是由从长度 - 1 到 0 的字节数组组成。以下示例使用 i2ctransfer 模拟 i2c_smbus_block_process_call()(您需要 i2c-tools v4.2 或更高版本)

# i2ctransfer -y 0 w3@0x30 3 1 0x10 r?
0x10 0x0f 0x0e 0x0d 0x0c 0x0b 0x0a 0x09 0x08 0x07 0x06 0x05 0x04 0x03 0x02 0x01 0x00

0x04 GET_VERSION_WITH_REP_START

CMD

DATAL

DATAH

DELAY

0x04

目前未使用

目前未使用

省略,部分命令!

部分命令。发送此命令后,测试单元将使用基于 UTS_RELEASE 的以 NUL 结尾的版本字符串回复读取消息。第一个字符始终为“v”,版本字符串的最大长度为 128 字节。但是,仅当读取消息通过重复启动连接到写入消息时,它才会响应。如果您的控制器驱动程序正确处理重复启动,则此操作将起作用

# i2ctransfer -y 0 w3@0x30 4 0 0 r128
0x76 0x36 0x2e 0x31 0x31 0x2e 0x30 0x2d 0x72 0x63 0x31 0x2d 0x30 0x30 0x30 0x30 ...

如果您有 i2c-tools 4.4 或更高版本,则可以立即打印出数据

# i2ctransfer -y -b 0 w3@0x30 4 0 0 r128
v6.11.0-rc1-00009-gd37a1b4d3fd0

两个消息之间的 STOP/START 组合将不起作用,因为它们不等同于重复启动。例如,这仅返回默认响应

# i2cset -y 0 0x30 4 0 0 i; i2cget -y 0 0x30
0x00

0x05 SMBUS_ALERT_REQUEST

CMD

DATAL

DATAH

DELAY

0x05

响应值(7 个最高有效位解释为 I2C 地址)

目前未使用

n * 10 毫秒

此测试通过 SMBAlert 引脚引发中断,主机控制器必须处理该中断。该引脚必须作为 GPIO 连接到测试单元。不允许 GPIO 访问进入睡眠状态。当前,只能使用固件节点来描述这一点。因此,对于设备树,您需要将类似以下内容添加到测试单元节点

gpios = <&gpio1 24 GPIO_ACTIVE_LOW>;

以下命令将以 1 秒的延迟触发警报,响应值为 0xc9

# i2cset -y 0 0x30 5 0xc9 0x00 100 i

如果主机控制器支持 SMBusAlert,则应显示带有调试级别的此消息

smbus_alert 0-000c: SMBALERT# from dev 0x64, flag 1

此消息可能会出现多次,因为测试单元是软件而不是硬件,因此可能无法足够快地响应主机的响应。但是,中断计数应仅增加 1

# cat /proc/interrupts | grep smbus_alert
 93:          1  gpio-rcar  26 Edge      smbus_alert

如果主机未在 1 秒内响应警报,则测试将中止,并且测试单元将报告错误。

对于此测试,测试单元将短暂地删除其分配的地址,并在 SMBus 警报响应地址 (0x0c) 上进行侦听。之后,它将重新分配其原始地址。