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 标志。这是一个很好的测试用例。返回的数据首先包含长度,然后是一个从 length-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 组合将不起作用,因为它们不等同于重复启动(REPEATED 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

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

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

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

对于此测试,测试单元将暂时放弃其分配的地址,并在 SMBus 警报响应地址 (0x0c) 上监听。之后它将重新分配其原始地址。