140b0b3f8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2f6887531SAndrew Worsley /*
3f6887531SAndrew Worsley * w1_ds2805 - w1 family 0d (DS28E05) driver
4f6887531SAndrew Worsley *
5f6887531SAndrew Worsley * Copyright (c) 2016 Andrew Worsley amworsley@gmail.com
6f6887531SAndrew Worsley */
7f6887531SAndrew Worsley
8f6887531SAndrew Worsley #include <linux/kernel.h>
9f6887531SAndrew Worsley #include <linux/module.h>
10f6887531SAndrew Worsley #include <linux/moduleparam.h>
11f6887531SAndrew Worsley #include <linux/device.h>
12f6887531SAndrew Worsley #include <linux/types.h>
13f6887531SAndrew Worsley #include <linux/delay.h>
14f6887531SAndrew Worsley
15f6887531SAndrew Worsley #include <linux/w1.h>
16f6887531SAndrew Worsley
17f6887531SAndrew Worsley #define W1_EEPROM_DS2805 0x0D
18f6887531SAndrew Worsley
19f6887531SAndrew Worsley #define W1_F0D_EEPROM_SIZE 128
20f6887531SAndrew Worsley #define W1_F0D_PAGE_BITS 3
21f6887531SAndrew Worsley #define W1_F0D_PAGE_SIZE (1<<W1_F0D_PAGE_BITS)
22f6887531SAndrew Worsley #define W1_F0D_PAGE_MASK 0x0F
23f6887531SAndrew Worsley
24f6887531SAndrew Worsley #define W1_F0D_SCRATCH_BITS 1
25f6887531SAndrew Worsley #define W1_F0D_SCRATCH_SIZE (1<<W1_F0D_SCRATCH_BITS)
26f6887531SAndrew Worsley #define W1_F0D_SCRATCH_MASK (W1_F0D_SCRATCH_SIZE-1)
27f6887531SAndrew Worsley
28f6887531SAndrew Worsley #define W1_F0D_READ_EEPROM 0xF0
29f6887531SAndrew Worsley #define W1_F0D_WRITE_EEPROM 0x55
30f6887531SAndrew Worsley #define W1_F0D_RELEASE 0xFF
31f6887531SAndrew Worsley
32f6887531SAndrew Worsley #define W1_F0D_CS_OK 0xAA /* Chip Status Ok */
33f6887531SAndrew Worsley
34f6887531SAndrew Worsley #define W1_F0D_TPROG_MS 16
35f6887531SAndrew Worsley
36f6887531SAndrew Worsley #define W1_F0D_READ_RETRIES 10
37f6887531SAndrew Worsley #define W1_F0D_READ_MAXLEN W1_F0D_EEPROM_SIZE
38f6887531SAndrew Worsley
39f6887531SAndrew Worsley /*
40f6887531SAndrew Worsley * Check the file size bounds and adjusts count as needed.
41f6887531SAndrew Worsley * This would not be needed if the file size didn't reset to 0 after a write.
42f6887531SAndrew Worsley */
w1_f0d_fix_count(loff_t off,size_t count,size_t size)43f6887531SAndrew Worsley static inline size_t w1_f0d_fix_count(loff_t off, size_t count, size_t size)
44f6887531SAndrew Worsley {
45f6887531SAndrew Worsley if (off > size)
46f6887531SAndrew Worsley return 0;
47f6887531SAndrew Worsley
48f6887531SAndrew Worsley if ((off + count) > size)
49f6887531SAndrew Worsley return size - off;
50f6887531SAndrew Worsley
51f6887531SAndrew Worsley return count;
52f6887531SAndrew Worsley }
53f6887531SAndrew Worsley
54f6887531SAndrew Worsley /*
55f6887531SAndrew Worsley * Read a block from W1 ROM two times and compares the results.
56f6887531SAndrew Worsley * If they are equal they are returned, otherwise the read
57f6887531SAndrew Worsley * is repeated W1_F0D_READ_RETRIES times.
58f6887531SAndrew Worsley *
59f6887531SAndrew Worsley * count must not exceed W1_F0D_READ_MAXLEN.
60f6887531SAndrew Worsley */
w1_f0d_readblock(struct w1_slave * sl,int off,int count,char * buf)61f6887531SAndrew Worsley static int w1_f0d_readblock(struct w1_slave *sl, int off, int count, char *buf)
62f6887531SAndrew Worsley {
63f6887531SAndrew Worsley u8 wrbuf[3];
64f6887531SAndrew Worsley u8 cmp[W1_F0D_READ_MAXLEN];
65f6887531SAndrew Worsley int tries = W1_F0D_READ_RETRIES;
66f6887531SAndrew Worsley
67f6887531SAndrew Worsley do {
68f6887531SAndrew Worsley wrbuf[0] = W1_F0D_READ_EEPROM;
69f6887531SAndrew Worsley wrbuf[1] = off & 0x7f;
70f6887531SAndrew Worsley wrbuf[2] = 0;
71f6887531SAndrew Worsley
72f6887531SAndrew Worsley if (w1_reset_select_slave(sl))
73f6887531SAndrew Worsley return -1;
74f6887531SAndrew Worsley
75f6887531SAndrew Worsley w1_write_block(sl->master, wrbuf, sizeof(wrbuf));
76f6887531SAndrew Worsley w1_read_block(sl->master, buf, count);
77f6887531SAndrew Worsley
78f6887531SAndrew Worsley if (w1_reset_select_slave(sl))
79f6887531SAndrew Worsley return -1;
80f6887531SAndrew Worsley
81f6887531SAndrew Worsley w1_write_block(sl->master, wrbuf, sizeof(wrbuf));
82f6887531SAndrew Worsley w1_read_block(sl->master, cmp, count);
83f6887531SAndrew Worsley
84f6887531SAndrew Worsley if (!memcmp(cmp, buf, count))
85f6887531SAndrew Worsley return 0;
86f6887531SAndrew Worsley } while (--tries);
87f6887531SAndrew Worsley
88f6887531SAndrew Worsley dev_err(&sl->dev, "proof reading failed %d times\n",
89f6887531SAndrew Worsley W1_F0D_READ_RETRIES);
90f6887531SAndrew Worsley
91f6887531SAndrew Worsley return -1;
92f6887531SAndrew Worsley }
93f6887531SAndrew Worsley
w1_f0d_read_bin(struct file * filp,struct kobject * kobj,struct bin_attribute * bin_attr,char * buf,loff_t off,size_t count)94f6887531SAndrew Worsley static ssize_t w1_f0d_read_bin(struct file *filp, struct kobject *kobj,
95f6887531SAndrew Worsley struct bin_attribute *bin_attr,
96f6887531SAndrew Worsley char *buf, loff_t off, size_t count)
97f6887531SAndrew Worsley {
98f6887531SAndrew Worsley struct w1_slave *sl = kobj_to_w1_slave(kobj);
99f6887531SAndrew Worsley int todo = count;
100f6887531SAndrew Worsley
101f6887531SAndrew Worsley count = w1_f0d_fix_count(off, count, W1_F0D_EEPROM_SIZE);
102f6887531SAndrew Worsley if (count == 0)
103f6887531SAndrew Worsley return 0;
104f6887531SAndrew Worsley
105f6887531SAndrew Worsley mutex_lock(&sl->master->mutex);
106f6887531SAndrew Worsley
107f6887531SAndrew Worsley /* read directly from the EEPROM in chunks of W1_F0D_READ_MAXLEN */
108f6887531SAndrew Worsley while (todo > 0) {
109f6887531SAndrew Worsley int block_read;
110f6887531SAndrew Worsley
111f6887531SAndrew Worsley if (todo >= W1_F0D_READ_MAXLEN)
112f6887531SAndrew Worsley block_read = W1_F0D_READ_MAXLEN;
113f6887531SAndrew Worsley else
114f6887531SAndrew Worsley block_read = todo;
115f6887531SAndrew Worsley
116f6887531SAndrew Worsley if (w1_f0d_readblock(sl, off, block_read, buf) < 0) {
117f6887531SAndrew Worsley count = -EIO;
118f6887531SAndrew Worsley break;
119f6887531SAndrew Worsley }
120f6887531SAndrew Worsley
121f6887531SAndrew Worsley todo -= W1_F0D_READ_MAXLEN;
122f6887531SAndrew Worsley buf += W1_F0D_READ_MAXLEN;
123f6887531SAndrew Worsley off += W1_F0D_READ_MAXLEN;
124f6887531SAndrew Worsley }
125f6887531SAndrew Worsley
126f6887531SAndrew Worsley mutex_unlock(&sl->master->mutex);
127f6887531SAndrew Worsley
128f6887531SAndrew Worsley return count;
129f6887531SAndrew Worsley }
130f6887531SAndrew Worsley
131f6887531SAndrew Worsley /*
132f6887531SAndrew Worsley * Writes to the scratchpad and reads it back for verification.
133f6887531SAndrew Worsley * Then copies the scratchpad to EEPROM.
134f6887531SAndrew Worsley * The data must be aligned at W1_F0D_SCRATCH_SIZE bytes and
135f6887531SAndrew Worsley * must be W1_F0D_SCRATCH_SIZE bytes long.
136f6887531SAndrew Worsley * The master must be locked.
137f6887531SAndrew Worsley *
138f6887531SAndrew Worsley * @param sl The slave structure
139f6887531SAndrew Worsley * @param addr Address for the write
140f6887531SAndrew Worsley * @param len length must be <= (W1_F0D_PAGE_SIZE - (addr & W1_F0D_PAGE_MASK))
141f6887531SAndrew Worsley * @param data The data to write
142f6887531SAndrew Worsley * @return 0=Success -1=failure
143f6887531SAndrew Worsley */
w1_f0d_write(struct w1_slave * sl,int addr,int len,const u8 * data)144f6887531SAndrew Worsley static int w1_f0d_write(struct w1_slave *sl, int addr, int len, const u8 *data)
145f6887531SAndrew Worsley {
146f6887531SAndrew Worsley int tries = W1_F0D_READ_RETRIES;
147f6887531SAndrew Worsley u8 wrbuf[3];
148f6887531SAndrew Worsley u8 rdbuf[W1_F0D_SCRATCH_SIZE];
149f6887531SAndrew Worsley u8 cs;
150f6887531SAndrew Worsley
151f6887531SAndrew Worsley if ((addr & 1) || (len != 2)) {
152f6887531SAndrew Worsley dev_err(&sl->dev, "%s: bad addr/len - addr=%#x len=%d\n",
153f6887531SAndrew Worsley __func__, addr, len);
154f6887531SAndrew Worsley return -1;
155f6887531SAndrew Worsley }
156f6887531SAndrew Worsley
157f6887531SAndrew Worsley retry:
158f6887531SAndrew Worsley
159f6887531SAndrew Worsley /* Write the data to the scratchpad */
160f6887531SAndrew Worsley if (w1_reset_select_slave(sl))
161f6887531SAndrew Worsley return -1;
162f6887531SAndrew Worsley
163f6887531SAndrew Worsley wrbuf[0] = W1_F0D_WRITE_EEPROM;
164f6887531SAndrew Worsley wrbuf[1] = addr & 0xff;
165f6887531SAndrew Worsley wrbuf[2] = 0xff; /* ?? from Example */
166f6887531SAndrew Worsley
167f6887531SAndrew Worsley w1_write_block(sl->master, wrbuf, sizeof(wrbuf));
168f6887531SAndrew Worsley w1_write_block(sl->master, data, len);
169f6887531SAndrew Worsley
170f6887531SAndrew Worsley w1_read_block(sl->master, rdbuf, sizeof(rdbuf));
171f6887531SAndrew Worsley /* Compare what was read against the data written */
172f6887531SAndrew Worsley if ((rdbuf[0] != data[0]) || (rdbuf[1] != data[1])) {
173f6887531SAndrew Worsley
174f6887531SAndrew Worsley if (--tries)
175f6887531SAndrew Worsley goto retry;
176f6887531SAndrew Worsley
177f6887531SAndrew Worsley dev_err(&sl->dev,
178f6887531SAndrew Worsley "could not write to eeprom, scratchpad compare failed %d times\n",
179f6887531SAndrew Worsley W1_F0D_READ_RETRIES);
180f6887531SAndrew Worsley pr_info("%s: rdbuf = %#x %#x data = %#x %#x\n",
181f6887531SAndrew Worsley __func__, rdbuf[0], rdbuf[1], data[0], data[1]);
182f6887531SAndrew Worsley
183f6887531SAndrew Worsley return -1;
184f6887531SAndrew Worsley }
185f6887531SAndrew Worsley
186f6887531SAndrew Worsley /* Trigger write out to EEPROM */
187f6887531SAndrew Worsley w1_write_8(sl->master, W1_F0D_RELEASE);
188f6887531SAndrew Worsley
189f6887531SAndrew Worsley /* Sleep for tprog ms to wait for the write to complete */
190f6887531SAndrew Worsley msleep(W1_F0D_TPROG_MS);
191f6887531SAndrew Worsley
192f6887531SAndrew Worsley /* Check CS (Command Status) == 0xAA ? */
193f6887531SAndrew Worsley cs = w1_read_8(sl->master);
194f6887531SAndrew Worsley if (cs != W1_F0D_CS_OK) {
195f6887531SAndrew Worsley dev_err(&sl->dev, "save to eeprom failed = CS=%#x\n", cs);
196f6887531SAndrew Worsley return -1;
197f6887531SAndrew Worsley }
198f6887531SAndrew Worsley
199f6887531SAndrew Worsley return 0;
200f6887531SAndrew Worsley }
201f6887531SAndrew Worsley
w1_f0d_write_bin(struct file * filp,struct kobject * kobj,struct bin_attribute * bin_attr,char * buf,loff_t off,size_t count)202f6887531SAndrew Worsley static ssize_t w1_f0d_write_bin(struct file *filp, struct kobject *kobj,
203f6887531SAndrew Worsley struct bin_attribute *bin_attr,
204f6887531SAndrew Worsley char *buf, loff_t off, size_t count)
205f6887531SAndrew Worsley {
206f6887531SAndrew Worsley struct w1_slave *sl = kobj_to_w1_slave(kobj);
207f6887531SAndrew Worsley int addr, len;
208f6887531SAndrew Worsley int copy;
209f6887531SAndrew Worsley
210f6887531SAndrew Worsley count = w1_f0d_fix_count(off, count, W1_F0D_EEPROM_SIZE);
211f6887531SAndrew Worsley if (count == 0)
212f6887531SAndrew Worsley return 0;
213f6887531SAndrew Worsley
214f6887531SAndrew Worsley mutex_lock(&sl->master->mutex);
215f6887531SAndrew Worsley
216f6887531SAndrew Worsley /* Can only write data in blocks of the size of the scratchpad */
217f6887531SAndrew Worsley addr = off;
218f6887531SAndrew Worsley len = count;
219f6887531SAndrew Worsley while (len > 0) {
220f6887531SAndrew Worsley
221f6887531SAndrew Worsley /* if len too short or addr not aligned */
222f6887531SAndrew Worsley if (len < W1_F0D_SCRATCH_SIZE || addr & W1_F0D_SCRATCH_MASK) {
223f6887531SAndrew Worsley char tmp[W1_F0D_SCRATCH_SIZE];
224f6887531SAndrew Worsley
225f6887531SAndrew Worsley /* read the block and update the parts to be written */
226f6887531SAndrew Worsley if (w1_f0d_readblock(sl, addr & ~W1_F0D_SCRATCH_MASK,
227f6887531SAndrew Worsley W1_F0D_SCRATCH_SIZE, tmp)) {
228f6887531SAndrew Worsley count = -EIO;
229f6887531SAndrew Worsley goto out_up;
230f6887531SAndrew Worsley }
231f6887531SAndrew Worsley
232f6887531SAndrew Worsley /* copy at most to the boundary of the PAGE or len */
233f6887531SAndrew Worsley copy = W1_F0D_SCRATCH_SIZE -
234f6887531SAndrew Worsley (addr & W1_F0D_SCRATCH_MASK);
235f6887531SAndrew Worsley
236f6887531SAndrew Worsley if (copy > len)
237f6887531SAndrew Worsley copy = len;
238f6887531SAndrew Worsley
239f6887531SAndrew Worsley memcpy(&tmp[addr & W1_F0D_SCRATCH_MASK], buf, copy);
240f6887531SAndrew Worsley if (w1_f0d_write(sl, addr & ~W1_F0D_SCRATCH_MASK,
241f6887531SAndrew Worsley W1_F0D_SCRATCH_SIZE, tmp) < 0) {
242f6887531SAndrew Worsley count = -EIO;
243f6887531SAndrew Worsley goto out_up;
244f6887531SAndrew Worsley }
245f6887531SAndrew Worsley } else {
246f6887531SAndrew Worsley
247f6887531SAndrew Worsley copy = W1_F0D_SCRATCH_SIZE;
248f6887531SAndrew Worsley if (w1_f0d_write(sl, addr, copy, buf) < 0) {
249f6887531SAndrew Worsley count = -EIO;
250f6887531SAndrew Worsley goto out_up;
251f6887531SAndrew Worsley }
252f6887531SAndrew Worsley }
253f6887531SAndrew Worsley buf += copy;
254f6887531SAndrew Worsley addr += copy;
255f6887531SAndrew Worsley len -= copy;
256f6887531SAndrew Worsley }
257f6887531SAndrew Worsley
258f6887531SAndrew Worsley out_up:
259f6887531SAndrew Worsley mutex_unlock(&sl->master->mutex);
260f6887531SAndrew Worsley
261f6887531SAndrew Worsley return count;
262f6887531SAndrew Worsley }
263f6887531SAndrew Worsley
264f6887531SAndrew Worsley static struct bin_attribute w1_f0d_bin_attr = {
265f6887531SAndrew Worsley .attr = {
266f6887531SAndrew Worsley .name = "eeprom",
267*921e0f2fSKrzysztof Kozlowski .mode = 0644,
268f6887531SAndrew Worsley },
269f6887531SAndrew Worsley .size = W1_F0D_EEPROM_SIZE,
270f6887531SAndrew Worsley .read = w1_f0d_read_bin,
271f6887531SAndrew Worsley .write = w1_f0d_write_bin,
272f6887531SAndrew Worsley };
273f6887531SAndrew Worsley
w1_f0d_add_slave(struct w1_slave * sl)274f6887531SAndrew Worsley static int w1_f0d_add_slave(struct w1_slave *sl)
275f6887531SAndrew Worsley {
276f6887531SAndrew Worsley return sysfs_create_bin_file(&sl->dev.kobj, &w1_f0d_bin_attr);
277f6887531SAndrew Worsley }
278f6887531SAndrew Worsley
w1_f0d_remove_slave(struct w1_slave * sl)279f6887531SAndrew Worsley static void w1_f0d_remove_slave(struct w1_slave *sl)
280f6887531SAndrew Worsley {
281f6887531SAndrew Worsley sysfs_remove_bin_file(&sl->dev.kobj, &w1_f0d_bin_attr);
282f6887531SAndrew Worsley }
283f6887531SAndrew Worsley
28457de2dfcSRikard Falkeborn static const struct w1_family_ops w1_f0d_fops = {
285f6887531SAndrew Worsley .add_slave = w1_f0d_add_slave,
286f6887531SAndrew Worsley .remove_slave = w1_f0d_remove_slave,
287f6887531SAndrew Worsley };
288f6887531SAndrew Worsley
2890e3743d8SMariusz Bialonczyk static struct w1_family w1_family_0d = {
290f6887531SAndrew Worsley .fid = W1_EEPROM_DS2805,
291f6887531SAndrew Worsley .fops = &w1_f0d_fops,
292f6887531SAndrew Worsley };
293f6887531SAndrew Worsley
29488adcd66SChen Huang module_w1_family(w1_family_0d);
295f6887531SAndrew Worsley
296f6887531SAndrew Worsley MODULE_LICENSE("GPL");
297f6887531SAndrew Worsley MODULE_AUTHOR("Andrew Worsley amworsley@gmail.com");
298f6887531SAndrew Worsley MODULE_DESCRIPTION("w1 family 0d driver for DS2805, 1kb EEPROM");
299