diff options
Diffstat (limited to 'drivers/w1/w1_ds2433.c')
-rw-r--r-- | drivers/w1/w1_ds2433.c | 117 |
1 files changed, 111 insertions, 6 deletions
diff --git a/drivers/w1/w1_ds2433.c b/drivers/w1/w1_ds2433.c index 9ec9163a0a9b..b7c24b34d270 100644 --- a/drivers/w1/w1_ds2433.c +++ b/drivers/w1/w1_ds2433.c | |||
@@ -3,9 +3,8 @@ | |||
3 | * | 3 | * |
4 | * Copyright (c) 2005 Ben Gardner <bgardner@wabtec.com> | 4 | * Copyright (c) 2005 Ben Gardner <bgardner@wabtec.com> |
5 | * | 5 | * |
6 | * This program is free software; you can redistribute it and/or modify | 6 | * This source code is licensed under the GNU General Public License, |
7 | * it under the smems of the GNU General Public License as published by | 7 | * Version 2. See the file COPYING for more details. |
8 | * the Free Software Foundation; version 2 of the License. | ||
9 | */ | 8 | */ |
10 | 9 | ||
11 | #include <linux/kernel.h> | 10 | #include <linux/kernel.h> |
@@ -14,6 +13,9 @@ | |||
14 | #include <linux/device.h> | 13 | #include <linux/device.h> |
15 | #include <linux/types.h> | 14 | #include <linux/types.h> |
16 | #include <linux/delay.h> | 15 | #include <linux/delay.h> |
16 | #ifdef CONFIG_W1_F23_CRC | ||
17 | #include <linux/crc16.h> | ||
18 | #endif | ||
17 | 19 | ||
18 | #include "w1.h" | 20 | #include "w1.h" |
19 | #include "w1_io.h" | 21 | #include "w1_io.h" |
@@ -25,18 +27,26 @@ MODULE_AUTHOR("Ben Gardner <bgardner@wabtec.com>"); | |||
25 | MODULE_DESCRIPTION("w1 family 23 driver for DS2433, 4kb EEPROM"); | 27 | MODULE_DESCRIPTION("w1 family 23 driver for DS2433, 4kb EEPROM"); |
26 | 28 | ||
27 | #define W1_EEPROM_SIZE 512 | 29 | #define W1_EEPROM_SIZE 512 |
30 | #define W1_PAGE_COUNT 16 | ||
28 | #define W1_PAGE_SIZE 32 | 31 | #define W1_PAGE_SIZE 32 |
29 | #define W1_PAGE_BITS 5 | 32 | #define W1_PAGE_BITS 5 |
30 | #define W1_PAGE_MASK 0x1F | 33 | #define W1_PAGE_MASK 0x1F |
31 | 34 | ||
35 | #define W1_F23_TIME 300 | ||
36 | |||
32 | #define W1_F23_READ_EEPROM 0xF0 | 37 | #define W1_F23_READ_EEPROM 0xF0 |
33 | #define W1_F23_WRITE_SCRATCH 0x0F | 38 | #define W1_F23_WRITE_SCRATCH 0x0F |
34 | #define W1_F23_READ_SCRATCH 0xAA | 39 | #define W1_F23_READ_SCRATCH 0xAA |
35 | #define W1_F23_COPY_SCRATCH 0x55 | 40 | #define W1_F23_COPY_SCRATCH 0x55 |
36 | 41 | ||
42 | struct w1_f23_data { | ||
43 | u8 memory[W1_EEPROM_SIZE]; | ||
44 | u32 validcrc; | ||
45 | }; | ||
46 | |||
37 | /** | 47 | /** |
38 | * Check the file size bounds and adjusts count as needed. | 48 | * Check the file size bounds and adjusts count as needed. |
39 | * This may not be needed if the sysfs layer checks bounds. | 49 | * This would not be needed if the file size didn't reset to 0 after a write. |
40 | */ | 50 | */ |
41 | static inline size_t w1_f23_fix_count(loff_t off, size_t count, size_t size) | 51 | static inline size_t w1_f23_fix_count(loff_t off, size_t count, size_t size) |
42 | { | 52 | { |
@@ -49,10 +59,45 @@ static inline size_t w1_f23_fix_count(loff_t off, size_t count, size_t size) | |||
49 | return count; | 59 | return count; |
50 | } | 60 | } |
51 | 61 | ||
52 | static ssize_t w1_f23_read_bin(struct kobject *kobj, char *buf, loff_t off, size_t count) | 62 | #ifdef CONFIG_W1_F23_CRC |
63 | static int w1_f23_refresh_block(struct w1_slave *sl, struct w1_f23_data *data, | ||
64 | int block) | ||
65 | { | ||
66 | u8 wrbuf[3]; | ||
67 | int off = block * W1_PAGE_SIZE; | ||
68 | |||
69 | if (data->validcrc & (1 << block)) | ||
70 | return 0; | ||
71 | |||
72 | if (w1_reset_select_slave(sl)) { | ||
73 | data->validcrc = 0; | ||
74 | return -EIO; | ||
75 | } | ||
76 | |||
77 | wrbuf[0] = W1_F23_READ_EEPROM; | ||
78 | wrbuf[1] = off & 0xff; | ||
79 | wrbuf[2] = off >> 8; | ||
80 | w1_write_block(sl->master, wrbuf, 3); | ||
81 | w1_read_block(sl->master, &data->memory[off], W1_PAGE_SIZE); | ||
82 | |||
83 | /* cache the block if the CRC is valid */ | ||
84 | if (crc16(CRC16_INIT, &data->memory[off], W1_PAGE_SIZE) == CRC16_VALID) | ||
85 | data->validcrc |= (1 << block); | ||
86 | |||
87 | return 0; | ||
88 | } | ||
89 | #endif /* CONFIG_W1_F23_CRC */ | ||
90 | |||
91 | static ssize_t w1_f23_read_bin(struct kobject *kobj, char *buf, loff_t off, | ||
92 | size_t count) | ||
53 | { | 93 | { |
54 | struct w1_slave *sl = kobj_to_w1_slave(kobj); | 94 | struct w1_slave *sl = kobj_to_w1_slave(kobj); |
95 | #ifdef CONFIG_W1_F23_CRC | ||
96 | struct w1_f23_data *data = sl->family_data; | ||
97 | int i, min_page, max_page; | ||
98 | #else | ||
55 | u8 wrbuf[3]; | 99 | u8 wrbuf[3]; |
100 | #endif | ||
56 | 101 | ||
57 | if ((count = w1_f23_fix_count(off, count, W1_EEPROM_SIZE)) == 0) | 102 | if ((count = w1_f23_fix_count(off, count, W1_EEPROM_SIZE)) == 0) |
58 | return 0; | 103 | return 0; |
@@ -63,6 +108,20 @@ static ssize_t w1_f23_read_bin(struct kobject *kobj, char *buf, loff_t off, size | |||
63 | goto out_dec; | 108 | goto out_dec; |
64 | } | 109 | } |
65 | 110 | ||
111 | #ifdef CONFIG_W1_F23_CRC | ||
112 | |||
113 | min_page = (off >> W1_PAGE_BITS); | ||
114 | max_page = (off + count - 1) >> W1_PAGE_BITS; | ||
115 | for (i = min_page; i <= max_page; i++) { | ||
116 | if (w1_f23_refresh_block(sl, data, i)) { | ||
117 | count = -EIO; | ||
118 | goto out_up; | ||
119 | } | ||
120 | } | ||
121 | memcpy(buf, &data->memory[off], count); | ||
122 | |||
123 | #else /* CONFIG_W1_F23_CRC */ | ||
124 | |||
66 | /* read directly from the EEPROM */ | 125 | /* read directly from the EEPROM */ |
67 | if (w1_reset_select_slave(sl)) { | 126 | if (w1_reset_select_slave(sl)) { |
68 | count = -EIO; | 127 | count = -EIO; |
@@ -75,6 +134,8 @@ static ssize_t w1_f23_read_bin(struct kobject *kobj, char *buf, loff_t off, size | |||
75 | w1_write_block(sl->master, wrbuf, 3); | 134 | w1_write_block(sl->master, wrbuf, 3); |
76 | w1_read_block(sl->master, buf, count); | 135 | w1_read_block(sl->master, buf, count); |
77 | 136 | ||
137 | #endif /* CONFIG_W1_F23_CRC */ | ||
138 | |||
78 | out_up: | 139 | out_up: |
79 | up(&sl->master->mutex); | 140 | up(&sl->master->mutex); |
80 | out_dec: | 141 | out_dec: |
@@ -85,6 +146,8 @@ out_dec: | |||
85 | 146 | ||
86 | /** | 147 | /** |
87 | * Writes to the scratchpad and reads it back for verification. | 148 | * Writes to the scratchpad and reads it back for verification. |
149 | * Then copies the scratchpad to EEPROM. | ||
150 | * The data must be on one page. | ||
88 | * The master must be locked. | 151 | * The master must be locked. |
89 | * | 152 | * |
90 | * @param sl The slave structure | 153 | * @param sl The slave structure |
@@ -148,6 +211,23 @@ static ssize_t w1_f23_write_bin(struct kobject *kobj, char *buf, loff_t off, | |||
148 | if ((count = w1_f23_fix_count(off, count, W1_EEPROM_SIZE)) == 0) | 211 | if ((count = w1_f23_fix_count(off, count, W1_EEPROM_SIZE)) == 0) |
149 | return 0; | 212 | return 0; |
150 | 213 | ||
214 | #ifdef CONFIG_W1_F23_CRC | ||
215 | /* can only write full blocks in cached mode */ | ||
216 | if ((off & W1_PAGE_MASK) || (count & W1_PAGE_MASK)) { | ||
217 | dev_err(&sl->dev, "invalid offset/count off=%d cnt=%d\n", | ||
218 | (int)off, count); | ||
219 | return -EINVAL; | ||
220 | } | ||
221 | |||
222 | /* make sure the block CRCs are valid */ | ||
223 | for (idx = 0; idx < count; idx += W1_PAGE_SIZE) { | ||
224 | if (crc16(CRC16_INIT, &buf[idx], W1_PAGE_SIZE) != CRC16_VALID) { | ||
225 | dev_err(&sl->dev, "bad CRC at offset %d\n", (int)off); | ||
226 | return -EINVAL; | ||
227 | } | ||
228 | } | ||
229 | #endif /* CONFIG_W1_F23_CRC */ | ||
230 | |||
151 | atomic_inc(&sl->refcnt); | 231 | atomic_inc(&sl->refcnt); |
152 | if (down_interruptible(&sl->master->mutex)) { | 232 | if (down_interruptible(&sl->master->mutex)) { |
153 | count = 0; | 233 | count = 0; |
@@ -190,11 +270,36 @@ static struct bin_attribute w1_f23_bin_attr = { | |||
190 | 270 | ||
191 | static int w1_f23_add_slave(struct w1_slave *sl) | 271 | static int w1_f23_add_slave(struct w1_slave *sl) |
192 | { | 272 | { |
193 | return sysfs_create_bin_file(&sl->dev.kobj, &w1_f23_bin_attr); | 273 | int err; |
274 | #ifdef CONFIG_W1_F23_CRC | ||
275 | struct w1_f23_data *data; | ||
276 | |||
277 | data = kmalloc(sizeof(struct w1_f23_data), GFP_KERNEL); | ||
278 | if (!data) | ||
279 | return -ENOMEM; | ||
280 | memset(data, 0, sizeof(struct w1_f23_data)); | ||
281 | sl->family_data = data; | ||
282 | |||
283 | #endif /* CONFIG_W1_F23_CRC */ | ||
284 | |||
285 | err = sysfs_create_bin_file(&sl->dev.kobj, &w1_f23_bin_attr); | ||
286 | |||
287 | #ifdef CONFIG_W1_F23_CRC | ||
288 | if (err) | ||
289 | kfree(data); | ||
290 | #endif /* CONFIG_W1_F23_CRC */ | ||
291 | |||
292 | return err; | ||
194 | } | 293 | } |
195 | 294 | ||
196 | static void w1_f23_remove_slave(struct w1_slave *sl) | 295 | static void w1_f23_remove_slave(struct w1_slave *sl) |
197 | { | 296 | { |
297 | #ifdef CONFIG_W1_F23_CRC | ||
298 | if (sl->family_data) { | ||
299 | kfree(sl->family_data); | ||
300 | sl->family_data = NULL; | ||
301 | } | ||
302 | #endif /* CONFIG_W1_F23_CRC */ | ||
198 | sysfs_remove_bin_file(&sl->dev.kobj, &w1_f23_bin_attr); | 303 | sysfs_remove_bin_file(&sl->dev.kobj, &w1_f23_bin_attr); |
199 | } | 304 | } |
200 | 305 | ||