Hi 17.04.2016, 02:08, "x29a" <0.x29...@googlemail.com>: > based on the PR (https://github.com/raspberrypi/linux/pull/1412) for the > raspberry pi kernel, i would like to propose my changes upstream as well. > > The changes entitle basic write operations for the 1Wire driver which are > needed to change e.g. the precision of temperature sensors like the very > popular DS18B20. Writing to SRAM and commiting the values to EEPROM is > possible. > > Since many sensors come "preconfigured" with a lower precision, people are > stuck that precision when running on a kernel based device (unlike the Dallas > 1Wire library for e.g. Arduino, which supports writing the > configuration/scratchpad).
This looks good to me, Greg, please pull it into your tree > Signed-off-by: x29a <0.x29...@gmail.com> Acked-by: Evgeniy Polyakov <z...@ioremap.net> > --- > Documentation/w1/slaves/w1_therm | 10 +- > drivers/w1/slaves/w1_therm.c | 218 +++++++++++++++++++++++++++++++++++++-- > drivers/w1/w1.h | 2 + > 3 files changed, 222 insertions(+), 8 deletions(-) > > diff --git a/Documentation/w1/slaves/w1_therm > b/Documentation/w1/slaves/w1_therm > index 13411fe..d1f93af 100644 > --- a/Documentation/w1/slaves/w1_therm > +++ b/Documentation/w1/slaves/w1_therm > @@ -33,7 +33,15 @@ temperature conversion at a time. If none of the devices > are parasite > powered it would be possible to convert all the devices at the same > time and then go back to read individual sensors. That isn't > currently supported. The driver also doesn't support reduced > -precision (which would also reduce the conversion time). > +precision (which would also reduce the conversion time) when reading values. > + > +Writing a value between 9 and 12 to the sysfs w1_slave file will change the > +precision of the sensor for the next readings. This value is in (volatile) > +SRAM, so it is reset when the sensor gets power-cycled. > + > +To store the current precision configuration into EEPROM, the value 0 > +has to be written to the sysfs w1_slave file. Since the EEPROM has a limited > +amount of writes (>50k), this command should be used wisely. > > The module parameter strong_pullup can be set to 0 to disable the > strong pullup, 1 to enable autodetection or 2 to force strong pullup. > diff --git a/drivers/w1/slaves/w1_therm.c b/drivers/w1/slaves/w1_therm.c > index 2f029e8..581a300 100644 > --- a/drivers/w1/slaves/w1_therm.c > +++ b/drivers/w1/slaves/w1_therm.c > @@ -92,10 +92,13 @@ static void w1_therm_remove_slave(struct w1_slave *sl) > static ssize_t w1_slave_show(struct device *device, > struct device_attribute *attr, char *buf); > > +static ssize_t w1_slave_store(struct device *device, > + struct device_attribute *attr, const char *buf, size_t size); > + > static ssize_t w1_seq_show(struct device *device, > struct device_attribute *attr, char *buf); > > -static DEVICE_ATTR_RO(w1_slave); > +static DEVICE_ATTR_RW(w1_slave); > static DEVICE_ATTR_RO(w1_seq); > > static struct attribute *w1_therm_attrs[] = { > @@ -154,8 +157,17 @@ struct w1_therm_family_converter > u16 reserved; > struct w1_family *f; > int (*convert)(u8 rom[9]); > + int (*precision)(struct device *device, int val); > + int (*eeprom)(struct device *device); > }; > > +/* write configuration to eeprom */ > +static inline int w1_therm_eeprom(struct device *device); > + > +/* Set precision for conversion */ > +static inline int w1_DS18B20_precision(struct device *device, int val); > +static inline int w1_DS18S20_precision(struct device *device, int val); > + > /* The return value is millidegrees Centigrade. */ > static inline int w1_DS18B20_convert_temp(u8 rom[9]); > static inline int w1_DS18S20_convert_temp(u8 rom[9]); > @@ -163,26 +175,194 @@ 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 > + .convert = w1_DS18S20_convert_temp, > + .precision = w1_DS18S20_precision, > + .eeprom = w1_therm_eeprom > }, > { > .f = &w1_therm_family_DS1822, > - .convert = w1_DS18B20_convert_temp > + .convert = w1_DS18B20_convert_temp, > + .precision = w1_DS18S20_precision, > + .eeprom = w1_therm_eeprom > }, > { > .f = &w1_therm_family_DS18B20, > - .convert = w1_DS18B20_convert_temp > + .convert = w1_DS18B20_convert_temp, > + .precision = w1_DS18B20_precision, > + .eeprom = w1_therm_eeprom > }, > { > .f = &w1_therm_family_DS28EA00, > - .convert = w1_DS18B20_convert_temp > + .convert = w1_DS18B20_convert_temp, > + .precision = w1_DS18S20_precision, > + .eeprom = w1_therm_eeprom > }, > { > .f = &w1_therm_family_DS1825, > - .convert = w1_DS18B20_convert_temp > + .convert = w1_DS18B20_convert_temp, > + .precision = w1_DS18S20_precision, > + .eeprom = w1_therm_eeprom > } > }; > > +static inline int w1_therm_eeprom(struct device *device) > +{ > + struct w1_slave *sl = dev_to_w1_slave(device); > + struct w1_master *dev = sl->master; > + u8 rom[9], external_power; > + int ret, max_trying = 10; > + u8 *family_data = sl->family_data; > + > + ret = mutex_lock_interruptible(&dev->bus_mutex); > + if (ret != 0) > + goto post_unlock; > + > + if (!sl->family_data) { > + ret = -ENODEV; > + goto pre_unlock; > + } > + > + /* prevent the slave from going away in sleep */ > + atomic_inc(THERM_REFCNT(family_data)); > + memset(rom, 0, sizeof(rom)); > + > + while (max_trying--) { > + if (!w1_reset_select_slave(sl)) { > + unsigned int tm = 10; > + unsigned long sleep_rem; > + > + /* check if in parasite mode */ > + w1_write_8(dev, W1_READ_PSUPPLY); > + external_power = w1_read_8(dev); > + > + if (w1_reset_select_slave(sl)) > + continue; > + > + /* 10ms strong pullup/delay after the copy command */ > + if (w1_strong_pullup == 2 || > + (!external_power && w1_strong_pullup)) > + w1_next_pullup(dev, tm); > + > + w1_write_8(dev, W1_COPY_SCRATCHPAD); > + > + if (external_power) { > + mutex_unlock(&dev->bus_mutex); > + > + sleep_rem = msleep_interruptible(tm); > + if (sleep_rem != 0) { > + ret = -EINTR; > + goto post_unlock; > + } > + > + ret = mutex_lock_interruptible(&dev->bus_mutex); > + if (ret != 0) > + goto post_unlock; > + } else if (!w1_strong_pullup) { > + sleep_rem = msleep_interruptible(tm); > + if (sleep_rem != 0) { > + ret = -EINTR; > + goto pre_unlock; > + } > + } > + > + break; > + } > + } > + > +pre_unlock: > + mutex_unlock(&dev->bus_mutex); > + > +post_unlock: > + atomic_dec(THERM_REFCNT(family_data)); > + return ret; > +} > + > +/* DS18S20 does not feature configuration register */ > +static inline int w1_DS18S20_precision(struct device *device, int val) > +{ > + return 0; > +} > + > +static inline int w1_DS18B20_precision(struct device *device, int val) > +{ > + struct w1_slave *sl = dev_to_w1_slave(device); > + struct w1_master *dev = sl->master; > + u8 rom[9], crc; > + int ret, max_trying = 10; > + u8 *family_data = sl->family_data; > + uint8_t precision_bits; > + uint8_t mask = 0x60; > + > + if(val > 12 || val < 9) { > + pr_warn("Unsupported precision\n"); > + return -1; > + } > + > + ret = mutex_lock_interruptible(&dev->bus_mutex); > + if (ret != 0) > + goto post_unlock; > + > + if (!sl->family_data) { > + ret = -ENODEV; > + goto pre_unlock; > + } > + > + /* prevent the slave from going away in sleep */ > + atomic_inc(THERM_REFCNT(family_data)); > + memset(rom, 0, sizeof(rom)); > + > + /* translate precision to bitmask (see datasheet page 9) */ > + switch (val) { > + case 9: > + precision_bits = 0x00; > + break; > + case 10: > + precision_bits = 0x20; > + break; > + case 11: > + precision_bits = 0x40; > + break; > + case 12: > + default: > + precision_bits = 0x60; > + break; > + } > + > + while (max_trying--) { > + crc = 0; > + > + if (!w1_reset_select_slave(sl)) { > + int count = 0; > + > + /* read values to only alter precision bits */ > + w1_write_8(dev, W1_READ_SCRATCHPAD); > + if ((count = w1_read_block(dev, rom, 9)) != 9) > + dev_warn(device, "w1_read_block() returned %u instead of 9.\n", count); > + > + crc = w1_calc_crc8(rom, 8); > + if (rom[8] == crc) { > + rom[4] = (rom[4] & ~mask) | (precision_bits & mask); > + > + if (!w1_reset_select_slave(sl)) { > + w1_write_8(dev, W1_WRITE_SCRATCHPAD); > + w1_write_8(dev, rom[2]); > + w1_write_8(dev, rom[3]); > + w1_write_8(dev, rom[4]); > + > + break; > + } > + } > + } > + } > + > +pre_unlock: > + mutex_unlock(&dev->bus_mutex); > + > +post_unlock: > + atomic_dec(THERM_REFCNT(family_data)); > + return ret; > +} > + > static inline int w1_DS18B20_convert_temp(u8 rom[9]) > { > s16 t = le16_to_cpup((__le16 *)rom); > @@ -220,6 +400,30 @@ static inline int w1_convert_temp(u8 rom[9], u8 fid) > return 0; > } > > +static ssize_t w1_slave_store(struct device *device, > + struct device_attribute *attr, const char *buf, > + size_t size) > +{ > + int val, ret; > + struct w1_slave *sl = dev_to_w1_slave(device); > + int i; > + > + ret = kstrtoint(buf, 0, &val); > + if (ret) > + return ret; > + > + for (i = 0; i < ARRAY_SIZE(w1_therm_families); ++i) { > + if (w1_therm_families[i].f->fid == sl->family->fid) { > + /* zero value indicates to write current configuration to eeprom */ > + if (0 == val) > + ret = w1_therm_families[i].eeprom(device); > + else > + ret = w1_therm_families[i].precision(device, val); > + break; > + } > + } > + return ret ? : size; > +} > > static ssize_t w1_slave_show(struct device *device, > struct device_attribute *attr, char *buf) > @@ -311,7 +515,7 @@ static ssize_t w1_slave_show(struct device *device, > for (i = 0; i < 9; ++i) > c -= snprintf(buf + PAGE_SIZE - c, c, "%02x ", rom[i]); > c -= snprintf(buf + PAGE_SIZE - c, c, ": crc=%02x %s\n", > - crc, (verdict) ? "YES" : "NO"); > + crc, (verdict) ? "YES" : "NO"); > if (verdict) > memcpy(family_data, rom, sizeof(rom)); > else > diff --git a/drivers/w1/w1.h b/drivers/w1/w1.h > index 56a49ba..129895f 100644 > --- a/drivers/w1/w1.h > +++ b/drivers/w1/w1.h > @@ -58,6 +58,8 @@ struct w1_reg_num > #define W1_ALARM_SEARCH 0xEC > #define W1_CONVERT_TEMP 0x44 > #define W1_SKIP_ROM 0xCC > +#define W1_COPY_SCRATCHPAD 0x48 > +#define W1_WRITE_SCRATCHPAD 0x4E > #define W1_READ_SCRATCHPAD 0xBE > #define W1_READ_ROM 0x33 > #define W1_READ_PSUPPLY 0xB4 > -- > 2.5.0