1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * w1_ds2433.c - w1 family 23 (DS2433) driver |
4 | * |
5 | * Copyright (c) 2005 Ben Gardner <bgardner@wabtec.com> |
6 | */ |
7 | |
8 | #include <linux/kernel.h> |
9 | #include <linux/module.h> |
10 | #include <linux/moduleparam.h> |
11 | #include <linux/device.h> |
12 | #include <linux/types.h> |
13 | #include <linux/delay.h> |
14 | #include <linux/slab.h> |
15 | #ifdef CONFIG_W1_SLAVE_DS2433_CRC |
16 | #include <linux/crc16.h> |
17 | |
18 | #define CRC16_INIT 0 |
19 | #define CRC16_VALID 0xb001 |
20 | |
21 | #endif |
22 | |
23 | #include <linux/w1.h> |
24 | |
25 | #define W1_EEPROM_DS2433 0x23 |
26 | |
27 | #define W1_EEPROM_SIZE 512 |
28 | #define W1_PAGE_COUNT 16 |
29 | #define W1_PAGE_SIZE 32 |
30 | #define W1_PAGE_BITS 5 |
31 | #define W1_PAGE_MASK 0x1F |
32 | |
33 | #define W1_F23_TIME 300 |
34 | |
35 | #define W1_F23_READ_EEPROM 0xF0 |
36 | #define W1_F23_WRITE_SCRATCH 0x0F |
37 | #define W1_F23_READ_SCRATCH 0xAA |
38 | #define W1_F23_COPY_SCRATCH 0x55 |
39 | |
40 | struct w1_f23_data { |
41 | u8 memory[W1_EEPROM_SIZE]; |
42 | u32 validcrc; |
43 | }; |
44 | |
45 | /* |
46 | * Check the file size bounds and adjusts count as needed. |
47 | * This would not be needed if the file size didn't reset to 0 after a write. |
48 | */ |
49 | static inline size_t w1_f23_fix_count(loff_t off, size_t count, size_t size) |
50 | { |
51 | if (off > size) |
52 | return 0; |
53 | |
54 | if ((off + count) > size) |
55 | return (size - off); |
56 | |
57 | return count; |
58 | } |
59 | |
60 | #ifdef CONFIG_W1_SLAVE_DS2433_CRC |
61 | static int w1_f23_refresh_block(struct w1_slave *sl, struct w1_f23_data *data, |
62 | int block) |
63 | { |
64 | u8 wrbuf[3]; |
65 | int off = block * W1_PAGE_SIZE; |
66 | |
67 | if (data->validcrc & (1 << block)) |
68 | return 0; |
69 | |
70 | if (w1_reset_select_slave(sl)) { |
71 | data->validcrc = 0; |
72 | return -EIO; |
73 | } |
74 | |
75 | wrbuf[0] = W1_F23_READ_EEPROM; |
76 | wrbuf[1] = off & 0xff; |
77 | wrbuf[2] = off >> 8; |
78 | w1_write_block(sl->master, wrbuf, 3); |
79 | w1_read_block(sl->master, &data->memory[off], W1_PAGE_SIZE); |
80 | |
81 | /* cache the block if the CRC is valid */ |
82 | if (crc16(CRC16_INIT, buffer: &data->memory[off], W1_PAGE_SIZE) == CRC16_VALID) |
83 | data->validcrc |= (1 << block); |
84 | |
85 | return 0; |
86 | } |
87 | #endif /* CONFIG_W1_SLAVE_DS2433_CRC */ |
88 | |
89 | static ssize_t eeprom_read(struct file *filp, struct kobject *kobj, |
90 | struct bin_attribute *bin_attr, char *buf, |
91 | loff_t off, size_t count) |
92 | { |
93 | struct w1_slave *sl = kobj_to_w1_slave(kobj); |
94 | #ifdef CONFIG_W1_SLAVE_DS2433_CRC |
95 | struct w1_f23_data *data = sl->family_data; |
96 | int i, min_page, max_page; |
97 | #else |
98 | u8 wrbuf[3]; |
99 | #endif |
100 | |
101 | count = w1_f23_fix_count(off, count, W1_EEPROM_SIZE); |
102 | if (!count) |
103 | return 0; |
104 | |
105 | mutex_lock(&sl->master->bus_mutex); |
106 | |
107 | #ifdef CONFIG_W1_SLAVE_DS2433_CRC |
108 | |
109 | min_page = (off >> W1_PAGE_BITS); |
110 | max_page = (off + count - 1) >> W1_PAGE_BITS; |
111 | for (i = min_page; i <= max_page; i++) { |
112 | if (w1_f23_refresh_block(sl, data, block: i)) { |
113 | count = -EIO; |
114 | goto out_up; |
115 | } |
116 | } |
117 | memcpy(buf, &data->memory[off], count); |
118 | |
119 | #else /* CONFIG_W1_SLAVE_DS2433_CRC */ |
120 | |
121 | /* read directly from the EEPROM */ |
122 | if (w1_reset_select_slave(sl)) { |
123 | count = -EIO; |
124 | goto out_up; |
125 | } |
126 | |
127 | wrbuf[0] = W1_F23_READ_EEPROM; |
128 | wrbuf[1] = off & 0xff; |
129 | wrbuf[2] = off >> 8; |
130 | w1_write_block(sl->master, wrbuf, 3); |
131 | w1_read_block(sl->master, buf, count); |
132 | |
133 | #endif /* CONFIG_W1_SLAVE_DS2433_CRC */ |
134 | |
135 | out_up: |
136 | mutex_unlock(lock: &sl->master->bus_mutex); |
137 | |
138 | return count; |
139 | } |
140 | |
141 | /** |
142 | * w1_f23_write() - Writes to the scratchpad and reads it back for verification. |
143 | * @sl: The slave structure |
144 | * @addr: Address for the write |
145 | * @len: length must be <= (W1_PAGE_SIZE - (addr & W1_PAGE_MASK)) |
146 | * @data: The data to write |
147 | * |
148 | * Then copies the scratchpad to EEPROM. |
149 | * The data must be on one page. |
150 | * The master must be locked. |
151 | * |
152 | * Return: 0=Success, -1=failure |
153 | */ |
154 | static int w1_f23_write(struct w1_slave *sl, int addr, int len, const u8 *data) |
155 | { |
156 | #ifdef CONFIG_W1_SLAVE_DS2433_CRC |
157 | struct w1_f23_data *f23 = sl->family_data; |
158 | #endif |
159 | u8 wrbuf[4]; |
160 | u8 rdbuf[W1_PAGE_SIZE + 3]; |
161 | u8 es = (addr + len - 1) & 0x1f; |
162 | |
163 | /* Write the data to the scratchpad */ |
164 | if (w1_reset_select_slave(sl)) |
165 | return -1; |
166 | |
167 | wrbuf[0] = W1_F23_WRITE_SCRATCH; |
168 | wrbuf[1] = addr & 0xff; |
169 | wrbuf[2] = addr >> 8; |
170 | |
171 | w1_write_block(sl->master, wrbuf, 3); |
172 | w1_write_block(sl->master, data, len); |
173 | |
174 | /* Read the scratchpad and verify */ |
175 | if (w1_reset_select_slave(sl)) |
176 | return -1; |
177 | |
178 | w1_write_8(sl->master, W1_F23_READ_SCRATCH); |
179 | w1_read_block(sl->master, rdbuf, len + 3); |
180 | |
181 | /* Compare what was read against the data written */ |
182 | if ((rdbuf[0] != wrbuf[1]) || (rdbuf[1] != wrbuf[2]) || |
183 | (rdbuf[2] != es) || (memcmp(p: data, q: &rdbuf[3], size: len) != 0)) |
184 | return -1; |
185 | |
186 | /* Copy the scratchpad to EEPROM */ |
187 | if (w1_reset_select_slave(sl)) |
188 | return -1; |
189 | |
190 | wrbuf[0] = W1_F23_COPY_SCRATCH; |
191 | wrbuf[3] = es; |
192 | w1_write_block(sl->master, wrbuf, 4); |
193 | |
194 | /* Sleep for 5 ms to wait for the write to complete */ |
195 | msleep(msecs: 5); |
196 | |
197 | /* Reset the bus to wake up the EEPROM (this may not be needed) */ |
198 | w1_reset_bus(sl->master); |
199 | #ifdef CONFIG_W1_SLAVE_DS2433_CRC |
200 | f23->validcrc &= ~(1 << (addr >> W1_PAGE_BITS)); |
201 | #endif |
202 | return 0; |
203 | } |
204 | |
205 | static ssize_t eeprom_write(struct file *filp, struct kobject *kobj, |
206 | struct bin_attribute *bin_attr, char *buf, |
207 | loff_t off, size_t count) |
208 | { |
209 | struct w1_slave *sl = kobj_to_w1_slave(kobj); |
210 | int addr, len, idx; |
211 | |
212 | count = w1_f23_fix_count(off, count, W1_EEPROM_SIZE); |
213 | if (!count) |
214 | return 0; |
215 | |
216 | #ifdef CONFIG_W1_SLAVE_DS2433_CRC |
217 | /* can only write full blocks in cached mode */ |
218 | if ((off & W1_PAGE_MASK) || (count & W1_PAGE_MASK)) { |
219 | dev_err(&sl->dev, "invalid offset/count off=%d cnt=%zd\n" , |
220 | (int)off, count); |
221 | return -EINVAL; |
222 | } |
223 | |
224 | /* make sure the block CRCs are valid */ |
225 | for (idx = 0; idx < count; idx += W1_PAGE_SIZE) { |
226 | if (crc16(CRC16_INIT, buffer: &buf[idx], W1_PAGE_SIZE) != CRC16_VALID) { |
227 | dev_err(&sl->dev, "bad CRC at offset %d\n" , (int)off); |
228 | return -EINVAL; |
229 | } |
230 | } |
231 | #endif /* CONFIG_W1_SLAVE_DS2433_CRC */ |
232 | |
233 | mutex_lock(&sl->master->bus_mutex); |
234 | |
235 | /* Can only write data to one page at a time */ |
236 | idx = 0; |
237 | while (idx < count) { |
238 | addr = off + idx; |
239 | len = W1_PAGE_SIZE - (addr & W1_PAGE_MASK); |
240 | if (len > (count - idx)) |
241 | len = count - idx; |
242 | |
243 | if (w1_f23_write(sl, addr, len, data: &buf[idx]) < 0) { |
244 | count = -EIO; |
245 | goto out_up; |
246 | } |
247 | idx += len; |
248 | } |
249 | |
250 | out_up: |
251 | mutex_unlock(lock: &sl->master->bus_mutex); |
252 | |
253 | return count; |
254 | } |
255 | |
256 | static BIN_ATTR_RW(eeprom, W1_EEPROM_SIZE); |
257 | |
258 | static struct bin_attribute *w1_f23_bin_attributes[] = { |
259 | &bin_attr_eeprom, |
260 | NULL, |
261 | }; |
262 | |
263 | static const struct attribute_group w1_f23_group = { |
264 | .bin_attrs = w1_f23_bin_attributes, |
265 | }; |
266 | |
267 | static const struct attribute_group *w1_f23_groups[] = { |
268 | &w1_f23_group, |
269 | NULL, |
270 | }; |
271 | |
272 | static int w1_f23_add_slave(struct w1_slave *sl) |
273 | { |
274 | #ifdef CONFIG_W1_SLAVE_DS2433_CRC |
275 | struct w1_f23_data *data; |
276 | |
277 | data = kzalloc(size: sizeof(struct w1_f23_data), GFP_KERNEL); |
278 | if (!data) |
279 | return -ENOMEM; |
280 | sl->family_data = data; |
281 | |
282 | #endif /* CONFIG_W1_SLAVE_DS2433_CRC */ |
283 | return 0; |
284 | } |
285 | |
286 | static void w1_f23_remove_slave(struct w1_slave *sl) |
287 | { |
288 | #ifdef CONFIG_W1_SLAVE_DS2433_CRC |
289 | kfree(objp: sl->family_data); |
290 | sl->family_data = NULL; |
291 | #endif /* CONFIG_W1_SLAVE_DS2433_CRC */ |
292 | } |
293 | |
294 | static const struct w1_family_ops w1_f23_fops = { |
295 | .add_slave = w1_f23_add_slave, |
296 | .remove_slave = w1_f23_remove_slave, |
297 | .groups = w1_f23_groups, |
298 | }; |
299 | |
300 | static struct w1_family w1_family_23 = { |
301 | .fid = W1_EEPROM_DS2433, |
302 | .fops = &w1_f23_fops, |
303 | }; |
304 | module_w1_family(w1_family_23); |
305 | |
306 | MODULE_AUTHOR("Ben Gardner <bgardner@wabtec.com>" ); |
307 | MODULE_DESCRIPTION("w1 family 23 driver for DS2433, 4kb EEPROM" ); |
308 | MODULE_LICENSE("GPL" ); |
309 | MODULE_ALIAS("w1-family-" __stringify(W1_EEPROM_DS2433)); |
310 | |