blob: 9346f810ce119402f5e872e77dce3d7f59971e60 [file] [log] [blame]
/* 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 <log.h>
#include <w1.h>
#include <linux/delay.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_plat(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_plat(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_plat(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_of_to_plat(struct udevice *dev)
{
struct w1_gpio_pdata *pdata = dev_get_plat(dev);
int ret;
ret = gpio_request_by_name(dev, "gpios", 0, &pdata->gpio, GPIOD_IS_IN);
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,
.of_to_plat = w1_gpio_of_to_plat,
.ops = &w1_gpio_ops,
.plat_auto = sizeof(struct w1_gpio_pdata),
};