18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * GPIO-based I2C Arbitration Using a Challenge & Response Mechanism
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2012 Google, Inc
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/delay.h>
98c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h>
108c2ecf20Sopenharmony_ci#include <linux/kernel.h>
118c2ecf20Sopenharmony_ci#include <linux/i2c.h>
128c2ecf20Sopenharmony_ci#include <linux/i2c-mux.h>
138c2ecf20Sopenharmony_ci#include <linux/module.h>
148c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
158c2ecf20Sopenharmony_ci#include <linux/slab.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci/**
198c2ecf20Sopenharmony_ci * struct i2c_arbitrator_data - Driver data for I2C arbitrator
208c2ecf20Sopenharmony_ci *
218c2ecf20Sopenharmony_ci * @our_gpio: GPIO descriptor we'll use to claim.
228c2ecf20Sopenharmony_ci * @their_gpio: GPIO descriptor that the other side will use to claim.
238c2ecf20Sopenharmony_ci * @slew_delay_us: microseconds to wait for a GPIO to go high.
248c2ecf20Sopenharmony_ci * @wait_retry_us: we'll attempt another claim after this many microseconds.
258c2ecf20Sopenharmony_ci * @wait_free_us: we'll give up after this many microseconds.
268c2ecf20Sopenharmony_ci */
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_cistruct i2c_arbitrator_data {
298c2ecf20Sopenharmony_ci	struct gpio_desc *our_gpio;
308c2ecf20Sopenharmony_ci	struct gpio_desc *their_gpio;
318c2ecf20Sopenharmony_ci	unsigned int slew_delay_us;
328c2ecf20Sopenharmony_ci	unsigned int wait_retry_us;
338c2ecf20Sopenharmony_ci	unsigned int wait_free_us;
348c2ecf20Sopenharmony_ci};
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci/**
388c2ecf20Sopenharmony_ci * i2c_arbitrator_select - claim the I2C bus
398c2ecf20Sopenharmony_ci *
408c2ecf20Sopenharmony_ci * Use the GPIO-based signalling protocol; return -EBUSY if we fail.
418c2ecf20Sopenharmony_ci */
428c2ecf20Sopenharmony_cistatic int i2c_arbitrator_select(struct i2c_mux_core *muxc, u32 chan)
438c2ecf20Sopenharmony_ci{
448c2ecf20Sopenharmony_ci	const struct i2c_arbitrator_data *arb = i2c_mux_priv(muxc);
458c2ecf20Sopenharmony_ci	unsigned long stop_retry, stop_time;
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	/* Start a round of trying to claim the bus */
488c2ecf20Sopenharmony_ci	stop_time = jiffies + usecs_to_jiffies(arb->wait_free_us) + 1;
498c2ecf20Sopenharmony_ci	do {
508c2ecf20Sopenharmony_ci		/* Indicate that we want to claim the bus */
518c2ecf20Sopenharmony_ci		gpiod_set_value(arb->our_gpio, 1);
528c2ecf20Sopenharmony_ci		udelay(arb->slew_delay_us);
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci		/* Wait for the other master to release it */
558c2ecf20Sopenharmony_ci		stop_retry = jiffies + usecs_to_jiffies(arb->wait_retry_us) + 1;
568c2ecf20Sopenharmony_ci		while (time_before(jiffies, stop_retry)) {
578c2ecf20Sopenharmony_ci			int gpio_val = gpiod_get_value(arb->their_gpio);
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci			if (!gpio_val) {
608c2ecf20Sopenharmony_ci				/* We got it, so return */
618c2ecf20Sopenharmony_ci				return 0;
628c2ecf20Sopenharmony_ci			}
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci			usleep_range(50, 200);
658c2ecf20Sopenharmony_ci		}
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci		/* It didn't release, so give up, wait, and try again */
688c2ecf20Sopenharmony_ci		gpiod_set_value(arb->our_gpio, 0);
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci		usleep_range(arb->wait_retry_us, arb->wait_retry_us * 2);
718c2ecf20Sopenharmony_ci	} while (time_before(jiffies, stop_time));
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	/* Give up, release our claim */
748c2ecf20Sopenharmony_ci	gpiod_set_value(arb->our_gpio, 0);
758c2ecf20Sopenharmony_ci	udelay(arb->slew_delay_us);
768c2ecf20Sopenharmony_ci	dev_err(muxc->dev, "Could not claim bus, timeout\n");
778c2ecf20Sopenharmony_ci	return -EBUSY;
788c2ecf20Sopenharmony_ci}
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci/**
818c2ecf20Sopenharmony_ci * i2c_arbitrator_deselect - release the I2C bus
828c2ecf20Sopenharmony_ci *
838c2ecf20Sopenharmony_ci * Release the I2C bus using the GPIO-based signalling protocol.
848c2ecf20Sopenharmony_ci */
858c2ecf20Sopenharmony_cistatic int i2c_arbitrator_deselect(struct i2c_mux_core *muxc, u32 chan)
868c2ecf20Sopenharmony_ci{
878c2ecf20Sopenharmony_ci	const struct i2c_arbitrator_data *arb = i2c_mux_priv(muxc);
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	/* Release the bus and wait for the other master to notice */
908c2ecf20Sopenharmony_ci	gpiod_set_value(arb->our_gpio, 0);
918c2ecf20Sopenharmony_ci	udelay(arb->slew_delay_us);
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	return 0;
948c2ecf20Sopenharmony_ci}
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_cistatic int i2c_arbitrator_probe(struct platform_device *pdev)
978c2ecf20Sopenharmony_ci{
988c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
998c2ecf20Sopenharmony_ci	struct device_node *np = dev->of_node;
1008c2ecf20Sopenharmony_ci	struct device_node *parent_np;
1018c2ecf20Sopenharmony_ci	struct i2c_mux_core *muxc;
1028c2ecf20Sopenharmony_ci	struct i2c_arbitrator_data *arb;
1038c2ecf20Sopenharmony_ci	struct gpio_desc *dummy;
1048c2ecf20Sopenharmony_ci	int ret;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	/* We only support probing from device tree; no platform_data */
1078c2ecf20Sopenharmony_ci	if (!np) {
1088c2ecf20Sopenharmony_ci		dev_err(dev, "Cannot find device tree node\n");
1098c2ecf20Sopenharmony_ci		return -ENODEV;
1108c2ecf20Sopenharmony_ci	}
1118c2ecf20Sopenharmony_ci	if (dev_get_platdata(dev)) {
1128c2ecf20Sopenharmony_ci		dev_err(dev, "Platform data is not supported\n");
1138c2ecf20Sopenharmony_ci		return -EINVAL;
1148c2ecf20Sopenharmony_ci	}
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	muxc = i2c_mux_alloc(NULL, dev, 1, sizeof(*arb), I2C_MUX_ARBITRATOR,
1178c2ecf20Sopenharmony_ci			     i2c_arbitrator_select, i2c_arbitrator_deselect);
1188c2ecf20Sopenharmony_ci	if (!muxc)
1198c2ecf20Sopenharmony_ci		return -ENOMEM;
1208c2ecf20Sopenharmony_ci	arb = i2c_mux_priv(muxc);
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, muxc);
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	/* Request GPIOs, our GPIO as unclaimed to begin with */
1258c2ecf20Sopenharmony_ci	arb->our_gpio = devm_gpiod_get(dev, "our-claim", GPIOD_OUT_LOW);
1268c2ecf20Sopenharmony_ci	if (IS_ERR(arb->our_gpio)) {
1278c2ecf20Sopenharmony_ci		dev_err(dev, "could not get \"our-claim\" GPIO (%ld)\n",
1288c2ecf20Sopenharmony_ci			PTR_ERR(arb->our_gpio));
1298c2ecf20Sopenharmony_ci		return PTR_ERR(arb->our_gpio);
1308c2ecf20Sopenharmony_ci	}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	arb->their_gpio = devm_gpiod_get(dev, "their-claim", GPIOD_IN);
1338c2ecf20Sopenharmony_ci	if (IS_ERR(arb->their_gpio)) {
1348c2ecf20Sopenharmony_ci		dev_err(dev, "could not get \"their-claim\" GPIO (%ld)\n",
1358c2ecf20Sopenharmony_ci			PTR_ERR(arb->their_gpio));
1368c2ecf20Sopenharmony_ci		return PTR_ERR(arb->their_gpio);
1378c2ecf20Sopenharmony_ci	}
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	/* At the moment we only support a single two master (us + 1 other) */
1408c2ecf20Sopenharmony_ci	dummy = devm_gpiod_get_index(dev, "their-claim", 1, GPIOD_IN);
1418c2ecf20Sopenharmony_ci	if (!IS_ERR(dummy)) {
1428c2ecf20Sopenharmony_ci		dev_err(dev, "Only one other master is supported\n");
1438c2ecf20Sopenharmony_ci		return -EINVAL;
1448c2ecf20Sopenharmony_ci	} else if (PTR_ERR(dummy) == -EPROBE_DEFER) {
1458c2ecf20Sopenharmony_ci		return -EPROBE_DEFER;
1468c2ecf20Sopenharmony_ci	}
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	/* Arbitration parameters */
1498c2ecf20Sopenharmony_ci	if (of_property_read_u32(np, "slew-delay-us", &arb->slew_delay_us))
1508c2ecf20Sopenharmony_ci		arb->slew_delay_us = 10;
1518c2ecf20Sopenharmony_ci	if (of_property_read_u32(np, "wait-retry-us", &arb->wait_retry_us))
1528c2ecf20Sopenharmony_ci		arb->wait_retry_us = 3000;
1538c2ecf20Sopenharmony_ci	if (of_property_read_u32(np, "wait-free-us", &arb->wait_free_us))
1548c2ecf20Sopenharmony_ci		arb->wait_free_us = 50000;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	/* Find our parent */
1578c2ecf20Sopenharmony_ci	parent_np = of_parse_phandle(np, "i2c-parent", 0);
1588c2ecf20Sopenharmony_ci	if (!parent_np) {
1598c2ecf20Sopenharmony_ci		dev_err(dev, "Cannot parse i2c-parent\n");
1608c2ecf20Sopenharmony_ci		return -EINVAL;
1618c2ecf20Sopenharmony_ci	}
1628c2ecf20Sopenharmony_ci	muxc->parent = of_get_i2c_adapter_by_node(parent_np);
1638c2ecf20Sopenharmony_ci	of_node_put(parent_np);
1648c2ecf20Sopenharmony_ci	if (!muxc->parent) {
1658c2ecf20Sopenharmony_ci		dev_err(dev, "Cannot find parent bus\n");
1668c2ecf20Sopenharmony_ci		return -EPROBE_DEFER;
1678c2ecf20Sopenharmony_ci	}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	/* Actually add the mux adapter */
1708c2ecf20Sopenharmony_ci	ret = i2c_mux_add_adapter(muxc, 0, 0, 0);
1718c2ecf20Sopenharmony_ci	if (ret)
1728c2ecf20Sopenharmony_ci		i2c_put_adapter(muxc->parent);
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	return ret;
1758c2ecf20Sopenharmony_ci}
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_cistatic int i2c_arbitrator_remove(struct platform_device *pdev)
1788c2ecf20Sopenharmony_ci{
1798c2ecf20Sopenharmony_ci	struct i2c_mux_core *muxc = platform_get_drvdata(pdev);
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	i2c_mux_del_adapters(muxc);
1828c2ecf20Sopenharmony_ci	i2c_put_adapter(muxc->parent);
1838c2ecf20Sopenharmony_ci	return 0;
1848c2ecf20Sopenharmony_ci}
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_cistatic const struct of_device_id i2c_arbitrator_of_match[] = {
1878c2ecf20Sopenharmony_ci	{ .compatible = "i2c-arb-gpio-challenge", },
1888c2ecf20Sopenharmony_ci	{},
1898c2ecf20Sopenharmony_ci};
1908c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, i2c_arbitrator_of_match);
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_cistatic struct platform_driver i2c_arbitrator_driver = {
1938c2ecf20Sopenharmony_ci	.probe	= i2c_arbitrator_probe,
1948c2ecf20Sopenharmony_ci	.remove	= i2c_arbitrator_remove,
1958c2ecf20Sopenharmony_ci	.driver	= {
1968c2ecf20Sopenharmony_ci		.name	= "i2c-arb-gpio-challenge",
1978c2ecf20Sopenharmony_ci		.of_match_table = i2c_arbitrator_of_match,
1988c2ecf20Sopenharmony_ci	},
1998c2ecf20Sopenharmony_ci};
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_cimodule_platform_driver(i2c_arbitrator_driver);
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("GPIO-based I2C Arbitration");
2048c2ecf20Sopenharmony_ciMODULE_AUTHOR("Doug Anderson <dianders@chromium.org>");
2058c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
2068c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:i2c-arb-gpio-challenge");
207