1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * w1_ds2431.c - w1 family 2d (DS2431) driver |
4 | * |
5 | * Copyright (c) 2008 Bernhard Weirich <bernhard.weirich@riedel.net> |
6 | * |
7 | * Heavily inspired by w1_DS2433 driver from Ben Gardner <bgardner@wabtec.com> |
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 | |
17 | #include <linux/w1.h> |
18 | |
19 | #define W1_EEPROM_DS2431 0x2D |
20 | |
21 | #define W1_F2D_EEPROM_SIZE 128 |
22 | #define W1_F2D_PAGE_COUNT 4 |
23 | #define W1_F2D_PAGE_BITS 5 |
24 | #define W1_F2D_PAGE_SIZE (1<<W1_F2D_PAGE_BITS) |
25 | #define W1_F2D_PAGE_MASK 0x1F |
26 | |
27 | #define W1_F2D_SCRATCH_BITS 3 |
28 | #define W1_F2D_SCRATCH_SIZE (1<<W1_F2D_SCRATCH_BITS) |
29 | #define W1_F2D_SCRATCH_MASK (W1_F2D_SCRATCH_SIZE-1) |
30 | |
31 | #define W1_F2D_READ_EEPROM 0xF0 |
32 | #define W1_F2D_WRITE_SCRATCH 0x0F |
33 | #define W1_F2D_READ_SCRATCH 0xAA |
34 | #define W1_F2D_COPY_SCRATCH 0x55 |
35 | |
36 | |
37 | #define W1_F2D_TPROG_MS 11 |
38 | |
39 | #define W1_F2D_READ_RETRIES 10 |
40 | #define W1_F2D_READ_MAXLEN 8 |
41 | |
42 | /* |
43 | * Check the file size bounds and adjusts count as needed. |
44 | * This would not be needed if the file size didn't reset to 0 after a write. |
45 | */ |
46 | static inline size_t w1_f2d_fix_count(loff_t off, size_t count, size_t size) |
47 | { |
48 | if (off > size) |
49 | return 0; |
50 | |
51 | if ((off + count) > size) |
52 | return size - off; |
53 | |
54 | return count; |
55 | } |
56 | |
57 | /* |
58 | * Read a block from W1 ROM two times and compares the results. |
59 | * If they are equal they are returned, otherwise the read |
60 | * is repeated W1_F2D_READ_RETRIES times. |
61 | * |
62 | * count must not exceed W1_F2D_READ_MAXLEN. |
63 | */ |
64 | static int w1_f2d_readblock(struct w1_slave *sl, int off, int count, char *buf) |
65 | { |
66 | u8 wrbuf[3]; |
67 | u8 cmp[W1_F2D_READ_MAXLEN]; |
68 | int tries = W1_F2D_READ_RETRIES; |
69 | |
70 | do { |
71 | wrbuf[0] = W1_F2D_READ_EEPROM; |
72 | wrbuf[1] = off & 0xff; |
73 | wrbuf[2] = off >> 8; |
74 | |
75 | if (w1_reset_select_slave(sl)) |
76 | return -1; |
77 | |
78 | w1_write_block(sl->master, wrbuf, 3); |
79 | w1_read_block(sl->master, buf, count); |
80 | |
81 | if (w1_reset_select_slave(sl)) |
82 | return -1; |
83 | |
84 | w1_write_block(sl->master, wrbuf, 3); |
85 | w1_read_block(sl->master, cmp, count); |
86 | |
87 | if (!memcmp(p: cmp, q: buf, size: count)) |
88 | return 0; |
89 | } while (--tries); |
90 | |
91 | dev_err(&sl->dev, "proof reading failed %d times\n" , |
92 | W1_F2D_READ_RETRIES); |
93 | |
94 | return -1; |
95 | } |
96 | |
97 | static ssize_t eeprom_read(struct file *filp, struct kobject *kobj, |
98 | struct bin_attribute *bin_attr, char *buf, |
99 | loff_t off, size_t count) |
100 | { |
101 | struct w1_slave *sl = kobj_to_w1_slave(kobj); |
102 | int todo = count; |
103 | |
104 | count = w1_f2d_fix_count(off, count, W1_F2D_EEPROM_SIZE); |
105 | if (count == 0) |
106 | return 0; |
107 | |
108 | mutex_lock(&sl->master->bus_mutex); |
109 | |
110 | /* read directly from the EEPROM in chunks of W1_F2D_READ_MAXLEN */ |
111 | while (todo > 0) { |
112 | int block_read; |
113 | |
114 | if (todo >= W1_F2D_READ_MAXLEN) |
115 | block_read = W1_F2D_READ_MAXLEN; |
116 | else |
117 | block_read = todo; |
118 | |
119 | if (w1_f2d_readblock(sl, off, count: block_read, buf) < 0) |
120 | count = -EIO; |
121 | |
122 | todo -= W1_F2D_READ_MAXLEN; |
123 | buf += W1_F2D_READ_MAXLEN; |
124 | off += W1_F2D_READ_MAXLEN; |
125 | } |
126 | |
127 | mutex_unlock(lock: &sl->master->bus_mutex); |
128 | |
129 | return count; |
130 | } |
131 | |
132 | /* |
133 | * Writes to the scratchpad and reads it back for verification. |
134 | * Then copies the scratchpad to EEPROM. |
135 | * The data must be aligned at W1_F2D_SCRATCH_SIZE bytes and |
136 | * must be W1_F2D_SCRATCH_SIZE bytes long. |
137 | * The master must be locked. |
138 | * |
139 | * @param sl The slave structure |
140 | * @param addr Address for the write |
141 | * @param len length must be <= (W1_F2D_PAGE_SIZE - (addr & W1_F2D_PAGE_MASK)) |
142 | * @param data The data to write |
143 | * @return 0=Success -1=failure |
144 | */ |
145 | static int w1_f2d_write(struct w1_slave *sl, int addr, int len, const u8 *data) |
146 | { |
147 | int tries = W1_F2D_READ_RETRIES; |
148 | u8 wrbuf[4]; |
149 | u8 rdbuf[W1_F2D_SCRATCH_SIZE + 3]; |
150 | u8 es = (addr + len - 1) % W1_F2D_SCRATCH_SIZE; |
151 | |
152 | retry: |
153 | |
154 | /* Write the data to the scratchpad */ |
155 | if (w1_reset_select_slave(sl)) |
156 | return -1; |
157 | |
158 | wrbuf[0] = W1_F2D_WRITE_SCRATCH; |
159 | wrbuf[1] = addr & 0xff; |
160 | wrbuf[2] = addr >> 8; |
161 | |
162 | w1_write_block(sl->master, wrbuf, 3); |
163 | w1_write_block(sl->master, data, len); |
164 | |
165 | /* Read the scratchpad and verify */ |
166 | if (w1_reset_select_slave(sl)) |
167 | return -1; |
168 | |
169 | w1_write_8(sl->master, W1_F2D_READ_SCRATCH); |
170 | w1_read_block(sl->master, rdbuf, len + 3); |
171 | |
172 | /* Compare what was read against the data written */ |
173 | if ((rdbuf[0] != wrbuf[1]) || (rdbuf[1] != wrbuf[2]) || |
174 | (rdbuf[2] != es) || (memcmp(p: data, q: &rdbuf[3], size: len) != 0)) { |
175 | |
176 | if (--tries) |
177 | goto retry; |
178 | |
179 | dev_err(&sl->dev, |
180 | "could not write to eeprom, scratchpad compare failed %d times\n" , |
181 | W1_F2D_READ_RETRIES); |
182 | |
183 | return -1; |
184 | } |
185 | |
186 | /* Copy the scratchpad to EEPROM */ |
187 | if (w1_reset_select_slave(sl)) |
188 | return -1; |
189 | |
190 | wrbuf[0] = W1_F2D_COPY_SCRATCH; |
191 | wrbuf[3] = es; |
192 | w1_write_block(sl->master, wrbuf, 4); |
193 | |
194 | /* Sleep for tprog ms to wait for the write to complete */ |
195 | msleep(W1_F2D_TPROG_MS); |
196 | |
197 | /* Reset the bus to wake up the EEPROM */ |
198 | w1_reset_bus(sl->master); |
199 | |
200 | return 0; |
201 | } |
202 | |
203 | static ssize_t eeprom_write(struct file *filp, struct kobject *kobj, |
204 | struct bin_attribute *bin_attr, char *buf, |
205 | loff_t off, size_t count) |
206 | { |
207 | struct w1_slave *sl = kobj_to_w1_slave(kobj); |
208 | int addr, len; |
209 | int copy; |
210 | |
211 | count = w1_f2d_fix_count(off, count, W1_F2D_EEPROM_SIZE); |
212 | if (count == 0) |
213 | return 0; |
214 | |
215 | mutex_lock(&sl->master->bus_mutex); |
216 | |
217 | /* Can only write data in blocks of the size of the scratchpad */ |
218 | addr = off; |
219 | len = count; |
220 | while (len > 0) { |
221 | |
222 | /* if len too short or addr not aligned */ |
223 | if (len < W1_F2D_SCRATCH_SIZE || addr & W1_F2D_SCRATCH_MASK) { |
224 | char tmp[W1_F2D_SCRATCH_SIZE]; |
225 | |
226 | /* read the block and update the parts to be written */ |
227 | if (w1_f2d_readblock(sl, off: addr & ~W1_F2D_SCRATCH_MASK, |
228 | W1_F2D_SCRATCH_SIZE, buf: tmp)) { |
229 | count = -EIO; |
230 | goto out_up; |
231 | } |
232 | |
233 | /* copy at most to the boundary of the PAGE or len */ |
234 | copy = W1_F2D_SCRATCH_SIZE - |
235 | (addr & W1_F2D_SCRATCH_MASK); |
236 | |
237 | if (copy > len) |
238 | copy = len; |
239 | |
240 | memcpy(&tmp[addr & W1_F2D_SCRATCH_MASK], buf, copy); |
241 | if (w1_f2d_write(sl, addr: addr & ~W1_F2D_SCRATCH_MASK, |
242 | W1_F2D_SCRATCH_SIZE, data: tmp) < 0) { |
243 | count = -EIO; |
244 | goto out_up; |
245 | } |
246 | } else { |
247 | |
248 | copy = W1_F2D_SCRATCH_SIZE; |
249 | if (w1_f2d_write(sl, addr, len: copy, data: buf) < 0) { |
250 | count = -EIO; |
251 | goto out_up; |
252 | } |
253 | } |
254 | buf += copy; |
255 | addr += copy; |
256 | len -= copy; |
257 | } |
258 | |
259 | out_up: |
260 | mutex_unlock(lock: &sl->master->bus_mutex); |
261 | |
262 | return count; |
263 | } |
264 | |
265 | static BIN_ATTR_RW(eeprom, W1_F2D_EEPROM_SIZE); |
266 | |
267 | static struct bin_attribute *w1_f2d_bin_attrs[] = { |
268 | &bin_attr_eeprom, |
269 | NULL, |
270 | }; |
271 | |
272 | static const struct attribute_group w1_f2d_group = { |
273 | .bin_attrs = w1_f2d_bin_attrs, |
274 | }; |
275 | |
276 | static const struct attribute_group *w1_f2d_groups[] = { |
277 | &w1_f2d_group, |
278 | NULL, |
279 | }; |
280 | |
281 | static const struct w1_family_ops w1_f2d_fops = { |
282 | .groups = w1_f2d_groups, |
283 | }; |
284 | |
285 | static struct w1_family w1_family_2d = { |
286 | .fid = W1_EEPROM_DS2431, |
287 | .fops = &w1_f2d_fops, |
288 | }; |
289 | module_w1_family(w1_family_2d); |
290 | |
291 | MODULE_AUTHOR("Bernhard Weirich <bernhard.weirich@riedel.net>" ); |
292 | MODULE_DESCRIPTION("w1 family 2d driver for DS2431, 1kb EEPROM" ); |
293 | MODULE_LICENSE("GPL" ); |
294 | MODULE_ALIAS("w1-family-" __stringify(W1_EEPROM_DS2431)); |
295 | |