xref: /linux/drivers/i2c/muxes/i2c-arb-gpio-challenge.c (revision b85d45947951d23cb22d90caecf4c1eb81342c96)
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/module.h>
23 #include <linux/of_gpio.h>
24 #include <linux/platform_device.h>
25 #include <linux/slab.h>
26 
27 
28 /**
29  * struct i2c_arbitrator_data - Driver data for I2C arbitrator
30  *
31  * @parent: Parent adapter
32  * @child: Child bus
33  * @our_gpio: GPIO we'll use to claim.
34  * @our_gpio_release: 0 if active high; 1 if active low; AKA if the GPIO ==
35  *   this then consider it released.
36  * @their_gpio: GPIO that the other side will use to claim.
37  * @their_gpio_release: 0 if active high; 1 if active low; AKA if the GPIO ==
38  *   this then consider it released.
39  * @slew_delay_us: microseconds to wait for a GPIO to go high.
40  * @wait_retry_us: we'll attempt another claim after this many microseconds.
41  * @wait_free_us: we'll give up after this many microseconds.
42  */
43 
44 struct i2c_arbitrator_data {
45 	struct i2c_adapter *parent;
46 	struct i2c_adapter *child;
47 	int our_gpio;
48 	int our_gpio_release;
49 	int their_gpio;
50 	int their_gpio_release;
51 	unsigned int slew_delay_us;
52 	unsigned int wait_retry_us;
53 	unsigned int wait_free_us;
54 };
55 
56 
57 /**
58  * i2c_arbitrator_select - claim the I2C bus
59  *
60  * Use the GPIO-based signalling protocol; return -EBUSY if we fail.
61  */
62 static int i2c_arbitrator_select(struct i2c_adapter *adap, void *data, u32 chan)
63 {
64 	const struct i2c_arbitrator_data *arb = data;
65 	unsigned long stop_retry, stop_time;
66 
67 	/* Start a round of trying to claim the bus */
68 	stop_time = jiffies + usecs_to_jiffies(arb->wait_free_us) + 1;
69 	do {
70 		/* Indicate that we want to claim the bus */
71 		gpio_set_value(arb->our_gpio, !arb->our_gpio_release);
72 		udelay(arb->slew_delay_us);
73 
74 		/* Wait for the other master to release it */
75 		stop_retry = jiffies + usecs_to_jiffies(arb->wait_retry_us) + 1;
76 		while (time_before(jiffies, stop_retry)) {
77 			int gpio_val = !!gpio_get_value(arb->their_gpio);
78 
79 			if (gpio_val == arb->their_gpio_release) {
80 				/* We got it, so return */
81 				return 0;
82 			}
83 
84 			usleep_range(50, 200);
85 		}
86 
87 		/* It didn't release, so give up, wait, and try again */
88 		gpio_set_value(arb->our_gpio, arb->our_gpio_release);
89 
90 		usleep_range(arb->wait_retry_us, arb->wait_retry_us * 2);
91 	} while (time_before(jiffies, stop_time));
92 
93 	/* Give up, release our claim */
94 	gpio_set_value(arb->our_gpio, arb->our_gpio_release);
95 	udelay(arb->slew_delay_us);
96 	dev_err(&adap->dev, "Could not claim bus, timeout\n");
97 	return -EBUSY;
98 }
99 
100 /**
101  * i2c_arbitrator_deselect - release the I2C bus
102  *
103  * Release the I2C bus using the GPIO-based signalling protocol.
104  */
105 static int i2c_arbitrator_deselect(struct i2c_adapter *adap, void *data,
106 				   u32 chan)
107 {
108 	const struct i2c_arbitrator_data *arb = data;
109 
110 	/* Release the bus and wait for the other master to notice */
111 	gpio_set_value(arb->our_gpio, arb->our_gpio_release);
112 	udelay(arb->slew_delay_us);
113 
114 	return 0;
115 }
116 
117 static int i2c_arbitrator_probe(struct platform_device *pdev)
118 {
119 	struct device *dev = &pdev->dev;
120 	struct device_node *np = dev->of_node;
121 	struct device_node *parent_np;
122 	struct i2c_arbitrator_data *arb;
123 	enum of_gpio_flags gpio_flags;
124 	unsigned long out_init;
125 	int ret;
126 
127 	/* We only support probing from device tree; no platform_data */
128 	if (!np) {
129 		dev_err(dev, "Cannot find device tree node\n");
130 		return -ENODEV;
131 	}
132 	if (dev_get_platdata(dev)) {
133 		dev_err(dev, "Platform data is not supported\n");
134 		return -EINVAL;
135 	}
136 
137 	arb = devm_kzalloc(dev, sizeof(*arb), GFP_KERNEL);
138 	if (!arb) {
139 		dev_err(dev, "Cannot allocate i2c_arbitrator_data\n");
140 		return -ENOMEM;
141 	}
142 	platform_set_drvdata(pdev, arb);
143 
144 	/* Request GPIOs */
145 	ret = of_get_named_gpio_flags(np, "our-claim-gpio", 0, &gpio_flags);
146 	if (!gpio_is_valid(ret)) {
147 		if (ret != -EPROBE_DEFER)
148 			dev_err(dev, "Error getting our-claim-gpio\n");
149 		return ret;
150 	}
151 	arb->our_gpio = ret;
152 	arb->our_gpio_release = !!(gpio_flags & OF_GPIO_ACTIVE_LOW);
153 	out_init = (gpio_flags & OF_GPIO_ACTIVE_LOW) ?
154 		GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW;
155 	ret = devm_gpio_request_one(dev, arb->our_gpio, out_init,
156 				    "our-claim-gpio");
157 	if (ret) {
158 		if (ret != -EPROBE_DEFER)
159 			dev_err(dev, "Error requesting our-claim-gpio\n");
160 		return ret;
161 	}
162 
163 	ret = of_get_named_gpio_flags(np, "their-claim-gpios", 0, &gpio_flags);
164 	if (!gpio_is_valid(ret)) {
165 		if (ret != -EPROBE_DEFER)
166 			dev_err(dev, "Error getting their-claim-gpio\n");
167 		return ret;
168 	}
169 	arb->their_gpio = ret;
170 	arb->their_gpio_release = !!(gpio_flags & OF_GPIO_ACTIVE_LOW);
171 	ret = devm_gpio_request_one(dev, arb->their_gpio, GPIOF_IN,
172 				    "their-claim-gpio");
173 	if (ret) {
174 		if (ret != -EPROBE_DEFER)
175 			dev_err(dev, "Error requesting their-claim-gpio\n");
176 		return ret;
177 	}
178 
179 	/* At the moment we only support a single two master (us + 1 other) */
180 	if (gpio_is_valid(of_get_named_gpio(np, "their-claim-gpios", 1))) {
181 		dev_err(dev, "Only one other master is supported\n");
182 		return -EINVAL;
183 	}
184 
185 	/* Arbitration parameters */
186 	if (of_property_read_u32(np, "slew-delay-us", &arb->slew_delay_us))
187 		arb->slew_delay_us = 10;
188 	if (of_property_read_u32(np, "wait-retry-us", &arb->wait_retry_us))
189 		arb->wait_retry_us = 3000;
190 	if (of_property_read_u32(np, "wait-free-us", &arb->wait_free_us))
191 		arb->wait_free_us = 50000;
192 
193 	/* Find our parent */
194 	parent_np = of_parse_phandle(np, "i2c-parent", 0);
195 	if (!parent_np) {
196 		dev_err(dev, "Cannot parse i2c-parent\n");
197 		return -EINVAL;
198 	}
199 	arb->parent = of_get_i2c_adapter_by_node(parent_np);
200 	of_node_put(parent_np);
201 	if (!arb->parent) {
202 		dev_err(dev, "Cannot find parent bus\n");
203 		return -EPROBE_DEFER;
204 	}
205 
206 	/* Actually add the mux adapter */
207 	arb->child = i2c_add_mux_adapter(arb->parent, dev, arb, 0, 0, 0,
208 					 i2c_arbitrator_select,
209 					 i2c_arbitrator_deselect);
210 	if (!arb->child) {
211 		dev_err(dev, "Failed to add adapter\n");
212 		ret = -ENODEV;
213 		i2c_put_adapter(arb->parent);
214 	}
215 
216 	return ret;
217 }
218 
219 static int i2c_arbitrator_remove(struct platform_device *pdev)
220 {
221 	struct i2c_arbitrator_data *arb = platform_get_drvdata(pdev);
222 
223 	i2c_del_mux_adapter(arb->child);
224 	i2c_put_adapter(arb->parent);
225 
226 	return 0;
227 }
228 
229 static const struct of_device_id i2c_arbitrator_of_match[] = {
230 	{ .compatible = "i2c-arb-gpio-challenge", },
231 	{},
232 };
233 MODULE_DEVICE_TABLE(of, i2c_arbitrator_of_match);
234 
235 static struct platform_driver i2c_arbitrator_driver = {
236 	.probe	= i2c_arbitrator_probe,
237 	.remove	= i2c_arbitrator_remove,
238 	.driver	= {
239 		.name	= "i2c-arb-gpio-challenge",
240 		.of_match_table = i2c_arbitrator_of_match,
241 	},
242 };
243 
244 module_platform_driver(i2c_arbitrator_driver);
245 
246 MODULE_DESCRIPTION("GPIO-based I2C Arbitration");
247 MODULE_AUTHOR("Doug Anderson <dianders@chromium.org>");
248 MODULE_LICENSE("GPL v2");
249 MODULE_ALIAS("platform:i2c-arb-gpio-challenge");
250