From dd6478d68b16ce3d165b9d0e4ac0c021923e0f5a Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Thu, 19 Jan 2017 21:26:51 +0100 Subject: [PATCH] w1: add DS2405 addressable switch driver 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 Acked-by: Evgeniy Polyakov Signed-off-by: Greg Kroah-Hartman --- drivers/w1/slaves/Kconfig | 8 ++ drivers/w1/slaves/Makefile | 1 + drivers/w1/slaves/w1_ds2405.c | 238 ++++++++++++++++++++++++++++++++++ drivers/w1/w1_family.h | 1 + drivers/w1/w1_io.c | 1 + 5 files changed, 249 insertions(+) create mode 100644 drivers/w1/slaves/w1_ds2405.c diff --git a/drivers/w1/slaves/Kconfig b/drivers/w1/slaves/Kconfig index cfe74d09932e..0ef9f2663dbd 100644 --- a/drivers/w1/slaves/Kconfig +++ b/drivers/w1/slaves/Kconfig @@ -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 diff --git a/drivers/w1/slaves/Makefile b/drivers/w1/slaves/Makefile index 1e9989afe7bf..b4a358955ef9 100644 --- a/drivers/w1/slaves/Makefile +++ b/drivers/w1/slaves/Makefile @@ -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 index 000000000000..1f350cf2b6ec --- /dev/null +++ b/drivers/w1/slaves/w1_ds2405.c @@ -0,0 +1,238 @@ +/* + * w1_ds2405.c + * + * Copyright (c) 2017 Maciej S. Szmigiero + * Based on w1_therm.c copyright (c) 2004 Evgeniy Polyakov + * + * + * 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 +#include +#include +#include +#include +#include +#include + +#include "../w1.h" +#include "../w1_family.h" + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Maciej S. Szmigiero "); +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); diff --git a/drivers/w1/w1_family.h b/drivers/w1/w1_family.h index 10a7a0767187..66cb0d2fe049 100644 --- a/drivers/w1/w1_family.h +++ b/drivers/w1/w1_family.h @@ -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 diff --git a/drivers/w1/w1_io.c b/drivers/w1/w1_io.c index f4bc8c100a01..a209869eff76 100644 --- a/drivers/w1/w1_io.c +++ b/drivers/w1/w1_io.c @@ -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. -- 2.20.1