162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * GPIO-based I2C Arbitration Using a Challenge & Response Mechanism
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2012 Google, Inc
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/delay.h>
962306a36Sopenharmony_ci#include <linux/gpio/consumer.h>
1062306a36Sopenharmony_ci#include <linux/kernel.h>
1162306a36Sopenharmony_ci#include <linux/i2c.h>
1262306a36Sopenharmony_ci#include <linux/i2c-mux.h>
1362306a36Sopenharmony_ci#include <linux/module.h>
1462306a36Sopenharmony_ci#include <linux/platform_device.h>
1562306a36Sopenharmony_ci#include <linux/slab.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci/**
1962306a36Sopenharmony_ci * struct i2c_arbitrator_data - Driver data for I2C arbitrator
2062306a36Sopenharmony_ci *
2162306a36Sopenharmony_ci * @our_gpio: GPIO descriptor we'll use to claim.
2262306a36Sopenharmony_ci * @their_gpio: GPIO descriptor that the other side will use to claim.
2362306a36Sopenharmony_ci * @slew_delay_us: microseconds to wait for a GPIO to go high.
2462306a36Sopenharmony_ci * @wait_retry_us: we'll attempt another claim after this many microseconds.
2562306a36Sopenharmony_ci * @wait_free_us: we'll give up after this many microseconds.
2662306a36Sopenharmony_ci */
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cistruct i2c_arbitrator_data {
2962306a36Sopenharmony_ci	struct gpio_desc *our_gpio;
3062306a36Sopenharmony_ci	struct gpio_desc *their_gpio;
3162306a36Sopenharmony_ci	unsigned int slew_delay_us;
3262306a36Sopenharmony_ci	unsigned int wait_retry_us;
3362306a36Sopenharmony_ci	unsigned int wait_free_us;
3462306a36Sopenharmony_ci};
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci/*
3862306a36Sopenharmony_ci * i2c_arbitrator_select - claim the I2C bus
3962306a36Sopenharmony_ci *
4062306a36Sopenharmony_ci * Use the GPIO-based signalling protocol; return -EBUSY if we fail.
4162306a36Sopenharmony_ci */
4262306a36Sopenharmony_cistatic int i2c_arbitrator_select(struct i2c_mux_core *muxc, u32 chan)
4362306a36Sopenharmony_ci{
4462306a36Sopenharmony_ci	const struct i2c_arbitrator_data *arb = i2c_mux_priv(muxc);
4562306a36Sopenharmony_ci	unsigned long stop_retry, stop_time;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	/* Start a round of trying to claim the bus */
4862306a36Sopenharmony_ci	stop_time = jiffies + usecs_to_jiffies(arb->wait_free_us) + 1;
4962306a36Sopenharmony_ci	do {
5062306a36Sopenharmony_ci		/* Indicate that we want to claim the bus */
5162306a36Sopenharmony_ci		gpiod_set_value(arb->our_gpio, 1);
5262306a36Sopenharmony_ci		udelay(arb->slew_delay_us);
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci		/* Wait for the other master to release it */
5562306a36Sopenharmony_ci		stop_retry = jiffies + usecs_to_jiffies(arb->wait_retry_us) + 1;
5662306a36Sopenharmony_ci		while (time_before(jiffies, stop_retry)) {
5762306a36Sopenharmony_ci			int gpio_val = gpiod_get_value(arb->their_gpio);
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci			if (!gpio_val) {
6062306a36Sopenharmony_ci				/* We got it, so return */
6162306a36Sopenharmony_ci				return 0;
6262306a36Sopenharmony_ci			}
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci			usleep_range(50, 200);
6562306a36Sopenharmony_ci		}
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci		/* It didn't release, so give up, wait, and try again */
6862306a36Sopenharmony_ci		gpiod_set_value(arb->our_gpio, 0);
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci		usleep_range(arb->wait_retry_us, arb->wait_retry_us * 2);
7162306a36Sopenharmony_ci	} while (time_before(jiffies, stop_time));
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	/* Give up, release our claim */
7462306a36Sopenharmony_ci	gpiod_set_value(arb->our_gpio, 0);
7562306a36Sopenharmony_ci	udelay(arb->slew_delay_us);
7662306a36Sopenharmony_ci	dev_err(muxc->dev, "Could not claim bus, timeout\n");
7762306a36Sopenharmony_ci	return -EBUSY;
7862306a36Sopenharmony_ci}
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci/*
8162306a36Sopenharmony_ci * i2c_arbitrator_deselect - release the I2C bus
8262306a36Sopenharmony_ci *
8362306a36Sopenharmony_ci * Release the I2C bus using the GPIO-based signalling protocol.
8462306a36Sopenharmony_ci */
8562306a36Sopenharmony_cistatic int i2c_arbitrator_deselect(struct i2c_mux_core *muxc, u32 chan)
8662306a36Sopenharmony_ci{
8762306a36Sopenharmony_ci	const struct i2c_arbitrator_data *arb = i2c_mux_priv(muxc);
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	/* Release the bus and wait for the other master to notice */
9062306a36Sopenharmony_ci	gpiod_set_value(arb->our_gpio, 0);
9162306a36Sopenharmony_ci	udelay(arb->slew_delay_us);
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	return 0;
9462306a36Sopenharmony_ci}
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_cistatic int i2c_arbitrator_probe(struct platform_device *pdev)
9762306a36Sopenharmony_ci{
9862306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
9962306a36Sopenharmony_ci	struct device_node *np = dev->of_node;
10062306a36Sopenharmony_ci	struct device_node *parent_np;
10162306a36Sopenharmony_ci	struct i2c_mux_core *muxc;
10262306a36Sopenharmony_ci	struct i2c_arbitrator_data *arb;
10362306a36Sopenharmony_ci	struct gpio_desc *dummy;
10462306a36Sopenharmony_ci	int ret;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	/* We only support probing from device tree; no platform_data */
10762306a36Sopenharmony_ci	if (!np) {
10862306a36Sopenharmony_ci		dev_err(dev, "Cannot find device tree node\n");
10962306a36Sopenharmony_ci		return -ENODEV;
11062306a36Sopenharmony_ci	}
11162306a36Sopenharmony_ci	if (dev_get_platdata(dev)) {
11262306a36Sopenharmony_ci		dev_err(dev, "Platform data is not supported\n");
11362306a36Sopenharmony_ci		return -EINVAL;
11462306a36Sopenharmony_ci	}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	muxc = i2c_mux_alloc(NULL, dev, 1, sizeof(*arb), I2C_MUX_ARBITRATOR,
11762306a36Sopenharmony_ci			     i2c_arbitrator_select, i2c_arbitrator_deselect);
11862306a36Sopenharmony_ci	if (!muxc)
11962306a36Sopenharmony_ci		return -ENOMEM;
12062306a36Sopenharmony_ci	arb = i2c_mux_priv(muxc);
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	platform_set_drvdata(pdev, muxc);
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	/* Request GPIOs, our GPIO as unclaimed to begin with */
12562306a36Sopenharmony_ci	arb->our_gpio = devm_gpiod_get(dev, "our-claim", GPIOD_OUT_LOW);
12662306a36Sopenharmony_ci	if (IS_ERR(arb->our_gpio)) {
12762306a36Sopenharmony_ci		dev_err(dev, "could not get \"our-claim\" GPIO (%ld)\n",
12862306a36Sopenharmony_ci			PTR_ERR(arb->our_gpio));
12962306a36Sopenharmony_ci		return PTR_ERR(arb->our_gpio);
13062306a36Sopenharmony_ci	}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	arb->their_gpio = devm_gpiod_get(dev, "their-claim", GPIOD_IN);
13362306a36Sopenharmony_ci	if (IS_ERR(arb->their_gpio)) {
13462306a36Sopenharmony_ci		dev_err(dev, "could not get \"their-claim\" GPIO (%ld)\n",
13562306a36Sopenharmony_ci			PTR_ERR(arb->their_gpio));
13662306a36Sopenharmony_ci		return PTR_ERR(arb->their_gpio);
13762306a36Sopenharmony_ci	}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	/* At the moment we only support a single two master (us + 1 other) */
14062306a36Sopenharmony_ci	dummy = devm_gpiod_get_index(dev, "their-claim", 1, GPIOD_IN);
14162306a36Sopenharmony_ci	if (!IS_ERR(dummy)) {
14262306a36Sopenharmony_ci		dev_err(dev, "Only one other master is supported\n");
14362306a36Sopenharmony_ci		return -EINVAL;
14462306a36Sopenharmony_ci	} else if (PTR_ERR(dummy) == -EPROBE_DEFER) {
14562306a36Sopenharmony_ci		return -EPROBE_DEFER;
14662306a36Sopenharmony_ci	}
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	/* Arbitration parameters */
14962306a36Sopenharmony_ci	if (of_property_read_u32(np, "slew-delay-us", &arb->slew_delay_us))
15062306a36Sopenharmony_ci		arb->slew_delay_us = 10;
15162306a36Sopenharmony_ci	if (of_property_read_u32(np, "wait-retry-us", &arb->wait_retry_us))
15262306a36Sopenharmony_ci		arb->wait_retry_us = 3000;
15362306a36Sopenharmony_ci	if (of_property_read_u32(np, "wait-free-us", &arb->wait_free_us))
15462306a36Sopenharmony_ci		arb->wait_free_us = 50000;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	/* Find our parent */
15762306a36Sopenharmony_ci	parent_np = of_parse_phandle(np, "i2c-parent", 0);
15862306a36Sopenharmony_ci	if (!parent_np) {
15962306a36Sopenharmony_ci		dev_err(dev, "Cannot parse i2c-parent\n");
16062306a36Sopenharmony_ci		return -EINVAL;
16162306a36Sopenharmony_ci	}
16262306a36Sopenharmony_ci	muxc->parent = of_get_i2c_adapter_by_node(parent_np);
16362306a36Sopenharmony_ci	of_node_put(parent_np);
16462306a36Sopenharmony_ci	if (!muxc->parent) {
16562306a36Sopenharmony_ci		dev_err(dev, "Cannot find parent bus\n");
16662306a36Sopenharmony_ci		return -EPROBE_DEFER;
16762306a36Sopenharmony_ci	}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	/* Actually add the mux adapter */
17062306a36Sopenharmony_ci	ret = i2c_mux_add_adapter(muxc, 0, 0, 0);
17162306a36Sopenharmony_ci	if (ret)
17262306a36Sopenharmony_ci		i2c_put_adapter(muxc->parent);
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	return ret;
17562306a36Sopenharmony_ci}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_cistatic void i2c_arbitrator_remove(struct platform_device *pdev)
17862306a36Sopenharmony_ci{
17962306a36Sopenharmony_ci	struct i2c_mux_core *muxc = platform_get_drvdata(pdev);
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	i2c_mux_del_adapters(muxc);
18262306a36Sopenharmony_ci	i2c_put_adapter(muxc->parent);
18362306a36Sopenharmony_ci}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_cistatic const struct of_device_id i2c_arbitrator_of_match[] = {
18662306a36Sopenharmony_ci	{ .compatible = "i2c-arb-gpio-challenge", },
18762306a36Sopenharmony_ci	{},
18862306a36Sopenharmony_ci};
18962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, i2c_arbitrator_of_match);
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_cistatic struct platform_driver i2c_arbitrator_driver = {
19262306a36Sopenharmony_ci	.probe	= i2c_arbitrator_probe,
19362306a36Sopenharmony_ci	.remove_new = i2c_arbitrator_remove,
19462306a36Sopenharmony_ci	.driver	= {
19562306a36Sopenharmony_ci		.name	= "i2c-arb-gpio-challenge",
19662306a36Sopenharmony_ci		.of_match_table = i2c_arbitrator_of_match,
19762306a36Sopenharmony_ci	},
19862306a36Sopenharmony_ci};
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_cimodule_platform_driver(i2c_arbitrator_driver);
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ciMODULE_DESCRIPTION("GPIO-based I2C Arbitration");
20362306a36Sopenharmony_ciMODULE_AUTHOR("Doug Anderson <dianders@chromium.org>");
20462306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
20562306a36Sopenharmony_ciMODULE_ALIAS("platform:i2c-arb-gpio-challenge");
206