w1: add DS2405 addressable switch driver
authorMaciej S. Szmigiero <mail@maciej.szmigiero.name>
Thu, 19 Jan 2017 20:26:51 +0000 (21:26 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 25 Jan 2017 10:46:42 +0000 (11:46 +0100)
This adds a driver for a DS2405 1-wire single-channel addressable switch.
The DS2405 can also work as a single-channel binary remote sensor.

This driver supports two attributes: "state" and "output" which are the
same attribute names as supported by existing DS2406, DS2408 and DS2413
drivers.

Signed-off-by: Maciej S. Szmigiero <mail@maciej.szmigiero.name>
Acked-by: Evgeniy Polyakov <zbr@ioremap.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/w1/slaves/Kconfig
drivers/w1/slaves/Makefile
drivers/w1/slaves/w1_ds2405.c [new file with mode: 0644]
drivers/w1/w1_family.h
drivers/w1/w1_io.c

index cfe74d09932e09f441c957a06662d97faccfd967..0ef9f2663dbd1f81b5f08b1b667c06303f7fa46b 100644 (file)
@@ -16,6 +16,14 @@ config W1_SLAVE_SMEM
          Say Y here if you want to connect 1-wire
          simple 64bit memory rom(ds2401/ds2411/ds1990*) to your wire.
 
+config W1_SLAVE_DS2405
+       tristate "DS2405 Addressable Switch"
+       help
+         Say Y or M here if you want to use a DS2405 1-wire
+         single-channel addressable switch.
+         This device can also work as a single-channel
+         binary remote sensor.
+
 config W1_SLAVE_DS2408
        tristate "8-Channel Addressable Switch (IO Expander) 0x29 family support (DS2408)"
        help
index 1e9989afe7bf74d72ab93375e71ddb985a3bcb39..b4a358955ef9b89a2bca762be87f3f2f0d78497d 100644 (file)
@@ -4,6 +4,7 @@
 
 obj-$(CONFIG_W1_SLAVE_THERM)   += w1_therm.o
 obj-$(CONFIG_W1_SLAVE_SMEM)    += w1_smem.o
+obj-$(CONFIG_W1_SLAVE_DS2405)  += w1_ds2405.o
 obj-$(CONFIG_W1_SLAVE_DS2408)  += w1_ds2408.o
 obj-$(CONFIG_W1_SLAVE_DS2413)  += w1_ds2413.o
 obj-$(CONFIG_W1_SLAVE_DS2406)  += w1_ds2406.o
diff --git a/drivers/w1/slaves/w1_ds2405.c b/drivers/w1/slaves/w1_ds2405.c
new file mode 100644 (file)
index 0000000..1f350cf
--- /dev/null
@@ -0,0 +1,238 @@
+/*
+ *     w1_ds2405.c
+ *
+ * Copyright (c) 2017 Maciej S. Szmigiero <mail@maciej.szmigiero.name>
+ * Based on w1_therm.c copyright (c) 2004 Evgeniy Polyakov <zbr@ioremap.net>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the therms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/mutex.h>
+#include <linux/string.h>
+#include <linux/types.h>
+
+#include "../w1.h"
+#include "../w1_family.h"
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Maciej S. Szmigiero <mail@maciej.szmigiero.name>");
+MODULE_DESCRIPTION("Driver for 1-wire Dallas DS2405 PIO.");
+MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_DS2405));
+
+static int w1_ds2405_select(struct w1_slave *sl, bool only_active)
+{
+       struct w1_master *dev = sl->master;
+
+       u64 dev_addr = le64_to_cpu(*(u64 *)&sl->reg_num);
+       unsigned int bit_ctr;
+
+       if (w1_reset_bus(dev) != 0)
+               return 0;
+
+       /*
+        * We cannot use a normal Match ROM command
+        * since doing so would toggle PIO state
+        */
+       w1_write_8(dev, only_active ? W1_ALARM_SEARCH : W1_SEARCH);
+
+       for (bit_ctr = 0; bit_ctr < 64; bit_ctr++) {
+               int bit2send = !!(dev_addr & BIT(bit_ctr));
+               u8 ret;
+
+               ret = w1_triplet(dev, bit2send);
+
+               if ((ret & (BIT(0) | BIT(1))) ==
+                   (BIT(0) | BIT(1))) /* no devices found */
+                       return 0;
+
+               if (!!(ret & BIT(2)) != bit2send)
+                       /* wrong direction taken - no such device */
+                       return 0;
+       }
+
+       return 1;
+}
+
+static int w1_ds2405_read_pio(struct w1_slave *sl)
+{
+       if (w1_ds2405_select(sl, true))
+               return 0; /* "active" means PIO is low */
+
+       if (w1_ds2405_select(sl, false))
+               return 1;
+
+       return -ENODEV;
+}
+
+static ssize_t state_show(struct device *device,
+                         struct device_attribute *attr, char *buf)
+{
+       struct w1_slave *sl = dev_to_w1_slave(device);
+       struct w1_master *dev = sl->master;
+
+       int ret;
+       ssize_t f_retval;
+       u8 state;
+
+       ret = mutex_lock_interruptible(&dev->bus_mutex);
+       if (ret)
+               return ret;
+
+       if (!w1_ds2405_select(sl, false)) {
+               f_retval = -ENODEV;
+               goto out_unlock;
+       }
+
+       state = w1_read_8(dev);
+       if (state != 0 &&
+           state != 0xff) {
+               dev_err(device, "non-consistent state %x\n", state);
+               f_retval = -EIO;
+               goto out_unlock;
+       }
+
+       *buf = state ? '1' : '0';
+       f_retval = 1;
+
+out_unlock:
+       w1_reset_bus(dev);
+       mutex_unlock(&dev->bus_mutex);
+
+       return f_retval;
+}
+
+static ssize_t output_show(struct device *device,
+                          struct device_attribute *attr, char *buf)
+{
+       struct w1_slave *sl = dev_to_w1_slave(device);
+       struct w1_master *dev = sl->master;
+
+       int ret;
+       ssize_t f_retval;
+
+       ret = mutex_lock_interruptible(&dev->bus_mutex);
+       if (ret)
+               return ret;
+
+       ret = w1_ds2405_read_pio(sl);
+       if (ret < 0) {
+               f_retval = ret;
+               goto out_unlock;
+       }
+
+       *buf = ret ? '1' : '0';
+       f_retval = 1;
+
+out_unlock:
+       w1_reset_bus(dev);
+       mutex_unlock(&dev->bus_mutex);
+
+       return f_retval;
+}
+
+static ssize_t output_store(struct device *device,
+                           struct device_attribute *attr,
+                           const char *buf, size_t count)
+{
+       struct w1_slave *sl = dev_to_w1_slave(device);
+       struct w1_master *dev = sl->master;
+
+       int ret, current_pio;
+       unsigned int val;
+       ssize_t f_retval;
+
+       if (count < 1)
+               return -EINVAL;
+
+       if (sscanf(buf, " %u%n", &val, &ret) < 1)
+               return -EINVAL;
+
+       if (val != 0 && val != 1)
+               return -EINVAL;
+
+       f_retval = ret;
+
+       ret = mutex_lock_interruptible(&dev->bus_mutex);
+       if (ret)
+               return ret;
+
+       current_pio = w1_ds2405_read_pio(sl);
+       if (current_pio < 0) {
+               f_retval = current_pio;
+               goto out_unlock;
+       }
+
+       if (current_pio == val)
+               goto out_unlock;
+
+       if (w1_reset_bus(dev) != 0) {
+               f_retval = -ENODEV;
+               goto out_unlock;
+       }
+
+       /*
+        * can't use w1_reset_select_slave() here since it uses Skip ROM if
+        * there is only one device on bus
+        */
+       do {
+               u64 dev_addr = le64_to_cpu(*(u64 *)&sl->reg_num);
+               u8 cmd[9];
+
+               cmd[0] = W1_MATCH_ROM;
+               memcpy(&cmd[1], &dev_addr, sizeof(dev_addr));
+
+               w1_write_block(dev, cmd, sizeof(cmd));
+       } while (0);
+
+out_unlock:
+       w1_reset_bus(dev);
+       mutex_unlock(&dev->bus_mutex);
+
+       return f_retval;
+}
+
+static DEVICE_ATTR_RO(state);
+static DEVICE_ATTR_RW(output);
+
+static struct attribute *w1_ds2405_attrs[] = {
+       &dev_attr_state.attr,
+       &dev_attr_output.attr,
+       NULL
+};
+
+ATTRIBUTE_GROUPS(w1_ds2405);
+
+static struct w1_family_ops w1_ds2405_fops = {
+       .groups = w1_ds2405_groups
+};
+
+static struct w1_family w1_family_ds2405 = {
+       .fid = W1_FAMILY_DS2405,
+       .fops = &w1_ds2405_fops
+};
+
+static int __init w1_ds2405_init(void)
+{
+       return w1_register_family(&w1_family_ds2405);
+}
+
+static void __exit w1_ds2405_fini(void)
+{
+       w1_unregister_family(&w1_family_ds2405);
+}
+
+module_init(w1_ds2405_init);
+module_exit(w1_ds2405_fini);
index 10a7a0767187c52d67a41c3fcffa7ecd38c80e63..66cb0d2fe04955cf4c5fe39c0d1edd4efb3ecdaf 100644 (file)
@@ -30,6 +30,7 @@
 #define W1_FAMILY_BQ27000      0x01
 #define W1_FAMILY_SMEM_01      0x01
 #define W1_FAMILY_SMEM_81      0x81
+#define W1_FAMILY_DS2405       0x05
 #define W1_THERM_DS18S20       0x10
 #define W1_FAMILY_DS28E04      0x1C
 #define W1_COUNTER_DS2423      0x1D
index f4bc8c100a01b1f04a62ebca2528c51c05f40520..a209869eff766e0d58dbe1a39f89034a3cfad788 100644 (file)
@@ -233,6 +233,7 @@ u8 w1_triplet(struct w1_master *dev, int bdir)
                return retval;
        }
 }
+EXPORT_SYMBOL_GPL(w1_triplet);
 
 /**
  * w1_read_8() - Reads 8 bits.