aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/i2c
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 /drivers/i2c
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>
Diffstat (limited to 'drivers/i2c')
-rw-r--r--drivers/i2c/busses/i2c-gpio.c74
1 files changed, 74 insertions, 0 deletions
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}