diff options
Diffstat (limited to 'roms/u-boot/drivers/i2c/muxes/i2c-arb-gpio-challenge.c')
-rw-r--r-- | roms/u-boot/drivers/i2c/muxes/i2c-arb-gpio-challenge.c | 150 |
1 files changed, 150 insertions, 0 deletions
diff --git a/roms/u-boot/drivers/i2c/muxes/i2c-arb-gpio-challenge.c b/roms/u-boot/drivers/i2c/muxes/i2c-arb-gpio-challenge.c new file mode 100644 index 000000000..ad730e0e7 --- /dev/null +++ b/roms/u-boot/drivers/i2c/muxes/i2c-arb-gpio-challenge.c @@ -0,0 +1,150 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2015 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <i2c.h> +#include <log.h> +#include <malloc.h> +#include <asm/global_data.h> +#include <asm/gpio.h> +#include <linux/delay.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(dev); + 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 = sizeof(struct i2c_arbitrator_priv), +}; |