i2c: testunit: add support for block process calls

Devices offering SMBus block process calls are rare, so add it to the
testunit. This is also a good test case for testing proper
I2C_M_RECV_LEN flag handling of I2C bus masters emulating SMBus.

Signed-off-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
Signed-off-by: Wolfram Sang <wsa@kernel.org>
This commit is contained in:
Wolfram Sang 2021-02-09 12:19:27 +01:00 committed by Wolfram Sang
parent bb3fe9ff53
commit b39ab96aa8
2 changed files with 31 additions and 4 deletions

View File

@ -22,8 +22,9 @@ Instantiating the device is regular. Example for bus 0, address 0x30:
After that, you will have a write-only device listening. Reads will just return
an 8-bit version number of the testunit. When writing, the device consists of 4
8-bit registers and all must be written to start a testcase, i.e. you must
always write 4 bytes to the device. The registers are:
8-bit registers and, except for some "partial" commands, all registers must be
written to start a testcase, i.e. you usually write 4 bytes to the device. The
registers are:
0x00 CMD - which test to trigger
0x01 DATAL - configuration byte 1 for the test
@ -67,3 +68,21 @@ status word is currently ignored in the Linux Kernel. Example to send a
notification after 10ms:
# i2cset -y 0 0x30 0x02 0x42 0x64 0x01 i
0x03 SMBUS_BLOCK_PROC_CALL (partial command)
DATAL - must be '1', i.e. one further byte will be written
DATAH - number of bytes to be sent back
DELAY - not applicable, partial command!
This test will respond to a block process call as defined by the SMBus
specification. The one data byte written specifies how many bytes will be sent
back in the following read transfer. Note that in this read transfer, the
testunit will prefix the length of the bytes to follow. So, if your host bus
driver emulates SMBus calls like the majority does, it needs to support the
I2C_M_RECV_LEN flag of an i2c_msg. This is a good testcase for it. The returned
data consists of the length first, and then of an array of bytes from length-1
to 0. Here is an example which emulates i2c_smbus_block_process_call() using
i2ctransfer (you need i2c-tools v4.2 or later):
# i2ctransfer -y 0 w3@0x30 0x03 0x01 0x10 r?
0x10 0x0f 0x0e 0x0d 0x0c 0x0b 0x0a 0x09 0x08 0x07 0x06 0x05 0x04 0x03 0x02 0x01 0x00

View File

@ -19,6 +19,7 @@
enum testunit_cmds {
TU_CMD_READ_BYTES = 1, /* save 0 for ABORT, RESET or similar */
TU_CMD_HOST_NOTIFY,
TU_CMD_SMBUS_BLOCK_PROC_CALL,
TU_NUM_CMDS
};
@ -88,6 +89,8 @@ static int i2c_slave_testunit_slave_cb(struct i2c_client *client,
enum i2c_slave_event event, u8 *val)
{
struct testunit_data *tu = i2c_get_clientdata(client);
bool is_proc_call = tu->reg_idx == 3 && tu->regs[TU_REG_DATAL] == 1 &&
tu->regs[TU_REG_CMD] == TU_CMD_SMBUS_BLOCK_PROC_CALL;
int ret = 0;
switch (event) {
@ -118,12 +121,17 @@ static int i2c_slave_testunit_slave_cb(struct i2c_client *client,
fallthrough;
case I2C_SLAVE_WRITE_REQUESTED:
memset(tu->regs, 0, TU_NUM_REGS);
tu->reg_idx = 0;
break;
case I2C_SLAVE_READ_REQUESTED:
case I2C_SLAVE_READ_PROCESSED:
*val = TU_CUR_VERSION;
if (is_proc_call && tu->regs[TU_REG_DATAH])
tu->regs[TU_REG_DATAH]--;
fallthrough;
case I2C_SLAVE_READ_REQUESTED:
*val = is_proc_call ? tu->regs[TU_REG_DATAH] : TU_CUR_VERSION;
break;
}