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

Simon Glass sjg at chromium.org
Mon Jul 6 18:38:27 CEST 2015


Hi Heiko,

On 5 July 2015 at 00:43, Heiko Schocher <hs at 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 at 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 at 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


More information about the U-Boot mailing list