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>lime2-spi
parent
d3e19cf919
commit
73aea285e3
@ -1 +1,3 @@ |
||||
obj-$(CONFIG_W1) += w1-uclass.o
|
||||
|
||||
obj-$(CONFIG_W1_GPIO) += w1-gpio.o
|
||||
|
@ -0,0 +1,176 @@ |
||||
/* 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), |
||||
}; |
Loading…
Reference in new issue