mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-12-21 02:33:07 +00:00
41575d8e4c
This construct is quite long-winded. In earlier days it made some sense since auto-allocation was a strange concept. But with driver model now used pretty universally, we can shorten this to 'auto'. This reduces verbosity and makes it easier to read. Coincidentally it also ensures that every declaration is on one line, thus making dtoc's job easier. Signed-off-by: Simon Glass <sjg@chromium.org>
149 lines
3.4 KiB
C
149 lines
3.4 KiB
C
// 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/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),
|
|
};
|