Skip to content

Commit 3d16973

Browse files
author
Wolfram Sang
committed
i2c: testunit: add SMBusAlert trigger
To test SMBusAlert handlers, let the testunit be able to trigger SMBusAlert interrupts. This new command needs a GPIO connected to the SMBAlert# line. Signed-off-by: Wolfram Sang <[email protected]>
1 parent 06e12ae commit 3d16973

File tree

2 files changed

+107
-0
lines changed

2 files changed

+107
-0
lines changed

Documentation/i2c/slave-testunit-backend.rst

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,3 +185,51 @@ default response::
185185

186186
# i2cset -y 0 0x30 4 0 0 i; i2cget -y 0 0x30
187187
0x00
188+
189+
0x05 SMBUS_ALERT_REQUEST
190+
~~~~~~~~~~~~~~~~~~~~~~~~
191+
192+
.. list-table::
193+
:header-rows: 1
194+
195+
* - CMD
196+
- DATAL
197+
- DATAH
198+
- DELAY
199+
200+
* - 0x05
201+
- response value (7 MSBs interpreted as I2C address)
202+
- currently unused
203+
- n * 10ms
204+
205+
This test raises an interrupt via the SMBAlert pin which the host controller
206+
must handle. The pin must be connected to the testunit as a GPIO. GPIO access
207+
is not allowed to sleep. Currently, this can only be described using firmware
208+
nodes. So, for devicetree, you would add something like this to the testunit
209+
node::
210+
211+
gpios = <&gpio1 24 GPIO_ACTIVE_LOW>;
212+
213+
The following command will trigger the alert with a response of 0xc9 after 1
214+
second of delay::
215+
216+
# i2cset -y 0 0x30 5 0xc9 0x00 100 i
217+
218+
If the host controller supports SMBusAlert, this message with debug level
219+
should appear::
220+
221+
smbus_alert 0-000c: SMBALERT# from dev 0x64, flag 1
222+
223+
This message may appear more than once because the testunit is software not
224+
hardware and, thus, may not be able to react to the response of the host fast
225+
enough. The interrupt count should increase only by one, though::
226+
227+
# cat /proc/interrupts | grep smbus_alert
228+
93: 1 gpio-rcar 26 Edge smbus_alert
229+
230+
If the host does not respond to the alert within 1 second, the test will be
231+
aborted and the testunit will report an error.
232+
233+
For this test, the testunit will shortly drop its assigned address and listen
234+
on the SMBus Alert Response Address (0x0c). It will reassign its original
235+
address afterwards.

drivers/i2c/i2c-slave-testunit.c

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88

99
#include <generated/utsrelease.h>
1010
#include <linux/bitops.h>
11+
#include <linux/completion.h>
12+
#include <linux/gpio/consumer.h>
1113
#include <linux/i2c.h>
1214
#include <linux/init.h>
1315
#include <linux/module.h>
@@ -22,6 +24,7 @@ enum testunit_cmds {
2224
TU_CMD_SMBUS_HOST_NOTIFY,
2325
TU_CMD_SMBUS_BLOCK_PROC_CALL,
2426
TU_CMD_GET_VERSION_WITH_REP_START,
27+
TU_CMD_SMBUS_ALERT_REQUEST,
2528
TU_NUM_CMDS
2629
};
2730

@@ -44,10 +47,37 @@ struct testunit_data {
4447
u8 read_idx;
4548
struct i2c_client *client;
4649
struct delayed_work worker;
50+
struct gpio_desc *gpio;
51+
struct completion alert_done;
4752
};
4853

4954
static char tu_version_info[] = "v" UTS_RELEASE "\n\0";
5055

56+
static int i2c_slave_testunit_smbalert_cb(struct i2c_client *client,
57+
enum i2c_slave_event event, u8 *val)
58+
{
59+
struct testunit_data *tu = i2c_get_clientdata(client);
60+
61+
switch (event) {
62+
case I2C_SLAVE_READ_PROCESSED:
63+
gpiod_set_value(tu->gpio, 0);
64+
fallthrough;
65+
case I2C_SLAVE_READ_REQUESTED:
66+
*val = tu->regs[TU_REG_DATAL];
67+
break;
68+
69+
case I2C_SLAVE_STOP:
70+
complete(&tu->alert_done);
71+
break;
72+
73+
case I2C_SLAVE_WRITE_REQUESTED:
74+
case I2C_SLAVE_WRITE_RECEIVED:
75+
return -EOPNOTSUPP;
76+
}
77+
78+
return 0;
79+
}
80+
5181
static int i2c_slave_testunit_slave_cb(struct i2c_client *client,
5282
enum i2c_slave_event event, u8 *val)
5383
{
@@ -127,8 +157,10 @@ static int i2c_slave_testunit_slave_cb(struct i2c_client *client,
127157
static void i2c_slave_testunit_work(struct work_struct *work)
128158
{
129159
struct testunit_data *tu = container_of(work, struct testunit_data, worker.work);
160+
unsigned long time_left;
130161
struct i2c_msg msg;
131162
u8 msgbuf[256];
163+
u16 orig_addr;
132164
int ret = 0;
133165

134166
msg.addr = I2C_CLIENT_END;
@@ -150,6 +182,26 @@ static void i2c_slave_testunit_work(struct work_struct *work)
150182
msgbuf[2] = tu->regs[TU_REG_DATAH];
151183
break;
152184

185+
case TU_CMD_SMBUS_ALERT_REQUEST:
186+
i2c_slave_unregister(tu->client);
187+
orig_addr = tu->client->addr;
188+
tu->client->addr = 0x0c;
189+
ret = i2c_slave_register(tu->client, i2c_slave_testunit_smbalert_cb);
190+
if (ret)
191+
goto out_smbalert;
192+
193+
reinit_completion(&tu->alert_done);
194+
gpiod_set_value(tu->gpio, 1);
195+
time_left = wait_for_completion_timeout(&tu->alert_done, HZ);
196+
if (!time_left)
197+
ret = -ETIMEDOUT;
198+
199+
i2c_slave_unregister(tu->client);
200+
out_smbalert:
201+
tu->client->addr = orig_addr;
202+
i2c_slave_register(tu->client, i2c_slave_testunit_slave_cb);
203+
break;
204+
153205
default:
154206
break;
155207
}
@@ -176,8 +228,15 @@ static int i2c_slave_testunit_probe(struct i2c_client *client)
176228

177229
tu->client = client;
178230
i2c_set_clientdata(client, tu);
231+
init_completion(&tu->alert_done);
179232
INIT_DELAYED_WORK(&tu->worker, i2c_slave_testunit_work);
180233

234+
tu->gpio = devm_gpiod_get_index_optional(&client->dev, NULL, 0, GPIOD_OUT_LOW);
235+
if (gpiod_cansleep(tu->gpio)) {
236+
dev_err(&client->dev, "GPIO access which may sleep is not allowed\n");
237+
return -EDEADLK;
238+
}
239+
181240
if (sizeof(tu_version_info) > TU_VERSION_MAX_LENGTH)
182241
tu_version_info[TU_VERSION_MAX_LENGTH - 1] = 0;
183242

0 commit comments

Comments
 (0)