1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * w1_ds2433.c - w1 family 23 (DS2433) & 43 (DS28EC20) eeprom driver |
4 | * |
5 | * Copyright (c) 2005 Ben Gardner <bgardner@wabtec.com> |
6 | * Copyright (c) 2023 Marc Ferland <marc.ferland@sonatest.com> |
7 | */ |
8 | |
9 | #include <linux/kernel.h> |
10 | #include <linux/module.h> |
11 | #include <linux/moduleparam.h> |
12 | #include <linux/device.h> |
13 | #include <linux/types.h> |
14 | #include <linux/delay.h> |
15 | #include <linux/slab.h> |
16 | #ifdef CONFIG_W1_SLAVE_DS2433_CRC |
17 | #include <linux/crc16.h> |
18 | |
19 | #define CRC16_INIT 0 |
20 | #define CRC16_VALID 0xb001 |
21 | |
22 | #endif |
23 | |
24 | #include <linux/w1.h> |
25 | |
26 | #define W1_EEPROM_DS2433 0x23 |
27 | #define W1_EEPROM_DS28EC20 0x43 |
28 | |
29 | #define W1_EEPROM_DS2433_SIZE 512 |
30 | #define W1_EEPROM_DS28EC20_SIZE 2560 |
31 | |
32 | #define W1_PAGE_SIZE 32 |
33 | #define W1_PAGE_BITS 5 |
34 | #define W1_PAGE_MASK 0x1F |
35 | #define W1_VALIDCRC_MAX 96 |
36 | |
37 | #define W1_F23_READ_EEPROM 0xF0 |
38 | #define W1_F23_WRITE_SCRATCH 0x0F |
39 | #define W1_F23_READ_SCRATCH 0xAA |
40 | #define W1_F23_COPY_SCRATCH 0x55 |
41 | |
42 | struct ds2433_config { |
43 | size_t eeprom_size; /* eeprom size in bytes */ |
44 | unsigned int page_count; /* number of 256 bits pages */ |
45 | unsigned int tprog; /* time in ms for page programming */ |
46 | }; |
47 | |
48 | static const struct ds2433_config config_f23 = { |
49 | .eeprom_size = W1_EEPROM_DS2433_SIZE, |
50 | .page_count = 16, |
51 | .tprog = 5, |
52 | }; |
53 | |
54 | static const struct ds2433_config config_f43 = { |
55 | .eeprom_size = W1_EEPROM_DS28EC20_SIZE, |
56 | .page_count = 80, |
57 | .tprog = 10, |
58 | }; |
59 | |
60 | struct w1_f23_data { |
61 | #ifdef CONFIG_W1_SLAVE_DS2433_CRC |
62 | u8 *memory; |
63 | DECLARE_BITMAP(validcrc, W1_VALIDCRC_MAX); |
64 | #endif |
65 | const struct ds2433_config *cfg; |
66 | }; |
67 | |
68 | /* |
69 | * Check the file size bounds and adjusts count as needed. |
70 | * This would not be needed if the file size didn't reset to 0 after a write. |
71 | */ |
72 | static inline size_t w1_f23_fix_count(loff_t off, size_t count, size_t size) |
73 | { |
74 | if (off > size) |
75 | return 0; |
76 | |
77 | if ((off + count) > size) |
78 | return (size - off); |
79 | |
80 | return count; |
81 | } |
82 | |
83 | #ifdef CONFIG_W1_SLAVE_DS2433_CRC |
84 | static int w1_f23_refresh_block(struct w1_slave *sl, struct w1_f23_data *data, |
85 | int block) |
86 | { |
87 | u8 wrbuf[3]; |
88 | int off = block * W1_PAGE_SIZE; |
89 | |
90 | if (test_bit(block, data->validcrc)) |
91 | return 0; |
92 | |
93 | if (w1_reset_select_slave(sl)) { |
94 | bitmap_zero(dst: data->validcrc, nbits: data->cfg->page_count); |
95 | return -EIO; |
96 | } |
97 | |
98 | wrbuf[0] = W1_F23_READ_EEPROM; |
99 | wrbuf[1] = off & 0xff; |
100 | wrbuf[2] = off >> 8; |
101 | w1_write_block(sl->master, wrbuf, 3); |
102 | w1_read_block(sl->master, &data->memory[off], W1_PAGE_SIZE); |
103 | |
104 | /* cache the block if the CRC is valid */ |
105 | if (crc16(CRC16_INIT, buffer: &data->memory[off], W1_PAGE_SIZE) == CRC16_VALID) |
106 | set_bit(nr: block, addr: data->validcrc); |
107 | |
108 | return 0; |
109 | } |
110 | #endif /* CONFIG_W1_SLAVE_DS2433_CRC */ |
111 | |
112 | static ssize_t eeprom_read(struct file *filp, struct kobject *kobj, |
113 | struct bin_attribute *bin_attr, char *buf, |
114 | loff_t off, size_t count) |
115 | { |
116 | struct w1_slave *sl = kobj_to_w1_slave(kobj); |
117 | #ifdef CONFIG_W1_SLAVE_DS2433_CRC |
118 | struct w1_f23_data *data = sl->family_data; |
119 | int i, min_page, max_page; |
120 | #else |
121 | u8 wrbuf[3]; |
122 | #endif |
123 | |
124 | count = w1_f23_fix_count(off, count, size: bin_attr->size); |
125 | if (!count) |
126 | return 0; |
127 | |
128 | mutex_lock(&sl->master->bus_mutex); |
129 | |
130 | #ifdef CONFIG_W1_SLAVE_DS2433_CRC |
131 | |
132 | min_page = (off >> W1_PAGE_BITS); |
133 | max_page = (off + count - 1) >> W1_PAGE_BITS; |
134 | for (i = min_page; i <= max_page; i++) { |
135 | if (w1_f23_refresh_block(sl, data, block: i)) { |
136 | count = -EIO; |
137 | goto out_up; |
138 | } |
139 | } |
140 | memcpy(buf, &data->memory[off], count); |
141 | |
142 | #else /* CONFIG_W1_SLAVE_DS2433_CRC */ |
143 | |
144 | /* read directly from the EEPROM */ |
145 | if (w1_reset_select_slave(sl)) { |
146 | count = -EIO; |
147 | goto out_up; |
148 | } |
149 | |
150 | wrbuf[0] = W1_F23_READ_EEPROM; |
151 | wrbuf[1] = off & 0xff; |
152 | wrbuf[2] = off >> 8; |
153 | w1_write_block(sl->master, wrbuf, 3); |
154 | w1_read_block(sl->master, buf, count); |
155 | |
156 | #endif /* CONFIG_W1_SLAVE_DS2433_CRC */ |
157 | |
158 | out_up: |
159 | mutex_unlock(lock: &sl->master->bus_mutex); |
160 | |
161 | return count; |
162 | } |
163 | |
164 | /** |
165 | * w1_f23_write() - Writes to the scratchpad and reads it back for verification. |
166 | * @sl: The slave structure |
167 | * @addr: Address for the write |
168 | * @len: length must be <= (W1_PAGE_SIZE - (addr & W1_PAGE_MASK)) |
169 | * @data: The data to write |
170 | * |
171 | * Then copies the scratchpad to EEPROM. |
172 | * The data must be on one page. |
173 | * The master must be locked. |
174 | * |
175 | * Return: 0=Success, -1=failure |
176 | */ |
177 | static int w1_f23_write(struct w1_slave *sl, int addr, int len, const u8 *data) |
178 | { |
179 | struct w1_f23_data *f23 = sl->family_data; |
180 | u8 wrbuf[4]; |
181 | u8 rdbuf[W1_PAGE_SIZE + 3]; |
182 | u8 es = (addr + len - 1) & 0x1f; |
183 | |
184 | /* Write the data to the scratchpad */ |
185 | if (w1_reset_select_slave(sl)) |
186 | return -1; |
187 | |
188 | wrbuf[0] = W1_F23_WRITE_SCRATCH; |
189 | wrbuf[1] = addr & 0xff; |
190 | wrbuf[2] = addr >> 8; |
191 | |
192 | w1_write_block(sl->master, wrbuf, 3); |
193 | w1_write_block(sl->master, data, len); |
194 | |
195 | /* Read the scratchpad and verify */ |
196 | if (w1_reset_select_slave(sl)) |
197 | return -1; |
198 | |
199 | w1_write_8(sl->master, W1_F23_READ_SCRATCH); |
200 | w1_read_block(sl->master, rdbuf, len + 3); |
201 | |
202 | /* Compare what was read against the data written */ |
203 | if ((rdbuf[0] != wrbuf[1]) || (rdbuf[1] != wrbuf[2]) || |
204 | (rdbuf[2] != es) || (memcmp(p: data, q: &rdbuf[3], size: len) != 0)) |
205 | return -1; |
206 | |
207 | /* Copy the scratchpad to EEPROM */ |
208 | if (w1_reset_select_slave(sl)) |
209 | return -1; |
210 | |
211 | wrbuf[0] = W1_F23_COPY_SCRATCH; |
212 | wrbuf[3] = es; |
213 | w1_write_block(sl->master, wrbuf, 4); |
214 | |
215 | /* Sleep for tprog ms to wait for the write to complete */ |
216 | msleep(msecs: f23->cfg->tprog); |
217 | |
218 | /* Reset the bus to wake up the EEPROM (this may not be needed) */ |
219 | w1_reset_bus(sl->master); |
220 | #ifdef CONFIG_W1_SLAVE_DS2433_CRC |
221 | clear_bit(nr: addr >> W1_PAGE_BITS, addr: f23->validcrc); |
222 | #endif |
223 | return 0; |
224 | } |
225 | |
226 | static ssize_t eeprom_write(struct file *filp, struct kobject *kobj, |
227 | struct bin_attribute *bin_attr, char *buf, |
228 | loff_t off, size_t count) |
229 | { |
230 | struct w1_slave *sl = kobj_to_w1_slave(kobj); |
231 | int addr, len, idx; |
232 | |
233 | count = w1_f23_fix_count(off, count, size: bin_attr->size); |
234 | if (!count) |
235 | return 0; |
236 | |
237 | #ifdef CONFIG_W1_SLAVE_DS2433_CRC |
238 | /* can only write full blocks in cached mode */ |
239 | if ((off & W1_PAGE_MASK) || (count & W1_PAGE_MASK)) { |
240 | dev_err(&sl->dev, "invalid offset/count off=%d cnt=%zd\n" , |
241 | (int)off, count); |
242 | return -EINVAL; |
243 | } |
244 | |
245 | /* make sure the block CRCs are valid */ |
246 | for (idx = 0; idx < count; idx += W1_PAGE_SIZE) { |
247 | if (crc16(CRC16_INIT, buffer: &buf[idx], W1_PAGE_SIZE) != CRC16_VALID) { |
248 | dev_err(&sl->dev, "bad CRC at offset %d\n" , (int)off); |
249 | return -EINVAL; |
250 | } |
251 | } |
252 | #endif /* CONFIG_W1_SLAVE_DS2433_CRC */ |
253 | |
254 | mutex_lock(&sl->master->bus_mutex); |
255 | |
256 | /* Can only write data to one page at a time */ |
257 | idx = 0; |
258 | while (idx < count) { |
259 | addr = off + idx; |
260 | len = W1_PAGE_SIZE - (addr & W1_PAGE_MASK); |
261 | if (len > (count - idx)) |
262 | len = count - idx; |
263 | |
264 | if (w1_f23_write(sl, addr, len, data: &buf[idx]) < 0) { |
265 | count = -EIO; |
266 | goto out_up; |
267 | } |
268 | idx += len; |
269 | } |
270 | |
271 | out_up: |
272 | mutex_unlock(lock: &sl->master->bus_mutex); |
273 | |
274 | return count; |
275 | } |
276 | |
277 | static struct bin_attribute bin_attr_f23_eeprom = { |
278 | .attr = { .name = "eeprom" , .mode = 0644 }, |
279 | .read = eeprom_read, |
280 | .write = eeprom_write, |
281 | .size = W1_EEPROM_DS2433_SIZE, |
282 | }; |
283 | |
284 | static struct bin_attribute bin_attr_f43_eeprom = { |
285 | .attr = { .name = "eeprom" , .mode = 0644 }, |
286 | .read = eeprom_read, |
287 | .write = eeprom_write, |
288 | .size = W1_EEPROM_DS28EC20_SIZE, |
289 | }; |
290 | |
291 | static struct bin_attribute *w1_f23_bin_attributes[] = { |
292 | &bin_attr_f23_eeprom, |
293 | NULL, |
294 | }; |
295 | |
296 | static const struct attribute_group w1_f23_group = { |
297 | .bin_attrs = w1_f23_bin_attributes, |
298 | }; |
299 | |
300 | static const struct attribute_group *w1_f23_groups[] = { |
301 | &w1_f23_group, |
302 | NULL, |
303 | }; |
304 | |
305 | static struct bin_attribute *w1_f43_bin_attributes[] = { |
306 | &bin_attr_f43_eeprom, |
307 | NULL, |
308 | }; |
309 | |
310 | static const struct attribute_group w1_f43_group = { |
311 | .bin_attrs = w1_f43_bin_attributes, |
312 | }; |
313 | |
314 | static const struct attribute_group *w1_f43_groups[] = { |
315 | &w1_f43_group, |
316 | NULL, |
317 | }; |
318 | |
319 | static int w1_f23_add_slave(struct w1_slave *sl) |
320 | { |
321 | struct w1_f23_data *data; |
322 | |
323 | data = kzalloc(size: sizeof(struct w1_f23_data), GFP_KERNEL); |
324 | if (!data) |
325 | return -ENOMEM; |
326 | |
327 | switch (sl->family->fid) { |
328 | case W1_EEPROM_DS2433: |
329 | data->cfg = &config_f23; |
330 | break; |
331 | case W1_EEPROM_DS28EC20: |
332 | data->cfg = &config_f43; |
333 | break; |
334 | } |
335 | |
336 | #ifdef CONFIG_W1_SLAVE_DS2433_CRC |
337 | if (data->cfg->page_count > W1_VALIDCRC_MAX) { |
338 | dev_err(&sl->dev, "page count too big for crc bitmap\n" ); |
339 | kfree(objp: data); |
340 | return -EINVAL; |
341 | } |
342 | data->memory = kzalloc(size: data->cfg->eeprom_size, GFP_KERNEL); |
343 | if (!data->memory) { |
344 | kfree(objp: data); |
345 | return -ENOMEM; |
346 | } |
347 | bitmap_zero(dst: data->validcrc, nbits: data->cfg->page_count); |
348 | #endif /* CONFIG_W1_SLAVE_DS2433_CRC */ |
349 | sl->family_data = data; |
350 | |
351 | return 0; |
352 | } |
353 | |
354 | static void w1_f23_remove_slave(struct w1_slave *sl) |
355 | { |
356 | struct w1_f23_data *data = sl->family_data; |
357 | sl->family_data = NULL; |
358 | #ifdef CONFIG_W1_SLAVE_DS2433_CRC |
359 | kfree(objp: data->memory); |
360 | #endif /* CONFIG_W1_SLAVE_DS2433_CRC */ |
361 | kfree(objp: data); |
362 | } |
363 | |
364 | static const struct w1_family_ops w1_f23_fops = { |
365 | .add_slave = w1_f23_add_slave, |
366 | .remove_slave = w1_f23_remove_slave, |
367 | .groups = w1_f23_groups, |
368 | }; |
369 | |
370 | static const struct w1_family_ops w1_f43_fops = { |
371 | .add_slave = w1_f23_add_slave, |
372 | .remove_slave = w1_f23_remove_slave, |
373 | .groups = w1_f43_groups, |
374 | }; |
375 | |
376 | static struct w1_family w1_family_23 = { |
377 | .fid = W1_EEPROM_DS2433, |
378 | .fops = &w1_f23_fops, |
379 | }; |
380 | |
381 | static struct w1_family w1_family_43 = { |
382 | .fid = W1_EEPROM_DS28EC20, |
383 | .fops = &w1_f43_fops, |
384 | }; |
385 | |
386 | static int __init w1_ds2433_init(void) |
387 | { |
388 | int err; |
389 | |
390 | err = w1_register_family(family: &w1_family_23); |
391 | if (err) |
392 | return err; |
393 | |
394 | err = w1_register_family(family: &w1_family_43); |
395 | if (err) |
396 | goto err_43; |
397 | |
398 | return 0; |
399 | |
400 | err_43: |
401 | w1_unregister_family(family: &w1_family_23); |
402 | return err; |
403 | } |
404 | |
405 | static void __exit w1_ds2433_exit(void) |
406 | { |
407 | w1_unregister_family(family: &w1_family_23); |
408 | w1_unregister_family(family: &w1_family_43); |
409 | } |
410 | |
411 | module_init(w1_ds2433_init); |
412 | module_exit(w1_ds2433_exit); |
413 | |
414 | MODULE_AUTHOR("Ben Gardner <bgardner@wabtec.com>" ); |
415 | MODULE_AUTHOR("Marc Ferland <marc.ferland@sonatest.com>" ); |
416 | MODULE_DESCRIPTION("w1 family 23/43 driver for DS2433 (4kb) and DS28EC20 (20kb)" ); |
417 | MODULE_LICENSE("GPL" ); |
418 | MODULE_ALIAS("w1-family-" __stringify(W1_EEPROM_DS2433)); |
419 | MODULE_ALIAS("w1-family-" __stringify(W1_EEPROM_DS28EC20)); |
420 | |