aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/i2c/i2c-arb-gpio-challenge.txt80
-rw-r--r--drivers/i2c/muxes/Kconfig12
-rw-r--r--drivers/i2c/muxes/Makefile2
-rw-r--r--drivers/i2c/muxes/i2c-arb-gpio-challenge.c251
4 files changed, 345 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/i2c/i2c-arb-gpio-challenge.txt b/Documentation/devicetree/bindings/i2c/i2c-arb-gpio-challenge.txt
new file mode 100644
index 000000000000..1ac8ea8ade1d
--- /dev/null
+++ b/Documentation/devicetree/bindings/i2c/i2c-arb-gpio-challenge.txt
@@ -0,0 +1,80 @@
1GPIO-based I2C Arbitration Using a Challenge & Response Mechanism
2=================================================================
3This uses GPIO lines and a challenge & response mechanism to arbitrate who is
4the master of an I2C bus in a multimaster situation.
5
6In many cases using GPIOs to arbitrate is not needed and a design can use
7the standard I2C multi-master rules. Using GPIOs is generally useful in
8the case where there is a device on the bus that has errata and/or bugs
9that makes standard multimaster mode not feasible.
10
11
12Algorithm:
13
14All masters on the bus have a 'bus claim' line which is an output that the
15others can see. These are all active low with pull-ups enabled. We'll
16describe these lines as:
17
18- OUR_CLAIM: output from us signaling to other hosts that we want the bus
19- THEIR_CLAIMS: output from others signaling that they want the bus
20
21The basic algorithm is to assert your line when you want the bus, then make
22sure that the other side doesn't want it also. A detailed explanation is best
23done with an example.
24
25Let's say we want to claim the bus. We:
261. Assert OUR_CLAIM.
272. Waits a little bit for the other sides to notice (slew time, say 10
28 microseconds).
293. Check THEIR_CLAIMS. If none are asserted then the we have the bus and we are
30 done.
314. Otherwise, wait for a few milliseconds and see if THEIR_CLAIMS are released.
325. If not, back off, release the claim and wait for a few more milliseconds.
336. Go back to 1 (until retry time has expired).
34
35
36Required properties:
37- compatible: i2c-arb-gpio-challenge
38- our-claim-gpio: The GPIO that we use to claim the bus.
39- their-claim-gpios: The GPIOs that the other sides use to claim the bus.
40 Note that some implementations may only support a single other master.
41- Standard I2C mux properties. See mux.txt in this directory.
42- Single I2C child bus node at reg 0. See mux.txt in this directory.
43
44Optional properties:
45- slew-delay-us: microseconds to wait for a GPIO to go high. Default is 10 us.
46- wait-retry-us: we'll attempt another claim after this many microseconds.
47 Default is 3000 us.
48- wait-free-us: we'll give up after this many microseconds. Default is 50000 us.
49
50
51Example:
52 i2c@12CA0000 {
53 compatible = "acme,some-i2c-device";
54 #address-cells = <1>;
55 #size-cells = <0>;
56 };
57
58 i2c-arbitrator {
59 compatible = "i2c-arb-gpio-challenge";
60 #address-cells = <1>;
61 #size-cells = <0>;
62
63 i2c-parent = <&{/i2c@12CA0000}>;
64
65 our-claim-gpio = <&gpf0 3 1>;
66 their-claim-gpios = <&gpe0 4 1>;
67 slew-delay-us = <10>;
68 wait-retry-us = <3000>;
69 wait-free-us = <50000>;
70
71 i2c@0 {
72 reg = <0>;
73 #address-cells = <1>;
74 #size-cells = <0>;
75
76 i2c@52 {
77 // Normal I2C device
78 };
79 };
80 };
diff --git a/drivers/i2c/muxes/Kconfig b/drivers/i2c/muxes/Kconfig
index 0be5b83c08fa..5faf244d2476 100644
--- a/drivers/i2c/muxes/Kconfig
+++ b/drivers/i2c/muxes/Kconfig
@@ -5,6 +5,18 @@
5menu "Multiplexer I2C Chip support" 5menu "Multiplexer I2C Chip support"
6 depends on I2C_MUX 6 depends on I2C_MUX
7 7
8config I2C_ARB_GPIO_CHALLENGE
9 tristate "GPIO-based I2C arbitration"
10 depends on GENERIC_GPIO && OF
11 help
12 If you say yes to this option, support will be included for an
13 I2C multimaster arbitration scheme using GPIOs and a challenge &
14 response mechanism where masters have to claim the bus by asserting
15 a GPIO.
16
17 This driver can also be built as a module. If so, the module
18 will be called i2c-arb-gpio-challenge.
19
8config I2C_MUX_GPIO 20config I2C_MUX_GPIO
9 tristate "GPIO-based I2C multiplexer" 21 tristate "GPIO-based I2C multiplexer"
10 depends on GENERIC_GPIO 22 depends on GENERIC_GPIO
diff --git a/drivers/i2c/muxes/Makefile b/drivers/i2c/muxes/Makefile
index 76da8692afff..465778b5d5dc 100644
--- a/drivers/i2c/muxes/Makefile
+++ b/drivers/i2c/muxes/Makefile
@@ -1,6 +1,8 @@
1# 1#
2# Makefile for multiplexer I2C chip drivers. 2# Makefile for multiplexer I2C chip drivers.
3 3
4obj-$(CONFIG_I2C_ARB_GPIO_CHALLENGE) += i2c-arb-gpio-challenge.o
5
4obj-$(CONFIG_I2C_MUX_GPIO) += i2c-mux-gpio.o 6obj-$(CONFIG_I2C_MUX_GPIO) += i2c-mux-gpio.o
5obj-$(CONFIG_I2C_MUX_PCA9541) += i2c-mux-pca9541.o 7obj-$(CONFIG_I2C_MUX_PCA9541) += i2c-mux-pca9541.o
6obj-$(CONFIG_I2C_MUX_PCA954x) += i2c-mux-pca954x.o 8obj-$(CONFIG_I2C_MUX_PCA954x) += i2c-mux-pca954x.o
diff --git a/drivers/i2c/muxes/i2c-arb-gpio-challenge.c b/drivers/i2c/muxes/i2c-arb-gpio-challenge.c
new file mode 100644
index 000000000000..210b6f7b9028
--- /dev/null
+++ b/drivers/i2c/muxes/i2c-arb-gpio-challenge.c
@@ -0,0 +1,251 @@
1/*
2 * GPIO-based I2C Arbitration Using a Challenge & Response Mechanism
3 *
4 * Copyright (C) 2012 Google, Inc
5 *
6 * This software is licensed under the terms of the GNU General Public
7 * License version 2, as published by the Free Software Foundation, and
8 * may be copied, distributed, and modified under those terms.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 */
16
17#include <linux/delay.h>
18#include <linux/gpio.h>
19#include <linux/kernel.h>
20#include <linux/i2c.h>
21#include <linux/i2c-mux.h>
22#include <linux/init.h>
23#include <linux/module.h>
24#include <linux/of_i2c.h>
25#include <linux/of_gpio.h>
26#include <linux/platform_device.h>
27#include <linux/slab.h>
28
29
30/**
31 * struct i2c_arbitrator_data - Driver data for I2C arbitrator
32 *
33 * @parent: Parent adapter
34 * @child: Child bus
35 * @our_gpio: GPIO we'll use to claim.
36 * @our_gpio_release: 0 if active high; 1 if active low; AKA if the GPIO ==
37 * this then consider it released.
38 * @their_gpio: GPIO that the other side will use to claim.
39 * @their_gpio_release: 0 if active high; 1 if active low; AKA if the GPIO ==
40 * this then consider it released.
41 * @slew_delay_us: microseconds to wait for a GPIO to go high.
42 * @wait_retry_us: we'll attempt another claim after this many microseconds.
43 * @wait_free_us: we'll give up after this many microseconds.
44 */
45
46struct i2c_arbitrator_data {
47 struct i2c_adapter *parent;
48 struct i2c_adapter *child;
49 int our_gpio;
50 int our_gpio_release;
51 int their_gpio;
52 int their_gpio_release;
53 unsigned int slew_delay_us;
54 unsigned int wait_retry_us;
55 unsigned int wait_free_us;
56};
57
58
59/**
60 * i2c_arbitrator_select - claim the I2C bus
61 *
62 * Use the GPIO-based signalling protocol; return -EBUSY if we fail.
63 */
64static int i2c_arbitrator_select(struct i2c_adapter *adap, void *data, u32 chan)
65{
66 const struct i2c_arbitrator_data *arb = data;
67 unsigned long stop_retry, stop_time;
68
69 /* Start a round of trying to claim the bus */
70 stop_time = jiffies + usecs_to_jiffies(arb->wait_free_us) + 1;
71 do {
72 /* Indicate that we want to claim the bus */
73 gpio_set_value(arb->our_gpio, !arb->our_gpio_release);
74 udelay(arb->slew_delay_us);
75
76 /* Wait for the other master to release it */
77 stop_retry = jiffies + usecs_to_jiffies(arb->wait_retry_us) + 1;
78 while (time_before(jiffies, stop_retry)) {
79 int gpio_val = !!gpio_get_value(arb->their_gpio);
80
81 if (gpio_val == arb->their_gpio_release) {
82 /* We got it, so return */
83 return 0;
84 }
85
86 usleep_range(50, 200);
87 }
88
89 /* It didn't release, so give up, wait, and try again */
90 gpio_set_value(arb->our_gpio, arb->our_gpio_release);
91
92 usleep_range(arb->wait_retry_us, arb->wait_retry_us * 2);
93 } while (time_before(jiffies, stop_time));
94
95 /* Give up, release our claim */
96 gpio_set_value(arb->our_gpio, arb->our_gpio_release);
97 udelay(arb->slew_delay_us);
98 dev_err(&adap->dev, "Could not claim bus, timeout\n");
99 return -EBUSY;
100}
101
102/**
103 * i2c_arbitrator_deselect - release the I2C bus
104 *
105 * Release the I2C bus using the GPIO-based signalling protocol.
106 */
107static int i2c_arbitrator_deselect(struct i2c_adapter *adap, void *data,
108 u32 chan)
109{
110 const struct i2c_arbitrator_data *arb = data;
111
112 /* Release the bus and wait for the other master to notice */
113 gpio_set_value(arb->our_gpio, arb->our_gpio_release);
114 udelay(arb->slew_delay_us);
115
116 return 0;
117}
118
119static int i2c_arbitrator_probe(struct platform_device *pdev)
120{
121 struct device *dev = &pdev->dev;
122 struct device_node *np = dev->of_node;
123 struct device_node *parent_np;
124 struct i2c_arbitrator_data *arb;
125 enum of_gpio_flags gpio_flags;
126 unsigned long out_init;
127 int ret;
128
129 /* We only support probing from device tree; no platform_data */
130 if (!np) {
131 dev_err(dev, "Cannot find device tree node\n");
132 return -ENODEV;
133 }
134 if (dev->platform_data) {
135 dev_err(dev, "Platform data is not supported\n");
136 return -EINVAL;
137 }
138
139 arb = devm_kzalloc(dev, sizeof(*arb), GFP_KERNEL);
140 if (!arb) {
141 dev_err(dev, "Cannot allocate i2c_arbitrator_data\n");
142 return -ENOMEM;
143 }
144 platform_set_drvdata(pdev, arb);
145
146 /* Request GPIOs */
147 ret = of_get_named_gpio_flags(np, "our-claim-gpio", 0, &gpio_flags);
148 if (!gpio_is_valid(ret)) {
149 if (ret != -EPROBE_DEFER)
150 dev_err(dev, "Error getting our-claim-gpio\n");
151 return ret;
152 }
153 arb->our_gpio = ret;
154 arb->our_gpio_release = !!(gpio_flags & OF_GPIO_ACTIVE_LOW);
155 out_init = (gpio_flags & OF_GPIO_ACTIVE_LOW) ?
156 GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW;
157 ret = devm_gpio_request_one(dev, arb->our_gpio, out_init,
158 "our-claim-gpio");
159 if (ret) {
160 if (ret != -EPROBE_DEFER)
161 dev_err(dev, "Error requesting our-claim-gpio\n");
162 return ret;
163 }
164
165 ret = of_get_named_gpio_flags(np, "their-claim-gpios", 0, &gpio_flags);
166 if (!gpio_is_valid(ret)) {
167 if (ret != -EPROBE_DEFER)
168 dev_err(dev, "Error getting their-claim-gpio\n");
169 return ret;
170 }
171 arb->their_gpio = ret;
172 arb->their_gpio_release = !!(gpio_flags & OF_GPIO_ACTIVE_LOW);
173 ret = devm_gpio_request_one(dev, arb->their_gpio, GPIOF_IN,
174 "their-claim-gpio");
175 if (ret) {
176 if (ret != -EPROBE_DEFER)
177 dev_err(dev, "Error requesting their-claim-gpio\n");
178 return ret;
179 }
180
181 /* At the moment we only support a single two master (us + 1 other) */
182 if (gpio_is_valid(of_get_named_gpio(np, "their-claim-gpios", 1))) {
183 dev_err(dev, "Only one other master is supported\n");
184 return -EINVAL;
185 }
186
187 /* Arbitration parameters */
188 if (of_property_read_u32(np, "slew-delay-us", &arb->slew_delay_us))
189 arb->slew_delay_us = 10;
190 if (of_property_read_u32(np, "wait-retry-us", &arb->wait_retry_us))
191 arb->wait_retry_us = 3000;
192 if (of_property_read_u32(np, "wait-free-us", &arb->wait_free_us))
193 arb->wait_free_us = 50000;
194
195 /* Find our parent */
196 parent_np = of_parse_phandle(np, "i2c-parent", 0);
197 if (!parent_np) {
198 dev_err(dev, "Cannot parse i2c-parent\n");
199 return -EINVAL;
200 }
201 arb->parent = of_find_i2c_adapter_by_node(parent_np);
202 if (!arb->parent) {
203 dev_err(dev, "Cannot find parent bus\n");
204 return -EINVAL;
205 }
206
207 /* Actually add the mux adapter */
208 arb->child = i2c_add_mux_adapter(arb->parent, dev, arb, 0, 0, 0,
209 i2c_arbitrator_select,
210 i2c_arbitrator_deselect);
211 if (!arb->child) {
212 dev_err(dev, "Failed to add adapter\n");
213 ret = -ENODEV;
214 i2c_put_adapter(arb->parent);
215 }
216
217 return ret;
218}
219
220static int i2c_arbitrator_remove(struct platform_device *pdev)
221{
222 struct i2c_arbitrator_data *arb = platform_get_drvdata(pdev);
223
224 i2c_del_mux_adapter(arb->child);
225 i2c_put_adapter(arb->parent);
226
227 return 0;
228}
229
230static const struct of_device_id i2c_arbitrator_of_match[] = {
231 { .compatible = "i2c-arb-gpio-challenge", },
232 {},
233};
234MODULE_DEVICE_TABLE(of, i2c_arbitrator_of_match);
235
236static struct platform_driver i2c_arbitrator_driver = {
237 .probe = i2c_arbitrator_probe,
238 .remove = i2c_arbitrator_remove,
239 .driver = {
240 .owner = THIS_MODULE,
241 .name = "i2c-arb-gpio-challenge",
242 .of_match_table = of_match_ptr(i2c_arbitrator_of_match),
243 },
244};
245
246module_platform_driver(i2c_arbitrator_driver);
247
248MODULE_DESCRIPTION("GPIO-based I2C Arbitration");
249MODULE_AUTHOR("Doug Anderson <dianders@chromium.org>");
250MODULE_LICENSE("GPL v2");
251MODULE_ALIAS("platform:i2c-arb-gpio-challenge");