1c6bf3842SAngelo Dureghello // SPDX-License-Identifier: GPL-2.0-only
2c6bf3842SAngelo Dureghello /*
3c6bf3842SAngelo Dureghello * w1_ds2430.c - w1 family 14 (DS2430) driver
4c6bf3842SAngelo Dureghello **
5c6bf3842SAngelo Dureghello * Copyright (c) 2019 Angelo Dureghello <angelo.dureghello@timesys.com>
6c6bf3842SAngelo Dureghello *
7c6bf3842SAngelo Dureghello * Cloned and modified from ds2431
8c6bf3842SAngelo Dureghello * Copyright (c) 2008 Bernhard Weirich <bernhard.weirich@riedel.net>
9c6bf3842SAngelo Dureghello *
10c6bf3842SAngelo Dureghello */
11c6bf3842SAngelo Dureghello
12c6bf3842SAngelo Dureghello #include <linux/kernel.h>
13c6bf3842SAngelo Dureghello #include <linux/module.h>
14c6bf3842SAngelo Dureghello #include <linux/moduleparam.h>
15c6bf3842SAngelo Dureghello #include <linux/device.h>
16c6bf3842SAngelo Dureghello #include <linux/types.h>
17c6bf3842SAngelo Dureghello #include <linux/delay.h>
18c6bf3842SAngelo Dureghello
19c6bf3842SAngelo Dureghello #include <linux/w1.h>
20c6bf3842SAngelo Dureghello
21c6bf3842SAngelo Dureghello #define W1_EEPROM_DS2430 0x14
22c6bf3842SAngelo Dureghello
23c6bf3842SAngelo Dureghello #define W1_F14_EEPROM_SIZE 32
24c6bf3842SAngelo Dureghello #define W1_F14_PAGE_COUNT 1
25c6bf3842SAngelo Dureghello #define W1_F14_PAGE_BITS 5
26c6bf3842SAngelo Dureghello #define W1_F14_PAGE_SIZE (1 << W1_F14_PAGE_BITS)
27c6bf3842SAngelo Dureghello #define W1_F14_PAGE_MASK 0x1F
28c6bf3842SAngelo Dureghello
29c6bf3842SAngelo Dureghello #define W1_F14_SCRATCH_BITS 5
30c6bf3842SAngelo Dureghello #define W1_F14_SCRATCH_SIZE (1 << W1_F14_SCRATCH_BITS)
31c6bf3842SAngelo Dureghello #define W1_F14_SCRATCH_MASK (W1_F14_SCRATCH_SIZE-1)
32c6bf3842SAngelo Dureghello
33c6bf3842SAngelo Dureghello #define W1_F14_READ_EEPROM 0xF0
34c6bf3842SAngelo Dureghello #define W1_F14_WRITE_SCRATCH 0x0F
35c6bf3842SAngelo Dureghello #define W1_F14_READ_SCRATCH 0xAA
36c6bf3842SAngelo Dureghello #define W1_F14_COPY_SCRATCH 0x55
37c6bf3842SAngelo Dureghello #define W1_F14_VALIDATION_KEY 0xa5
38c6bf3842SAngelo Dureghello
39c6bf3842SAngelo Dureghello #define W1_F14_TPROG_MS 11
40c6bf3842SAngelo Dureghello #define W1_F14_READ_RETRIES 10
41c6bf3842SAngelo Dureghello #define W1_F14_READ_MAXLEN W1_F14_SCRATCH_SIZE
42c6bf3842SAngelo Dureghello
43c6bf3842SAngelo Dureghello /*
44c6bf3842SAngelo Dureghello * Check the file size bounds and adjusts count as needed.
45c6bf3842SAngelo Dureghello * This would not be needed if the file size didn't reset to 0 after a write.
46c6bf3842SAngelo Dureghello */
w1_f14_fix_count(loff_t off,size_t count,size_t size)47c6bf3842SAngelo Dureghello static inline size_t w1_f14_fix_count(loff_t off, size_t count, size_t size)
48c6bf3842SAngelo Dureghello {
49c6bf3842SAngelo Dureghello if (off > size)
50c6bf3842SAngelo Dureghello return 0;
51c6bf3842SAngelo Dureghello
52c6bf3842SAngelo Dureghello if ((off + count) > size)
53c6bf3842SAngelo Dureghello return size - off;
54c6bf3842SAngelo Dureghello
55c6bf3842SAngelo Dureghello return count;
56c6bf3842SAngelo Dureghello }
57c6bf3842SAngelo Dureghello
58c6bf3842SAngelo Dureghello /*
59c6bf3842SAngelo Dureghello * Read a block from W1 ROM two times and compares the results.
60c6bf3842SAngelo Dureghello * If they are equal they are returned, otherwise the read
61c6bf3842SAngelo Dureghello * is repeated W1_F14_READ_RETRIES times.
62c6bf3842SAngelo Dureghello *
63c6bf3842SAngelo Dureghello * count must not exceed W1_F14_READ_MAXLEN.
64c6bf3842SAngelo Dureghello */
w1_f14_readblock(struct w1_slave * sl,int off,int count,char * buf)65c6bf3842SAngelo Dureghello static int w1_f14_readblock(struct w1_slave *sl, int off, int count, char *buf)
66c6bf3842SAngelo Dureghello {
67c6bf3842SAngelo Dureghello u8 wrbuf[2];
68c6bf3842SAngelo Dureghello u8 cmp[W1_F14_READ_MAXLEN];
69c6bf3842SAngelo Dureghello int tries = W1_F14_READ_RETRIES;
70c6bf3842SAngelo Dureghello
71c6bf3842SAngelo Dureghello do {
72c6bf3842SAngelo Dureghello wrbuf[0] = W1_F14_READ_EEPROM;
73c6bf3842SAngelo Dureghello wrbuf[1] = off & 0xff;
74c6bf3842SAngelo Dureghello
75c6bf3842SAngelo Dureghello if (w1_reset_select_slave(sl))
76c6bf3842SAngelo Dureghello return -1;
77c6bf3842SAngelo Dureghello
78c6bf3842SAngelo Dureghello w1_write_block(sl->master, wrbuf, 2);
79c6bf3842SAngelo Dureghello w1_read_block(sl->master, buf, count);
80c6bf3842SAngelo Dureghello
81c6bf3842SAngelo Dureghello if (w1_reset_select_slave(sl))
82c6bf3842SAngelo Dureghello return -1;
83c6bf3842SAngelo Dureghello
84c6bf3842SAngelo Dureghello w1_write_block(sl->master, wrbuf, 2);
85c6bf3842SAngelo Dureghello w1_read_block(sl->master, cmp, count);
86c6bf3842SAngelo Dureghello
87c6bf3842SAngelo Dureghello if (!memcmp(cmp, buf, count))
88c6bf3842SAngelo Dureghello return 0;
89c6bf3842SAngelo Dureghello } while (--tries);
90c6bf3842SAngelo Dureghello
91c6bf3842SAngelo Dureghello dev_err(&sl->dev, "proof reading failed %d times\n",
92c6bf3842SAngelo Dureghello W1_F14_READ_RETRIES);
93c6bf3842SAngelo Dureghello
94c6bf3842SAngelo Dureghello return -1;
95c6bf3842SAngelo Dureghello }
96c6bf3842SAngelo Dureghello
eeprom_read(struct file * filp,struct kobject * kobj,struct bin_attribute * bin_attr,char * buf,loff_t off,size_t count)97c6bf3842SAngelo Dureghello static ssize_t eeprom_read(struct file *filp, struct kobject *kobj,
98c6bf3842SAngelo Dureghello struct bin_attribute *bin_attr, char *buf,
99c6bf3842SAngelo Dureghello loff_t off, size_t count)
100c6bf3842SAngelo Dureghello {
101c6bf3842SAngelo Dureghello struct w1_slave *sl = kobj_to_w1_slave(kobj);
102c6bf3842SAngelo Dureghello int todo = count;
103c6bf3842SAngelo Dureghello
104c6bf3842SAngelo Dureghello count = w1_f14_fix_count(off, count, W1_F14_EEPROM_SIZE);
105c6bf3842SAngelo Dureghello if (count == 0)
106c6bf3842SAngelo Dureghello return 0;
107c6bf3842SAngelo Dureghello
108c6bf3842SAngelo Dureghello mutex_lock(&sl->master->bus_mutex);
109c6bf3842SAngelo Dureghello
110c6bf3842SAngelo Dureghello /* read directly from the EEPROM in chunks of W1_F14_READ_MAXLEN */
111c6bf3842SAngelo Dureghello while (todo > 0) {
112c6bf3842SAngelo Dureghello int block_read;
113c6bf3842SAngelo Dureghello
114c6bf3842SAngelo Dureghello if (todo >= W1_F14_READ_MAXLEN)
115c6bf3842SAngelo Dureghello block_read = W1_F14_READ_MAXLEN;
116c6bf3842SAngelo Dureghello else
117c6bf3842SAngelo Dureghello block_read = todo;
118c6bf3842SAngelo Dureghello
119c6bf3842SAngelo Dureghello if (w1_f14_readblock(sl, off, block_read, buf) < 0)
120c6bf3842SAngelo Dureghello count = -EIO;
121c6bf3842SAngelo Dureghello
122c6bf3842SAngelo Dureghello todo -= W1_F14_READ_MAXLEN;
123c6bf3842SAngelo Dureghello buf += W1_F14_READ_MAXLEN;
124c6bf3842SAngelo Dureghello off += W1_F14_READ_MAXLEN;
125c6bf3842SAngelo Dureghello }
126c6bf3842SAngelo Dureghello
127c6bf3842SAngelo Dureghello mutex_unlock(&sl->master->bus_mutex);
128c6bf3842SAngelo Dureghello
129c6bf3842SAngelo Dureghello return count;
130c6bf3842SAngelo Dureghello }
131c6bf3842SAngelo Dureghello
132c6bf3842SAngelo Dureghello /*
133c6bf3842SAngelo Dureghello * Writes to the scratchpad and reads it back for verification.
134c6bf3842SAngelo Dureghello * Then copies the scratchpad to EEPROM.
135c6bf3842SAngelo Dureghello * The data must be aligned at W1_F14_SCRATCH_SIZE bytes and
136c6bf3842SAngelo Dureghello * must be W1_F14_SCRATCH_SIZE bytes long.
137c6bf3842SAngelo Dureghello * The master must be locked.
138c6bf3842SAngelo Dureghello *
139c6bf3842SAngelo Dureghello * @param sl The slave structure
140c6bf3842SAngelo Dureghello * @param addr Address for the write
141c6bf3842SAngelo Dureghello * @param len length must be <= (W1_F14_PAGE_SIZE - (addr & W1_F14_PAGE_MASK))
142c6bf3842SAngelo Dureghello * @param data The data to write
143c6bf3842SAngelo Dureghello * @return 0=Success -1=failure
144c6bf3842SAngelo Dureghello */
w1_f14_write(struct w1_slave * sl,int addr,int len,const u8 * data)145c6bf3842SAngelo Dureghello static int w1_f14_write(struct w1_slave *sl, int addr, int len, const u8 *data)
146c6bf3842SAngelo Dureghello {
147c6bf3842SAngelo Dureghello int tries = W1_F14_READ_RETRIES;
148c6bf3842SAngelo Dureghello u8 wrbuf[2];
149c6bf3842SAngelo Dureghello u8 rdbuf[W1_F14_SCRATCH_SIZE + 3];
150c6bf3842SAngelo Dureghello
151c6bf3842SAngelo Dureghello retry:
152c6bf3842SAngelo Dureghello
153c6bf3842SAngelo Dureghello /* Write the data to the scratchpad */
154c6bf3842SAngelo Dureghello if (w1_reset_select_slave(sl))
155c6bf3842SAngelo Dureghello return -1;
156c6bf3842SAngelo Dureghello
157c6bf3842SAngelo Dureghello wrbuf[0] = W1_F14_WRITE_SCRATCH;
158c6bf3842SAngelo Dureghello wrbuf[1] = addr & 0xff;
159c6bf3842SAngelo Dureghello
160c6bf3842SAngelo Dureghello w1_write_block(sl->master, wrbuf, 2);
161c6bf3842SAngelo Dureghello w1_write_block(sl->master, data, len);
162c6bf3842SAngelo Dureghello
163c6bf3842SAngelo Dureghello /* Read the scratchpad and verify */
164c6bf3842SAngelo Dureghello if (w1_reset_select_slave(sl))
165c6bf3842SAngelo Dureghello return -1;
166c6bf3842SAngelo Dureghello
167c6bf3842SAngelo Dureghello w1_write_8(sl->master, W1_F14_READ_SCRATCH);
168c6bf3842SAngelo Dureghello w1_read_block(sl->master, rdbuf, len + 2);
169c6bf3842SAngelo Dureghello
170c6bf3842SAngelo Dureghello /*
171c6bf3842SAngelo Dureghello * Compare what was read against the data written
172c6bf3842SAngelo Dureghello * Note: on read scratchpad, device returns 2 bulk 0xff bytes,
173c6bf3842SAngelo Dureghello * to be discarded.
174c6bf3842SAngelo Dureghello */
175c6bf3842SAngelo Dureghello if ((memcmp(data, &rdbuf[2], len) != 0)) {
176c6bf3842SAngelo Dureghello
177c6bf3842SAngelo Dureghello if (--tries)
178c6bf3842SAngelo Dureghello goto retry;
179c6bf3842SAngelo Dureghello
180c6bf3842SAngelo Dureghello dev_err(&sl->dev,
181c6bf3842SAngelo Dureghello "could not write to eeprom, scratchpad compare failed %d times\n",
182c6bf3842SAngelo Dureghello W1_F14_READ_RETRIES);
183c6bf3842SAngelo Dureghello
184c6bf3842SAngelo Dureghello return -1;
185c6bf3842SAngelo Dureghello }
186c6bf3842SAngelo Dureghello
187c6bf3842SAngelo Dureghello /* Copy the scratchpad to EEPROM */
188c6bf3842SAngelo Dureghello if (w1_reset_select_slave(sl))
189c6bf3842SAngelo Dureghello return -1;
190c6bf3842SAngelo Dureghello
191c6bf3842SAngelo Dureghello wrbuf[0] = W1_F14_COPY_SCRATCH;
192c6bf3842SAngelo Dureghello wrbuf[1] = W1_F14_VALIDATION_KEY;
193c6bf3842SAngelo Dureghello w1_write_block(sl->master, wrbuf, 2);
194c6bf3842SAngelo Dureghello
195c6bf3842SAngelo Dureghello /* Sleep for tprog ms to wait for the write to complete */
196c6bf3842SAngelo Dureghello msleep(W1_F14_TPROG_MS);
197c6bf3842SAngelo Dureghello
198c6bf3842SAngelo Dureghello /* Reset the bus to wake up the EEPROM */
199c6bf3842SAngelo Dureghello w1_reset_bus(sl->master);
200c6bf3842SAngelo Dureghello
201c6bf3842SAngelo Dureghello return 0;
202c6bf3842SAngelo Dureghello }
203c6bf3842SAngelo Dureghello
eeprom_write(struct file * filp,struct kobject * kobj,struct bin_attribute * bin_attr,char * buf,loff_t off,size_t count)204c6bf3842SAngelo Dureghello static ssize_t eeprom_write(struct file *filp, struct kobject *kobj,
205c6bf3842SAngelo Dureghello struct bin_attribute *bin_attr, char *buf,
206c6bf3842SAngelo Dureghello loff_t off, size_t count)
207c6bf3842SAngelo Dureghello {
208c6bf3842SAngelo Dureghello struct w1_slave *sl = kobj_to_w1_slave(kobj);
209c6bf3842SAngelo Dureghello int addr, len;
210c6bf3842SAngelo Dureghello int copy;
211c6bf3842SAngelo Dureghello
212c6bf3842SAngelo Dureghello count = w1_f14_fix_count(off, count, W1_F14_EEPROM_SIZE);
213c6bf3842SAngelo Dureghello if (count == 0)
214c6bf3842SAngelo Dureghello return 0;
215c6bf3842SAngelo Dureghello
216c6bf3842SAngelo Dureghello mutex_lock(&sl->master->bus_mutex);
217c6bf3842SAngelo Dureghello
218c6bf3842SAngelo Dureghello /* Can only write data in blocks of the size of the scratchpad */
219c6bf3842SAngelo Dureghello addr = off;
220c6bf3842SAngelo Dureghello len = count;
221c6bf3842SAngelo Dureghello while (len > 0) {
222c6bf3842SAngelo Dureghello
223c6bf3842SAngelo Dureghello /* if len too short or addr not aligned */
224c6bf3842SAngelo Dureghello if (len < W1_F14_SCRATCH_SIZE || addr & W1_F14_SCRATCH_MASK) {
225c6bf3842SAngelo Dureghello char tmp[W1_F14_SCRATCH_SIZE];
226c6bf3842SAngelo Dureghello
227c6bf3842SAngelo Dureghello /* read the block and update the parts to be written */
228c6bf3842SAngelo Dureghello if (w1_f14_readblock(sl, addr & ~W1_F14_SCRATCH_MASK,
229c6bf3842SAngelo Dureghello W1_F14_SCRATCH_SIZE, tmp)) {
230c6bf3842SAngelo Dureghello count = -EIO;
231c6bf3842SAngelo Dureghello goto out_up;
232c6bf3842SAngelo Dureghello }
233c6bf3842SAngelo Dureghello
234c6bf3842SAngelo Dureghello /* copy at most to the boundary of the PAGE or len */
235c6bf3842SAngelo Dureghello copy = W1_F14_SCRATCH_SIZE -
236c6bf3842SAngelo Dureghello (addr & W1_F14_SCRATCH_MASK);
237c6bf3842SAngelo Dureghello
238c6bf3842SAngelo Dureghello if (copy > len)
239c6bf3842SAngelo Dureghello copy = len;
240c6bf3842SAngelo Dureghello
241c6bf3842SAngelo Dureghello memcpy(&tmp[addr & W1_F14_SCRATCH_MASK], buf, copy);
242c6bf3842SAngelo Dureghello if (w1_f14_write(sl, addr & ~W1_F14_SCRATCH_MASK,
243c6bf3842SAngelo Dureghello W1_F14_SCRATCH_SIZE, tmp) < 0) {
244c6bf3842SAngelo Dureghello count = -EIO;
245c6bf3842SAngelo Dureghello goto out_up;
246c6bf3842SAngelo Dureghello }
247c6bf3842SAngelo Dureghello } else {
248c6bf3842SAngelo Dureghello
249c6bf3842SAngelo Dureghello copy = W1_F14_SCRATCH_SIZE;
250c6bf3842SAngelo Dureghello if (w1_f14_write(sl, addr, copy, buf) < 0) {
251c6bf3842SAngelo Dureghello count = -EIO;
252c6bf3842SAngelo Dureghello goto out_up;
253c6bf3842SAngelo Dureghello }
254c6bf3842SAngelo Dureghello }
255c6bf3842SAngelo Dureghello buf += copy;
256c6bf3842SAngelo Dureghello addr += copy;
257c6bf3842SAngelo Dureghello len -= copy;
258c6bf3842SAngelo Dureghello }
259c6bf3842SAngelo Dureghello
260c6bf3842SAngelo Dureghello out_up:
261c6bf3842SAngelo Dureghello mutex_unlock(&sl->master->bus_mutex);
262c6bf3842SAngelo Dureghello
263c6bf3842SAngelo Dureghello return count;
264c6bf3842SAngelo Dureghello }
265c6bf3842SAngelo Dureghello
266c6bf3842SAngelo Dureghello static BIN_ATTR_RW(eeprom, W1_F14_EEPROM_SIZE);
267c6bf3842SAngelo Dureghello
268c6bf3842SAngelo Dureghello static struct bin_attribute *w1_f14_bin_attrs[] = {
269c6bf3842SAngelo Dureghello &bin_attr_eeprom,
270c6bf3842SAngelo Dureghello NULL,
271c6bf3842SAngelo Dureghello };
272c6bf3842SAngelo Dureghello
273c6bf3842SAngelo Dureghello static const struct attribute_group w1_f14_group = {
274c6bf3842SAngelo Dureghello .bin_attrs = w1_f14_bin_attrs,
275c6bf3842SAngelo Dureghello };
276c6bf3842SAngelo Dureghello
277c6bf3842SAngelo Dureghello static const struct attribute_group *w1_f14_groups[] = {
278c6bf3842SAngelo Dureghello &w1_f14_group,
279c6bf3842SAngelo Dureghello NULL,
280c6bf3842SAngelo Dureghello };
281c6bf3842SAngelo Dureghello
282*57de2dfcSRikard Falkeborn static const struct w1_family_ops w1_f14_fops = {
283c6bf3842SAngelo Dureghello .groups = w1_f14_groups,
284c6bf3842SAngelo Dureghello };
285c6bf3842SAngelo Dureghello
286c6bf3842SAngelo Dureghello static struct w1_family w1_family_14 = {
287c6bf3842SAngelo Dureghello .fid = W1_EEPROM_DS2430,
288c6bf3842SAngelo Dureghello .fops = &w1_f14_fops,
289c6bf3842SAngelo Dureghello };
290c6bf3842SAngelo Dureghello module_w1_family(w1_family_14);
291c6bf3842SAngelo Dureghello
292c6bf3842SAngelo Dureghello MODULE_AUTHOR("Angelo Dureghello <angelo.dureghello@timesys.com>");
293cafa1a5bSAngelo Dureghello MODULE_DESCRIPTION("w1 family 14 driver for DS2430, 256b EEPROM");
294c6bf3842SAngelo Dureghello MODULE_LICENSE("GPL");
295c6bf3842SAngelo Dureghello MODULE_ALIAS("w1-family-" __stringify(W1_EEPROM_DS2430));
296