/* * Copyright (c) 2015 Google, Inc * Written by Simon Glass * * SPDX-License-Identifier: GPL-2.0+ */ #include #include #include #include #include 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), };