w1: Add 1-Wire gpio driver
authorMaxime Ripard <maxime.ripard@free-electrons.com>
Tue, 18 Sep 2018 07:35:25 +0000 (10:35 +0300)
committerTom Rini <trini@konsulko.com>
Sat, 29 Sep 2018 00:22:34 +0000 (20:22 -0400)
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>
drivers/w1/Kconfig
drivers/w1/Makefile
drivers/w1/w1-gpio.c [new file with mode: 0644]

index 64b27c6cd1a22a1d48511afb3f56c6f6b2e4de67..d6e045739d0c42f9818bffe891c8b3a02539f908 100644 (file)
@@ -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
index f81693bacb44ef0674545581de114ddcba24564b..7fd8697f8419448083b096b150f64746cefd419c 100644 (file)
@@ -1 +1,3 @@
 obj-$(CONFIG_W1) += w1-uclass.o
+
+obj-$(CONFIG_W1_GPIO) += w1-gpio.o
diff --git a/drivers/w1/w1-gpio.c b/drivers/w1/w1-gpio.c
new file mode 100644 (file)
index 0000000..5e5d6b3
--- /dev/null
@@ -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),
+};