aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/w1
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 /drivers/w1
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>
Diffstat (limited to 'drivers/w1')
-rw-r--r--drivers/w1/slaves/w1_therm.c218
-rw-r--r--drivers/w1/w1.h2
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)
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