1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * w1_ds2408.c - w1 family 29 (DS2408) driver |
4 | * |
5 | * Copyright (c) 2010 Jean-Francois Dagenais <dagenaisj@sonatest.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 | |
16 | #include <linux/w1.h> |
17 | |
18 | #define W1_FAMILY_DS2408 0x29 |
19 | |
20 | #define W1_F29_RETRIES 3 |
21 | |
22 | #define W1_F29_REG_LOGIG_STATE 0x88 /* R */ |
23 | #define W1_F29_REG_OUTPUT_LATCH_STATE 0x89 /* R */ |
24 | #define W1_F29_REG_ACTIVITY_LATCH_STATE 0x8A /* R */ |
25 | #define W1_F29_REG_COND_SEARCH_SELECT_MASK 0x8B /* RW */ |
26 | #define W1_F29_REG_COND_SEARCH_POL_SELECT 0x8C /* RW */ |
27 | #define W1_F29_REG_CONTROL_AND_STATUS 0x8D /* RW */ |
28 | |
29 | #define W1_F29_FUNC_READ_PIO_REGS 0xF0 |
30 | #define W1_F29_FUNC_CHANN_ACCESS_READ 0xF5 |
31 | #define W1_F29_FUNC_CHANN_ACCESS_WRITE 0x5A |
32 | /* also used to write the control/status reg (0x8D): */ |
33 | #define W1_F29_FUNC_WRITE_COND_SEARCH_REG 0xCC |
34 | #define W1_F29_FUNC_RESET_ACTIVITY_LATCHES 0xC3 |
35 | |
36 | #define W1_F29_SUCCESS_CONFIRM_BYTE 0xAA |
37 | |
38 | static int _read_reg(struct w1_slave *sl, u8 address, unsigned char *buf) |
39 | { |
40 | u8 wrbuf[3]; |
41 | |
42 | dev_dbg(&sl->dev, "Reading with slave: %p, reg addr: %0#4x, buff addr: %p" , |
43 | sl, (unsigned int)address, buf); |
44 | |
45 | if (!buf) |
46 | return -EINVAL; |
47 | |
48 | mutex_lock(&sl->master->bus_mutex); |
49 | dev_dbg(&sl->dev, "mutex locked" ); |
50 | |
51 | if (w1_reset_select_slave(sl)) { |
52 | mutex_unlock(lock: &sl->master->bus_mutex); |
53 | return -EIO; |
54 | } |
55 | |
56 | wrbuf[0] = W1_F29_FUNC_READ_PIO_REGS; |
57 | wrbuf[1] = address; |
58 | wrbuf[2] = 0; |
59 | w1_write_block(sl->master, wrbuf, 3); |
60 | *buf = w1_read_8(sl->master); |
61 | |
62 | mutex_unlock(lock: &sl->master->bus_mutex); |
63 | dev_dbg(&sl->dev, "mutex unlocked" ); |
64 | return 1; |
65 | } |
66 | |
67 | static ssize_t state_read(struct file *filp, struct kobject *kobj, |
68 | struct bin_attribute *bin_attr, char *buf, loff_t off, |
69 | size_t count) |
70 | { |
71 | dev_dbg(&kobj_to_w1_slave(kobj)->dev, |
72 | "Reading %s kobj: %p, off: %0#10x, count: %zu, buff addr: %p" , |
73 | bin_attr->attr.name, kobj, (unsigned int)off, count, buf); |
74 | if (count != 1 || off != 0) |
75 | return -EFAULT; |
76 | return _read_reg(sl: kobj_to_w1_slave(kobj), W1_F29_REG_LOGIG_STATE, buf); |
77 | } |
78 | |
79 | static ssize_t output_read(struct file *filp, struct kobject *kobj, |
80 | struct bin_attribute *bin_attr, char *buf, |
81 | loff_t off, size_t count) |
82 | { |
83 | dev_dbg(&kobj_to_w1_slave(kobj)->dev, |
84 | "Reading %s kobj: %p, off: %0#10x, count: %zu, buff addr: %p" , |
85 | bin_attr->attr.name, kobj, (unsigned int)off, count, buf); |
86 | if (count != 1 || off != 0) |
87 | return -EFAULT; |
88 | return _read_reg(sl: kobj_to_w1_slave(kobj), |
89 | W1_F29_REG_OUTPUT_LATCH_STATE, buf); |
90 | } |
91 | |
92 | static ssize_t activity_read(struct file *filp, struct kobject *kobj, |
93 | struct bin_attribute *bin_attr, char *buf, |
94 | loff_t off, size_t count) |
95 | { |
96 | dev_dbg(&kobj_to_w1_slave(kobj)->dev, |
97 | "Reading %s kobj: %p, off: %0#10x, count: %zu, buff addr: %p" , |
98 | bin_attr->attr.name, kobj, (unsigned int)off, count, buf); |
99 | if (count != 1 || off != 0) |
100 | return -EFAULT; |
101 | return _read_reg(sl: kobj_to_w1_slave(kobj), |
102 | W1_F29_REG_ACTIVITY_LATCH_STATE, buf); |
103 | } |
104 | |
105 | static ssize_t cond_search_mask_read(struct file *filp, struct kobject *kobj, |
106 | struct bin_attribute *bin_attr, char *buf, |
107 | loff_t off, size_t count) |
108 | { |
109 | dev_dbg(&kobj_to_w1_slave(kobj)->dev, |
110 | "Reading %s kobj: %p, off: %0#10x, count: %zu, buff addr: %p" , |
111 | bin_attr->attr.name, kobj, (unsigned int)off, count, buf); |
112 | if (count != 1 || off != 0) |
113 | return -EFAULT; |
114 | return _read_reg(sl: kobj_to_w1_slave(kobj), |
115 | W1_F29_REG_COND_SEARCH_SELECT_MASK, buf); |
116 | } |
117 | |
118 | static ssize_t cond_search_polarity_read(struct file *filp, |
119 | struct kobject *kobj, |
120 | struct bin_attribute *bin_attr, |
121 | char *buf, loff_t off, size_t count) |
122 | { |
123 | if (count != 1 || off != 0) |
124 | return -EFAULT; |
125 | return _read_reg(sl: kobj_to_w1_slave(kobj), |
126 | W1_F29_REG_COND_SEARCH_POL_SELECT, buf); |
127 | } |
128 | |
129 | static ssize_t status_control_read(struct file *filp, struct kobject *kobj, |
130 | struct bin_attribute *bin_attr, char *buf, |
131 | loff_t off, size_t count) |
132 | { |
133 | if (count != 1 || off != 0) |
134 | return -EFAULT; |
135 | return _read_reg(sl: kobj_to_w1_slave(kobj), |
136 | W1_F29_REG_CONTROL_AND_STATUS, buf); |
137 | } |
138 | |
139 | #ifdef CONFIG_W1_SLAVE_DS2408_READBACK |
140 | static bool optional_read_back_valid(struct w1_slave *sl, u8 expected) |
141 | { |
142 | u8 w1_buf[3]; |
143 | |
144 | if (w1_reset_resume_command(sl->master)) |
145 | return false; |
146 | |
147 | w1_buf[0] = W1_F29_FUNC_READ_PIO_REGS; |
148 | w1_buf[1] = W1_F29_REG_OUTPUT_LATCH_STATE; |
149 | w1_buf[2] = 0; |
150 | |
151 | w1_write_block(sl->master, w1_buf, 3); |
152 | |
153 | return (w1_read_8(sl->master) == expected); |
154 | } |
155 | #else |
156 | static bool optional_read_back_valid(struct w1_slave *sl, u8 expected) |
157 | { |
158 | return true; |
159 | } |
160 | #endif |
161 | |
162 | static ssize_t output_write(struct file *filp, struct kobject *kobj, |
163 | struct bin_attribute *bin_attr, char *buf, |
164 | loff_t off, size_t count) |
165 | { |
166 | struct w1_slave *sl = kobj_to_w1_slave(kobj); |
167 | u8 w1_buf[3]; |
168 | unsigned int retries = W1_F29_RETRIES; |
169 | ssize_t bytes_written = -EIO; |
170 | |
171 | if (count != 1 || off != 0) |
172 | return -EFAULT; |
173 | |
174 | dev_dbg(&sl->dev, "locking mutex for write_output" ); |
175 | mutex_lock(&sl->master->bus_mutex); |
176 | dev_dbg(&sl->dev, "mutex locked" ); |
177 | |
178 | if (w1_reset_select_slave(sl)) |
179 | goto out; |
180 | |
181 | do { |
182 | w1_buf[0] = W1_F29_FUNC_CHANN_ACCESS_WRITE; |
183 | w1_buf[1] = *buf; |
184 | w1_buf[2] = ~(*buf); |
185 | |
186 | w1_write_block(sl->master, w1_buf, 3); |
187 | |
188 | if (w1_read_8(sl->master) == W1_F29_SUCCESS_CONFIRM_BYTE && |
189 | optional_read_back_valid(sl, expected: *buf)) { |
190 | bytes_written = 1; |
191 | goto out; |
192 | } |
193 | |
194 | if (w1_reset_resume_command(sl->master)) |
195 | goto out; /* unrecoverable error */ |
196 | /* try again, the slave is ready for a command */ |
197 | } while (--retries); |
198 | |
199 | out: |
200 | mutex_unlock(lock: &sl->master->bus_mutex); |
201 | |
202 | dev_dbg(&sl->dev, "%s, mutex unlocked retries:%d\n" , |
203 | (bytes_written > 0) ? "succeeded" : "error" , retries); |
204 | |
205 | return bytes_written; |
206 | } |
207 | |
208 | |
209 | /* |
210 | * Writing to the activity file resets the activity latches. |
211 | */ |
212 | static ssize_t activity_write(struct file *filp, struct kobject *kobj, |
213 | struct bin_attribute *bin_attr, char *buf, |
214 | loff_t off, size_t count) |
215 | { |
216 | struct w1_slave *sl = kobj_to_w1_slave(kobj); |
217 | unsigned int retries = W1_F29_RETRIES; |
218 | |
219 | if (count != 1 || off != 0) |
220 | return -EFAULT; |
221 | |
222 | mutex_lock(&sl->master->bus_mutex); |
223 | |
224 | if (w1_reset_select_slave(sl)) |
225 | goto error; |
226 | |
227 | while (retries--) { |
228 | w1_write_8(sl->master, W1_F29_FUNC_RESET_ACTIVITY_LATCHES); |
229 | if (w1_read_8(sl->master) == W1_F29_SUCCESS_CONFIRM_BYTE) { |
230 | mutex_unlock(lock: &sl->master->bus_mutex); |
231 | return 1; |
232 | } |
233 | if (w1_reset_resume_command(sl->master)) |
234 | goto error; |
235 | } |
236 | |
237 | error: |
238 | mutex_unlock(lock: &sl->master->bus_mutex); |
239 | return -EIO; |
240 | } |
241 | |
242 | static ssize_t status_control_write(struct file *filp, struct kobject *kobj, |
243 | struct bin_attribute *bin_attr, char *buf, |
244 | loff_t off, size_t count) |
245 | { |
246 | struct w1_slave *sl = kobj_to_w1_slave(kobj); |
247 | u8 w1_buf[4]; |
248 | unsigned int retries = W1_F29_RETRIES; |
249 | |
250 | if (count != 1 || off != 0) |
251 | return -EFAULT; |
252 | |
253 | mutex_lock(&sl->master->bus_mutex); |
254 | |
255 | if (w1_reset_select_slave(sl)) |
256 | goto error; |
257 | |
258 | while (retries--) { |
259 | w1_buf[0] = W1_F29_FUNC_WRITE_COND_SEARCH_REG; |
260 | w1_buf[1] = W1_F29_REG_CONTROL_AND_STATUS; |
261 | w1_buf[2] = 0; |
262 | w1_buf[3] = *buf; |
263 | |
264 | w1_write_block(sl->master, w1_buf, 4); |
265 | if (w1_reset_resume_command(sl->master)) |
266 | goto error; |
267 | |
268 | w1_buf[0] = W1_F29_FUNC_READ_PIO_REGS; |
269 | w1_buf[1] = W1_F29_REG_CONTROL_AND_STATUS; |
270 | w1_buf[2] = 0; |
271 | |
272 | w1_write_block(sl->master, w1_buf, 3); |
273 | if (w1_read_8(sl->master) == *buf) { |
274 | /* success! */ |
275 | mutex_unlock(lock: &sl->master->bus_mutex); |
276 | return 1; |
277 | } |
278 | } |
279 | error: |
280 | mutex_unlock(lock: &sl->master->bus_mutex); |
281 | |
282 | return -EIO; |
283 | } |
284 | |
285 | /* |
286 | * This is a special sequence we must do to ensure the P0 output is not stuck |
287 | * in test mode. This is described in rev 2 of the ds2408's datasheet |
288 | * (http://datasheets.maximintegrated.com/en/ds/DS2408.pdf) under |
289 | * "APPLICATION INFORMATION/Power-up timing". |
290 | */ |
291 | static int w1_f29_disable_test_mode(struct w1_slave *sl) |
292 | { |
293 | int res; |
294 | u8 magic[10] = {0x96, }; |
295 | u64 rn = le64_to_cpu(*((u64 *)&sl->reg_num)); |
296 | |
297 | memcpy(&magic[1], &rn, 8); |
298 | magic[9] = 0x3C; |
299 | |
300 | mutex_lock(&sl->master->bus_mutex); |
301 | |
302 | res = w1_reset_bus(sl->master); |
303 | if (res) |
304 | goto out; |
305 | w1_write_block(sl->master, magic, ARRAY_SIZE(magic)); |
306 | |
307 | res = w1_reset_bus(sl->master); |
308 | out: |
309 | mutex_unlock(lock: &sl->master->bus_mutex); |
310 | return res; |
311 | } |
312 | |
313 | static BIN_ATTR_RO(state, 1); |
314 | static BIN_ATTR_RW(output, 1); |
315 | static BIN_ATTR_RW(activity, 1); |
316 | static BIN_ATTR_RO(cond_search_mask, 1); |
317 | static BIN_ATTR_RO(cond_search_polarity, 1); |
318 | static BIN_ATTR_RW(status_control, 1); |
319 | |
320 | static struct bin_attribute *w1_f29_bin_attrs[] = { |
321 | &bin_attr_state, |
322 | &bin_attr_output, |
323 | &bin_attr_activity, |
324 | &bin_attr_cond_search_mask, |
325 | &bin_attr_cond_search_polarity, |
326 | &bin_attr_status_control, |
327 | NULL, |
328 | }; |
329 | |
330 | static const struct attribute_group w1_f29_group = { |
331 | .bin_attrs = w1_f29_bin_attrs, |
332 | }; |
333 | |
334 | static const struct attribute_group *w1_f29_groups[] = { |
335 | &w1_f29_group, |
336 | NULL, |
337 | }; |
338 | |
339 | static const struct w1_family_ops w1_f29_fops = { |
340 | .add_slave = w1_f29_disable_test_mode, |
341 | .groups = w1_f29_groups, |
342 | }; |
343 | |
344 | static struct w1_family w1_family_29 = { |
345 | .fid = W1_FAMILY_DS2408, |
346 | .fops = &w1_f29_fops, |
347 | }; |
348 | module_w1_family(w1_family_29); |
349 | |
350 | MODULE_AUTHOR("Jean-Francois Dagenais <dagenaisj@sonatest.com>" ); |
351 | MODULE_DESCRIPTION("w1 family 29 driver for DS2408 8 Pin IO" ); |
352 | MODULE_LICENSE("GPL" ); |
353 | MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_DS2408)); |
354 | |