From 14911c6f48ec9571343ac36ae02f2db68bf9e7f9 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Tue, 28 Nov 2017 16:53:32 +0100 Subject: i2c: gpio: add fault injector Add fault injection capabilities to the i2c-gpio driver. When connected to another I2C bus, it can create unusual states which the other I2C bus master driver needs to handle. Only for debugging! Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-gpio.c | 111 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) (limited to 'drivers/i2c/busses/i2c-gpio.c') diff --git a/drivers/i2c/busses/i2c-gpio.c b/drivers/i2c/busses/i2c-gpio.c index d80ea6ce91bb..3312ef981cb7 100644 --- a/drivers/i2c/busses/i2c-gpio.c +++ b/drivers/i2c/busses/i2c-gpio.c @@ -7,6 +7,8 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ +#include +#include #include #include #include @@ -23,6 +25,9 @@ struct i2c_gpio_private_data { struct i2c_adapter adap; struct i2c_algo_bit_data bit_data; struct i2c_gpio_platform_data pdata; +#ifdef CONFIG_I2C_GPIO_FAULT_INJECTOR + struct dentry *debug_dir; +#endif }; /* @@ -64,6 +69,108 @@ static int i2c_gpio_getscl(void *data) return gpiod_get_value(priv->scl); } +#ifdef CONFIG_I2C_GPIO_FAULT_INJECTOR +static struct dentry *i2c_gpio_debug_dir; + +#define setsda(bd, val) ((bd)->setsda((bd)->data, val)) +#define setscl(bd, val) ((bd)->setscl((bd)->data, val)) +#define getsda(bd) ((bd)->getsda((bd)->data)) +#define getscl(bd) ((bd)->getscl((bd)->data)) + +#define WIRE_ATTRIBUTE(wire) \ +static int fops_##wire##_get(void *data, u64 *val) \ +{ \ + struct i2c_gpio_private_data *priv = data; \ + \ + i2c_lock_adapter(&priv->adap); \ + *val = get##wire(&priv->bit_data); \ + i2c_unlock_adapter(&priv->adap); \ + return 0; \ +} \ +static int fops_##wire##_set(void *data, u64 val) \ +{ \ + struct i2c_gpio_private_data *priv = data; \ + \ + i2c_lock_adapter(&priv->adap); \ + set##wire(&priv->bit_data, val); \ + i2c_unlock_adapter(&priv->adap); \ + return 0; \ +} \ +DEFINE_DEBUGFS_ATTRIBUTE(fops_##wire, fops_##wire##_get, fops_##wire##_set, "%llu\n") + +WIRE_ATTRIBUTE(scl); +WIRE_ATTRIBUTE(sda); + +static int fops_incomplete_transfer_set(void *data, u64 addr) +{ + struct i2c_gpio_private_data *priv = data; + struct i2c_algo_bit_data *bit_data = &priv->bit_data; + int i, pattern; + + if (addr > 0x7f) + return -EINVAL; + + /* ADDR (7 bit) + RD (1 bit) + SDA hi (1 bit) */ + pattern = (addr << 2) | 3; + + i2c_lock_adapter(&priv->adap); + + /* START condition */ + setsda(bit_data, 0); + udelay(bit_data->udelay); + + /* Send ADDR+RD, request ACK, don't send STOP */ + for (i = 8; i >= 0; i--) { + setscl(bit_data, 0); + udelay(bit_data->udelay / 2); + setsda(bit_data, (pattern >> i) & 1); + udelay((bit_data->udelay + 1) / 2); + setscl(bit_data, 1); + udelay(bit_data->udelay); + } + + i2c_unlock_adapter(&priv->adap); + + return 0; +} +DEFINE_DEBUGFS_ATTRIBUTE(fops_incomplete_transfer, NULL, fops_incomplete_transfer_set, "%llu\n"); + +static void i2c_gpio_fault_injector_init(struct platform_device *pdev) +{ + struct i2c_gpio_private_data *priv = platform_get_drvdata(pdev); + + /* + * If there will be a debugfs-dir per i2c adapter somewhen, put the + * 'fault-injector' dir there. Until then, we have a global dir with + * all adapters as subdirs. + */ + if (!i2c_gpio_debug_dir) { + i2c_gpio_debug_dir = debugfs_create_dir("i2c-fault-injector", NULL); + if (!i2c_gpio_debug_dir) + return; + } + + priv->debug_dir = debugfs_create_dir(pdev->name, i2c_gpio_debug_dir); + if (!priv->debug_dir) + return; + + debugfs_create_file_unsafe("scl", 0600, priv->debug_dir, priv, &fops_scl); + debugfs_create_file_unsafe("sda", 0600, priv->debug_dir, priv, &fops_sda); + debugfs_create_file_unsafe("incomplete_transfer", 0200, priv->debug_dir, + priv, &fops_incomplete_transfer); +} + +static void i2c_gpio_fault_injector_exit(struct platform_device *pdev) +{ + struct i2c_gpio_private_data *priv = platform_get_drvdata(pdev); + + debugfs_remove_recursive(priv->debug_dir); +} +#else +static inline void i2c_gpio_fault_injector_init(struct platform_device *pdev) {} +static inline void i2c_gpio_fault_injector_exit(struct platform_device *pdev) {} +#endif /* CONFIG_I2C_GPIO_FAULT_INJECTOR*/ + static void of_i2c_gpio_get_props(struct device_node *np, struct i2c_gpio_platform_data *pdata) { @@ -228,6 +335,8 @@ static int i2c_gpio_probe(struct platform_device *pdev) pdata->scl_is_output_only ? ", no clock stretching" : ""); + i2c_gpio_fault_injector_init(pdev); + return 0; } @@ -236,6 +345,8 @@ static int i2c_gpio_remove(struct platform_device *pdev) struct i2c_gpio_private_data *priv; struct i2c_adapter *adap; + i2c_gpio_fault_injector_exit(pdev); + priv = platform_get_drvdata(pdev); adap = &priv->adap; -- cgit From f11a04464ae57e8db1bb7634547842b43e36a898 Mon Sep 17 00:00:00 2001 From: Jan Kundrát Date: Fri, 22 Dec 2017 22:47:16 +0100 Subject: i2c: gpio: Enable working over slow can_sleep GPIOs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "Slow" GPIOs (usually those connected over an SPI or an I2C bus) are, well, slow in their operation. It is generally a good idea to avoid using them for time-critical operation, but sometimes the hardware just sucks, and the software has to cope. In addition to that, the I2C bus itself does not actually define any strict timing limits; the bus is free to go all the way down to DC. The timeouts (and therefore the slowest acceptable frequency) are present only in SMBus. The `can_sleep` is IMHO a wrong concept to use here. My SPI-to-quad-UART chip (MAX14830) is connected via a 26MHz SPI bus, and it happily drives SCL at 200kHz (5µs pulses) during my benchmarks. That's faster than the maximal allowed speed of the traditional I2C. The previous version of this code did not really block operation over slow GPIO pins, anyway. Instead, it just resorted to printing a warning with a backtrace each time a GPIO pin was accessed, thereby slowing things down even more. Finally, it's not just me. A similar patch was originally submitted in 2015 [1]. [1] https://patchwork.ozlabs.org/patch/450956/ Signed-off-by: Jan Kundrát Acked-by: Uwe Kleine-König Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-gpio.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'drivers/i2c/busses/i2c-gpio.c') diff --git a/drivers/i2c/busses/i2c-gpio.c b/drivers/i2c/busses/i2c-gpio.c index 3312ef981cb7..58abb3eced58 100644 --- a/drivers/i2c/busses/i2c-gpio.c +++ b/drivers/i2c/busses/i2c-gpio.c @@ -39,7 +39,7 @@ static void i2c_gpio_setsda_val(void *data, int state) { struct i2c_gpio_private_data *priv = data; - gpiod_set_value(priv->sda, state); + gpiod_set_value_cansleep(priv->sda, state); } /* @@ -52,21 +52,21 @@ static void i2c_gpio_setscl_val(void *data, int state) { struct i2c_gpio_private_data *priv = data; - gpiod_set_value(priv->scl, state); + gpiod_set_value_cansleep(priv->scl, state); } static int i2c_gpio_getsda(void *data) { struct i2c_gpio_private_data *priv = data; - return gpiod_get_value(priv->sda); + return gpiod_get_value_cansleep(priv->sda); } static int i2c_gpio_getscl(void *data) { struct i2c_gpio_private_data *priv = data; - return gpiod_get_value(priv->scl); + return gpiod_get_value_cansleep(priv->scl); } #ifdef CONFIG_I2C_GPIO_FAULT_INJECTOR @@ -286,6 +286,9 @@ static int i2c_gpio_probe(struct platform_device *pdev) if (IS_ERR(priv->scl)) return PTR_ERR(priv->scl); + if (gpiod_cansleep(priv->sda) || gpiod_cansleep(priv->scl)) + dev_warn(dev, "Slow GPIO pins might wreak havoc into I2C/SMBus bus timing"); + bit_data->setsda = i2c_gpio_setsda_val; bit_data->setscl = i2c_gpio_setscl_val; -- cgit