aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/bcm27xx/patches-5.4/950-0771-w1_therm-optimizing-temperature-read-timings.patch
diff options
context:
space:
mode:
authorÁlvaro Fernández Rojas <noltari@gmail.com>2021-02-18 18:04:33 +0100
committerÁlvaro Fernández Rojas <noltari@gmail.com>2021-02-18 23:42:32 +0100
commitf07e572f6447465d8938679533d604e402b0f066 (patch)
treecb333bd2a67e59e7c07659514850a0fd55fc825e /target/linux/bcm27xx/patches-5.4/950-0771-w1_therm-optimizing-temperature-read-timings.patch
parent5d3a6fd970619dfc55f8259035c3027d7613a2a6 (diff)
downloadupstream-f07e572f6447465d8938679533d604e402b0f066.tar.gz
upstream-f07e572f6447465d8938679533d604e402b0f066.tar.bz2
upstream-f07e572f6447465d8938679533d604e402b0f066.zip
bcm27xx: import latest patches from the RPi foundation
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>
Diffstat (limited to 'target/linux/bcm27xx/patches-5.4/950-0771-w1_therm-optimizing-temperature-read-timings.patch')
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0771-w1_therm-optimizing-temperature-read-timings.patch528
1 files changed, 528 insertions, 0 deletions
diff --git a/target/linux/bcm27xx/patches-5.4/950-0771-w1_therm-optimizing-temperature-read-timings.patch b/target/linux/bcm27xx/patches-5.4/950-0771-w1_therm-optimizing-temperature-read-timings.patch
new file mode 100644
index 0000000000..142560c6da
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0771-w1_therm-optimizing-temperature-read-timings.patch
@@ -0,0 +1,528 @@
+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: