diff options
author | Matt Campbell <mattrcampbell@gmail.com> | 2015-04-28 07:44:17 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2015-05-24 15:08:59 -0400 |
commit | d9411e57dc7fcdbf28eb825d090b06b4248a95bc (patch) | |
tree | ecb950c779a3d706ae9679983e0fab66c911b5dd /drivers | |
parent | f7134eea05b2fb4a2c0935f8a540539fff01f3eb (diff) |
w1: Add support for DS28EA00 sequence to w1-therm
This patch provides support for the DS28EA00 digital thermometer.
The DS28EA00 provides an additional two pins for implementing a sequence
detection algorithm. This feature allows you to determine the physical
location of the chip in the 1-wire bus without needing pre-existing
knowledge of the bus ordering. Support is provided through the sysfs
w1_seq file. The file will contain a single line with an integer value
representing the device index in the bus starting at 0.
Signed-off-by: Matt Campbell <mattrcampbell@gmail.com>
Acked-by: Evgeniy Polyakov <zbr@ioremap.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/w1/slaves/w1_therm.c | 102 |
1 files changed, 101 insertions, 1 deletions
diff --git a/drivers/w1/slaves/w1_therm.c b/drivers/w1/slaves/w1_therm.c index 55eb86c9e214..d21e6864b06f 100644 --- a/drivers/w1/slaves/w1_therm.c +++ b/drivers/w1/slaves/w1_therm.c | |||
@@ -92,13 +92,24 @@ 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_seq_show(struct device *device, | ||
96 | struct device_attribute *attr, char *buf); | ||
97 | |||
95 | static DEVICE_ATTR_RO(w1_slave); | 98 | static DEVICE_ATTR_RO(w1_slave); |
99 | static DEVICE_ATTR_RO(w1_seq); | ||
96 | 100 | ||
97 | static struct attribute *w1_therm_attrs[] = { | 101 | static struct attribute *w1_therm_attrs[] = { |
98 | &dev_attr_w1_slave.attr, | 102 | &dev_attr_w1_slave.attr, |
99 | NULL, | 103 | NULL, |
100 | }; | 104 | }; |
105 | |||
106 | static struct attribute *w1_ds28ea00_attrs[] = { | ||
107 | &dev_attr_w1_slave.attr, | ||
108 | &dev_attr_w1_seq.attr, | ||
109 | NULL, | ||
110 | }; | ||
101 | ATTRIBUTE_GROUPS(w1_therm); | 111 | ATTRIBUTE_GROUPS(w1_therm); |
112 | ATTRIBUTE_GROUPS(w1_ds28ea00); | ||
102 | 113 | ||
103 | static struct w1_family_ops w1_therm_fops = { | 114 | static struct w1_family_ops w1_therm_fops = { |
104 | .add_slave = w1_therm_add_slave, | 115 | .add_slave = w1_therm_add_slave, |
@@ -106,6 +117,12 @@ static struct w1_family_ops w1_therm_fops = { | |||
106 | .groups = w1_therm_groups, | 117 | .groups = w1_therm_groups, |
107 | }; | 118 | }; |
108 | 119 | ||
120 | static struct w1_family_ops w1_ds28ea00_fops = { | ||
121 | .add_slave = w1_therm_add_slave, | ||
122 | .remove_slave = w1_therm_remove_slave, | ||
123 | .groups = w1_ds28ea00_groups, | ||
124 | }; | ||
125 | |||
109 | static struct w1_family w1_therm_family_DS18S20 = { | 126 | static struct w1_family w1_therm_family_DS18S20 = { |
110 | .fid = W1_THERM_DS18S20, | 127 | .fid = W1_THERM_DS18S20, |
111 | .fops = &w1_therm_fops, | 128 | .fops = &w1_therm_fops, |
@@ -123,7 +140,7 @@ static struct w1_family w1_therm_family_DS1822 = { | |||
123 | 140 | ||
124 | static struct w1_family w1_therm_family_DS28EA00 = { | 141 | static struct w1_family w1_therm_family_DS28EA00 = { |
125 | .fid = W1_THERM_DS28EA00, | 142 | .fid = W1_THERM_DS28EA00, |
126 | .fops = &w1_therm_fops, | 143 | .fops = &w1_ds28ea00_fops, |
127 | }; | 144 | }; |
128 | 145 | ||
129 | static struct w1_family w1_therm_family_DS1825 = { | 146 | static struct w1_family w1_therm_family_DS1825 = { |
@@ -316,6 +333,89 @@ post_unlock: | |||
316 | return ret; | 333 | return ret; |
317 | } | 334 | } |
318 | 335 | ||
336 | #define W1_42_CHAIN 0x99 | ||
337 | #define W1_42_CHAIN_OFF 0x3C | ||
338 | #define W1_42_CHAIN_OFF_INV 0xC3 | ||
339 | #define W1_42_CHAIN_ON 0x5A | ||
340 | #define W1_42_CHAIN_ON_INV 0xA5 | ||
341 | #define W1_42_CHAIN_DONE 0x96 | ||
342 | #define W1_42_CHAIN_DONE_INV 0x69 | ||
343 | #define W1_42_COND_READ 0x0F | ||
344 | #define W1_42_SUCCESS_CONFIRM_BYTE 0xAA | ||
345 | #define W1_42_FINISHED_BYTE 0xFF | ||
346 | static ssize_t w1_seq_show(struct device *device, | ||
347 | struct device_attribute *attr, char *buf) | ||
348 | { | ||
349 | struct w1_slave *sl = dev_to_w1_slave(device); | ||
350 | ssize_t c = PAGE_SIZE; | ||
351 | int rv; | ||
352 | int i; | ||
353 | u8 ack; | ||
354 | u64 rn; | ||
355 | struct w1_reg_num *reg_num; | ||
356 | int seq = 0; | ||
357 | |||
358 | mutex_lock(&sl->master->mutex); | ||
359 | /* Place all devices in CHAIN state */ | ||
360 | if (w1_reset_bus(sl->master)) | ||
361 | goto error; | ||
362 | w1_write_8(sl->master, W1_SKIP_ROM); | ||
363 | w1_write_8(sl->master, W1_42_CHAIN); | ||
364 | w1_write_8(sl->master, W1_42_CHAIN_ON); | ||
365 | w1_write_8(sl->master, W1_42_CHAIN_ON_INV); | ||
366 | msleep(sl->master->pullup_duration); | ||
367 | |||
368 | /* check for acknowledgment */ | ||
369 | ack = w1_read_8(sl->master); | ||
370 | if (ack != W1_42_SUCCESS_CONFIRM_BYTE) | ||
371 | goto error; | ||
372 | |||
373 | /* In case the bus fails to send 0xFF, limit*/ | ||
374 | for (i = 0; i <= 64; i++) { | ||
375 | if (w1_reset_bus(sl->master)) | ||
376 | goto error; | ||
377 | |||
378 | w1_write_8(sl->master, W1_42_COND_READ); | ||
379 | rv = w1_read_block(sl->master, (u8 *)&rn, 8); | ||
380 | reg_num = (struct w1_reg_num *) &rn; | ||
381 | if ((char)reg_num->family == W1_42_FINISHED_BYTE) | ||
382 | break; | ||
383 | if (sl->reg_num.id == reg_num->id) | ||
384 | seq = i; | ||
385 | |||
386 | w1_write_8(sl->master, W1_42_CHAIN); | ||
387 | w1_write_8(sl->master, W1_42_CHAIN_DONE); | ||
388 | w1_write_8(sl->master, W1_42_CHAIN_DONE_INV); | ||
389 | w1_read_block(sl->master, &ack, sizeof(ack)); | ||
390 | |||
391 | /* check for acknowledgment */ | ||
392 | ack = w1_read_8(sl->master); | ||
393 | if (ack != W1_42_SUCCESS_CONFIRM_BYTE) | ||
394 | goto error; | ||
395 | |||
396 | } | ||
397 | |||
398 | /* Exit from CHAIN state */ | ||
399 | if (w1_reset_bus(sl->master)) | ||
400 | goto error; | ||
401 | w1_write_8(sl->master, W1_SKIP_ROM); | ||
402 | w1_write_8(sl->master, W1_42_CHAIN); | ||
403 | w1_write_8(sl->master, W1_42_CHAIN_OFF); | ||
404 | w1_write_8(sl->master, W1_42_CHAIN_OFF_INV); | ||
405 | |||
406 | /* check for acknowledgment */ | ||
407 | ack = w1_read_8(sl->master); | ||
408 | if (ack != W1_42_SUCCESS_CONFIRM_BYTE) | ||
409 | goto error; | ||
410 | mutex_unlock(&sl->master->mutex); | ||
411 | |||
412 | c -= snprintf(buf + PAGE_SIZE - c, c, "%d\n", seq); | ||
413 | return PAGE_SIZE - c; | ||
414 | error: | ||
415 | mutex_unlock(&sl->master->bus_mutex); | ||
416 | return -EIO; | ||
417 | } | ||
418 | |||
319 | static int __init w1_therm_init(void) | 419 | static int __init w1_therm_init(void) |
320 | { | 420 | { |
321 | int err, i; | 421 | int err, i; |