mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-12-19 10:44:14 +08:00
i2c: gpio: fault-injector: add 'lose_arbitration' injector
Add a fault injector simulating 'arbitration lost' from multi-master setups. Read the docs for its usage. A helper function for future fault injectors using SCL interrupts is created to achieve this. Signed-off-by: Wolfram Sang <wsa+renesas@sang-engineering.com> Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
This commit is contained in:
parent
6b9932bc28
commit
63e57b6f19
@ -83,3 +83,28 @@ This is why bus recovery (up to 9 clock pulses) must either check SDA or send
|
||||
additional STOP conditions to ensure the bus has been released. Otherwise
|
||||
random data will be written to a device!
|
||||
|
||||
Lost arbitration
|
||||
================
|
||||
|
||||
Here, we want to simulate the condition where the master under test loses the
|
||||
bus arbitration against another master in a multi-master setup.
|
||||
|
||||
"lose_arbitration"
|
||||
------------------
|
||||
|
||||
This file is write only and you need to write the duration of the arbitration
|
||||
intereference (in µs, maximum is 100ms). The calling process will then sleep
|
||||
and wait for the next bus clock. The process is interruptible, though.
|
||||
|
||||
Arbitration lost is achieved by waiting for SCL going down by the master under
|
||||
test and then pulling SDA low for some time. So, the I2C address sent out
|
||||
should be corrupted and that should be detected properly. That means that the
|
||||
address sent out should have a lot of '1' bits to be able to detect corruption.
|
||||
There doesn't need to be a device at this address because arbitration lost
|
||||
should be detected beforehand. Also note, that SCL going down is monitored
|
||||
using interrupts, so the interrupt latency might cause the first bits to be not
|
||||
corrupted. A good starting point for using this fault injector on an otherwise
|
||||
idle bus is:
|
||||
|
||||
# echo 200 > lose_arbitration &
|
||||
# i2cget -y <bus_to_test> 0x3f
|
||||
|
@ -7,12 +7,14 @@
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#include <linux/completion.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/i2c-algo-bit.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_data/i2c-gpio.h>
|
||||
@ -27,6 +29,9 @@ struct i2c_gpio_private_data {
|
||||
struct i2c_gpio_platform_data pdata;
|
||||
#ifdef CONFIG_I2C_GPIO_FAULT_INJECTOR
|
||||
struct dentry *debug_dir;
|
||||
/* these must be protected by bus lock */
|
||||
struct completion scl_irq_completion;
|
||||
u64 scl_irq_data;
|
||||
#endif
|
||||
};
|
||||
|
||||
@ -162,6 +167,70 @@ static int fops_incomplete_write_byte_set(void *data, u64 addr)
|
||||
}
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(fops_incomplete_write_byte, NULL, fops_incomplete_write_byte_set, "%llu\n");
|
||||
|
||||
static int i2c_gpio_fi_act_on_scl_irq(struct i2c_gpio_private_data *priv,
|
||||
irqreturn_t handler(int, void*))
|
||||
{
|
||||
int ret, irq = gpiod_to_irq(priv->scl);
|
||||
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
i2c_lock_bus(&priv->adap, I2C_LOCK_ROOT_ADAPTER);
|
||||
|
||||
ret = gpiod_direction_input(priv->scl);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
|
||||
reinit_completion(&priv->scl_irq_completion);
|
||||
|
||||
ret = request_irq(irq, handler, IRQF_TRIGGER_FALLING,
|
||||
"i2c_gpio_fault_injector_scl_irq", priv);
|
||||
if (ret)
|
||||
goto output;
|
||||
|
||||
wait_for_completion_interruptible(&priv->scl_irq_completion);
|
||||
|
||||
free_irq(irq, priv);
|
||||
output:
|
||||
ret = gpiod_direction_output(priv->scl, 1) ?: ret;
|
||||
unlock:
|
||||
i2c_unlock_bus(&priv->adap, I2C_LOCK_ROOT_ADAPTER);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static irqreturn_t lose_arbitration_irq(int irq, void *dev_id)
|
||||
{
|
||||
struct i2c_gpio_private_data *priv = dev_id;
|
||||
|
||||
setsda(&priv->bit_data, 0);
|
||||
udelay(priv->scl_irq_data);
|
||||
setsda(&priv->bit_data, 1);
|
||||
|
||||
complete(&priv->scl_irq_completion);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int fops_lose_arbitration_set(void *data, u64 duration)
|
||||
{
|
||||
struct i2c_gpio_private_data *priv = data;
|
||||
|
||||
if (duration > 100 * 1000)
|
||||
return -EINVAL;
|
||||
|
||||
priv->scl_irq_data = duration;
|
||||
/*
|
||||
* Interrupt on falling SCL. This ensures that the master under test has
|
||||
* really started the transfer. Interrupt on falling SDA did only
|
||||
* exercise 'bus busy' detection on some HW but not 'arbitration lost'.
|
||||
* Note that the interrupt latency may cause the first bits to be
|
||||
* transmitted correctly.
|
||||
*/
|
||||
return i2c_gpio_fi_act_on_scl_irq(priv, lose_arbitration_irq);
|
||||
}
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(fops_lose_arbitration, NULL, fops_lose_arbitration_set, "%llu\n");
|
||||
|
||||
static void i2c_gpio_fault_injector_init(struct platform_device *pdev)
|
||||
{
|
||||
struct i2c_gpio_private_data *priv = platform_get_drvdata(pdev);
|
||||
@ -181,10 +250,15 @@ static void i2c_gpio_fault_injector_init(struct platform_device *pdev)
|
||||
if (!priv->debug_dir)
|
||||
return;
|
||||
|
||||
init_completion(&priv->scl_irq_completion);
|
||||
|
||||
debugfs_create_file_unsafe("incomplete_address_phase", 0200, priv->debug_dir,
|
||||
priv, &fops_incomplete_addr_phase);
|
||||
debugfs_create_file_unsafe("incomplete_write_byte", 0200, priv->debug_dir,
|
||||
priv, &fops_incomplete_write_byte);
|
||||
if (priv->bit_data.getscl)
|
||||
debugfs_create_file_unsafe("lose_arbitration", 0200, priv->debug_dir,
|
||||
priv, &fops_lose_arbitration);
|
||||
debugfs_create_file_unsafe("scl", 0600, priv->debug_dir, priv, &fops_scl);
|
||||
debugfs_create_file_unsafe("sda", 0600, priv->debug_dir, priv, &fops_sda);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user