summaryrefslogtreecommitdiffstats
path: root/drivers/w1/slaves
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 18:49:45 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 18:49:45 +0000
commit2c3c1048746a4622d8c89a29670120dc8fab93c4 (patch)
tree848558de17fb3008cdf4d861b01ac7781903ce39 /drivers/w1/slaves
parentInitial commit. (diff)
downloadlinux-2c3c1048746a4622d8c89a29670120dc8fab93c4.tar.xz
linux-2c3c1048746a4622d8c89a29670120dc8fab93c4.zip
Adding upstream version 6.1.76.upstream/6.1.76
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'drivers/w1/slaves')
-rw-r--r--drivers/w1/slaves/Kconfig170
-rw-r--r--drivers/w1/slaves/Makefile22
-rw-r--r--drivers/w1/slaves/w1_ds2405.c218
-rw-r--r--drivers/w1/slaves/w1_ds2406.c154
-rw-r--r--drivers/w1/slaves/w1_ds2408.c353
-rw-r--r--drivers/w1/slaves/w1_ds2413.c159
-rw-r--r--drivers/w1/slaves/w1_ds2423.c133
-rw-r--r--drivers/w1/slaves/w1_ds2430.c295
-rw-r--r--drivers/w1/slaves/w1_ds2431.c294
-rw-r--r--drivers/w1/slaves/w1_ds2433.c306
-rw-r--r--drivers/w1/slaves/w1_ds2438.c518
-rw-r--r--drivers/w1/slaves/w1_ds250x.c290
-rw-r--r--drivers/w1/slaves/w1_ds2780.c159
-rw-r--r--drivers/w1/slaves/w1_ds2780.h125
-rw-r--r--drivers/w1/slaves/w1_ds2781.c156
-rw-r--r--drivers/w1/slaves/w1_ds2781.h130
-rw-r--r--drivers/w1/slaves/w1_ds2805.c298
-rw-r--r--drivers/w1/slaves/w1_ds28e04.c414
-rw-r--r--drivers/w1/slaves/w1_ds28e17.c755
-rw-r--r--drivers/w1/slaves/w1_smem.c59
-rw-r--r--drivers/w1/slaves/w1_therm.c2224
21 files changed, 7232 insertions, 0 deletions
diff --git a/drivers/w1/slaves/Kconfig b/drivers/w1/slaves/Kconfig
new file mode 100644
index 000000000..687753889
--- /dev/null
+++ b/drivers/w1/slaves/Kconfig
@@ -0,0 +1,170 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# 1-wire slaves configuration
+#
+
+menu "1-wire Slaves"
+
+config W1_SLAVE_THERM
+ tristate "Thermal family implementation"
+ help
+ Say Y here if you want to connect 1-wire thermal sensors to your
+ wire.
+
+config W1_SLAVE_SMEM
+ tristate "Simple 64bit memory family implementation"
+ help
+ 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
+ Say Y here if you want to use a 1-wire
+ DS2408 8-Channel Addressable Switch device support
+
+config W1_SLAVE_DS2408_READBACK
+ bool "Read-back values written to DS2408's output register"
+ depends on W1_SLAVE_DS2408
+ default y
+ help
+ Enabling this will cause the driver to read back the values written
+ to the chip's output register in order to detect errors.
+
+ This is slower but useful when debugging chips and/or busses.
+
+config W1_SLAVE_DS2413
+ tristate "Dual Channel Addressable Switch 0x3a family support (DS2413)"
+ help
+ Say Y here if you want to use a 1-wire
+ DS2413 Dual Channel Addressable Switch device support
+
+config W1_SLAVE_DS2406
+ tristate "Dual Channel Addressable Switch 0x12 family support (DS2406)"
+ select CRC16
+ help
+ Say Y or M here if you want to use a 1-wire
+ DS2406 Dual Channel Addressable Switch. EPROM read/write
+ support for these devices is not implemented.
+
+config W1_SLAVE_DS2423
+ tristate "Counter 1-wire device (DS2423)"
+ select CRC16
+ help
+ If you enable this you can read the counter values available
+ in the DS2423 chipset from the w1_slave file under the
+ sys file system.
+
+ Say Y here if you want to use a 1-wire
+ counter family device (DS2423).
+
+config W1_SLAVE_DS2805
+ tristate "112-byte EEPROM support (DS28E05)"
+ help
+ Say Y here if you want to use a 1-wire
+ is a 112-byte user-programmable EEPROM is
+ organized as 7 pages of 16 bytes each with 64bit
+ unique number. Requires OverDrive Speed to talk to.
+
+config W1_SLAVE_DS2430
+ tristate "256b EEPROM family support (DS2430)"
+ help
+ Say Y here if you want to use a 1-wire 256bit EEPROM
+ family device (DS2430).
+ This EEPROM is organized as one page of 32 bytes for random
+ access.
+
+config W1_SLAVE_DS2431
+ tristate "1kb EEPROM family support (DS2431)"
+ help
+ Say Y here if you want to use a 1-wire
+ 1kb EEPROM family device (DS2431)
+
+config W1_SLAVE_DS2433
+ tristate "4kb EEPROM family support (DS2433)"
+ help
+ Say Y here if you want to use a 1-wire
+ 4kb EEPROM family device (DS2433).
+
+config W1_SLAVE_DS2433_CRC
+ bool "Protect DS2433 data with a CRC16"
+ depends on W1_SLAVE_DS2433
+ select CRC16
+ help
+ Say Y here to protect DS2433 data with a CRC16.
+ Each block has 30 bytes of data and a two byte CRC16.
+ Full block writes are only allowed if the CRC is valid.
+
+config W1_SLAVE_DS2438
+ tristate "DS2438 Smart Battery Monitor 0x26 family support"
+ help
+ Say Y here if you want to use a 1-wire
+ DS2438 Smart Battery Monitor device support
+
+config W1_SLAVE_DS250X
+ tristate "512b/1kb/16kb EPROM family support"
+ select CRC16
+ help
+ Say Y here if you want to use a 1-wire
+ 512b/1kb/16kb EPROM family device (DS250x).
+
+config W1_SLAVE_DS2780
+ tristate "Dallas 2780 battery monitor chip"
+ help
+ If you enable this you will have the DS2780 battery monitor
+ chip support.
+
+ The battery monitor chip is used in many batteries/devices
+ as the one who is responsible for charging/discharging/monitoring
+ Li+ batteries.
+
+ If you are unsure, say N.
+
+config W1_SLAVE_DS2781
+ tristate "Dallas 2781 battery monitor chip"
+ help
+ If you enable this you will have the DS2781 battery monitor
+ chip support.
+
+ The battery monitor chip is used in many batteries/devices
+ as the one who is responsible for charging/discharging/monitoring
+ Li+ batteries.
+
+ If you are unsure, say N.
+
+config W1_SLAVE_DS28E04
+ tristate "4096-Bit Addressable 1-Wire EEPROM with PIO (DS28E04-100)"
+ select CRC16
+ help
+ If you enable this you will have the DS28E04-100
+ chip support.
+
+ Say Y here if you want to use a 1-wire
+ 4kb EEPROM with PIO family device (DS28E04).
+
+ If you are unsure, say N.
+
+config W1_SLAVE_DS28E17
+ tristate "1-wire-to-I2C master bridge (DS28E17)"
+ select CRC16
+ depends on I2C
+ help
+ Say Y here if you want to use the DS28E17 1-wire-to-I2C master bridge.
+ For each DS28E17 detected, a new I2C adapter is created within the
+ kernel. I2C devices on that bus can be configured to be used by the
+ kernel and userspace tools as on any other "native" I2C bus.
+
+ This driver is also available as a module. If so, the module
+ will be called w1_ds28e17.
+
+ If you are unsure, say N.
+
+endmenu
diff --git a/drivers/w1/slaves/Makefile b/drivers/w1/slaves/Makefile
new file mode 100644
index 000000000..278bcf2a9
--- /dev/null
+++ b/drivers/w1/slaves/Makefile
@@ -0,0 +1,22 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for the Dallas's 1-wire slaves.
+#
+
+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
+obj-$(CONFIG_W1_SLAVE_DS2423) += w1_ds2423.o
+obj-$(CONFIG_W1_SLAVE_DS2430) += w1_ds2430.o
+obj-$(CONFIG_W1_SLAVE_DS2431) += w1_ds2431.o
+obj-$(CONFIG_W1_SLAVE_DS2805) += w1_ds2805.o
+obj-$(CONFIG_W1_SLAVE_DS2433) += w1_ds2433.o
+obj-$(CONFIG_W1_SLAVE_DS2438) += w1_ds2438.o
+obj-$(CONFIG_W1_SLAVE_DS250X) += w1_ds250x.o
+obj-$(CONFIG_W1_SLAVE_DS2780) += w1_ds2780.o
+obj-$(CONFIG_W1_SLAVE_DS2781) += w1_ds2781.o
+obj-$(CONFIG_W1_SLAVE_DS28E04) += w1_ds28e04.o
+obj-$(CONFIG_W1_SLAVE_DS28E17) += w1_ds28e17.o
diff --git a/drivers/w1/slaves/w1_ds2405.c b/drivers/w1/slaves/w1_ds2405.c
new file mode 100644
index 000000000..1d9a1183e
--- /dev/null
+++ b/drivers/w1/slaves/w1_ds2405.c
@@ -0,0 +1,218 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * 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>
+ */
+
+#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 <linux/w1.h>
+
+#define W1_FAMILY_DS2405 0x05
+
+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 const 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
+};
+
+module_w1_family(w1_family_ds2405);
diff --git a/drivers/w1/slaves/w1_ds2406.c b/drivers/w1/slaves/w1_ds2406.c
new file mode 100644
index 000000000..6c269af73
--- /dev/null
+++ b/drivers/w1/slaves/w1_ds2406.c
@@ -0,0 +1,154 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * w1_ds2406.c - w1 family 12 (DS2406) driver
+ * based on w1_ds2413.c by Mariusz Bialonczyk <manio@skyboo.net>
+ *
+ * Copyright (c) 2014 Scott Alfter <scott@alfter.us>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/crc16.h>
+
+#include <linux/w1.h>
+
+#define W1_FAMILY_DS2406 0x12
+
+#define W1_F12_FUNC_READ_STATUS 0xAA
+#define W1_F12_FUNC_WRITE_STATUS 0x55
+
+static ssize_t w1_f12_read_state(
+ struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t off, size_t count)
+{
+ u8 w1_buf[6]={W1_F12_FUNC_READ_STATUS, 7, 0, 0, 0, 0};
+ struct w1_slave *sl = kobj_to_w1_slave(kobj);
+ u16 crc=0;
+ int i;
+ ssize_t rtnval=1;
+
+ if (off != 0)
+ return 0;
+ if (!buf)
+ return -EINVAL;
+
+ mutex_lock(&sl->master->bus_mutex);
+
+ if (w1_reset_select_slave(sl)) {
+ mutex_unlock(&sl->master->bus_mutex);
+ return -EIO;
+ }
+
+ w1_write_block(sl->master, w1_buf, 3);
+ w1_read_block(sl->master, w1_buf+3, 3);
+ for (i=0; i<6; i++)
+ crc=crc16_byte(crc, w1_buf[i]);
+ if (crc==0xb001) /* good read? */
+ *buf=((w1_buf[3]>>5)&3)|0x30;
+ else
+ rtnval=-EIO;
+
+ mutex_unlock(&sl->master->bus_mutex);
+
+ return rtnval;
+}
+
+static ssize_t w1_f12_write_output(
+ struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct w1_slave *sl = kobj_to_w1_slave(kobj);
+ u8 w1_buf[6]={W1_F12_FUNC_WRITE_STATUS, 7, 0, 0, 0, 0};
+ u16 crc=0;
+ int i;
+ ssize_t rtnval=1;
+
+ if (count != 1 || off != 0)
+ return -EFAULT;
+
+ mutex_lock(&sl->master->bus_mutex);
+
+ if (w1_reset_select_slave(sl)) {
+ mutex_unlock(&sl->master->bus_mutex);
+ return -EIO;
+ }
+
+ w1_buf[3] = (((*buf)&3)<<5)|0x1F;
+ w1_write_block(sl->master, w1_buf, 4);
+ w1_read_block(sl->master, w1_buf+4, 2);
+ for (i=0; i<6; i++)
+ crc=crc16_byte(crc, w1_buf[i]);
+ if (crc==0xb001) /* good read? */
+ w1_write_8(sl->master, 0xFF);
+ else
+ rtnval=-EIO;
+
+ mutex_unlock(&sl->master->bus_mutex);
+ return rtnval;
+}
+
+#define NB_SYSFS_BIN_FILES 2
+static struct bin_attribute w1_f12_sysfs_bin_files[NB_SYSFS_BIN_FILES] = {
+ {
+ .attr = {
+ .name = "state",
+ .mode = S_IRUGO,
+ },
+ .size = 1,
+ .read = w1_f12_read_state,
+ },
+ {
+ .attr = {
+ .name = "output",
+ .mode = S_IRUGO | S_IWUSR | S_IWGRP,
+ },
+ .size = 1,
+ .write = w1_f12_write_output,
+ }
+};
+
+static int w1_f12_add_slave(struct w1_slave *sl)
+{
+ int err = 0;
+ int i;
+
+ for (i = 0; i < NB_SYSFS_BIN_FILES && !err; ++i)
+ err = sysfs_create_bin_file(
+ &sl->dev.kobj,
+ &(w1_f12_sysfs_bin_files[i]));
+ if (err)
+ while (--i >= 0)
+ sysfs_remove_bin_file(&sl->dev.kobj,
+ &(w1_f12_sysfs_bin_files[i]));
+ return err;
+}
+
+static void w1_f12_remove_slave(struct w1_slave *sl)
+{
+ int i;
+ for (i = NB_SYSFS_BIN_FILES - 1; i >= 0; --i)
+ sysfs_remove_bin_file(&sl->dev.kobj,
+ &(w1_f12_sysfs_bin_files[i]));
+}
+
+static const struct w1_family_ops w1_f12_fops = {
+ .add_slave = w1_f12_add_slave,
+ .remove_slave = w1_f12_remove_slave,
+};
+
+static struct w1_family w1_family_12 = {
+ .fid = W1_FAMILY_DS2406,
+ .fops = &w1_f12_fops,
+};
+module_w1_family(w1_family_12);
+
+MODULE_AUTHOR("Scott Alfter <scott@alfter.us>");
+MODULE_DESCRIPTION("w1 family 12 driver for DS2406 2 Pin IO");
+MODULE_LICENSE("GPL");
diff --git a/drivers/w1/slaves/w1_ds2408.c b/drivers/w1/slaves/w1_ds2408.c
new file mode 100644
index 000000000..ad102c577
--- /dev/null
+++ b/drivers/w1/slaves/w1_ds2408.c
@@ -0,0 +1,353 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * w1_ds2408.c - w1 family 29 (DS2408) driver
+ *
+ * Copyright (c) 2010 Jean-Francois Dagenais <dagenaisj@sonatest.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+
+#include <linux/w1.h>
+
+#define W1_FAMILY_DS2408 0x29
+
+#define W1_F29_RETRIES 3
+
+#define W1_F29_REG_LOGIG_STATE 0x88 /* R */
+#define W1_F29_REG_OUTPUT_LATCH_STATE 0x89 /* R */
+#define W1_F29_REG_ACTIVITY_LATCH_STATE 0x8A /* R */
+#define W1_F29_REG_COND_SEARCH_SELECT_MASK 0x8B /* RW */
+#define W1_F29_REG_COND_SEARCH_POL_SELECT 0x8C /* RW */
+#define W1_F29_REG_CONTROL_AND_STATUS 0x8D /* RW */
+
+#define W1_F29_FUNC_READ_PIO_REGS 0xF0
+#define W1_F29_FUNC_CHANN_ACCESS_READ 0xF5
+#define W1_F29_FUNC_CHANN_ACCESS_WRITE 0x5A
+/* also used to write the control/status reg (0x8D): */
+#define W1_F29_FUNC_WRITE_COND_SEARCH_REG 0xCC
+#define W1_F29_FUNC_RESET_ACTIVITY_LATCHES 0xC3
+
+#define W1_F29_SUCCESS_CONFIRM_BYTE 0xAA
+
+static int _read_reg(struct w1_slave *sl, u8 address, unsigned char* buf)
+{
+ u8 wrbuf[3];
+ dev_dbg(&sl->dev,
+ "Reading with slave: %p, reg addr: %0#4x, buff addr: %p",
+ sl, (unsigned int)address, buf);
+
+ if (!buf)
+ return -EINVAL;
+
+ mutex_lock(&sl->master->bus_mutex);
+ dev_dbg(&sl->dev, "mutex locked");
+
+ if (w1_reset_select_slave(sl)) {
+ mutex_unlock(&sl->master->bus_mutex);
+ return -EIO;
+ }
+
+ wrbuf[0] = W1_F29_FUNC_READ_PIO_REGS;
+ wrbuf[1] = address;
+ wrbuf[2] = 0;
+ w1_write_block(sl->master, wrbuf, 3);
+ *buf = w1_read_8(sl->master);
+
+ mutex_unlock(&sl->master->bus_mutex);
+ dev_dbg(&sl->dev, "mutex unlocked");
+ return 1;
+}
+
+static ssize_t state_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr, char *buf, loff_t off,
+ size_t count)
+{
+ dev_dbg(&kobj_to_w1_slave(kobj)->dev,
+ "Reading %s kobj: %p, off: %0#10x, count: %zu, buff addr: %p",
+ bin_attr->attr.name, kobj, (unsigned int)off, count, buf);
+ if (count != 1 || off != 0)
+ return -EFAULT;
+ return _read_reg(kobj_to_w1_slave(kobj), W1_F29_REG_LOGIG_STATE, buf);
+}
+
+static ssize_t output_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr, char *buf,
+ loff_t off, size_t count)
+{
+ dev_dbg(&kobj_to_w1_slave(kobj)->dev,
+ "Reading %s kobj: %p, off: %0#10x, count: %zu, buff addr: %p",
+ bin_attr->attr.name, kobj, (unsigned int)off, count, buf);
+ if (count != 1 || off != 0)
+ return -EFAULT;
+ return _read_reg(kobj_to_w1_slave(kobj),
+ W1_F29_REG_OUTPUT_LATCH_STATE, buf);
+}
+
+static ssize_t activity_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr, char *buf,
+ loff_t off, size_t count)
+{
+ dev_dbg(&kobj_to_w1_slave(kobj)->dev,
+ "Reading %s kobj: %p, off: %0#10x, count: %zu, buff addr: %p",
+ bin_attr->attr.name, kobj, (unsigned int)off, count, buf);
+ if (count != 1 || off != 0)
+ return -EFAULT;
+ return _read_reg(kobj_to_w1_slave(kobj),
+ W1_F29_REG_ACTIVITY_LATCH_STATE, buf);
+}
+
+static ssize_t cond_search_mask_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr, char *buf,
+ loff_t off, size_t count)
+{
+ dev_dbg(&kobj_to_w1_slave(kobj)->dev,
+ "Reading %s kobj: %p, off: %0#10x, count: %zu, buff addr: %p",
+ bin_attr->attr.name, kobj, (unsigned int)off, count, buf);
+ if (count != 1 || off != 0)
+ return -EFAULT;
+ return _read_reg(kobj_to_w1_slave(kobj),
+ W1_F29_REG_COND_SEARCH_SELECT_MASK, buf);
+}
+
+static ssize_t cond_search_polarity_read(struct file *filp,
+ struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t off, size_t count)
+{
+ if (count != 1 || off != 0)
+ return -EFAULT;
+ return _read_reg(kobj_to_w1_slave(kobj),
+ W1_F29_REG_COND_SEARCH_POL_SELECT, buf);
+}
+
+static ssize_t status_control_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr, char *buf,
+ loff_t off, size_t count)
+{
+ if (count != 1 || off != 0)
+ return -EFAULT;
+ return _read_reg(kobj_to_w1_slave(kobj),
+ W1_F29_REG_CONTROL_AND_STATUS, buf);
+}
+
+#ifdef CONFIG_W1_SLAVE_DS2408_READBACK
+static bool optional_read_back_valid(struct w1_slave *sl, u8 expected)
+{
+ u8 w1_buf[3];
+
+ if (w1_reset_resume_command(sl->master))
+ return false;
+
+ w1_buf[0] = W1_F29_FUNC_READ_PIO_REGS;
+ w1_buf[1] = W1_F29_REG_OUTPUT_LATCH_STATE;
+ w1_buf[2] = 0;
+
+ w1_write_block(sl->master, w1_buf, 3);
+
+ return (w1_read_8(sl->master) == expected);
+}
+#else
+static bool optional_read_back_valid(struct w1_slave *sl, u8 expected)
+{
+ return true;
+}
+#endif
+
+static ssize_t output_write(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr, char *buf,
+ loff_t off, size_t count)
+{
+ struct w1_slave *sl = kobj_to_w1_slave(kobj);
+ u8 w1_buf[3];
+ unsigned int retries = W1_F29_RETRIES;
+ ssize_t bytes_written = -EIO;
+
+ if (count != 1 || off != 0)
+ return -EFAULT;
+
+ dev_dbg(&sl->dev, "locking mutex for write_output");
+ mutex_lock(&sl->master->bus_mutex);
+ dev_dbg(&sl->dev, "mutex locked");
+
+ if (w1_reset_select_slave(sl))
+ goto out;
+
+ do {
+ w1_buf[0] = W1_F29_FUNC_CHANN_ACCESS_WRITE;
+ w1_buf[1] = *buf;
+ w1_buf[2] = ~(*buf);
+
+ w1_write_block(sl->master, w1_buf, 3);
+
+ if (w1_read_8(sl->master) == W1_F29_SUCCESS_CONFIRM_BYTE &&
+ optional_read_back_valid(sl, *buf)) {
+ bytes_written = 1;
+ goto out;
+ }
+
+ if (w1_reset_resume_command(sl->master))
+ goto out; /* unrecoverable error */
+ /* try again, the slave is ready for a command */
+ } while (--retries);
+
+out:
+ mutex_unlock(&sl->master->bus_mutex);
+
+ dev_dbg(&sl->dev, "%s, mutex unlocked retries:%d\n",
+ (bytes_written > 0) ? "succeeded" : "error", retries);
+
+ return bytes_written;
+}
+
+
+/**
+ * Writing to the activity file resets the activity latches.
+ */
+static ssize_t activity_write(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr, char *buf,
+ loff_t off, size_t count)
+{
+ struct w1_slave *sl = kobj_to_w1_slave(kobj);
+ unsigned int retries = W1_F29_RETRIES;
+
+ if (count != 1 || off != 0)
+ return -EFAULT;
+
+ mutex_lock(&sl->master->bus_mutex);
+
+ if (w1_reset_select_slave(sl))
+ goto error;
+
+ while (retries--) {
+ w1_write_8(sl->master, W1_F29_FUNC_RESET_ACTIVITY_LATCHES);
+ if (w1_read_8(sl->master) == W1_F29_SUCCESS_CONFIRM_BYTE) {
+ mutex_unlock(&sl->master->bus_mutex);
+ return 1;
+ }
+ if (w1_reset_resume_command(sl->master))
+ goto error;
+ }
+
+error:
+ mutex_unlock(&sl->master->bus_mutex);
+ return -EIO;
+}
+
+static ssize_t status_control_write(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr, char *buf,
+ loff_t off, size_t count)
+{
+ struct w1_slave *sl = kobj_to_w1_slave(kobj);
+ u8 w1_buf[4];
+ unsigned int retries = W1_F29_RETRIES;
+
+ if (count != 1 || off != 0)
+ return -EFAULT;
+
+ mutex_lock(&sl->master->bus_mutex);
+
+ if (w1_reset_select_slave(sl))
+ goto error;
+
+ while (retries--) {
+ w1_buf[0] = W1_F29_FUNC_WRITE_COND_SEARCH_REG;
+ w1_buf[1] = W1_F29_REG_CONTROL_AND_STATUS;
+ w1_buf[2] = 0;
+ w1_buf[3] = *buf;
+
+ w1_write_block(sl->master, w1_buf, 4);
+ if (w1_reset_resume_command(sl->master))
+ goto error;
+
+ w1_buf[0] = W1_F29_FUNC_READ_PIO_REGS;
+ w1_buf[1] = W1_F29_REG_CONTROL_AND_STATUS;
+ w1_buf[2] = 0;
+
+ w1_write_block(sl->master, w1_buf, 3);
+ if (w1_read_8(sl->master) == *buf) {
+ /* success! */
+ mutex_unlock(&sl->master->bus_mutex);
+ return 1;
+ }
+ }
+error:
+ mutex_unlock(&sl->master->bus_mutex);
+
+ return -EIO;
+}
+
+/*
+ * This is a special sequence we must do to ensure the P0 output is not stuck
+ * in test mode. This is described in rev 2 of the ds2408's datasheet
+ * (http://datasheets.maximintegrated.com/en/ds/DS2408.pdf) under
+ * "APPLICATION INFORMATION/Power-up timing".
+ */
+static int w1_f29_disable_test_mode(struct w1_slave *sl)
+{
+ int res;
+ u8 magic[10] = {0x96, };
+ u64 rn = le64_to_cpu(*((u64*)&sl->reg_num));
+
+ memcpy(&magic[1], &rn, 8);
+ magic[9] = 0x3C;
+
+ mutex_lock(&sl->master->bus_mutex);
+
+ res = w1_reset_bus(sl->master);
+ if (res)
+ goto out;
+ w1_write_block(sl->master, magic, ARRAY_SIZE(magic));
+
+ res = w1_reset_bus(sl->master);
+out:
+ mutex_unlock(&sl->master->bus_mutex);
+ return res;
+}
+
+static BIN_ATTR_RO(state, 1);
+static BIN_ATTR_RW(output, 1);
+static BIN_ATTR_RW(activity, 1);
+static BIN_ATTR_RO(cond_search_mask, 1);
+static BIN_ATTR_RO(cond_search_polarity, 1);
+static BIN_ATTR_RW(status_control, 1);
+
+static struct bin_attribute *w1_f29_bin_attrs[] = {
+ &bin_attr_state,
+ &bin_attr_output,
+ &bin_attr_activity,
+ &bin_attr_cond_search_mask,
+ &bin_attr_cond_search_polarity,
+ &bin_attr_status_control,
+ NULL,
+};
+
+static const struct attribute_group w1_f29_group = {
+ .bin_attrs = w1_f29_bin_attrs,
+};
+
+static const struct attribute_group *w1_f29_groups[] = {
+ &w1_f29_group,
+ NULL,
+};
+
+static const struct w1_family_ops w1_f29_fops = {
+ .add_slave = w1_f29_disable_test_mode,
+ .groups = w1_f29_groups,
+};
+
+static struct w1_family w1_family_29 = {
+ .fid = W1_FAMILY_DS2408,
+ .fops = &w1_f29_fops,
+};
+module_w1_family(w1_family_29);
+
+MODULE_AUTHOR("Jean-Francois Dagenais <dagenaisj@sonatest.com>");
+MODULE_DESCRIPTION("w1 family 29 driver for DS2408 8 Pin IO");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_DS2408));
diff --git a/drivers/w1/slaves/w1_ds2413.c b/drivers/w1/slaves/w1_ds2413.c
new file mode 100644
index 000000000..c8cfac555
--- /dev/null
+++ b/drivers/w1/slaves/w1_ds2413.c
@@ -0,0 +1,159 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * w1_ds2413.c - w1 family 3a (DS2413) driver
+ * based on w1_ds2408.c by Jean-Francois Dagenais <dagenaisj@sonatest.com>
+ *
+ * Copyright (c) 2013 Mariusz Bialonczyk <manio@skyboo.net>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+
+#include <linux/w1.h>
+
+#define W1_FAMILY_DS2413 0x3A
+
+#define W1_F3A_RETRIES 3
+#define W1_F3A_FUNC_PIO_ACCESS_READ 0xF5
+#define W1_F3A_FUNC_PIO_ACCESS_WRITE 0x5A
+#define W1_F3A_SUCCESS_CONFIRM_BYTE 0xAA
+#define W1_F3A_INVALID_PIO_STATE 0xFF
+
+static ssize_t state_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr, char *buf, loff_t off,
+ size_t count)
+{
+ struct w1_slave *sl = kobj_to_w1_slave(kobj);
+ unsigned int retries = W1_F3A_RETRIES;
+ ssize_t bytes_read = -EIO;
+ u8 state;
+
+ dev_dbg(&sl->dev,
+ "Reading %s kobj: %p, off: %0#10x, count: %zu, buff addr: %p",
+ bin_attr->attr.name, kobj, (unsigned int)off, count, buf);
+
+ if (off != 0)
+ return 0;
+ if (!buf)
+ return -EINVAL;
+
+ mutex_lock(&sl->master->bus_mutex);
+ dev_dbg(&sl->dev, "mutex locked");
+
+next:
+ if (w1_reset_select_slave(sl))
+ goto out;
+
+ while (retries--) {
+ w1_write_8(sl->master, W1_F3A_FUNC_PIO_ACCESS_READ);
+
+ state = w1_read_8(sl->master);
+ if ((state & 0x0F) == ((~state >> 4) & 0x0F)) {
+ /* complement is correct */
+ *buf = state;
+ bytes_read = 1;
+ goto out;
+ } else if (state == W1_F3A_INVALID_PIO_STATE) {
+ /* slave didn't respond, try to select it again */
+ dev_warn(&sl->dev, "slave device did not respond to PIO_ACCESS_READ, " \
+ "reselecting, retries left: %d\n", retries);
+ goto next;
+ }
+
+ if (w1_reset_resume_command(sl->master))
+ goto out; /* unrecoverable error */
+
+ dev_warn(&sl->dev, "PIO_ACCESS_READ error, retries left: %d\n", retries);
+ }
+
+out:
+ mutex_unlock(&sl->master->bus_mutex);
+ dev_dbg(&sl->dev, "%s, mutex unlocked, retries: %d\n",
+ (bytes_read > 0) ? "succeeded" : "error", retries);
+ return bytes_read;
+}
+
+static BIN_ATTR_RO(state, 1);
+
+static ssize_t output_write(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr, char *buf,
+ loff_t off, size_t count)
+{
+ struct w1_slave *sl = kobj_to_w1_slave(kobj);
+ u8 w1_buf[3];
+ unsigned int retries = W1_F3A_RETRIES;
+ ssize_t bytes_written = -EIO;
+
+ if (count != 1 || off != 0)
+ return -EFAULT;
+
+ dev_dbg(&sl->dev, "locking mutex for write_output");
+ mutex_lock(&sl->master->bus_mutex);
+ dev_dbg(&sl->dev, "mutex locked");
+
+ if (w1_reset_select_slave(sl))
+ goto out;
+
+ /* according to the DS2413 datasheet the most significant 6 bits
+ should be set to "1"s, so do it now */
+ *buf = *buf | 0xFC;
+
+ while (retries--) {
+ w1_buf[0] = W1_F3A_FUNC_PIO_ACCESS_WRITE;
+ w1_buf[1] = *buf;
+ w1_buf[2] = ~(*buf);
+ w1_write_block(sl->master, w1_buf, 3);
+
+ if (w1_read_8(sl->master) == W1_F3A_SUCCESS_CONFIRM_BYTE) {
+ bytes_written = 1;
+ goto out;
+ }
+ if (w1_reset_resume_command(sl->master))
+ goto out; /* unrecoverable error */
+
+ dev_warn(&sl->dev, "PIO_ACCESS_WRITE error, retries left: %d\n", retries);
+ }
+
+out:
+ mutex_unlock(&sl->master->bus_mutex);
+ dev_dbg(&sl->dev, "%s, mutex unlocked, retries: %d\n",
+ (bytes_written > 0) ? "succeeded" : "error", retries);
+ return bytes_written;
+}
+
+static BIN_ATTR(output, S_IRUGO | S_IWUSR | S_IWGRP, NULL, output_write, 1);
+
+static struct bin_attribute *w1_f3a_bin_attrs[] = {
+ &bin_attr_state,
+ &bin_attr_output,
+ NULL,
+};
+
+static const struct attribute_group w1_f3a_group = {
+ .bin_attrs = w1_f3a_bin_attrs,
+};
+
+static const struct attribute_group *w1_f3a_groups[] = {
+ &w1_f3a_group,
+ NULL,
+};
+
+static const struct w1_family_ops w1_f3a_fops = {
+ .groups = w1_f3a_groups,
+};
+
+static struct w1_family w1_family_3a = {
+ .fid = W1_FAMILY_DS2413,
+ .fops = &w1_f3a_fops,
+};
+module_w1_family(w1_family_3a);
+
+MODULE_AUTHOR("Mariusz Bialonczyk <manio@skyboo.net>");
+MODULE_DESCRIPTION("w1 family 3a driver for DS2413 2 Pin IO");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_DS2413));
diff --git a/drivers/w1/slaves/w1_ds2423.c b/drivers/w1/slaves/w1_ds2423.c
new file mode 100644
index 000000000..b6bd18d5b
--- /dev/null
+++ b/drivers/w1/slaves/w1_ds2423.c
@@ -0,0 +1,133 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * w1_ds2423.c
+ *
+ * Copyright (c) 2010 Mika Laitio <lamikr@pilppa.org>
+ *
+ * This driver will read and write the value of 4 counters to w1_slave file in
+ * sys filesystem.
+ * Inspired by the w1_therm and w1_ds2431 drivers.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/crc16.h>
+
+#include <linux/w1.h>
+
+#define W1_COUNTER_DS2423 0x1D
+
+#define CRC16_VALID 0xb001
+#define CRC16_INIT 0
+
+#define COUNTER_COUNT 4
+#define READ_BYTE_COUNT 42
+
+static ssize_t w1_slave_show(struct device *device,
+ struct device_attribute *attr, char *out_buf)
+{
+ struct w1_slave *sl = dev_to_w1_slave(device);
+ struct w1_master *dev = sl->master;
+ u8 rbuf[COUNTER_COUNT * READ_BYTE_COUNT];
+ u8 wrbuf[3];
+ int rom_addr;
+ int read_byte_count;
+ int result;
+ ssize_t c;
+ int ii;
+ int p;
+ int crc;
+
+ c = PAGE_SIZE;
+ rom_addr = (12 << 5) + 31;
+ wrbuf[0] = 0xA5;
+ wrbuf[1] = rom_addr & 0xFF;
+ wrbuf[2] = rom_addr >> 8;
+ mutex_lock(&dev->bus_mutex);
+ if (!w1_reset_select_slave(sl)) {
+ w1_write_block(dev, wrbuf, 3);
+ read_byte_count = 0;
+ for (p = 0; p < 4; p++) {
+ /*
+ * 1 byte for first bytes in ram page read
+ * 4 bytes for counter
+ * 4 bytes for zero bits
+ * 2 bytes for crc
+ * 31 remaining bytes from the ram page
+ */
+ read_byte_count += w1_read_block(dev,
+ rbuf + (p * READ_BYTE_COUNT), READ_BYTE_COUNT);
+ for (ii = 0; ii < READ_BYTE_COUNT; ++ii)
+ c -= snprintf(out_buf + PAGE_SIZE - c,
+ c, "%02x ",
+ rbuf[(p * READ_BYTE_COUNT) + ii]);
+ if (read_byte_count != (p + 1) * READ_BYTE_COUNT) {
+ dev_warn(device,
+ "w1_counter_read() returned %u bytes "
+ "instead of %d bytes wanted.\n",
+ read_byte_count,
+ READ_BYTE_COUNT);
+ c -= snprintf(out_buf + PAGE_SIZE - c,
+ c, "crc=NO\n");
+ } else {
+ if (p == 0) {
+ crc = crc16(CRC16_INIT, wrbuf, 3);
+ crc = crc16(crc, rbuf, 11);
+ } else {
+ /*
+ * DS2423 calculates crc from all bytes
+ * read after the previous crc bytes.
+ */
+ crc = crc16(CRC16_INIT,
+ (rbuf + 11) +
+ ((p - 1) * READ_BYTE_COUNT),
+ READ_BYTE_COUNT);
+ }
+ if (crc == CRC16_VALID) {
+ result = 0;
+ for (ii = 4; ii > 0; ii--) {
+ result <<= 8;
+ result |= rbuf[(p *
+ READ_BYTE_COUNT) + ii];
+ }
+ c -= snprintf(out_buf + PAGE_SIZE - c,
+ c, "crc=YES c=%d\n", result);
+ } else {
+ c -= snprintf(out_buf + PAGE_SIZE - c,
+ c, "crc=NO\n");
+ }
+ }
+ }
+ } else {
+ c -= snprintf(out_buf + PAGE_SIZE - c, c, "Connection error");
+ }
+ mutex_unlock(&dev->bus_mutex);
+ return PAGE_SIZE - c;
+}
+
+static DEVICE_ATTR_RO(w1_slave);
+
+static struct attribute *w1_f1d_attrs[] = {
+ &dev_attr_w1_slave.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(w1_f1d);
+
+static const struct w1_family_ops w1_f1d_fops = {
+ .groups = w1_f1d_groups,
+};
+
+static struct w1_family w1_family_1d = {
+ .fid = W1_COUNTER_DS2423,
+ .fops = &w1_f1d_fops,
+};
+module_w1_family(w1_family_1d);
+
+MODULE_AUTHOR("Mika Laitio <lamikr@pilppa.org>");
+MODULE_DESCRIPTION("w1 family 1d driver for DS2423, 4 counters and 4kb ram");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("w1-family-" __stringify(W1_COUNTER_DS2423));
diff --git a/drivers/w1/slaves/w1_ds2430.c b/drivers/w1/slaves/w1_ds2430.c
new file mode 100644
index 000000000..0ea7d779d
--- /dev/null
+++ b/drivers/w1/slaves/w1_ds2430.c
@@ -0,0 +1,295 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * w1_ds2430.c - w1 family 14 (DS2430) driver
+ **
+ * Copyright (c) 2019 Angelo Dureghello <angelo.dureghello@timesys.com>
+ *
+ * Cloned and modified from ds2431
+ * Copyright (c) 2008 Bernhard Weirich <bernhard.weirich@riedel.net>
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+
+#include <linux/w1.h>
+
+#define W1_EEPROM_DS2430 0x14
+
+#define W1_F14_EEPROM_SIZE 32
+#define W1_F14_PAGE_COUNT 1
+#define W1_F14_PAGE_BITS 5
+#define W1_F14_PAGE_SIZE (1 << W1_F14_PAGE_BITS)
+#define W1_F14_PAGE_MASK 0x1F
+
+#define W1_F14_SCRATCH_BITS 5
+#define W1_F14_SCRATCH_SIZE (1 << W1_F14_SCRATCH_BITS)
+#define W1_F14_SCRATCH_MASK (W1_F14_SCRATCH_SIZE-1)
+
+#define W1_F14_READ_EEPROM 0xF0
+#define W1_F14_WRITE_SCRATCH 0x0F
+#define W1_F14_READ_SCRATCH 0xAA
+#define W1_F14_COPY_SCRATCH 0x55
+#define W1_F14_VALIDATION_KEY 0xa5
+
+#define W1_F14_TPROG_MS 11
+#define W1_F14_READ_RETRIES 10
+#define W1_F14_READ_MAXLEN W1_F14_SCRATCH_SIZE
+
+/*
+ * Check the file size bounds and adjusts count as needed.
+ * This would not be needed if the file size didn't reset to 0 after a write.
+ */
+static inline size_t w1_f14_fix_count(loff_t off, size_t count, size_t size)
+{
+ if (off > size)
+ return 0;
+
+ if ((off + count) > size)
+ return size - off;
+
+ return count;
+}
+
+/*
+ * Read a block from W1 ROM two times and compares the results.
+ * If they are equal they are returned, otherwise the read
+ * is repeated W1_F14_READ_RETRIES times.
+ *
+ * count must not exceed W1_F14_READ_MAXLEN.
+ */
+static int w1_f14_readblock(struct w1_slave *sl, int off, int count, char *buf)
+{
+ u8 wrbuf[2];
+ u8 cmp[W1_F14_READ_MAXLEN];
+ int tries = W1_F14_READ_RETRIES;
+
+ do {
+ wrbuf[0] = W1_F14_READ_EEPROM;
+ wrbuf[1] = off & 0xff;
+
+ if (w1_reset_select_slave(sl))
+ return -1;
+
+ w1_write_block(sl->master, wrbuf, 2);
+ w1_read_block(sl->master, buf, count);
+
+ if (w1_reset_select_slave(sl))
+ return -1;
+
+ w1_write_block(sl->master, wrbuf, 2);
+ w1_read_block(sl->master, cmp, count);
+
+ if (!memcmp(cmp, buf, count))
+ return 0;
+ } while (--tries);
+
+ dev_err(&sl->dev, "proof reading failed %d times\n",
+ W1_F14_READ_RETRIES);
+
+ return -1;
+}
+
+static ssize_t eeprom_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr, char *buf,
+ loff_t off, size_t count)
+{
+ struct w1_slave *sl = kobj_to_w1_slave(kobj);
+ int todo = count;
+
+ count = w1_f14_fix_count(off, count, W1_F14_EEPROM_SIZE);
+ if (count == 0)
+ return 0;
+
+ mutex_lock(&sl->master->bus_mutex);
+
+ /* read directly from the EEPROM in chunks of W1_F14_READ_MAXLEN */
+ while (todo > 0) {
+ int block_read;
+
+ if (todo >= W1_F14_READ_MAXLEN)
+ block_read = W1_F14_READ_MAXLEN;
+ else
+ block_read = todo;
+
+ if (w1_f14_readblock(sl, off, block_read, buf) < 0)
+ count = -EIO;
+
+ todo -= W1_F14_READ_MAXLEN;
+ buf += W1_F14_READ_MAXLEN;
+ off += W1_F14_READ_MAXLEN;
+ }
+
+ mutex_unlock(&sl->master->bus_mutex);
+
+ return count;
+}
+
+/*
+ * Writes to the scratchpad and reads it back for verification.
+ * Then copies the scratchpad to EEPROM.
+ * The data must be aligned at W1_F14_SCRATCH_SIZE bytes and
+ * must be W1_F14_SCRATCH_SIZE bytes long.
+ * The master must be locked.
+ *
+ * @param sl The slave structure
+ * @param addr Address for the write
+ * @param len length must be <= (W1_F14_PAGE_SIZE - (addr & W1_F14_PAGE_MASK))
+ * @param data The data to write
+ * @return 0=Success -1=failure
+ */
+static int w1_f14_write(struct w1_slave *sl, int addr, int len, const u8 *data)
+{
+ int tries = W1_F14_READ_RETRIES;
+ u8 wrbuf[2];
+ u8 rdbuf[W1_F14_SCRATCH_SIZE + 3];
+
+retry:
+
+ /* Write the data to the scratchpad */
+ if (w1_reset_select_slave(sl))
+ return -1;
+
+ wrbuf[0] = W1_F14_WRITE_SCRATCH;
+ wrbuf[1] = addr & 0xff;
+
+ w1_write_block(sl->master, wrbuf, 2);
+ w1_write_block(sl->master, data, len);
+
+ /* Read the scratchpad and verify */
+ if (w1_reset_select_slave(sl))
+ return -1;
+
+ w1_write_8(sl->master, W1_F14_READ_SCRATCH);
+ w1_read_block(sl->master, rdbuf, len + 2);
+
+ /*
+ * Compare what was read against the data written
+ * Note: on read scratchpad, device returns 2 bulk 0xff bytes,
+ * to be discarded.
+ */
+ if ((memcmp(data, &rdbuf[2], len) != 0)) {
+
+ if (--tries)
+ goto retry;
+
+ dev_err(&sl->dev,
+ "could not write to eeprom, scratchpad compare failed %d times\n",
+ W1_F14_READ_RETRIES);
+
+ return -1;
+ }
+
+ /* Copy the scratchpad to EEPROM */
+ if (w1_reset_select_slave(sl))
+ return -1;
+
+ wrbuf[0] = W1_F14_COPY_SCRATCH;
+ wrbuf[1] = W1_F14_VALIDATION_KEY;
+ w1_write_block(sl->master, wrbuf, 2);
+
+ /* Sleep for tprog ms to wait for the write to complete */
+ msleep(W1_F14_TPROG_MS);
+
+ /* Reset the bus to wake up the EEPROM */
+ w1_reset_bus(sl->master);
+
+ return 0;
+}
+
+static ssize_t eeprom_write(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr, char *buf,
+ loff_t off, size_t count)
+{
+ struct w1_slave *sl = kobj_to_w1_slave(kobj);
+ int addr, len;
+ int copy;
+
+ count = w1_f14_fix_count(off, count, W1_F14_EEPROM_SIZE);
+ if (count == 0)
+ return 0;
+
+ mutex_lock(&sl->master->bus_mutex);
+
+ /* Can only write data in blocks of the size of the scratchpad */
+ addr = off;
+ len = count;
+ while (len > 0) {
+
+ /* if len too short or addr not aligned */
+ if (len < W1_F14_SCRATCH_SIZE || addr & W1_F14_SCRATCH_MASK) {
+ char tmp[W1_F14_SCRATCH_SIZE];
+
+ /* read the block and update the parts to be written */
+ if (w1_f14_readblock(sl, addr & ~W1_F14_SCRATCH_MASK,
+ W1_F14_SCRATCH_SIZE, tmp)) {
+ count = -EIO;
+ goto out_up;
+ }
+
+ /* copy at most to the boundary of the PAGE or len */
+ copy = W1_F14_SCRATCH_SIZE -
+ (addr & W1_F14_SCRATCH_MASK);
+
+ if (copy > len)
+ copy = len;
+
+ memcpy(&tmp[addr & W1_F14_SCRATCH_MASK], buf, copy);
+ if (w1_f14_write(sl, addr & ~W1_F14_SCRATCH_MASK,
+ W1_F14_SCRATCH_SIZE, tmp) < 0) {
+ count = -EIO;
+ goto out_up;
+ }
+ } else {
+
+ copy = W1_F14_SCRATCH_SIZE;
+ if (w1_f14_write(sl, addr, copy, buf) < 0) {
+ count = -EIO;
+ goto out_up;
+ }
+ }
+ buf += copy;
+ addr += copy;
+ len -= copy;
+ }
+
+out_up:
+ mutex_unlock(&sl->master->bus_mutex);
+
+ return count;
+}
+
+static BIN_ATTR_RW(eeprom, W1_F14_EEPROM_SIZE);
+
+static struct bin_attribute *w1_f14_bin_attrs[] = {
+ &bin_attr_eeprom,
+ NULL,
+};
+
+static const struct attribute_group w1_f14_group = {
+ .bin_attrs = w1_f14_bin_attrs,
+};
+
+static const struct attribute_group *w1_f14_groups[] = {
+ &w1_f14_group,
+ NULL,
+};
+
+static const struct w1_family_ops w1_f14_fops = {
+ .groups = w1_f14_groups,
+};
+
+static struct w1_family w1_family_14 = {
+ .fid = W1_EEPROM_DS2430,
+ .fops = &w1_f14_fops,
+};
+module_w1_family(w1_family_14);
+
+MODULE_AUTHOR("Angelo Dureghello <angelo.dureghello@timesys.com>");
+MODULE_DESCRIPTION("w1 family 14 driver for DS2430, 256b EEPROM");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("w1-family-" __stringify(W1_EEPROM_DS2430));
diff --git a/drivers/w1/slaves/w1_ds2431.c b/drivers/w1/slaves/w1_ds2431.c
new file mode 100644
index 000000000..6856b1c29
--- /dev/null
+++ b/drivers/w1/slaves/w1_ds2431.c
@@ -0,0 +1,294 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * w1_ds2431.c - w1 family 2d (DS2431) driver
+ *
+ * Copyright (c) 2008 Bernhard Weirich <bernhard.weirich@riedel.net>
+ *
+ * Heavily inspired by w1_DS2433 driver from Ben Gardner <bgardner@wabtec.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+
+#include <linux/w1.h>
+
+#define W1_EEPROM_DS2431 0x2D
+
+#define W1_F2D_EEPROM_SIZE 128
+#define W1_F2D_PAGE_COUNT 4
+#define W1_F2D_PAGE_BITS 5
+#define W1_F2D_PAGE_SIZE (1<<W1_F2D_PAGE_BITS)
+#define W1_F2D_PAGE_MASK 0x1F
+
+#define W1_F2D_SCRATCH_BITS 3
+#define W1_F2D_SCRATCH_SIZE (1<<W1_F2D_SCRATCH_BITS)
+#define W1_F2D_SCRATCH_MASK (W1_F2D_SCRATCH_SIZE-1)
+
+#define W1_F2D_READ_EEPROM 0xF0
+#define W1_F2D_WRITE_SCRATCH 0x0F
+#define W1_F2D_READ_SCRATCH 0xAA
+#define W1_F2D_COPY_SCRATCH 0x55
+
+
+#define W1_F2D_TPROG_MS 11
+
+#define W1_F2D_READ_RETRIES 10
+#define W1_F2D_READ_MAXLEN 8
+
+/*
+ * Check the file size bounds and adjusts count as needed.
+ * This would not be needed if the file size didn't reset to 0 after a write.
+ */
+static inline size_t w1_f2d_fix_count(loff_t off, size_t count, size_t size)
+{
+ if (off > size)
+ return 0;
+
+ if ((off + count) > size)
+ return size - off;
+
+ return count;
+}
+
+/*
+ * Read a block from W1 ROM two times and compares the results.
+ * If they are equal they are returned, otherwise the read
+ * is repeated W1_F2D_READ_RETRIES times.
+ *
+ * count must not exceed W1_F2D_READ_MAXLEN.
+ */
+static int w1_f2d_readblock(struct w1_slave *sl, int off, int count, char *buf)
+{
+ u8 wrbuf[3];
+ u8 cmp[W1_F2D_READ_MAXLEN];
+ int tries = W1_F2D_READ_RETRIES;
+
+ do {
+ wrbuf[0] = W1_F2D_READ_EEPROM;
+ wrbuf[1] = off & 0xff;
+ wrbuf[2] = off >> 8;
+
+ if (w1_reset_select_slave(sl))
+ return -1;
+
+ w1_write_block(sl->master, wrbuf, 3);
+ w1_read_block(sl->master, buf, count);
+
+ if (w1_reset_select_slave(sl))
+ return -1;
+
+ w1_write_block(sl->master, wrbuf, 3);
+ w1_read_block(sl->master, cmp, count);
+
+ if (!memcmp(cmp, buf, count))
+ return 0;
+ } while (--tries);
+
+ dev_err(&sl->dev, "proof reading failed %d times\n",
+ W1_F2D_READ_RETRIES);
+
+ return -1;
+}
+
+static ssize_t eeprom_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr, char *buf,
+ loff_t off, size_t count)
+{
+ struct w1_slave *sl = kobj_to_w1_slave(kobj);
+ int todo = count;
+
+ count = w1_f2d_fix_count(off, count, W1_F2D_EEPROM_SIZE);
+ if (count == 0)
+ return 0;
+
+ mutex_lock(&sl->master->bus_mutex);
+
+ /* read directly from the EEPROM in chunks of W1_F2D_READ_MAXLEN */
+ while (todo > 0) {
+ int block_read;
+
+ if (todo >= W1_F2D_READ_MAXLEN)
+ block_read = W1_F2D_READ_MAXLEN;
+ else
+ block_read = todo;
+
+ if (w1_f2d_readblock(sl, off, block_read, buf) < 0)
+ count = -EIO;
+
+ todo -= W1_F2D_READ_MAXLEN;
+ buf += W1_F2D_READ_MAXLEN;
+ off += W1_F2D_READ_MAXLEN;
+ }
+
+ mutex_unlock(&sl->master->bus_mutex);
+
+ return count;
+}
+
+/*
+ * Writes to the scratchpad and reads it back for verification.
+ * Then copies the scratchpad to EEPROM.
+ * The data must be aligned at W1_F2D_SCRATCH_SIZE bytes and
+ * must be W1_F2D_SCRATCH_SIZE bytes long.
+ * The master must be locked.
+ *
+ * @param sl The slave structure
+ * @param addr Address for the write
+ * @param len length must be <= (W1_F2D_PAGE_SIZE - (addr & W1_F2D_PAGE_MASK))
+ * @param data The data to write
+ * @return 0=Success -1=failure
+ */
+static int w1_f2d_write(struct w1_slave *sl, int addr, int len, const u8 *data)
+{
+ int tries = W1_F2D_READ_RETRIES;
+ u8 wrbuf[4];
+ u8 rdbuf[W1_F2D_SCRATCH_SIZE + 3];
+ u8 es = (addr + len - 1) % W1_F2D_SCRATCH_SIZE;
+
+retry:
+
+ /* Write the data to the scratchpad */
+ if (w1_reset_select_slave(sl))
+ return -1;
+
+ wrbuf[0] = W1_F2D_WRITE_SCRATCH;
+ wrbuf[1] = addr & 0xff;
+ wrbuf[2] = addr >> 8;
+
+ w1_write_block(sl->master, wrbuf, 3);
+ w1_write_block(sl->master, data, len);
+
+ /* Read the scratchpad and verify */
+ if (w1_reset_select_slave(sl))
+ return -1;
+
+ w1_write_8(sl->master, W1_F2D_READ_SCRATCH);
+ w1_read_block(sl->master, rdbuf, len + 3);
+
+ /* Compare what was read against the data written */
+ if ((rdbuf[0] != wrbuf[1]) || (rdbuf[1] != wrbuf[2]) ||
+ (rdbuf[2] != es) || (memcmp(data, &rdbuf[3], len) != 0)) {
+
+ if (--tries)
+ goto retry;
+
+ dev_err(&sl->dev,
+ "could not write to eeprom, scratchpad compare failed %d times\n",
+ W1_F2D_READ_RETRIES);
+
+ return -1;
+ }
+
+ /* Copy the scratchpad to EEPROM */
+ if (w1_reset_select_slave(sl))
+ return -1;
+
+ wrbuf[0] = W1_F2D_COPY_SCRATCH;
+ wrbuf[3] = es;
+ w1_write_block(sl->master, wrbuf, 4);
+
+ /* Sleep for tprog ms to wait for the write to complete */
+ msleep(W1_F2D_TPROG_MS);
+
+ /* Reset the bus to wake up the EEPROM */
+ w1_reset_bus(sl->master);
+
+ return 0;
+}
+
+static ssize_t eeprom_write(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr, char *buf,
+ loff_t off, size_t count)
+{
+ struct w1_slave *sl = kobj_to_w1_slave(kobj);
+ int addr, len;
+ int copy;
+
+ count = w1_f2d_fix_count(off, count, W1_F2D_EEPROM_SIZE);
+ if (count == 0)
+ return 0;
+
+ mutex_lock(&sl->master->bus_mutex);
+
+ /* Can only write data in blocks of the size of the scratchpad */
+ addr = off;
+ len = count;
+ while (len > 0) {
+
+ /* if len too short or addr not aligned */
+ if (len < W1_F2D_SCRATCH_SIZE || addr & W1_F2D_SCRATCH_MASK) {
+ char tmp[W1_F2D_SCRATCH_SIZE];
+
+ /* read the block and update the parts to be written */
+ if (w1_f2d_readblock(sl, addr & ~W1_F2D_SCRATCH_MASK,
+ W1_F2D_SCRATCH_SIZE, tmp)) {
+ count = -EIO;
+ goto out_up;
+ }
+
+ /* copy at most to the boundary of the PAGE or len */
+ copy = W1_F2D_SCRATCH_SIZE -
+ (addr & W1_F2D_SCRATCH_MASK);
+
+ if (copy > len)
+ copy = len;
+
+ memcpy(&tmp[addr & W1_F2D_SCRATCH_MASK], buf, copy);
+ if (w1_f2d_write(sl, addr & ~W1_F2D_SCRATCH_MASK,
+ W1_F2D_SCRATCH_SIZE, tmp) < 0) {
+ count = -EIO;
+ goto out_up;
+ }
+ } else {
+
+ copy = W1_F2D_SCRATCH_SIZE;
+ if (w1_f2d_write(sl, addr, copy, buf) < 0) {
+ count = -EIO;
+ goto out_up;
+ }
+ }
+ buf += copy;
+ addr += copy;
+ len -= copy;
+ }
+
+out_up:
+ mutex_unlock(&sl->master->bus_mutex);
+
+ return count;
+}
+
+static BIN_ATTR_RW(eeprom, W1_F2D_EEPROM_SIZE);
+
+static struct bin_attribute *w1_f2d_bin_attrs[] = {
+ &bin_attr_eeprom,
+ NULL,
+};
+
+static const struct attribute_group w1_f2d_group = {
+ .bin_attrs = w1_f2d_bin_attrs,
+};
+
+static const struct attribute_group *w1_f2d_groups[] = {
+ &w1_f2d_group,
+ NULL,
+};
+
+static const struct w1_family_ops w1_f2d_fops = {
+ .groups = w1_f2d_groups,
+};
+
+static struct w1_family w1_family_2d = {
+ .fid = W1_EEPROM_DS2431,
+ .fops = &w1_f2d_fops,
+};
+module_w1_family(w1_family_2d);
+
+MODULE_AUTHOR("Bernhard Weirich <bernhard.weirich@riedel.net>");
+MODULE_DESCRIPTION("w1 family 2d driver for DS2431, 1kb EEPROM");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("w1-family-" __stringify(W1_EEPROM_DS2431));
diff --git a/drivers/w1/slaves/w1_ds2433.c b/drivers/w1/slaves/w1_ds2433.c
new file mode 100644
index 000000000..0f72df15a
--- /dev/null
+++ b/drivers/w1/slaves/w1_ds2433.c
@@ -0,0 +1,306 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * w1_ds2433.c - w1 family 23 (DS2433) driver
+ *
+ * Copyright (c) 2005 Ben Gardner <bgardner@wabtec.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#ifdef CONFIG_W1_SLAVE_DS2433_CRC
+#include <linux/crc16.h>
+
+#define CRC16_INIT 0
+#define CRC16_VALID 0xb001
+
+#endif
+
+#include <linux/w1.h>
+
+#define W1_EEPROM_DS2433 0x23
+
+#define W1_EEPROM_SIZE 512
+#define W1_PAGE_COUNT 16
+#define W1_PAGE_SIZE 32
+#define W1_PAGE_BITS 5
+#define W1_PAGE_MASK 0x1F
+
+#define W1_F23_TIME 300
+
+#define W1_F23_READ_EEPROM 0xF0
+#define W1_F23_WRITE_SCRATCH 0x0F
+#define W1_F23_READ_SCRATCH 0xAA
+#define W1_F23_COPY_SCRATCH 0x55
+
+struct w1_f23_data {
+ u8 memory[W1_EEPROM_SIZE];
+ u32 validcrc;
+};
+
+/**
+ * Check the file size bounds and adjusts count as needed.
+ * This would not be needed if the file size didn't reset to 0 after a write.
+ */
+static inline size_t w1_f23_fix_count(loff_t off, size_t count, size_t size)
+{
+ if (off > size)
+ return 0;
+
+ if ((off + count) > size)
+ return (size - off);
+
+ return count;
+}
+
+#ifdef CONFIG_W1_SLAVE_DS2433_CRC
+static int w1_f23_refresh_block(struct w1_slave *sl, struct w1_f23_data *data,
+ int block)
+{
+ u8 wrbuf[3];
+ int off = block * W1_PAGE_SIZE;
+
+ if (data->validcrc & (1 << block))
+ return 0;
+
+ if (w1_reset_select_slave(sl)) {
+ data->validcrc = 0;
+ return -EIO;
+ }
+
+ wrbuf[0] = W1_F23_READ_EEPROM;
+ wrbuf[1] = off & 0xff;
+ wrbuf[2] = off >> 8;
+ w1_write_block(sl->master, wrbuf, 3);
+ w1_read_block(sl->master, &data->memory[off], W1_PAGE_SIZE);
+
+ /* cache the block if the CRC is valid */
+ if (crc16(CRC16_INIT, &data->memory[off], W1_PAGE_SIZE) == CRC16_VALID)
+ data->validcrc |= (1 << block);
+
+ return 0;
+}
+#endif /* CONFIG_W1_SLAVE_DS2433_CRC */
+
+static ssize_t eeprom_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr, char *buf,
+ loff_t off, size_t count)
+{
+ struct w1_slave *sl = kobj_to_w1_slave(kobj);
+#ifdef CONFIG_W1_SLAVE_DS2433_CRC
+ struct w1_f23_data *data = sl->family_data;
+ int i, min_page, max_page;
+#else
+ u8 wrbuf[3];
+#endif
+
+ if ((count = w1_f23_fix_count(off, count, W1_EEPROM_SIZE)) == 0)
+ return 0;
+
+ mutex_lock(&sl->master->bus_mutex);
+
+#ifdef CONFIG_W1_SLAVE_DS2433_CRC
+
+ min_page = (off >> W1_PAGE_BITS);
+ max_page = (off + count - 1) >> W1_PAGE_BITS;
+ for (i = min_page; i <= max_page; i++) {
+ if (w1_f23_refresh_block(sl, data, i)) {
+ count = -EIO;
+ goto out_up;
+ }
+ }
+ memcpy(buf, &data->memory[off], count);
+
+#else /* CONFIG_W1_SLAVE_DS2433_CRC */
+
+ /* read directly from the EEPROM */
+ if (w1_reset_select_slave(sl)) {
+ count = -EIO;
+ goto out_up;
+ }
+
+ wrbuf[0] = W1_F23_READ_EEPROM;
+ wrbuf[1] = off & 0xff;
+ wrbuf[2] = off >> 8;
+ w1_write_block(sl->master, wrbuf, 3);
+ w1_read_block(sl->master, buf, count);
+
+#endif /* CONFIG_W1_SLAVE_DS2433_CRC */
+
+out_up:
+ mutex_unlock(&sl->master->bus_mutex);
+
+ return count;
+}
+
+/**
+ * Writes to the scratchpad and reads it back for verification.
+ * Then copies the scratchpad to EEPROM.
+ * The data must be on one page.
+ * The master must be locked.
+ *
+ * @param sl The slave structure
+ * @param addr Address for the write
+ * @param len length must be <= (W1_PAGE_SIZE - (addr & W1_PAGE_MASK))
+ * @param data The data to write
+ * @return 0=Success -1=failure
+ */
+static int w1_f23_write(struct w1_slave *sl, int addr, int len, const u8 *data)
+{
+#ifdef CONFIG_W1_SLAVE_DS2433_CRC
+ struct w1_f23_data *f23 = sl->family_data;
+#endif
+ u8 wrbuf[4];
+ u8 rdbuf[W1_PAGE_SIZE + 3];
+ u8 es = (addr + len - 1) & 0x1f;
+
+ /* Write the data to the scratchpad */
+ if (w1_reset_select_slave(sl))
+ return -1;
+
+ wrbuf[0] = W1_F23_WRITE_SCRATCH;
+ wrbuf[1] = addr & 0xff;
+ wrbuf[2] = addr >> 8;
+
+ w1_write_block(sl->master, wrbuf, 3);
+ w1_write_block(sl->master, data, len);
+
+ /* Read the scratchpad and verify */
+ if (w1_reset_select_slave(sl))
+ return -1;
+
+ w1_write_8(sl->master, W1_F23_READ_SCRATCH);
+ w1_read_block(sl->master, rdbuf, len + 3);
+
+ /* Compare what was read against the data written */
+ if ((rdbuf[0] != wrbuf[1]) || (rdbuf[1] != wrbuf[2]) ||
+ (rdbuf[2] != es) || (memcmp(data, &rdbuf[3], len) != 0))
+ return -1;
+
+ /* Copy the scratchpad to EEPROM */
+ if (w1_reset_select_slave(sl))
+ return -1;
+
+ wrbuf[0] = W1_F23_COPY_SCRATCH;
+ wrbuf[3] = es;
+ w1_write_block(sl->master, wrbuf, 4);
+
+ /* Sleep for 5 ms to wait for the write to complete */
+ msleep(5);
+
+ /* Reset the bus to wake up the EEPROM (this may not be needed) */
+ w1_reset_bus(sl->master);
+#ifdef CONFIG_W1_SLAVE_DS2433_CRC
+ f23->validcrc &= ~(1 << (addr >> W1_PAGE_BITS));
+#endif
+ return 0;
+}
+
+static ssize_t eeprom_write(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr, char *buf,
+ loff_t off, size_t count)
+{
+ struct w1_slave *sl = kobj_to_w1_slave(kobj);
+ int addr, len, idx;
+
+ if ((count = w1_f23_fix_count(off, count, W1_EEPROM_SIZE)) == 0)
+ return 0;
+
+#ifdef CONFIG_W1_SLAVE_DS2433_CRC
+ /* can only write full blocks in cached mode */
+ if ((off & W1_PAGE_MASK) || (count & W1_PAGE_MASK)) {
+ dev_err(&sl->dev, "invalid offset/count off=%d cnt=%zd\n",
+ (int)off, count);
+ return -EINVAL;
+ }
+
+ /* make sure the block CRCs are valid */
+ for (idx = 0; idx < count; idx += W1_PAGE_SIZE) {
+ if (crc16(CRC16_INIT, &buf[idx], W1_PAGE_SIZE) != CRC16_VALID) {
+ dev_err(&sl->dev, "bad CRC at offset %d\n", (int)off);
+ return -EINVAL;
+ }
+ }
+#endif /* CONFIG_W1_SLAVE_DS2433_CRC */
+
+ mutex_lock(&sl->master->bus_mutex);
+
+ /* Can only write data to one page at a time */
+ idx = 0;
+ while (idx < count) {
+ addr = off + idx;
+ len = W1_PAGE_SIZE - (addr & W1_PAGE_MASK);
+ if (len > (count - idx))
+ len = count - idx;
+
+ if (w1_f23_write(sl, addr, len, &buf[idx]) < 0) {
+ count = -EIO;
+ goto out_up;
+ }
+ idx += len;
+ }
+
+out_up:
+ mutex_unlock(&sl->master->bus_mutex);
+
+ return count;
+}
+
+static BIN_ATTR_RW(eeprom, W1_EEPROM_SIZE);
+
+static struct bin_attribute *w1_f23_bin_attributes[] = {
+ &bin_attr_eeprom,
+ NULL,
+};
+
+static const struct attribute_group w1_f23_group = {
+ .bin_attrs = w1_f23_bin_attributes,
+};
+
+static const struct attribute_group *w1_f23_groups[] = {
+ &w1_f23_group,
+ NULL,
+};
+
+static int w1_f23_add_slave(struct w1_slave *sl)
+{
+#ifdef CONFIG_W1_SLAVE_DS2433_CRC
+ struct w1_f23_data *data;
+
+ data = kzalloc(sizeof(struct w1_f23_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+ sl->family_data = data;
+
+#endif /* CONFIG_W1_SLAVE_DS2433_CRC */
+ return 0;
+}
+
+static void w1_f23_remove_slave(struct w1_slave *sl)
+{
+#ifdef CONFIG_W1_SLAVE_DS2433_CRC
+ kfree(sl->family_data);
+ sl->family_data = NULL;
+#endif /* CONFIG_W1_SLAVE_DS2433_CRC */
+}
+
+static const struct w1_family_ops w1_f23_fops = {
+ .add_slave = w1_f23_add_slave,
+ .remove_slave = w1_f23_remove_slave,
+ .groups = w1_f23_groups,
+};
+
+static struct w1_family w1_family_23 = {
+ .fid = W1_EEPROM_DS2433,
+ .fops = &w1_f23_fops,
+};
+module_w1_family(w1_family_23);
+
+MODULE_AUTHOR("Ben Gardner <bgardner@wabtec.com>");
+MODULE_DESCRIPTION("w1 family 23 driver for DS2433, 4kb EEPROM");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("w1-family-" __stringify(W1_EEPROM_DS2433));
diff --git a/drivers/w1/slaves/w1_ds2438.c b/drivers/w1/slaves/w1_ds2438.c
new file mode 100644
index 000000000..ca64f99c8
--- /dev/null
+++ b/drivers/w1/slaves/w1_ds2438.c
@@ -0,0 +1,518 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * 1-Wire implementation for the ds2438 chip
+ *
+ * Copyright (c) 2017 Mariusz Bialonczyk <manio@skyboo.net>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+
+#include <linux/w1.h>
+
+#define W1_FAMILY_DS2438 0x26
+
+#define W1_DS2438_RETRIES 3
+
+/* Memory commands */
+#define W1_DS2438_READ_SCRATCH 0xBE
+#define W1_DS2438_WRITE_SCRATCH 0x4E
+#define W1_DS2438_COPY_SCRATCH 0x48
+#define W1_DS2438_RECALL_MEMORY 0xB8
+/* Register commands */
+#define W1_DS2438_CONVERT_TEMP 0x44
+#define W1_DS2438_CONVERT_VOLTAGE 0xB4
+
+#define DS2438_PAGE_SIZE 8
+#define DS2438_ADC_INPUT_VAD 0
+#define DS2438_ADC_INPUT_VDD 1
+#define DS2438_MAX_CONVERSION_TIME 10 /* ms */
+
+/* Page #0 definitions */
+#define DS2438_STATUS_REG 0x00 /* Status/Configuration Register */
+#define DS2438_STATUS_IAD (1 << 0) /* Current A/D Control Bit */
+#define DS2438_STATUS_CA (1 << 1) /* Current Accumulator Configuration */
+#define DS2438_STATUS_EE (1 << 2) /* Current Accumulator Shadow Selector bit */
+#define DS2438_STATUS_AD (1 << 3) /* Voltage A/D Input Select Bit */
+#define DS2438_STATUS_TB (1 << 4) /* Temperature Busy Flag */
+#define DS2438_STATUS_NVB (1 << 5) /* Nonvolatile Memory Busy Flag */
+#define DS2438_STATUS_ADB (1 << 6) /* A/D Converter Busy Flag */
+
+#define DS2438_TEMP_LSB 0x01
+#define DS2438_TEMP_MSB 0x02
+#define DS2438_VOLTAGE_LSB 0x03
+#define DS2438_VOLTAGE_MSB 0x04
+#define DS2438_CURRENT_LSB 0x05
+#define DS2438_CURRENT_MSB 0x06
+#define DS2438_THRESHOLD 0x07
+
+/* Page #1 definitions */
+#define DS2438_ETM_0 0x00
+#define DS2438_ETM_1 0x01
+#define DS2438_ETM_2 0x02
+#define DS2438_ETM_3 0x03
+#define DS2438_ICA 0x04
+#define DS2438_OFFSET_LSB 0x05
+#define DS2438_OFFSET_MSB 0x06
+
+static int w1_ds2438_get_page(struct w1_slave *sl, int pageno, u8 *buf)
+{
+ unsigned int retries = W1_DS2438_RETRIES;
+ u8 w1_buf[2];
+ u8 crc;
+ size_t count;
+
+ while (retries--) {
+ crc = 0;
+
+ if (w1_reset_select_slave(sl))
+ continue;
+ w1_buf[0] = W1_DS2438_RECALL_MEMORY;
+ w1_buf[1] = (u8)pageno;
+ w1_write_block(sl->master, w1_buf, 2);
+
+ if (w1_reset_select_slave(sl))
+ continue;
+ w1_buf[0] = W1_DS2438_READ_SCRATCH;
+ w1_buf[1] = (u8)pageno;
+ w1_write_block(sl->master, w1_buf, 2);
+
+ count = w1_read_block(sl->master, buf, DS2438_PAGE_SIZE + 1);
+ if (count == DS2438_PAGE_SIZE + 1) {
+ crc = w1_calc_crc8(buf, DS2438_PAGE_SIZE);
+
+ /* check for correct CRC */
+ if ((u8)buf[DS2438_PAGE_SIZE] == crc)
+ return 0;
+ }
+ }
+ return -1;
+}
+
+static int w1_ds2438_get_temperature(struct w1_slave *sl, int16_t *temperature)
+{
+ unsigned int retries = W1_DS2438_RETRIES;
+ u8 w1_buf[DS2438_PAGE_SIZE + 1 /*for CRC*/];
+ unsigned int tm = DS2438_MAX_CONVERSION_TIME;
+ unsigned long sleep_rem;
+ int ret;
+
+ mutex_lock(&sl->master->bus_mutex);
+
+ while (retries--) {
+ if (w1_reset_select_slave(sl))
+ continue;
+ w1_write_8(sl->master, W1_DS2438_CONVERT_TEMP);
+
+ mutex_unlock(&sl->master->bus_mutex);
+ sleep_rem = msleep_interruptible(tm);
+ if (sleep_rem != 0) {
+ ret = -1;
+ goto post_unlock;
+ }
+
+ if (mutex_lock_interruptible(&sl->master->bus_mutex) != 0) {
+ ret = -1;
+ goto post_unlock;
+ }
+
+ break;
+ }
+
+ if (w1_ds2438_get_page(sl, 0, w1_buf) == 0) {
+ *temperature = (((int16_t) w1_buf[DS2438_TEMP_MSB]) << 8) | ((uint16_t) w1_buf[DS2438_TEMP_LSB]);
+ ret = 0;
+ } else
+ ret = -1;
+
+ mutex_unlock(&sl->master->bus_mutex);
+
+post_unlock:
+ return ret;
+}
+
+static int w1_ds2438_change_config_bit(struct w1_slave *sl, u8 mask, u8 value)
+{
+ unsigned int retries = W1_DS2438_RETRIES;
+ u8 w1_buf[3];
+ u8 status;
+ int perform_write = 0;
+
+ while (retries--) {
+ if (w1_reset_select_slave(sl))
+ continue;
+ w1_buf[0] = W1_DS2438_RECALL_MEMORY;
+ w1_buf[1] = 0x00;
+ w1_write_block(sl->master, w1_buf, 2);
+
+ if (w1_reset_select_slave(sl))
+ continue;
+ w1_buf[0] = W1_DS2438_READ_SCRATCH;
+ w1_buf[1] = 0x00;
+ w1_write_block(sl->master, w1_buf, 2);
+
+ /* reading one byte of result */
+ status = w1_read_8(sl->master);
+
+ /* if bit0=1, set a value to a mask for easy compare */
+ if (value)
+ value = mask;
+
+ if ((status & mask) == value)
+ return 0; /* already set as requested */
+
+ /* changing bit */
+ status ^= mask;
+ perform_write = 1;
+
+ break;
+ }
+
+ if (perform_write) {
+ retries = W1_DS2438_RETRIES;
+ while (retries--) {
+ if (w1_reset_select_slave(sl))
+ continue;
+ w1_buf[0] = W1_DS2438_WRITE_SCRATCH;
+ w1_buf[1] = 0x00;
+ w1_buf[2] = status;
+ w1_write_block(sl->master, w1_buf, 3);
+
+ if (w1_reset_select_slave(sl))
+ continue;
+ w1_buf[0] = W1_DS2438_COPY_SCRATCH;
+ w1_buf[1] = 0x00;
+ w1_write_block(sl->master, w1_buf, 2);
+
+ return 0;
+ }
+ }
+ return -1;
+}
+
+static int w1_ds2438_change_offset_register(struct w1_slave *sl, u8 *value)
+{
+ unsigned int retries = W1_DS2438_RETRIES;
+ u8 w1_buf[9];
+ u8 w1_page1_buf[DS2438_PAGE_SIZE + 1 /*for CRC*/];
+
+ if (w1_ds2438_get_page(sl, 1, w1_page1_buf) == 0) {
+ memcpy(&w1_buf[2], w1_page1_buf, DS2438_PAGE_SIZE - 1); /* last register reserved */
+ w1_buf[7] = value[0]; /* change only offset register */
+ w1_buf[8] = value[1];
+ while (retries--) {
+ if (w1_reset_select_slave(sl))
+ continue;
+ w1_buf[0] = W1_DS2438_WRITE_SCRATCH;
+ w1_buf[1] = 0x01; /* write to page 1 */
+ w1_write_block(sl->master, w1_buf, 9);
+
+ if (w1_reset_select_slave(sl))
+ continue;
+ w1_buf[0] = W1_DS2438_COPY_SCRATCH;
+ w1_buf[1] = 0x01;
+ w1_write_block(sl->master, w1_buf, 2);
+ return 0;
+ }
+ }
+ return -1;
+}
+
+static int w1_ds2438_get_voltage(struct w1_slave *sl,
+ int adc_input, uint16_t *voltage)
+{
+ unsigned int retries = W1_DS2438_RETRIES;
+ u8 w1_buf[DS2438_PAGE_SIZE + 1 /*for CRC*/];
+ unsigned int tm = DS2438_MAX_CONVERSION_TIME;
+ unsigned long sleep_rem;
+ int ret;
+
+ mutex_lock(&sl->master->bus_mutex);
+
+ if (w1_ds2438_change_config_bit(sl, DS2438_STATUS_AD, adc_input)) {
+ ret = -1;
+ goto pre_unlock;
+ }
+
+ while (retries--) {
+ if (w1_reset_select_slave(sl))
+ continue;
+ w1_write_8(sl->master, W1_DS2438_CONVERT_VOLTAGE);
+
+ mutex_unlock(&sl->master->bus_mutex);
+ sleep_rem = msleep_interruptible(tm);
+ if (sleep_rem != 0) {
+ ret = -1;
+ goto post_unlock;
+ }
+
+ if (mutex_lock_interruptible(&sl->master->bus_mutex) != 0) {
+ ret = -1;
+ goto post_unlock;
+ }
+
+ break;
+ }
+
+ if (w1_ds2438_get_page(sl, 0, w1_buf) == 0) {
+ *voltage = (((uint16_t) w1_buf[DS2438_VOLTAGE_MSB]) << 8) | ((uint16_t) w1_buf[DS2438_VOLTAGE_LSB]);
+ ret = 0;
+ } else
+ ret = -1;
+
+pre_unlock:
+ mutex_unlock(&sl->master->bus_mutex);
+
+post_unlock:
+ return ret;
+}
+
+static int w1_ds2438_get_current(struct w1_slave *sl, int16_t *voltage)
+{
+ u8 w1_buf[DS2438_PAGE_SIZE + 1 /*for CRC*/];
+ int ret;
+
+ mutex_lock(&sl->master->bus_mutex);
+
+ if (w1_ds2438_get_page(sl, 0, w1_buf) == 0) {
+ /* The voltage measured across current sense resistor RSENS. */
+ *voltage = (((int16_t) w1_buf[DS2438_CURRENT_MSB]) << 8) | ((int16_t) w1_buf[DS2438_CURRENT_LSB]);
+ ret = 0;
+ } else
+ ret = -1;
+
+ mutex_unlock(&sl->master->bus_mutex);
+
+ return ret;
+}
+
+static ssize_t iad_write(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr, char *buf,
+ loff_t off, size_t count)
+{
+ struct w1_slave *sl = kobj_to_w1_slave(kobj);
+ int ret;
+
+ if (count != 1 || off != 0)
+ return -EFAULT;
+
+ mutex_lock(&sl->master->bus_mutex);
+
+ if (w1_ds2438_change_config_bit(sl, DS2438_STATUS_IAD, *buf & 0x01) == 0)
+ ret = 1;
+ else
+ ret = -EIO;
+
+ mutex_unlock(&sl->master->bus_mutex);
+
+ return ret;
+}
+
+static ssize_t iad_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr, char *buf,
+ loff_t off, size_t count)
+{
+ struct w1_slave *sl = kobj_to_w1_slave(kobj);
+ int ret;
+ int16_t voltage;
+
+ if (off != 0)
+ return 0;
+ if (!buf)
+ return -EINVAL;
+
+ if (w1_ds2438_get_current(sl, &voltage) == 0)
+ ret = snprintf(buf, count, "%i\n", voltage);
+ else
+ ret = -EIO;
+
+ return ret;
+}
+
+static ssize_t page0_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr, char *buf,
+ loff_t off, size_t count)
+{
+ struct w1_slave *sl = kobj_to_w1_slave(kobj);
+ int ret;
+ u8 w1_buf[DS2438_PAGE_SIZE + 1 /*for CRC*/];
+
+ if (off != 0)
+ return 0;
+ if (!buf)
+ return -EINVAL;
+
+ mutex_lock(&sl->master->bus_mutex);
+
+ /* Read no more than page0 size */
+ if (count > DS2438_PAGE_SIZE)
+ count = DS2438_PAGE_SIZE;
+
+ if (w1_ds2438_get_page(sl, 0, w1_buf) == 0) {
+ memcpy(buf, &w1_buf, count);
+ ret = count;
+ } else
+ ret = -EIO;
+
+ mutex_unlock(&sl->master->bus_mutex);
+
+ return ret;
+}
+
+static ssize_t page1_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr, char *buf,
+ loff_t off, size_t count)
+{
+ struct w1_slave *sl = kobj_to_w1_slave(kobj);
+ int ret;
+ u8 w1_buf[DS2438_PAGE_SIZE + 1 /*for CRC*/];
+
+ if (off != 0)
+ return 0;
+ if (!buf)
+ return -EINVAL;
+
+ mutex_lock(&sl->master->bus_mutex);
+
+ /* Read no more than page1 size */
+ if (count > DS2438_PAGE_SIZE)
+ count = DS2438_PAGE_SIZE;
+
+ if (w1_ds2438_get_page(sl, 1, w1_buf) == 0) {
+ memcpy(buf, &w1_buf, count);
+ ret = count;
+ } else
+ ret = -EIO;
+
+ mutex_unlock(&sl->master->bus_mutex);
+
+ return ret;
+}
+
+static ssize_t offset_write(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr, char *buf,
+ loff_t off, size_t count)
+{
+ struct w1_slave *sl = kobj_to_w1_slave(kobj);
+ int ret;
+
+ mutex_lock(&sl->master->bus_mutex);
+
+ if (w1_ds2438_change_offset_register(sl, buf) == 0)
+ ret = count;
+ else
+ ret = -EIO;
+
+ mutex_unlock(&sl->master->bus_mutex);
+
+ return ret;
+}
+
+static ssize_t temperature_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr, char *buf,
+ loff_t off, size_t count)
+{
+ struct w1_slave *sl = kobj_to_w1_slave(kobj);
+ int ret;
+ int16_t temp;
+
+ if (off != 0)
+ return 0;
+ if (!buf)
+ return -EINVAL;
+
+ if (w1_ds2438_get_temperature(sl, &temp) == 0)
+ ret = snprintf(buf, count, "%i\n", temp);
+ else
+ ret = -EIO;
+
+ return ret;
+}
+
+static ssize_t vad_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr, char *buf,
+ loff_t off, size_t count)
+{
+ struct w1_slave *sl = kobj_to_w1_slave(kobj);
+ int ret;
+ uint16_t voltage;
+
+ if (off != 0)
+ return 0;
+ if (!buf)
+ return -EINVAL;
+
+ if (w1_ds2438_get_voltage(sl, DS2438_ADC_INPUT_VAD, &voltage) == 0)
+ ret = snprintf(buf, count, "%u\n", voltage);
+ else
+ ret = -EIO;
+
+ return ret;
+}
+
+static ssize_t vdd_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr, char *buf,
+ loff_t off, size_t count)
+{
+ struct w1_slave *sl = kobj_to_w1_slave(kobj);
+ int ret;
+ uint16_t voltage;
+
+ if (off != 0)
+ return 0;
+ if (!buf)
+ return -EINVAL;
+
+ if (w1_ds2438_get_voltage(sl, DS2438_ADC_INPUT_VDD, &voltage) == 0)
+ ret = snprintf(buf, count, "%u\n", voltage);
+ else
+ ret = -EIO;
+
+ return ret;
+}
+
+static BIN_ATTR_RW(iad, 0);
+static BIN_ATTR_RO(page0, DS2438_PAGE_SIZE);
+static BIN_ATTR_RO(page1, DS2438_PAGE_SIZE);
+static BIN_ATTR_WO(offset, 2);
+static BIN_ATTR_RO(temperature, 0/* real length varies */);
+static BIN_ATTR_RO(vad, 0/* real length varies */);
+static BIN_ATTR_RO(vdd, 0/* real length varies */);
+
+static struct bin_attribute *w1_ds2438_bin_attrs[] = {
+ &bin_attr_iad,
+ &bin_attr_page0,
+ &bin_attr_page1,
+ &bin_attr_offset,
+ &bin_attr_temperature,
+ &bin_attr_vad,
+ &bin_attr_vdd,
+ NULL,
+};
+
+static const struct attribute_group w1_ds2438_group = {
+ .bin_attrs = w1_ds2438_bin_attrs,
+};
+
+static const struct attribute_group *w1_ds2438_groups[] = {
+ &w1_ds2438_group,
+ NULL,
+};
+
+static const struct w1_family_ops w1_ds2438_fops = {
+ .groups = w1_ds2438_groups,
+};
+
+static struct w1_family w1_ds2438_family = {
+ .fid = W1_FAMILY_DS2438,
+ .fops = &w1_ds2438_fops,
+};
+module_w1_family(w1_ds2438_family);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mariusz Bialonczyk <manio@skyboo.net>");
+MODULE_DESCRIPTION("1-wire driver for Maxim/Dallas DS2438 Smart Battery Monitor");
+MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_DS2438));
diff --git a/drivers/w1/slaves/w1_ds250x.c b/drivers/w1/slaves/w1_ds250x.c
new file mode 100644
index 000000000..7592c7050
--- /dev/null
+++ b/drivers/w1/slaves/w1_ds250x.c
@@ -0,0 +1,290 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * w1_ds250x.c - w1 family 09/0b/89/91 (DS250x) driver
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/crc16.h>
+
+#include <linux/w1.h>
+#include <linux/nvmem-provider.h>
+
+#define W1_DS2501_UNW_FAMILY 0x91
+#define W1_DS2501_SIZE 64
+
+#define W1_DS2502_FAMILY 0x09
+#define W1_DS2502_UNW_FAMILY 0x89
+#define W1_DS2502_SIZE 128
+
+#define W1_DS2505_FAMILY 0x0b
+#define W1_DS2505_SIZE 2048
+
+#define W1_PAGE_SIZE 32
+
+#define W1_EXT_READ_MEMORY 0xA5
+#define W1_READ_DATA_CRC 0xC3
+
+#define OFF2PG(off) ((off) / W1_PAGE_SIZE)
+
+#define CRC16_INIT 0
+#define CRC16_VALID 0xb001
+
+struct w1_eprom_data {
+ size_t size;
+ int (*read)(struct w1_slave *sl, int pageno);
+ u8 eprom[W1_DS2505_SIZE];
+ DECLARE_BITMAP(page_present, W1_DS2505_SIZE / W1_PAGE_SIZE);
+ char nvmem_name[64];
+};
+
+static int w1_ds2502_read_page(struct w1_slave *sl, int pageno)
+{
+ struct w1_eprom_data *data = sl->family_data;
+ int pgoff = pageno * W1_PAGE_SIZE;
+ int ret = -EIO;
+ u8 buf[3];
+ u8 crc8;
+
+ if (test_bit(pageno, data->page_present))
+ return 0; /* page already present */
+
+ mutex_lock(&sl->master->bus_mutex);
+
+ if (w1_reset_select_slave(sl))
+ goto err;
+
+ buf[0] = W1_READ_DATA_CRC;
+ buf[1] = pgoff & 0xff;
+ buf[2] = pgoff >> 8;
+ w1_write_block(sl->master, buf, 3);
+
+ crc8 = w1_read_8(sl->master);
+ if (w1_calc_crc8(buf, 3) != crc8)
+ goto err;
+
+ w1_read_block(sl->master, &data->eprom[pgoff], W1_PAGE_SIZE);
+
+ crc8 = w1_read_8(sl->master);
+ if (w1_calc_crc8(&data->eprom[pgoff], W1_PAGE_SIZE) != crc8)
+ goto err;
+
+ set_bit(pageno, data->page_present); /* mark page present */
+ ret = 0;
+err:
+ mutex_unlock(&sl->master->bus_mutex);
+ return ret;
+}
+
+static int w1_ds2505_read_page(struct w1_slave *sl, int pageno)
+{
+ struct w1_eprom_data *data = sl->family_data;
+ int redir_retries = 16;
+ int pgoff, epoff;
+ int ret = -EIO;
+ u8 buf[6];
+ u8 redir;
+ u16 crc;
+
+ if (test_bit(pageno, data->page_present))
+ return 0; /* page already present */
+
+ epoff = pgoff = pageno * W1_PAGE_SIZE;
+ mutex_lock(&sl->master->bus_mutex);
+
+retry:
+ if (w1_reset_select_slave(sl))
+ goto err;
+
+ buf[0] = W1_EXT_READ_MEMORY;
+ buf[1] = pgoff & 0xff;
+ buf[2] = pgoff >> 8;
+ w1_write_block(sl->master, buf, 3);
+ w1_read_block(sl->master, buf + 3, 3); /* redir, crc16 */
+ redir = buf[3];
+ crc = crc16(CRC16_INIT, buf, 6);
+
+ if (crc != CRC16_VALID)
+ goto err;
+
+
+ if (redir != 0xff) {
+ redir_retries--;
+ if (redir_retries < 0)
+ goto err;
+
+ pgoff = (redir ^ 0xff) * W1_PAGE_SIZE;
+ goto retry;
+ }
+
+ w1_read_block(sl->master, &data->eprom[epoff], W1_PAGE_SIZE);
+ w1_read_block(sl->master, buf, 2); /* crc16 */
+ crc = crc16(CRC16_INIT, &data->eprom[epoff], W1_PAGE_SIZE);
+ crc = crc16(crc, buf, 2);
+
+ if (crc != CRC16_VALID)
+ goto err;
+
+ set_bit(pageno, data->page_present);
+ ret = 0;
+err:
+ mutex_unlock(&sl->master->bus_mutex);
+ return ret;
+}
+
+static int w1_nvmem_read(void *priv, unsigned int off, void *buf, size_t count)
+{
+ struct w1_slave *sl = priv;
+ struct w1_eprom_data *data = sl->family_data;
+ size_t eprom_size = data->size;
+ int ret;
+ int i;
+
+ if (off > eprom_size)
+ return -EINVAL;
+
+ if ((off + count) > eprom_size)
+ count = eprom_size - off;
+
+ i = OFF2PG(off);
+ do {
+ ret = data->read(sl, i++);
+ if (ret < 0)
+ return ret;
+ } while (i < OFF2PG(off + count));
+
+ memcpy(buf, &data->eprom[off], count);
+ return 0;
+}
+
+static int w1_eprom_add_slave(struct w1_slave *sl)
+{
+ struct w1_eprom_data *data;
+ struct nvmem_device *nvmem;
+ struct nvmem_config nvmem_cfg = {
+ .dev = &sl->dev,
+ .reg_read = w1_nvmem_read,
+ .type = NVMEM_TYPE_OTP,
+ .read_only = true,
+ .word_size = 1,
+ .priv = sl,
+ .id = -1
+ };
+
+ data = devm_kzalloc(&sl->dev, sizeof(struct w1_eprom_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ sl->family_data = data;
+ switch (sl->family->fid) {
+ case W1_DS2501_UNW_FAMILY:
+ data->size = W1_DS2501_SIZE;
+ data->read = w1_ds2502_read_page;
+ break;
+ case W1_DS2502_FAMILY:
+ case W1_DS2502_UNW_FAMILY:
+ data->size = W1_DS2502_SIZE;
+ data->read = w1_ds2502_read_page;
+ break;
+ case W1_DS2505_FAMILY:
+ data->size = W1_DS2505_SIZE;
+ data->read = w1_ds2505_read_page;
+ break;
+ }
+
+ if (sl->master->bus_master->dev_id)
+ snprintf(data->nvmem_name, sizeof(data->nvmem_name),
+ "%s-%02x-%012llx",
+ sl->master->bus_master->dev_id, sl->reg_num.family,
+ (unsigned long long)sl->reg_num.id);
+ else
+ snprintf(data->nvmem_name, sizeof(data->nvmem_name),
+ "%02x-%012llx",
+ sl->reg_num.family,
+ (unsigned long long)sl->reg_num.id);
+
+ nvmem_cfg.name = data->nvmem_name;
+ nvmem_cfg.size = data->size;
+
+ nvmem = devm_nvmem_register(&sl->dev, &nvmem_cfg);
+ return PTR_ERR_OR_ZERO(nvmem);
+}
+
+static const struct w1_family_ops w1_eprom_fops = {
+ .add_slave = w1_eprom_add_slave,
+};
+
+static struct w1_family w1_family_09 = {
+ .fid = W1_DS2502_FAMILY,
+ .fops = &w1_eprom_fops,
+};
+
+static struct w1_family w1_family_0b = {
+ .fid = W1_DS2505_FAMILY,
+ .fops = &w1_eprom_fops,
+};
+
+static struct w1_family w1_family_89 = {
+ .fid = W1_DS2502_UNW_FAMILY,
+ .fops = &w1_eprom_fops,
+};
+
+static struct w1_family w1_family_91 = {
+ .fid = W1_DS2501_UNW_FAMILY,
+ .fops = &w1_eprom_fops,
+};
+
+static int __init w1_ds250x_init(void)
+{
+ int err;
+
+ err = w1_register_family(&w1_family_09);
+ if (err)
+ return err;
+
+ err = w1_register_family(&w1_family_0b);
+ if (err)
+ goto err_0b;
+
+ err = w1_register_family(&w1_family_89);
+ if (err)
+ goto err_89;
+
+ err = w1_register_family(&w1_family_91);
+ if (err)
+ goto err_91;
+
+ return 0;
+
+err_91:
+ w1_unregister_family(&w1_family_89);
+err_89:
+ w1_unregister_family(&w1_family_0b);
+err_0b:
+ w1_unregister_family(&w1_family_09);
+ return err;
+}
+
+static void __exit w1_ds250x_exit(void)
+{
+ w1_unregister_family(&w1_family_09);
+ w1_unregister_family(&w1_family_0b);
+ w1_unregister_family(&w1_family_89);
+ w1_unregister_family(&w1_family_91);
+}
+
+module_init(w1_ds250x_init);
+module_exit(w1_ds250x_exit);
+
+MODULE_AUTHOR("Thomas Bogendoerfer <tbogendoerfe@suse.de>");
+MODULE_DESCRIPTION("w1 family driver for DS250x Add Only Memory");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("w1-family-" __stringify(W1_DS2502_FAMILY));
+MODULE_ALIAS("w1-family-" __stringify(W1_DS2505_FAMILY));
+MODULE_ALIAS("w1-family-" __stringify(W1_DS2501_UNW_FAMILY));
+MODULE_ALIAS("w1-family-" __stringify(W1_DS2502_UNW_FAMILY));
diff --git a/drivers/w1/slaves/w1_ds2780.c b/drivers/w1/slaves/w1_ds2780.c
new file mode 100644
index 000000000..9dcb5a54f
--- /dev/null
+++ b/drivers/w1/slaves/w1_ds2780.c
@@ -0,0 +1,159 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * 1-Wire implementation for the ds2780 chip
+ *
+ * Copyright (C) 2010 Indesign, LLC
+ *
+ * Author: Clifton Barnes <cabarnes@indesign-llc.com>
+ *
+ * Based on w1-ds2760 driver
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <linux/idr.h>
+
+#include <linux/w1.h>
+
+#include "w1_ds2780.h"
+
+#define W1_FAMILY_DS2780 0x32
+
+static int w1_ds2780_do_io(struct device *dev, char *buf, int addr,
+ size_t count, int io)
+{
+ struct w1_slave *sl = container_of(dev, struct w1_slave, dev);
+
+ if (addr > DS2780_DATA_SIZE || addr < 0)
+ return 0;
+
+ count = min_t(int, count, DS2780_DATA_SIZE - addr);
+
+ if (w1_reset_select_slave(sl) == 0) {
+ if (io) {
+ w1_write_8(sl->master, W1_DS2780_WRITE_DATA);
+ w1_write_8(sl->master, addr);
+ w1_write_block(sl->master, buf, count);
+ } else {
+ w1_write_8(sl->master, W1_DS2780_READ_DATA);
+ w1_write_8(sl->master, addr);
+ count = w1_read_block(sl->master, buf, count);
+ }
+ }
+
+ return count;
+}
+
+int w1_ds2780_io(struct device *dev, char *buf, int addr, size_t count,
+ int io)
+{
+ struct w1_slave *sl = container_of(dev, struct w1_slave, dev);
+ int ret;
+
+ if (!dev)
+ return -ENODEV;
+
+ mutex_lock(&sl->master->bus_mutex);
+
+ ret = w1_ds2780_do_io(dev, buf, addr, count, io);
+
+ mutex_unlock(&sl->master->bus_mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL(w1_ds2780_io);
+
+int w1_ds2780_eeprom_cmd(struct device *dev, int addr, int cmd)
+{
+ struct w1_slave *sl = container_of(dev, struct w1_slave, dev);
+
+ if (!dev)
+ return -EINVAL;
+
+ mutex_lock(&sl->master->bus_mutex);
+
+ if (w1_reset_select_slave(sl) == 0) {
+ w1_write_8(sl->master, cmd);
+ w1_write_8(sl->master, addr);
+ }
+
+ mutex_unlock(&sl->master->bus_mutex);
+ return 0;
+}
+EXPORT_SYMBOL(w1_ds2780_eeprom_cmd);
+
+static ssize_t w1_slave_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr, char *buf,
+ loff_t off, size_t count)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ return w1_ds2780_io(dev, buf, off, count, 0);
+}
+
+static BIN_ATTR_RO(w1_slave, DS2780_DATA_SIZE);
+
+static struct bin_attribute *w1_ds2780_bin_attrs[] = {
+ &bin_attr_w1_slave,
+ NULL,
+};
+
+static const struct attribute_group w1_ds2780_group = {
+ .bin_attrs = w1_ds2780_bin_attrs,
+};
+
+static const struct attribute_group *w1_ds2780_groups[] = {
+ &w1_ds2780_group,
+ NULL,
+};
+
+static int w1_ds2780_add_slave(struct w1_slave *sl)
+{
+ int ret;
+ struct platform_device *pdev;
+
+ pdev = platform_device_alloc("ds2780-battery", PLATFORM_DEVID_AUTO);
+ if (!pdev)
+ return -ENOMEM;
+ pdev->dev.parent = &sl->dev;
+
+ ret = platform_device_add(pdev);
+ if (ret)
+ goto pdev_add_failed;
+
+ dev_set_drvdata(&sl->dev, pdev);
+
+ return 0;
+
+pdev_add_failed:
+ platform_device_put(pdev);
+
+ return ret;
+}
+
+static void w1_ds2780_remove_slave(struct w1_slave *sl)
+{
+ struct platform_device *pdev = dev_get_drvdata(&sl->dev);
+
+ platform_device_unregister(pdev);
+}
+
+static const struct w1_family_ops w1_ds2780_fops = {
+ .add_slave = w1_ds2780_add_slave,
+ .remove_slave = w1_ds2780_remove_slave,
+ .groups = w1_ds2780_groups,
+};
+
+static struct w1_family w1_ds2780_family = {
+ .fid = W1_FAMILY_DS2780,
+ .fops = &w1_ds2780_fops,
+};
+module_w1_family(w1_ds2780_family);
+
+MODULE_AUTHOR("Clifton Barnes <cabarnes@indesign-llc.com>");
+MODULE_DESCRIPTION("1-wire Driver for Maxim/Dallas DS2780 Stand-Alone Fuel Gauge IC");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_DS2780));
diff --git a/drivers/w1/slaves/w1_ds2780.h b/drivers/w1/slaves/w1_ds2780.h
new file mode 100644
index 000000000..99e38ed78
--- /dev/null
+++ b/drivers/w1/slaves/w1_ds2780.h
@@ -0,0 +1,125 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * 1-Wire implementation for the ds2780 chip
+ *
+ * Copyright (C) 2010 Indesign, LLC
+ *
+ * Author: Clifton Barnes <cabarnes@indesign-llc.com>
+ *
+ * Based on w1-ds2760 driver
+ */
+
+#ifndef _W1_DS2780_H
+#define _W1_DS2780_H
+
+/* Function commands */
+#define W1_DS2780_READ_DATA 0x69
+#define W1_DS2780_WRITE_DATA 0x6C
+#define W1_DS2780_COPY_DATA 0x48
+#define W1_DS2780_RECALL_DATA 0xB8
+#define W1_DS2780_LOCK 0x6A
+
+/* Register map */
+/* Register 0x00 Reserved */
+#define DS2780_STATUS_REG 0x01
+#define DS2780_RAAC_MSB_REG 0x02
+#define DS2780_RAAC_LSB_REG 0x03
+#define DS2780_RSAC_MSB_REG 0x04
+#define DS2780_RSAC_LSB_REG 0x05
+#define DS2780_RARC_REG 0x06
+#define DS2780_RSRC_REG 0x07
+#define DS2780_IAVG_MSB_REG 0x08
+#define DS2780_IAVG_LSB_REG 0x09
+#define DS2780_TEMP_MSB_REG 0x0A
+#define DS2780_TEMP_LSB_REG 0x0B
+#define DS2780_VOLT_MSB_REG 0x0C
+#define DS2780_VOLT_LSB_REG 0x0D
+#define DS2780_CURRENT_MSB_REG 0x0E
+#define DS2780_CURRENT_LSB_REG 0x0F
+#define DS2780_ACR_MSB_REG 0x10
+#define DS2780_ACR_LSB_REG 0x11
+#define DS2780_ACRL_MSB_REG 0x12
+#define DS2780_ACRL_LSB_REG 0x13
+#define DS2780_AS_REG 0x14
+#define DS2780_SFR_REG 0x15
+#define DS2780_FULL_MSB_REG 0x16
+#define DS2780_FULL_LSB_REG 0x17
+#define DS2780_AE_MSB_REG 0x18
+#define DS2780_AE_LSB_REG 0x19
+#define DS2780_SE_MSB_REG 0x1A
+#define DS2780_SE_LSB_REG 0x1B
+/* Register 0x1C - 0x1E Reserved */
+#define DS2780_EEPROM_REG 0x1F
+#define DS2780_EEPROM_BLOCK0_START 0x20
+/* Register 0x20 - 0x2F User EEPROM */
+#define DS2780_EEPROM_BLOCK0_END 0x2F
+/* Register 0x30 - 0x5F Reserved */
+#define DS2780_EEPROM_BLOCK1_START 0x60
+#define DS2780_CONTROL_REG 0x60
+#define DS2780_AB_REG 0x61
+#define DS2780_AC_MSB_REG 0x62
+#define DS2780_AC_LSB_REG 0x63
+#define DS2780_VCHG_REG 0x64
+#define DS2780_IMIN_REG 0x65
+#define DS2780_VAE_REG 0x66
+#define DS2780_IAE_REG 0x67
+#define DS2780_AE_40_REG 0x68
+#define DS2780_RSNSP_REG 0x69
+#define DS2780_FULL_40_MSB_REG 0x6A
+#define DS2780_FULL_40_LSB_REG 0x6B
+#define DS2780_FULL_3040_SLOPE_REG 0x6C
+#define DS2780_FULL_2030_SLOPE_REG 0x6D
+#define DS2780_FULL_1020_SLOPE_REG 0x6E
+#define DS2780_FULL_0010_SLOPE_REG 0x6F
+#define DS2780_AE_3040_SLOPE_REG 0x70
+#define DS2780_AE_2030_SLOPE_REG 0x71
+#define DS2780_AE_1020_SLOPE_REG 0x72
+#define DS2780_AE_0010_SLOPE_REG 0x73
+#define DS2780_SE_3040_SLOPE_REG 0x74
+#define DS2780_SE_2030_SLOPE_REG 0x75
+#define DS2780_SE_1020_SLOPE_REG 0x76
+#define DS2780_SE_0010_SLOPE_REG 0x77
+#define DS2780_RSGAIN_MSB_REG 0x78
+#define DS2780_RSGAIN_LSB_REG 0x79
+#define DS2780_RSTC_REG 0x7A
+#define DS2780_FRSGAIN_MSB_REG 0x7B
+#define DS2780_FRSGAIN_LSB_REG 0x7C
+#define DS2780_EEPROM_BLOCK1_END 0x7C
+/* Register 0x7D - 0xFF Reserved */
+
+/* Number of valid register addresses */
+#define DS2780_DATA_SIZE 0x80
+
+/* Status register bits */
+#define DS2780_STATUS_REG_CHGTF (1 << 7)
+#define DS2780_STATUS_REG_AEF (1 << 6)
+#define DS2780_STATUS_REG_SEF (1 << 5)
+#define DS2780_STATUS_REG_LEARNF (1 << 4)
+/* Bit 3 Reserved */
+#define DS2780_STATUS_REG_UVF (1 << 2)
+#define DS2780_STATUS_REG_PORF (1 << 1)
+/* Bit 0 Reserved */
+
+/* Control register bits */
+/* Bit 7 Reserved */
+#define DS2780_CONTROL_REG_UVEN (1 << 6)
+#define DS2780_CONTROL_REG_PMOD (1 << 5)
+#define DS2780_CONTROL_REG_RNAOP (1 << 4)
+/* Bit 0 - 3 Reserved */
+
+/* Special feature register bits */
+/* Bit 1 - 7 Reserved */
+#define DS2780_SFR_REG_PIOSC (1 << 0)
+
+/* EEPROM register bits */
+#define DS2780_EEPROM_REG_EEC (1 << 7)
+#define DS2780_EEPROM_REG_LOCK (1 << 6)
+/* Bit 2 - 6 Reserved */
+#define DS2780_EEPROM_REG_BL1 (1 << 1)
+#define DS2780_EEPROM_REG_BL0 (1 << 0)
+
+extern int w1_ds2780_io(struct device *dev, char *buf, int addr, size_t count,
+ int io);
+extern int w1_ds2780_eeprom_cmd(struct device *dev, int addr, int cmd);
+
+#endif /* !_W1_DS2780_H */
diff --git a/drivers/w1/slaves/w1_ds2781.c b/drivers/w1/slaves/w1_ds2781.c
new file mode 100644
index 000000000..2cb7c020b
--- /dev/null
+++ b/drivers/w1/slaves/w1_ds2781.c
@@ -0,0 +1,156 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * 1-Wire implementation for the ds2781 chip
+ *
+ * Author: Renata Sayakhova <renata@oktetlabs.ru>
+ *
+ * Based on w1-ds2780 driver
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+
+#include <linux/w1.h>
+
+#include "w1_ds2781.h"
+
+#define W1_FAMILY_DS2781 0x3D
+
+static int w1_ds2781_do_io(struct device *dev, char *buf, int addr,
+ size_t count, int io)
+{
+ struct w1_slave *sl = container_of(dev, struct w1_slave, dev);
+
+ if (addr > DS2781_DATA_SIZE || addr < 0)
+ return 0;
+
+ count = min_t(int, count, DS2781_DATA_SIZE - addr);
+
+ if (w1_reset_select_slave(sl) == 0) {
+ if (io) {
+ w1_write_8(sl->master, W1_DS2781_WRITE_DATA);
+ w1_write_8(sl->master, addr);
+ w1_write_block(sl->master, buf, count);
+ } else {
+ w1_write_8(sl->master, W1_DS2781_READ_DATA);
+ w1_write_8(sl->master, addr);
+ count = w1_read_block(sl->master, buf, count);
+ }
+ }
+
+ return count;
+}
+
+int w1_ds2781_io(struct device *dev, char *buf, int addr, size_t count,
+ int io)
+{
+ struct w1_slave *sl = container_of(dev, struct w1_slave, dev);
+ int ret;
+
+ if (!dev)
+ return -ENODEV;
+
+ mutex_lock(&sl->master->bus_mutex);
+
+ ret = w1_ds2781_do_io(dev, buf, addr, count, io);
+
+ mutex_unlock(&sl->master->bus_mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL(w1_ds2781_io);
+
+int w1_ds2781_eeprom_cmd(struct device *dev, int addr, int cmd)
+{
+ struct w1_slave *sl = container_of(dev, struct w1_slave, dev);
+
+ if (!dev)
+ return -EINVAL;
+
+ mutex_lock(&sl->master->bus_mutex);
+
+ if (w1_reset_select_slave(sl) == 0) {
+ w1_write_8(sl->master, cmd);
+ w1_write_8(sl->master, addr);
+ }
+
+ mutex_unlock(&sl->master->bus_mutex);
+ return 0;
+}
+EXPORT_SYMBOL(w1_ds2781_eeprom_cmd);
+
+static ssize_t w1_slave_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr, char *buf,
+ loff_t off, size_t count)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ return w1_ds2781_io(dev, buf, off, count, 0);
+}
+
+static BIN_ATTR_RO(w1_slave, DS2781_DATA_SIZE);
+
+static struct bin_attribute *w1_ds2781_bin_attrs[] = {
+ &bin_attr_w1_slave,
+ NULL,
+};
+
+static const struct attribute_group w1_ds2781_group = {
+ .bin_attrs = w1_ds2781_bin_attrs,
+};
+
+static const struct attribute_group *w1_ds2781_groups[] = {
+ &w1_ds2781_group,
+ NULL,
+};
+
+static int w1_ds2781_add_slave(struct w1_slave *sl)
+{
+ int ret;
+ struct platform_device *pdev;
+
+ pdev = platform_device_alloc("ds2781-battery", PLATFORM_DEVID_AUTO);
+ if (!pdev)
+ return -ENOMEM;
+ pdev->dev.parent = &sl->dev;
+
+ ret = platform_device_add(pdev);
+ if (ret)
+ goto pdev_add_failed;
+
+ dev_set_drvdata(&sl->dev, pdev);
+
+ return 0;
+
+pdev_add_failed:
+ platform_device_put(pdev);
+
+ return ret;
+}
+
+static void w1_ds2781_remove_slave(struct w1_slave *sl)
+{
+ struct platform_device *pdev = dev_get_drvdata(&sl->dev);
+
+ platform_device_unregister(pdev);
+}
+
+static const struct w1_family_ops w1_ds2781_fops = {
+ .add_slave = w1_ds2781_add_slave,
+ .remove_slave = w1_ds2781_remove_slave,
+ .groups = w1_ds2781_groups,
+};
+
+static struct w1_family w1_ds2781_family = {
+ .fid = W1_FAMILY_DS2781,
+ .fops = &w1_ds2781_fops,
+};
+module_w1_family(w1_ds2781_family);
+
+MODULE_AUTHOR("Renata Sayakhova <renata@oktetlabs.ru>");
+MODULE_DESCRIPTION("1-wire Driver for Maxim/Dallas DS2781 Stand-Alone Fuel Gauge IC");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_DS2781));
diff --git a/drivers/w1/slaves/w1_ds2781.h b/drivers/w1/slaves/w1_ds2781.h
new file mode 100644
index 000000000..fa902dfa0
--- /dev/null
+++ b/drivers/w1/slaves/w1_ds2781.h
@@ -0,0 +1,130 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * 1-Wire implementation for the ds2780 chip
+ *
+ * Author: Renata Sayakhova <renata@oktetlabs.ru>
+ *
+ * Based on w1-ds2760 driver
+ */
+
+#ifndef _W1_DS2781_H
+#define _W1_DS2781_H
+
+/* Function commands */
+#define W1_DS2781_READ_DATA 0x69
+#define W1_DS2781_WRITE_DATA 0x6C
+#define W1_DS2781_COPY_DATA 0x48
+#define W1_DS2781_RECALL_DATA 0xB8
+#define W1_DS2781_LOCK 0x6A
+
+/* Register map */
+/* Register 0x00 Reserved */
+#define DS2781_STATUS 0x01
+#define DS2781_RAAC_MSB 0x02
+#define DS2781_RAAC_LSB 0x03
+#define DS2781_RSAC_MSB 0x04
+#define DS2781_RSAC_LSB 0x05
+#define DS2781_RARC 0x06
+#define DS2781_RSRC 0x07
+#define DS2781_IAVG_MSB 0x08
+#define DS2781_IAVG_LSB 0x09
+#define DS2781_TEMP_MSB 0x0A
+#define DS2781_TEMP_LSB 0x0B
+#define DS2781_VOLT_MSB 0x0C
+#define DS2781_VOLT_LSB 0x0D
+#define DS2781_CURRENT_MSB 0x0E
+#define DS2781_CURRENT_LSB 0x0F
+#define DS2781_ACR_MSB 0x10
+#define DS2781_ACR_LSB 0x11
+#define DS2781_ACRL_MSB 0x12
+#define DS2781_ACRL_LSB 0x13
+#define DS2781_AS 0x14
+#define DS2781_SFR 0x15
+#define DS2781_FULL_MSB 0x16
+#define DS2781_FULL_LSB 0x17
+#define DS2781_AE_MSB 0x18
+#define DS2781_AE_LSB 0x19
+#define DS2781_SE_MSB 0x1A
+#define DS2781_SE_LSB 0x1B
+/* Register 0x1C - 0x1E Reserved */
+#define DS2781_EEPROM 0x1F
+#define DS2781_EEPROM_BLOCK0_START 0x20
+/* Register 0x20 - 0x2F User EEPROM */
+#define DS2781_EEPROM_BLOCK0_END 0x2F
+/* Register 0x30 - 0x5F Reserved */
+#define DS2781_EEPROM_BLOCK1_START 0x60
+#define DS2781_CONTROL 0x60
+#define DS2781_AB 0x61
+#define DS2781_AC_MSB 0x62
+#define DS2781_AC_LSB 0x63
+#define DS2781_VCHG 0x64
+#define DS2781_IMIN 0x65
+#define DS2781_VAE 0x66
+#define DS2781_IAE 0x67
+#define DS2781_AE_40 0x68
+#define DS2781_RSNSP 0x69
+#define DS2781_FULL_40_MSB 0x6A
+#define DS2781_FULL_40_LSB 0x6B
+#define DS2781_FULL_4_SLOPE 0x6C
+#define DS2781_FULL_3_SLOPE 0x6D
+#define DS2781_FULL_2_SLOPE 0x6E
+#define DS2781_FULL_1_SLOPE 0x6F
+#define DS2781_AE_4_SLOPE 0x70
+#define DS2781_AE_3_SLOPE 0x71
+#define DS2781_AE_2_SLOPE 0x72
+#define DS2781_AE_1_SLOPE 0x73
+#define DS2781_SE_4_SLOPE 0x74
+#define DS2781_SE_3_SLOPE 0x75
+#define DS2781_SE_2_SLOPE 0x76
+#define DS2781_SE_1_SLOPE 0x77
+#define DS2781_RSGAIN_MSB 0x78
+#define DS2781_RSGAIN_LSB 0x79
+#define DS2781_RSTC 0x7A
+#define DS2781_COB 0x7B
+#define DS2781_TBP34 0x7C
+#define DS2781_TBP23 0x7D
+#define DS2781_TBP12 0x7E
+#define DS2781_EEPROM_BLOCK1_END 0x7F
+/* Register 0x7D - 0xFF Reserved */
+
+#define DS2781_FSGAIN_MSB 0xB0
+#define DS2781_FSGAIN_LSB 0xB1
+
+/* Number of valid register addresses */
+#define DS2781_DATA_SIZE 0xB2
+
+/* Status register bits */
+#define DS2781_STATUS_CHGTF (1 << 7)
+#define DS2781_STATUS_AEF (1 << 6)
+#define DS2781_STATUS_SEF (1 << 5)
+#define DS2781_STATUS_LEARNF (1 << 4)
+/* Bit 3 Reserved */
+#define DS2781_STATUS_UVF (1 << 2)
+#define DS2781_STATUS_PORF (1 << 1)
+/* Bit 0 Reserved */
+
+/* Control register bits */
+/* Bit 7 Reserved */
+#define DS2781_CONTROL_NBEN (1 << 7)
+#define DS2781_CONTROL_UVEN (1 << 6)
+#define DS2781_CONTROL_PMOD (1 << 5)
+#define DS2781_CONTROL_RNAOP (1 << 4)
+#define DS1781_CONTROL_UVTH (1 << 3)
+/* Bit 0 - 2 Reserved */
+
+/* Special feature register bits */
+/* Bit 1 - 7 Reserved */
+#define DS2781_SFR_PIOSC (1 << 0)
+
+/* EEPROM register bits */
+#define DS2781_EEPROM_EEC (1 << 7)
+#define DS2781_EEPROM_LOCK (1 << 6)
+/* Bit 2 - 6 Reserved */
+#define DS2781_EEPROM_BL1 (1 << 1)
+#define DS2781_EEPROM_BL0 (1 << 0)
+
+extern int w1_ds2781_io(struct device *dev, char *buf, int addr, size_t count,
+ int io);
+extern int w1_ds2781_eeprom_cmd(struct device *dev, int addr, int cmd);
+
+#endif /* !_W1_DS2781_H */
diff --git a/drivers/w1/slaves/w1_ds2805.c b/drivers/w1/slaves/w1_ds2805.c
new file mode 100644
index 000000000..6b5d12ba1
--- /dev/null
+++ b/drivers/w1/slaves/w1_ds2805.c
@@ -0,0 +1,298 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * w1_ds2805 - w1 family 0d (DS28E05) driver
+ *
+ * Copyright (c) 2016 Andrew Worsley amworsley@gmail.com
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+
+#include <linux/w1.h>
+
+#define W1_EEPROM_DS2805 0x0D
+
+#define W1_F0D_EEPROM_SIZE 128
+#define W1_F0D_PAGE_BITS 3
+#define W1_F0D_PAGE_SIZE (1<<W1_F0D_PAGE_BITS)
+#define W1_F0D_PAGE_MASK 0x0F
+
+#define W1_F0D_SCRATCH_BITS 1
+#define W1_F0D_SCRATCH_SIZE (1<<W1_F0D_SCRATCH_BITS)
+#define W1_F0D_SCRATCH_MASK (W1_F0D_SCRATCH_SIZE-1)
+
+#define W1_F0D_READ_EEPROM 0xF0
+#define W1_F0D_WRITE_EEPROM 0x55
+#define W1_F0D_RELEASE 0xFF
+
+#define W1_F0D_CS_OK 0xAA /* Chip Status Ok */
+
+#define W1_F0D_TPROG_MS 16
+
+#define W1_F0D_READ_RETRIES 10
+#define W1_F0D_READ_MAXLEN W1_F0D_EEPROM_SIZE
+
+/*
+ * Check the file size bounds and adjusts count as needed.
+ * This would not be needed if the file size didn't reset to 0 after a write.
+ */
+static inline size_t w1_f0d_fix_count(loff_t off, size_t count, size_t size)
+{
+ if (off > size)
+ return 0;
+
+ if ((off + count) > size)
+ return size - off;
+
+ return count;
+}
+
+/*
+ * Read a block from W1 ROM two times and compares the results.
+ * If they are equal they are returned, otherwise the read
+ * is repeated W1_F0D_READ_RETRIES times.
+ *
+ * count must not exceed W1_F0D_READ_MAXLEN.
+ */
+static int w1_f0d_readblock(struct w1_slave *sl, int off, int count, char *buf)
+{
+ u8 wrbuf[3];
+ u8 cmp[W1_F0D_READ_MAXLEN];
+ int tries = W1_F0D_READ_RETRIES;
+
+ do {
+ wrbuf[0] = W1_F0D_READ_EEPROM;
+ wrbuf[1] = off & 0x7f;
+ wrbuf[2] = 0;
+
+ if (w1_reset_select_slave(sl))
+ return -1;
+
+ w1_write_block(sl->master, wrbuf, sizeof(wrbuf));
+ w1_read_block(sl->master, buf, count);
+
+ if (w1_reset_select_slave(sl))
+ return -1;
+
+ w1_write_block(sl->master, wrbuf, sizeof(wrbuf));
+ w1_read_block(sl->master, cmp, count);
+
+ if (!memcmp(cmp, buf, count))
+ return 0;
+ } while (--tries);
+
+ dev_err(&sl->dev, "proof reading failed %d times\n",
+ W1_F0D_READ_RETRIES);
+
+ return -1;
+}
+
+static ssize_t w1_f0d_read_bin(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct w1_slave *sl = kobj_to_w1_slave(kobj);
+ int todo = count;
+
+ count = w1_f0d_fix_count(off, count, W1_F0D_EEPROM_SIZE);
+ if (count == 0)
+ return 0;
+
+ mutex_lock(&sl->master->mutex);
+
+ /* read directly from the EEPROM in chunks of W1_F0D_READ_MAXLEN */
+ while (todo > 0) {
+ int block_read;
+
+ if (todo >= W1_F0D_READ_MAXLEN)
+ block_read = W1_F0D_READ_MAXLEN;
+ else
+ block_read = todo;
+
+ if (w1_f0d_readblock(sl, off, block_read, buf) < 0) {
+ count = -EIO;
+ break;
+ }
+
+ todo -= W1_F0D_READ_MAXLEN;
+ buf += W1_F0D_READ_MAXLEN;
+ off += W1_F0D_READ_MAXLEN;
+ }
+
+ mutex_unlock(&sl->master->mutex);
+
+ return count;
+}
+
+/*
+ * Writes to the scratchpad and reads it back for verification.
+ * Then copies the scratchpad to EEPROM.
+ * The data must be aligned at W1_F0D_SCRATCH_SIZE bytes and
+ * must be W1_F0D_SCRATCH_SIZE bytes long.
+ * The master must be locked.
+ *
+ * @param sl The slave structure
+ * @param addr Address for the write
+ * @param len length must be <= (W1_F0D_PAGE_SIZE - (addr & W1_F0D_PAGE_MASK))
+ * @param data The data to write
+ * @return 0=Success -1=failure
+ */
+static int w1_f0d_write(struct w1_slave *sl, int addr, int len, const u8 *data)
+{
+ int tries = W1_F0D_READ_RETRIES;
+ u8 wrbuf[3];
+ u8 rdbuf[W1_F0D_SCRATCH_SIZE];
+ u8 cs;
+
+ if ((addr & 1) || (len != 2)) {
+ dev_err(&sl->dev, "%s: bad addr/len - addr=%#x len=%d\n",
+ __func__, addr, len);
+ return -1;
+ }
+
+retry:
+
+ /* Write the data to the scratchpad */
+ if (w1_reset_select_slave(sl))
+ return -1;
+
+ wrbuf[0] = W1_F0D_WRITE_EEPROM;
+ wrbuf[1] = addr & 0xff;
+ wrbuf[2] = 0xff; /* ?? from Example */
+
+ w1_write_block(sl->master, wrbuf, sizeof(wrbuf));
+ w1_write_block(sl->master, data, len);
+
+ w1_read_block(sl->master, rdbuf, sizeof(rdbuf));
+ /* Compare what was read against the data written */
+ if ((rdbuf[0] != data[0]) || (rdbuf[1] != data[1])) {
+
+ if (--tries)
+ goto retry;
+
+ dev_err(&sl->dev,
+ "could not write to eeprom, scratchpad compare failed %d times\n",
+ W1_F0D_READ_RETRIES);
+ pr_info("%s: rdbuf = %#x %#x data = %#x %#x\n",
+ __func__, rdbuf[0], rdbuf[1], data[0], data[1]);
+
+ return -1;
+ }
+
+ /* Trigger write out to EEPROM */
+ w1_write_8(sl->master, W1_F0D_RELEASE);
+
+ /* Sleep for tprog ms to wait for the write to complete */
+ msleep(W1_F0D_TPROG_MS);
+
+ /* Check CS (Command Status) == 0xAA ? */
+ cs = w1_read_8(sl->master);
+ if (cs != W1_F0D_CS_OK) {
+ dev_err(&sl->dev, "save to eeprom failed = CS=%#x\n", cs);
+ return -1;
+ }
+
+ return 0;
+}
+
+static ssize_t w1_f0d_write_bin(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct w1_slave *sl = kobj_to_w1_slave(kobj);
+ int addr, len;
+ int copy;
+
+ count = w1_f0d_fix_count(off, count, W1_F0D_EEPROM_SIZE);
+ if (count == 0)
+ return 0;
+
+ mutex_lock(&sl->master->mutex);
+
+ /* Can only write data in blocks of the size of the scratchpad */
+ addr = off;
+ len = count;
+ while (len > 0) {
+
+ /* if len too short or addr not aligned */
+ if (len < W1_F0D_SCRATCH_SIZE || addr & W1_F0D_SCRATCH_MASK) {
+ char tmp[W1_F0D_SCRATCH_SIZE];
+
+ /* read the block and update the parts to be written */
+ if (w1_f0d_readblock(sl, addr & ~W1_F0D_SCRATCH_MASK,
+ W1_F0D_SCRATCH_SIZE, tmp)) {
+ count = -EIO;
+ goto out_up;
+ }
+
+ /* copy at most to the boundary of the PAGE or len */
+ copy = W1_F0D_SCRATCH_SIZE -
+ (addr & W1_F0D_SCRATCH_MASK);
+
+ if (copy > len)
+ copy = len;
+
+ memcpy(&tmp[addr & W1_F0D_SCRATCH_MASK], buf, copy);
+ if (w1_f0d_write(sl, addr & ~W1_F0D_SCRATCH_MASK,
+ W1_F0D_SCRATCH_SIZE, tmp) < 0) {
+ count = -EIO;
+ goto out_up;
+ }
+ } else {
+
+ copy = W1_F0D_SCRATCH_SIZE;
+ if (w1_f0d_write(sl, addr, copy, buf) < 0) {
+ count = -EIO;
+ goto out_up;
+ }
+ }
+ buf += copy;
+ addr += copy;
+ len -= copy;
+ }
+
+out_up:
+ mutex_unlock(&sl->master->mutex);
+
+ return count;
+}
+
+static struct bin_attribute w1_f0d_bin_attr = {
+ .attr = {
+ .name = "eeprom",
+ .mode = S_IRUGO | S_IWUSR,
+ },
+ .size = W1_F0D_EEPROM_SIZE,
+ .read = w1_f0d_read_bin,
+ .write = w1_f0d_write_bin,
+};
+
+static int w1_f0d_add_slave(struct w1_slave *sl)
+{
+ return sysfs_create_bin_file(&sl->dev.kobj, &w1_f0d_bin_attr);
+}
+
+static void w1_f0d_remove_slave(struct w1_slave *sl)
+{
+ sysfs_remove_bin_file(&sl->dev.kobj, &w1_f0d_bin_attr);
+}
+
+static const struct w1_family_ops w1_f0d_fops = {
+ .add_slave = w1_f0d_add_slave,
+ .remove_slave = w1_f0d_remove_slave,
+};
+
+static struct w1_family w1_family_0d = {
+ .fid = W1_EEPROM_DS2805,
+ .fops = &w1_f0d_fops,
+};
+
+module_w1_family(w1_family_0d);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Andrew Worsley amworsley@gmail.com");
+MODULE_DESCRIPTION("w1 family 0d driver for DS2805, 1kb EEPROM");
diff --git a/drivers/w1/slaves/w1_ds28e04.c b/drivers/w1/slaves/w1_ds28e04.c
new file mode 100644
index 000000000..6cef6e2ed
--- /dev/null
+++ b/drivers/w1/slaves/w1_ds28e04.c
@@ -0,0 +1,414 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * w1_ds28e04.c - w1 family 1C (DS28E04) driver
+ *
+ * Copyright (c) 2012 Markus Franke <franke.m@sebakmt.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/crc16.h>
+#include <linux/uaccess.h>
+
+#define CRC16_INIT 0
+#define CRC16_VALID 0xb001
+
+#include <linux/w1.h>
+
+#define W1_FAMILY_DS28E04 0x1C
+
+/* Allow the strong pullup to be disabled, but default to enabled.
+ * If it was disabled a parasite powered device might not get the required
+ * current to copy the data from the scratchpad to EEPROM. If it is enabled
+ * parasite powered devices have a better chance of getting the current
+ * required.
+ */
+static int w1_strong_pullup = 1;
+module_param_named(strong_pullup, w1_strong_pullup, int, 0);
+
+/* enable/disable CRC checking on DS28E04-100 memory accesses */
+static bool w1_enable_crccheck = true;
+
+#define W1_EEPROM_SIZE 512
+#define W1_PAGE_COUNT 16
+#define W1_PAGE_SIZE 32
+#define W1_PAGE_BITS 5
+#define W1_PAGE_MASK 0x1F
+
+#define W1_F1C_READ_EEPROM 0xF0
+#define W1_F1C_WRITE_SCRATCH 0x0F
+#define W1_F1C_READ_SCRATCH 0xAA
+#define W1_F1C_COPY_SCRATCH 0x55
+#define W1_F1C_ACCESS_WRITE 0x5A
+
+#define W1_1C_REG_LOGIC_STATE 0x220
+
+struct w1_f1C_data {
+ u8 memory[W1_EEPROM_SIZE];
+ u32 validcrc;
+};
+
+/**
+ * Check the file size bounds and adjusts count as needed.
+ * This would not be needed if the file size didn't reset to 0 after a write.
+ */
+static inline size_t w1_f1C_fix_count(loff_t off, size_t count, size_t size)
+{
+ if (off > size)
+ return 0;
+
+ if ((off + count) > size)
+ return size - off;
+
+ return count;
+}
+
+static int w1_f1C_refresh_block(struct w1_slave *sl, struct w1_f1C_data *data,
+ int block)
+{
+ u8 wrbuf[3];
+ int off = block * W1_PAGE_SIZE;
+
+ if (data->validcrc & (1 << block))
+ return 0;
+
+ if (w1_reset_select_slave(sl)) {
+ data->validcrc = 0;
+ return -EIO;
+ }
+
+ wrbuf[0] = W1_F1C_READ_EEPROM;
+ wrbuf[1] = off & 0xff;
+ wrbuf[2] = off >> 8;
+ w1_write_block(sl->master, wrbuf, 3);
+ w1_read_block(sl->master, &data->memory[off], W1_PAGE_SIZE);
+
+ /* cache the block if the CRC is valid */
+ if (crc16(CRC16_INIT, &data->memory[off], W1_PAGE_SIZE) == CRC16_VALID)
+ data->validcrc |= (1 << block);
+
+ return 0;
+}
+
+static int w1_f1C_read(struct w1_slave *sl, int addr, int len, char *data)
+{
+ u8 wrbuf[3];
+
+ /* read directly from the EEPROM */
+ if (w1_reset_select_slave(sl))
+ return -EIO;
+
+ wrbuf[0] = W1_F1C_READ_EEPROM;
+ wrbuf[1] = addr & 0xff;
+ wrbuf[2] = addr >> 8;
+
+ w1_write_block(sl->master, wrbuf, sizeof(wrbuf));
+ return w1_read_block(sl->master, data, len);
+}
+
+static ssize_t eeprom_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr, char *buf,
+ loff_t off, size_t count)
+{
+ struct w1_slave *sl = kobj_to_w1_slave(kobj);
+ struct w1_f1C_data *data = sl->family_data;
+ int i, min_page, max_page;
+
+ count = w1_f1C_fix_count(off, count, W1_EEPROM_SIZE);
+ if (count == 0)
+ return 0;
+
+ mutex_lock(&sl->master->mutex);
+
+ if (w1_enable_crccheck) {
+ min_page = (off >> W1_PAGE_BITS);
+ max_page = (off + count - 1) >> W1_PAGE_BITS;
+ for (i = min_page; i <= max_page; i++) {
+ if (w1_f1C_refresh_block(sl, data, i)) {
+ count = -EIO;
+ goto out_up;
+ }
+ }
+ memcpy(buf, &data->memory[off], count);
+ } else {
+ count = w1_f1C_read(sl, off, count, buf);
+ }
+
+out_up:
+ mutex_unlock(&sl->master->mutex);
+
+ return count;
+}
+
+/**
+ * Writes to the scratchpad and reads it back for verification.
+ * Then copies the scratchpad to EEPROM.
+ * The data must be on one page.
+ * The master must be locked.
+ *
+ * @param sl The slave structure
+ * @param addr Address for the write
+ * @param len length must be <= (W1_PAGE_SIZE - (addr & W1_PAGE_MASK))
+ * @param data The data to write
+ * @return 0=Success -1=failure
+ */
+static int w1_f1C_write(struct w1_slave *sl, int addr, int len, const u8 *data)
+{
+ u8 wrbuf[4];
+ u8 rdbuf[W1_PAGE_SIZE + 3];
+ u8 es = (addr + len - 1) & 0x1f;
+ unsigned int tm = 10;
+ int i;
+ struct w1_f1C_data *f1C = sl->family_data;
+
+ /* Write the data to the scratchpad */
+ if (w1_reset_select_slave(sl))
+ return -1;
+
+ wrbuf[0] = W1_F1C_WRITE_SCRATCH;
+ wrbuf[1] = addr & 0xff;
+ wrbuf[2] = addr >> 8;
+
+ w1_write_block(sl->master, wrbuf, 3);
+ w1_write_block(sl->master, data, len);
+
+ /* Read the scratchpad and verify */
+ if (w1_reset_select_slave(sl))
+ return -1;
+
+ w1_write_8(sl->master, W1_F1C_READ_SCRATCH);
+ w1_read_block(sl->master, rdbuf, len + 3);
+
+ /* Compare what was read against the data written */
+ if ((rdbuf[0] != wrbuf[1]) || (rdbuf[1] != wrbuf[2]) ||
+ (rdbuf[2] != es) || (memcmp(data, &rdbuf[3], len) != 0))
+ return -1;
+
+ /* Copy the scratchpad to EEPROM */
+ if (w1_reset_select_slave(sl))
+ return -1;
+
+ wrbuf[0] = W1_F1C_COPY_SCRATCH;
+ wrbuf[3] = es;
+
+ for (i = 0; i < sizeof(wrbuf); ++i) {
+ /* issue 10ms strong pullup (or delay) on the last byte
+ for writing the data from the scratchpad to EEPROM */
+ if (w1_strong_pullup && i == sizeof(wrbuf)-1)
+ w1_next_pullup(sl->master, tm);
+
+ w1_write_8(sl->master, wrbuf[i]);
+ }
+
+ if (!w1_strong_pullup)
+ msleep(tm);
+
+ if (w1_enable_crccheck) {
+ /* invalidate cached data */
+ f1C->validcrc &= ~(1 << (addr >> W1_PAGE_BITS));
+ }
+
+ /* Reset the bus to wake up the EEPROM (this may not be needed) */
+ w1_reset_bus(sl->master);
+
+ return 0;
+}
+
+static ssize_t eeprom_write(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr, char *buf,
+ loff_t off, size_t count)
+
+{
+ struct w1_slave *sl = kobj_to_w1_slave(kobj);
+ int addr, len, idx;
+
+ count = w1_f1C_fix_count(off, count, W1_EEPROM_SIZE);
+ if (count == 0)
+ return 0;
+
+ if (w1_enable_crccheck) {
+ /* can only write full blocks in cached mode */
+ if ((off & W1_PAGE_MASK) || (count & W1_PAGE_MASK)) {
+ dev_err(&sl->dev, "invalid offset/count off=%d cnt=%zd\n",
+ (int)off, count);
+ return -EINVAL;
+ }
+
+ /* make sure the block CRCs are valid */
+ for (idx = 0; idx < count; idx += W1_PAGE_SIZE) {
+ if (crc16(CRC16_INIT, &buf[idx], W1_PAGE_SIZE)
+ != CRC16_VALID) {
+ dev_err(&sl->dev, "bad CRC at offset %d\n",
+ (int)off);
+ return -EINVAL;
+ }
+ }
+ }
+
+ mutex_lock(&sl->master->mutex);
+
+ /* Can only write data to one page at a time */
+ idx = 0;
+ while (idx < count) {
+ addr = off + idx;
+ len = W1_PAGE_SIZE - (addr & W1_PAGE_MASK);
+ if (len > (count - idx))
+ len = count - idx;
+
+ if (w1_f1C_write(sl, addr, len, &buf[idx]) < 0) {
+ count = -EIO;
+ goto out_up;
+ }
+ idx += len;
+ }
+
+out_up:
+ mutex_unlock(&sl->master->mutex);
+
+ return count;
+}
+
+static BIN_ATTR_RW(eeprom, W1_EEPROM_SIZE);
+
+static ssize_t pio_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr, char *buf, loff_t off,
+ size_t count)
+
+{
+ struct w1_slave *sl = kobj_to_w1_slave(kobj);
+ int ret;
+
+ /* check arguments */
+ if (off != 0 || count != 1 || buf == NULL)
+ return -EINVAL;
+
+ mutex_lock(&sl->master->mutex);
+ ret = w1_f1C_read(sl, W1_1C_REG_LOGIC_STATE, count, buf);
+ mutex_unlock(&sl->master->mutex);
+
+ return ret;
+}
+
+static ssize_t pio_write(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr, char *buf, loff_t off,
+ size_t count)
+
+{
+ struct w1_slave *sl = kobj_to_w1_slave(kobj);
+ u8 wrbuf[3];
+ u8 ack;
+
+ /* check arguments */
+ if (off != 0 || count != 1 || buf == NULL)
+ return -EINVAL;
+
+ mutex_lock(&sl->master->mutex);
+
+ /* Write the PIO data */
+ if (w1_reset_select_slave(sl)) {
+ mutex_unlock(&sl->master->mutex);
+ return -1;
+ }
+
+ /* set bit 7..2 to value '1' */
+ *buf = *buf | 0xFC;
+
+ wrbuf[0] = W1_F1C_ACCESS_WRITE;
+ wrbuf[1] = *buf;
+ wrbuf[2] = ~(*buf);
+ w1_write_block(sl->master, wrbuf, 3);
+
+ w1_read_block(sl->master, &ack, sizeof(ack));
+
+ mutex_unlock(&sl->master->mutex);
+
+ /* check for acknowledgement */
+ if (ack != 0xAA)
+ return -EIO;
+
+ return count;
+}
+
+static BIN_ATTR_RW(pio, 1);
+
+static ssize_t crccheck_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ return sysfs_emit(buf, "%d\n", w1_enable_crccheck);
+}
+
+static ssize_t crccheck_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int err = kstrtobool(buf, &w1_enable_crccheck);
+
+ if (err)
+ return err;
+
+ return count;
+}
+
+static DEVICE_ATTR_RW(crccheck);
+
+static struct attribute *w1_f1C_attrs[] = {
+ &dev_attr_crccheck.attr,
+ NULL,
+};
+
+static struct bin_attribute *w1_f1C_bin_attrs[] = {
+ &bin_attr_eeprom,
+ &bin_attr_pio,
+ NULL,
+};
+
+static const struct attribute_group w1_f1C_group = {
+ .attrs = w1_f1C_attrs,
+ .bin_attrs = w1_f1C_bin_attrs,
+};
+
+static const struct attribute_group *w1_f1C_groups[] = {
+ &w1_f1C_group,
+ NULL,
+};
+
+static int w1_f1C_add_slave(struct w1_slave *sl)
+{
+ struct w1_f1C_data *data = NULL;
+
+ if (w1_enable_crccheck) {
+ data = kzalloc(sizeof(struct w1_f1C_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+ sl->family_data = data;
+ }
+
+ return 0;
+}
+
+static void w1_f1C_remove_slave(struct w1_slave *sl)
+{
+ kfree(sl->family_data);
+ sl->family_data = NULL;
+}
+
+static const struct w1_family_ops w1_f1C_fops = {
+ .add_slave = w1_f1C_add_slave,
+ .remove_slave = w1_f1C_remove_slave,
+ .groups = w1_f1C_groups,
+};
+
+static struct w1_family w1_family_1C = {
+ .fid = W1_FAMILY_DS28E04,
+ .fops = &w1_f1C_fops,
+};
+module_w1_family(w1_family_1C);
+
+MODULE_AUTHOR("Markus Franke <franke.m@sebakmt.com>, <franm@hrz.tu-chemnitz.de>");
+MODULE_DESCRIPTION("w1 family 1C driver for DS28E04, 4kb EEPROM and PIO");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_DS28E04));
diff --git a/drivers/w1/slaves/w1_ds28e17.c b/drivers/w1/slaves/w1_ds28e17.c
new file mode 100644
index 000000000..aed10b72f
--- /dev/null
+++ b/drivers/w1/slaves/w1_ds28e17.c
@@ -0,0 +1,755 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * w1_ds28e17.c - w1 family 19 (DS28E17) driver
+ *
+ * Copyright (c) 2016 Jan Kandziora <jjj@gmx.de>
+ */
+
+#include <linux/crc16.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+
+#define CRC16_INIT 0
+
+#include <linux/w1.h>
+
+#define W1_FAMILY_DS28E17 0x19
+
+/* Module setup. */
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Jan Kandziora <jjj@gmx.de>");
+MODULE_DESCRIPTION("w1 family 19 driver for DS28E17, 1-wire to I2C master bridge");
+MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_DS28E17));
+
+
+/* Default I2C speed to be set when a DS28E17 is detected. */
+static int i2c_speed = 100;
+module_param_named(speed, i2c_speed, int, (S_IRUSR | S_IWUSR));
+MODULE_PARM_DESC(speed, "Default I2C speed to be set when a DS28E17 is detected");
+
+/* Default I2C stretch value to be set when a DS28E17 is detected. */
+static char i2c_stretch = 1;
+module_param_named(stretch, i2c_stretch, byte, (S_IRUSR | S_IWUSR));
+MODULE_PARM_DESC(stretch, "Default I2C stretch value to be set when a DS28E17 is detected");
+
+/* DS28E17 device command codes. */
+#define W1_F19_WRITE_DATA_WITH_STOP 0x4B
+#define W1_F19_WRITE_DATA_NO_STOP 0x5A
+#define W1_F19_WRITE_DATA_ONLY 0x69
+#define W1_F19_WRITE_DATA_ONLY_WITH_STOP 0x78
+#define W1_F19_READ_DATA_WITH_STOP 0x87
+#define W1_F19_WRITE_READ_DATA_WITH_STOP 0x2D
+#define W1_F19_WRITE_CONFIGURATION 0xD2
+#define W1_F19_READ_CONFIGURATION 0xE1
+#define W1_F19_ENABLE_SLEEP_MODE 0x1E
+#define W1_F19_READ_DEVICE_REVISION 0xC4
+
+/* DS28E17 status bits */
+#define W1_F19_STATUS_CRC 0x01
+#define W1_F19_STATUS_ADDRESS 0x02
+#define W1_F19_STATUS_START 0x08
+
+/*
+ * Maximum number of I2C bytes to transfer within one CRC16 protected onewire
+ * command.
+ * */
+#define W1_F19_WRITE_DATA_LIMIT 255
+
+/* Maximum number of I2C bytes to read with one onewire command. */
+#define W1_F19_READ_DATA_LIMIT 255
+
+/* Constants for calculating the busy sleep. */
+#define W1_F19_BUSY_TIMEBASES { 90, 23, 10 }
+#define W1_F19_BUSY_GRATUITY 1000
+
+/* Number of checks for the busy flag before timeout. */
+#define W1_F19_BUSY_CHECKS 1000
+
+
+/* Slave specific data. */
+struct w1_f19_data {
+ u8 speed;
+ u8 stretch;
+ struct i2c_adapter adapter;
+};
+
+
+/* Wait a while until the busy flag clears. */
+static int w1_f19_i2c_busy_wait(struct w1_slave *sl, size_t count)
+{
+ const unsigned long timebases[3] = W1_F19_BUSY_TIMEBASES;
+ struct w1_f19_data *data = sl->family_data;
+ unsigned int checks;
+
+ /* Check the busy flag first in any case.*/
+ if (w1_touch_bit(sl->master, 1) == 0)
+ return 0;
+
+ /*
+ * Do a generously long sleep in the beginning,
+ * as we have to wait at least this time for all
+ * the I2C bytes at the given speed to be transferred.
+ */
+ usleep_range(timebases[data->speed] * (data->stretch) * count,
+ timebases[data->speed] * (data->stretch) * count
+ + W1_F19_BUSY_GRATUITY);
+
+ /* Now continusly check the busy flag sent by the DS28E17. */
+ checks = W1_F19_BUSY_CHECKS;
+ while ((checks--) > 0) {
+ /* Return success if the busy flag is cleared. */
+ if (w1_touch_bit(sl->master, 1) == 0)
+ return 0;
+
+ /* Wait one non-streched byte timeslot. */
+ udelay(timebases[data->speed]);
+ }
+
+ /* Timeout. */
+ dev_warn(&sl->dev, "busy timeout\n");
+ return -ETIMEDOUT;
+}
+
+
+/* Utility function: result. */
+static size_t w1_f19_error(struct w1_slave *sl, u8 w1_buf[])
+{
+ /* Warnings. */
+ if (w1_buf[0] & W1_F19_STATUS_CRC)
+ dev_warn(&sl->dev, "crc16 mismatch\n");
+ if (w1_buf[0] & W1_F19_STATUS_ADDRESS)
+ dev_warn(&sl->dev, "i2c device not responding\n");
+ if ((w1_buf[0] & (W1_F19_STATUS_CRC | W1_F19_STATUS_ADDRESS)) == 0
+ && w1_buf[1] != 0) {
+ dev_warn(&sl->dev, "i2c short write, %d bytes not acknowledged\n",
+ w1_buf[1]);
+ }
+
+ /* Check error conditions. */
+ if (w1_buf[0] & W1_F19_STATUS_ADDRESS)
+ return -ENXIO;
+ if (w1_buf[0] & W1_F19_STATUS_START)
+ return -EAGAIN;
+ if (w1_buf[0] != 0 || w1_buf[1] != 0)
+ return -EIO;
+
+ /* All ok. */
+ return 0;
+}
+
+
+/* Utility function: write data to I2C slave, single chunk. */
+static int __w1_f19_i2c_write(struct w1_slave *sl,
+ const u8 *command, size_t command_count,
+ const u8 *buffer, size_t count)
+{
+ u16 crc;
+ int error;
+ u8 w1_buf[2];
+
+ /* Send command and I2C data to DS28E17. */
+ crc = crc16(CRC16_INIT, command, command_count);
+ w1_write_block(sl->master, command, command_count);
+
+ w1_buf[0] = count;
+ crc = crc16(crc, w1_buf, 1);
+ w1_write_8(sl->master, w1_buf[0]);
+
+ crc = crc16(crc, buffer, count);
+ w1_write_block(sl->master, buffer, count);
+
+ w1_buf[0] = ~(crc & 0xFF);
+ w1_buf[1] = ~((crc >> 8) & 0xFF);
+ w1_write_block(sl->master, w1_buf, 2);
+
+ /* Wait until busy flag clears (or timeout). */
+ if (w1_f19_i2c_busy_wait(sl, count + 1) < 0)
+ return -ETIMEDOUT;
+
+ /* Read status from DS28E17. */
+ w1_read_block(sl->master, w1_buf, 2);
+
+ /* Check error conditions. */
+ error = w1_f19_error(sl, w1_buf);
+ if (error < 0)
+ return error;
+
+ /* Return number of bytes written. */
+ return count;
+}
+
+
+/* Write data to I2C slave. */
+static int w1_f19_i2c_write(struct w1_slave *sl, u16 i2c_address,
+ const u8 *buffer, size_t count, bool stop)
+{
+ int result;
+ int remaining = count;
+ const u8 *p;
+ u8 command[2];
+
+ /* Check input. */
+ if (count == 0)
+ return -EOPNOTSUPP;
+
+ /* Check whether we need multiple commands. */
+ if (count <= W1_F19_WRITE_DATA_LIMIT) {
+ /*
+ * Small data amount. Data can be sent with
+ * a single onewire command.
+ */
+
+ /* Send all data to DS28E17. */
+ command[0] = (stop ? W1_F19_WRITE_DATA_WITH_STOP
+ : W1_F19_WRITE_DATA_NO_STOP);
+ command[1] = i2c_address << 1;
+ result = __w1_f19_i2c_write(sl, command, 2, buffer, count);
+ } else {
+ /* Large data amount. Data has to be sent in multiple chunks. */
+
+ /* Send first chunk to DS28E17. */
+ p = buffer;
+ command[0] = W1_F19_WRITE_DATA_NO_STOP;
+ command[1] = i2c_address << 1;
+ result = __w1_f19_i2c_write(sl, command, 2, p,
+ W1_F19_WRITE_DATA_LIMIT);
+ if (result < 0)
+ return result;
+
+ /* Resume to same DS28E17. */
+ if (w1_reset_resume_command(sl->master))
+ return -EIO;
+
+ /* Next data chunk. */
+ p += W1_F19_WRITE_DATA_LIMIT;
+ remaining -= W1_F19_WRITE_DATA_LIMIT;
+
+ while (remaining > W1_F19_WRITE_DATA_LIMIT) {
+ /* Send intermediate chunk to DS28E17. */
+ command[0] = W1_F19_WRITE_DATA_ONLY;
+ result = __w1_f19_i2c_write(sl, command, 1, p,
+ W1_F19_WRITE_DATA_LIMIT);
+ if (result < 0)
+ return result;
+
+ /* Resume to same DS28E17. */
+ if (w1_reset_resume_command(sl->master))
+ return -EIO;
+
+ /* Next data chunk. */
+ p += W1_F19_WRITE_DATA_LIMIT;
+ remaining -= W1_F19_WRITE_DATA_LIMIT;
+ }
+
+ /* Send final chunk to DS28E17. */
+ command[0] = (stop ? W1_F19_WRITE_DATA_ONLY_WITH_STOP
+ : W1_F19_WRITE_DATA_ONLY);
+ result = __w1_f19_i2c_write(sl, command, 1, p, remaining);
+ }
+
+ return result;
+}
+
+
+/* Read data from I2C slave. */
+static int w1_f19_i2c_read(struct w1_slave *sl, u16 i2c_address,
+ u8 *buffer, size_t count)
+{
+ u16 crc;
+ int error;
+ u8 w1_buf[5];
+
+ /* Check input. */
+ if (count == 0)
+ return -EOPNOTSUPP;
+
+ /* Send command to DS28E17. */
+ w1_buf[0] = W1_F19_READ_DATA_WITH_STOP;
+ w1_buf[1] = i2c_address << 1 | 0x01;
+ w1_buf[2] = count;
+ crc = crc16(CRC16_INIT, w1_buf, 3);
+ w1_buf[3] = ~(crc & 0xFF);
+ w1_buf[4] = ~((crc >> 8) & 0xFF);
+ w1_write_block(sl->master, w1_buf, 5);
+
+ /* Wait until busy flag clears (or timeout). */
+ if (w1_f19_i2c_busy_wait(sl, count + 1) < 0)
+ return -ETIMEDOUT;
+
+ /* Read status from DS28E17. */
+ w1_buf[0] = w1_read_8(sl->master);
+ w1_buf[1] = 0;
+
+ /* Check error conditions. */
+ error = w1_f19_error(sl, w1_buf);
+ if (error < 0)
+ return error;
+
+ /* Read received I2C data from DS28E17. */
+ return w1_read_block(sl->master, buffer, count);
+}
+
+
+/* Write to, then read data from I2C slave. */
+static int w1_f19_i2c_write_read(struct w1_slave *sl, u16 i2c_address,
+ const u8 *wbuffer, size_t wcount, u8 *rbuffer, size_t rcount)
+{
+ u16 crc;
+ int error;
+ u8 w1_buf[3];
+
+ /* Check input. */
+ if (wcount == 0 || rcount == 0)
+ return -EOPNOTSUPP;
+
+ /* Send command and I2C data to DS28E17. */
+ w1_buf[0] = W1_F19_WRITE_READ_DATA_WITH_STOP;
+ w1_buf[1] = i2c_address << 1;
+ w1_buf[2] = wcount;
+ crc = crc16(CRC16_INIT, w1_buf, 3);
+ w1_write_block(sl->master, w1_buf, 3);
+
+ crc = crc16(crc, wbuffer, wcount);
+ w1_write_block(sl->master, wbuffer, wcount);
+
+ w1_buf[0] = rcount;
+ crc = crc16(crc, w1_buf, 1);
+ w1_buf[1] = ~(crc & 0xFF);
+ w1_buf[2] = ~((crc >> 8) & 0xFF);
+ w1_write_block(sl->master, w1_buf, 3);
+
+ /* Wait until busy flag clears (or timeout). */
+ if (w1_f19_i2c_busy_wait(sl, wcount + rcount + 2) < 0)
+ return -ETIMEDOUT;
+
+ /* Read status from DS28E17. */
+ w1_read_block(sl->master, w1_buf, 2);
+
+ /* Check error conditions. */
+ error = w1_f19_error(sl, w1_buf);
+ if (error < 0)
+ return error;
+
+ /* Read received I2C data from DS28E17. */
+ return w1_read_block(sl->master, rbuffer, rcount);
+}
+
+
+/* Do an I2C master transfer. */
+static int w1_f19_i2c_master_transfer(struct i2c_adapter *adapter,
+ struct i2c_msg *msgs, int num)
+{
+ struct w1_slave *sl = (struct w1_slave *) adapter->algo_data;
+ int i = 0;
+ int result = 0;
+
+ /* Start onewire transaction. */
+ mutex_lock(&sl->master->bus_mutex);
+
+ /* Select DS28E17. */
+ if (w1_reset_select_slave(sl)) {
+ i = -EIO;
+ goto error;
+ }
+
+ /* Loop while there are still messages to transfer. */
+ while (i < num) {
+ /*
+ * Check for special case: Small write followed
+ * by read to same I2C device.
+ */
+ if (i < (num-1)
+ && msgs[i].addr == msgs[i+1].addr
+ && !(msgs[i].flags & I2C_M_RD)
+ && (msgs[i+1].flags & I2C_M_RD)
+ && (msgs[i].len <= W1_F19_WRITE_DATA_LIMIT)) {
+ /*
+ * The DS28E17 has a combined transfer
+ * for small write+read.
+ */
+ result = w1_f19_i2c_write_read(sl, msgs[i].addr,
+ msgs[i].buf, msgs[i].len,
+ msgs[i+1].buf, msgs[i+1].len);
+ if (result < 0) {
+ i = result;
+ goto error;
+ }
+
+ /*
+ * Check if we should interpret the read data
+ * as a length byte. The DS28E17 unfortunately
+ * has no read without stop, so we can just do
+ * another simple read in that case.
+ */
+ if (msgs[i+1].flags & I2C_M_RECV_LEN) {
+ result = w1_f19_i2c_read(sl, msgs[i+1].addr,
+ &(msgs[i+1].buf[1]), msgs[i+1].buf[0]);
+ if (result < 0) {
+ i = result;
+ goto error;
+ }
+ }
+
+ /* Eat up read message, too. */
+ i++;
+ } else if (msgs[i].flags & I2C_M_RD) {
+ /* Read transfer. */
+ result = w1_f19_i2c_read(sl, msgs[i].addr,
+ msgs[i].buf, msgs[i].len);
+ if (result < 0) {
+ i = result;
+ goto error;
+ }
+
+ /*
+ * Check if we should interpret the read data
+ * as a length byte. The DS28E17 unfortunately
+ * has no read without stop, so we can just do
+ * another simple read in that case.
+ */
+ if (msgs[i].flags & I2C_M_RECV_LEN) {
+ result = w1_f19_i2c_read(sl,
+ msgs[i].addr,
+ &(msgs[i].buf[1]),
+ msgs[i].buf[0]);
+ if (result < 0) {
+ i = result;
+ goto error;
+ }
+ }
+ } else {
+ /*
+ * Write transfer.
+ * Stop condition only for last
+ * transfer.
+ */
+ result = w1_f19_i2c_write(sl,
+ msgs[i].addr,
+ msgs[i].buf,
+ msgs[i].len,
+ i == (num-1));
+ if (result < 0) {
+ i = result;
+ goto error;
+ }
+ }
+
+ /* Next message. */
+ i++;
+
+ /* Are there still messages to send/receive? */
+ if (i < num) {
+ /* Yes. Resume to same DS28E17. */
+ if (w1_reset_resume_command(sl->master)) {
+ i = -EIO;
+ goto error;
+ }
+ }
+ }
+
+error:
+ /* End onewire transaction. */
+ mutex_unlock(&sl->master->bus_mutex);
+
+ /* Return number of messages processed or error. */
+ return i;
+}
+
+
+/* Get I2C adapter functionality. */
+static u32 w1_f19_i2c_functionality(struct i2c_adapter *adapter)
+{
+ /*
+ * Plain I2C functions only.
+ * SMBus is emulated by the kernel's I2C layer.
+ * No "I2C_FUNC_SMBUS_QUICK"
+ * No "I2C_FUNC_SMBUS_READ_BLOCK_DATA"
+ * No "I2C_FUNC_SMBUS_BLOCK_PROC_CALL"
+ */
+ return I2C_FUNC_I2C |
+ I2C_FUNC_SMBUS_BYTE |
+ I2C_FUNC_SMBUS_BYTE_DATA |
+ I2C_FUNC_SMBUS_WORD_DATA |
+ I2C_FUNC_SMBUS_PROC_CALL |
+ I2C_FUNC_SMBUS_WRITE_BLOCK_DATA |
+ I2C_FUNC_SMBUS_I2C_BLOCK |
+ I2C_FUNC_SMBUS_PEC;
+}
+
+
+/* I2C adapter quirks. */
+static const struct i2c_adapter_quirks w1_f19_i2c_adapter_quirks = {
+ .max_read_len = W1_F19_READ_DATA_LIMIT,
+};
+
+/* I2C algorithm. */
+static const struct i2c_algorithm w1_f19_i2c_algorithm = {
+ .master_xfer = w1_f19_i2c_master_transfer,
+ .functionality = w1_f19_i2c_functionality,
+};
+
+
+/* Read I2C speed from DS28E17. */
+static int w1_f19_get_i2c_speed(struct w1_slave *sl)
+{
+ struct w1_f19_data *data = sl->family_data;
+ int result = -EIO;
+
+ /* Start onewire transaction. */
+ mutex_lock(&sl->master->bus_mutex);
+
+ /* Select slave. */
+ if (w1_reset_select_slave(sl))
+ goto error;
+
+ /* Read slave configuration byte. */
+ w1_write_8(sl->master, W1_F19_READ_CONFIGURATION);
+ result = w1_read_8(sl->master);
+ if (result < 0 || result > 2) {
+ result = -EIO;
+ goto error;
+ }
+
+ /* Update speed in slave specific data. */
+ data->speed = result;
+
+error:
+ /* End onewire transaction. */
+ mutex_unlock(&sl->master->bus_mutex);
+
+ return result;
+}
+
+
+/* Set I2C speed on DS28E17. */
+static int __w1_f19_set_i2c_speed(struct w1_slave *sl, u8 speed)
+{
+ struct w1_f19_data *data = sl->family_data;
+ const int i2c_speeds[3] = { 100, 400, 900 };
+ u8 w1_buf[2];
+
+ /* Select slave. */
+ if (w1_reset_select_slave(sl))
+ return -EIO;
+
+ w1_buf[0] = W1_F19_WRITE_CONFIGURATION;
+ w1_buf[1] = speed;
+ w1_write_block(sl->master, w1_buf, 2);
+
+ /* Update speed in slave specific data. */
+ data->speed = speed;
+
+ dev_info(&sl->dev, "i2c speed set to %d kBaud\n", i2c_speeds[speed]);
+
+ return 0;
+}
+
+static int w1_f19_set_i2c_speed(struct w1_slave *sl, u8 speed)
+{
+ int result;
+
+ /* Start onewire transaction. */
+ mutex_lock(&sl->master->bus_mutex);
+
+ /* Set I2C speed on DS28E17. */
+ result = __w1_f19_set_i2c_speed(sl, speed);
+
+ /* End onewire transaction. */
+ mutex_unlock(&sl->master->bus_mutex);
+
+ return result;
+}
+
+
+/* Sysfs attributes. */
+
+/* I2C speed attribute for a single chip. */
+static ssize_t speed_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct w1_slave *sl = dev_to_w1_slave(dev);
+ int result;
+
+ /* Read current speed from slave. Updates data->speed. */
+ result = w1_f19_get_i2c_speed(sl);
+ if (result < 0)
+ return result;
+
+ /* Return current speed value. */
+ return sprintf(buf, "%d\n", result);
+}
+
+static ssize_t speed_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct w1_slave *sl = dev_to_w1_slave(dev);
+ int error;
+
+ /* Valid values are: "100", "400", "900" */
+ if (count < 3 || count > 4 || !buf)
+ return -EINVAL;
+ if (count == 4 && buf[3] != '\n')
+ return -EINVAL;
+ if (buf[1] != '0' || buf[2] != '0')
+ return -EINVAL;
+
+ /* Set speed on slave. */
+ switch (buf[0]) {
+ case '1':
+ error = w1_f19_set_i2c_speed(sl, 0);
+ break;
+ case '4':
+ error = w1_f19_set_i2c_speed(sl, 1);
+ break;
+ case '9':
+ error = w1_f19_set_i2c_speed(sl, 2);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (error < 0)
+ return error;
+
+ /* Return bytes written. */
+ return count;
+}
+
+static DEVICE_ATTR_RW(speed);
+
+
+/* Busy stretch attribute for a single chip. */
+static ssize_t stretch_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct w1_slave *sl = dev_to_w1_slave(dev);
+ struct w1_f19_data *data = sl->family_data;
+
+ /* Return current stretch value. */
+ return sprintf(buf, "%d\n", data->stretch);
+}
+
+static ssize_t stretch_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct w1_slave *sl = dev_to_w1_slave(dev);
+ struct w1_f19_data *data = sl->family_data;
+
+ /* Valid values are '1' to '9' */
+ if (count < 1 || count > 2 || !buf)
+ return -EINVAL;
+ if (count == 2 && buf[1] != '\n')
+ return -EINVAL;
+ if (buf[0] < '1' || buf[0] > '9')
+ return -EINVAL;
+
+ /* Set busy stretch value. */
+ data->stretch = buf[0] & 0x0F;
+
+ /* Return bytes written. */
+ return count;
+}
+
+static DEVICE_ATTR_RW(stretch);
+
+
+/* All attributes. */
+static struct attribute *w1_f19_attrs[] = {
+ &dev_attr_speed.attr,
+ &dev_attr_stretch.attr,
+ NULL,
+};
+
+static const struct attribute_group w1_f19_group = {
+ .attrs = w1_f19_attrs,
+};
+
+static const struct attribute_group *w1_f19_groups[] = {
+ &w1_f19_group,
+ NULL,
+};
+
+
+/* Slave add and remove functions. */
+static int w1_f19_add_slave(struct w1_slave *sl)
+{
+ struct w1_f19_data *data = NULL;
+
+ /* Allocate memory for slave specific data. */
+ data = devm_kzalloc(&sl->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+ sl->family_data = data;
+
+ /* Setup default I2C speed on slave. */
+ switch (i2c_speed) {
+ case 100:
+ __w1_f19_set_i2c_speed(sl, 0);
+ break;
+ case 400:
+ __w1_f19_set_i2c_speed(sl, 1);
+ break;
+ case 900:
+ __w1_f19_set_i2c_speed(sl, 2);
+ break;
+ default:
+ /*
+ * A i2c_speed module parameter of anything else
+ * than 100, 400, 900 means not to touch the
+ * speed of the DS28E17.
+ * We assume 400kBaud, the power-on value.
+ */
+ data->speed = 1;
+ }
+
+ /*
+ * Setup default busy stretch
+ * configuration for the DS28E17.
+ */
+ data->stretch = i2c_stretch;
+
+ /* Setup I2C adapter. */
+ data->adapter.owner = THIS_MODULE;
+ data->adapter.algo = &w1_f19_i2c_algorithm;
+ data->adapter.algo_data = sl;
+ strcpy(data->adapter.name, "w1-");
+ strcat(data->adapter.name, sl->name);
+ data->adapter.dev.parent = &sl->dev;
+ data->adapter.quirks = &w1_f19_i2c_adapter_quirks;
+
+ return i2c_add_adapter(&data->adapter);
+}
+
+static void w1_f19_remove_slave(struct w1_slave *sl)
+{
+ struct w1_f19_data *family_data = sl->family_data;
+
+ /* Delete I2C adapter. */
+ i2c_del_adapter(&family_data->adapter);
+
+ /* Free slave specific data. */
+ devm_kfree(&sl->dev, family_data);
+ sl->family_data = NULL;
+}
+
+
+/* Declarations within the w1 subsystem. */
+static const struct w1_family_ops w1_f19_fops = {
+ .add_slave = w1_f19_add_slave,
+ .remove_slave = w1_f19_remove_slave,
+ .groups = w1_f19_groups,
+};
+
+static struct w1_family w1_family_19 = {
+ .fid = W1_FAMILY_DS28E17,
+ .fops = &w1_f19_fops,
+};
+
+module_w1_family(w1_family_19);
diff --git a/drivers/w1/slaves/w1_smem.c b/drivers/w1/slaves/w1_smem.c
new file mode 100644
index 000000000..09afcf2a4
--- /dev/null
+++ b/drivers/w1/slaves/w1_smem.c
@@ -0,0 +1,59 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * w1_smem.c
+ *
+ * Copyright (c) 2004 Evgeniy Polyakov <zbr@ioremap.net>
+ */
+
+#include <asm/types.h>
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/types.h>
+
+#include <linux/w1.h>
+
+#define W1_FAMILY_SMEM_01 0x01
+#define W1_FAMILY_SMEM_81 0x81
+
+static struct w1_family w1_smem_family_01 = {
+ .fid = W1_FAMILY_SMEM_01,
+};
+
+static struct w1_family w1_smem_family_81 = {
+ .fid = W1_FAMILY_SMEM_81,
+};
+
+static int __init w1_smem_init(void)
+{
+ int err;
+
+ err = w1_register_family(&w1_smem_family_01);
+ if (err)
+ return err;
+
+ err = w1_register_family(&w1_smem_family_81);
+ if (err) {
+ w1_unregister_family(&w1_smem_family_01);
+ return err;
+ }
+
+ return 0;
+}
+
+static void __exit w1_smem_fini(void)
+{
+ w1_unregister_family(&w1_smem_family_01);
+ w1_unregister_family(&w1_smem_family_81);
+}
+
+module_init(w1_smem_init);
+module_exit(w1_smem_fini);
+
+MODULE_AUTHOR("Evgeniy Polyakov <zbr@ioremap.net>");
+MODULE_DESCRIPTION("Driver for 1-wire Dallas network protocol, 64bit memory family.");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_SMEM_01));
+MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_SMEM_81));
diff --git a/drivers/w1/slaves/w1_therm.c b/drivers/w1/slaves/w1_therm.c
new file mode 100644
index 000000000..99c58bd9d
--- /dev/null
+++ b/drivers/w1/slaves/w1_therm.c
@@ -0,0 +1,2224 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * w1_therm.c
+ *
+ * Copyright (c) 2004 Evgeniy Polyakov <zbr@ioremap.net>
+ */
+
+#include <asm/types.h>
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/sched.h>
+#include <linux/device.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/hwmon.h>
+#include <linux/string.h>
+#include <linux/jiffies.h>
+
+#include <linux/w1.h>
+
+#define W1_THERM_DS18S20 0x10
+#define W1_THERM_DS1822 0x22
+#define W1_THERM_DS18B20 0x28
+#define W1_THERM_DS1825 0x3B
+#define W1_THERM_DS28EA00 0x42
+
+/*
+ * Allow the strong pullup to be disabled, but default to enabled.
+ * If it was disabled a parasite powered device might not get the require
+ * current to do a temperature conversion. If it is enabled parasite powered
+ * devices have a better chance of getting the current required.
+ * In case the parasite power-detection is not working (seems to be the case
+ * for some DS18S20) the strong pullup can also be forced, regardless of the
+ * power state of the devices.
+ *
+ * Summary of options:
+ * - strong_pullup = 0 Disable strong pullup completely
+ * - strong_pullup = 1 Enable automatic strong pullup detection
+ * - strong_pullup = 2 Force strong pullup
+ */
+static int w1_strong_pullup = 1;
+module_param_named(strong_pullup, w1_strong_pullup, int, 0);
+
+/* Counter for devices supporting bulk reading */
+static u16 bulk_read_device_counter; /* =0 as per C standard */
+
+/* This command should be in public header w1.h but is not */
+#define W1_RECALL_EEPROM 0xB8
+
+/* Nb of try for an operation */
+#define W1_THERM_MAX_TRY 5
+
+/* ms delay to retry bus mutex */
+#define W1_THERM_RETRY_DELAY 20
+
+/* delay in ms to write in EEPROM */
+#define W1_THERM_EEPROM_WRITE_DELAY 10
+
+#define EEPROM_CMD_WRITE "save" /* cmd for write eeprom sysfs */
+#define EEPROM_CMD_READ "restore" /* cmd for read eeprom sysfs */
+#define BULK_TRIGGER_CMD "trigger" /* cmd to trigger a bulk read */
+
+#define MIN_TEMP -55 /* min temperature that can be measured */
+#define MAX_TEMP 125 /* max temperature that can be measured */
+
+/* Allowed values for sysfs conv_time attribute */
+#define CONV_TIME_DEFAULT 0
+#define CONV_TIME_MEASURE 1
+
+/* Bits in sysfs "features" value */
+#define W1_THERM_CHECK_RESULT 1 /* Enable conversion success check */
+#define W1_THERM_POLL_COMPLETION 2 /* Poll for conversion completion */
+#define W1_THERM_FEATURES_MASK 3 /* All values mask */
+
+/* Poll period in milliseconds. Should be less then a shortest operation on the device */
+#define W1_POLL_PERIOD 32
+#define W1_POLL_CONVERT_TEMP 2000 /* Timeout for W1_CONVERT_TEMP, ms */
+#define W1_POLL_RECALL_EEPROM 500 /* Timeout for W1_RECALL_EEPROM, ms*/
+
+/* Masks for resolution functions, work with all devices */
+/* Bit mask for config register for all devices, bits 7,6,5 */
+#define W1_THERM_RESOLUTION_MASK 0xE0
+/* Bit offset of resolution in config register for all devices */
+#define W1_THERM_RESOLUTION_SHIFT 5
+/* Bit offset of resolution in config register for all devices */
+#define W1_THERM_RESOLUTION_SHIFT 5
+/* Add this to bit value to get resolution */
+#define W1_THERM_RESOLUTION_MIN 9
+/* Maximum allowed value */
+#define W1_THERM_RESOLUTION_MAX 14
+
+/* Helpers Macros */
+
+/*
+ * return a pointer on the slave w1_therm_family_converter struct:
+ * always test family data existence before using this macro
+ */
+#define SLAVE_SPECIFIC_FUNC(sl) \
+ (((struct w1_therm_family_data *)(sl->family_data))->specific_functions)
+
+/*
+ * return the power mode of the sl slave : 1-ext, 0-parasite, <0 unknown
+ * always test family data existence before using this macro
+ */
+#define SLAVE_POWERMODE(sl) \
+ (((struct w1_therm_family_data *)(sl->family_data))->external_powered)
+
+/*
+ * return the resolution in bit of the sl slave : <0 unknown
+ * always test family data existence before using this macro
+ */
+#define SLAVE_RESOLUTION(sl) \
+ (((struct w1_therm_family_data *)(sl->family_data))->resolution)
+
+/*
+ * return the conv_time_override of the sl slave
+ * always test family data existence before using this macro
+ */
+ #define SLAVE_CONV_TIME_OVERRIDE(sl) \
+ (((struct w1_therm_family_data *)(sl->family_data))->conv_time_override)
+
+/*
+ * return the features of the sl slave
+ * always test family data existence before using this macro
+ */
+ #define SLAVE_FEATURES(sl) \
+ (((struct w1_therm_family_data *)(sl->family_data))->features)
+
+/*
+ * return whether or not a converT command has been issued to the slave
+ * * 0: no bulk read is pending
+ * * -1: conversion is in progress
+ * * 1: conversion done, result to be read
+ */
+#define SLAVE_CONVERT_TRIGGERED(sl) \
+ (((struct w1_therm_family_data *)(sl->family_data))->convert_triggered)
+
+/* return the address of the refcnt in the family data */
+#define THERM_REFCNT(family_data) \
+ (&((struct w1_therm_family_data *)family_data)->refcnt)
+
+/* Structs definition */
+
+/**
+ * struct w1_therm_family_converter - bind device specific functions
+ * @broken: flag for non-registred families
+ * @reserved: not used here
+ * @f: pointer to the device binding structure
+ * @convert: pointer to the device conversion function
+ * @get_conversion_time: pointer to the device conversion time function
+ * @set_resolution: pointer to the device set_resolution function
+ * @get_resolution: pointer to the device get_resolution function
+ * @write_data: pointer to the device writing function (2 or 3 bytes)
+ * @bulk_read: true if device family support bulk read, false otherwise
+ */
+struct w1_therm_family_converter {
+ u8 broken;
+ u16 reserved;
+ struct w1_family *f;
+ int (*convert)(u8 rom[9]);
+ int (*get_conversion_time)(struct w1_slave *sl);
+ int (*set_resolution)(struct w1_slave *sl, int val);
+ int (*get_resolution)(struct w1_slave *sl);
+ int (*write_data)(struct w1_slave *sl, const u8 *data);
+ bool bulk_read;
+};
+
+/**
+ * struct w1_therm_family_data - device data
+ * @rom: ROM device id (64bit Lasered ROM code + 1 CRC byte)
+ * @refcnt: ref count
+ * @external_powered: 1 device powered externally,
+ * 0 device parasite powered,
+ * -x error or undefined
+ * @resolution: current device resolution
+ * @convert_triggered: conversion state of the device
+ * @conv_time_override: user selected conversion time or CONV_TIME_DEFAULT
+ * @features: bit mask - enable temperature validity check, poll for completion
+ * @specific_functions: pointer to struct of device specific function
+ */
+struct w1_therm_family_data {
+ uint8_t rom[9];
+ atomic_t refcnt;
+ int external_powered;
+ int resolution;
+ int convert_triggered;
+ int conv_time_override;
+ unsigned int features;
+ struct w1_therm_family_converter *specific_functions;
+};
+
+/**
+ * struct therm_info - store temperature reading
+ * @rom: read device data (8 data bytes + 1 CRC byte)
+ * @crc: computed crc from rom
+ * @verdict: 1 crc checked, 0 crc not matching
+ */
+struct therm_info {
+ u8 rom[9];
+ u8 crc;
+ u8 verdict;
+};
+
+/* Hardware Functions declaration */
+
+/**
+ * reset_select_slave() - reset and select a slave
+ * @sl: the slave to select
+ *
+ * Resets the bus and select the slave by sending a ROM MATCH cmd
+ * w1_reset_select_slave() from w1_io.c could not be used here because
+ * it sent a SKIP ROM command if only one device is on the line.
+ * At the beginning of the such process, sl->master->slave_count is 1 even if
+ * more devices are on the line, causing collision on the line.
+ *
+ * Context: The w1 master lock must be held.
+ *
+ * Return: 0 if success, negative kernel error code otherwise.
+ */
+static int reset_select_slave(struct w1_slave *sl);
+
+/**
+ * convert_t() - Query the device for temperature conversion and read
+ * @sl: pointer to the slave to read
+ * @info: pointer to a structure to store the read results
+ *
+ * Return: 0 if success, -kernel error code otherwise
+ */
+static int convert_t(struct w1_slave *sl, struct therm_info *info);
+
+/**
+ * read_scratchpad() - read the data in device RAM
+ * @sl: pointer to the slave to read
+ * @info: pointer to a structure to store the read results
+ *
+ * Return: 0 if success, -kernel error code otherwise
+ */
+static int read_scratchpad(struct w1_slave *sl, struct therm_info *info);
+
+/**
+ * write_scratchpad() - write nb_bytes in the device RAM
+ * @sl: pointer to the slave to write in
+ * @data: pointer to an array of 3 bytes, as 3 bytes MUST be written
+ * @nb_bytes: number of bytes to be written (2 for DS18S20, 3 otherwise)
+ *
+ * Return: 0 if success, -kernel error code otherwise
+ */
+static int write_scratchpad(struct w1_slave *sl, const u8 *data, u8 nb_bytes);
+
+/**
+ * copy_scratchpad() - Copy the content of scratchpad in device EEPROM
+ * @sl: slave involved
+ *
+ * Return: 0 if success, -kernel error code otherwise
+ */
+static int copy_scratchpad(struct w1_slave *sl);
+
+/**
+ * recall_eeprom() - Restore EEPROM data to device RAM
+ * @sl: slave involved
+ *
+ * Return: 0 if success, -kernel error code otherwise
+ */
+static int recall_eeprom(struct w1_slave *sl);
+
+/**
+ * read_powermode() - Query the power mode of the slave
+ * @sl: slave to retrieve the power mode
+ *
+ * Ask the device to get its power mode (external or parasite)
+ * and store the power status in the &struct w1_therm_family_data.
+ *
+ * Return:
+ * * 0 parasite powered device
+ * * 1 externally powered device
+ * * <0 kernel error code
+ */
+static int read_powermode(struct w1_slave *sl);
+
+/**
+ * trigger_bulk_read() - function to trigger a bulk read on the bus
+ * @dev_master: the device master of the bus
+ *
+ * Send a SKIP ROM follow by a CONVERT T commmand on the bus.
+ * It also set the status flag in each slave &struct w1_therm_family_data
+ * to signal that a conversion is in progress.
+ *
+ * Return: 0 if success, -kernel error code otherwise
+ */
+static int trigger_bulk_read(struct w1_master *dev_master);
+
+/* Sysfs interface declaration */
+
+static ssize_t w1_slave_show(struct device *device,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t w1_slave_store(struct device *device,
+ struct device_attribute *attr, const char *buf, size_t size);
+
+static ssize_t w1_seq_show(struct device *device,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t temperature_show(struct device *device,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t ext_power_show(struct device *device,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t resolution_show(struct device *device,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t resolution_store(struct device *device,
+ struct device_attribute *attr, const char *buf, size_t size);
+
+static ssize_t eeprom_cmd_store(struct device *device,
+ struct device_attribute *attr, const char *buf, size_t size);
+
+static ssize_t alarms_store(struct device *device,
+ struct device_attribute *attr, const char *buf, size_t size);
+
+static ssize_t alarms_show(struct device *device,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t therm_bulk_read_store(struct device *device,
+ struct device_attribute *attr, const char *buf, size_t size);
+
+static ssize_t therm_bulk_read_show(struct device *device,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t conv_time_show(struct device *device,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t conv_time_store(struct device *device,
+ struct device_attribute *attr, const char *buf,
+ size_t size);
+
+static ssize_t features_show(struct device *device,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t features_store(struct device *device,
+ struct device_attribute *attr, const char *buf,
+ size_t size);
+/* Attributes declarations */
+
+static DEVICE_ATTR_RW(w1_slave);
+static DEVICE_ATTR_RO(w1_seq);
+static DEVICE_ATTR_RO(temperature);
+static DEVICE_ATTR_RO(ext_power);
+static DEVICE_ATTR_RW(resolution);
+static DEVICE_ATTR_WO(eeprom_cmd);
+static DEVICE_ATTR_RW(alarms);
+static DEVICE_ATTR_RW(conv_time);
+static DEVICE_ATTR_RW(features);
+
+static DEVICE_ATTR_RW(therm_bulk_read); /* attribut at master level */
+
+/* Interface Functions declaration */
+
+/**
+ * w1_therm_add_slave() - Called when a new slave is discovered
+ * @sl: slave just discovered by the master.
+ *
+ * Called by the master when the slave is discovered on the bus. Used to
+ * initialize slave state before the beginning of any communication.
+ *
+ * Return: 0 - If success, negative kernel code otherwise
+ */
+static int w1_therm_add_slave(struct w1_slave *sl);
+
+/**
+ * w1_therm_remove_slave() - Called when a slave is removed
+ * @sl: slave to be removed.
+ *
+ * Called by the master when the slave is considered not to be on the bus
+ * anymore. Used to free memory.
+ */
+static void w1_therm_remove_slave(struct w1_slave *sl);
+
+/* Family attributes */
+
+static struct attribute *w1_therm_attrs[] = {
+ &dev_attr_w1_slave.attr,
+ &dev_attr_temperature.attr,
+ &dev_attr_ext_power.attr,
+ &dev_attr_resolution.attr,
+ &dev_attr_eeprom_cmd.attr,
+ &dev_attr_alarms.attr,
+ &dev_attr_conv_time.attr,
+ &dev_attr_features.attr,
+ NULL,
+};
+
+static struct attribute *w1_ds18s20_attrs[] = {
+ &dev_attr_w1_slave.attr,
+ &dev_attr_temperature.attr,
+ &dev_attr_ext_power.attr,
+ &dev_attr_eeprom_cmd.attr,
+ &dev_attr_alarms.attr,
+ &dev_attr_conv_time.attr,
+ &dev_attr_features.attr,
+ NULL,
+};
+
+static struct attribute *w1_ds28ea00_attrs[] = {
+ &dev_attr_w1_slave.attr,
+ &dev_attr_w1_seq.attr,
+ &dev_attr_temperature.attr,
+ &dev_attr_ext_power.attr,
+ &dev_attr_resolution.attr,
+ &dev_attr_eeprom_cmd.attr,
+ &dev_attr_alarms.attr,
+ &dev_attr_conv_time.attr,
+ &dev_attr_features.attr,
+ NULL,
+};
+
+/* Attribute groups */
+
+ATTRIBUTE_GROUPS(w1_therm);
+ATTRIBUTE_GROUPS(w1_ds18s20);
+ATTRIBUTE_GROUPS(w1_ds28ea00);
+
+#if IS_REACHABLE(CONFIG_HWMON)
+static int w1_read_temp(struct device *dev, u32 attr, int channel,
+ long *val);
+
+static umode_t w1_is_visible(const void *_data, enum hwmon_sensor_types type,
+ u32 attr, int channel)
+{
+ return attr == hwmon_temp_input ? 0444 : 0;
+}
+
+static int w1_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
+{
+ switch (type) {
+ case hwmon_temp:
+ return w1_read_temp(dev, attr, channel, val);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static const u32 w1_temp_config[] = {
+ HWMON_T_INPUT,
+ 0
+};
+
+static const struct hwmon_channel_info w1_temp = {
+ .type = hwmon_temp,
+ .config = w1_temp_config,
+};
+
+static const struct hwmon_channel_info *w1_info[] = {
+ &w1_temp,
+ NULL
+};
+
+static const struct hwmon_ops w1_hwmon_ops = {
+ .is_visible = w1_is_visible,
+ .read = w1_read,
+};
+
+static const struct hwmon_chip_info w1_chip_info = {
+ .ops = &w1_hwmon_ops,
+ .info = w1_info,
+};
+#define W1_CHIPINFO (&w1_chip_info)
+#else
+#define W1_CHIPINFO NULL
+#endif
+
+/* Family operations */
+
+static const struct w1_family_ops w1_therm_fops = {
+ .add_slave = w1_therm_add_slave,
+ .remove_slave = w1_therm_remove_slave,
+ .groups = w1_therm_groups,
+ .chip_info = W1_CHIPINFO,
+};
+
+static const struct w1_family_ops w1_ds18s20_fops = {
+ .add_slave = w1_therm_add_slave,
+ .remove_slave = w1_therm_remove_slave,
+ .groups = w1_ds18s20_groups,
+ .chip_info = W1_CHIPINFO,
+};
+
+static const struct w1_family_ops w1_ds28ea00_fops = {
+ .add_slave = w1_therm_add_slave,
+ .remove_slave = w1_therm_remove_slave,
+ .groups = w1_ds28ea00_groups,
+ .chip_info = W1_CHIPINFO,
+};
+
+/* Family binding operations struct */
+
+static struct w1_family w1_therm_family_DS18S20 = {
+ .fid = W1_THERM_DS18S20,
+ .fops = &w1_ds18s20_fops,
+};
+
+static struct w1_family w1_therm_family_DS18B20 = {
+ .fid = W1_THERM_DS18B20,
+ .fops = &w1_therm_fops,
+};
+
+static struct w1_family w1_therm_family_DS1822 = {
+ .fid = W1_THERM_DS1822,
+ .fops = &w1_therm_fops,
+};
+
+static struct w1_family w1_therm_family_DS28EA00 = {
+ .fid = W1_THERM_DS28EA00,
+ .fops = &w1_ds28ea00_fops,
+};
+
+static struct w1_family w1_therm_family_DS1825 = {
+ .fid = W1_THERM_DS1825,
+ .fops = &w1_therm_fops,
+};
+
+/* Device dependent func */
+
+static inline int w1_DS18B20_convert_time(struct w1_slave *sl)
+{
+ int ret;
+
+ if (!sl->family_data)
+ return -ENODEV; /* device unknown */
+
+ if (SLAVE_CONV_TIME_OVERRIDE(sl) != CONV_TIME_DEFAULT)
+ return SLAVE_CONV_TIME_OVERRIDE(sl);
+
+ /* Return the conversion time, depending on resolution,
+ * select maximum conversion time among all compatible devices
+ */
+ switch (SLAVE_RESOLUTION(sl)) {
+ case 9:
+ ret = 95;
+ break;
+ case 10:
+ ret = 190;
+ break;
+ case 11:
+ ret = 375;
+ break;
+ case 12:
+ ret = 750;
+ break;
+ case 13:
+ ret = 850; /* GX20MH01 only. Datasheet says 500ms, but that's not enough. */
+ break;
+ case 14:
+ ret = 1600; /* GX20MH01 only. Datasheet says 1000ms - not enough */
+ break;
+ default:
+ ret = 750;
+ }
+ return ret;
+}
+
+static inline int w1_DS18S20_convert_time(struct w1_slave *sl)
+{
+ if (!sl->family_data)
+ return -ENODEV; /* device unknown */
+
+ if (SLAVE_CONV_TIME_OVERRIDE(sl) == CONV_TIME_DEFAULT)
+ return 750; /* default for DS18S20 */
+ else
+ return SLAVE_CONV_TIME_OVERRIDE(sl);
+}
+
+static inline int w1_DS1825_convert_time(struct w1_slave *sl)
+{
+ int ret;
+
+ if (!sl->family_data)
+ return -ENODEV; /* device unknown */
+
+ if (SLAVE_CONV_TIME_OVERRIDE(sl) != CONV_TIME_DEFAULT)
+ return SLAVE_CONV_TIME_OVERRIDE(sl);
+
+ /* Return the conversion time, depending on resolution,
+ * select maximum conversion time among all compatible devices
+ */
+ switch (SLAVE_RESOLUTION(sl)) {
+ case 9:
+ ret = 95;
+ break;
+ case 10:
+ ret = 190;
+ break;
+ case 11:
+ ret = 375;
+ break;
+ case 12:
+ ret = 750;
+ break;
+ case 14:
+ ret = 100; /* MAX31850 only. Datasheet says 100ms */
+ break;
+ default:
+ ret = 750;
+ }
+ return ret;
+}
+
+static inline int w1_DS18B20_write_data(struct w1_slave *sl,
+ const u8 *data)
+{
+ return write_scratchpad(sl, data, 3);
+}
+
+static inline int w1_DS18S20_write_data(struct w1_slave *sl,
+ const u8 *data)
+{
+ /* No config register */
+ return write_scratchpad(sl, data, 2);
+}
+
+static inline int w1_DS18B20_set_resolution(struct w1_slave *sl, int val)
+{
+ int ret;
+ struct therm_info info, info2;
+
+ /* DS18B20 resolution is 9 to 12 bits */
+ /* GX20MH01 resolution is 9 to 14 bits */
+ /* MAX31850 resolution is fixed 14 bits */
+ if (val < W1_THERM_RESOLUTION_MIN || val > W1_THERM_RESOLUTION_MAX)
+ return -EINVAL;
+
+ /* Calc bit value from resolution */
+ val = (val - W1_THERM_RESOLUTION_MIN) << W1_THERM_RESOLUTION_SHIFT;
+
+ /*
+ * Read the scratchpad to change only the required bits
+ * (bit5 & bit 6 from byte 4)
+ */
+ ret = read_scratchpad(sl, &info);
+
+ if (ret)
+ return ret;
+
+
+ info.rom[4] &= ~W1_THERM_RESOLUTION_MASK;
+ info.rom[4] |= val;
+
+ /* Write data in the device RAM */
+ ret = w1_DS18B20_write_data(sl, info.rom + 2);
+ if (ret)
+ return ret;
+
+ /* Have to read back the resolution to verify an actual value
+ * GX20MH01 and DS18B20 are indistinguishable by family number, but resolutions differ
+ * Some DS18B20 clones don't support resolution change
+ */
+ ret = read_scratchpad(sl, &info2);
+ if (ret)
+ /* Scratchpad read fail */
+ return ret;
+
+ if ((info2.rom[4] & W1_THERM_RESOLUTION_MASK) == (info.rom[4] & W1_THERM_RESOLUTION_MASK))
+ return 0;
+
+ /* Resolution verify error */
+ return -EIO;
+}
+
+static inline int w1_DS18B20_get_resolution(struct w1_slave *sl)
+{
+ int ret;
+ int resolution;
+ struct therm_info info;
+
+ ret = read_scratchpad(sl, &info);
+
+ if (ret)
+ return ret;
+
+ resolution = ((info.rom[4] & W1_THERM_RESOLUTION_MASK) >> W1_THERM_RESOLUTION_SHIFT)
+ + W1_THERM_RESOLUTION_MIN;
+ /* GX20MH01 has one special case:
+ * >=14 means 14 bits when getting resolution from bit value.
+ * MAX31850 delivers fixed 15 and has 14 bits.
+ * Other devices have no more then 12 bits.
+ */
+ if (resolution > W1_THERM_RESOLUTION_MAX)
+ resolution = W1_THERM_RESOLUTION_MAX;
+
+ return resolution;
+}
+
+/**
+ * w1_DS18B20_convert_temp() - temperature computation for DS18B20
+ * @rom: data read from device RAM (8 data bytes + 1 CRC byte)
+ *
+ * Can be called for any DS18B20 compliant device.
+ *
+ * Return: value in millidegrees Celsius.
+ */
+static inline int w1_DS18B20_convert_temp(u8 rom[9])
+{
+ u16 bv;
+ s16 t;
+
+ /* Signed 16-bit value to unsigned, cpu order */
+ bv = le16_to_cpup((__le16 *)rom);
+
+ /* Config register bit R2 = 1 - GX20MH01 in 13 or 14 bit resolution mode */
+ if (rom[4] & 0x80) {
+ /* Insert two temperature bits from config register */
+ /* Avoid arithmetic shift of signed value */
+ bv = (bv << 2) | (rom[4] & 3);
+ t = (s16) bv; /* Degrees, lowest bit is 2^-6 */
+ return (int)t * 1000 / 64; /* Sign-extend to int; millidegrees */
+ }
+ t = (s16)bv; /* Degrees, lowest bit is 2^-4 */
+ return (int)t * 1000 / 16; /* Sign-extend to int; millidegrees */
+}
+
+/**
+ * w1_DS18S20_convert_temp() - temperature computation for DS18S20
+ * @rom: data read from device RAM (8 data bytes + 1 CRC byte)
+ *
+ * Can be called for any DS18S20 compliant device.
+ *
+ * Return: value in millidegrees Celsius.
+ */
+static inline int w1_DS18S20_convert_temp(u8 rom[9])
+{
+ int t, h;
+
+ if (!rom[7]) {
+ pr_debug("%s: Invalid argument for conversion\n", __func__);
+ return 0;
+ }
+
+ if (rom[1] == 0)
+ t = ((s32)rom[0] >> 1)*1000;
+ else
+ t = 1000*(-1*(s32)(0x100-rom[0]) >> 1);
+
+ t -= 250;
+ h = 1000*((s32)rom[7] - (s32)rom[6]);
+ h /= (s32)rom[7];
+ t += h;
+
+ return t;
+}
+
+/**
+ * w1_DS1825_convert_temp() - temperature computation for DS1825
+ * @rom: data read from device RAM (8 data bytes + 1 CRC byte)
+ *
+ * Can be called for any DS1825 compliant device.
+ * Is used by MAX31850, too
+ *
+ * Return: value in millidegrees Celsius.
+ */
+
+static inline int w1_DS1825_convert_temp(u8 rom[9])
+{
+ u16 bv;
+ s16 t;
+
+ /* Signed 16-bit value to unsigned, cpu order */
+ bv = le16_to_cpup((__le16 *)rom);
+
+ /* Config register bit 7 = 1 - MA31850 found, 14 bit resolution */
+ if (rom[4] & 0x80) {
+ /* Mask out bits 0 (Fault) and 1 (Reserved) */
+ /* Avoid arithmetic shift of signed value */
+ bv = (bv & 0xFFFC); /* Degrees, lowest 4 bits are 2^-1, 2^-2 and 2 zero bits */
+ }
+ t = (s16)bv; /* Degrees, lowest bit is 2^-4 */
+ return (int)t * 1000 / 16; /* Sign-extend to int; millidegrees */
+}
+
+/* Device capability description */
+/* GX20MH01 device shares family number and structure with DS18B20 */
+
+static struct w1_therm_family_converter w1_therm_families[] = {
+ {
+ .f = &w1_therm_family_DS18S20,
+ .convert = w1_DS18S20_convert_temp,
+ .get_conversion_time = w1_DS18S20_convert_time,
+ .set_resolution = NULL, /* no config register */
+ .get_resolution = NULL, /* no config register */
+ .write_data = w1_DS18S20_write_data,
+ .bulk_read = true
+ },
+ {
+ .f = &w1_therm_family_DS1822,
+ .convert = w1_DS18B20_convert_temp,
+ .get_conversion_time = w1_DS18B20_convert_time,
+ .set_resolution = w1_DS18B20_set_resolution,
+ .get_resolution = w1_DS18B20_get_resolution,
+ .write_data = w1_DS18B20_write_data,
+ .bulk_read = true
+ },
+ {
+ /* Also used for GX20MH01 */
+ .f = &w1_therm_family_DS18B20,
+ .convert = w1_DS18B20_convert_temp,
+ .get_conversion_time = w1_DS18B20_convert_time,
+ .set_resolution = w1_DS18B20_set_resolution,
+ .get_resolution = w1_DS18B20_get_resolution,
+ .write_data = w1_DS18B20_write_data,
+ .bulk_read = true
+ },
+ {
+ .f = &w1_therm_family_DS28EA00,
+ .convert = w1_DS18B20_convert_temp,
+ .get_conversion_time = w1_DS18B20_convert_time,
+ .set_resolution = w1_DS18B20_set_resolution,
+ .get_resolution = w1_DS18B20_get_resolution,
+ .write_data = w1_DS18B20_write_data,
+ .bulk_read = false
+ },
+ {
+ /* Also used for MAX31850 */
+ .f = &w1_therm_family_DS1825,
+ .convert = w1_DS1825_convert_temp,
+ .get_conversion_time = w1_DS1825_convert_time,
+ .set_resolution = w1_DS18B20_set_resolution,
+ .get_resolution = w1_DS18B20_get_resolution,
+ .write_data = w1_DS18B20_write_data,
+ .bulk_read = true
+ }
+};
+
+/* Helpers Functions */
+
+/**
+ * device_family() - Retrieve a pointer on &struct w1_therm_family_converter
+ * @sl: slave to retrieve the device specific structure
+ *
+ * Return: pointer to the slaves's family converter, NULL if not known
+ */
+static struct w1_therm_family_converter *device_family(struct w1_slave *sl)
+{
+ struct w1_therm_family_converter *ret = NULL;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(w1_therm_families); ++i) {
+ if (w1_therm_families[i].f->fid == sl->family->fid) {
+ ret = &w1_therm_families[i];
+ break;
+ }
+ }
+ return ret;
+}
+
+/**
+ * bus_mutex_lock() - Acquire the mutex
+ * @lock: w1 bus mutex to acquire
+ *
+ * It try to acquire the mutex W1_THERM_MAX_TRY times and wait
+ * W1_THERM_RETRY_DELAY between 2 attempts.
+ *
+ * Return: true is mutex is acquired and lock, false otherwise
+ */
+static inline bool bus_mutex_lock(struct mutex *lock)
+{
+ int max_trying = W1_THERM_MAX_TRY;
+
+ /* try to acquire the mutex, if not, sleep retry_delay before retry) */
+ while (mutex_lock_interruptible(lock) != 0 && max_trying > 0) {
+ unsigned long sleep_rem;
+
+ sleep_rem = msleep_interruptible(W1_THERM_RETRY_DELAY);
+ if (!sleep_rem)
+ max_trying--;
+ }
+
+ if (!max_trying)
+ return false; /* Didn't acquire the bus mutex */
+
+ return true;
+}
+
+/**
+ * check_family_data() - Check if family data and specific functions are present
+ * @sl: W1 device data
+ *
+ * Return: 0 - OK, negative value - error
+ */
+static int check_family_data(struct w1_slave *sl)
+{
+ if ((!sl->family_data) || (!SLAVE_SPECIFIC_FUNC(sl))) {
+ dev_info(&sl->dev,
+ "%s: Device is not supported by the driver\n", __func__);
+ return -EINVAL; /* No device family */
+ }
+ return 0;
+}
+
+/**
+ * bulk_read_support() - check if slave support bulk read
+ * @sl: device to check the ability
+ *
+ * Return: true if bulk read is supported, false if not or error
+ */
+static inline bool bulk_read_support(struct w1_slave *sl)
+{
+ if (SLAVE_SPECIFIC_FUNC(sl))
+ return SLAVE_SPECIFIC_FUNC(sl)->bulk_read;
+
+ dev_info(&sl->dev,
+ "%s: Device not supported by the driver\n", __func__);
+
+ return false; /* No device family */
+}
+
+/**
+ * conversion_time() - get the Tconv for the slave
+ * @sl: device to get the conversion time
+ *
+ * On device supporting resolution settings, conversion time depend
+ * on the resolution setting. This helper function get the slave timing,
+ * depending on its current setting.
+ *
+ * Return: conversion time in ms, negative values are kernel error code
+ */
+static inline int conversion_time(struct w1_slave *sl)
+{
+ if (SLAVE_SPECIFIC_FUNC(sl))
+ return SLAVE_SPECIFIC_FUNC(sl)->get_conversion_time(sl);
+
+ dev_info(&sl->dev,
+ "%s: Device not supported by the driver\n", __func__);
+
+ return -ENODEV; /* No device family */
+}
+
+/**
+ * temperature_from_RAM() - Convert the read info to temperature
+ * @sl: device that sent the RAM data
+ * @rom: read value on the slave device RAM
+ *
+ * Device dependent, the function bind the correct computation method.
+ *
+ * Return: temperature in 1/1000degC, 0 on error.
+ */
+static inline int temperature_from_RAM(struct w1_slave *sl, u8 rom[9])
+{
+ if (SLAVE_SPECIFIC_FUNC(sl))
+ return SLAVE_SPECIFIC_FUNC(sl)->convert(rom);
+
+ dev_info(&sl->dev,
+ "%s: Device not supported by the driver\n", __func__);
+
+ return 0; /* No device family */
+}
+
+/**
+ * int_to_short() - Safe casting of int to short
+ *
+ * @i: integer to be converted to short
+ *
+ * Device register use 1 byte to store signed integer.
+ * This helper function convert the int in a signed short,
+ * using the min/max values that device can measure as limits.
+ * min/max values are defined by macro.
+ *
+ * Return: a short in the range of min/max value
+ */
+static inline s8 int_to_short(int i)
+{
+ /* Prepare to cast to short by eliminating out of range values */
+ i = clamp(i, MIN_TEMP, MAX_TEMP);
+ return (s8) i;
+}
+
+/* Interface Functions */
+
+static int w1_therm_add_slave(struct w1_slave *sl)
+{
+ struct w1_therm_family_converter *sl_family_conv;
+
+ /* Allocate memory */
+ sl->family_data = kzalloc(sizeof(struct w1_therm_family_data),
+ GFP_KERNEL);
+ if (!sl->family_data)
+ return -ENOMEM;
+
+ atomic_set(THERM_REFCNT(sl->family_data), 1);
+
+ /* Get a pointer to the device specific function struct */
+ sl_family_conv = device_family(sl);
+ if (!sl_family_conv) {
+ kfree(sl->family_data);
+ return -ENODEV;
+ }
+ /* save this pointer to the device structure */
+ SLAVE_SPECIFIC_FUNC(sl) = sl_family_conv;
+
+ if (bulk_read_support(sl)) {
+ /*
+ * add the sys entry to trigger bulk_read
+ * at master level only the 1st time
+ */
+ if (!bulk_read_device_counter) {
+ int err = device_create_file(&sl->master->dev,
+ &dev_attr_therm_bulk_read);
+
+ if (err)
+ dev_warn(&sl->dev,
+ "%s: Device has been added, but bulk read is unavailable. err=%d\n",
+ __func__, err);
+ }
+ /* Increment the counter */
+ bulk_read_device_counter++;
+ }
+
+ /* Getting the power mode of the device {external, parasite} */
+ SLAVE_POWERMODE(sl) = read_powermode(sl);
+
+ if (SLAVE_POWERMODE(sl) < 0) {
+ /* no error returned as device has been added */
+ dev_warn(&sl->dev,
+ "%s: Device has been added, but power_mode may be corrupted. err=%d\n",
+ __func__, SLAVE_POWERMODE(sl));
+ }
+
+ /* Getting the resolution of the device */
+ if (SLAVE_SPECIFIC_FUNC(sl)->get_resolution) {
+ SLAVE_RESOLUTION(sl) =
+ SLAVE_SPECIFIC_FUNC(sl)->get_resolution(sl);
+ if (SLAVE_RESOLUTION(sl) < 0) {
+ /* no error returned as device has been added */
+ dev_warn(&sl->dev,
+ "%s:Device has been added, but resolution may be corrupted. err=%d\n",
+ __func__, SLAVE_RESOLUTION(sl));
+ }
+ }
+
+ /* Finally initialize convert_triggered flag */
+ SLAVE_CONVERT_TRIGGERED(sl) = 0;
+
+ return 0;
+}
+
+static void w1_therm_remove_slave(struct w1_slave *sl)
+{
+ int refcnt = atomic_sub_return(1, THERM_REFCNT(sl->family_data));
+
+ if (bulk_read_support(sl)) {
+ bulk_read_device_counter--;
+ /* Delete the entry if no more device support the feature */
+ if (!bulk_read_device_counter)
+ device_remove_file(&sl->master->dev,
+ &dev_attr_therm_bulk_read);
+ }
+
+ while (refcnt) {
+ msleep(1000);
+ refcnt = atomic_read(THERM_REFCNT(sl->family_data));
+ }
+ kfree(sl->family_data);
+ sl->family_data = NULL;
+}
+
+/* Hardware Functions */
+
+/* Safe version of reset_select_slave - avoid using the one in w_io.c */
+static int reset_select_slave(struct w1_slave *sl)
+{
+ u8 match[9] = { W1_MATCH_ROM, };
+ u64 rn = le64_to_cpu(*((u64 *)&sl->reg_num));
+
+ if (w1_reset_bus(sl->master))
+ return -ENODEV;
+
+ memcpy(&match[1], &rn, 8);
+ w1_write_block(sl->master, match, 9);
+
+ return 0;
+}
+
+/**
+ * w1_poll_completion - Poll for operation completion, with timeout
+ * @dev_master: the device master of the bus
+ * @tout_ms: timeout in milliseconds
+ *
+ * The device is answering 0's while an operation is in progress and 1's after it completes
+ * Timeout may happen if the previous command was not recognised due to a line noise
+ *
+ * Return: 0 - OK, negative error - timeout
+ */
+static int w1_poll_completion(struct w1_master *dev_master, int tout_ms)
+{
+ int i;
+
+ for (i = 0; i < tout_ms/W1_POLL_PERIOD; i++) {
+ /* Delay is before poll, for device to recognize a command */
+ msleep(W1_POLL_PERIOD);
+
+ /* Compare all 8 bits to mitigate a noise on the bus */
+ if (w1_read_8(dev_master) == 0xFF)
+ break;
+ }
+ if (i == tout_ms/W1_POLL_PERIOD)
+ return -EIO;
+
+ return 0;
+}
+
+static int convert_t(struct w1_slave *sl, struct therm_info *info)
+{
+ struct w1_master *dev_master = sl->master;
+ int max_trying = W1_THERM_MAX_TRY;
+ int t_conv;
+ int ret = -ENODEV;
+ bool strong_pullup;
+
+ if (!sl->family_data)
+ goto error;
+
+ strong_pullup = (w1_strong_pullup == 2 ||
+ (!SLAVE_POWERMODE(sl) &&
+ w1_strong_pullup));
+
+ if (strong_pullup && SLAVE_FEATURES(sl) & W1_THERM_POLL_COMPLETION) {
+ dev_warn(&sl->dev,
+ "%s: Disabling W1_THERM_POLL_COMPLETION in parasite power mode.\n",
+ __func__);
+ SLAVE_FEATURES(sl) &= ~W1_THERM_POLL_COMPLETION;
+ }
+
+ /* get conversion duration device and id dependent */
+ t_conv = conversion_time(sl);
+
+ memset(info->rom, 0, sizeof(info->rom));
+
+ /* prevent the slave from going away in sleep */
+ atomic_inc(THERM_REFCNT(sl->family_data));
+
+ if (!bus_mutex_lock(&dev_master->bus_mutex)) {
+ ret = -EAGAIN; /* Didn't acquire the mutex */
+ goto dec_refcnt;
+ }
+
+ while (max_trying-- && ret) { /* ret should be 0 */
+
+ info->verdict = 0;
+ info->crc = 0;
+ /* safe version to select slave */
+ if (!reset_select_slave(sl)) {
+ unsigned long sleep_rem;
+
+ /* 750ms strong pullup (or delay) after the convert */
+ if (strong_pullup)
+ w1_next_pullup(dev_master, t_conv);
+
+ w1_write_8(dev_master, W1_CONVERT_TEMP);
+
+ if (SLAVE_FEATURES(sl) & W1_THERM_POLL_COMPLETION) {
+ ret = w1_poll_completion(dev_master, W1_POLL_CONVERT_TEMP);
+ if (ret) {
+ dev_dbg(&sl->dev, "%s: Timeout\n", __func__);
+ goto mt_unlock;
+ }
+ mutex_unlock(&dev_master->bus_mutex);
+ } else if (!strong_pullup) { /*no device need pullup */
+ sleep_rem = msleep_interruptible(t_conv);
+ if (sleep_rem != 0) {
+ ret = -EINTR;
+ goto mt_unlock;
+ }
+ mutex_unlock(&dev_master->bus_mutex);
+ } else { /*some device need pullup */
+ mutex_unlock(&dev_master->bus_mutex);
+ sleep_rem = msleep_interruptible(t_conv);
+ if (sleep_rem != 0) {
+ ret = -EINTR;
+ goto dec_refcnt;
+ }
+ }
+ ret = read_scratchpad(sl, info);
+
+ /* If enabled, check for conversion success */
+ if ((SLAVE_FEATURES(sl) & W1_THERM_CHECK_RESULT) &&
+ (info->rom[6] == 0xC) &&
+ ((info->rom[1] == 0x5 && info->rom[0] == 0x50) ||
+ (info->rom[1] == 0x7 && info->rom[0] == 0xFF))
+ ) {
+ /* Invalid reading (scratchpad byte 6 = 0xC)
+ * due to insufficient conversion time
+ * or power failure.
+ */
+ ret = -EIO;
+ }
+
+ goto dec_refcnt;
+ }
+
+ }
+
+mt_unlock:
+ mutex_unlock(&dev_master->bus_mutex);
+dec_refcnt:
+ atomic_dec(THERM_REFCNT(sl->family_data));
+error:
+ return ret;
+}
+
+static int conv_time_measure(struct w1_slave *sl, int *conv_time)
+{
+ struct therm_info inf,
+ *info = &inf;
+ struct w1_master *dev_master = sl->master;
+ int max_trying = W1_THERM_MAX_TRY;
+ int ret = -ENODEV;
+ bool strong_pullup;
+
+ if (!sl->family_data)
+ goto error;
+
+ strong_pullup = (w1_strong_pullup == 2 ||
+ (!SLAVE_POWERMODE(sl) &&
+ w1_strong_pullup));
+
+ if (strong_pullup) {
+ pr_info("%s: Measure with strong_pullup is not supported.\n", __func__);
+ return -EINVAL;
+ }
+
+ memset(info->rom, 0, sizeof(info->rom));
+
+ /* prevent the slave from going away in sleep */
+ atomic_inc(THERM_REFCNT(sl->family_data));
+
+ if (!bus_mutex_lock(&dev_master->bus_mutex)) {
+ ret = -EAGAIN; /* Didn't acquire the mutex */
+ goto dec_refcnt;
+ }
+
+ while (max_trying-- && ret) { /* ret should be 0 */
+ info->verdict = 0;
+ info->crc = 0;
+ /* safe version to select slave */
+ if (!reset_select_slave(sl)) {
+ int j_start, j_end;
+
+ /*no device need pullup */
+ w1_write_8(dev_master, W1_CONVERT_TEMP);
+
+ j_start = jiffies;
+ ret = w1_poll_completion(dev_master, W1_POLL_CONVERT_TEMP);
+ if (ret) {
+ dev_dbg(&sl->dev, "%s: Timeout\n", __func__);
+ goto mt_unlock;
+ }
+ j_end = jiffies;
+ /* 1.2x increase for variation and changes over temperature range */
+ *conv_time = jiffies_to_msecs(j_end-j_start)*12/10;
+ pr_debug("W1 Measure complete, conv_time = %d, HZ=%d.\n",
+ *conv_time, HZ);
+ if (*conv_time <= CONV_TIME_MEASURE) {
+ ret = -EIO;
+ goto mt_unlock;
+ }
+ mutex_unlock(&dev_master->bus_mutex);
+ ret = read_scratchpad(sl, info);
+ goto dec_refcnt;
+ }
+
+ }
+mt_unlock:
+ mutex_unlock(&dev_master->bus_mutex);
+dec_refcnt:
+ atomic_dec(THERM_REFCNT(sl->family_data));
+error:
+ return ret;
+}
+
+static int read_scratchpad(struct w1_slave *sl, struct therm_info *info)
+{
+ struct w1_master *dev_master = sl->master;
+ int max_trying = W1_THERM_MAX_TRY;
+ int ret = -ENODEV;
+
+ info->verdict = 0;
+
+ if (!sl->family_data)
+ goto error;
+
+ memset(info->rom, 0, sizeof(info->rom));
+
+ /* prevent the slave from going away in sleep */
+ atomic_inc(THERM_REFCNT(sl->family_data));
+
+ if (!bus_mutex_lock(&dev_master->bus_mutex)) {
+ ret = -EAGAIN; /* Didn't acquire the mutex */
+ goto dec_refcnt;
+ }
+
+ while (max_trying-- && ret) { /* ret should be 0 */
+ /* safe version to select slave */
+ if (!reset_select_slave(sl)) {
+ u8 nb_bytes_read;
+
+ w1_write_8(dev_master, W1_READ_SCRATCHPAD);
+
+ nb_bytes_read = w1_read_block(dev_master, info->rom, 9);
+ if (nb_bytes_read != 9) {
+ dev_warn(&sl->dev,
+ "w1_read_block(): returned %u instead of 9.\n",
+ nb_bytes_read);
+ ret = -EIO;
+ }
+
+ info->crc = w1_calc_crc8(info->rom, 8);
+
+ if (info->rom[8] == info->crc) {
+ info->verdict = 1;
+ ret = 0;
+ } else
+ ret = -EIO; /* CRC not checked */
+ }
+
+ }
+ mutex_unlock(&dev_master->bus_mutex);
+
+dec_refcnt:
+ atomic_dec(THERM_REFCNT(sl->family_data));
+error:
+ return ret;
+}
+
+static int write_scratchpad(struct w1_slave *sl, const u8 *data, u8 nb_bytes)
+{
+ struct w1_master *dev_master = sl->master;
+ int max_trying = W1_THERM_MAX_TRY;
+ int ret = -ENODEV;
+
+ if (!sl->family_data)
+ goto error;
+
+ /* prevent the slave from going away in sleep */
+ atomic_inc(THERM_REFCNT(sl->family_data));
+
+ if (!bus_mutex_lock(&dev_master->bus_mutex)) {
+ ret = -EAGAIN; /* Didn't acquire the mutex */
+ goto dec_refcnt;
+ }
+
+ while (max_trying-- && ret) { /* ret should be 0 */
+ /* safe version to select slave */
+ if (!reset_select_slave(sl)) {
+ w1_write_8(dev_master, W1_WRITE_SCRATCHPAD);
+ w1_write_block(dev_master, data, nb_bytes);
+ ret = 0;
+ }
+ }
+ mutex_unlock(&dev_master->bus_mutex);
+
+dec_refcnt:
+ atomic_dec(THERM_REFCNT(sl->family_data));
+error:
+ return ret;
+}
+
+static int copy_scratchpad(struct w1_slave *sl)
+{
+ struct w1_master *dev_master = sl->master;
+ int max_trying = W1_THERM_MAX_TRY;
+ int t_write, ret = -ENODEV;
+ bool strong_pullup;
+
+ if (!sl->family_data)
+ goto error;
+
+ t_write = W1_THERM_EEPROM_WRITE_DELAY;
+ strong_pullup = (w1_strong_pullup == 2 ||
+ (!SLAVE_POWERMODE(sl) &&
+ w1_strong_pullup));
+
+ /* prevent the slave from going away in sleep */
+ atomic_inc(THERM_REFCNT(sl->family_data));
+
+ if (!bus_mutex_lock(&dev_master->bus_mutex)) {
+ ret = -EAGAIN; /* Didn't acquire the mutex */
+ goto dec_refcnt;
+ }
+
+ while (max_trying-- && ret) { /* ret should be 0 */
+ /* safe version to select slave */
+ if (!reset_select_slave(sl)) {
+ unsigned long sleep_rem;
+
+ /* 10ms strong pullup (or delay) after the convert */
+ if (strong_pullup)
+ w1_next_pullup(dev_master, t_write);
+
+ w1_write_8(dev_master, W1_COPY_SCRATCHPAD);
+
+ if (strong_pullup) {
+ sleep_rem = msleep_interruptible(t_write);
+ if (sleep_rem != 0) {
+ ret = -EINTR;
+ goto mt_unlock;
+ }
+ }
+ ret = 0;
+ }
+
+ }
+
+mt_unlock:
+ mutex_unlock(&dev_master->bus_mutex);
+dec_refcnt:
+ atomic_dec(THERM_REFCNT(sl->family_data));
+error:
+ return ret;
+}
+
+static int recall_eeprom(struct w1_slave *sl)
+{
+ struct w1_master *dev_master = sl->master;
+ int max_trying = W1_THERM_MAX_TRY;
+ int ret = -ENODEV;
+
+ if (!sl->family_data)
+ goto error;
+
+ /* prevent the slave from going away in sleep */
+ atomic_inc(THERM_REFCNT(sl->family_data));
+
+ if (!bus_mutex_lock(&dev_master->bus_mutex)) {
+ ret = -EAGAIN; /* Didn't acquire the mutex */
+ goto dec_refcnt;
+ }
+
+ while (max_trying-- && ret) { /* ret should be 0 */
+ /* safe version to select slave */
+ if (!reset_select_slave(sl)) {
+
+ w1_write_8(dev_master, W1_RECALL_EEPROM);
+ ret = w1_poll_completion(dev_master, W1_POLL_RECALL_EEPROM);
+ }
+
+ }
+
+ mutex_unlock(&dev_master->bus_mutex);
+
+dec_refcnt:
+ atomic_dec(THERM_REFCNT(sl->family_data));
+error:
+ return ret;
+}
+
+static int read_powermode(struct w1_slave *sl)
+{
+ struct w1_master *dev_master = sl->master;
+ int max_trying = W1_THERM_MAX_TRY;
+ int ret = -ENODEV;
+
+ if (!sl->family_data)
+ goto error;
+
+ /* prevent the slave from going away in sleep */
+ atomic_inc(THERM_REFCNT(sl->family_data));
+
+ if (!bus_mutex_lock(&dev_master->bus_mutex)) {
+ ret = -EAGAIN; /* Didn't acquire the mutex */
+ goto dec_refcnt;
+ }
+
+ while ((max_trying--) && (ret < 0)) {
+ /* safe version to select slave */
+ if (!reset_select_slave(sl)) {
+ w1_write_8(dev_master, W1_READ_PSUPPLY);
+ /*
+ * Emit a read time slot and read only one bit,
+ * 1 is externally powered,
+ * 0 is parasite powered
+ */
+ ret = w1_touch_bit(dev_master, 1);
+ /* ret should be either 1 either 0 */
+ }
+ }
+ mutex_unlock(&dev_master->bus_mutex);
+
+dec_refcnt:
+ atomic_dec(THERM_REFCNT(sl->family_data));
+error:
+ return ret;
+}
+
+static int trigger_bulk_read(struct w1_master *dev_master)
+{
+ struct w1_slave *sl = NULL; /* used to iterate through slaves */
+ int max_trying = W1_THERM_MAX_TRY;
+ int t_conv = 0;
+ int ret = -ENODEV;
+ bool strong_pullup = false;
+
+ /*
+ * Check whether there are parasite powered device on the bus,
+ * and compute duration of conversion for these devices
+ * so we can apply a strong pullup if required
+ */
+ list_for_each_entry(sl, &dev_master->slist, w1_slave_entry) {
+ if (!sl->family_data)
+ goto error;
+ if (bulk_read_support(sl)) {
+ int t_cur = conversion_time(sl);
+
+ t_conv = t_cur > t_conv ? t_cur : t_conv;
+ strong_pullup = strong_pullup ||
+ (w1_strong_pullup == 2 ||
+ (!SLAVE_POWERMODE(sl) &&
+ w1_strong_pullup));
+ }
+ }
+
+ /*
+ * t_conv is the max conversion time required on the bus
+ * If its 0, no device support the bulk read feature
+ */
+ if (!t_conv)
+ goto error;
+
+ if (!bus_mutex_lock(&dev_master->bus_mutex)) {
+ ret = -EAGAIN; /* Didn't acquire the mutex */
+ goto error;
+ }
+
+ while ((max_trying--) && (ret < 0)) { /* ret should be either 0 */
+
+ if (!w1_reset_bus(dev_master)) { /* Just reset the bus */
+ unsigned long sleep_rem;
+
+ w1_write_8(dev_master, W1_SKIP_ROM);
+
+ if (strong_pullup) /* Apply pullup if required */
+ w1_next_pullup(dev_master, t_conv);
+
+ w1_write_8(dev_master, W1_CONVERT_TEMP);
+
+ /* set a flag to instruct that converT pending */
+ list_for_each_entry(sl,
+ &dev_master->slist, w1_slave_entry) {
+ if (bulk_read_support(sl))
+ SLAVE_CONVERT_TRIGGERED(sl) = -1;
+ }
+
+ if (strong_pullup) { /* some device need pullup */
+ sleep_rem = msleep_interruptible(t_conv);
+ if (sleep_rem != 0) {
+ ret = -EINTR;
+ goto mt_unlock;
+ }
+ mutex_unlock(&dev_master->bus_mutex);
+ } else {
+ mutex_unlock(&dev_master->bus_mutex);
+ sleep_rem = msleep_interruptible(t_conv);
+ if (sleep_rem != 0) {
+ ret = -EINTR;
+ goto set_flag;
+ }
+ }
+ ret = 0;
+ goto set_flag;
+ }
+ }
+
+mt_unlock:
+ mutex_unlock(&dev_master->bus_mutex);
+set_flag:
+ /* set a flag to register convsersion is done */
+ list_for_each_entry(sl, &dev_master->slist, w1_slave_entry) {
+ if (bulk_read_support(sl))
+ SLAVE_CONVERT_TRIGGERED(sl) = 1;
+ }
+error:
+ return ret;
+}
+
+/* Sysfs Interface definition */
+
+static ssize_t w1_slave_show(struct device *device,
+ struct device_attribute *attr, char *buf)
+{
+ struct w1_slave *sl = dev_to_w1_slave(device);
+ struct therm_info info;
+ u8 *family_data = sl->family_data;
+ int ret, i;
+ ssize_t c = PAGE_SIZE;
+
+ if (bulk_read_support(sl)) {
+ if (SLAVE_CONVERT_TRIGGERED(sl) < 0) {
+ dev_dbg(device,
+ "%s: Conversion in progress, retry later\n",
+ __func__);
+ return 0;
+ } else if (SLAVE_CONVERT_TRIGGERED(sl) > 0) {
+ /* A bulk read has been issued, read the device RAM */
+ ret = read_scratchpad(sl, &info);
+ SLAVE_CONVERT_TRIGGERED(sl) = 0;
+ } else
+ ret = convert_t(sl, &info);
+ } else
+ ret = convert_t(sl, &info);
+
+ if (ret < 0) {
+ dev_dbg(device,
+ "%s: Temperature data may be corrupted. err=%d\n",
+ __func__, ret);
+ return 0;
+ }
+
+ for (i = 0; i < 9; ++i)
+ c -= snprintf(buf + PAGE_SIZE - c, c, "%02x ", info.rom[i]);
+ c -= snprintf(buf + PAGE_SIZE - c, c, ": crc=%02x %s\n",
+ info.crc, (info.verdict) ? "YES" : "NO");
+
+ if (info.verdict)
+ memcpy(family_data, info.rom, sizeof(info.rom));
+ else
+ dev_warn(device, "%s:Read failed CRC check\n", __func__);
+
+ for (i = 0; i < 9; ++i)
+ c -= snprintf(buf + PAGE_SIZE - c, c, "%02x ",
+ ((u8 *)family_data)[i]);
+
+ c -= snprintf(buf + PAGE_SIZE - c, c, "t=%d\n",
+ temperature_from_RAM(sl, info.rom));
+
+ ret = PAGE_SIZE - c;
+ return ret;
+}
+
+static ssize_t w1_slave_store(struct device *device,
+ struct device_attribute *attr, const char *buf,
+ size_t size)
+{
+ int val, ret = 0;
+ struct w1_slave *sl = dev_to_w1_slave(device);
+
+ ret = kstrtoint(buf, 10, &val); /* converting user entry to int */
+
+ if (ret) { /* conversion error */
+ dev_info(device,
+ "%s: conversion error. err= %d\n", __func__, ret);
+ return size; /* return size to avoid call back again */
+ }
+
+ if ((!sl->family_data) || (!SLAVE_SPECIFIC_FUNC(sl))) {
+ dev_info(device,
+ "%s: Device not supported by the driver\n", __func__);
+ return size; /* No device family */
+ }
+
+ if (val == 0) /* val=0 : trigger a EEPROM save */
+ ret = copy_scratchpad(sl);
+ else {
+ if (SLAVE_SPECIFIC_FUNC(sl)->set_resolution)
+ ret = SLAVE_SPECIFIC_FUNC(sl)->set_resolution(sl, val);
+ }
+
+ if (ret) {
+ dev_warn(device, "%s: Set resolution - error %d\n", __func__, ret);
+ /* Propagate error to userspace */
+ return ret;
+ }
+ SLAVE_RESOLUTION(sl) = val;
+ /* Reset the conversion time to default - it depends on resolution */
+ SLAVE_CONV_TIME_OVERRIDE(sl) = CONV_TIME_DEFAULT;
+
+ return size; /* always return size to avoid infinite calling */
+}
+
+static ssize_t temperature_show(struct device *device,
+ struct device_attribute *attr, char *buf)
+{
+ struct w1_slave *sl = dev_to_w1_slave(device);
+ struct therm_info info;
+ int ret = 0;
+
+ if ((!sl->family_data) || (!SLAVE_SPECIFIC_FUNC(sl))) {
+ dev_info(device,
+ "%s: Device not supported by the driver\n", __func__);
+ return 0; /* No device family */
+ }
+
+ if (bulk_read_support(sl)) {
+ if (SLAVE_CONVERT_TRIGGERED(sl) < 0) {
+ dev_dbg(device,
+ "%s: Conversion in progress, retry later\n",
+ __func__);
+ return 0;
+ } else if (SLAVE_CONVERT_TRIGGERED(sl) > 0) {
+ /* A bulk read has been issued, read the device RAM */
+ ret = read_scratchpad(sl, &info);
+ SLAVE_CONVERT_TRIGGERED(sl) = 0;
+ } else
+ ret = convert_t(sl, &info);
+ } else
+ ret = convert_t(sl, &info);
+
+ if (ret < 0) {
+ dev_dbg(device,
+ "%s: Temperature data may be corrupted. err=%d\n",
+ __func__, ret);
+ return 0;
+ }
+
+ return sprintf(buf, "%d\n", temperature_from_RAM(sl, info.rom));
+}
+
+static ssize_t ext_power_show(struct device *device,
+ struct device_attribute *attr, char *buf)
+{
+ struct w1_slave *sl = dev_to_w1_slave(device);
+
+ if (!sl->family_data) {
+ dev_info(device,
+ "%s: Device not supported by the driver\n", __func__);
+ return 0; /* No device family */
+ }
+
+ /* Getting the power mode of the device {external, parasite} */
+ SLAVE_POWERMODE(sl) = read_powermode(sl);
+
+ if (SLAVE_POWERMODE(sl) < 0) {
+ dev_dbg(device,
+ "%s: Power_mode may be corrupted. err=%d\n",
+ __func__, SLAVE_POWERMODE(sl));
+ }
+ return sprintf(buf, "%d\n", SLAVE_POWERMODE(sl));
+}
+
+static ssize_t resolution_show(struct device *device,
+ struct device_attribute *attr, char *buf)
+{
+ struct w1_slave *sl = dev_to_w1_slave(device);
+
+ if ((!sl->family_data) || (!SLAVE_SPECIFIC_FUNC(sl))) {
+ dev_info(device,
+ "%s: Device not supported by the driver\n", __func__);
+ return 0; /* No device family */
+ }
+
+ /* get the correct function depending on the device */
+ SLAVE_RESOLUTION(sl) = SLAVE_SPECIFIC_FUNC(sl)->get_resolution(sl);
+ if (SLAVE_RESOLUTION(sl) < 0) {
+ dev_dbg(device,
+ "%s: Resolution may be corrupted. err=%d\n",
+ __func__, SLAVE_RESOLUTION(sl));
+ }
+
+ return sprintf(buf, "%d\n", SLAVE_RESOLUTION(sl));
+}
+
+static ssize_t resolution_store(struct device *device,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct w1_slave *sl = dev_to_w1_slave(device);
+ int val;
+ int ret = 0;
+
+ ret = kstrtoint(buf, 10, &val); /* converting user entry to int */
+
+ if (ret) { /* conversion error */
+ dev_info(device,
+ "%s: conversion error. err= %d\n", __func__, ret);
+ return size; /* return size to avoid call back again */
+ }
+
+ if ((!sl->family_data) || (!SLAVE_SPECIFIC_FUNC(sl))) {
+ dev_info(device,
+ "%s: Device not supported by the driver\n", __func__);
+ return size; /* No device family */
+ }
+
+ /*
+ * Don't deal with the val enterd by user,
+ * only device knows what is correct or not
+ */
+
+ /* get the correct function depending on the device */
+ ret = SLAVE_SPECIFIC_FUNC(sl)->set_resolution(sl, val);
+
+ if (ret)
+ return ret;
+
+ SLAVE_RESOLUTION(sl) = val;
+ /* Reset the conversion time to default because it depends on resolution */
+ SLAVE_CONV_TIME_OVERRIDE(sl) = CONV_TIME_DEFAULT;
+
+ return size;
+}
+
+static ssize_t eeprom_cmd_store(struct device *device,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct w1_slave *sl = dev_to_w1_slave(device);
+ int ret = -EINVAL; /* Invalid argument */
+
+ if (size == sizeof(EEPROM_CMD_WRITE)) {
+ if (!strncmp(buf, EEPROM_CMD_WRITE, sizeof(EEPROM_CMD_WRITE)-1))
+ ret = copy_scratchpad(sl);
+ } else if (size == sizeof(EEPROM_CMD_READ)) {
+ if (!strncmp(buf, EEPROM_CMD_READ, sizeof(EEPROM_CMD_READ)-1))
+ ret = recall_eeprom(sl);
+ }
+
+ if (ret)
+ dev_info(device, "%s: error in process %d\n", __func__, ret);
+
+ return size;
+}
+
+static ssize_t alarms_show(struct device *device,
+ struct device_attribute *attr, char *buf)
+{
+ struct w1_slave *sl = dev_to_w1_slave(device);
+ int ret;
+ s8 th = 0, tl = 0;
+ struct therm_info scratchpad;
+
+ ret = read_scratchpad(sl, &scratchpad);
+
+ if (!ret) {
+ th = scratchpad.rom[2]; /* TH is byte 2 */
+ tl = scratchpad.rom[3]; /* TL is byte 3 */
+ } else {
+ dev_info(device,
+ "%s: error reading alarms register %d\n",
+ __func__, ret);
+ }
+
+ return sprintf(buf, "%hd %hd\n", tl, th);
+}
+
+static ssize_t alarms_store(struct device *device,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct w1_slave *sl = dev_to_w1_slave(device);
+ struct therm_info info;
+ u8 new_config_register[3]; /* array of data to be written */
+ int temp, ret;
+ char *token = NULL;
+ s8 tl, th; /* 1 byte per value + temp ring order */
+ char *p_args, *orig;
+
+ p_args = orig = kmalloc(size, GFP_KERNEL);
+ /* Safe string copys as buf is const */
+ if (!p_args) {
+ dev_warn(device,
+ "%s: error unable to allocate memory %d\n",
+ __func__, -ENOMEM);
+ return size;
+ }
+ strcpy(p_args, buf);
+
+ /* Split string using space char */
+ token = strsep(&p_args, " ");
+
+ if (!token) {
+ dev_info(device,
+ "%s: error parsing args %d\n", __func__, -EINVAL);
+ goto free_m;
+ }
+
+ /* Convert 1st entry to int */
+ ret = kstrtoint (token, 10, &temp);
+ if (ret) {
+ dev_info(device,
+ "%s: error parsing args %d\n", __func__, ret);
+ goto free_m;
+ }
+
+ tl = int_to_short(temp);
+
+ /* Split string using space char */
+ token = strsep(&p_args, " ");
+ if (!token) {
+ dev_info(device,
+ "%s: error parsing args %d\n", __func__, -EINVAL);
+ goto free_m;
+ }
+ /* Convert 2nd entry to int */
+ ret = kstrtoint (token, 10, &temp);
+ if (ret) {
+ dev_info(device,
+ "%s: error parsing args %d\n", __func__, ret);
+ goto free_m;
+ }
+
+ /* Prepare to cast to short by eliminating out of range values */
+ th = int_to_short(temp);
+
+ /* Reorder if required th and tl */
+ if (tl > th)
+ swap(tl, th);
+
+ /*
+ * Read the scratchpad to change only the required bits
+ * (th : byte 2 - tl: byte 3)
+ */
+ ret = read_scratchpad(sl, &info);
+ if (!ret) {
+ new_config_register[0] = th; /* Byte 2 */
+ new_config_register[1] = tl; /* Byte 3 */
+ new_config_register[2] = info.rom[4];/* Byte 4 */
+ } else {
+ dev_info(device,
+ "%s: error reading from the slave device %d\n",
+ __func__, ret);
+ goto free_m;
+ }
+
+ /* Write data in the device RAM */
+ if (!SLAVE_SPECIFIC_FUNC(sl)) {
+ dev_info(device,
+ "%s: Device not supported by the driver %d\n",
+ __func__, -ENODEV);
+ goto free_m;
+ }
+
+ ret = SLAVE_SPECIFIC_FUNC(sl)->write_data(sl, new_config_register);
+ if (ret)
+ dev_info(device,
+ "%s: error writing to the slave device %d\n",
+ __func__, ret);
+
+free_m:
+ /* free allocated memory */
+ kfree(orig);
+
+ return size;
+}
+
+static ssize_t therm_bulk_read_store(struct device *device,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct w1_master *dev_master = dev_to_w1_master(device);
+ int ret = -EINVAL; /* Invalid argument */
+
+ if (size == sizeof(BULK_TRIGGER_CMD))
+ if (!strncmp(buf, BULK_TRIGGER_CMD,
+ sizeof(BULK_TRIGGER_CMD)-1))
+ ret = trigger_bulk_read(dev_master);
+
+ if (ret)
+ dev_info(device,
+ "%s: unable to trigger a bulk read on the bus. err=%d\n",
+ __func__, ret);
+
+ return size;
+}
+
+static ssize_t therm_bulk_read_show(struct device *device,
+ struct device_attribute *attr, char *buf)
+{
+ struct w1_master *dev_master = dev_to_w1_master(device);
+ struct w1_slave *sl = NULL;
+ int ret = 0;
+
+ list_for_each_entry(sl, &dev_master->slist, w1_slave_entry) {
+ if (sl->family_data) {
+ if (bulk_read_support(sl)) {
+ if (SLAVE_CONVERT_TRIGGERED(sl) == -1) {
+ ret = -1;
+ goto show_result;
+ }
+ if (SLAVE_CONVERT_TRIGGERED(sl) == 1)
+ /* continue to check other slaves */
+ ret = 1;
+ }
+ }
+ }
+show_result:
+ return sprintf(buf, "%d\n", ret);
+}
+
+static ssize_t conv_time_show(struct device *device,
+ struct device_attribute *attr, char *buf)
+{
+ struct w1_slave *sl = dev_to_w1_slave(device);
+
+ if ((!sl->family_data) || (!SLAVE_SPECIFIC_FUNC(sl))) {
+ dev_info(device,
+ "%s: Device is not supported by the driver\n", __func__);
+ return 0; /* No device family */
+ }
+ return sprintf(buf, "%d\n", conversion_time(sl));
+}
+
+static ssize_t conv_time_store(struct device *device,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ int val, ret = 0;
+ struct w1_slave *sl = dev_to_w1_slave(device);
+
+ if (kstrtoint(buf, 10, &val)) /* converting user entry to int */
+ return -EINVAL;
+
+ if (check_family_data(sl))
+ return -ENODEV;
+
+ if (val != CONV_TIME_MEASURE) {
+ if (val >= CONV_TIME_DEFAULT)
+ SLAVE_CONV_TIME_OVERRIDE(sl) = val;
+ else
+ return -EINVAL;
+
+ } else {
+ int conv_time;
+
+ ret = conv_time_measure(sl, &conv_time);
+ if (ret)
+ return -EIO;
+ SLAVE_CONV_TIME_OVERRIDE(sl) = conv_time;
+ }
+ return size;
+}
+
+static ssize_t features_show(struct device *device,
+ struct device_attribute *attr, char *buf)
+{
+ struct w1_slave *sl = dev_to_w1_slave(device);
+
+ if ((!sl->family_data) || (!SLAVE_SPECIFIC_FUNC(sl))) {
+ dev_info(device,
+ "%s: Device not supported by the driver\n", __func__);
+ return 0; /* No device family */
+ }
+ return sprintf(buf, "%u\n", SLAVE_FEATURES(sl));
+}
+
+static ssize_t features_store(struct device *device,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ int val, ret = 0;
+ bool strong_pullup;
+ struct w1_slave *sl = dev_to_w1_slave(device);
+
+ ret = kstrtouint(buf, 10, &val); /* converting user entry to int */
+ if (ret)
+ return -EINVAL; /* invalid number */
+
+ if ((!sl->family_data) || (!SLAVE_SPECIFIC_FUNC(sl))) {
+ dev_info(device, "%s: Device not supported by the driver\n", __func__);
+ return -ENODEV;
+ }
+
+ if ((val & W1_THERM_FEATURES_MASK) != val)
+ return -EINVAL;
+
+ SLAVE_FEATURES(sl) = val;
+
+ strong_pullup = (w1_strong_pullup == 2 ||
+ (!SLAVE_POWERMODE(sl) &&
+ w1_strong_pullup));
+
+ if (strong_pullup && SLAVE_FEATURES(sl) & W1_THERM_POLL_COMPLETION) {
+ dev_warn(&sl->dev,
+ "%s: W1_THERM_POLL_COMPLETION disabled in parasite power mode.\n",
+ __func__);
+ SLAVE_FEATURES(sl) &= ~W1_THERM_POLL_COMPLETION;
+ }
+
+ return size;
+}
+
+#if IS_REACHABLE(CONFIG_HWMON)
+static int w1_read_temp(struct device *device, u32 attr, int channel,
+ long *val)
+{
+ struct w1_slave *sl = dev_get_drvdata(device);
+ struct therm_info info;
+ int ret;
+
+ switch (attr) {
+ case hwmon_temp_input:
+ ret = convert_t(sl, &info);
+ if (ret)
+ return ret;
+
+ if (!info.verdict) {
+ ret = -EIO;
+ return ret;
+ }
+
+ *val = temperature_from_RAM(sl, info.rom);
+ ret = 0;
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
+ return ret;
+}
+#endif
+
+#define W1_42_CHAIN 0x99
+#define W1_42_CHAIN_OFF 0x3C
+#define W1_42_CHAIN_OFF_INV 0xC3
+#define W1_42_CHAIN_ON 0x5A
+#define W1_42_CHAIN_ON_INV 0xA5
+#define W1_42_CHAIN_DONE 0x96
+#define W1_42_CHAIN_DONE_INV 0x69
+#define W1_42_COND_READ 0x0F
+#define W1_42_SUCCESS_CONFIRM_BYTE 0xAA
+#define W1_42_FINISHED_BYTE 0xFF
+static ssize_t w1_seq_show(struct device *device,
+ struct device_attribute *attr, char *buf)
+{
+ struct w1_slave *sl = dev_to_w1_slave(device);
+ ssize_t c = PAGE_SIZE;
+ int i;
+ u8 ack;
+ u64 rn;
+ struct w1_reg_num *reg_num;
+ int seq = 0;
+
+ mutex_lock(&sl->master->bus_mutex);
+ /* Place all devices in CHAIN state */
+ if (w1_reset_bus(sl->master))
+ goto error;
+ w1_write_8(sl->master, W1_SKIP_ROM);
+ w1_write_8(sl->master, W1_42_CHAIN);
+ w1_write_8(sl->master, W1_42_CHAIN_ON);
+ w1_write_8(sl->master, W1_42_CHAIN_ON_INV);
+ msleep(sl->master->pullup_duration);
+
+ /* check for acknowledgment */
+ ack = w1_read_8(sl->master);
+ if (ack != W1_42_SUCCESS_CONFIRM_BYTE)
+ goto error;
+
+ /* In case the bus fails to send 0xFF, limit */
+ for (i = 0; i <= 64; i++) {
+ if (w1_reset_bus(sl->master))
+ goto error;
+
+ w1_write_8(sl->master, W1_42_COND_READ);
+ w1_read_block(sl->master, (u8 *)&rn, 8);
+ reg_num = (struct w1_reg_num *) &rn;
+ if (reg_num->family == W1_42_FINISHED_BYTE)
+ break;
+ if (sl->reg_num.id == reg_num->id)
+ seq = i;
+
+ if (w1_reset_bus(sl->master))
+ goto error;
+
+ /* Put the device into chain DONE state */
+ w1_write_8(sl->master, W1_MATCH_ROM);
+ w1_write_block(sl->master, (u8 *)&rn, 8);
+ w1_write_8(sl->master, W1_42_CHAIN);
+ w1_write_8(sl->master, W1_42_CHAIN_DONE);
+ w1_write_8(sl->master, W1_42_CHAIN_DONE_INV);
+
+ /* check for acknowledgment */
+ ack = w1_read_8(sl->master);
+ if (ack != W1_42_SUCCESS_CONFIRM_BYTE)
+ goto error;
+ }
+
+ /* Exit from CHAIN state */
+ if (w1_reset_bus(sl->master))
+ goto error;
+ w1_write_8(sl->master, W1_SKIP_ROM);
+ w1_write_8(sl->master, W1_42_CHAIN);
+ w1_write_8(sl->master, W1_42_CHAIN_OFF);
+ w1_write_8(sl->master, W1_42_CHAIN_OFF_INV);
+
+ /* check for acknowledgment */
+ ack = w1_read_8(sl->master);
+ if (ack != W1_42_SUCCESS_CONFIRM_BYTE)
+ goto error;
+ mutex_unlock(&sl->master->bus_mutex);
+
+ c -= snprintf(buf + PAGE_SIZE - c, c, "%d\n", seq);
+ return PAGE_SIZE - c;
+error:
+ mutex_unlock(&sl->master->bus_mutex);
+ return -EIO;
+}
+
+static int __init w1_therm_init(void)
+{
+ int err, i;
+
+ for (i = 0; i < ARRAY_SIZE(w1_therm_families); ++i) {
+ err = w1_register_family(w1_therm_families[i].f);
+ if (err)
+ w1_therm_families[i].broken = 1;
+ }
+
+ return 0;
+}
+
+static void __exit w1_therm_fini(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(w1_therm_families); ++i)
+ if (!w1_therm_families[i].broken)
+ w1_unregister_family(w1_therm_families[i].f);
+}
+
+module_init(w1_therm_init);
+module_exit(w1_therm_fini);
+
+MODULE_AUTHOR("Evgeniy Polyakov <zbr@ioremap.net>");
+MODULE_DESCRIPTION("Driver for 1-wire Dallas network protocol, temperature family.");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("w1-family-" __stringify(W1_THERM_DS18S20));
+MODULE_ALIAS("w1-family-" __stringify(W1_THERM_DS1822));
+MODULE_ALIAS("w1-family-" __stringify(W1_THERM_DS18B20));
+MODULE_ALIAS("w1-family-" __stringify(W1_THERM_DS1825));
+MODULE_ALIAS("w1-family-" __stringify(W1_THERM_DS28EA00));