summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBen Sen <0.x29a.0@gmail.com>2016-05-01 17:23:33 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2016-05-01 17:37:49 -0400
commit0a19f129d71f18e73249d54de96c835186b8607e (patch)
tree70932d81781981285d03768deaf0bc8fb4cfd36b
parentae53e3740036b5350c4a10dc6fd00fbe3f5134ff (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>
-rw-r--r--Documentation/w1/slaves/w1_therm10
-rw-r--r--drivers/w1/slaves/w1_therm.c218
-rw-r--r--drivers/w1/w1.h2
3 files changed, 222 insertions, 8 deletions
diff --git a/Documentation/w1/slaves/w1_therm b/Documentation/w1/slaves/w1_therm
index 13411fe52f7f..d1f93af36f38 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
33powered it would be possible to convert all the devices at the same 33powered it would be possible to convert all the devices at the same
34time and then go back to read individual sensors. That isn't 34time and then go back to read individual sensors. That isn't
35currently supported. The driver also doesn't support reduced 35currently supported. The driver also doesn't support reduced
36precision (which would also reduce the conversion time). 36precision (which would also reduce the conversion time) when reading values.
37
38Writing a value between 9 and 12 to the sysfs w1_slave file will change the
39precision of the sensor for the next readings. This value is in (volatile)
40SRAM, so it is reset when the sensor gets power-cycled.
41
42To store the current precision configuration into EEPROM, the value 0
43has to be written to the sysfs w1_slave file. Since the EEPROM has a limited
44amount of writes (>50k), this command should be used wisely.
37 45
38The module parameter strong_pullup can be set to 0 to disable the 46The module parameter strong_pullup can be set to 0 to disable the
39strong pullup, 1 to enable autodetection or 2 to force strong pullup. 47strong 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 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)
92static ssize_t w1_slave_show(struct device *device, 92static ssize_t w1_slave_show(struct device *device,
93 struct device_attribute *attr, char *buf); 93 struct device_attribute *attr, char *buf);
94 94
95static ssize_t w1_slave_store(struct device *device,
96 struct device_attribute *attr, const char *buf, size_t size);
97
95static ssize_t w1_seq_show(struct device *device, 98static ssize_t w1_seq_show(struct device *device,
96 struct device_attribute *attr, char *buf); 99 struct device_attribute *attr, char *buf);
97 100
98static DEVICE_ATTR_RO(w1_slave); 101static DEVICE_ATTR_RW(w1_slave);
99static DEVICE_ATTR_RO(w1_seq); 102static DEVICE_ATTR_RO(w1_seq);
100 103
101static struct attribute *w1_therm_attrs[] = { 104static 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 */
165static inline int w1_therm_eeprom(struct device *device);
166
167/* Set precision for conversion */
168static inline int w1_DS18B20_precision(struct device *device, int val);
169static 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. */
160static inline int w1_DS18B20_convert_temp(u8 rom[9]); 172static inline int w1_DS18B20_convert_temp(u8 rom[9]);
161static inline int w1_DS18S20_convert_temp(u8 rom[9]); 173static inline int w1_DS18S20_convert_temp(u8 rom[9]);
@@ -163,26 +175,194 @@ static inline int w1_DS18S20_convert_temp(u8 rom[9]);
163static struct w1_therm_family_converter w1_therm_families[] = { 175static 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
208static 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
272pre_unlock:
273 mutex_unlock(&dev->bus_mutex);
274
275post_unlock:
276 atomic_dec(THERM_REFCNT(family_data));
277 return ret;
278}
279
280/* DS18S20 does not feature configuration register */
281static inline int w1_DS18S20_precision(struct device *device, int val)
282{
283 return 0;
284}
285
286static 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
358pre_unlock:
359 mutex_unlock(&dev->bus_mutex);
360
361post_unlock:
362 atomic_dec(THERM_REFCNT(family_data));
363 return ret;
364}
365
186static inline int w1_DS18B20_convert_temp(u8 rom[9]) 366static 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
403static 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
224static ssize_t w1_slave_show(struct device *device, 428static 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