[PATCH 1/2] i2c-core: Add gpio based bus arbitration implementation

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



The arbitrator is a general purpose function which uses two GPIOs to
communicate with another device to claim/release a bus.

i2c_transfer()
	if adapter->gpio_arbit
		i2c_bus_claim();
			__i2c_transfer();
		i2c_bus_release();

Signed-off-by: Simon Glass <sjg@xxxxxxxxxxxx>
Cc: Grant Grundler <grundler@xxxxxxxxxxxx>
Signed-off-by: Naveen Krishna Chatradhi <ch.naveen@xxxxxxxxxxx>
---
 .../devicetree/bindings/i2c/arbitrator-i2c.txt     |   56 ++++++++++++++++
 drivers/i2c/i2c-core.c                             |   67 ++++++++++++++++++++
 drivers/of/of_i2c.c                                |   27 ++++++++
 include/linux/i2c.h                                |   17 +++++
 include/linux/of_i2c.h                             |    2 +
 5 files changed, 169 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/i2c/arbitrator-i2c.txt

diff --git a/Documentation/devicetree/bindings/i2c/arbitrator-i2c.txt b/Documentation/devicetree/bindings/i2c/arbitrator-i2c.txt
new file mode 100644
index 0000000..cb91ea8
--- /dev/null
+++ b/Documentation/devicetree/bindings/i2c/arbitrator-i2c.txt
@@ -0,0 +1,56 @@
+Device-Tree bindings for i2c gpio based bus arbitrator
+
+bus-arbitration-gpios :
+	Two GPIOs to use with the GPIO-based bus arbitration protocol
+(see below).
+The first should be an output, and is used to claim the I2C bus,
+the second should be an input, and signals that the other side (Client)
+wants to claim the bus. This allows two masters to share the same I2C bus.
+
+Required properties:
+	- bus-needs-gpio-arbitration;
+	- bus-arbitration-gpios: AP_CLAIM and Client_CLAIM gpios
+	- bus-arbitration-slew-delay-us:
+	- bus-arbitration-wait-retry-us:
+	- bus-arbitration-wait-free-us:
+
+Example nodes:
+
+i2c@0 {
+	/* If you want GPIO-based bus arbitration */
+	bus-needs-gpio-arbitration;
+	bus-arbitration-gpios = <&gpf0 3 1 0 0>, /* AP_CLAIM */
+		<&gpe0 4 0 3 0>; /* EC_CLAIM */
+
+	bus-arbitration-slew-delay-us = <10>;
+	bus-arbitration-wait-retry-us = <2000>;
+	bus-arbitration-wait-free-us = <50000>;
+};
+
+GPIO-based Arbitration
+======================
+This uses GPIO lines between the AP (SoC) and an attached EC (embedded
+controller) which both want to talk on the same I2C bus as master.
+
+The AP and EC each have a 'bus claim' line, which is an output that the
+other can see. These are both active low, with pull-ups enabled.
+
+- AP_CLAIM: output from AP, signalling to the EC that the AP wants the bus
+- EC_CLAIM: output from EC, signalling to the AP that the EC wants the bus
+
+
+Algorithm
+---------
+The basic algorithm is to assert your line when you want the bus, then make
+sure that the other side doesn't want it also. A detailed explanation is best
+done with an example.
+
+Let's say the AP wants to claim the bus. It:
+1. Asserts AP_CLAIM
+2. Waits a little bit for the other side to notice (slew time, say 10
+microseconds)
+3. Checks EC_CLAIM. If this is not asserted, then the AP has the bus, and
+we are done
+4. Otherwise, wait for a few milliseconds and see if EC_CLAIM is released
+5. If not, back off, release the claim and wait for a few more milliseconds
+6. Go back to 1 (until retry time has expired)
diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c
index a7edf98..222a6da 100644
--- a/drivers/i2c/i2c-core.c
+++ b/drivers/i2c/i2c-core.c
@@ -32,6 +32,7 @@
 #include <linux/i2c.h>
 #include <linux/init.h>
 #include <linux/idr.h>
+#include <linux/delay.h>
 #include <linux/mutex.h>
 #include <linux/of_device.h>
 #include <linux/completion.h>
@@ -39,6 +40,7 @@
 #include <linux/irqflags.h>
 #include <linux/rwsem.h>
 #include <linux/pm_runtime.h>
+#include <linux/gpio.h>
 #include <asm/uaccess.h>
 
 #include "i2c-core.h"
@@ -1256,6 +1258,59 @@ void i2c_release_client(struct i2c_client *client)
 }
 EXPORT_SYMBOL(i2c_release_client);
 
+/**
+ * If we have enabled arbitration on this bus, claim the i2c bus, using
+ * the GPIO-based signalling protocol.
+ */
+static int i2c_bus_claim(struct i2c_gpio_arbit *i2c_arbit)
+{
+	unsigned long stop_retry, stop_time;
+	unsigned int gpio;
+
+	/* Start a round of trying to claim the bus */
+	stop_time = jiffies + usecs_to_jiffies(i2c_arbit->wait_free_us) + 1;
+	do {
+		/* Indicate that we want to claim the bus */
+		gpio_set_value(i2c_arbit->arb_gpios[I2C_ARB_GPIO_AP], 0);
+		udelay(i2c_arbit->slew_delay_us);
+
+		/* Wait for the EC to release it */
+		stop_retry = jiffies +
+			usecs_to_jiffies(i2c_arbit->wait_retry_us) + 1;
+		while (time_before(jiffies, stop_retry)) {
+			gpio = i2c_arbit->arb_gpios[I2C_ARB_GPIO_EC];
+			if (gpio_get_value(gpio)) {
+				/* We got it, so return */
+				return 0;
+			}
+
+			usleep_range(50, 200);
+		}
+
+		/* It didn't release, so give up, wait, and try again */
+		gpio_set_value(i2c_arbit->arb_gpios[I2C_ARB_GPIO_AP], 1);
+
+		usleep_range(i2c_arbit->wait_retry_us,
+					i2c_arbit->wait_retry_us * 2);
+	} while (time_before(jiffies, stop_time));
+
+	/* Give up, release our claim */
+	gpio_set_value(i2c_arbit->arb_gpios[I2C_ARB_GPIO_AP], 1);
+	udelay(i2c_arbit->slew_delay_us);
+
+	return -EBUSY;
+}
+
+/**
+ * If we have enabled arbitration on this bus, release the i2c bus.
+ */
+static void i2c_bus_release(struct i2c_gpio_arbit *i2c_arbit)
+{
+	/* Release the bus and wait for the EC to notice */
+	gpio_set_value(i2c_arbit->arb_gpios[I2C_ARB_GPIO_AP], 1);
+	udelay(i2c_arbit->slew_delay_us);
+}
+
 struct i2c_cmd_arg {
 	unsigned	cmd;
 	void		*arg;
@@ -1412,7 +1467,19 @@ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
 			i2c_lock_adapter(adap);
 		}
 
+		if (adap->gpio_arbit) {
+			if (i2c_bus_claim(adap->gpio_arbit)) {
+				dev_err(&adap->dev, "I2C: Could not claim bus, timeout\n");
+				return -EBUSY;
+			}
+		}
+
 		ret = __i2c_transfer(adap, msgs, num);
+
+		/* Release the bus if needed */
+		if (adap->gpio_arbit)
+			i2c_bus_release(adap->gpio_arbit);
+
 		i2c_unlock_adapter(adap);
 
 		return ret;
diff --git a/drivers/of/of_i2c.c b/drivers/of/of_i2c.c
index 3550f3b..4ec3f44 100644
--- a/drivers/of/of_i2c.c
+++ b/drivers/of/of_i2c.c
@@ -111,4 +111,31 @@ struct i2c_adapter *of_find_i2c_adapter_by_node(struct device_node *node)
 }
 EXPORT_SYMBOL(of_find_i2c_adapter_by_node);
 
+/*
+ * Property "bus-needs-gpio-arbitration" in DT node enables
+ * GPIO based bus arbitration.
+ * Populates the default timing values if not specified.
+ *
+ * Returns NULL, if "bus-needs-gpio-arbitration" is not specified.
+ */
+struct i2c_gpio_arbit *of_get_arbitrator_info(struct device_node *np,
+				struct i2c_gpio_arbit *i2c_arbit)
+{
+	if (of_get_property(np, "bus-needs-gpio-arbitration", NULL)) {
+		if (of_property_read_u32(np, "bus-arbitration-slew-delay-us",
+				&i2c_arbit->slew_delay_us))
+			i2c_arbit->slew_delay_us = 10;
+		if (of_property_read_u32(np, "bus-arbitration-wait-retry-us",
+				&i2c_arbit->wait_retry_us))
+			i2c_arbit->wait_retry_us = 2000;
+		if (of_property_read_u32(np, "bus-arbitration-wait-free-us",
+				&i2c_arbit->wait_free_us))
+			i2c_arbit->wait_free_us = 50000;
+	} else
+		i2c_arbit = NULL;
+
+	return i2c_arbit;
+}
+EXPORT_SYMBOL_GPL(of_get_arbitrator_info);
+
 MODULE_LICENSE("GPL");
diff --git a/include/linux/i2c.h b/include/linux/i2c.h
index 800de22..d1ae491 100644
--- a/include/linux/i2c.h
+++ b/include/linux/i2c.h
@@ -295,6 +295,22 @@ struct i2c_board_info {
 #define I2C_BOARD_INFO(dev_type, dev_addr) \
 	.type = dev_type, .addr = (dev_addr)
 
+enum {
+	I2C_ARB_GPIO_AP,		/* AP claims i2c bus */
+	I2C_ARB_GPIO_EC,		/* EC claims i2c bus */
+
+	I2C_ARB_GPIO_COUNT,
+};
+
+/* I2C bus GPIO based arbitration information */
+struct i2c_gpio_arbit {
+	int			arb_gpios[I2C_ARB_GPIO_COUNT];
+
+	/* Arbitration parameters */
+	unsigned int		slew_delay_us;
+	unsigned int		wait_retry_us;
+	unsigned int		wait_free_us;
+};
 
 #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
 /* Add-on boards should register/unregister their devices; e.g. a board
@@ -388,6 +404,7 @@ struct i2c_adapter {
 	int nr;
 	char name[48];
 	struct completion dev_released;
+	struct i2c_gpio_arbit *gpio_arbit;
 
 	struct mutex userspace_clients_lock;
 	struct list_head userspace_clients;
diff --git a/include/linux/of_i2c.h b/include/linux/of_i2c.h
index 1cb775f..c57ffba 100644
--- a/include/linux/of_i2c.h
+++ b/include/linux/of_i2c.h
@@ -24,6 +24,8 @@ extern struct i2c_client *of_find_i2c_device_by_node(struct device_node *node);
 extern struct i2c_adapter *of_find_i2c_adapter_by_node(
 						struct device_node *node);
 
+extern struct i2c_gpio_arbit *of_get_arbitrator_info(struct device_node *node,
+					struct i2c_gpio_arbit *i2c_arbit);
 #else
 static inline void of_i2c_register_devices(struct i2c_adapter *adap)
 {
-- 
1.7.9.5

--
To unsubscribe from this list: send the line "unsubscribe linux-i2c" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux GPIO]     [Linux SPI]     [Linux Hardward Monitoring]     [LM Sensors]     [Linux USB Devel]     [Linux Media]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux