diff options
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.patch | 528 |
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: |