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>
529 lines
16 KiB
Diff
529 lines
16 KiB
Diff
From 23769ad113407ceb21a4285a8194c7d788149269 Mon Sep 17 00:00:00 2001
|
|
From: Akira Shimahara <akira215corp@gmail.com>
|
|
Date: Mon, 11 May 2020 22:37:42 +0200
|
|
Subject: [PATCH] w1_therm: optimizing temperature read timings
|
|
|
|
commit 67b392f7b8edfa6f427fecd98722acab34c1c99f upstream.
|
|
|
|
Optimizing temperature reading by reducing waiting conversion time
|
|
according to device resolution settings, as per device specification.
|
|
This is device dependent as not all the devices supports resolution
|
|
setting, so it has been added in device family structures.
|
|
|
|
The process to read the temperature on the device has been adapted in a
|
|
new function 'convert_t()', which replace the former 'read_therm()', is
|
|
introduce to deal with this timing. Strong pull up is also applied during
|
|
the required time, according to device power status needs and
|
|
'strong_pullup' module parameter.
|
|
|
|
'temperature_from_RAM()' function is introduced to get the correct
|
|
temperature computation (device dependent) from device RAM data.
|
|
|
|
An new sysfs entry has been added to ouptut only temperature. The old
|
|
entry w1_slave has been kept for compatibility, without changing its
|
|
output format.
|
|
|
|
Updating Documentation/ABI/testing/sysfs-driver-w1_therm accordingly.
|
|
|
|
Signed-off-by: Akira Shimahara <akira215corp@gmail.com>
|
|
Link: https://lore.kernel.org/r/20200511203742.411039-1-akira215corp@gmail.com
|
|
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
---
|
|
.../ABI/testing/sysfs-driver-w1_therm | 12 +
|
|
drivers/w1/slaves/w1_therm.c | 286 +++++++++++-------
|
|
2 files changed, 197 insertions(+), 101 deletions(-)
|
|
|
|
--- a/Documentation/ABI/testing/sysfs-driver-w1_therm
|
|
+++ b/Documentation/ABI/testing/sysfs-driver-w1_therm
|
|
@@ -41,6 +41,18 @@ Users: any user space application which
|
|
w1_term device
|
|
|
|
|
|
+What: /sys/bus/w1/devices/.../temperature
|
|
+Date: May 2020
|
|
+Contact: Akira Shimahara <akira215corp@gmail.com>
|
|
+Description:
|
|
+ (RO) return the temperature in 1/1000 degC.
|
|
+ Note that the conversion duration depend on the resolution (if
|
|
+ device support this feature). It takes 94ms in 9bits
|
|
+ resolution, 750ms for 12bits.
|
|
+Users: any user space application which wants to communicate with
|
|
+ w1_term device
|
|
+
|
|
+
|
|
What: /sys/bus/w1/devices/.../w1_slave
|
|
Date: May 2020
|
|
Contact: Akira Shimahara <akira215corp@gmail.com>
|
|
--- a/drivers/w1/slaves/w1_therm.c
|
|
+++ b/drivers/w1/slaves/w1_therm.c
|
|
@@ -93,6 +93,7 @@ module_param_named(strong_pullup, w1_str
|
|
* @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
|
|
*/
|
|
@@ -101,6 +102,7 @@ struct w1_therm_family_converter {
|
|
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);
|
|
};
|
|
@@ -154,6 +156,15 @@ struct therm_info {
|
|
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
|
|
@@ -213,6 +224,9 @@ static ssize_t w1_slave_store(struct dev
|
|
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);
|
|
|
|
@@ -229,6 +243,7 @@ static ssize_t eeprom_store(struct devic
|
|
|
|
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);
|
|
@@ -259,6 +274,7 @@ static void w1_therm_remove_slave(struct
|
|
|
|
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.attr,
|
|
@@ -267,6 +283,7 @@ static struct attribute *w1_therm_attrs[
|
|
|
|
static struct attribute *w1_ds18s20_attrs[] = {
|
|
&dev_attr_w1_slave.attr,
|
|
+ &dev_attr_temperature.attr,
|
|
&dev_attr_ext_power.attr,
|
|
&dev_attr_eeprom.attr,
|
|
NULL,
|
|
@@ -275,6 +292,7 @@ static struct attribute *w1_ds18s20_attr
|
|
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.attr,
|
|
@@ -389,6 +407,37 @@ static struct w1_family w1_therm_family_
|
|
|
|
/* Device dependent func */
|
|
|
|
+static inline int w1_DS18B20_convert_time(struct w1_slave *sl)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ if (!sl->family_data)
|
|
+ return -ENODEV; /* device unknown */
|
|
+
|
|
+ /* return time in ms for conversion operation */
|
|
+ switch (SLAVE_RESOLUTION(sl)) {
|
|
+ case 9:
|
|
+ ret = 95;
|
|
+ break;
|
|
+ case 10:
|
|
+ ret = 190;
|
|
+ break;
|
|
+ case 11:
|
|
+ ret = 375;
|
|
+ break;
|
|
+ case 12:
|
|
+ default:
|
|
+ ret = 750;
|
|
+ }
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static inline int w1_DS18S20_convert_time(struct w1_slave *sl)
|
|
+{
|
|
+ (void)(sl);
|
|
+ return 750; /* always 750ms for DS18S20 */
|
|
+}
|
|
+
|
|
static inline int w1_DS18B20_write_data(struct w1_slave *sl,
|
|
const u8 *data)
|
|
{
|
|
@@ -480,8 +529,10 @@ static inline int w1_DS18S20_convert_tem
|
|
{
|
|
int t, h;
|
|
|
|
- if (!rom[7])
|
|
+ if (!rom[7]) {
|
|
+ pr_debug("%s: Invalid argument for conversion\n", __func__);
|
|
return 0;
|
|
+ }
|
|
|
|
if (rom[1] == 0)
|
|
t = ((s32)rom[0] >> 1)*1000;
|
|
@@ -500,34 +551,39 @@ static inline int w1_DS18S20_convert_tem
|
|
|
|
static struct w1_therm_family_converter w1_therm_families[] = {
|
|
{
|
|
- .f = &w1_therm_family_DS18S20,
|
|
- .convert = w1_DS18S20_convert_temp,
|
|
- .set_resolution = NULL, /* no config register */
|
|
- .get_resolution = NULL, /* no config register */
|
|
+ .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 */
|
|
},
|
|
{
|
|
- .f = &w1_therm_family_DS1822,
|
|
- .convert = w1_DS18B20_convert_temp,
|
|
- .set_resolution = w1_DS18B20_set_resolution,
|
|
- .get_resolution = w1_DS18B20_get_resolution,
|
|
+ .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,
|
|
},
|
|
{
|
|
- .f = &w1_therm_family_DS18B20,
|
|
- .convert = w1_DS18B20_convert_temp,
|
|
- .set_resolution = w1_DS18B20_set_resolution,
|
|
- .get_resolution = w1_DS18B20_get_resolution,
|
|
+ .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,
|
|
},
|
|
{
|
|
- .f = &w1_therm_family_DS28EA00,
|
|
- .convert = w1_DS18B20_convert_temp,
|
|
- .set_resolution = w1_DS18B20_set_resolution,
|
|
- .get_resolution = w1_DS18B20_get_resolution,
|
|
+ .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,
|
|
},
|
|
{
|
|
- .f = &w1_therm_family_DS1825,
|
|
- .convert = w1_DS18B20_convert_temp,
|
|
- .set_resolution = w1_DS18B20_set_resolution,
|
|
- .get_resolution = w1_DS18B20_get_resolution,
|
|
+ .f = &w1_therm_family_DS1825,
|
|
+ .convert = w1_DS18B20_convert_temp,
|
|
+ .get_conversion_time = w1_DS18B20_convert_time,
|
|
+ .set_resolution = w1_DS18B20_set_resolution,
|
|
+ .get_resolution = w1_DS18B20_get_resolution,
|
|
}
|
|
};
|
|
|
|
@@ -582,24 +638,44 @@ static inline bool bus_mutex_lock(struct
|
|
}
|
|
|
|
/**
|
|
- * w1_convert_temp() - temperature conversion binding function
|
|
- * @rom: data read from device RAM (8 data bytes + 1 CRC byte)
|
|
- * @fid: device family id
|
|
+ * conversion_time() - get the Tconv for the slave
|
|
+ * @sl: device to get the conversion time
|
|
*
|
|
- * The function call the temperature computation function according to
|
|
- * device family.
|
|
+ * 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: value in millidegrees Celsius.
|
|
+ * Return: conversion time in ms, negative values are kernel error code
|
|
*/
|
|
-static inline int w1_convert_temp(u8 rom[9], u8 fid)
|
|
+static inline int conversion_time(struct w1_slave *sl)
|
|
{
|
|
- int i;
|
|
+ if (SLAVE_SPECIFIC_FUNC(sl))
|
|
+ return SLAVE_SPECIFIC_FUNC(sl)->get_conversion_time(sl);
|
|
|
|
- for (i = 0; i < ARRAY_SIZE(w1_therm_families); ++i)
|
|
- if (w1_therm_families[i].f->fid == fid)
|
|
- return w1_therm_families[i].convert(rom);
|
|
+ dev_info(&sl->dev,
|
|
+ "%s: Device not supported by the driver\n", __func__);
|
|
|
|
- return 0;
|
|
+ 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 */
|
|
}
|
|
|
|
/* Interface Functions */
|
|
@@ -679,96 +755,74 @@ static int reset_select_slave(struct w1_
|
|
return 0;
|
|
}
|
|
|
|
-static ssize_t read_therm(struct device *device,
|
|
- struct w1_slave *sl, struct therm_info *info)
|
|
+static int convert_t(struct w1_slave *sl, struct therm_info *info)
|
|
{
|
|
- struct w1_master *dev = sl->master;
|
|
- u8 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_conv;
|
|
+ int ret = -ENODEV;
|
|
+ bool strong_pullup;
|
|
|
|
- if (!family_data) {
|
|
- ret = -ENODEV;
|
|
+ if (!sl->family_data)
|
|
goto error;
|
|
- }
|
|
|
|
- /* prevent the slave from going away in sleep */
|
|
- atomic_inc(THERM_REFCNT(family_data));
|
|
+ strong_pullup = (w1_strong_pullup == 2 ||
|
|
+ (!SLAVE_POWERMODE(sl) &&
|
|
+ w1_strong_pullup));
|
|
|
|
- ret = mutex_lock_interruptible(&dev->bus_mutex);
|
|
- if (ret != 0)
|
|
- goto dec_refcnt;
|
|
+ /* get conversion duration device and id dependent */
|
|
+ t_conv = conversion_time(sl);
|
|
|
|
memset(info->rom, 0, sizeof(info->rom));
|
|
|
|
- while (max_trying--) {
|
|
+ /* 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 count = 0;
|
|
- unsigned int tm = 750;
|
|
unsigned long sleep_rem;
|
|
|
|
- w1_write_8(dev, W1_READ_PSUPPLY);
|
|
- external_power = w1_read_8(dev);
|
|
-
|
|
- if (reset_select_slave(sl))
|
|
- continue;
|
|
-
|
|
/* 750ms strong pullup (or delay) after the convert */
|
|
- if (w1_strong_pullup == 2 ||
|
|
- (!external_power && w1_strong_pullup))
|
|
- w1_next_pullup(dev, tm);
|
|
-
|
|
- w1_write_8(dev, W1_CONVERT_TEMP);
|
|
+ if (strong_pullup)
|
|
+ w1_next_pullup(dev_master, t_conv);
|
|
|
|
- if (external_power) {
|
|
- mutex_unlock(&dev->bus_mutex);
|
|
+ w1_write_8(dev_master, W1_CONVERT_TEMP);
|
|
|
|
- sleep_rem = msleep_interruptible(tm);
|
|
+ if (strong_pullup) { /*some device need pullup */
|
|
+ sleep_rem = msleep_interruptible(t_conv);
|
|
if (sleep_rem != 0) {
|
|
ret = -EINTR;
|
|
- goto dec_refcnt;
|
|
+ goto mt_unlock;
|
|
}
|
|
+ mutex_unlock(&dev_master->bus_mutex);
|
|
+ } else { /*no device need pullup */
|
|
+ mutex_unlock(&dev_master->bus_mutex);
|
|
|
|
- ret = mutex_lock_interruptible(&dev->bus_mutex);
|
|
- if (ret != 0)
|
|
- goto dec_refcnt;
|
|
- } else if (!w1_strong_pullup) {
|
|
- sleep_rem = msleep_interruptible(tm);
|
|
+ sleep_rem = msleep_interruptible(t_conv);
|
|
if (sleep_rem != 0) {
|
|
ret = -EINTR;
|
|
- goto mt_unlock;
|
|
+ goto dec_refcnt;
|
|
}
|
|
}
|
|
-
|
|
- if (!reset_select_slave(sl)) {
|
|
-
|
|
- w1_write_8(dev, W1_READ_SCRATCHPAD);
|
|
- count = w1_read_block(dev, info->rom, 9);
|
|
- if (count != 9) {
|
|
- dev_warn(device, "w1_read_block() "
|
|
- "returned %u instead of 9.\n",
|
|
- count);
|
|
- }
|
|
-
|
|
- info->crc = w1_calc_crc8(info->rom, 8);
|
|
-
|
|
- if (info->rom[8] == info->crc)
|
|
- info->verdict = 1;
|
|
- }
|
|
+ ret = read_scratchpad(sl, info);
|
|
+ goto dec_refcnt;
|
|
}
|
|
|
|
- if (info->verdict)
|
|
- break;
|
|
}
|
|
|
|
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;
|
|
}
|
|
@@ -1000,27 +1054,33 @@ static ssize_t w1_slave_show(struct devi
|
|
u8 *family_data = sl->family_data;
|
|
int ret, i;
|
|
ssize_t c = PAGE_SIZE;
|
|
- u8 fid = sl->family->fid;
|
|
|
|
- ret = read_therm(device, sl, &info);
|
|
- if (ret)
|
|
- return ret;
|
|
+ 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, "Read failed CRC check\n");
|
|
+ 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",
|
|
- w1_convert_temp(info.rom, fid));
|
|
+ temperature_from_RAM(sl, info.rom));
|
|
+
|
|
ret = PAGE_SIZE - c;
|
|
return ret;
|
|
}
|
|
@@ -1063,6 +1123,31 @@ static ssize_t w1_slave_store(struct dev
|
|
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 */
|
|
+ }
|
|
+
|
|
+ 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)
|
|
{
|
|
@@ -1172,12 +1257,11 @@ static int w1_read_temp(struct device *d
|
|
{
|
|
struct w1_slave *sl = dev_get_drvdata(device);
|
|
struct therm_info info;
|
|
- u8 fid = sl->family->fid;
|
|
int ret;
|
|
|
|
switch (attr) {
|
|
case hwmon_temp_input:
|
|
- ret = read_therm(device, sl, &info);
|
|
+ ret = convert_t(sl, &info);
|
|
if (ret)
|
|
return ret;
|
|
|
|
@@ -1186,7 +1270,7 @@ static int w1_read_temp(struct device *d
|
|
return ret;
|
|
}
|
|
|
|
- *val = w1_convert_temp(info.rom, fid);
|
|
+ *val = temperature_from_RAM(sl, info.rom);
|
|
ret = 0;
|
|
break;
|
|
default:
|