1

i2c: testunit: add command to support versioning and test rep_start

For some devices, it is essential that controllers handle repeated start
correctly and do not replace it with a stop/start combination. This
addition helps to test that because it will only return a version string
if repeated start is done properly.

Signed-off-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
This commit is contained in:
Wolfram Sang 2024-08-11 23:23:15 +02:00
parent faf3c102c6
commit 6b21470af0
2 changed files with 61 additions and 2 deletions

View File

@ -133,3 +133,41 @@ later)::
# i2ctransfer -y 0 w3@0x30 3 1 0x10 r? # 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 0x10 0x0f 0x0e 0x0d 0x0c 0x0b 0x0a 0x09 0x08 0x07 0x06 0x05 0x04 0x03 0x02 0x01 0x00
0x04 GET_VERSION_WITH_REP_START
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. list-table::
:header-rows: 1
* - CMD
- DATAL
- DATAH
- DELAY
* - 0x04
- currently unused
- currently unused
- leave out, partial command!
Partial command. After sending this command, the testunit will reply to a read
message with a NUL terminated version string based on UTS_RELEASE. The first
character is always a 'v' and the length of the version string is at maximum
128 bytes. However, it will only respond if the read message is connected to
the write message via repeated start. If your controller driver handles
repeated start correctly, this will work::
# 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 ...
If you have i2c-tools 4.4 or later, you can print out the data right away::
# i2ctransfer -y -b 0 w3@0x30 4 0 0 r128
v6.11.0-rc1-00009-gd37a1b4d3fd0
STOP/START combinations between the two messages will *not* work because they
are not equivalent to a REPEATED START. As an example, this returns just the
default response::
# i2cset -y 0 0x30 4 0 0 i; i2cget -y 0 0x30
0x01

View File

@ -6,6 +6,7 @@
* Copyright (C) 2020 by Renesas Electronics Corporation * Copyright (C) 2020 by Renesas Electronics Corporation
*/ */
#include <generated/utsrelease.h>
#include <linux/bitops.h> #include <linux/bitops.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/init.h> #include <linux/init.h>
@ -15,11 +16,13 @@
#include <linux/workqueue.h> /* FIXME: is system_long_wq the best choice? */ #include <linux/workqueue.h> /* FIXME: is system_long_wq the best choice? */
#define TU_CUR_VERSION 0x01 #define TU_CUR_VERSION 0x01
#define TU_VERSION_MAX_LENGTH 128
enum testunit_cmds { enum testunit_cmds {
TU_CMD_READ_BYTES = 1, /* save 0 for ABORT, RESET or similar */ TU_CMD_READ_BYTES = 1, /* save 0 for ABORT, RESET or similar */
TU_CMD_SMBUS_HOST_NOTIFY, TU_CMD_SMBUS_HOST_NOTIFY,
TU_CMD_SMBUS_BLOCK_PROC_CALL, TU_CMD_SMBUS_BLOCK_PROC_CALL,
TU_CMD_GET_VERSION_WITH_REP_START,
TU_NUM_CMDS TU_NUM_CMDS
}; };
@ -39,10 +42,13 @@ struct testunit_data {
unsigned long flags; unsigned long flags;
u8 regs[TU_NUM_REGS]; u8 regs[TU_NUM_REGS];
u8 reg_idx; u8 reg_idx;
u8 read_idx;
struct i2c_client *client; struct i2c_client *client;
struct delayed_work worker; struct delayed_work worker;
}; };
static char tu_version_info[] = "v" UTS_RELEASE "\n\0";
static void i2c_slave_testunit_work(struct work_struct *work) static void i2c_slave_testunit_work(struct work_struct *work)
{ {
struct testunit_data *tu = container_of(work, struct testunit_data, worker.work); struct testunit_data *tu = container_of(work, struct testunit_data, worker.work);
@ -91,6 +97,8 @@ static int i2c_slave_testunit_slave_cb(struct i2c_client *client,
struct testunit_data *tu = i2c_get_clientdata(client); struct testunit_data *tu = i2c_get_clientdata(client);
bool is_proc_call = tu->reg_idx == 3 && tu->regs[TU_REG_DATAL] == 1 && 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; tu->regs[TU_REG_CMD] == TU_CMD_SMBUS_BLOCK_PROC_CALL;
bool is_get_version = tu->reg_idx == 3 &&
tu->regs[TU_REG_CMD] == TU_CMD_GET_VERSION_WITH_REP_START;
int ret = 0; int ret = 0;
switch (event) { switch (event) {
@ -100,6 +108,7 @@ static int i2c_slave_testunit_slave_cb(struct i2c_client *client,
memset(tu->regs, 0, TU_NUM_REGS); memset(tu->regs, 0, TU_NUM_REGS);
tu->reg_idx = 0; tu->reg_idx = 0;
tu->read_idx = 0;
break; break;
case I2C_SLAVE_WRITE_RECEIVED: case I2C_SLAVE_WRITE_RECEIVED:
@ -136,12 +145,21 @@ static int i2c_slave_testunit_slave_cb(struct i2c_client *client,
break; break;
case I2C_SLAVE_READ_PROCESSED: case I2C_SLAVE_READ_PROCESSED:
if (is_proc_call && tu->regs[TU_REG_DATAH]) /* Advance until we reach the NUL character */
if (is_get_version && tu_version_info[tu->read_idx] != 0)
tu->read_idx++;
else if (is_proc_call && tu->regs[TU_REG_DATAH])
tu->regs[TU_REG_DATAH]--; tu->regs[TU_REG_DATAH]--;
fallthrough; fallthrough;
case I2C_SLAVE_READ_REQUESTED: case I2C_SLAVE_READ_REQUESTED:
*val = is_proc_call ? tu->regs[TU_REG_DATAH] : TU_CUR_VERSION; if (is_get_version)
*val = tu_version_info[tu->read_idx];
else if (is_proc_call)
*val = tu->regs[TU_REG_DATAH];
else
*val = TU_CUR_VERSION;
break; break;
} }
@ -160,6 +178,9 @@ static int i2c_slave_testunit_probe(struct i2c_client *client)
i2c_set_clientdata(client, tu); i2c_set_clientdata(client, tu);
INIT_DELAYED_WORK(&tu->worker, i2c_slave_testunit_work); INIT_DELAYED_WORK(&tu->worker, i2c_slave_testunit_work);
if (sizeof(tu_version_info) > TU_VERSION_MAX_LENGTH)
tu_version_info[TU_VERSION_MAX_LENGTH - 1] = 0;
return i2c_slave_register(client, i2c_slave_testunit_slave_cb); return i2c_slave_register(client, i2c_slave_testunit_slave_cb);
}; };