diff mbox

[U-Boot,08/55] i2c: Add a mux for GPIO-based I2C bus arbitration

Message ID 1435882592-487-9-git-send-email-sjg@chromium.org
State Superseded
Delegated to: Simon Glass
Headers show

Commit Message

Simon Glass July 3, 2015, 12:15 a.m. UTC
While I2C supports multi-master buses this is difficult to get right. This
driver provides a scheme based on two 'claim' GPIOs, one driven by the AP
and one driver by the EC. With these they can communicate and reliably
share the bus. This scheme has minimal overhead and involves very little
code. It is used on snow to permit the EC and the AP to share access to
the main system PMIC and battery. The scheme can survive reboots by either
side without difficulty.

Signed-off-by: Simon Glass <sjg@chromium.org>
---

 drivers/i2c/muxes/Kconfig                  |   9 ++
 drivers/i2c/muxes/Makefile                 |   1 +
 drivers/i2c/muxes/i2c-arb-gpio-challenge.c | 147 +++++++++++++++++++++++++++++
 3 files changed, 157 insertions(+)
 create mode 100644 drivers/i2c/muxes/i2c-arb-gpio-challenge.c

Comments

Heiko Schocher July 5, 2015, 6:43 a.m. UTC | #1
Hello Simon,

Am 03.07.2015 um 02:15 schrieb Simon Glass:
> While I2C supports multi-master buses this is difficult to get right. This

What do you mean here? Where are the problems? You have an i2c mux, or?

> driver provides a scheme based on two 'claim' GPIOs, one driven by the AP
> and one driver by the EC. With these they can communicate and reliably

What is AP and EC ?

> share the bus. This scheme has minimal overhead and involves very little
> code. It is used on snow to permit the EC and the AP to share access to
> the main system PMIC and battery. The scheme can survive reboots by either
> side without difficulty.
>
> Signed-off-by: Simon Glass <sjg@chromium.org>
> ---
>
>   drivers/i2c/muxes/Kconfig                  |   9 ++
>   drivers/i2c/muxes/Makefile                 |   1 +
>   drivers/i2c/muxes/i2c-arb-gpio-challenge.c | 147 +++++++++++++++++++++++++++++

Nice!

Could you add a readme, which explains this a little bit more?

>   3 files changed, 157 insertions(+)
>   create mode 100644 drivers/i2c/muxes/i2c-arb-gpio-challenge.c
>
> diff --git a/drivers/i2c/muxes/Kconfig b/drivers/i2c/muxes/Kconfig
> index a05b32d..bd3e078 100644
> --- a/drivers/i2c/muxes/Kconfig
> +++ b/drivers/i2c/muxes/Kconfig
> @@ -6,3 +6,12 @@ config I2C_MUX
>   	  one of several buses using some sort of control mechanism. The
>   	  bus select is handled automatically when that bus is accessed,
>   	  using a suitable I2C MUX driver.
> +
> +config I2C_ARB_GPIO_CHALLENGE
> +        bool "GPIO-based I2C arbitration"
> +        depends on I2C_MUX
> +        help
> +          If you say yes to this option, support will be included for an
> +          I2C multimaster arbitration scheme using GPIOs and a challenge &
> +          response mechanism where masters have to claim the bus by asserting
> +          a GPIO.
> diff --git a/drivers/i2c/muxes/Makefile b/drivers/i2c/muxes/Makefile
> index 7583e3a..612cc27 100644
> --- a/drivers/i2c/muxes/Makefile
> +++ b/drivers/i2c/muxes/Makefile
> @@ -3,4 +3,5 @@
>   #
>   # SPDX-License-Identifier:	GPL-2.0+
>   #
> +obj-$(CONFIG_I2C_ARB_GPIO_CHALLENGE) += i2c-arb-gpio-challenge.o
>   obj-$(CONFIG_I2C_MUX) += i2c-mux-uclass.o
> diff --git a/drivers/i2c/muxes/i2c-arb-gpio-challenge.c b/drivers/i2c/muxes/i2c-arb-gpio-challenge.c
> new file mode 100644
> index 0000000..3f072c7
> --- /dev/null
> +++ b/drivers/i2c/muxes/i2c-arb-gpio-challenge.c
> @@ -0,0 +1,147 @@
> +/*
> + * Copyright (c) 2015 Google, Inc
> + * Written by Simon Glass <sjg@chromium.org>
> + *
> + * SPDX-License-Identifier:	GPL-2.0+
> + */
> +
> +#include <common.h>
> +#include <dm.h>
> +#include <errno.h>
> +#include <i2c.h>
> +#include <asm/gpio.h>
> +
> +DECLARE_GLOBAL_DATA_PTR;
> +
> +struct i2c_arbitrator_priv {
> +	struct gpio_desc ap_claim;
> +	struct gpio_desc ec_claim;
> +	uint slew_delay_us;
> +	uint wait_retry_ms;
> +	uint wait_free_ms;
> +};
> +
> +int i2c_arbitrator_deselect(struct udevice *mux, struct udevice *bus,
> +			    uint channel)
> +{
> +	struct i2c_arbitrator_priv *priv = dev_get_priv(mux);
> +	int ret;
> +
> +	debug("%s: %s\n", __func__, mux->name);
> +	ret = dm_gpio_set_value(&priv->ap_claim, 0);
> +	udelay(priv->slew_delay_us);
> +
> +	return ret;
> +}
> +
> +int i2c_arbitrator_select(struct udevice *mux, struct udevice *bus,
> +			  uint channel)
> +{
> +	struct i2c_arbitrator_priv *priv = dev_get_priv(mux);
> +	unsigned start;
> +	int ret;
> +
> +	debug("%s: %s\n", __func__, mux->name);
> +	/* Start a round of trying to claim the bus */
> +	start = get_timer(0);
> +	do {
> +		unsigned start_retry;
> +		int waiting = 0;
> +
> +		/* Indicate that we want to claim the bus */
> +		ret = dm_gpio_set_value(&priv->ap_claim, 1);
> +		if (ret)
> +			goto err;
> +		udelay(priv->slew_delay_us);
> +
> +		/* Wait for the EC to release it */
> +		start_retry = get_timer(0);
> +		while (get_timer(start_retry) < priv->wait_retry_ms) {
> +			ret = dm_gpio_get_value(&priv->ec_claim);
> +			if (ret < 0) {
> +				goto err;
> +			} else if (!ret) {
> +				/* We got it, so return */
> +				return 0;
> +			}
> +
> +			if (!waiting)
> +				waiting = 1;
> +		}
> +
> +		/* It didn't release, so give up, wait, and try again */
> +		ret = dm_gpio_set_value(&priv->ap_claim, 0);
> +		if (ret)
> +			goto err;
> +
> +		mdelay(priv->wait_retry_ms);
> +	} while (get_timer(start) < priv->wait_free_ms);
> +
> +	/* Give up, release our claim */
> +	printf("I2C: Could not claim bus, timeout %lu\n", get_timer(start));
> +	ret = -ETIMEDOUT;
> +	ret = 0;
> +err:
> +	return ret;
> +}
> +
> +static int i2c_arbitrator_probe(struct udevice *dev)
> +{
> +	struct i2c_arbitrator_priv *priv = dev_get_priv(dev);
> +	const void *blob = gd->fdt_blob;
> +	int node = dev->of_offset;
> +	int ret;
> +
> +	debug("%s: %s\n", __func__, dev->name);
> +	priv->slew_delay_us = fdtdec_get_int(blob, node, "slew-delay-us", 0);
> +	priv->wait_retry_ms = fdtdec_get_int(blob, node, "wait-retry-us", 0) /
> +		1000;
> +	priv->wait_free_ms = fdtdec_get_int(blob, node, "wait-free-us", 0) /
> +		1000;
> +	ret = gpio_request_by_name(dev, "our-claim-gpio", 0, &priv->ap_claim,
> +				   GPIOD_IS_OUT);
> +	if (ret)
> +		goto err;
> +	ret = gpio_request_by_name(dev, "their-claim-gpios", 0, &priv->ec_claim,
> +				   GPIOD_IS_IN);

What is "our" and "their"?

bye,
Heiko
> +	if (ret)
> +		goto err_ec_gpio;
> +
> +	return 0;
> +
> +err_ec_gpio:
> +	dm_gpio_free(dev, &priv->ap_claim);
> +err:
> +	debug("%s: ret=%d\n", __func__, ret);
> +	return ret;
> +}
> +
> +static int i2c_arbitrator_remove(struct udevice *dev)
> +{
> +	struct i2c_arbitrator_priv *priv = dev_get_priv(dev);
> +
> +	dm_gpio_free(dev, &priv->ap_claim);
> +	dm_gpio_free(dev, &priv->ec_claim);
> +
> +	return 0;
> +}
> +
> +static const struct i2c_mux_ops i2c_arbitrator_ops = {
> +	.select		= i2c_arbitrator_select,
> +	.deselect	= i2c_arbitrator_deselect,
> +};
> +
> +static const struct udevice_id i2c_arbitrator_ids[] = {
> +	{ .compatible = "i2c-arb-gpio-challenge" },
> +	{ }
> +};
> +
> +U_BOOT_DRIVER(i2c_arbitrator) = {
> +	.name = "i2c_arbitrator",
> +	.id = UCLASS_I2C_MUX,
> +	.of_match = i2c_arbitrator_ids,
> +	.probe = i2c_arbitrator_probe,
> +	.remove = i2c_arbitrator_remove,
> +	.ops = &i2c_arbitrator_ops,
> +	.priv_auto_alloc_size = sizeof(struct i2c_arbitrator_priv),
> +};
>
Simon Glass July 6, 2015, 4:38 p.m. UTC | #2
Hi Heiko,

On 5 July 2015 at 00:43, Heiko Schocher <hs@denx.de> wrote:
> Hello Simon,
>
> Am 03.07.2015 um 02:15 schrieb Simon Glass:
>>
>> While I2C supports multi-master buses this is difficult to get right. This
>
>
> What do you mean here? Where are the problems? You have an i2c mux, or?
>
>> driver provides a scheme based on two 'claim' GPIOs, one driven by the AP
>> and one driver by the EC. With these they can communicate and reliably
>
>
> What is AP and EC ?

Application Processor, i.e. the main CPU (e.,g. Cortex-A15 SoC)
Embedded controller (e.g. Cortex-M3 microcontroller)

I'll update the commit message.
>
>> share the bus. This scheme has minimal overhead and involves very little
>> code. It is used on snow to permit the EC and the AP to share access to
>> the main system PMIC and battery. The scheme can survive reboots by either
>> side without difficulty.
>>
>> Signed-off-by: Simon Glass <sjg@chromium.org>
>> ---
>>
>>   drivers/i2c/muxes/Kconfig                  |   9 ++
>>   drivers/i2c/muxes/Makefile                 |   1 +
>>   drivers/i2c/muxes/i2c-arb-gpio-challenge.c | 147
>> +++++++++++++++++++++++++++++
>
>
> Nice!
>
> Could you add a readme, which explains this a little bit more?

Will do.

>
>
>>   3 files changed, 157 insertions(+)
>>   create mode 100644 drivers/i2c/muxes/i2c-arb-gpio-challenge.c
>>
>> diff --git a/drivers/i2c/muxes/Kconfig b/drivers/i2c/muxes/Kconfig
>> index a05b32d..bd3e078 100644
>> --- a/drivers/i2c/muxes/Kconfig
>> +++ b/drivers/i2c/muxes/Kconfig
>> @@ -6,3 +6,12 @@ config I2C_MUX
>>           one of several buses using some sort of control mechanism. The
>>           bus select is handled automatically when that bus is accessed,
>>           using a suitable I2C MUX driver.
>> +
>> +config I2C_ARB_GPIO_CHALLENGE
>> +        bool "GPIO-based I2C arbitration"
>> +        depends on I2C_MUX
>> +        help
>> +          If you say yes to this option, support will be included for an
>> +          I2C multimaster arbitration scheme using GPIOs and a challenge
>> &
>> +          response mechanism where masters have to claim the bus by
>> asserting
>> +          a GPIO.
>> diff --git a/drivers/i2c/muxes/Makefile b/drivers/i2c/muxes/Makefile
>> index 7583e3a..612cc27 100644
>> --- a/drivers/i2c/muxes/Makefile
>> +++ b/drivers/i2c/muxes/Makefile
>> @@ -3,4 +3,5 @@
>>   #
>>   # SPDX-License-Identifier:    GPL-2.0+
>>   #
>> +obj-$(CONFIG_I2C_ARB_GPIO_CHALLENGE) += i2c-arb-gpio-challenge.o
>>   obj-$(CONFIG_I2C_MUX) += i2c-mux-uclass.o
>> diff --git a/drivers/i2c/muxes/i2c-arb-gpio-challenge.c
>> b/drivers/i2c/muxes/i2c-arb-gpio-challenge.c
>> new file mode 100644
>> index 0000000..3f072c7
>> --- /dev/null
>> +++ b/drivers/i2c/muxes/i2c-arb-gpio-challenge.c
>> @@ -0,0 +1,147 @@
>> +/*
>> + * Copyright (c) 2015 Google, Inc
>> + * Written by Simon Glass <sjg@chromium.org>
>> + *
>> + * SPDX-License-Identifier:    GPL-2.0+
>> + */
>> +
>> +#include <common.h>
>> +#include <dm.h>
>> +#include <errno.h>
>> +#include <i2c.h>
>> +#include <asm/gpio.h>
>> +
>> +DECLARE_GLOBAL_DATA_PTR;
>> +
>> +struct i2c_arbitrator_priv {
>> +       struct gpio_desc ap_claim;
>> +       struct gpio_desc ec_claim;
>> +       uint slew_delay_us;
>> +       uint wait_retry_ms;
>> +       uint wait_free_ms;
>> +};
>> +
>> +int i2c_arbitrator_deselect(struct udevice *mux, struct udevice *bus,
>> +                           uint channel)
>> +{
>> +       struct i2c_arbitrator_priv *priv = dev_get_priv(mux);
>> +       int ret;
>> +
>> +       debug("%s: %s\n", __func__, mux->name);
>> +       ret = dm_gpio_set_value(&priv->ap_claim, 0);
>> +       udelay(priv->slew_delay_us);
>> +
>> +       return ret;
>> +}
>> +
>> +int i2c_arbitrator_select(struct udevice *mux, struct udevice *bus,
>> +                         uint channel)
>> +{
>> +       struct i2c_arbitrator_priv *priv = dev_get_priv(mux);
>> +       unsigned start;
>> +       int ret;
>> +
>> +       debug("%s: %s\n", __func__, mux->name);
>> +       /* Start a round of trying to claim the bus */
>> +       start = get_timer(0);
>> +       do {
>> +               unsigned start_retry;
>> +               int waiting = 0;
>> +
>> +               /* Indicate that we want to claim the bus */
>> +               ret = dm_gpio_set_value(&priv->ap_claim, 1);
>> +               if (ret)
>> +                       goto err;
>> +               udelay(priv->slew_delay_us);
>> +
>> +               /* Wait for the EC to release it */
>> +               start_retry = get_timer(0);
>> +               while (get_timer(start_retry) < priv->wait_retry_ms) {
>> +                       ret = dm_gpio_get_value(&priv->ec_claim);
>> +                       if (ret < 0) {
>> +                               goto err;
>> +                       } else if (!ret) {
>> +                               /* We got it, so return */
>> +                               return 0;
>> +                       }
>> +
>> +                       if (!waiting)
>> +                               waiting = 1;
>> +               }
>> +
>> +               /* It didn't release, so give up, wait, and try again */
>> +               ret = dm_gpio_set_value(&priv->ap_claim, 0);
>> +               if (ret)
>> +                       goto err;
>> +
>> +               mdelay(priv->wait_retry_ms);
>> +       } while (get_timer(start) < priv->wait_free_ms);
>> +
>> +       /* Give up, release our claim */
>> +       printf("I2C: Could not claim bus, timeout %lu\n",
>> get_timer(start));
>> +       ret = -ETIMEDOUT;
>> +       ret = 0;
>> +err:
>> +       return ret;
>> +}
>> +
>> +static int i2c_arbitrator_probe(struct udevice *dev)
>> +{
>> +       struct i2c_arbitrator_priv *priv = dev_get_priv(dev);
>> +       const void *blob = gd->fdt_blob;
>> +       int node = dev->of_offset;
>> +       int ret;
>> +
>> +       debug("%s: %s\n", __func__, dev->name);
>> +       priv->slew_delay_us = fdtdec_get_int(blob, node, "slew-delay-us",
>> 0);
>> +       priv->wait_retry_ms = fdtdec_get_int(blob, node, "wait-retry-us",
>> 0) /
>> +               1000;
>> +       priv->wait_free_ms = fdtdec_get_int(blob, node, "wait-free-us", 0)
>> /
>> +               1000;
>> +       ret = gpio_request_by_name(dev, "our-claim-gpio", 0,
>> &priv->ap_claim,
>> +                                  GPIOD_IS_OUT);
>> +       if (ret)
>> +               goto err;
>> +       ret = gpio_request_by_name(dev, "their-claim-gpios", 0,
>> &priv->ec_claim,
>> +                                  GPIOD_IS_IN);
>
>
> What is "our" and "their"?

'our' means the one 'we' use, i.e. the AP. This is the binding used by
the kernel so I have kept it the same.

>
> bye,
> Heiko
>
>> +       if (ret)
>> +               goto err_ec_gpio;
>> +
>> +       return 0;
>> +
>> +err_ec_gpio:
>> +       dm_gpio_free(dev, &priv->ap_claim);
>> +err:
>> +       debug("%s: ret=%d\n", __func__, ret);
>> +       return ret;
>> +}
>> +
>> +static int i2c_arbitrator_remove(struct udevice *dev)
>> +{
>> +       struct i2c_arbitrator_priv *priv = dev_get_priv(dev);
>> +
>> +       dm_gpio_free(dev, &priv->ap_claim);
>> +       dm_gpio_free(dev, &priv->ec_claim);
>> +
>> +       return 0;
>> +}
>> +
>> +static const struct i2c_mux_ops i2c_arbitrator_ops = {
>> +       .select         = i2c_arbitrator_select,
>> +       .deselect       = i2c_arbitrator_deselect,
>> +};
>> +
>> +static const struct udevice_id i2c_arbitrator_ids[] = {
>> +       { .compatible = "i2c-arb-gpio-challenge" },
>> +       { }
>> +};
>> +
>> +U_BOOT_DRIVER(i2c_arbitrator) = {
>> +       .name = "i2c_arbitrator",
>> +       .id = UCLASS_I2C_MUX,
>> +       .of_match = i2c_arbitrator_ids,
>> +       .probe = i2c_arbitrator_probe,
>> +       .remove = i2c_arbitrator_remove,
>> +       .ops = &i2c_arbitrator_ops,
>> +       .priv_auto_alloc_size = sizeof(struct i2c_arbitrator_priv),
>> +};
>>

Regards,
Simon
diff mbox

Patch

diff --git a/drivers/i2c/muxes/Kconfig b/drivers/i2c/muxes/Kconfig
index a05b32d..bd3e078 100644
--- a/drivers/i2c/muxes/Kconfig
+++ b/drivers/i2c/muxes/Kconfig
@@ -6,3 +6,12 @@  config I2C_MUX
 	  one of several buses using some sort of control mechanism. The
 	  bus select is handled automatically when that bus is accessed,
 	  using a suitable I2C MUX driver.
+
+config I2C_ARB_GPIO_CHALLENGE
+        bool "GPIO-based I2C arbitration"
+        depends on I2C_MUX
+        help
+          If you say yes to this option, support will be included for an
+          I2C multimaster arbitration scheme using GPIOs and a challenge &
+          response mechanism where masters have to claim the bus by asserting
+          a GPIO.
diff --git a/drivers/i2c/muxes/Makefile b/drivers/i2c/muxes/Makefile
index 7583e3a..612cc27 100644
--- a/drivers/i2c/muxes/Makefile
+++ b/drivers/i2c/muxes/Makefile
@@ -3,4 +3,5 @@ 
 #
 # SPDX-License-Identifier:	GPL-2.0+
 #
+obj-$(CONFIG_I2C_ARB_GPIO_CHALLENGE) += i2c-arb-gpio-challenge.o
 obj-$(CONFIG_I2C_MUX) += i2c-mux-uclass.o
diff --git a/drivers/i2c/muxes/i2c-arb-gpio-challenge.c b/drivers/i2c/muxes/i2c-arb-gpio-challenge.c
new file mode 100644
index 0000000..3f072c7
--- /dev/null
+++ b/drivers/i2c/muxes/i2c-arb-gpio-challenge.c
@@ -0,0 +1,147 @@ 
+/*
+ * Copyright (c) 2015 Google, Inc
+ * Written by Simon Glass <sjg@chromium.org>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <i2c.h>
+#include <asm/gpio.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct i2c_arbitrator_priv {
+	struct gpio_desc ap_claim;
+	struct gpio_desc ec_claim;
+	uint slew_delay_us;
+	uint wait_retry_ms;
+	uint wait_free_ms;
+};
+
+int i2c_arbitrator_deselect(struct udevice *mux, struct udevice *bus,
+			    uint channel)
+{
+	struct i2c_arbitrator_priv *priv = dev_get_priv(mux);
+	int ret;
+
+	debug("%s: %s\n", __func__, mux->name);
+	ret = dm_gpio_set_value(&priv->ap_claim, 0);
+	udelay(priv->slew_delay_us);
+
+	return ret;
+}
+
+int i2c_arbitrator_select(struct udevice *mux, struct udevice *bus,
+			  uint channel)
+{
+	struct i2c_arbitrator_priv *priv = dev_get_priv(mux);
+	unsigned start;
+	int ret;
+
+	debug("%s: %s\n", __func__, mux->name);
+	/* Start a round of trying to claim the bus */
+	start = get_timer(0);
+	do {
+		unsigned start_retry;
+		int waiting = 0;
+
+		/* Indicate that we want to claim the bus */
+		ret = dm_gpio_set_value(&priv->ap_claim, 1);
+		if (ret)
+			goto err;
+		udelay(priv->slew_delay_us);
+
+		/* Wait for the EC to release it */
+		start_retry = get_timer(0);
+		while (get_timer(start_retry) < priv->wait_retry_ms) {
+			ret = dm_gpio_get_value(&priv->ec_claim);
+			if (ret < 0) {
+				goto err;
+			} else if (!ret) {
+				/* We got it, so return */
+				return 0;
+			}
+
+			if (!waiting)
+				waiting = 1;
+		}
+
+		/* It didn't release, so give up, wait, and try again */
+		ret = dm_gpio_set_value(&priv->ap_claim, 0);
+		if (ret)
+			goto err;
+
+		mdelay(priv->wait_retry_ms);
+	} while (get_timer(start) < priv->wait_free_ms);
+
+	/* Give up, release our claim */
+	printf("I2C: Could not claim bus, timeout %lu\n", get_timer(start));
+	ret = -ETIMEDOUT;
+	ret = 0;
+err:
+	return ret;
+}
+
+static int i2c_arbitrator_probe(struct udevice *dev)
+{
+	struct i2c_arbitrator_priv *priv = dev_get_priv(dev);
+	const void *blob = gd->fdt_blob;
+	int node = dev->of_offset;
+	int ret;
+
+	debug("%s: %s\n", __func__, dev->name);
+	priv->slew_delay_us = fdtdec_get_int(blob, node, "slew-delay-us", 0);
+	priv->wait_retry_ms = fdtdec_get_int(blob, node, "wait-retry-us", 0) /
+		1000;
+	priv->wait_free_ms = fdtdec_get_int(blob, node, "wait-free-us", 0) /
+		1000;
+	ret = gpio_request_by_name(dev, "our-claim-gpio", 0, &priv->ap_claim,
+				   GPIOD_IS_OUT);
+	if (ret)
+		goto err;
+	ret = gpio_request_by_name(dev, "their-claim-gpios", 0, &priv->ec_claim,
+				   GPIOD_IS_IN);
+	if (ret)
+		goto err_ec_gpio;
+
+	return 0;
+
+err_ec_gpio:
+	dm_gpio_free(dev, &priv->ap_claim);
+err:
+	debug("%s: ret=%d\n", __func__, ret);
+	return ret;
+}
+
+static int i2c_arbitrator_remove(struct udevice *dev)
+{
+	struct i2c_arbitrator_priv *priv = dev_get_priv(dev);
+
+	dm_gpio_free(dev, &priv->ap_claim);
+	dm_gpio_free(dev, &priv->ec_claim);
+
+	return 0;
+}
+
+static const struct i2c_mux_ops i2c_arbitrator_ops = {
+	.select		= i2c_arbitrator_select,
+	.deselect	= i2c_arbitrator_deselect,
+};
+
+static const struct udevice_id i2c_arbitrator_ids[] = {
+	{ .compatible = "i2c-arb-gpio-challenge" },
+	{ }
+};
+
+U_BOOT_DRIVER(i2c_arbitrator) = {
+	.name = "i2c_arbitrator",
+	.id = UCLASS_I2C_MUX,
+	.of_match = i2c_arbitrator_ids,
+	.probe = i2c_arbitrator_probe,
+	.remove = i2c_arbitrator_remove,
+	.ops = &i2c_arbitrator_ops,
+	.priv_auto_alloc_size = sizeof(struct i2c_arbitrator_priv),
+};