summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWolfram Sang <wsa+renesas@sang-engineering.com>2019-02-19 11:39:45 -0500
committerWolfram Sang <wsa@the-dreams.de>2019-02-23 04:33:51 -0500
commit63e57b6f191db99ffdd0dc6c7b9ee6b2cf7abb04 (patch)
tree9a2544d399233f0e601dd5a0b7260be0aa8a9089
parent6b9932bc28fd61b83a8cb86bf0de1a86e5c18fa3 (diff)
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>
-rw-r--r--Documentation/i2c/gpio-fault-injection25
-rw-r--r--drivers/i2c/busses/i2c-gpio.c74
2 files changed, 99 insertions, 0 deletions
diff --git a/Documentation/i2c/gpio-fault-injection b/Documentation/i2c/gpio-fault-injection
index 1a44e3edc0c4..1f1bb96a64bd 100644
--- a/Documentation/i2c/gpio-fault-injection
+++ b/Documentation/i2c/gpio-fault-injection
@@ -83,3 +83,28 @@ This is why bus recovery (up to 9 clock pulses) must either check SDA or send
83additional STOP conditions to ensure the bus has been released. Otherwise 83additional STOP conditions to ensure the bus has been released. Otherwise
84random data will be written to a device! 84random data will be written to a device!
85 85
86Lost arbitration
87================
88
89Here, we want to simulate the condition where the master under test loses the
90bus arbitration against another master in a multi-master setup.
91
92"lose_arbitration"
93------------------
94
95This file is write only and you need to write the duration of the arbitration
96intereference (in µs, maximum is 100ms). The calling process will then sleep
97and wait for the next bus clock. The process is interruptible, though.
98
99Arbitration lost is achieved by waiting for SCL going down by the master under
100test and then pulling SDA low for some time. So, the I2C address sent out
101should be corrupted and that should be detected properly. That means that the
102address sent out should have a lot of '1' bits to be able to detect corruption.
103There doesn't need to be a device at this address because arbitration lost
104should be detected beforehand. Also note, that SCL going down is monitored
105using interrupts, so the interrupt latency might cause the first bits to be not
106corrupted. A good starting point for using this fault injector on an otherwise
107idle bus is:
108
109# echo 200 > lose_arbitration &
110# i2cget -y <bus_to_test> 0x3f
diff --git a/drivers/i2c/busses/i2c-gpio.c b/drivers/i2c/busses/i2c-gpio.c
index 2d532cc042f5..76e43783f50f 100644
--- a/drivers/i2c/busses/i2c-gpio.c
+++ b/drivers/i2c/busses/i2c-gpio.c
@@ -7,12 +7,14 @@
7 * it under the terms of the GNU General Public License version 2 as 7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation. 8 * published by the Free Software Foundation.
9 */ 9 */
10#include <linux/completion.h>
10#include <linux/debugfs.h> 11#include <linux/debugfs.h>
11#include <linux/delay.h> 12#include <linux/delay.h>
12#include <linux/gpio/consumer.h> 13#include <linux/gpio/consumer.h>
13#include <linux/i2c-algo-bit.h> 14#include <linux/i2c-algo-bit.h>
14#include <linux/i2c.h> 15#include <linux/i2c.h>
15#include <linux/init.h> 16#include <linux/init.h>
17#include <linux/interrupt.h>
16#include <linux/module.h> 18#include <linux/module.h>
17#include <linux/of.h> 19#include <linux/of.h>
18#include <linux/platform_data/i2c-gpio.h> 20#include <linux/platform_data/i2c-gpio.h>
@@ -27,6 +29,9 @@ struct i2c_gpio_private_data {
27 struct i2c_gpio_platform_data pdata; 29 struct i2c_gpio_platform_data pdata;
28#ifdef CONFIG_I2C_GPIO_FAULT_INJECTOR 30#ifdef CONFIG_I2C_GPIO_FAULT_INJECTOR
29 struct dentry *debug_dir; 31 struct dentry *debug_dir;
32 /* these must be protected by bus lock */
33 struct completion scl_irq_completion;
34 u64 scl_irq_data;
30#endif 35#endif
31}; 36};
32 37
@@ -162,6 +167,70 @@ static int fops_incomplete_write_byte_set(void *data, u64 addr)
162} 167}
163DEFINE_DEBUGFS_ATTRIBUTE(fops_incomplete_write_byte, NULL, fops_incomplete_write_byte_set, "%llu\n"); 168DEFINE_DEBUGFS_ATTRIBUTE(fops_incomplete_write_byte, NULL, fops_incomplete_write_byte_set, "%llu\n");
164 169
170static int i2c_gpio_fi_act_on_scl_irq(struct i2c_gpio_private_data *priv,
171 irqreturn_t handler(int, void*))
172{
173 int ret, irq = gpiod_to_irq(priv->scl);
174
175 if (irq < 0)
176 return irq;
177
178 i2c_lock_bus(&priv->adap, I2C_LOCK_ROOT_ADAPTER);
179
180 ret = gpiod_direction_input(priv->scl);
181 if (ret)
182 goto unlock;
183
184 reinit_completion(&priv->scl_irq_completion);
185
186 ret = request_irq(irq, handler, IRQF_TRIGGER_FALLING,
187 "i2c_gpio_fault_injector_scl_irq", priv);
188 if (ret)
189 goto output;
190
191 wait_for_completion_interruptible(&priv->scl_irq_completion);
192
193 free_irq(irq, priv);
194 output:
195 ret = gpiod_direction_output(priv->scl, 1) ?: ret;
196 unlock:
197 i2c_unlock_bus(&priv->adap, I2C_LOCK_ROOT_ADAPTER);
198
199 return ret;
200}
201
202static irqreturn_t lose_arbitration_irq(int irq, void *dev_id)
203{
204 struct i2c_gpio_private_data *priv = dev_id;
205
206 setsda(&priv->bit_data, 0);
207 udelay(priv->scl_irq_data);
208 setsda(&priv->bit_data, 1);
209
210 complete(&priv->scl_irq_completion);
211
212 return IRQ_HANDLED;
213}
214
215static int fops_lose_arbitration_set(void *data, u64 duration)
216{
217 struct i2c_gpio_private_data *priv = data;
218
219 if (duration > 100 * 1000)
220 return -EINVAL;
221
222 priv->scl_irq_data = duration;
223 /*
224 * Interrupt on falling SCL. This ensures that the master under test has
225 * really started the transfer. Interrupt on falling SDA did only
226 * exercise 'bus busy' detection on some HW but not 'arbitration lost'.
227 * Note that the interrupt latency may cause the first bits to be
228 * transmitted correctly.
229 */
230 return i2c_gpio_fi_act_on_scl_irq(priv, lose_arbitration_irq);
231}
232DEFINE_DEBUGFS_ATTRIBUTE(fops_lose_arbitration, NULL, fops_lose_arbitration_set, "%llu\n");
233
165static void i2c_gpio_fault_injector_init(struct platform_device *pdev) 234static void i2c_gpio_fault_injector_init(struct platform_device *pdev)
166{ 235{
167 struct i2c_gpio_private_data *priv = platform_get_drvdata(pdev); 236 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)
181 if (!priv->debug_dir) 250 if (!priv->debug_dir)
182 return; 251 return;
183 252
253 init_completion(&priv->scl_irq_completion);
254
184 debugfs_create_file_unsafe("incomplete_address_phase", 0200, priv->debug_dir, 255 debugfs_create_file_unsafe("incomplete_address_phase", 0200, priv->debug_dir,
185 priv, &fops_incomplete_addr_phase); 256 priv, &fops_incomplete_addr_phase);
186 debugfs_create_file_unsafe("incomplete_write_byte", 0200, priv->debug_dir, 257 debugfs_create_file_unsafe("incomplete_write_byte", 0200, priv->debug_dir,
187 priv, &fops_incomplete_write_byte); 258 priv, &fops_incomplete_write_byte);
259 if (priv->bit_data.getscl)
260 debugfs_create_file_unsafe("lose_arbitration", 0200, priv->debug_dir,
261 priv, &fops_lose_arbitration);
188 debugfs_create_file_unsafe("scl", 0600, priv->debug_dir, priv, &fops_scl); 262 debugfs_create_file_unsafe("scl", 0600, priv->debug_dir, priv, &fops_scl);
189 debugfs_create_file_unsafe("sda", 0600, priv->debug_dir, priv, &fops_sda); 263 debugfs_create_file_unsafe("sda", 0600, priv->debug_dir, priv, &fops_sda);
190} 264}