mirror of
https://github.com/Fishwaldo/u-boot.git
synced 2025-03-21 14:41:31 +00:00
w1: Add 1-Wire gpio driver
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>
This commit is contained in:
parent
d3e19cf919
commit
73aea285e3
3 changed files with 185 additions and 0 deletions
|
@ -13,6 +13,13 @@ config W1
|
|||
|
||||
if W1
|
||||
|
||||
config W1_GPIO
|
||||
bool "Enable 1-wire GPIO bitbanging"
|
||||
default no
|
||||
depends on DM_GPIO
|
||||
help
|
||||
Emulate a 1-wire bus using a GPIO.
|
||||
|
||||
endif
|
||||
|
||||
endmenu
|
||||
|
|
|
@ -1 +1,3 @@
|
|||
obj-$(CONFIG_W1) += w1-uclass.o
|
||||
|
||||
obj-$(CONFIG_W1_GPIO) += w1-gpio.o
|
||||
|
|
176
drivers/w1/w1-gpio.c
Normal file
176
drivers/w1/w1-gpio.c
Normal file
|
@ -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…
Add table
Reference in a new issue