diff options
author | Andrew Worsley <amworsley@gmail.com> | 2017-08-27 07:34:23 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2017-08-28 11:23:29 -0400 |
commit | f6887531c0c6b106dbe9aa5ed6eaa166d93df52e (patch) | |
tree | 2b1d5a2c7dfc085c0531108a0be77049f65933fb /drivers/w1 | |
parent | 54865314f5a1bfade1555ca6b26653729a0f3e23 (diff) |
drivers: w1: Add 1w slave driver for DS28E05 EEPROM
Add a one wire driver for the DS28E05 one wire slave chip. This chip
requires OverDrive support to talk to it.
Signed-off-by: Andrew Worsley <amworsley@gmail.com>
Acked-by: Evgeniy Polyakov <zbr@ioremap.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/w1')
-rw-r--r-- | drivers/w1/slaves/Kconfig | 8 | ||||
-rw-r--r-- | drivers/w1/slaves/Makefile | 1 | ||||
-rw-r--r-- | drivers/w1/slaves/w1_ds2805.c | 313 | ||||
-rw-r--r-- | drivers/w1/w1.c | 2 |
4 files changed, 324 insertions, 0 deletions
diff --git a/drivers/w1/slaves/Kconfig b/drivers/w1/slaves/Kconfig index fb68465908f2..bb4d7ed2ce55 100644 --- a/drivers/w1/slaves/Kconfig +++ b/drivers/w1/slaves/Kconfig | |||
@@ -65,6 +65,14 @@ config W1_SLAVE_DS2423 | |||
65 | Say Y here if you want to use a 1-wire | 65 | Say Y here if you want to use a 1-wire |
66 | counter family device (DS2423). | 66 | counter family device (DS2423). |
67 | 67 | ||
68 | config W1_SLAVE_DS2805 | ||
69 | tristate "112-byte EEPROM support (DS28E05)" | ||
70 | help | ||
71 | Say Y here if you want to use a 1-wire | ||
72 | is a 112-byte user-programmable EEPROM is | ||
73 | organized as 7 pages of 16 bytes each with 64bit | ||
74 | unique number. Requires OverDrive Speed to talk to. | ||
75 | |||
68 | config W1_SLAVE_DS2431 | 76 | config W1_SLAVE_DS2431 |
69 | tristate "1kb EEPROM family support (DS2431)" | 77 | tristate "1kb EEPROM family support (DS2431)" |
70 | help | 78 | help |
diff --git a/drivers/w1/slaves/Makefile b/drivers/w1/slaves/Makefile index 54c63e420302..4622d8fed362 100644 --- a/drivers/w1/slaves/Makefile +++ b/drivers/w1/slaves/Makefile | |||
@@ -10,6 +10,7 @@ obj-$(CONFIG_W1_SLAVE_DS2413) += w1_ds2413.o | |||
10 | obj-$(CONFIG_W1_SLAVE_DS2406) += w1_ds2406.o | 10 | obj-$(CONFIG_W1_SLAVE_DS2406) += w1_ds2406.o |
11 | obj-$(CONFIG_W1_SLAVE_DS2423) += w1_ds2423.o | 11 | obj-$(CONFIG_W1_SLAVE_DS2423) += w1_ds2423.o |
12 | obj-$(CONFIG_W1_SLAVE_DS2431) += w1_ds2431.o | 12 | obj-$(CONFIG_W1_SLAVE_DS2431) += w1_ds2431.o |
13 | obj-$(CONFIG_W1_SLAVE_DS2805) += w1_ds2805.o | ||
13 | obj-$(CONFIG_W1_SLAVE_DS2433) += w1_ds2433.o | 14 | obj-$(CONFIG_W1_SLAVE_DS2433) += w1_ds2433.o |
14 | obj-$(CONFIG_W1_SLAVE_DS2438) += w1_ds2438.o | 15 | obj-$(CONFIG_W1_SLAVE_DS2438) += w1_ds2438.o |
15 | obj-$(CONFIG_W1_SLAVE_DS2760) += w1_ds2760.o | 16 | obj-$(CONFIG_W1_SLAVE_DS2760) += w1_ds2760.o |
diff --git a/drivers/w1/slaves/w1_ds2805.c b/drivers/w1/slaves/w1_ds2805.c new file mode 100644 index 000000000000..29348d283a65 --- /dev/null +++ b/drivers/w1/slaves/w1_ds2805.c | |||
@@ -0,0 +1,313 @@ | |||
1 | /* | ||
2 | * w1_ds2805 - w1 family 0d (DS28E05) driver | ||
3 | * | ||
4 | * Copyright (c) 2016 Andrew Worsley amworsley@gmail.com | ||
5 | * | ||
6 | * This source code is licensed under the GNU General Public License, | ||
7 | * Version 2. See the file COPYING for more details. | ||
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_DS2805 0x0D | ||
20 | |||
21 | #define W1_F0D_EEPROM_SIZE 128 | ||
22 | #define W1_F0D_PAGE_BITS 3 | ||
23 | #define W1_F0D_PAGE_SIZE (1<<W1_F0D_PAGE_BITS) | ||
24 | #define W1_F0D_PAGE_MASK 0x0F | ||
25 | |||
26 | #define W1_F0D_SCRATCH_BITS 1 | ||
27 | #define W1_F0D_SCRATCH_SIZE (1<<W1_F0D_SCRATCH_BITS) | ||
28 | #define W1_F0D_SCRATCH_MASK (W1_F0D_SCRATCH_SIZE-1) | ||
29 | |||
30 | #define W1_F0D_READ_EEPROM 0xF0 | ||
31 | #define W1_F0D_WRITE_EEPROM 0x55 | ||
32 | #define W1_F0D_RELEASE 0xFF | ||
33 | |||
34 | #define W1_F0D_CS_OK 0xAA /* Chip Status Ok */ | ||
35 | |||
36 | #define W1_F0D_TPROG_MS 16 | ||
37 | |||
38 | #define W1_F0D_READ_RETRIES 10 | ||
39 | #define W1_F0D_READ_MAXLEN W1_F0D_EEPROM_SIZE | ||
40 | |||
41 | /* | ||
42 | * Check the file size bounds and adjusts count as needed. | ||
43 | * This would not be needed if the file size didn't reset to 0 after a write. | ||
44 | */ | ||
45 | static inline size_t w1_f0d_fix_count(loff_t off, size_t count, size_t size) | ||
46 | { | ||
47 | if (off > size) | ||
48 | return 0; | ||
49 | |||
50 | if ((off + count) > size) | ||
51 | return size - off; | ||
52 | |||
53 | return count; | ||
54 | } | ||
55 | |||
56 | /* | ||
57 | * Read a block from W1 ROM two times and compares the results. | ||
58 | * If they are equal they are returned, otherwise the read | ||
59 | * is repeated W1_F0D_READ_RETRIES times. | ||
60 | * | ||
61 | * count must not exceed W1_F0D_READ_MAXLEN. | ||
62 | */ | ||
63 | static int w1_f0d_readblock(struct w1_slave *sl, int off, int count, char *buf) | ||
64 | { | ||
65 | u8 wrbuf[3]; | ||
66 | u8 cmp[W1_F0D_READ_MAXLEN]; | ||
67 | int tries = W1_F0D_READ_RETRIES; | ||
68 | |||
69 | do { | ||
70 | wrbuf[0] = W1_F0D_READ_EEPROM; | ||
71 | wrbuf[1] = off & 0x7f; | ||
72 | wrbuf[2] = 0; | ||
73 | |||
74 | if (w1_reset_select_slave(sl)) | ||
75 | return -1; | ||
76 | |||
77 | w1_write_block(sl->master, wrbuf, sizeof(wrbuf)); | ||
78 | w1_read_block(sl->master, buf, count); | ||
79 | |||
80 | if (w1_reset_select_slave(sl)) | ||
81 | return -1; | ||
82 | |||
83 | w1_write_block(sl->master, wrbuf, sizeof(wrbuf)); | ||
84 | w1_read_block(sl->master, cmp, count); | ||
85 | |||
86 | if (!memcmp(cmp, buf, count)) | ||
87 | return 0; | ||
88 | } while (--tries); | ||
89 | |||
90 | dev_err(&sl->dev, "proof reading failed %d times\n", | ||
91 | W1_F0D_READ_RETRIES); | ||
92 | |||
93 | return -1; | ||
94 | } | ||
95 | |||
96 | static ssize_t w1_f0d_read_bin(struct file *filp, struct kobject *kobj, | ||
97 | struct bin_attribute *bin_attr, | ||
98 | char *buf, loff_t off, size_t count) | ||
99 | { | ||
100 | struct w1_slave *sl = kobj_to_w1_slave(kobj); | ||
101 | int todo = count; | ||
102 | |||
103 | count = w1_f0d_fix_count(off, count, W1_F0D_EEPROM_SIZE); | ||
104 | if (count == 0) | ||
105 | return 0; | ||
106 | |||
107 | mutex_lock(&sl->master->mutex); | ||
108 | |||
109 | /* read directly from the EEPROM in chunks of W1_F0D_READ_MAXLEN */ | ||
110 | while (todo > 0) { | ||
111 | int block_read; | ||
112 | |||
113 | if (todo >= W1_F0D_READ_MAXLEN) | ||
114 | block_read = W1_F0D_READ_MAXLEN; | ||
115 | else | ||
116 | block_read = todo; | ||
117 | |||
118 | if (w1_f0d_readblock(sl, off, block_read, buf) < 0) { | ||
119 | count = -EIO; | ||
120 | break; | ||
121 | } | ||
122 | |||
123 | todo -= W1_F0D_READ_MAXLEN; | ||
124 | buf += W1_F0D_READ_MAXLEN; | ||
125 | off += W1_F0D_READ_MAXLEN; | ||
126 | } | ||
127 | |||
128 | mutex_unlock(&sl->master->mutex); | ||
129 | |||
130 | return count; | ||
131 | } | ||
132 | |||
133 | /* | ||
134 | * Writes to the scratchpad and reads it back for verification. | ||
135 | * Then copies the scratchpad to EEPROM. | ||
136 | * The data must be aligned at W1_F0D_SCRATCH_SIZE bytes and | ||
137 | * must be W1_F0D_SCRATCH_SIZE bytes long. | ||
138 | * The master must be locked. | ||
139 | * | ||
140 | * @param sl The slave structure | ||
141 | * @param addr Address for the write | ||
142 | * @param len length must be <= (W1_F0D_PAGE_SIZE - (addr & W1_F0D_PAGE_MASK)) | ||
143 | * @param data The data to write | ||
144 | * @return 0=Success -1=failure | ||
145 | */ | ||
146 | static int w1_f0d_write(struct w1_slave *sl, int addr, int len, const u8 *data) | ||
147 | { | ||
148 | int tries = W1_F0D_READ_RETRIES; | ||
149 | u8 wrbuf[3]; | ||
150 | u8 rdbuf[W1_F0D_SCRATCH_SIZE]; | ||
151 | u8 cs; | ||
152 | |||
153 | if ((addr & 1) || (len != 2)) { | ||
154 | dev_err(&sl->dev, "%s: bad addr/len - addr=%#x len=%d\n", | ||
155 | __func__, addr, len); | ||
156 | return -1; | ||
157 | } | ||
158 | |||
159 | retry: | ||
160 | |||
161 | /* Write the data to the scratchpad */ | ||
162 | if (w1_reset_select_slave(sl)) | ||
163 | return -1; | ||
164 | |||
165 | wrbuf[0] = W1_F0D_WRITE_EEPROM; | ||
166 | wrbuf[1] = addr & 0xff; | ||
167 | wrbuf[2] = 0xff; /* ?? from Example */ | ||
168 | |||
169 | w1_write_block(sl->master, wrbuf, sizeof(wrbuf)); | ||
170 | w1_write_block(sl->master, data, len); | ||
171 | |||
172 | w1_read_block(sl->master, rdbuf, sizeof(rdbuf)); | ||
173 | /* Compare what was read against the data written */ | ||
174 | if ((rdbuf[0] != data[0]) || (rdbuf[1] != data[1])) { | ||
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_F0D_READ_RETRIES); | ||
182 | pr_info("%s: rdbuf = %#x %#x data = %#x %#x\n", | ||
183 | __func__, rdbuf[0], rdbuf[1], data[0], data[1]); | ||
184 | |||
185 | return -1; | ||
186 | } | ||
187 | |||
188 | /* Trigger write out to EEPROM */ | ||
189 | w1_write_8(sl->master, W1_F0D_RELEASE); | ||
190 | |||
191 | /* Sleep for tprog ms to wait for the write to complete */ | ||
192 | msleep(W1_F0D_TPROG_MS); | ||
193 | |||
194 | /* Check CS (Command Status) == 0xAA ? */ | ||
195 | cs = w1_read_8(sl->master); | ||
196 | if (cs != W1_F0D_CS_OK) { | ||
197 | dev_err(&sl->dev, "save to eeprom failed = CS=%#x\n", cs); | ||
198 | return -1; | ||
199 | } | ||
200 | |||
201 | return 0; | ||
202 | } | ||
203 | |||
204 | static ssize_t w1_f0d_write_bin(struct file *filp, struct kobject *kobj, | ||
205 | struct bin_attribute *bin_attr, | ||
206 | char *buf, loff_t off, size_t count) | ||
207 | { | ||
208 | struct w1_slave *sl = kobj_to_w1_slave(kobj); | ||
209 | int addr, len; | ||
210 | int copy; | ||
211 | |||
212 | count = w1_f0d_fix_count(off, count, W1_F0D_EEPROM_SIZE); | ||
213 | if (count == 0) | ||
214 | return 0; | ||
215 | |||
216 | mutex_lock(&sl->master->mutex); | ||
217 | |||
218 | /* Can only write data in blocks of the size of the scratchpad */ | ||
219 | addr = off; | ||
220 | len = count; | ||
221 | while (len > 0) { | ||
222 | |||
223 | /* if len too short or addr not aligned */ | ||
224 | if (len < W1_F0D_SCRATCH_SIZE || addr & W1_F0D_SCRATCH_MASK) { | ||
225 | char tmp[W1_F0D_SCRATCH_SIZE]; | ||
226 | |||
227 | /* read the block and update the parts to be written */ | ||
228 | if (w1_f0d_readblock(sl, addr & ~W1_F0D_SCRATCH_MASK, | ||
229 | W1_F0D_SCRATCH_SIZE, tmp)) { | ||
230 | count = -EIO; | ||
231 | goto out_up; | ||
232 | } | ||
233 | |||
234 | /* copy at most to the boundary of the PAGE or len */ | ||
235 | copy = W1_F0D_SCRATCH_SIZE - | ||
236 | (addr & W1_F0D_SCRATCH_MASK); | ||
237 | |||
238 | if (copy > len) | ||
239 | copy = len; | ||
240 | |||
241 | memcpy(&tmp[addr & W1_F0D_SCRATCH_MASK], buf, copy); | ||
242 | if (w1_f0d_write(sl, addr & ~W1_F0D_SCRATCH_MASK, | ||
243 | W1_F0D_SCRATCH_SIZE, tmp) < 0) { | ||
244 | count = -EIO; | ||
245 | goto out_up; | ||
246 | } | ||
247 | } else { | ||
248 | |||
249 | copy = W1_F0D_SCRATCH_SIZE; | ||
250 | if (w1_f0d_write(sl, addr, copy, buf) < 0) { | ||
251 | count = -EIO; | ||
252 | goto out_up; | ||
253 | } | ||
254 | } | ||
255 | buf += copy; | ||
256 | addr += copy; | ||
257 | len -= copy; | ||
258 | } | ||
259 | |||
260 | out_up: | ||
261 | mutex_unlock(&sl->master->mutex); | ||
262 | |||
263 | return count; | ||
264 | } | ||
265 | |||
266 | static struct bin_attribute w1_f0d_bin_attr = { | ||
267 | .attr = { | ||
268 | .name = "eeprom", | ||
269 | .mode = S_IRUGO | S_IWUSR, | ||
270 | }, | ||
271 | .size = W1_F0D_EEPROM_SIZE, | ||
272 | .read = w1_f0d_read_bin, | ||
273 | .write = w1_f0d_write_bin, | ||
274 | }; | ||
275 | |||
276 | static int w1_f0d_add_slave(struct w1_slave *sl) | ||
277 | { | ||
278 | return sysfs_create_bin_file(&sl->dev.kobj, &w1_f0d_bin_attr); | ||
279 | } | ||
280 | |||
281 | static void w1_f0d_remove_slave(struct w1_slave *sl) | ||
282 | { | ||
283 | sysfs_remove_bin_file(&sl->dev.kobj, &w1_f0d_bin_attr); | ||
284 | } | ||
285 | |||
286 | static struct w1_family_ops w1_f0d_fops = { | ||
287 | .add_slave = w1_f0d_add_slave, | ||
288 | .remove_slave = w1_f0d_remove_slave, | ||
289 | }; | ||
290 | |||
291 | static struct w1_family w1_family_2d = { | ||
292 | .fid = W1_EEPROM_DS2805, | ||
293 | .fops = &w1_f0d_fops, | ||
294 | }; | ||
295 | |||
296 | static int __init w1_f0d_init(void) | ||
297 | { | ||
298 | pr_info("%s()\n", __func__); | ||
299 | return w1_register_family(&w1_family_2d); | ||
300 | } | ||
301 | |||
302 | static void __exit w1_f0d_fini(void) | ||
303 | { | ||
304 | pr_info("%s()\n", __func__); | ||
305 | w1_unregister_family(&w1_family_2d); | ||
306 | } | ||
307 | |||
308 | module_init(w1_f0d_init); | ||
309 | module_exit(w1_f0d_fini); | ||
310 | |||
311 | MODULE_LICENSE("GPL"); | ||
312 | MODULE_AUTHOR("Andrew Worsley amworsley@gmail.com"); | ||
313 | MODULE_DESCRIPTION("w1 family 0d driver for DS2805, 1kb EEPROM"); | ||
diff --git a/drivers/w1/w1.c b/drivers/w1/w1.c index 0f614dfcb09f..3c76e1ca4b83 100644 --- a/drivers/w1/w1.c +++ b/drivers/w1/w1.c | |||
@@ -729,6 +729,8 @@ int w1_attach_slave_device(struct w1_master *dev, struct w1_reg_num *rn) | |||
729 | atomic_set(&sl->refcnt, 1); | 729 | atomic_set(&sl->refcnt, 1); |
730 | atomic_inc(&sl->master->refcnt); | 730 | atomic_inc(&sl->master->refcnt); |
731 | dev->slave_count++; | 731 | dev->slave_count++; |
732 | dev_info(&dev->dev, "Attaching one wire slave %02x.%012llx crc %02x\n", | ||
733 | rn->family, (unsigned long long)rn->id, rn->crc); | ||
732 | 734 | ||
733 | /* slave modules need to be loaded in a context with unlocked mutex */ | 735 | /* slave modules need to be loaded in a context with unlocked mutex */ |
734 | mutex_unlock(&dev->mutex); | 736 | mutex_unlock(&dev->mutex); |