diff options
author | Ben Sen <0.x29a.0@gmail.com> | 2016-05-01 17:23:33 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2016-05-01 17:37:49 -0400 |
commit | 0a19f129d71f18e73249d54de96c835186b8607e (patch) | |
tree | 70932d81781981285d03768deaf0bc8fb4cfd36b /drivers/w1 | |
parent | ae53e3740036b5350c4a10dc6fd00fbe3f5134ff (diff) |
w1: add ability to set (SRAM) and store (EEPROM) configuration for temp sensors like DS18B20
Since many temperature sensors come "preconfigured" with a lower
precision, people are stuck at 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 patch adds write
support for the scratchpad/precision registers via w1_slave sysfs.
Signed-off-by: Ben Sen <0.x29a.0@gmail.com>
Acked-by: Evgeniy Polyakov <zbr@ioremap.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/w1')
-rw-r--r-- | drivers/w1/slaves/w1_therm.c | 218 | ||||
-rw-r--r-- | drivers/w1/w1.h | 2 |
2 files changed, 213 insertions, 7 deletions
diff --git a/drivers/w1/slaves/w1_therm.c b/drivers/w1/slaves/w1_therm.c index 2f029e8f4f95..581a300fd6cd 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) | |||
92 | static ssize_t w1_slave_show(struct device *device, | 92 | static ssize_t w1_slave_show(struct device *device, |
93 | struct device_attribute *attr, char *buf); | 93 | struct device_attribute *attr, char *buf); |
94 | 94 | ||
95 | static ssize_t w1_slave_store(struct device *device, | ||
96 | struct device_attribute *attr, const char *buf, size_t size); | ||
97 | |||
95 | static ssize_t w1_seq_show(struct device *device, | 98 | static ssize_t w1_seq_show(struct device *device, |
96 | struct device_attribute *attr, char *buf); | 99 | struct device_attribute *attr, char *buf); |
97 | 100 | ||
98 | static DEVICE_ATTR_RO(w1_slave); | 101 | static DEVICE_ATTR_RW(w1_slave); |
99 | static DEVICE_ATTR_RO(w1_seq); | 102 | static DEVICE_ATTR_RO(w1_seq); |
100 | 103 | ||
101 | static struct attribute *w1_therm_attrs[] = { | 104 | static struct attribute *w1_therm_attrs[] = { |
@@ -154,8 +157,17 @@ struct w1_therm_family_converter | |||
154 | u16 reserved; | 157 | u16 reserved; |
155 | struct w1_family *f; | 158 | struct w1_family *f; |
156 | int (*convert)(u8 rom[9]); | 159 | int (*convert)(u8 rom[9]); |
160 | int (*precision)(struct device *device, int val); | ||
161 | int (*eeprom)(struct device *device); | ||
157 | }; | 162 | }; |
158 | 163 | ||
164 | /* write configuration to eeprom */ | ||
165 | static inline int w1_therm_eeprom(struct device *device); | ||
166 | |||
167 | /* Set precision for conversion */ | ||
168 | static inline int w1_DS18B20_precision(struct device *device, int val); | ||
169 | static inline int w1_DS18S20_precision(struct device *device, int val); | ||
170 | |||
159 | /* The return value is millidegrees Centigrade. */ | 171 | /* The return value is millidegrees Centigrade. */ |
160 | static inline int w1_DS18B20_convert_temp(u8 rom[9]); | 172 | static inline int w1_DS18B20_convert_temp(u8 rom[9]); |
161 | static inline int w1_DS18S20_convert_temp(u8 rom[9]); | 173 | static inline int w1_DS18S20_convert_temp(u8 rom[9]); |
@@ -163,26 +175,194 @@ static inline int w1_DS18S20_convert_temp(u8 rom[9]); | |||
163 | static struct w1_therm_family_converter w1_therm_families[] = { | 175 | static struct w1_therm_family_converter w1_therm_families[] = { |
164 | { | 176 | { |
165 | .f = &w1_therm_family_DS18S20, | 177 | .f = &w1_therm_family_DS18S20, |
166 | .convert = w1_DS18S20_convert_temp | 178 | .convert = w1_DS18S20_convert_temp, |
179 | .precision = w1_DS18S20_precision, | ||
180 | .eeprom = w1_therm_eeprom | ||
167 | }, | 181 | }, |
168 | { | 182 | { |
169 | .f = &w1_therm_family_DS1822, | 183 | .f = &w1_therm_family_DS1822, |
170 | .convert = w1_DS18B20_convert_temp | 184 | .convert = w1_DS18B20_convert_temp, |
185 | .precision = w1_DS18S20_precision, | ||
186 | .eeprom = w1_therm_eeprom | ||
171 | }, | 187 | }, |
172 | { | 188 | { |
173 | .f = &w1_therm_family_DS18B20, | 189 | .f = &w1_therm_family_DS18B20, |
174 | .convert = w1_DS18B20_convert_temp | 190 | .convert = w1_DS18B20_convert_temp, |
191 | .precision = w1_DS18B20_precision, | ||
192 | .eeprom = w1_therm_eeprom | ||
175 | }, | 193 | }, |
176 | { | 194 | { |
177 | .f = &w1_therm_family_DS28EA00, | 195 | .f = &w1_therm_family_DS28EA00, |
178 | .convert = w1_DS18B20_convert_temp | 196 | .convert = w1_DS18B20_convert_temp, |
197 | .precision = w1_DS18S20_precision, | ||
198 | .eeprom = w1_therm_eeprom | ||
179 | }, | 199 | }, |
180 | { | 200 | { |
181 | .f = &w1_therm_family_DS1825, | 201 | .f = &w1_therm_family_DS1825, |
182 | .convert = w1_DS18B20_convert_temp | 202 | .convert = w1_DS18B20_convert_temp, |
203 | .precision = w1_DS18S20_precision, | ||
204 | .eeprom = w1_therm_eeprom | ||
183 | } | 205 | } |
184 | }; | 206 | }; |
185 | 207 | ||
208 | static inline int w1_therm_eeprom(struct device *device) | ||
209 | { | ||
210 | struct w1_slave *sl = dev_to_w1_slave(device); | ||
211 | struct w1_master *dev = sl->master; | ||
212 | u8 rom[9], external_power; | ||
213 | int ret, max_trying = 10; | ||
214 | u8 *family_data = sl->family_data; | ||
215 | |||
216 | ret = mutex_lock_interruptible(&dev->bus_mutex); | ||
217 | if (ret != 0) | ||
218 | goto post_unlock; | ||
219 | |||
220 | if (!sl->family_data) { | ||
221 | ret = -ENODEV; | ||
222 | goto pre_unlock; | ||
223 | } | ||
224 | |||
225 | /* prevent the slave from going away in sleep */ | ||
226 | atomic_inc(THERM_REFCNT(family_data)); | ||
227 | memset(rom, 0, sizeof(rom)); | ||
228 | |||
229 | while (max_trying--) { | ||
230 | if (!w1_reset_select_slave(sl)) { | ||
231 | unsigned int tm = 10; | ||
232 | unsigned long sleep_rem; | ||
233 | |||
234 | /* check if in parasite mode */ | ||
235 | w1_write_8(dev, W1_READ_PSUPPLY); | ||
236 | external_power = w1_read_8(dev); | ||
237 | |||
238 | if (w1_reset_select_slave(sl)) | ||
239 | continue; | ||
240 | |||
241 | /* 10ms strong pullup/delay after the copy command */ | ||
242 | if (w1_strong_pullup == 2 || | ||
243 | (!external_power && w1_strong_pullup)) | ||
244 | w1_next_pullup(dev, tm); | ||
245 | |||
246 | w1_write_8(dev, W1_COPY_SCRATCHPAD); | ||
247 | |||
248 | if (external_power) { | ||
249 | mutex_unlock(&dev->bus_mutex); | ||
250 | |||
251 | sleep_rem = msleep_interruptible(tm); | ||
252 | if (sleep_rem != 0) { | ||
253 | ret = -EINTR; | ||
254 | goto post_unlock; | ||
255 | } | ||
256 | |||
257 | ret = mutex_lock_interruptible(&dev->bus_mutex); | ||
258 | if (ret != 0) | ||
259 | goto post_unlock; | ||
260 | } else if (!w1_strong_pullup) { | ||
261 | sleep_rem = msleep_interruptible(tm); | ||
262 | if (sleep_rem != 0) { | ||
263 | ret = -EINTR; | ||
264 | goto pre_unlock; | ||
265 | } | ||
266 | } | ||
267 | |||
268 | break; | ||
269 | } | ||
270 | } | ||
271 | |||
272 | pre_unlock: | ||
273 | mutex_unlock(&dev->bus_mutex); | ||
274 | |||
275 | post_unlock: | ||
276 | atomic_dec(THERM_REFCNT(family_data)); | ||
277 | return ret; | ||
278 | } | ||
279 | |||
280 | /* DS18S20 does not feature configuration register */ | ||
281 | static inline int w1_DS18S20_precision(struct device *device, int val) | ||
282 | { | ||
283 | return 0; | ||
284 | } | ||
285 | |||
286 | static inline int w1_DS18B20_precision(struct device *device, int val) | ||
287 | { | ||
288 | struct w1_slave *sl = dev_to_w1_slave(device); | ||
289 | struct w1_master *dev = sl->master; | ||
290 | u8 rom[9], crc; | ||
291 | int ret, max_trying = 10; | ||
292 | u8 *family_data = sl->family_data; | ||
293 | uint8_t precision_bits; | ||
294 | uint8_t mask = 0x60; | ||
295 | |||
296 | if(val > 12 || val < 9) { | ||
297 | pr_warn("Unsupported precision\n"); | ||
298 | return -1; | ||
299 | } | ||
300 | |||
301 | ret = mutex_lock_interruptible(&dev->bus_mutex); | ||
302 | if (ret != 0) | ||
303 | goto post_unlock; | ||
304 | |||
305 | if (!sl->family_data) { | ||
306 | ret = -ENODEV; | ||
307 | goto pre_unlock; | ||
308 | } | ||
309 | |||
310 | /* prevent the slave from going away in sleep */ | ||
311 | atomic_inc(THERM_REFCNT(family_data)); | ||
312 | memset(rom, 0, sizeof(rom)); | ||
313 | |||
314 | /* translate precision to bitmask (see datasheet page 9) */ | ||
315 | switch (val) { | ||
316 | case 9: | ||
317 | precision_bits = 0x00; | ||
318 | break; | ||
319 | case 10: | ||
320 | precision_bits = 0x20; | ||
321 | break; | ||
322 | case 11: | ||
323 | precision_bits = 0x40; | ||
324 | break; | ||
325 | case 12: | ||
326 | default: | ||
327 | precision_bits = 0x60; | ||
328 | break; | ||
329 | } | ||
330 | |||
331 | while (max_trying--) { | ||
332 | crc = 0; | ||
333 | |||
334 | if (!w1_reset_select_slave(sl)) { | ||
335 | int count = 0; | ||
336 | |||
337 | /* read values to only alter precision bits */ | ||
338 | w1_write_8(dev, W1_READ_SCRATCHPAD); | ||
339 | if ((count = w1_read_block(dev, rom, 9)) != 9) | ||
340 | dev_warn(device, "w1_read_block() returned %u instead of 9.\n", count); | ||
341 | |||
342 | crc = w1_calc_crc8(rom, 8); | ||
343 | if (rom[8] == crc) { | ||
344 | rom[4] = (rom[4] & ~mask) | (precision_bits & mask); | ||
345 | |||
346 | if (!w1_reset_select_slave(sl)) { | ||
347 | w1_write_8(dev, W1_WRITE_SCRATCHPAD); | ||
348 | w1_write_8(dev, rom[2]); | ||
349 | w1_write_8(dev, rom[3]); | ||
350 | w1_write_8(dev, rom[4]); | ||
351 | |||
352 | break; | ||
353 | } | ||
354 | } | ||
355 | } | ||
356 | } | ||
357 | |||
358 | pre_unlock: | ||
359 | mutex_unlock(&dev->bus_mutex); | ||
360 | |||
361 | post_unlock: | ||
362 | atomic_dec(THERM_REFCNT(family_data)); | ||
363 | return ret; | ||
364 | } | ||
365 | |||
186 | static inline int w1_DS18B20_convert_temp(u8 rom[9]) | 366 | static inline int w1_DS18B20_convert_temp(u8 rom[9]) |
187 | { | 367 | { |
188 | s16 t = le16_to_cpup((__le16 *)rom); | 368 | s16 t = le16_to_cpup((__le16 *)rom); |
@@ -220,6 +400,30 @@ static inline int w1_convert_temp(u8 rom[9], u8 fid) | |||
220 | return 0; | 400 | return 0; |
221 | } | 401 | } |
222 | 402 | ||
403 | static ssize_t w1_slave_store(struct device *device, | ||
404 | struct device_attribute *attr, const char *buf, | ||
405 | size_t size) | ||
406 | { | ||
407 | int val, ret; | ||
408 | struct w1_slave *sl = dev_to_w1_slave(device); | ||
409 | int i; | ||
410 | |||
411 | ret = kstrtoint(buf, 0, &val); | ||
412 | if (ret) | ||
413 | return ret; | ||
414 | |||
415 | for (i = 0; i < ARRAY_SIZE(w1_therm_families); ++i) { | ||
416 | if (w1_therm_families[i].f->fid == sl->family->fid) { | ||
417 | /* zero value indicates to write current configuration to eeprom */ | ||
418 | if (0 == val) | ||
419 | ret = w1_therm_families[i].eeprom(device); | ||
420 | else | ||
421 | ret = w1_therm_families[i].precision(device, val); | ||
422 | break; | ||
423 | } | ||
424 | } | ||
425 | return ret ? : size; | ||
426 | } | ||
223 | 427 | ||
224 | static ssize_t w1_slave_show(struct device *device, | 428 | static ssize_t w1_slave_show(struct device *device, |
225 | struct device_attribute *attr, char *buf) | 429 | struct device_attribute *attr, char *buf) |
@@ -311,7 +515,7 @@ static ssize_t w1_slave_show(struct device *device, | |||
311 | for (i = 0; i < 9; ++i) | 515 | for (i = 0; i < 9; ++i) |
312 | c -= snprintf(buf + PAGE_SIZE - c, c, "%02x ", rom[i]); | 516 | c -= snprintf(buf + PAGE_SIZE - c, c, "%02x ", rom[i]); |
313 | c -= snprintf(buf + PAGE_SIZE - c, c, ": crc=%02x %s\n", | 517 | c -= snprintf(buf + PAGE_SIZE - c, c, ": crc=%02x %s\n", |
314 | crc, (verdict) ? "YES" : "NO"); | 518 | crc, (verdict) ? "YES" : "NO"); |
315 | if (verdict) | 519 | if (verdict) |
316 | memcpy(family_data, rom, sizeof(rom)); | 520 | memcpy(family_data, rom, sizeof(rom)); |
317 | else | 521 | else |
diff --git a/drivers/w1/w1.h b/drivers/w1/w1.h index 56a49ba41d83..129895f562b0 100644 --- a/drivers/w1/w1.h +++ b/drivers/w1/w1.h | |||
@@ -58,6 +58,8 @@ struct w1_reg_num | |||
58 | #define W1_ALARM_SEARCH 0xEC | 58 | #define W1_ALARM_SEARCH 0xEC |
59 | #define W1_CONVERT_TEMP 0x44 | 59 | #define W1_CONVERT_TEMP 0x44 |
60 | #define W1_SKIP_ROM 0xCC | 60 | #define W1_SKIP_ROM 0xCC |
61 | #define W1_COPY_SCRATCHPAD 0x48 | ||
62 | #define W1_WRITE_SCRATCHPAD 0x4E | ||
61 | #define W1_READ_SCRATCHPAD 0xBE | 63 | #define W1_READ_SCRATCHPAD 0xBE |
62 | #define W1_READ_ROM 0x33 | 64 | #define W1_READ_ROM 0x33 |
63 | #define W1_READ_PSUPPLY 0xB4 | 65 | #define W1_READ_PSUPPLY 0xB4 |