diff options
author | Bernhard Weirich <bernhard.weirich@riedel.net> | 2008-10-16 01:05:11 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-10-16 14:21:51 -0400 |
commit | d8273674721faaf84bec2190c0c7a82972b37f73 (patch) | |
tree | 30d3221ef2363c56adccb299f33b6d8601f22045 /drivers/w1/slaves | |
parent | ade6d810b585d749db24d734947a30a29470cccd (diff) |
w1: new driver. DS2431 chip
[akpm@linux-foundation.org: minor fixlets and cleanups]
Signed-off-by: Bernhard Weirich <bernhard.weirich@riedel.net>
Signed-off-by: Evgeniy Polyakov <johnpol@2ka.mipt.ru>
Cc: Ben Gardner <bgardner@wabtec.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/w1/slaves')
-rw-r--r-- | drivers/w1/slaves/w1_ds2431.c | 312 |
1 files changed, 312 insertions, 0 deletions
diff --git a/drivers/w1/slaves/w1_ds2431.c b/drivers/w1/slaves/w1_ds2431.c new file mode 100644 index 000000000000..2c6c0cf6a20f --- /dev/null +++ b/drivers/w1/slaves/w1_ds2431.c | |||
@@ -0,0 +1,312 @@ | |||
1 | /* | ||
2 | * w1_ds2431.c - w1 family 2d (DS2431) driver | ||
3 | * | ||
4 | * Copyright (c) 2008 Bernhard Weirich <bernhard.weirich@riedel.net> | ||
5 | * | ||
6 | * Heavily inspired by w1_DS2433 driver from Ben Gardner <bgardner@wabtec.com> | ||
7 | * | ||
8 | * This source code is licensed under the GNU General Public License, | ||
9 | * Version 2. See the file COPYING for more details. | ||
10 | */ | ||
11 | |||
12 | #include <linux/kernel.h> | ||
13 | #include <linux/module.h> | ||
14 | #include <linux/moduleparam.h> | ||
15 | #include <linux/device.h> | ||
16 | #include <linux/types.h> | ||
17 | #include <linux/delay.h> | ||
18 | |||
19 | #include "../w1.h" | ||
20 | #include "../w1_int.h" | ||
21 | #include "../w1_family.h" | ||
22 | |||
23 | #define W1_F2D_EEPROM_SIZE 128 | ||
24 | #define W1_F2D_PAGE_COUNT 4 | ||
25 | #define W1_F2D_PAGE_BITS 5 | ||
26 | #define W1_F2D_PAGE_SIZE (1<<W1_F2D_PAGE_BITS) | ||
27 | #define W1_F2D_PAGE_MASK 0x1F | ||
28 | |||
29 | #define W1_F2D_SCRATCH_BITS 3 | ||
30 | #define W1_F2D_SCRATCH_SIZE (1<<W1_F2D_SCRATCH_BITS) | ||
31 | #define W1_F2D_SCRATCH_MASK (W1_F2D_SCRATCH_SIZE-1) | ||
32 | |||
33 | #define W1_F2D_READ_EEPROM 0xF0 | ||
34 | #define W1_F2D_WRITE_SCRATCH 0x0F | ||
35 | #define W1_F2D_READ_SCRATCH 0xAA | ||
36 | #define W1_F2D_COPY_SCRATCH 0x55 | ||
37 | |||
38 | |||
39 | #define W1_F2D_TPROG_MS 11 | ||
40 | |||
41 | #define W1_F2D_READ_RETRIES 10 | ||
42 | #define W1_F2D_READ_MAXLEN 8 | ||
43 | |||
44 | /* | ||
45 | * Check the file size bounds and adjusts count as needed. | ||
46 | * This would not be needed if the file size didn't reset to 0 after a write. | ||
47 | */ | ||
48 | static inline size_t w1_f2d_fix_count(loff_t off, size_t count, size_t size) | ||
49 | { | ||
50 | if (off > size) | ||
51 | return 0; | ||
52 | |||
53 | if ((off + count) > size) | ||
54 | return size - off; | ||
55 | |||
56 | return count; | ||
57 | } | ||
58 | |||
59 | /* | ||
60 | * Read a block from W1 ROM two times and compares the results. | ||
61 | * If they are equal they are returned, otherwise the read | ||
62 | * is repeated W1_F2D_READ_RETRIES times. | ||
63 | * | ||
64 | * count must not exceed W1_F2D_READ_MAXLEN. | ||
65 | */ | ||
66 | static int w1_f2d_readblock(struct w1_slave *sl, int off, int count, char *buf) | ||
67 | { | ||
68 | u8 wrbuf[3]; | ||
69 | u8 cmp[W1_F2D_READ_MAXLEN]; | ||
70 | int tries = W1_F2D_READ_RETRIES; | ||
71 | |||
72 | do { | ||
73 | wrbuf[0] = W1_F2D_READ_EEPROM; | ||
74 | wrbuf[1] = off & 0xff; | ||
75 | wrbuf[2] = off >> 8; | ||
76 | |||
77 | if (w1_reset_select_slave(sl)) | ||
78 | return -1; | ||
79 | |||
80 | w1_write_block(sl->master, wrbuf, 3); | ||
81 | w1_read_block(sl->master, buf, count); | ||
82 | |||
83 | if (w1_reset_select_slave(sl)) | ||
84 | return -1; | ||
85 | |||
86 | w1_write_block(sl->master, wrbuf, 3); | ||
87 | w1_read_block(sl->master, cmp, count); | ||
88 | |||
89 | if (!memcmp(cmp, buf, count)) | ||
90 | return 0; | ||
91 | } while (--tries); | ||
92 | |||
93 | dev_err(&sl->dev, "proof reading failed %d times\n", | ||
94 | W1_F2D_READ_RETRIES); | ||
95 | |||
96 | return -1; | ||
97 | } | ||
98 | |||
99 | static ssize_t w1_f2d_read_bin(struct kobject *kobj, | ||
100 | struct bin_attribute *bin_attr, | ||
101 | char *buf, loff_t off, size_t count) | ||
102 | { | ||
103 | struct w1_slave *sl = kobj_to_w1_slave(kobj); | ||
104 | int todo = count; | ||
105 | |||
106 | count = w1_f2d_fix_count(off, count, W1_F2D_EEPROM_SIZE); | ||
107 | if (count == 0) | ||
108 | return 0; | ||
109 | |||
110 | mutex_lock(&sl->master->mutex); | ||
111 | |||
112 | /* read directly from the EEPROM in chunks of W1_F2D_READ_MAXLEN */ | ||
113 | while (todo > 0) { | ||
114 | int block_read; | ||
115 | |||
116 | if (todo >= W1_F2D_READ_MAXLEN) | ||
117 | block_read = W1_F2D_READ_MAXLEN; | ||
118 | else | ||
119 | block_read = todo; | ||
120 | |||
121 | if (w1_f2d_readblock(sl, off, block_read, buf) < 0) | ||
122 | count = -EIO; | ||
123 | |||
124 | todo -= W1_F2D_READ_MAXLEN; | ||
125 | buf += W1_F2D_READ_MAXLEN; | ||
126 | off += W1_F2D_READ_MAXLEN; | ||
127 | } | ||
128 | |||
129 | mutex_unlock(&sl->master->mutex); | ||
130 | |||
131 | return count; | ||
132 | } | ||
133 | |||
134 | /* | ||
135 | * Writes to the scratchpad and reads it back for verification. | ||
136 | * Then copies the scratchpad to EEPROM. | ||
137 | * The data must be aligned at W1_F2D_SCRATCH_SIZE bytes and | ||
138 | * must be W1_F2D_SCRATCH_SIZE bytes long. | ||
139 | * The master must be locked. | ||
140 | * | ||
141 | * @param sl The slave structure | ||
142 | * @param addr Address for the write | ||
143 | * @param len length must be <= (W1_F2D_PAGE_SIZE - (addr & W1_F2D_PAGE_MASK)) | ||
144 | * @param data The data to write | ||
145 | * @return 0=Success -1=failure | ||
146 | */ | ||
147 | static int w1_f2d_write(struct w1_slave *sl, int addr, int len, const u8 *data) | ||
148 | { | ||
149 | int tries = W1_F2D_READ_RETRIES; | ||
150 | u8 wrbuf[4]; | ||
151 | u8 rdbuf[W1_F2D_SCRATCH_SIZE + 3]; | ||
152 | u8 es = (addr + len - 1) % W1_F2D_SCRATCH_SIZE; | ||
153 | |||
154 | retry: | ||
155 | |||
156 | /* Write the data to the scratchpad */ | ||
157 | if (w1_reset_select_slave(sl)) | ||
158 | return -1; | ||
159 | |||
160 | wrbuf[0] = W1_F2D_WRITE_SCRATCH; | ||
161 | wrbuf[1] = addr & 0xff; | ||
162 | wrbuf[2] = addr >> 8; | ||
163 | |||
164 | w1_write_block(sl->master, wrbuf, 3); | ||
165 | w1_write_block(sl->master, data, len); | ||
166 | |||
167 | /* Read the scratchpad and verify */ | ||
168 | if (w1_reset_select_slave(sl)) | ||
169 | return -1; | ||
170 | |||
171 | w1_write_8(sl->master, W1_F2D_READ_SCRATCH); | ||
172 | w1_read_block(sl->master, rdbuf, len + 3); | ||
173 | |||
174 | /* Compare what was read against the data written */ | ||
175 | if ((rdbuf[0] != wrbuf[1]) || (rdbuf[1] != wrbuf[2]) || | ||
176 | (rdbuf[2] != es) || (memcmp(data, &rdbuf[3], len) != 0)) { | ||
177 | |||
178 | if (--tries) | ||
179 | goto retry; | ||
180 | |||
181 | dev_err(&sl->dev, | ||
182 | "could not write to eeprom, scratchpad compare failed %d times\n", | ||
183 | W1_F2D_READ_RETRIES); | ||
184 | |||
185 | return -1; | ||
186 | } | ||
187 | |||
188 | /* Copy the scratchpad to EEPROM */ | ||
189 | if (w1_reset_select_slave(sl)) | ||
190 | return -1; | ||
191 | |||
192 | wrbuf[0] = W1_F2D_COPY_SCRATCH; | ||
193 | wrbuf[3] = es; | ||
194 | w1_write_block(sl->master, wrbuf, 4); | ||
195 | |||
196 | /* Sleep for tprog ms to wait for the write to complete */ | ||
197 | msleep(W1_F2D_TPROG_MS); | ||
198 | |||
199 | /* Reset the bus to wake up the EEPROM */ | ||
200 | w1_reset_bus(sl->master); | ||
201 | |||
202 | return 0; | ||
203 | } | ||
204 | |||
205 | static ssize_t w1_f2d_write_bin(struct kobject *kobj, | ||
206 | struct bin_attribute *bin_attr, | ||
207 | char *buf, loff_t off, size_t count) | ||
208 | { | ||
209 | struct w1_slave *sl = kobj_to_w1_slave(kobj); | ||
210 | int addr, len; | ||
211 | int copy; | ||
212 | |||
213 | count = w1_f2d_fix_count(off, count, W1_F2D_EEPROM_SIZE); | ||
214 | if (count == 0) | ||
215 | return 0; | ||
216 | |||
217 | mutex_lock(&sl->master->mutex); | ||
218 | |||
219 | /* Can only write data in blocks of the size of the scratchpad */ | ||
220 | addr = off; | ||
221 | len = count; | ||
222 | while (len > 0) { | ||
223 | |||
224 | /* if len too short or addr not aligned */ | ||
225 | if (len < W1_F2D_SCRATCH_SIZE || addr & W1_F2D_SCRATCH_MASK) { | ||
226 | char tmp[W1_F2D_SCRATCH_SIZE]; | ||
227 | |||
228 | /* read the block and update the parts to be written */ | ||
229 | if (w1_f2d_readblock(sl, addr & ~W1_F2D_SCRATCH_MASK, | ||
230 | W1_F2D_SCRATCH_SIZE, tmp)) { | ||
231 | count = -EIO; | ||
232 | goto out_up; | ||
233 | } | ||
234 | |||
235 | /* copy at most to the boundary of the PAGE or len */ | ||
236 | copy = W1_F2D_SCRATCH_SIZE - | ||
237 | (addr & W1_F2D_SCRATCH_MASK); | ||
238 | |||
239 | if (copy > len) | ||
240 | copy = len; | ||
241 | |||
242 | memcpy(&tmp[addr & W1_F2D_SCRATCH_MASK], buf, copy); | ||
243 | if (w1_f2d_write(sl, addr & ~W1_F2D_SCRATCH_MASK, | ||
244 | W1_F2D_SCRATCH_SIZE, tmp) < 0) { | ||
245 | count = -EIO; | ||
246 | goto out_up; | ||
247 | } | ||
248 | } else { | ||
249 | |||
250 | copy = W1_F2D_SCRATCH_SIZE; | ||
251 | if (w1_f2d_write(sl, addr, copy, buf) < 0) { | ||
252 | count = -EIO; | ||
253 | goto out_up; | ||
254 | } | ||
255 | } | ||
256 | buf += copy; | ||
257 | addr += copy; | ||
258 | len -= copy; | ||
259 | } | ||
260 | |||
261 | out_up: | ||
262 | mutex_unlock(&sl->master->mutex); | ||
263 | |||
264 | return count; | ||
265 | } | ||
266 | |||
267 | static struct bin_attribute w1_f2d_bin_attr = { | ||
268 | .attr = { | ||
269 | .name = "eeprom", | ||
270 | .mode = S_IRUGO | S_IWUSR, | ||
271 | }, | ||
272 | .size = W1_F2D_EEPROM_SIZE, | ||
273 | .read = w1_f2d_read_bin, | ||
274 | .write = w1_f2d_write_bin, | ||
275 | }; | ||
276 | |||
277 | static int w1_f2d_add_slave(struct w1_slave *sl) | ||
278 | { | ||
279 | return sysfs_create_bin_file(&sl->dev.kobj, &w1_f2d_bin_attr); | ||
280 | } | ||
281 | |||
282 | static void w1_f2d_remove_slave(struct w1_slave *sl) | ||
283 | { | ||
284 | sysfs_remove_bin_file(&sl->dev.kobj, &w1_f2d_bin_attr); | ||
285 | } | ||
286 | |||
287 | static struct w1_family_ops w1_f2d_fops = { | ||
288 | .add_slave = w1_f2d_add_slave, | ||
289 | .remove_slave = w1_f2d_remove_slave, | ||
290 | }; | ||
291 | |||
292 | static struct w1_family w1_family_2d = { | ||
293 | .fid = W1_EEPROM_DS2431, | ||
294 | .fops = &w1_f2d_fops, | ||
295 | }; | ||
296 | |||
297 | static int __init w1_f2d_init(void) | ||
298 | { | ||
299 | return w1_register_family(&w1_family_2d); | ||
300 | } | ||
301 | |||
302 | static void __exit w1_f2d_fini(void) | ||
303 | { | ||
304 | w1_unregister_family(&w1_family_2d); | ||
305 | } | ||
306 | |||
307 | module_init(w1_f2d_init); | ||
308 | module_exit(w1_f2d_fini); | ||
309 | |||
310 | MODULE_LICENSE("GPL"); | ||
311 | MODULE_AUTHOR("Bernhard Weirich <bernhard.weirich@riedel.net>"); | ||
312 | MODULE_DESCRIPTION("w1 family 2d driver for DS2431, 1kb EEPROM"); | ||