mirror of
git://git.yoctoproject.org/linux-yocto.git
synced 2025-07-05 13:25:20 +02:00
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 <wsa+renesas@sang-engineering.com>
This commit is contained in:
parent
06e12ae5f0
commit
3d16973f77
|
@ -185,3 +185,51 @@ default response::
|
||||||
|
|
||||||
# i2cset -y 0 0x30 4 0 0 i; i2cget -y 0 0x30
|
# i2cset -y 0 0x30 4 0 0 i; i2cget -y 0 0x30
|
||||||
0x00
|
0x00
|
||||||
|
|
||||||
|
0x05 SMBUS_ALERT_REQUEST
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
.. list-table::
|
||||||
|
:header-rows: 1
|
||||||
|
|
||||||
|
* - CMD
|
||||||
|
- DATAL
|
||||||
|
- DATAH
|
||||||
|
- DELAY
|
||||||
|
|
||||||
|
* - 0x05
|
||||||
|
- response value (7 MSBs interpreted as I2C address)
|
||||||
|
- currently unused
|
||||||
|
- n * 10ms
|
||||||
|
|
||||||
|
This test raises an interrupt via the SMBAlert pin which the host controller
|
||||||
|
must handle. The pin must be connected to the testunit as a GPIO. GPIO access
|
||||||
|
is not allowed to sleep. Currently, this can only be described using firmware
|
||||||
|
nodes. So, for devicetree, you would add something like this to the testunit
|
||||||
|
node::
|
||||||
|
|
||||||
|
gpios = <&gpio1 24 GPIO_ACTIVE_LOW>;
|
||||||
|
|
||||||
|
The following command will trigger the alert with a response of 0xc9 after 1
|
||||||
|
second of delay::
|
||||||
|
|
||||||
|
# i2cset -y 0 0x30 5 0xc9 0x00 100 i
|
||||||
|
|
||||||
|
If the host controller supports SMBusAlert, this message with debug level
|
||||||
|
should appear::
|
||||||
|
|
||||||
|
smbus_alert 0-000c: SMBALERT# from dev 0x64, flag 1
|
||||||
|
|
||||||
|
This message may appear more than once because the testunit is software not
|
||||||
|
hardware and, thus, may not be able to react to the response of the host fast
|
||||||
|
enough. The interrupt count should increase only by one, though::
|
||||||
|
|
||||||
|
# cat /proc/interrupts | grep smbus_alert
|
||||||
|
93: 1 gpio-rcar 26 Edge smbus_alert
|
||||||
|
|
||||||
|
If the host does not respond to the alert within 1 second, the test will be
|
||||||
|
aborted and the testunit will report an error.
|
||||||
|
|
||||||
|
For this test, the testunit will shortly drop its assigned address and listen
|
||||||
|
on the SMBus Alert Response Address (0x0c). It will reassign its original
|
||||||
|
address afterwards.
|
||||||
|
|
|
@ -8,6 +8,8 @@
|
||||||
|
|
||||||
#include <generated/utsrelease.h>
|
#include <generated/utsrelease.h>
|
||||||
#include <linux/bitops.h>
|
#include <linux/bitops.h>
|
||||||
|
#include <linux/completion.h>
|
||||||
|
#include <linux/gpio/consumer.h>
|
||||||
#include <linux/i2c.h>
|
#include <linux/i2c.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
@ -22,6 +24,7 @@ enum testunit_cmds {
|
||||||
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_CMD_GET_VERSION_WITH_REP_START,
|
||||||
|
TU_CMD_SMBUS_ALERT_REQUEST,
|
||||||
TU_NUM_CMDS
|
TU_NUM_CMDS
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -44,10 +47,37 @@ struct testunit_data {
|
||||||
u8 read_idx;
|
u8 read_idx;
|
||||||
struct i2c_client *client;
|
struct i2c_client *client;
|
||||||
struct delayed_work worker;
|
struct delayed_work worker;
|
||||||
|
struct gpio_desc *gpio;
|
||||||
|
struct completion alert_done;
|
||||||
};
|
};
|
||||||
|
|
||||||
static char tu_version_info[] = "v" UTS_RELEASE "\n\0";
|
static char tu_version_info[] = "v" UTS_RELEASE "\n\0";
|
||||||
|
|
||||||
|
static int i2c_slave_testunit_smbalert_cb(struct i2c_client *client,
|
||||||
|
enum i2c_slave_event event, u8 *val)
|
||||||
|
{
|
||||||
|
struct testunit_data *tu = i2c_get_clientdata(client);
|
||||||
|
|
||||||
|
switch (event) {
|
||||||
|
case I2C_SLAVE_READ_PROCESSED:
|
||||||
|
gpiod_set_value(tu->gpio, 0);
|
||||||
|
fallthrough;
|
||||||
|
case I2C_SLAVE_READ_REQUESTED:
|
||||||
|
*val = tu->regs[TU_REG_DATAL];
|
||||||
|
break;
|
||||||
|
|
||||||
|
case I2C_SLAVE_STOP:
|
||||||
|
complete(&tu->alert_done);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case I2C_SLAVE_WRITE_REQUESTED:
|
||||||
|
case I2C_SLAVE_WRITE_RECEIVED:
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int i2c_slave_testunit_slave_cb(struct i2c_client *client,
|
static int i2c_slave_testunit_slave_cb(struct i2c_client *client,
|
||||||
enum i2c_slave_event event, u8 *val)
|
enum i2c_slave_event event, u8 *val)
|
||||||
{
|
{
|
||||||
|
@ -127,8 +157,10 @@ static int i2c_slave_testunit_slave_cb(struct i2c_client *client,
|
||||||
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);
|
||||||
|
unsigned long time_left;
|
||||||
struct i2c_msg msg;
|
struct i2c_msg msg;
|
||||||
u8 msgbuf[256];
|
u8 msgbuf[256];
|
||||||
|
u16 orig_addr;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
msg.addr = I2C_CLIENT_END;
|
msg.addr = I2C_CLIENT_END;
|
||||||
|
@ -150,6 +182,26 @@ static void i2c_slave_testunit_work(struct work_struct *work)
|
||||||
msgbuf[2] = tu->regs[TU_REG_DATAH];
|
msgbuf[2] = tu->regs[TU_REG_DATAH];
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case TU_CMD_SMBUS_ALERT_REQUEST:
|
||||||
|
i2c_slave_unregister(tu->client);
|
||||||
|
orig_addr = tu->client->addr;
|
||||||
|
tu->client->addr = 0x0c;
|
||||||
|
ret = i2c_slave_register(tu->client, i2c_slave_testunit_smbalert_cb);
|
||||||
|
if (ret)
|
||||||
|
goto out_smbalert;
|
||||||
|
|
||||||
|
reinit_completion(&tu->alert_done);
|
||||||
|
gpiod_set_value(tu->gpio, 1);
|
||||||
|
time_left = wait_for_completion_timeout(&tu->alert_done, HZ);
|
||||||
|
if (!time_left)
|
||||||
|
ret = -ETIMEDOUT;
|
||||||
|
|
||||||
|
i2c_slave_unregister(tu->client);
|
||||||
|
out_smbalert:
|
||||||
|
tu->client->addr = orig_addr;
|
||||||
|
i2c_slave_register(tu->client, i2c_slave_testunit_slave_cb);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -176,8 +228,15 @@ static int i2c_slave_testunit_probe(struct i2c_client *client)
|
||||||
|
|
||||||
tu->client = client;
|
tu->client = client;
|
||||||
i2c_set_clientdata(client, tu);
|
i2c_set_clientdata(client, tu);
|
||||||
|
init_completion(&tu->alert_done);
|
||||||
INIT_DELAYED_WORK(&tu->worker, i2c_slave_testunit_work);
|
INIT_DELAYED_WORK(&tu->worker, i2c_slave_testunit_work);
|
||||||
|
|
||||||
|
tu->gpio = devm_gpiod_get_index_optional(&client->dev, NULL, 0, GPIOD_OUT_LOW);
|
||||||
|
if (gpiod_cansleep(tu->gpio)) {
|
||||||
|
dev_err(&client->dev, "GPIO access which may sleep is not allowed\n");
|
||||||
|
return -EDEADLK;
|
||||||
|
}
|
||||||
|
|
||||||
if (sizeof(tu_version_info) > TU_VERSION_MAX_LENGTH)
|
if (sizeof(tu_version_info) > TU_VERSION_MAX_LENGTH)
|
||||||
tu_version_info[TU_VERSION_MAX_LENGTH - 1] = 0;
|
tu_version_info[TU_VERSION_MAX_LENGTH - 1] = 0;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user