new file mode 100644
@@ -0,0 +1,76 @@
+GPIO-based Arbitration used by the ARM Chromebook (exynos5250-snow)
+===================================================================
+This uses GPIO lines between the AP (Application Processor) 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
+
+This mechanism is used instead of standard I2C multimaster to avoid some of the
+subtle driver and silicon bugs that are often present with I2C multimaster.
+
+
+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).
+
+
+Required properties:
+- compatible: i2c-arbitrator-cros-ec
+- ap-claim-gpio: The GPIO that we (the AP) use to claim the bus.
+- ec-claim-gpio: The GPIO that the other side (the EC) uses the claim the bus.
+- Standard I2C mux properties. See mux.txt in this directory.
+- Single I2C child bus node at reg 0. See mux.txt in this directory.
+
+Optional properties:
+- slew-delay-us: microseconds to wait for a GPIO to go high. Default is 10 us.
+- wait-retry-us: we'll attempt another claim after this many microseconds.
+ Default is 3000 us.
+- wait-free-us: we'll give up after this many microseconds. Default is 50000 us.
+
+
+Example:
+ i2c@12CA0000 {
+ compatible = "acme,some-i2c-device";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+
+ i2c-arbitrator {
+ compatible = "i2c-arbitrator-cros-ec";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ i2c-parent = <&{/i2c@12CA0000}>;
+
+ ap-claim-gpio = <&gpf0 3 1 0x10000 0>;
+ ec-claim-gpio = <&gpe0 4 0 0x10003 0>;
+ slew-delay-us = <10>;
+ wait-retry-us = <3000>;
+ wait-free-us = <50000>;
+
+ i2c@0 {
+ reg = <0>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ i2c@52 {
+ // Normal I2C device
+ };
+ };
+ };
@@ -5,6 +5,17 @@
menu "Multiplexer I2C Chip support"
depends on I2C_MUX
+config I2C_ARBITRATOR_CROS_EC
+ tristate "GPIO-based I2C arbitrator used on exynos5250-snow"
+ depends on GENERIC_GPIO && OF
+ help
+ If you say yes to this option, support will be included for an
+ I2C multimaster arbitration scheme using GPIOs that is used in
+ the Samsung ARM Chromebook (exynos5250-snow).
+
+ This driver can also be built as a module. If so, the module
+ will be called i2c-arbitrator-cros-ec.
+
config I2C_MUX_GPIO
tristate "GPIO-based I2C multiplexer"
depends on GENERIC_GPIO
@@ -1,6 +1,8 @@
#
# Makefile for multiplexer I2C chip drivers.
+obj-$(CONFIG_I2C_ARBITRATOR_CROS_EC) += i2c-arbitrator-cros-ec.o
+
obj-$(CONFIG_I2C_MUX_GPIO) += i2c-mux-gpio.o
obj-$(CONFIG_I2C_MUX_PCA9541) += i2c-mux-pca9541.o
obj-$(CONFIG_I2C_MUX_PCA954x) += i2c-mux-pca954x.o
new file mode 100644
@@ -0,0 +1,222 @@
+/*
+ * I2C arbitrator driver for the ARM Chromebook
+ *
+ * Copyright (C) 2012 Google, Inc
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/kernel.h>
+#include <linux/i2c.h>
+#include <linux/i2c-mux.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/of_i2c.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+
+/**
+ * struct i2c_arbitrator_data - Driver data for I2C arbitrator
+ *
+ * @parent: Parent adapter
+ * @child: Child bus
+ * @ap_gpio: GPIO we'll use to claim.
+ * @ap_gpio_release: 0 if active high; 1 if active low; AKA if the GPIO == this
+ * then consider it released.
+ * @ec_gpio: GPIO that the other side will use to claim.
+ * @ec_gpio_release: 0 if active high; 1 if active low; AKA if the GPIO == this
+ * then consider it released.
+ * @slew_delay_us: microseconds to wait for a GPIO to go high.
+ * @wait_retry_us: we'll attempt another claim after this many microseconds.
+ * @wait_free_us: we'll give up after this many microseconds.
+ */
+
+struct i2c_arbitrator_data {
+ struct i2c_adapter *parent;
+ struct i2c_adapter *child;
+
+ int ap_gpio;
+ int ap_gpio_release;
+ int ec_gpio;
+ int ec_gpio_release;
+ unsigned int slew_delay_us;
+ unsigned int wait_retry_us;
+ unsigned int wait_free_us;
+};
+
+
+/**
+ * i2c_arbitrator_select - claim the I2C bus
+ *
+ * Use the GPIO-based signalling protocol; return -EBUSY if we fail.
+ */
+static int i2c_arbitrator_select(struct i2c_adapter *adap, void *data, u32 chan)
+{
+ const struct i2c_arbitrator_data *arb = data;
+ unsigned long stop_retry, stop_time;
+
+ /* Start a round of trying to claim the bus */
+ stop_time = jiffies + usecs_to_jiffies(arb->wait_free_us) + 1;
+ do {
+ /* Indicate that we want to claim the bus */
+ gpio_set_value(arb->ap_gpio, !arb->ap_gpio_release);
+ udelay(arb->slew_delay_us);
+
+ /* Wait for the EC to release it */
+ stop_retry = jiffies + usecs_to_jiffies(arb->wait_retry_us) + 1;
+ while (time_before(jiffies, stop_retry)) {
+ int gpio_val = !!gpio_get_value(arb->ec_gpio);
+
+ if (gpio_val == arb->ec_gpio_release) {
+ /* 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(arb->ap_gpio, arb->ap_gpio_release);
+
+ usleep_range(arb->wait_retry_us, arb->wait_retry_us * 2);
+ } while (time_before(jiffies, stop_time));
+
+ /* Give up, release our claim */
+ gpio_set_value(arb->ap_gpio, arb->ap_gpio_release);
+ udelay(arb->slew_delay_us);
+ dev_err(&adap->dev, "Could not claim bus, timeout\n");
+ return -EBUSY;
+}
+
+/**
+ * i2c_arbitrator_deselect - release the I2C bus
+ *
+ * Release the I2C bus using the GPIO-based signalling protocol.
+ */
+static int i2c_arbitrator_deselect(struct i2c_adapter *adap, void *data,
+ u32 chan)
+{
+ const struct i2c_arbitrator_data *arb = data;
+
+ /* Release the bus and wait for the EC to notice */
+ gpio_set_value(arb->ap_gpio, arb->ap_gpio_release);
+ udelay(arb->slew_delay_us);
+
+ return 0;
+}
+
+static int i2c_arbitrator_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct device_node *parent_np;
+ struct i2c_arbitrator_data *arb;
+ enum of_gpio_flags gpio_flags;
+ unsigned long out_init;
+ int ret;
+
+ /* We only support probing from device tree; no platform_data */
+ if (WARN_ON(!np))
+ return -ENODEV;
+ if (WARN_ON(pdev->dev.platform_data))
+ return -EINVAL;
+
+ arb = devm_kzalloc(&pdev->dev, sizeof(*arb), GFP_KERNEL);
+ if (WARN_ON(!arb))
+ return -ENOMEM;
+ platform_set_drvdata(pdev, arb);
+
+ /* Request GPIOs */
+ ret = of_get_named_gpio_flags(np, "ap-claim-gpio", 0, &gpio_flags);
+ if (ret == -EPROBE_DEFER || WARN_ON(!gpio_is_valid(ret)))
+ return ret;
+ arb->ap_gpio = ret;
+ arb->ap_gpio_release = !!(gpio_flags & OF_GPIO_ACTIVE_LOW);
+ out_init = (gpio_flags & OF_GPIO_ACTIVE_LOW) ?
+ GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW;
+ ret = devm_gpio_request_one(&pdev->dev, arb->ap_gpio, out_init,
+ "ap-claim-gpio");
+ if (ret == -EPROBE_DEFER || WARN_ON(ret))
+ return ret;
+
+ ret = of_get_named_gpio_flags(np, "ec-claim-gpio", 0, &gpio_flags);
+ if (ret == -EPROBE_DEFER || WARN_ON(!gpio_is_valid(ret)))
+ return ret;
+ arb->ec_gpio = ret;
+ arb->ec_gpio_release = !!(gpio_flags & OF_GPIO_ACTIVE_LOW);
+ ret = devm_gpio_request_one(&pdev->dev, arb->ec_gpio, GPIOF_IN,
+ "ec-claim-gpio");
+ if (ret == -EPROBE_DEFER || WARN_ON(ret))
+ return ret;
+
+ /* Arbitration parameters */
+ if (of_property_read_u32(np, "slew-delay-us", &arb->slew_delay_us))
+ arb->slew_delay_us = 10;
+ if (of_property_read_u32(np, "wait-retry-us", &arb->wait_retry_us))
+ arb->wait_retry_us = 3000;
+ if (of_property_read_u32(np, "wait-free-us", &arb->wait_free_us))
+ arb->wait_free_us = 50000;
+
+ /* Find our parent */
+ parent_np = of_parse_phandle(np, "i2c-parent", 0);
+ if (WARN_ON(!parent_np))
+ return -EINVAL;
+ arb->parent = of_find_i2c_adapter_by_node(parent_np);
+ if (WARN_ON(!arb->parent))
+ return -EINVAL;
+
+ /* Actually add the mux adapter */
+ arb->child = i2c_add_mux_adapter(arb->parent, &pdev->dev, arb, 0, 0, 0,
+ i2c_arbitrator_select,
+ i2c_arbitrator_deselect);
+ if (WARN_ON(!arb->child)) {
+ ret = -ENODEV;
+ i2c_put_adapter(arb->parent);
+ }
+
+ return ret;
+}
+
+static int i2c_arbitrator_remove(struct platform_device *pdev)
+{
+ struct i2c_arbitrator_data *arb = platform_get_drvdata(pdev);
+
+ i2c_del_mux_adapter(arb->child);
+ i2c_put_adapter(arb->parent);
+
+ return 0;
+}
+
+static const struct of_device_id i2c_arbitrator_of_match[] = {
+ { .compatible = "i2c-arbitrator-cros-ec", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, i2c_arbitrator_of_match);
+
+static struct platform_driver i2c_arbitrator_driver = {
+ .probe = i2c_arbitrator_probe,
+ .remove = i2c_arbitrator_remove,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "i2c-arbitrator-cros-ec",
+ .of_match_table = of_match_ptr(i2c_arbitrator_of_match),
+ },
+};
+
+module_platform_driver(i2c_arbitrator_driver);
+
+MODULE_DESCRIPTION("I2C arbitrator driver for the ARM Chromebook");
+MODULE_AUTHOR("Doug Anderson <dianders@chromium.org>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:i2c-arbitrator-cros-ec");