diff options
author | Wolfram Sang <wsa+renesas@sang-engineering.com> | 2019-02-19 11:39:45 -0500 |
---|---|---|
committer | Wolfram Sang <wsa@the-dreams.de> | 2019-02-23 04:33:51 -0500 |
commit | 63e57b6f191db99ffdd0dc6c7b9ee6b2cf7abb04 (patch) | |
tree | 9a2544d399233f0e601dd5a0b7260be0aa8a9089 | |
parent | 6b9932bc28fd61b83a8cb86bf0de1a86e5c18fa3 (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-injection | 25 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-gpio.c | 74 |
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 | |||
83 | additional STOP conditions to ensure the bus has been released. Otherwise | 83 | additional STOP conditions to ensure the bus has been released. Otherwise |
84 | random data will be written to a device! | 84 | random data will be written to a device! |
85 | 85 | ||
86 | Lost arbitration | ||
87 | ================ | ||
88 | |||
89 | Here, we want to simulate the condition where the master under test loses the | ||
90 | bus arbitration against another master in a multi-master setup. | ||
91 | |||
92 | "lose_arbitration" | ||
93 | ------------------ | ||
94 | |||
95 | This file is write only and you need to write the duration of the arbitration | ||
96 | intereference (in µs, maximum is 100ms). The calling process will then sleep | ||
97 | and wait for the next bus clock. The process is interruptible, though. | ||
98 | |||
99 | Arbitration lost is achieved by waiting for SCL going down by the master under | ||
100 | test and then pulling SDA low for some time. So, the I2C address sent out | ||
101 | should be corrupted and that should be detected properly. That means that the | ||
102 | address sent out should have a lot of '1' bits to be able to detect corruption. | ||
103 | There doesn't need to be a device at this address because arbitration lost | ||
104 | should be detected beforehand. Also note, that SCL going down is monitored | ||
105 | using interrupts, so the interrupt latency might cause the first bits to be not | ||
106 | corrupted. A good starting point for using this fault injector on an otherwise | ||
107 | idle 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 | } |
163 | DEFINE_DEBUGFS_ATTRIBUTE(fops_incomplete_write_byte, NULL, fops_incomplete_write_byte_set, "%llu\n"); | 168 | DEFINE_DEBUGFS_ATTRIBUTE(fops_incomplete_write_byte, NULL, fops_incomplete_write_byte_set, "%llu\n"); |
164 | 169 | ||
170 | static 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 | |||
202 | static 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 | |||
215 | static 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 | } | ||
232 | DEFINE_DEBUGFS_ATTRIBUTE(fops_lose_arbitration, NULL, fops_lose_arbitration_set, "%llu\n"); | ||
233 | |||
165 | static void i2c_gpio_fault_injector_init(struct platform_device *pdev) | 234 | static 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 | } |