aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarkus Franke <franm@hrz.tu-chemnitz.de>2012-05-25 18:45:12 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-06-13 19:47:10 -0400
commitfbf7f7b4e2ae40f790828c86d31beff2d49e9ac8 (patch)
tree101a1272b8f1ebdf948945ade2aa7cff1b9a7311
parenta59d6293e5372d7c35212932e083e2a541151eff (diff)
w1: Add 1-wire slave device driver for DS28E04-100
Signed-off-by: Markus Franke <franm@hrz.tu-chemnitz.de> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--Documentation/ABI/stable/sysfs-driver-w1_ds28e0415
-rw-r--r--Documentation/w1/slaves/w1_ds28e0436
-rw-r--r--drivers/w1/slaves/Kconfig13
-rw-r--r--drivers/w1/slaves/Makefile1
-rw-r--r--drivers/w1/slaves/w1_ds28e04.c469
-rw-r--r--drivers/w1/w1_family.h1
6 files changed, 535 insertions, 0 deletions
diff --git a/Documentation/ABI/stable/sysfs-driver-w1_ds28e04 b/Documentation/ABI/stable/sysfs-driver-w1_ds28e04
new file mode 100644
index 00000000000..26579ee868c
--- /dev/null
+++ b/Documentation/ABI/stable/sysfs-driver-w1_ds28e04
@@ -0,0 +1,15 @@
1What: /sys/bus/w1/devices/.../pio
2Date: May 2012
3Contact: Markus Franke <franm@hrz.tu-chemnitz.de>
4Description: read/write the contents of the two PIO's of the DS28E04-100
5 see Documentation/w1/slaves/w1_ds28e04 for detailed information
6Users: any user space application which wants to communicate with DS28E04-100
7
8
9
10What: /sys/bus/w1/devices/.../eeprom
11Date: May 2012
12Contact: Markus Franke <franm@hrz.tu-chemnitz.de>
13Description: read/write the contents of the EEPROM memory of the DS28E04-100
14 see Documentation/w1/slaves/w1_ds28e04 for detailed information
15Users: any user space application which wants to communicate with DS28E04-100
diff --git a/Documentation/w1/slaves/w1_ds28e04 b/Documentation/w1/slaves/w1_ds28e04
new file mode 100644
index 00000000000..85bc9a7e02f
--- /dev/null
+++ b/Documentation/w1/slaves/w1_ds28e04
@@ -0,0 +1,36 @@
1Kernel driver w1_ds28e04
2========================
3
4Supported chips:
5 * Maxim DS28E04-100 4096-Bit Addressable 1-Wire EEPROM with PIO
6
7supported family codes:
8 W1_FAMILY_DS28E04 0x1C
9
10Author: Markus Franke, <franke.m@sebakmt.com> <franm@hrz.tu-chemnitz.de>
11
12Description
13-----------
14
15Support is provided through the sysfs files "eeprom" and "pio". CRC checking
16during memory accesses can optionally be enabled/disabled via the device
17attribute "crccheck". The strong pull-up can optionally be enabled/disabled
18via the module parameter "w1_strong_pullup".
19
20Memory Access
21
22 A read operation on the "eeprom" file reads the given amount of bytes
23 from the EEPROM of the DS28E04.
24
25 A write operation on the "eeprom" file writes the given byte sequence
26 to the EEPROM of the DS28E04. If CRC checking mode is enabled only
27 fully alligned blocks of 32 bytes with valid CRC16 values (in bytes 30
28 and 31) are allowed to be written.
29
30PIO Access
31
32 The 2 PIOs of the DS28E04-100 are accessible via the "pio" sysfs file.
33
34 The current status of the PIO's is returned as an 8 bit value. Bit 0/1
35 represent the state of PIO_0/PIO_1. Bits 2..7 do not care. The PIO's are
36 driven low-active, i.e. the driver delivers/expects low-active values.
diff --git a/drivers/w1/slaves/Kconfig b/drivers/w1/slaves/Kconfig
index eb9e376d624..67526690acb 100644
--- a/drivers/w1/slaves/Kconfig
+++ b/drivers/w1/slaves/Kconfig
@@ -94,6 +94,19 @@ config W1_SLAVE_DS2781
94 94
95 If you are unsure, say N. 95 If you are unsure, say N.
96 96
97config W1_SLAVE_DS28E04
98 tristate "4096-Bit Addressable 1-Wire EEPROM with PIO (DS28E04-100)"
99 depends on W1
100 select CRC16
101 help
102 If you enable this you will have the DS28E04-100
103 chip support.
104
105 Say Y here if you want to use a 1-wire
106 4kb EEPROM with PIO family device (DS28E04).
107
108 If you are unsure, say N.
109
97config W1_SLAVE_BQ27000 110config W1_SLAVE_BQ27000
98 tristate "BQ27000 slave support" 111 tristate "BQ27000 slave support"
99 depends on W1 112 depends on W1
diff --git a/drivers/w1/slaves/Makefile b/drivers/w1/slaves/Makefile
index c4f1859fb52..05188f6aab5 100644
--- a/drivers/w1/slaves/Makefile
+++ b/drivers/w1/slaves/Makefile
@@ -12,3 +12,4 @@ obj-$(CONFIG_W1_SLAVE_DS2760) += w1_ds2760.o
12obj-$(CONFIG_W1_SLAVE_DS2780) += w1_ds2780.o 12obj-$(CONFIG_W1_SLAVE_DS2780) += w1_ds2780.o
13obj-$(CONFIG_W1_SLAVE_DS2781) += w1_ds2781.o 13obj-$(CONFIG_W1_SLAVE_DS2781) += w1_ds2781.o
14obj-$(CONFIG_W1_SLAVE_BQ27000) += w1_bq27000.o 14obj-$(CONFIG_W1_SLAVE_BQ27000) += w1_bq27000.o
15obj-$(CONFIG_W1_SLAVE_DS28E04) += w1_ds28e04.o
diff --git a/drivers/w1/slaves/w1_ds28e04.c b/drivers/w1/slaves/w1_ds28e04.c
new file mode 100644
index 00000000000..98117db595b
--- /dev/null
+++ b/drivers/w1/slaves/w1_ds28e04.c
@@ -0,0 +1,469 @@
1/*
2 * w1_ds28e04.c - w1 family 1C (DS28E04) driver
3 *
4 * Copyright (c) 2012 Markus Franke <franke.m@sebakmt.com>
5 *
6 * This source code is licensed under the GNU General Public License,
7 * Version 2. See the file COPYING for more details.
8 */
9
10#include <linux/kernel.h>
11#include <linux/module.h>
12#include <linux/moduleparam.h>
13#include <linux/device.h>
14#include <linux/types.h>
15#include <linux/delay.h>
16#include <linux/slab.h>
17#include <linux/crc16.h>
18#include <linux/uaccess.h>
19
20#define CRC16_INIT 0
21#define CRC16_VALID 0xb001
22
23#include "../w1.h"
24#include "../w1_int.h"
25#include "../w1_family.h"
26
27MODULE_LICENSE("GPL");
28MODULE_AUTHOR("Markus Franke <franke.m@sebakmt.com>, <franm@hrz.tu-chemnitz.de>");
29MODULE_DESCRIPTION("w1 family 1C driver for DS28E04, 4kb EEPROM and PIO");
30
31/* Allow the strong pullup to be disabled, but default to enabled.
32 * If it was disabled a parasite powered device might not get the required
33 * current to copy the data from the scratchpad to EEPROM. If it is enabled
34 * parasite powered devices have a better chance of getting the current
35 * required.
36 */
37static int w1_strong_pullup = 1;
38module_param_named(strong_pullup, w1_strong_pullup, int, 0);
39
40/* enable/disable CRC checking on DS28E04-100 memory accesses */
41static char w1_enable_crccheck = 1;
42
43#define W1_EEPROM_SIZE 512
44#define W1_PAGE_COUNT 16
45#define W1_PAGE_SIZE 32
46#define W1_PAGE_BITS 5
47#define W1_PAGE_MASK 0x1F
48
49#define W1_F1C_READ_EEPROM 0xF0
50#define W1_F1C_WRITE_SCRATCH 0x0F
51#define W1_F1C_READ_SCRATCH 0xAA
52#define W1_F1C_COPY_SCRATCH 0x55
53#define W1_F1C_ACCESS_WRITE 0x5A
54
55#define W1_1C_REG_LOGIC_STATE 0x220
56
57struct w1_f1C_data {
58 u8 memory[W1_EEPROM_SIZE];
59 u32 validcrc;
60};
61
62/**
63 * Check the file size bounds and adjusts count as needed.
64 * This would not be needed if the file size didn't reset to 0 after a write.
65 */
66static inline size_t w1_f1C_fix_count(loff_t off, size_t count, size_t size)
67{
68 if (off > size)
69 return 0;
70
71 if ((off + count) > size)
72 return size - off;
73
74 return count;
75}
76
77static int w1_f1C_refresh_block(struct w1_slave *sl, struct w1_f1C_data *data,
78 int block)
79{
80 u8 wrbuf[3];
81 int off = block * W1_PAGE_SIZE;
82
83 if (data->validcrc & (1 << block))
84 return 0;
85
86 if (w1_reset_select_slave(sl)) {
87 data->validcrc = 0;
88 return -EIO;
89 }
90
91 wrbuf[0] = W1_F1C_READ_EEPROM;
92 wrbuf[1] = off & 0xff;
93 wrbuf[2] = off >> 8;
94 w1_write_block(sl->master, wrbuf, 3);
95 w1_read_block(sl->master, &data->memory[off], W1_PAGE_SIZE);
96
97 /* cache the block if the CRC is valid */
98 if (crc16(CRC16_INIT, &data->memory[off], W1_PAGE_SIZE) == CRC16_VALID)
99 data->validcrc |= (1 << block);
100
101 return 0;
102}
103
104static int w1_f1C_read(struct w1_slave *sl, int addr, int len, char *data)
105{
106 u8 wrbuf[3];
107
108 /* read directly from the EEPROM */
109 if (w1_reset_select_slave(sl))
110 return -EIO;
111
112 wrbuf[0] = W1_F1C_READ_EEPROM;
113 wrbuf[1] = addr & 0xff;
114 wrbuf[2] = addr >> 8;
115
116 w1_write_block(sl->master, wrbuf, sizeof(wrbuf));
117 return w1_read_block(sl->master, data, len);
118}
119
120static ssize_t w1_f1C_read_bin(struct file *filp, struct kobject *kobj,
121 struct bin_attribute *bin_attr,
122 char *buf, loff_t off, size_t count)
123{
124 struct w1_slave *sl = kobj_to_w1_slave(kobj);
125 struct w1_f1C_data *data = sl->family_data;
126 int i, min_page, max_page;
127
128 count = w1_f1C_fix_count(off, count, W1_EEPROM_SIZE);
129 if (count == 0)
130 return 0;
131
132 mutex_lock(&sl->master->mutex);
133
134 if (w1_enable_crccheck) {
135 min_page = (off >> W1_PAGE_BITS);
136 max_page = (off + count - 1) >> W1_PAGE_BITS;
137 for (i = min_page; i <= max_page; i++) {
138 if (w1_f1C_refresh_block(sl, data, i)) {
139 count = -EIO;
140 goto out_up;
141 }
142 }
143 memcpy(buf, &data->memory[off], count);
144 } else {
145 count = w1_f1C_read(sl, off, count, buf);
146 }
147
148out_up:
149 mutex_unlock(&sl->master->mutex);
150
151 return count;
152}
153
154/**
155 * Writes to the scratchpad and reads it back for verification.
156 * Then copies the scratchpad to EEPROM.
157 * The data must be on one page.
158 * The master must be locked.
159 *
160 * @param sl The slave structure
161 * @param addr Address for the write
162 * @param len length must be <= (W1_PAGE_SIZE - (addr & W1_PAGE_MASK))
163 * @param data The data to write
164 * @return 0=Success -1=failure
165 */
166static int w1_f1C_write(struct w1_slave *sl, int addr, int len, const u8 *data)
167{
168 u8 wrbuf[4];
169 u8 rdbuf[W1_PAGE_SIZE + 3];
170 u8 es = (addr + len - 1) & 0x1f;
171 unsigned int tm = 10;
172 int i;
173 struct w1_f1C_data *f1C = sl->family_data;
174
175 /* Write the data to the scratchpad */
176 if (w1_reset_select_slave(sl))
177 return -1;
178
179 wrbuf[0] = W1_F1C_WRITE_SCRATCH;
180 wrbuf[1] = addr & 0xff;
181 wrbuf[2] = addr >> 8;
182
183 w1_write_block(sl->master, wrbuf, 3);
184 w1_write_block(sl->master, data, len);
185
186 /* Read the scratchpad and verify */
187 if (w1_reset_select_slave(sl))
188 return -1;
189
190 w1_write_8(sl->master, W1_F1C_READ_SCRATCH);
191 w1_read_block(sl->master, rdbuf, len + 3);
192
193 /* Compare what was read against the data written */
194 if ((rdbuf[0] != wrbuf[1]) || (rdbuf[1] != wrbuf[2]) ||
195 (rdbuf[2] != es) || (memcmp(data, &rdbuf[3], len) != 0))
196 return -1;
197
198 /* Copy the scratchpad to EEPROM */
199 if (w1_reset_select_slave(sl))
200 return -1;
201
202 wrbuf[0] = W1_F1C_COPY_SCRATCH;
203 wrbuf[3] = es;
204
205 for (i = 0; i < sizeof(wrbuf); ++i) {
206 /* issue 10ms strong pullup (or delay) on the last byte
207 for writing the data from the scratchpad to EEPROM */
208 if (w1_strong_pullup && i == sizeof(wrbuf)-1)
209 w1_next_pullup(sl->master, tm);
210
211 w1_write_8(sl->master, wrbuf[i]);
212 }
213
214 if (!w1_strong_pullup)
215 msleep(tm);
216
217 if (w1_enable_crccheck) {
218 /* invalidate cached data */
219 f1C->validcrc &= ~(1 << (addr >> W1_PAGE_BITS));
220 }
221
222 /* Reset the bus to wake up the EEPROM (this may not be needed) */
223 w1_reset_bus(sl->master);
224
225 return 0;
226}
227
228static ssize_t w1_f1C_write_bin(struct file *filp, struct kobject *kobj,
229 struct bin_attribute *bin_attr,
230 char *buf, loff_t off, size_t count)
231
232{
233 struct w1_slave *sl = kobj_to_w1_slave(kobj);
234 int addr, len, idx;
235
236 count = w1_f1C_fix_count(off, count, W1_EEPROM_SIZE);
237 if (count == 0)
238 return 0;
239
240 if (w1_enable_crccheck) {
241 /* can only write full blocks in cached mode */
242 if ((off & W1_PAGE_MASK) || (count & W1_PAGE_MASK)) {
243 dev_err(&sl->dev, "invalid offset/count off=%d cnt=%zd\n",
244 (int)off, count);
245 return -EINVAL;
246 }
247
248 /* make sure the block CRCs are valid */
249 for (idx = 0; idx < count; idx += W1_PAGE_SIZE) {
250 if (crc16(CRC16_INIT, &buf[idx], W1_PAGE_SIZE)
251 != CRC16_VALID) {
252 dev_err(&sl->dev, "bad CRC at offset %d\n",
253 (int)off);
254 return -EINVAL;
255 }
256 }
257 }
258
259 mutex_lock(&sl->master->mutex);
260
261 /* Can only write data to one page at a time */
262 idx = 0;
263 while (idx < count) {
264 addr = off + idx;
265 len = W1_PAGE_SIZE - (addr & W1_PAGE_MASK);
266 if (len > (count - idx))
267 len = count - idx;
268
269 if (w1_f1C_write(sl, addr, len, &buf[idx]) < 0) {
270 count = -EIO;
271 goto out_up;
272 }
273 idx += len;
274 }
275
276out_up:
277 mutex_unlock(&sl->master->mutex);
278
279 return count;
280}
281
282static ssize_t w1_f1C_read_pio(struct file *filp, struct kobject *kobj,
283 struct bin_attribute *bin_attr,
284 char *buf, loff_t off, size_t count)
285
286{
287 struct w1_slave *sl = kobj_to_w1_slave(kobj);
288 int ret;
289
290 /* check arguments */
291 if (off != 0 || count != 1 || buf == NULL)
292 return -EINVAL;
293
294 mutex_lock(&sl->master->mutex);
295 ret = w1_f1C_read(sl, W1_1C_REG_LOGIC_STATE, count, buf);
296 mutex_unlock(&sl->master->mutex);
297
298 return ret;
299}
300
301static ssize_t w1_f1C_write_pio(struct file *filp, struct kobject *kobj,
302 struct bin_attribute *bin_attr,
303 char *buf, loff_t off, size_t count)
304
305{
306 struct w1_slave *sl = kobj_to_w1_slave(kobj);
307 u8 wrbuf[3];
308 u8 ack;
309
310 /* check arguments */
311 if (off != 0 || count != 1 || buf == NULL)
312 return -EINVAL;
313
314 mutex_lock(&sl->master->mutex);
315
316 /* Write the PIO data */
317 if (w1_reset_select_slave(sl)) {
318 mutex_unlock(&sl->master->mutex);
319 return -1;
320 }
321
322 /* set bit 7..2 to value '1' */
323 *buf = *buf | 0xFC;
324
325 wrbuf[0] = W1_F1C_ACCESS_WRITE;
326 wrbuf[1] = *buf;
327 wrbuf[2] = ~(*buf);
328 w1_write_block(sl->master, wrbuf, 3);
329
330 w1_read_block(sl->master, &ack, sizeof(ack));
331
332 mutex_unlock(&sl->master->mutex);
333
334 /* check for acknowledgement */
335 if (ack != 0xAA)
336 return -EIO;
337
338 return count;
339}
340
341static ssize_t w1_f1C_show_crccheck(struct device *dev,
342 struct device_attribute *attr, char *buf)
343{
344 if (put_user(w1_enable_crccheck + 0x30, buf))
345 return -EFAULT;
346
347 return sizeof(w1_enable_crccheck);
348}
349
350static ssize_t w1_f1C_store_crccheck(struct device *dev,
351 struct device_attribute *attr,
352 const char *buf, size_t count)
353{
354 char val;
355
356 if (count != 1 || !buf)
357 return -EINVAL;
358
359 if (get_user(val, buf))
360 return -EFAULT;
361
362 /* convert to decimal */
363 val = val - 0x30;
364 if (val != 0 && val != 1)
365 return -EINVAL;
366
367 /* set the new value */
368 w1_enable_crccheck = val;
369
370 return sizeof(w1_enable_crccheck);
371}
372
373#define NB_SYSFS_BIN_FILES 2
374static struct bin_attribute w1_f1C_bin_attr[NB_SYSFS_BIN_FILES] = {
375 {
376 .attr = {
377 .name = "eeprom",
378 .mode = S_IRUGO | S_IWUSR,
379 },
380 .size = W1_EEPROM_SIZE,
381 .read = w1_f1C_read_bin,
382 .write = w1_f1C_write_bin,
383 },
384 {
385 .attr = {
386 .name = "pio",
387 .mode = S_IRUGO | S_IWUSR,
388 },
389 .size = 1,
390 .read = w1_f1C_read_pio,
391 .write = w1_f1C_write_pio,
392 }
393};
394
395static DEVICE_ATTR(crccheck, S_IWUSR | S_IRUGO,
396 w1_f1C_show_crccheck, w1_f1C_store_crccheck);
397
398static int w1_f1C_add_slave(struct w1_slave *sl)
399{
400 int err = 0;
401 int i;
402 struct w1_f1C_data *data = NULL;
403
404 if (w1_enable_crccheck) {
405 data = kzalloc(sizeof(struct w1_f1C_data), GFP_KERNEL);
406 if (!data)
407 return -ENOMEM;
408 sl->family_data = data;
409 }
410
411 /* create binary sysfs attributes */
412 for (i = 0; i < NB_SYSFS_BIN_FILES && !err; ++i)
413 err = sysfs_create_bin_file(
414 &sl->dev.kobj, &(w1_f1C_bin_attr[i]));
415
416 if (!err) {
417 /* create device attributes */
418 err = device_create_file(&sl->dev, &dev_attr_crccheck);
419 }
420
421 if (err) {
422 /* remove binary sysfs attributes */
423 for (i = 0; i < NB_SYSFS_BIN_FILES; ++i)
424 sysfs_remove_bin_file(
425 &sl->dev.kobj, &(w1_f1C_bin_attr[i]));
426
427 kfree(data);
428 }
429
430 return err;
431}
432
433static void w1_f1C_remove_slave(struct w1_slave *sl)
434{
435 int i;
436
437 kfree(sl->family_data);
438 sl->family_data = NULL;
439
440 /* remove device attributes */
441 device_remove_file(&sl->dev, &dev_attr_crccheck);
442
443 /* remove binary sysfs attributes */
444 for (i = 0; i < NB_SYSFS_BIN_FILES; ++i)
445 sysfs_remove_bin_file(&sl->dev.kobj, &(w1_f1C_bin_attr[i]));
446}
447
448static struct w1_family_ops w1_f1C_fops = {
449 .add_slave = w1_f1C_add_slave,
450 .remove_slave = w1_f1C_remove_slave,
451};
452
453static struct w1_family w1_family_1C = {
454 .fid = W1_FAMILY_DS28E04,
455 .fops = &w1_f1C_fops,
456};
457
458static int __init w1_f1C_init(void)
459{
460 return w1_register_family(&w1_family_1C);
461}
462
463static void __exit w1_f1C_fini(void)
464{
465 w1_unregister_family(&w1_family_1C);
466}
467
468module_init(w1_f1C_init);
469module_exit(w1_f1C_fini);
diff --git a/drivers/w1/w1_family.h b/drivers/w1/w1_family.h
index 874aeb05011..b00ada44a89 100644
--- a/drivers/w1/w1_family.h
+++ b/drivers/w1/w1_family.h
@@ -30,6 +30,7 @@
30#define W1_FAMILY_SMEM_01 0x01 30#define W1_FAMILY_SMEM_01 0x01
31#define W1_FAMILY_SMEM_81 0x81 31#define W1_FAMILY_SMEM_81 0x81
32#define W1_THERM_DS18S20 0x10 32#define W1_THERM_DS18S20 0x10
33#define W1_FAMILY_DS28E04 0x1C
33#define W1_COUNTER_DS2423 0x1D 34#define W1_COUNTER_DS2423 0x1D
34#define W1_THERM_DS1822 0x22 35#define W1_THERM_DS1822 0x22
35#define W1_EEPROM_DS2433 0x23 36#define W1_EEPROM_DS2433 0x23