diff options
Diffstat (limited to 'drivers/w1')
-rw-r--r-- | drivers/w1/masters/mxc_w1.c | 14 | ||||
-rw-r--r-- | drivers/w1/slaves/w1_ds2405.c | 2 | ||||
-rw-r--r-- | drivers/w1/slaves/w1_ds2406.c | 2 | ||||
-rw-r--r-- | drivers/w1/slaves/w1_ds2408.c | 2 | ||||
-rw-r--r-- | drivers/w1/slaves/w1_ds2413.c | 2 | ||||
-rw-r--r-- | drivers/w1/slaves/w1_ds2423.c | 2 | ||||
-rw-r--r-- | drivers/w1/slaves/w1_ds2430.c | 2 | ||||
-rw-r--r-- | drivers/w1/slaves/w1_ds2431.c | 2 | ||||
-rw-r--r-- | drivers/w1/slaves/w1_ds2433.c | 2 | ||||
-rw-r--r-- | drivers/w1/slaves/w1_ds2438.c | 2 | ||||
-rw-r--r-- | drivers/w1/slaves/w1_ds250x.c | 2 | ||||
-rw-r--r-- | drivers/w1/slaves/w1_ds2780.c | 2 | ||||
-rw-r--r-- | drivers/w1/slaves/w1_ds2781.c | 2 | ||||
-rw-r--r-- | drivers/w1/slaves/w1_ds2805.c | 2 | ||||
-rw-r--r-- | drivers/w1/slaves/w1_ds28e04.c | 2 | ||||
-rw-r--r-- | drivers/w1/slaves/w1_ds28e17.c | 2 | ||||
-rw-r--r-- | drivers/w1/slaves/w1_therm.c | 459 | ||||
-rw-r--r-- | drivers/w1/w1.c | 4 |
18 files changed, 431 insertions, 76 deletions
diff --git a/drivers/w1/masters/mxc_w1.c b/drivers/w1/masters/mxc_w1.c index 1ca880e01476..090cbbf9e1e2 100644 --- a/drivers/w1/masters/mxc_w1.c +++ b/drivers/w1/masters/mxc_w1.c @@ -7,7 +7,7 @@ #include <linux/clk.h> #include <linux/delay.h> #include <linux/io.h> -#include <linux/jiffies.h> +#include <linux/ktime.h> #include <linux/module.h> #include <linux/mod_devicetable.h> #include <linux/platform_device.h> @@ -40,12 +40,12 @@ struct mxc_w1_device { static u8 mxc_w1_ds2_reset_bus(void *data) { struct mxc_w1_device *dev = data; - unsigned long timeout; + ktime_t timeout; writeb(MXC_W1_CONTROL_RPP, dev->regs + MXC_W1_CONTROL); /* Wait for reset sequence 511+512us, use 1500us for sure */ - timeout = jiffies + usecs_to_jiffies(1500); + timeout = ktime_add_us(ktime_get(), 1500); udelay(511 + 512); @@ -55,7 +55,7 @@ static u8 mxc_w1_ds2_reset_bus(void *data) /* PST bit is valid after the RPP bit is self-cleared */ if (!(ctrl & MXC_W1_CONTROL_RPP)) return !(ctrl & MXC_W1_CONTROL_PST); - } while (time_is_after_jiffies(timeout)); + } while (ktime_before(ktime_get(), timeout)); return 1; } @@ -68,12 +68,12 @@ static u8 mxc_w1_ds2_reset_bus(void *data) static u8 mxc_w1_ds2_touch_bit(void *data, u8 bit) { struct mxc_w1_device *dev = data; - unsigned long timeout; + ktime_t timeout; writeb(MXC_W1_CONTROL_WR(bit), dev->regs + MXC_W1_CONTROL); /* Wait for read/write bit (60us, Max 120us), use 200us for sure */ - timeout = jiffies + usecs_to_jiffies(200); + timeout = ktime_add_us(ktime_get(), 200); udelay(60); @@ -83,7 +83,7 @@ static u8 mxc_w1_ds2_touch_bit(void *data, u8 bit) /* RDST bit is valid after the WR1/RD bit is self-cleared */ if (!(ctrl & MXC_W1_CONTROL_WR(bit))) return !!(ctrl & MXC_W1_CONTROL_RDST); - } while (time_is_after_jiffies(timeout)); + } while (ktime_before(ktime_get(), timeout)); return 0; } diff --git a/drivers/w1/slaves/w1_ds2405.c b/drivers/w1/slaves/w1_ds2405.c index 86cd97309d87..1d9a1183e83f 100644 --- a/drivers/w1/slaves/w1_ds2405.c +++ b/drivers/w1/slaves/w1_ds2405.c @@ -206,7 +206,7 @@ static struct attribute *w1_ds2405_attrs[] = { ATTRIBUTE_GROUPS(w1_ds2405); -static struct w1_family_ops w1_ds2405_fops = { +static const struct w1_family_ops w1_ds2405_fops = { .groups = w1_ds2405_groups }; diff --git a/drivers/w1/slaves/w1_ds2406.c b/drivers/w1/slaves/w1_ds2406.c index 762e5e4e2b48..6c269af73c80 100644 --- a/drivers/w1/slaves/w1_ds2406.c +++ b/drivers/w1/slaves/w1_ds2406.c @@ -138,7 +138,7 @@ static void w1_f12_remove_slave(struct w1_slave *sl) &(w1_f12_sysfs_bin_files[i])); } -static struct w1_family_ops w1_f12_fops = { +static const struct w1_family_ops w1_f12_fops = { .add_slave = w1_f12_add_slave, .remove_slave = w1_f12_remove_slave, }; diff --git a/drivers/w1/slaves/w1_ds2408.c b/drivers/w1/slaves/w1_ds2408.c index 83f8d94bb814..ad102c577122 100644 --- a/drivers/w1/slaves/w1_ds2408.c +++ b/drivers/w1/slaves/w1_ds2408.c @@ -336,7 +336,7 @@ static const struct attribute_group *w1_f29_groups[] = { NULL, }; -static struct w1_family_ops w1_f29_fops = { +static const struct w1_family_ops w1_f29_fops = { .add_slave = w1_f29_disable_test_mode, .groups = w1_f29_groups, }; diff --git a/drivers/w1/slaves/w1_ds2413.c b/drivers/w1/slaves/w1_ds2413.c index f1fb18afbcea..c8cfac555b48 100644 --- a/drivers/w1/slaves/w1_ds2413.c +++ b/drivers/w1/slaves/w1_ds2413.c @@ -143,7 +143,7 @@ static const struct attribute_group *w1_f3a_groups[] = { NULL, }; -static struct w1_family_ops w1_f3a_fops = { +static const struct w1_family_ops w1_f3a_fops = { .groups = w1_f3a_groups, }; diff --git a/drivers/w1/slaves/w1_ds2423.c b/drivers/w1/slaves/w1_ds2423.c index f4367282dcc1..b6bd18d5b3f6 100644 --- a/drivers/w1/slaves/w1_ds2423.c +++ b/drivers/w1/slaves/w1_ds2423.c @@ -117,7 +117,7 @@ static struct attribute *w1_f1d_attrs[] = { }; ATTRIBUTE_GROUPS(w1_f1d); -static struct w1_family_ops w1_f1d_fops = { +static const struct w1_family_ops w1_f1d_fops = { .groups = w1_f1d_groups, }; diff --git a/drivers/w1/slaves/w1_ds2430.c b/drivers/w1/slaves/w1_ds2430.c index 75bb8a88620b..0ea7d779d17a 100644 --- a/drivers/w1/slaves/w1_ds2430.c +++ b/drivers/w1/slaves/w1_ds2430.c @@ -279,7 +279,7 @@ static const struct attribute_group *w1_f14_groups[] = { NULL, }; -static struct w1_family_ops w1_f14_fops = { +static const struct w1_family_ops w1_f14_fops = { .groups = w1_f14_groups, }; diff --git a/drivers/w1/slaves/w1_ds2431.c b/drivers/w1/slaves/w1_ds2431.c index e5bd7e2354d7..6856b1c29e17 100644 --- a/drivers/w1/slaves/w1_ds2431.c +++ b/drivers/w1/slaves/w1_ds2431.c @@ -278,7 +278,7 @@ static const struct attribute_group *w1_f2d_groups[] = { NULL, }; -static struct w1_family_ops w1_f2d_fops = { +static const struct w1_family_ops w1_f2d_fops = { .groups = w1_f2d_groups, }; diff --git a/drivers/w1/slaves/w1_ds2433.c b/drivers/w1/slaves/w1_ds2433.c index 1f805c86517a..0f72df15a024 100644 --- a/drivers/w1/slaves/w1_ds2433.c +++ b/drivers/w1/slaves/w1_ds2433.c @@ -288,7 +288,7 @@ static void w1_f23_remove_slave(struct w1_slave *sl) #endif /* CONFIG_W1_SLAVE_DS2433_CRC */ } -static struct w1_family_ops w1_f23_fops = { +static const struct w1_family_ops w1_f23_fops = { .add_slave = w1_f23_add_slave, .remove_slave = w1_f23_remove_slave, .groups = w1_f23_groups, diff --git a/drivers/w1/slaves/w1_ds2438.c b/drivers/w1/slaves/w1_ds2438.c index d199e5a25cc0..5cfb0ae23e91 100644 --- a/drivers/w1/slaves/w1_ds2438.c +++ b/drivers/w1/slaves/w1_ds2438.c @@ -412,7 +412,7 @@ static const struct attribute_group *w1_ds2438_groups[] = { NULL, }; -static struct w1_family_ops w1_ds2438_fops = { +static const struct w1_family_ops w1_ds2438_fops = { .groups = w1_ds2438_groups, }; diff --git a/drivers/w1/slaves/w1_ds250x.c b/drivers/w1/slaves/w1_ds250x.c index e507117444d8..7592c7050d1d 100644 --- a/drivers/w1/slaves/w1_ds250x.c +++ b/drivers/w1/slaves/w1_ds250x.c @@ -215,7 +215,7 @@ static int w1_eprom_add_slave(struct w1_slave *sl) return PTR_ERR_OR_ZERO(nvmem); } -static struct w1_family_ops w1_eprom_fops = { +static const struct w1_family_ops w1_eprom_fops = { .add_slave = w1_eprom_add_slave, }; diff --git a/drivers/w1/slaves/w1_ds2780.c b/drivers/w1/slaves/w1_ds2780.c index c689b1b987b8..c281fe5ed688 100644 --- a/drivers/w1/slaves/w1_ds2780.c +++ b/drivers/w1/slaves/w1_ds2780.c @@ -141,7 +141,7 @@ static void w1_ds2780_remove_slave(struct w1_slave *sl) platform_device_unregister(pdev); } -static struct w1_family_ops w1_ds2780_fops = { +static const struct w1_family_ops w1_ds2780_fops = { .add_slave = w1_ds2780_add_slave, .remove_slave = w1_ds2780_remove_slave, .groups = w1_ds2780_groups, diff --git a/drivers/w1/slaves/w1_ds2781.c b/drivers/w1/slaves/w1_ds2781.c index 84d6ceec5da5..f0d393ae070b 100644 --- a/drivers/w1/slaves/w1_ds2781.c +++ b/drivers/w1/slaves/w1_ds2781.c @@ -138,7 +138,7 @@ static void w1_ds2781_remove_slave(struct w1_slave *sl) platform_device_unregister(pdev); } -static struct w1_family_ops w1_ds2781_fops = { +static const struct w1_family_ops w1_ds2781_fops = { .add_slave = w1_ds2781_add_slave, .remove_slave = w1_ds2781_remove_slave, .groups = w1_ds2781_groups, diff --git a/drivers/w1/slaves/w1_ds2805.c b/drivers/w1/slaves/w1_ds2805.c index ccb753a474b1..206186db727d 100644 --- a/drivers/w1/slaves/w1_ds2805.c +++ b/drivers/w1/slaves/w1_ds2805.c @@ -281,7 +281,7 @@ static void w1_f0d_remove_slave(struct w1_slave *sl) sysfs_remove_bin_file(&sl->dev.kobj, &w1_f0d_bin_attr); } -static struct w1_family_ops w1_f0d_fops = { +static const struct w1_family_ops w1_f0d_fops = { .add_slave = w1_f0d_add_slave, .remove_slave = w1_f0d_remove_slave, }; diff --git a/drivers/w1/slaves/w1_ds28e04.c b/drivers/w1/slaves/w1_ds28e04.c index 8a640f159078..e4f336111edc 100644 --- a/drivers/w1/slaves/w1_ds28e04.c +++ b/drivers/w1/slaves/w1_ds28e04.c @@ -410,7 +410,7 @@ static void w1_f1C_remove_slave(struct w1_slave *sl) sl->family_data = NULL; } -static struct w1_family_ops w1_f1C_fops = { +static const struct w1_family_ops w1_f1C_fops = { .add_slave = w1_f1C_add_slave, .remove_slave = w1_f1C_remove_slave, .groups = w1_f1C_groups, diff --git a/drivers/w1/slaves/w1_ds28e17.c b/drivers/w1/slaves/w1_ds28e17.c index 046ddda83df9..6b00db7169ab 100644 --- a/drivers/w1/slaves/w1_ds28e17.c +++ b/drivers/w1/slaves/w1_ds28e17.c @@ -741,7 +741,7 @@ static void w1_f19_remove_slave(struct w1_slave *sl) /* Declarations within the w1 subsystem. */ -static struct w1_family_ops w1_f19_fops = { +static const struct w1_family_ops w1_f19_fops = { .add_slave = w1_f19_add_slave, .remove_slave = w1_f19_remove_slave, .groups = w1_f19_groups, diff --git a/drivers/w1/slaves/w1_therm.c b/drivers/w1/slaves/w1_therm.c index c1b4eda16719..cddf60b7309c 100644 --- a/drivers/w1/slaves/w1_therm.c +++ b/drivers/w1/slaves/w1_therm.c @@ -17,6 +17,7 @@ #include <linux/delay.h> #include <linux/hwmon.h> #include <linux/string.h> +#include <linux/jiffies.h> #include <linux/w1.h> @@ -65,6 +66,32 @@ static u16 bulk_read_device_counter; /* =0 as per C standard */ #define MIN_TEMP -55 /* min temperature that can be mesured */ #define MAX_TEMP 125 /* max temperature that can be mesured */ +/* Allowed values for sysfs conv_time attribute */ +#define CONV_TIME_DEFAULT 0 +#define CONV_TIME_MEASURE 1 + +/* Bits in sysfs "features" value */ +#define W1_THERM_CHECK_RESULT 1 /* Enable conversion success check */ +#define W1_THERM_POLL_COMPLETION 2 /* Poll for conversion completion */ +#define W1_THERM_FEATURES_MASK 3 /* All values mask */ + +/* Poll period in milliseconds. Should be less then a shortest operation on the device */ +#define W1_POLL_PERIOD 32 +#define W1_POLL_CONVERT_TEMP 2000 /* Timeout for W1_CONVERT_TEMP, ms */ +#define W1_POLL_RECALL_EEPROM 500 /* Timeout for W1_RECALL_EEPROM, ms*/ + +/* Masks for resolution functions, work with all devices */ +/* Bit mask for config register for all devices, bits 7,6,5 */ +#define W1_THERM_RESOLUTION_MASK 0xE0 +/* Bit offset of resolution in config register for all devices */ +#define W1_THERM_RESOLUTION_SHIFT 5 +/* Bit offset of resolution in config register for all devices */ +#define W1_THERM_RESOLUTION_SHIFT 5 +/* Add this to bit value to get resolution */ +#define W1_THERM_RESOLUTION_MIN 9 +/* Maximum allowed value */ +#define W1_THERM_RESOLUTION_MAX 14 + /* Helpers Macros */ /* @@ -89,6 +116,20 @@ static u16 bulk_read_device_counter; /* =0 as per C standard */ (((struct w1_therm_family_data *)(sl->family_data))->resolution) /* + * return the conv_time_override of the sl slave + * always test family data existence before using this macro + */ + #define SLAVE_CONV_TIME_OVERRIDE(sl) \ + (((struct w1_therm_family_data *)(sl->family_data))->conv_time_override) + +/* + * return the features of the sl slave + * always test family data existence before using this macro + */ + #define SLAVE_FEATURES(sl) \ + (((struct w1_therm_family_data *)(sl->family_data))->features) + +/* * return whether or not a converT command has been issued to the slave * * 0: no bulk read is pending * * -1: conversion is in progress @@ -136,6 +177,8 @@ struct w1_therm_family_converter { * -x error or undefined * @resolution: current device resolution * @convert_triggered: conversion state of the device + * @conv_time_override: user selected conversion time or CONV_TIME_DEFAULT + * @features: bit mask - enable temperature validity check, poll for completion * @specific_functions: pointer to struct of device specific function */ struct w1_therm_family_data { @@ -144,6 +187,8 @@ struct w1_therm_family_data { int external_powered; int resolution; int convert_triggered; + int conv_time_override; + unsigned int features; struct w1_therm_family_converter *specific_functions; }; @@ -285,6 +330,19 @@ static ssize_t therm_bulk_read_store(struct device *device, static ssize_t therm_bulk_read_show(struct device *device, struct device_attribute *attr, char *buf); +static ssize_t conv_time_show(struct device *device, + struct device_attribute *attr, char *buf); + +static ssize_t conv_time_store(struct device *device, + struct device_attribute *attr, const char *buf, + size_t size); + +static ssize_t features_show(struct device *device, + struct device_attribute *attr, char *buf); + +static ssize_t features_store(struct device *device, + struct device_attribute *attr, const char *buf, + size_t size); /* Attributes declarations */ static DEVICE_ATTR_RW(w1_slave); @@ -294,6 +352,8 @@ static DEVICE_ATTR_RO(ext_power); static DEVICE_ATTR_RW(resolution); static DEVICE_ATTR_WO(eeprom); static DEVICE_ATTR_RW(alarms); +static DEVICE_ATTR_RW(conv_time); +static DEVICE_ATTR_RW(features); static DEVICE_ATTR_RW(therm_bulk_read); /* attribut at master level */ @@ -328,6 +388,8 @@ static struct attribute *w1_therm_attrs[] = { &dev_attr_resolution.attr, &dev_attr_eeprom.attr, &dev_attr_alarms.attr, + &dev_attr_conv_time.attr, + &dev_attr_features.attr, NULL, }; @@ -337,6 +399,8 @@ static struct attribute *w1_ds18s20_attrs[] = { &dev_attr_ext_power.attr, &dev_attr_eeprom.attr, &dev_attr_alarms.attr, + &dev_attr_conv_time.attr, + &dev_attr_features.attr, NULL, }; @@ -348,6 +412,8 @@ static struct attribute *w1_ds28ea00_attrs[] = { &dev_attr_resolution.attr, &dev_attr_eeprom.attr, &dev_attr_alarms.attr, + &dev_attr_conv_time.attr, + &dev_attr_features.attr, NULL, }; @@ -409,21 +475,21 @@ static const struct hwmon_chip_info w1_chip_info = { /* Family operations */ -static struct w1_family_ops w1_therm_fops = { +static const struct w1_family_ops w1_therm_fops = { .add_slave = w1_therm_add_slave, .remove_slave = w1_therm_remove_slave, .groups = w1_therm_groups, .chip_info = W1_CHIPINFO, }; -static struct w1_family_ops w1_ds18s20_fops = { +static const struct w1_family_ops w1_ds18s20_fops = { .add_slave = w1_therm_add_slave, .remove_slave = w1_therm_remove_slave, .groups = w1_ds18s20_groups, .chip_info = W1_CHIPINFO, }; -static struct w1_family_ops w1_ds28ea00_fops = { +static const struct w1_family_ops w1_ds28ea00_fops = { .add_slave = w1_therm_add_slave, .remove_slave = w1_therm_remove_slave, .groups = w1_ds28ea00_groups, @@ -466,7 +532,12 @@ static inline int w1_DS18B20_convert_time(struct w1_slave *sl) if (!sl->family_data) return -ENODEV; /* device unknown */ - /* return time in ms for conversion operation */ + if (SLAVE_CONV_TIME_OVERRIDE(sl) != CONV_TIME_DEFAULT) + return SLAVE_CONV_TIME_OVERRIDE(sl); + + /* Return the conversion time, depending on resolution, + * select maximum conversion time among all compatible devices + */ switch (SLAVE_RESOLUTION(sl)) { case 9: ret = 95; @@ -478,6 +549,14 @@ static inline int w1_DS18B20_convert_time(struct w1_slave *sl) ret = 375; break; case 12: + ret = 750; + break; + case 13: + ret = 850; /* GX20MH01 only. Datasheet says 500ms, but that's not enough. */ + break; + case 14: + ret = 1600; /* GX20MH01 only. Datasheet says 1000ms - not enough */ + break; default: ret = 750; } @@ -486,8 +565,13 @@ static inline int w1_DS18B20_convert_time(struct w1_slave *sl) static inline int w1_DS18S20_convert_time(struct w1_slave *sl) { - (void)(sl); - return 750; /* always 750ms for DS18S20 */ + if (!sl->family_data) + return -ENODEV; /* device unknown */ + + if (SLAVE_CONV_TIME_OVERRIDE(sl) == CONV_TIME_DEFAULT) + return 750; /* default for DS18S20 */ + else + return SLAVE_CONV_TIME_OVERRIDE(sl); } static inline int w1_DS18B20_write_data(struct w1_slave *sl, @@ -506,52 +590,71 @@ static inline int w1_DS18S20_write_data(struct w1_slave *sl, static inline int w1_DS18B20_set_resolution(struct w1_slave *sl, int val) { int ret; - u8 new_config_register[3]; /* array of data to be written */ - struct therm_info info; + struct therm_info info, info2; - /* resolution of DS18B20 is in the range [9..12] bits */ - if (val < 9 || val > 12) + /* DS18B20 resolution is 9 to 12 bits */ + /* GX20MH01 resolution is 9 to 14 bits */ + if (val < W1_THERM_RESOLUTION_MIN || val > W1_THERM_RESOLUTION_MAX) return -EINVAL; - val -= 9; /* soustract 9 the lowest resolution in bit */ - val = (val << 5); /* shift to position bit 5 & bit 6 */ + /* Calc bit value from resolution */ + val = (val - W1_THERM_RESOLUTION_MIN) << W1_THERM_RESOLUTION_SHIFT; /* * Read the scratchpad to change only the required bits * (bit5 & bit 6 from byte 4) */ ret = read_scratchpad(sl, &info); - if (!ret) { - new_config_register[0] = info.rom[2]; - new_config_register[1] = info.rom[3]; - /* config register is byte 4 & mask 0b10011111*/ - new_config_register[2] = (info.rom[4] & 0x9F) | - (u8) val; - } else + + if (ret) return ret; + + info.rom[4] &= ~W1_THERM_RESOLUTION_MASK; + info.rom[4] |= val; + /* Write data in the device RAM */ - ret = w1_DS18B20_write_data(sl, new_config_register); + ret = w1_DS18B20_write_data(sl, info.rom + 2); + if (ret) + return ret; - return ret; + /* Have to read back the resolution to verify an actual value + * GX20MH01 and DS18B20 are indistinguishable by family number, but resolutions differ + * Some DS18B20 clones don't support resolution change + */ + ret = read_scratchpad(sl, &info2); + if (ret) + /* Scratchpad read fail */ + return ret; + + if ((info2.rom[4] & W1_THERM_RESOLUTION_MASK) == (info.rom[4] & W1_THERM_RESOLUTION_MASK)) + return 0; + + /* Resolution verify error */ + return -EIO; } static inline int w1_DS18B20_get_resolution(struct w1_slave *sl) { int ret; - u8 config_register; + int resolution; struct therm_info info; ret = read_scratchpad(sl, &info); - if (!ret) { - config_register = info.rom[4]; /* config register is byte 4 */ - config_register &= 0x60; /* 0b01100000 keep only bit 5 & 6 */ - config_register = (config_register >> 5); /* shift */ - config_register += 9; /* add 9 the lowest resolution in bit */ - ret = (int) config_register; - } - return ret; + if (ret) + return ret; + + resolution = ((info.rom[4] & W1_THERM_RESOLUTION_MASK) >> W1_THERM_RESOLUTION_SHIFT) + + W1_THERM_RESOLUTION_MIN; + /* GX20MH01 has one special case: + * >=14 means 14 bits when getting resolution from bit value. + * Other devices have no more then 12 bits. + */ + if (resolution > W1_THERM_RESOLUTION_MAX) + resolution = W1_THERM_RESOLUTION_MAX; + + return resolution; } /** @@ -564,11 +667,28 @@ static inline int w1_DS18B20_get_resolution(struct w1_slave *sl) */ static inline int w1_DS18B20_convert_temp(u8 rom[9]) { - s16 t = le16_to_cpup((__le16 *)rom); + int t; + u32 bv; + + /* Config register bit R2 = 1 - GX20MH01 in 13 or 14 bit resolution mode */ + if (rom[4] & 0x80) { + /* Signed 16-bit value to unsigned, cpu order */ + bv = le16_to_cpup((__le16 *)rom); + + /* Insert two temperature bits from config register */ + /* Avoid arithmetic shift of signed value */ + bv = (bv << 2) | (rom[4] & 3); + + t = (int) sign_extend32(bv, 17); /* Degrees, lowest bit is 2^-6 */ + return (t*1000)/64; /* Millidegrees */ + } + t = (int)le16_to_cpup((__le16 *)rom); return t*1000/16; } + + /** * w1_DS18S20_convert_temp() - temperature computation for DS18S20 * @rom: data read from device RAM (8 data bytes + 1 CRC byte) @@ -600,6 +720,7 @@ static inline int w1_DS18S20_convert_temp(u8 rom[9]) } /* Device capability description */ +/* GX20MH01 device shares family number and structure with DS18B20 */ static struct w1_therm_family_converter w1_therm_families[] = { { @@ -621,6 +742,7 @@ static struct w1_therm_family_converter w1_therm_families[] = { .bulk_read = true }, { + /* Also used for GX20MH01 */ .f = &w1_therm_family_DS18B20, .convert = w1_DS18B20_convert_temp, .get_conversion_time = w1_DS18B20_convert_time, @@ -700,6 +822,22 @@ static inline bool bus_mutex_lock(struct mutex *lock) } /** + * check_family_data() - Check if family data and specific functions are present + * @sl: W1 device data + * + * Return: 0 - OK, negative value - error + */ +static int check_family_data(struct w1_slave *sl) +{ + if ((!sl->family_data) || (!SLAVE_SPECIFIC_FUNC(sl))) { + dev_info(&sl->dev, + "%s: Device is not supported by the driver\n", __func__); + return -EINVAL; /* No device family */ + } + return 0; +} + +/** * support_bulk_read() - check if slave support bulk read * @sl: device to check the ability * @@ -883,6 +1021,34 @@ static int reset_select_slave(struct w1_slave *sl) return 0; } +/** + * w1_poll_completion - Poll for operation completion, with timeout + * @dev_master: the device master of the bus + * @tout_ms: timeout in milliseconds + * + * The device is answering 0's while an operation is in progress and 1's after it completes + * Timeout may happen if the previous command was not recognised due to a line noise + * + * Return: 0 - OK, negative error - timeout + */ +static int w1_poll_completion(struct w1_master *dev_master, int tout_ms) +{ + int i; + + for (i = 0; i < tout_ms/W1_POLL_PERIOD; i++) { + /* Delay is before poll, for device to recognize a command */ + msleep(W1_POLL_PERIOD); + + /* Compare all 8 bits to mitigate a noise on the bus */ + if (w1_read_8(dev_master) == 0xFF) + break; + } + if (i == tout_ms/W1_POLL_PERIOD) + return -EIO; + + return 0; +} + static int convert_t(struct w1_slave *sl, struct therm_info *info) { struct w1_master *dev_master = sl->master; @@ -898,6 +1064,13 @@ static int convert_t(struct w1_slave *sl, struct therm_info *info) (!SLAVE_POWERMODE(sl) && w1_strong_pullup)); + if (strong_pullup && SLAVE_FEATURES(sl) & W1_THERM_POLL_COMPLETION) { + dev_warn(&sl->dev, + "%s: Disabling W1_THERM_POLL_COMPLETION in parasite power mode.\n", + __func__); + SLAVE_FEATURES(sl) &= ~W1_THERM_POLL_COMPLETION; + } + /* get conversion duration device and id dependent */ t_conv = conversion_time(sl); @@ -933,15 +1106,38 @@ static int convert_t(struct w1_slave *sl, struct therm_info *info) } mutex_unlock(&dev_master->bus_mutex); } else { /*no device need pullup */ - mutex_unlock(&dev_master->bus_mutex); - - sleep_rem = msleep_interruptible(t_conv); - if (sleep_rem != 0) { - ret = -EINTR; - goto dec_refcnt; + if (SLAVE_FEATURES(sl) & W1_THERM_POLL_COMPLETION) { + ret = w1_poll_completion(dev_master, W1_POLL_CONVERT_TEMP); + if (ret) { + dev_dbg(&sl->dev, "%s: Timeout\n", __func__); + goto mt_unlock; + } + mutex_unlock(&dev_master->bus_mutex); + } else { + /* Fixed delay */ + mutex_unlock(&dev_master->bus_mutex); + sleep_rem = msleep_interruptible(t_conv); + if (sleep_rem != 0) { + ret = -EINTR; + goto dec_refcnt; + } } } ret = read_scratchpad(sl, info); + + /* If enabled, check for conversion success */ + if ((SLAVE_FEATURES(sl) & W1_THERM_CHECK_RESULT) && + (info->rom[6] == 0xC) && + ((info->rom[1] == 0x5 && info->rom[0] == 0x50) || + (info->rom[1] == 0x7 && info->rom[0] == 0xFF)) + ) { + /* Invalid reading (scratchpad byte 6 = 0xC) + * due to insufficient conversion time + * or power failure. + */ + ret = -EIO; + } + goto dec_refcnt; } @@ -955,6 +1151,76 @@ error: return ret; } +static int conv_time_measure(struct w1_slave *sl, int *conv_time) +{ + struct therm_info inf, + *info = &inf; + struct w1_master *dev_master = sl->master; + int max_trying = W1_THERM_MAX_TRY; + int ret = -ENODEV; + bool strong_pullup; + + if (!sl->family_data) + goto error; + + strong_pullup = (w1_strong_pullup == 2 || + (!SLAVE_POWERMODE(sl) && + w1_strong_pullup)); + + if (strong_pullup) { + pr_info("%s: Measure with strong_pullup is not supported.\n", __func__); + return -EINVAL; + } + + memset(info->rom, 0, sizeof(info->rom)); + + /* 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 j_start, j_end; + + /*no device need pullup */ + w1_write_8(dev_master, W1_CONVERT_TEMP); + + j_start = jiffies; + ret = w1_poll_completion(dev_master, W1_POLL_CONVERT_TEMP); + if (ret) { + dev_dbg(&sl->dev, "%s: Timeout\n", __func__); + goto mt_unlock; + } + j_end = jiffies; + /* 1.2x increase for variation and changes over temperature range */ + *conv_time = jiffies_to_msecs(j_end-j_start)*12/10; + pr_debug("W1 Measure complete, conv_time = %d, HZ=%d.\n", + *conv_time, HZ); + if (*conv_time <= CONV_TIME_MEASURE) { + ret = -EIO; + goto mt_unlock; + } + mutex_unlock(&dev_master->bus_mutex); + ret = read_scratchpad(sl, info); + goto dec_refcnt; + } + + } +mt_unlock: + mutex_unlock(&dev_master->bus_mutex); +dec_refcnt: + atomic_dec(THERM_REFCNT(sl->family_data)); +error: + return ret; +} + static int read_scratchpad(struct w1_slave *sl, struct therm_info *info) { struct w1_master *dev_master = sl->master; @@ -1118,10 +1384,7 @@ static int recall_eeprom(struct w1_slave *sl) 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); + ret = w1_poll_completion(dev_master, W1_POLL_RECALL_EEPROM); } } @@ -1345,11 +1608,13 @@ static ssize_t w1_slave_store(struct device *device, } if (ret) { - dev_info(device, - "%s: writing error %d\n", __func__, ret); - /* return size to avoid call back again */ - } else - SLAVE_RESOLUTION(sl) = val; + dev_warn(device, "%s: Set resolution - error %d\n", __func__, ret); + /* Propagate error to userspace */ + return ret; + } + SLAVE_RESOLUTION(sl) = val; + /* Reset the conversion time to default - it depends on resolution */ + SLAVE_CONV_TIME_OVERRIDE(sl) = CONV_TIME_DEFAULT; return size; /* always return size to avoid infinite calling */ } @@ -1465,12 +1730,12 @@ static ssize_t resolution_store(struct device *device, /* get the correct function depending on the device */ ret = SLAVE_SPECIFIC_FUNC(sl)->set_resolution(sl, val); - if (ret) { - dev_info(device, - "%s: writing error %d\n", __func__, ret); - /* return size to avoid call back again */ - } else - SLAVE_RESOLUTION(sl) = val; + if (ret) + return ret; + + SLAVE_RESOLUTION(sl) = val; + /* Reset the conversion time to default because it depends on resolution */ + SLAVE_CONV_TIME_OVERRIDE(sl) = CONV_TIME_DEFAULT; return size; } @@ -1660,6 +1925,96 @@ show_result: return sprintf(buf, "%d\n", ret); } +static ssize_t conv_time_show(struct device *device, + struct device_attribute *attr, char *buf) +{ + struct w1_slave *sl = dev_to_w1_slave(device); + + if ((!sl->family_data) || (!SLAVE_SPECIFIC_FUNC(sl))) { + dev_info(device, + "%s: Device is not supported by the driver\n", __func__); + return 0; /* No device family */ + } + return sprintf(buf, "%d\n", conversion_time(sl)); +} + +static ssize_t conv_time_store(struct device *device, + struct device_attribute *attr, const char *buf, size_t size) +{ + int val, ret = 0; + struct w1_slave *sl = dev_to_w1_slave(device); + + if (kstrtoint(buf, 10, &val)) /* converting user entry to int */ + return -EINVAL; + + if (check_family_data(sl)) + return -ENODEV; + + if (val != CONV_TIME_MEASURE) { + if (val >= CONV_TIME_DEFAULT) + SLAVE_CONV_TIME_OVERRIDE(sl) = val; + else + return -EINVAL; + + } else { + int conv_time; + + ret = conv_time_measure(sl, &conv_time); + if (ret) + return -EIO; + SLAVE_CONV_TIME_OVERRIDE(sl) = conv_time; + } + return size; +} + +static ssize_t features_show(struct device *device, + struct device_attribute *attr, char *buf) +{ + struct w1_slave *sl = dev_to_w1_slave(device); + + 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 */ + } + return sprintf(buf, "%u\n", SLAVE_FEATURES(sl)); +} + +static ssize_t features_store(struct device *device, + struct device_attribute *attr, const char *buf, size_t size) +{ + int val, ret = 0; + bool strong_pullup; + struct w1_slave *sl = dev_to_w1_slave(device); + + ret = kstrtouint(buf, 10, &val); /* converting user entry to int */ + if (ret) + return -EINVAL; /* invalid number */ + + if ((!sl->family_data) || (!SLAVE_SPECIFIC_FUNC(sl))) { + dev_info(device, "%s: Device not supported by the driver\n", __func__); + return -ENODEV; + } + + if ((val & W1_THERM_FEATURES_MASK) != val) + return -EINVAL; + + SLAVE_FEATURES(sl) = val; + + strong_pullup = (w1_strong_pullup == 2 || + (!SLAVE_POWERMODE(sl) && + w1_strong_pullup)); + + if (strong_pullup && SLAVE_FEATURES(sl) & W1_THERM_POLL_COMPLETION) { + dev_warn(&sl->dev, + "%s: W1_THERM_POLL_COMPLETION disabled in parasite power mode.\n", + __func__); + SLAVE_FEATURES(sl) &= ~W1_THERM_POLL_COMPLETION; + } + + return size; +} + #if IS_REACHABLE(CONFIG_HWMON) static int w1_read_temp(struct device *device, u32 attr, int channel, long *val) diff --git a/drivers/w1/w1.c b/drivers/w1/w1.c index e58c7592008d..15a2ee32f116 100644 --- a/drivers/w1/w1.c +++ b/drivers/w1/w1.c @@ -160,7 +160,7 @@ static const struct attribute_group *w1_slave_default_groups[] = { NULL, }; -static struct w1_family_ops w1_default_fops = { +static const struct w1_family_ops w1_default_fops = { .groups = w1_slave_default_groups, }; @@ -613,7 +613,7 @@ end: static int w1_family_notify(unsigned long action, struct w1_slave *sl) { - struct w1_family_ops *fops; + const struct w1_family_ops *fops; int err; fops = sl->family->fops; |