f07e572f64
bcm2708: boot tested on RPi B+ v1.2 bcm2709: boot tested on RPi 3B v1.2 and RPi 4B v1.1 4G bcm2710: boot tested on RPi 3B v1.2 bcm2711: boot tested on RPi 4B v1.1 4G Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
373 lines
10 KiB
Diff
373 lines
10 KiB
Diff
From 855a8d82506c7c2428e13beebe8dbf7739ea6176 Mon Sep 17 00:00:00 2001
|
|
From: Akira Shimahara <akira215corp@gmail.com>
|
|
Date: Mon, 11 May 2020 22:37:25 +0200
|
|
Subject: [PATCH] w1_therm: adding eeprom sysfs entry
|
|
|
|
commit 45d457a4cf24455eefd076a01a3d86414fc2ff1e upstream.
|
|
|
|
The driver implement 2 hardware functions to access device RAM:
|
|
* copy_scratchpad
|
|
* recall_scratchpad
|
|
They act according to device specifications.
|
|
|
|
As EEPROM operations are not device dependent (all w1_therm can perform
|
|
EEPROM read/write operation following the same protocol), it is removed
|
|
from device families structures.
|
|
|
|
Updating Documentation/ABI/testing/sysfs-driver-w1_therm accordingly.
|
|
|
|
Signed-off-by: Akira Shimahara <akira215corp@gmail.com>
|
|
Link: https://lore.kernel.org/r/20200511203725.410844-1-akira215corp@gmail.com
|
|
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
---
|
|
.../ABI/testing/sysfs-driver-w1_therm | 14 ++
|
|
drivers/w1/slaves/w1_therm.c | 175 ++++++++++++------
|
|
2 files changed, 132 insertions(+), 57 deletions(-)
|
|
|
|
--- a/Documentation/ABI/testing/sysfs-driver-w1_therm
|
|
+++ b/Documentation/ABI/testing/sysfs-driver-w1_therm
|
|
@@ -1,3 +1,17 @@
|
|
+What: /sys/bus/w1/devices/.../eeprom
|
|
+Date: May 2020
|
|
+Contact: Akira Shimahara <akira215corp@gmail.com>
|
|
+Description:
|
|
+ (WO) writing that file will either trigger a save of the
|
|
+ device data to its embedded EEPROM, either restore data
|
|
+ embedded in device EEPROM. Be aware that devices support
|
|
+ limited EEPROM writing cycles (typical 50k)
|
|
+ * 'save': save device RAM to EEPROM
|
|
+ * 'restore': restore EEPROM data in device RAM
|
|
+Users: any user space application which wants to communicate with
|
|
+ w1_term device
|
|
+
|
|
+
|
|
What: /sys/bus/w1/devices/.../ext_power
|
|
Date: May 2020
|
|
Contact: Akira Shimahara <akira215corp@gmail.com>
|
|
--- a/drivers/w1/slaves/w1_therm.c
|
|
+++ b/drivers/w1/slaves/w1_therm.c
|
|
@@ -43,12 +43,21 @@
|
|
static int w1_strong_pullup = 1;
|
|
module_param_named(strong_pullup, w1_strong_pullup, int, 0);
|
|
|
|
+/* 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 */
|
|
+
|
|
/* Helpers Macros */
|
|
|
|
/*
|
|
@@ -86,7 +95,6 @@ module_param_named(strong_pullup, w1_str
|
|
* @convert: pointer to the device conversion function
|
|
* @set_resolution: pointer to the device set_resolution function
|
|
* @get_resolution: pointer to the device get_resolution function
|
|
- * @eeprom: pointer to eeprom function
|
|
*/
|
|
struct w1_therm_family_converter {
|
|
u8 broken;
|
|
@@ -95,7 +103,6 @@ struct w1_therm_family_converter {
|
|
int (*convert)(u8 rom[9]);
|
|
int (*set_resolution)(struct w1_slave *sl, int val);
|
|
int (*get_resolution)(struct w1_slave *sl);
|
|
- int (*eeprom)(struct device *device);
|
|
};
|
|
|
|
/**
|
|
@@ -166,6 +173,22 @@ static int read_scratchpad(struct w1_sla
|
|
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
|
|
*
|
|
@@ -199,12 +222,16 @@ static ssize_t resolution_show(struct de
|
|
static ssize_t resolution_store(struct device *device,
|
|
struct device_attribute *attr, const char *buf, size_t size);
|
|
|
|
+static ssize_t eeprom_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(ext_power);
|
|
static DEVICE_ATTR_RW(resolution);
|
|
+static DEVICE_ATTR_WO(eeprom);
|
|
|
|
/* Interface Functions declaration */
|
|
|
|
@@ -234,12 +261,14 @@ static struct attribute *w1_therm_attrs[
|
|
&dev_attr_w1_slave.attr,
|
|
&dev_attr_ext_power.attr,
|
|
&dev_attr_resolution.attr,
|
|
+ &dev_attr_eeprom.attr,
|
|
NULL,
|
|
};
|
|
|
|
static struct attribute *w1_ds18s20_attrs[] = {
|
|
&dev_attr_w1_slave.attr,
|
|
&dev_attr_ext_power.attr,
|
|
+ &dev_attr_eeprom.attr,
|
|
NULL,
|
|
};
|
|
|
|
@@ -248,6 +277,7 @@ static struct attribute *w1_ds28ea00_att
|
|
&dev_attr_w1_seq.attr,
|
|
&dev_attr_ext_power.attr,
|
|
&dev_attr_resolution.attr,
|
|
+ &dev_attr_eeprom.attr,
|
|
NULL,
|
|
};
|
|
|
|
@@ -359,9 +389,6 @@ static struct w1_family w1_therm_family_
|
|
|
|
/* Device dependent func */
|
|
|
|
-/* write configuration to eeprom */
|
|
-static inline int w1_therm_eeprom(struct device *device);
|
|
-
|
|
static inline int w1_DS18B20_write_data(struct w1_slave *sl,
|
|
const u8 *data)
|
|
{
|
|
@@ -477,35 +504,30 @@ static struct w1_therm_family_converter
|
|
.convert = w1_DS18S20_convert_temp,
|
|
.set_resolution = NULL, /* no config register */
|
|
.get_resolution = NULL, /* no config register */
|
|
- .eeprom = w1_therm_eeprom
|
|
},
|
|
{
|
|
.f = &w1_therm_family_DS1822,
|
|
.convert = w1_DS18B20_convert_temp,
|
|
.set_resolution = w1_DS18B20_set_resolution,
|
|
.get_resolution = w1_DS18B20_get_resolution,
|
|
- .eeprom = w1_therm_eeprom
|
|
},
|
|
{
|
|
.f = &w1_therm_family_DS18B20,
|
|
.convert = w1_DS18B20_convert_temp,
|
|
.set_resolution = w1_DS18B20_set_resolution,
|
|
.get_resolution = w1_DS18B20_get_resolution,
|
|
- .eeprom = w1_therm_eeprom
|
|
},
|
|
{
|
|
.f = &w1_therm_family_DS28EA00,
|
|
.convert = w1_DS18B20_convert_temp,
|
|
.set_resolution = w1_DS18B20_set_resolution,
|
|
.get_resolution = w1_DS18B20_get_resolution,
|
|
- .eeprom = w1_therm_eeprom
|
|
},
|
|
{
|
|
.f = &w1_therm_family_DS1825,
|
|
.convert = w1_DS18B20_convert_temp,
|
|
.set_resolution = w1_DS18B20_set_resolution,
|
|
.get_resolution = w1_DS18B20_get_resolution,
|
|
- .eeprom = w1_therm_eeprom
|
|
}
|
|
};
|
|
|
|
@@ -838,75 +860,94 @@ error:
|
|
return ret;
|
|
}
|
|
|
|
-static inline int w1_therm_eeprom(struct device *device)
|
|
+static int copy_scratchpad(struct w1_slave *sl)
|
|
{
|
|
- struct w1_slave *sl = dev_to_w1_slave(device);
|
|
- struct w1_master *dev = sl->master;
|
|
- u8 rom[9], external_power;
|
|
- int ret, max_trying = 10;
|
|
- u8 *family_data = sl->family_data;
|
|
+ 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) {
|
|
- ret = -ENODEV;
|
|
+ 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(family_data));
|
|
+ atomic_inc(THERM_REFCNT(sl->family_data));
|
|
|
|
- ret = mutex_lock_interruptible(&dev->bus_mutex);
|
|
- if (ret != 0)
|
|
+ if (!bus_mutex_lock(&dev_master->bus_mutex)) {
|
|
+ ret = -EAGAIN; /* Didn't acquire the mutex */
|
|
goto dec_refcnt;
|
|
+ }
|
|
|
|
- memset(rom, 0, sizeof(rom));
|
|
-
|
|
- while (max_trying--) {
|
|
+ while (max_trying-- && ret) { /* ret should be 0 */
|
|
+ /* safe version to select slave */
|
|
if (!reset_select_slave(sl)) {
|
|
- unsigned int tm = 10;
|
|
unsigned long sleep_rem;
|
|
|
|
- /* check if in parasite mode */
|
|
- w1_write_8(dev, W1_READ_PSUPPLY);
|
|
- external_power = w1_read_8(dev);
|
|
-
|
|
- if (reset_select_slave(sl))
|
|
- continue;
|
|
-
|
|
- /* 10ms strong pullup/delay after the copy command */
|
|
- if (w1_strong_pullup == 2 ||
|
|
- (!external_power && w1_strong_pullup))
|
|
- w1_next_pullup(dev, tm);
|
|
-
|
|
- w1_write_8(dev, W1_COPY_SCRATCHPAD);
|
|
-
|
|
- if (external_power) {
|
|
- mutex_unlock(&dev->bus_mutex);
|
|
+ /* 10ms strong pullup (or delay) after the convert */
|
|
+ if (strong_pullup)
|
|
+ w1_next_pullup(dev_master, t_write);
|
|
|
|
- sleep_rem = msleep_interruptible(tm);
|
|
- if (sleep_rem != 0) {
|
|
- ret = -EINTR;
|
|
- goto dec_refcnt;
|
|
- }
|
|
+ w1_write_8(dev_master, W1_COPY_SCRATCHPAD);
|
|
|
|
- ret = mutex_lock_interruptible(&dev->bus_mutex);
|
|
- if (ret != 0)
|
|
- goto dec_refcnt;
|
|
- } else if (!w1_strong_pullup) {
|
|
- sleep_rem = msleep_interruptible(tm);
|
|
+ if (strong_pullup) {
|
|
+ sleep_rem = msleep_interruptible(t_write);
|
|
if (sleep_rem != 0) {
|
|
ret = -EINTR;
|
|
goto mt_unlock;
|
|
}
|
|
}
|
|
-
|
|
- break;
|
|
+ ret = 0;
|
|
}
|
|
+
|
|
}
|
|
|
|
mt_unlock:
|
|
- mutex_unlock(&dev->bus_mutex);
|
|
+ mutex_unlock(&dev_master->bus_mutex);
|
|
dec_refcnt:
|
|
- atomic_dec(THERM_REFCNT(family_data));
|
|
+ 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 = 1; /* Slave will pull line to 0 */
|
|
+ while (ret)
|
|
+ ret = 1 - w1_touch_bit(dev_master, 1);
|
|
+ }
|
|
+
|
|
+ }
|
|
+
|
|
+ mutex_unlock(&dev_master->bus_mutex);
|
|
+
|
|
+dec_refcnt:
|
|
+ atomic_dec(THERM_REFCNT(sl->family_data));
|
|
error:
|
|
return ret;
|
|
}
|
|
@@ -1006,7 +1047,7 @@ static ssize_t w1_slave_store(struct dev
|
|
}
|
|
|
|
if (val == 0) /* val=0 : trigger a EEPROM save */
|
|
- ret = SLAVE_SPECIFIC_FUNC(sl)->eeprom(device);
|
|
+ ret = copy_scratchpad(sl);
|
|
else {
|
|
if (SLAVE_SPECIFIC_FUNC(sl)->set_resolution)
|
|
ret = SLAVE_SPECIFIC_FUNC(sl)->set_resolution(sl, val);
|
|
@@ -1104,6 +1145,26 @@ static ssize_t resolution_store(struct d
|
|
|
|
return size;
|
|
}
|
|
+
|
|
+static ssize_t eeprom_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;
|
|
+}
|
|
|
|
#if IS_REACHABLE(CONFIG_HWMON)
|
|
static int w1_read_temp(struct device *device, u32 attr, int channel,
|