xref: /openbmc/linux/drivers/w1/slaves/w1_ds2805.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
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