i2c-arb-gpio-challenge.c (5745B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * GPIO-based I2C Arbitration Using a Challenge & Response Mechanism 4 * 5 * Copyright (C) 2012 Google, Inc 6 */ 7 8#include <linux/delay.h> 9#include <linux/gpio/consumer.h> 10#include <linux/kernel.h> 11#include <linux/i2c.h> 12#include <linux/i2c-mux.h> 13#include <linux/module.h> 14#include <linux/platform_device.h> 15#include <linux/slab.h> 16 17 18/** 19 * struct i2c_arbitrator_data - Driver data for I2C arbitrator 20 * 21 * @our_gpio: GPIO descriptor we'll use to claim. 22 * @their_gpio: GPIO descriptor that the other side will use to claim. 23 * @slew_delay_us: microseconds to wait for a GPIO to go high. 24 * @wait_retry_us: we'll attempt another claim after this many microseconds. 25 * @wait_free_us: we'll give up after this many microseconds. 26 */ 27 28struct i2c_arbitrator_data { 29 struct gpio_desc *our_gpio; 30 struct gpio_desc *their_gpio; 31 unsigned int slew_delay_us; 32 unsigned int wait_retry_us; 33 unsigned int wait_free_us; 34}; 35 36 37/* 38 * i2c_arbitrator_select - claim the I2C bus 39 * 40 * Use the GPIO-based signalling protocol; return -EBUSY if we fail. 41 */ 42static int i2c_arbitrator_select(struct i2c_mux_core *muxc, u32 chan) 43{ 44 const struct i2c_arbitrator_data *arb = i2c_mux_priv(muxc); 45 unsigned long stop_retry, stop_time; 46 47 /* Start a round of trying to claim the bus */ 48 stop_time = jiffies + usecs_to_jiffies(arb->wait_free_us) + 1; 49 do { 50 /* Indicate that we want to claim the bus */ 51 gpiod_set_value(arb->our_gpio, 1); 52 udelay(arb->slew_delay_us); 53 54 /* Wait for the other master to release it */ 55 stop_retry = jiffies + usecs_to_jiffies(arb->wait_retry_us) + 1; 56 while (time_before(jiffies, stop_retry)) { 57 int gpio_val = gpiod_get_value(arb->their_gpio); 58 59 if (!gpio_val) { 60 /* We got it, so return */ 61 return 0; 62 } 63 64 usleep_range(50, 200); 65 } 66 67 /* It didn't release, so give up, wait, and try again */ 68 gpiod_set_value(arb->our_gpio, 0); 69 70 usleep_range(arb->wait_retry_us, arb->wait_retry_us * 2); 71 } while (time_before(jiffies, stop_time)); 72 73 /* Give up, release our claim */ 74 gpiod_set_value(arb->our_gpio, 0); 75 udelay(arb->slew_delay_us); 76 dev_err(muxc->dev, "Could not claim bus, timeout\n"); 77 return -EBUSY; 78} 79 80/* 81 * i2c_arbitrator_deselect - release the I2C bus 82 * 83 * Release the I2C bus using the GPIO-based signalling protocol. 84 */ 85static int i2c_arbitrator_deselect(struct i2c_mux_core *muxc, u32 chan) 86{ 87 const struct i2c_arbitrator_data *arb = i2c_mux_priv(muxc); 88 89 /* Release the bus and wait for the other master to notice */ 90 gpiod_set_value(arb->our_gpio, 0); 91 udelay(arb->slew_delay_us); 92 93 return 0; 94} 95 96static int i2c_arbitrator_probe(struct platform_device *pdev) 97{ 98 struct device *dev = &pdev->dev; 99 struct device_node *np = dev->of_node; 100 struct device_node *parent_np; 101 struct i2c_mux_core *muxc; 102 struct i2c_arbitrator_data *arb; 103 struct gpio_desc *dummy; 104 int ret; 105 106 /* We only support probing from device tree; no platform_data */ 107 if (!np) { 108 dev_err(dev, "Cannot find device tree node\n"); 109 return -ENODEV; 110 } 111 if (dev_get_platdata(dev)) { 112 dev_err(dev, "Platform data is not supported\n"); 113 return -EINVAL; 114 } 115 116 muxc = i2c_mux_alloc(NULL, dev, 1, sizeof(*arb), I2C_MUX_ARBITRATOR, 117 i2c_arbitrator_select, i2c_arbitrator_deselect); 118 if (!muxc) 119 return -ENOMEM; 120 arb = i2c_mux_priv(muxc); 121 122 platform_set_drvdata(pdev, muxc); 123 124 /* Request GPIOs, our GPIO as unclaimed to begin with */ 125 arb->our_gpio = devm_gpiod_get(dev, "our-claim", GPIOD_OUT_LOW); 126 if (IS_ERR(arb->our_gpio)) { 127 dev_err(dev, "could not get \"our-claim\" GPIO (%ld)\n", 128 PTR_ERR(arb->our_gpio)); 129 return PTR_ERR(arb->our_gpio); 130 } 131 132 arb->their_gpio = devm_gpiod_get(dev, "their-claim", GPIOD_IN); 133 if (IS_ERR(arb->their_gpio)) { 134 dev_err(dev, "could not get \"their-claim\" GPIO (%ld)\n", 135 PTR_ERR(arb->their_gpio)); 136 return PTR_ERR(arb->their_gpio); 137 } 138 139 /* At the moment we only support a single two master (us + 1 other) */ 140 dummy = devm_gpiod_get_index(dev, "their-claim", 1, GPIOD_IN); 141 if (!IS_ERR(dummy)) { 142 dev_err(dev, "Only one other master is supported\n"); 143 return -EINVAL; 144 } else if (PTR_ERR(dummy) == -EPROBE_DEFER) { 145 return -EPROBE_DEFER; 146 } 147 148 /* Arbitration parameters */ 149 if (of_property_read_u32(np, "slew-delay-us", &arb->slew_delay_us)) 150 arb->slew_delay_us = 10; 151 if (of_property_read_u32(np, "wait-retry-us", &arb->wait_retry_us)) 152 arb->wait_retry_us = 3000; 153 if (of_property_read_u32(np, "wait-free-us", &arb->wait_free_us)) 154 arb->wait_free_us = 50000; 155 156 /* Find our parent */ 157 parent_np = of_parse_phandle(np, "i2c-parent", 0); 158 if (!parent_np) { 159 dev_err(dev, "Cannot parse i2c-parent\n"); 160 return -EINVAL; 161 } 162 muxc->parent = of_get_i2c_adapter_by_node(parent_np); 163 of_node_put(parent_np); 164 if (!muxc->parent) { 165 dev_err(dev, "Cannot find parent bus\n"); 166 return -EPROBE_DEFER; 167 } 168 169 /* Actually add the mux adapter */ 170 ret = i2c_mux_add_adapter(muxc, 0, 0, 0); 171 if (ret) 172 i2c_put_adapter(muxc->parent); 173 174 return ret; 175} 176 177static int i2c_arbitrator_remove(struct platform_device *pdev) 178{ 179 struct i2c_mux_core *muxc = platform_get_drvdata(pdev); 180 181 i2c_mux_del_adapters(muxc); 182 i2c_put_adapter(muxc->parent); 183 return 0; 184} 185 186static const struct of_device_id i2c_arbitrator_of_match[] = { 187 { .compatible = "i2c-arb-gpio-challenge", }, 188 {}, 189}; 190MODULE_DEVICE_TABLE(of, i2c_arbitrator_of_match); 191 192static struct platform_driver i2c_arbitrator_driver = { 193 .probe = i2c_arbitrator_probe, 194 .remove = i2c_arbitrator_remove, 195 .driver = { 196 .name = "i2c-arb-gpio-challenge", 197 .of_match_table = i2c_arbitrator_of_match, 198 }, 199}; 200 201module_platform_driver(i2c_arbitrator_driver); 202 203MODULE_DESCRIPTION("GPIO-based I2C Arbitration"); 204MODULE_AUTHOR("Doug Anderson <dianders@chromium.org>"); 205MODULE_LICENSE("GPL v2"); 206MODULE_ALIAS("platform:i2c-arb-gpio-challenge");