mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-28 15:41:40 +00:00
73aea285e3
Add a bus driver for bitbanging a 1-Wire bus over a GPIO. Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com> [eugen.hristev@microchip.com: fixed some issues] Signed-off-by: Eugen Hristev <eugen.hristev@microchip.com>
176 lines
3.8 KiB
C
176 lines
3.8 KiB
C
/* SPDX-License-Identifier: GPL-2.0+
|
|
*
|
|
* Copyright (c) 2015 Free Electrons
|
|
* Copyright (c) 2015 NextThing Co
|
|
*
|
|
* Maxime Ripard <maxime.ripard@free-electrons.com>
|
|
*
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <dm.h>
|
|
#include <w1.h>
|
|
|
|
#include <asm/gpio.h>
|
|
|
|
#define W1_TIMING_A 6
|
|
#define W1_TIMING_B 64
|
|
#define W1_TIMING_C 60
|
|
#define W1_TIMING_D 10
|
|
#define W1_TIMING_E 9
|
|
#define W1_TIMING_F 55
|
|
#define W1_TIMING_G 0
|
|
#define W1_TIMING_H 480
|
|
#define W1_TIMING_I 70
|
|
#define W1_TIMING_J 410
|
|
|
|
struct w1_gpio_pdata {
|
|
struct gpio_desc gpio;
|
|
u64 search_id;
|
|
};
|
|
|
|
static bool w1_gpio_read_bit(struct udevice *dev)
|
|
{
|
|
struct w1_gpio_pdata *pdata = dev_get_platdata(dev);
|
|
int val;
|
|
|
|
dm_gpio_set_dir_flags(&pdata->gpio, GPIOD_IS_OUT);
|
|
udelay(W1_TIMING_A);
|
|
|
|
dm_gpio_set_dir_flags(&pdata->gpio, GPIOD_IS_IN);
|
|
udelay(W1_TIMING_E);
|
|
|
|
val = dm_gpio_get_value(&pdata->gpio);
|
|
if (val < 0)
|
|
debug("error in retrieving GPIO value");
|
|
udelay(W1_TIMING_F);
|
|
|
|
return val;
|
|
}
|
|
|
|
static u8 w1_gpio_read_byte(struct udevice *dev)
|
|
{
|
|
int i;
|
|
u8 ret = 0;
|
|
|
|
for (i = 0; i < 8; ++i)
|
|
ret |= (w1_gpio_read_bit(dev) ? 1 : 0) << i;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void w1_gpio_write_bit(struct udevice *dev, bool bit)
|
|
{
|
|
struct w1_gpio_pdata *pdata = dev_get_platdata(dev);
|
|
|
|
dm_gpio_set_dir_flags(&pdata->gpio, GPIOD_IS_OUT);
|
|
|
|
bit ? udelay(W1_TIMING_A) : udelay(W1_TIMING_C);
|
|
|
|
dm_gpio_set_value(&pdata->gpio, 1);
|
|
|
|
bit ? udelay(W1_TIMING_B) : udelay(W1_TIMING_D);
|
|
}
|
|
|
|
static void w1_gpio_write_byte(struct udevice *dev, u8 byte)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < 8; ++i)
|
|
w1_gpio_write_bit(dev, (byte >> i) & 0x1);
|
|
}
|
|
|
|
static bool w1_gpio_reset(struct udevice *dev)
|
|
{
|
|
struct w1_gpio_pdata *pdata = dev_get_platdata(dev);
|
|
int val;
|
|
|
|
/* initiate the reset pulse. first we must pull the bus to low */
|
|
dm_gpio_set_dir_flags(&pdata->gpio, GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE);
|
|
udelay(W1_TIMING_G);
|
|
|
|
dm_gpio_set_value(&pdata->gpio, 0);
|
|
/* wait for the specified time with the bus kept low */
|
|
udelay(W1_TIMING_H);
|
|
|
|
/* now we must read the presence pulse */
|
|
dm_gpio_set_dir_flags(&pdata->gpio, GPIOD_IS_IN);
|
|
udelay(W1_TIMING_I);
|
|
|
|
val = dm_gpio_get_value(&pdata->gpio);
|
|
if (val < 0)
|
|
debug("error in retrieving GPIO value");
|
|
|
|
/* if nobody pulled the bus down , it means nobody is on the bus */
|
|
if (val != 0)
|
|
return 1;
|
|
/* we have the bus pulled down, let's wait for the specified presence time */
|
|
udelay(W1_TIMING_J);
|
|
|
|
/* read again, the other end should leave the bus free */
|
|
val = dm_gpio_get_value(&pdata->gpio);
|
|
if (val < 0)
|
|
debug("error in retrieving GPIO value");
|
|
|
|
/* bus is not going up again, so we have an error */
|
|
if (val != 1)
|
|
return 1;
|
|
|
|
/* all good, presence detected */
|
|
return 0;
|
|
}
|
|
|
|
static u8 w1_gpio_triplet(struct udevice *dev, bool bdir)
|
|
{
|
|
u8 id_bit = w1_gpio_read_bit(dev);
|
|
u8 comp_bit = w1_gpio_read_bit(dev);
|
|
u8 retval;
|
|
|
|
if (id_bit && comp_bit)
|
|
return 0x03; /* error */
|
|
|
|
if (!id_bit && !comp_bit) {
|
|
/* Both bits are valid, take the direction given */
|
|
retval = bdir ? 0x04 : 0;
|
|
} else {
|
|
/* Only one bit is valid, take that direction */
|
|
bdir = id_bit;
|
|
retval = id_bit ? 0x05 : 0x02;
|
|
}
|
|
|
|
w1_gpio_write_bit(dev, bdir);
|
|
return retval;
|
|
}
|
|
|
|
static const struct w1_ops w1_gpio_ops = {
|
|
.read_byte = w1_gpio_read_byte,
|
|
.reset = w1_gpio_reset,
|
|
.triplet = w1_gpio_triplet,
|
|
.write_byte = w1_gpio_write_byte,
|
|
};
|
|
|
|
static int w1_gpio_ofdata_to_platdata(struct udevice *dev)
|
|
{
|
|
struct w1_gpio_pdata *pdata = dev_get_platdata(dev);
|
|
int ret;
|
|
|
|
ret = gpio_request_by_name(dev, "gpios", 0, &pdata->gpio, 0);
|
|
if (ret < 0)
|
|
printf("Error claiming GPIO %d\n", ret);
|
|
|
|
return ret;
|
|
};
|
|
|
|
static const struct udevice_id w1_gpio_id[] = {
|
|
{ "w1-gpio", 0 },
|
|
{ },
|
|
};
|
|
|
|
U_BOOT_DRIVER(w1_gpio_drv) = {
|
|
.id = UCLASS_W1,
|
|
.name = "w1_gpio_drv",
|
|
.of_match = w1_gpio_id,
|
|
.ofdata_to_platdata = w1_gpio_ofdata_to_platdata,
|
|
.ops = &w1_gpio_ops,
|
|
.platdata_auto_alloc_size = sizeof(struct w1_gpio_pdata),
|
|
};
|