1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * w1_ds250x.c - w1 family 09/0b/89/91 (DS250x) driver |
4 | */ |
5 | |
6 | #include <linux/kernel.h> |
7 | #include <linux/module.h> |
8 | #include <linux/moduleparam.h> |
9 | #include <linux/device.h> |
10 | #include <linux/types.h> |
11 | #include <linux/delay.h> |
12 | #include <linux/slab.h> |
13 | #include <linux/crc16.h> |
14 | |
15 | #include <linux/w1.h> |
16 | #include <linux/nvmem-provider.h> |
17 | |
18 | #define W1_DS2501_UNW_FAMILY 0x91 |
19 | #define W1_DS2501_SIZE 64 |
20 | |
21 | #define W1_DS2502_FAMILY 0x09 |
22 | #define W1_DS2502_UNW_FAMILY 0x89 |
23 | #define W1_DS2502_SIZE 128 |
24 | |
25 | #define W1_DS2505_FAMILY 0x0b |
26 | #define W1_DS2505_SIZE 2048 |
27 | |
28 | #define W1_PAGE_SIZE 32 |
29 | |
30 | #define W1_EXT_READ_MEMORY 0xA5 |
31 | #define W1_READ_DATA_CRC 0xC3 |
32 | |
33 | #define OFF2PG(off) ((off) / W1_PAGE_SIZE) |
34 | |
35 | #define CRC16_INIT 0 |
36 | #define CRC16_VALID 0xb001 |
37 | |
38 | struct w1_eprom_data { |
39 | size_t size; |
40 | int (*read)(struct w1_slave *sl, int pageno); |
41 | u8 eprom[W1_DS2505_SIZE]; |
42 | DECLARE_BITMAP(page_present, W1_DS2505_SIZE / W1_PAGE_SIZE); |
43 | char nvmem_name[64]; |
44 | }; |
45 | |
46 | static int w1_ds2502_read_page(struct w1_slave *sl, int pageno) |
47 | { |
48 | struct w1_eprom_data *data = sl->family_data; |
49 | int pgoff = pageno * W1_PAGE_SIZE; |
50 | int ret = -EIO; |
51 | u8 buf[3]; |
52 | u8 crc8; |
53 | |
54 | if (test_bit(pageno, data->page_present)) |
55 | return 0; /* page already present */ |
56 | |
57 | mutex_lock(&sl->master->bus_mutex); |
58 | |
59 | if (w1_reset_select_slave(sl)) |
60 | goto err; |
61 | |
62 | buf[0] = W1_READ_DATA_CRC; |
63 | buf[1] = pgoff & 0xff; |
64 | buf[2] = pgoff >> 8; |
65 | w1_write_block(sl->master, buf, 3); |
66 | |
67 | crc8 = w1_read_8(sl->master); |
68 | if (w1_calc_crc8(buf, 3) != crc8) |
69 | goto err; |
70 | |
71 | w1_read_block(sl->master, &data->eprom[pgoff], W1_PAGE_SIZE); |
72 | |
73 | crc8 = w1_read_8(sl->master); |
74 | if (w1_calc_crc8(&data->eprom[pgoff], W1_PAGE_SIZE) != crc8) |
75 | goto err; |
76 | |
77 | set_bit(nr: pageno, addr: data->page_present); /* mark page present */ |
78 | ret = 0; |
79 | err: |
80 | mutex_unlock(lock: &sl->master->bus_mutex); |
81 | return ret; |
82 | } |
83 | |
84 | static int w1_ds2505_read_page(struct w1_slave *sl, int pageno) |
85 | { |
86 | struct w1_eprom_data *data = sl->family_data; |
87 | int redir_retries = 16; |
88 | int pgoff, epoff; |
89 | int ret = -EIO; |
90 | u8 buf[6]; |
91 | u8 redir; |
92 | u16 crc; |
93 | |
94 | if (test_bit(pageno, data->page_present)) |
95 | return 0; /* page already present */ |
96 | |
97 | epoff = pgoff = pageno * W1_PAGE_SIZE; |
98 | mutex_lock(&sl->master->bus_mutex); |
99 | |
100 | retry: |
101 | if (w1_reset_select_slave(sl)) |
102 | goto err; |
103 | |
104 | buf[0] = W1_EXT_READ_MEMORY; |
105 | buf[1] = pgoff & 0xff; |
106 | buf[2] = pgoff >> 8; |
107 | w1_write_block(sl->master, buf, 3); |
108 | w1_read_block(sl->master, buf + 3, 3); /* redir, crc16 */ |
109 | redir = buf[3]; |
110 | crc = crc16(CRC16_INIT, buffer: buf, len: 6); |
111 | |
112 | if (crc != CRC16_VALID) |
113 | goto err; |
114 | |
115 | |
116 | if (redir != 0xff) { |
117 | redir_retries--; |
118 | if (redir_retries < 0) |
119 | goto err; |
120 | |
121 | pgoff = (redir ^ 0xff) * W1_PAGE_SIZE; |
122 | goto retry; |
123 | } |
124 | |
125 | w1_read_block(sl->master, &data->eprom[epoff], W1_PAGE_SIZE); |
126 | w1_read_block(sl->master, buf, 2); /* crc16 */ |
127 | crc = crc16(CRC16_INIT, buffer: &data->eprom[epoff], W1_PAGE_SIZE); |
128 | crc = crc16(crc, buffer: buf, len: 2); |
129 | |
130 | if (crc != CRC16_VALID) |
131 | goto err; |
132 | |
133 | set_bit(nr: pageno, addr: data->page_present); |
134 | ret = 0; |
135 | err: |
136 | mutex_unlock(lock: &sl->master->bus_mutex); |
137 | return ret; |
138 | } |
139 | |
140 | static int w1_nvmem_read(void *priv, unsigned int off, void *buf, size_t count) |
141 | { |
142 | struct w1_slave *sl = priv; |
143 | struct w1_eprom_data *data = sl->family_data; |
144 | size_t eprom_size = data->size; |
145 | int ret; |
146 | int i; |
147 | |
148 | if (off > eprom_size) |
149 | return -EINVAL; |
150 | |
151 | if ((off + count) > eprom_size) |
152 | count = eprom_size - off; |
153 | |
154 | i = OFF2PG(off); |
155 | do { |
156 | ret = data->read(sl, i++); |
157 | if (ret < 0) |
158 | return ret; |
159 | } while (i < OFF2PG(off + count)); |
160 | |
161 | memcpy(buf, &data->eprom[off], count); |
162 | return 0; |
163 | } |
164 | |
165 | static int w1_eprom_add_slave(struct w1_slave *sl) |
166 | { |
167 | struct w1_eprom_data *data; |
168 | struct nvmem_device *nvmem; |
169 | struct nvmem_config nvmem_cfg = { |
170 | .dev = &sl->dev, |
171 | .add_legacy_fixed_of_cells = true, |
172 | .reg_read = w1_nvmem_read, |
173 | .type = NVMEM_TYPE_OTP, |
174 | .read_only = true, |
175 | .word_size = 1, |
176 | .priv = sl, |
177 | .id = -1 |
178 | }; |
179 | |
180 | data = devm_kzalloc(dev: &sl->dev, size: sizeof(struct w1_eprom_data), GFP_KERNEL); |
181 | if (!data) |
182 | return -ENOMEM; |
183 | |
184 | sl->family_data = data; |
185 | switch (sl->family->fid) { |
186 | case W1_DS2501_UNW_FAMILY: |
187 | data->size = W1_DS2501_SIZE; |
188 | data->read = w1_ds2502_read_page; |
189 | break; |
190 | case W1_DS2502_FAMILY: |
191 | case W1_DS2502_UNW_FAMILY: |
192 | data->size = W1_DS2502_SIZE; |
193 | data->read = w1_ds2502_read_page; |
194 | break; |
195 | case W1_DS2505_FAMILY: |
196 | data->size = W1_DS2505_SIZE; |
197 | data->read = w1_ds2505_read_page; |
198 | break; |
199 | } |
200 | |
201 | if (sl->master->bus_master->dev_id) |
202 | snprintf(buf: data->nvmem_name, size: sizeof(data->nvmem_name), |
203 | fmt: "%s-%02x-%012llx" , |
204 | sl->master->bus_master->dev_id, sl->reg_num.family, |
205 | (unsigned long long)sl->reg_num.id); |
206 | else |
207 | snprintf(buf: data->nvmem_name, size: sizeof(data->nvmem_name), |
208 | fmt: "%02x-%012llx" , |
209 | sl->reg_num.family, |
210 | (unsigned long long)sl->reg_num.id); |
211 | |
212 | nvmem_cfg.name = data->nvmem_name; |
213 | nvmem_cfg.size = data->size; |
214 | |
215 | nvmem = devm_nvmem_register(dev: &sl->dev, cfg: &nvmem_cfg); |
216 | return PTR_ERR_OR_ZERO(ptr: nvmem); |
217 | } |
218 | |
219 | static const struct w1_family_ops w1_eprom_fops = { |
220 | .add_slave = w1_eprom_add_slave, |
221 | }; |
222 | |
223 | static struct w1_family w1_family_09 = { |
224 | .fid = W1_DS2502_FAMILY, |
225 | .fops = &w1_eprom_fops, |
226 | }; |
227 | |
228 | static struct w1_family w1_family_0b = { |
229 | .fid = W1_DS2505_FAMILY, |
230 | .fops = &w1_eprom_fops, |
231 | }; |
232 | |
233 | static struct w1_family w1_family_89 = { |
234 | .fid = W1_DS2502_UNW_FAMILY, |
235 | .fops = &w1_eprom_fops, |
236 | }; |
237 | |
238 | static struct w1_family w1_family_91 = { |
239 | .fid = W1_DS2501_UNW_FAMILY, |
240 | .fops = &w1_eprom_fops, |
241 | }; |
242 | |
243 | static int __init w1_ds250x_init(void) |
244 | { |
245 | int err; |
246 | |
247 | err = w1_register_family(family: &w1_family_09); |
248 | if (err) |
249 | return err; |
250 | |
251 | err = w1_register_family(family: &w1_family_0b); |
252 | if (err) |
253 | goto err_0b; |
254 | |
255 | err = w1_register_family(family: &w1_family_89); |
256 | if (err) |
257 | goto err_89; |
258 | |
259 | err = w1_register_family(family: &w1_family_91); |
260 | if (err) |
261 | goto err_91; |
262 | |
263 | return 0; |
264 | |
265 | err_91: |
266 | w1_unregister_family(family: &w1_family_89); |
267 | err_89: |
268 | w1_unregister_family(family: &w1_family_0b); |
269 | err_0b: |
270 | w1_unregister_family(family: &w1_family_09); |
271 | return err; |
272 | } |
273 | |
274 | static void __exit w1_ds250x_exit(void) |
275 | { |
276 | w1_unregister_family(family: &w1_family_09); |
277 | w1_unregister_family(family: &w1_family_0b); |
278 | w1_unregister_family(family: &w1_family_89); |
279 | w1_unregister_family(family: &w1_family_91); |
280 | } |
281 | |
282 | module_init(w1_ds250x_init); |
283 | module_exit(w1_ds250x_exit); |
284 | |
285 | MODULE_AUTHOR("Thomas Bogendoerfer <tbogendoerfe@suse.de>" ); |
286 | MODULE_DESCRIPTION("w1 family driver for DS250x Add Only Memory" ); |
287 | MODULE_LICENSE("GPL" ); |
288 | MODULE_ALIAS("w1-family-" __stringify(W1_DS2502_FAMILY)); |
289 | MODULE_ALIAS("w1-family-" __stringify(W1_DS2505_FAMILY)); |
290 | MODULE_ALIAS("w1-family-" __stringify(W1_DS2501_UNW_FAMILY)); |
291 | MODULE_ALIAS("w1-family-" __stringify(W1_DS2502_UNW_FAMILY)); |
292 | |