diff options
-rw-r--r-- | Documentation/ABI/testing/sysfs-driver-w1_therm | 12 | ||||
-rw-r--r-- | drivers/w1/slaves/w1_therm.c | 286 |
2 files changed, 197 insertions, 101 deletions
diff --git a/Documentation/ABI/testing/sysfs-driver-w1_therm b/Documentation/ABI/testing/sysfs-driver-w1_therm index 8b7ee89ac6ce..6ffd3e3286a7 100644 --- 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 wants to communicate with 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> diff --git a/drivers/w1/slaves/w1_therm.c b/drivers/w1/slaves/w1_therm.c index 1ba07b0833fa..5a81ce2a4e74 100644 --- a/drivers/w1/slaves/w1_therm.c +++ b/drivers/w1/slaves/w1_therm.c @@ -93,6 +93,7 @@ module_param_named(strong_pullup, w1_strong_pullup, int, 0); * @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 device *device, 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 device *device, 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 w1_slave *sl); 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_attrs[] = { 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_DS1825 = { /* 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_temp(u8 rom[9]) { 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_temp(u8 rom[9]) 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 mutex *lock) } /** - * 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_slave *sl) 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; - } - } - - 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); + goto dec_refcnt; } - - 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 device *device, 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 device *device, 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 *device, u32 attr, int channel, { 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 *device, u32 attr, int channel, return ret; } - *val = w1_convert_temp(info.rom, fid); + *val = temperature_from_RAM(sl, info.rom); ret = 0; break; default: |