Merge tag 'u-boot-imx-20200623' of https://gitlab.denx.de/u-boot/custodians/u-boot-imx
[oweals/u-boot.git] / drivers / w1-eeprom / ds2502.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Driver for DS-2502 One wire "Add only Memory".
4  *
5  * The chip has 4 pages of 32 bytes.
6  * In addition it has 8 out of band status bytes that are used, by software,
7  * as page redirection bytes by an algorithm described in the data sheet.
8  * This is useful since data cannot be erased once written but it can be
9  * "patched" up to four times by switching pages.
10  *
11  * So, when a read request is entirely in the first page automatically
12  * apply the page redirection bytes (which allows the device to be seen as
13  * a 32 byte PROM, writable 4 times).
14  *
15  * If the read request is outside of or larger than the first page then read
16  * the raw data (which allows the device to be seen as a 128 byte PROM,
17  * writable once).
18  *
19  * Copyright (c) 2018 Flowbird
20  * Martin Fuzzey <martin.fuzzey@flowbird.group>
21  */
22
23 #include <common.h>
24 #include <dm.h>
25 #include <dm/device_compat.h>
26 #include <linux/err.h>
27 #include <w1-eeprom.h>
28 #include <w1.h>
29
30 #define DS2502_PAGE_SIZE        32
31 #define DS2502_PAGE_COUNT       4
32 #define DS2502_STATUS_SIZE      8
33
34 #define DS2502_CMD_READ_STATUS  0xAA
35 #define DS2502_CMD_READ_GEN_CRC 0xC3
36
37 /* u-boot crc8() is CCITT CRC8, we need x^8 + x^5 + x^4 + 1 LSB first */
38 static unsigned int ds2502_crc8(const u8 *buf, int len)
39 {
40         static const u8 poly = 0x8C;  /* (1 + x^4 + x^5) + x^8 */
41         u8 crc = 0;
42         int i;
43
44         for (i = 0; i < len; i++) {
45                 u8 data = buf[i];
46                 int j;
47
48                 for (j = 0; j < 8; j++) {
49                         u8 mix = (crc ^ data) & 1;
50
51                         crc >>= 1;
52                         if (mix)
53                                 crc ^= poly;
54                         data >>= 1;
55                 }
56         }
57         return crc;
58 }
59
60 static int ds2502_read(struct udevice *dev, u8 cmd,
61                        int bytes_in_page, int pos,
62                        u8 *buf, int bytes_for_user)
63 {
64         int retry;
65         int ret = 0;
66
67         for (retry = 0; retry < 3; retry++) {
68                 u8 pagebuf[DS2502_PAGE_SIZE + 1]; /* 1 byte for CRC8 */
69                 u8 crc;
70                 int i;
71
72                 ret = w1_reset_select(dev);
73                 if (ret)
74                         return ret;
75
76                 /* send read to end of page and generate CRC command */
77                 pagebuf[0] = cmd;
78                 pagebuf[1] = pos & 0xff;
79                 pagebuf[2] = pos >> 8;
80                 crc = ds2502_crc8(pagebuf, 3);
81                 for (i = 0; i < 3; i++)
82                         w1_write_byte(dev, pagebuf[i]);
83
84                 /* Check command CRC */
85                 ret = w1_read_byte(dev);
86                 if (ret < 0) {
87                         dev_dbg(dev, "Error %d reading command CRC\n", ret);
88                         continue;
89                 }
90
91                 if (ret != crc) {
92                         dev_dbg(dev,
93                                 "bad CRC8 for cmd %02x got=%02X exp=%02X\n",
94                                 cmd, ret, crc);
95                         ret = -EIO;
96                         continue;
97                 }
98
99                 /* read data and check CRC */
100                 ret = w1_read_buf(dev, pagebuf, bytes_in_page + 1);
101                 if (ret < 0) {
102                         dev_dbg(dev, "Error %d reading data\n", ret);
103                         continue;
104                 }
105
106                 crc = ds2502_crc8(pagebuf, bytes_in_page);
107                 if (crc == pagebuf[bytes_in_page]) {
108                         memcpy(buf, pagebuf, bytes_for_user);
109                         ret = 0;
110                         break;
111                 }
112                 dev_dbg(dev, "Bad CRC8 got=%02X exp=%02X pos=%04X\n",
113                         pagebuf[bytes_in_page], crc, pos);
114                 ret = -EIO;
115         }
116
117         return ret;
118 }
119
120 static inline int ds2502_read_status_bytes(struct udevice *dev, u8 *buf)
121 {
122         return ds2502_read(dev, DS2502_CMD_READ_STATUS,
123                                 DS2502_STATUS_SIZE, 0,
124                                 buf, DS2502_STATUS_SIZE);
125 }
126
127 /*
128  * Status bytes (from index 1) contain 1's complement page indirection
129  * So for N writes:
130  * N=1: ff ff ff ff ff ff ff 00
131  * N=2: ff fe ff ff ff ff ff 00
132  * N=3: ff fe fd ff ff ff ff 00
133  * N=4: ff fe fd fc ff ff ff 00
134  */
135 static int ds2502_indirect_page(struct udevice *dev, u8 *status, int page)
136 {
137         int page_seen = 0;
138
139         do {
140                 u8 sb = status[page + 1];
141
142                 if (sb == 0xff)
143                         break;
144
145                 page = ~sb & 0xff;
146
147                 if (page >= DS2502_PAGE_COUNT) {
148                         dev_err(dev,
149                                 "Illegal page redirection status byte %02x\n",
150                                 sb);
151                         return -EINVAL;
152                 }
153
154                 if (page_seen & (1 << page)) {
155                         dev_err(dev, "Infinite loop in page redirection\n");
156                         return -EINVAL;
157                 }
158
159                 page_seen |= (1 << page);
160         } while (1);
161
162         return page;
163 }
164
165 static int ds2502_read_buf(struct udevice *dev, unsigned int offset,
166                            u8 *buf, unsigned int count)
167 {
168         unsigned int min_page = offset / DS2502_PAGE_SIZE;
169         unsigned int max_page = (offset + count - 1) / DS2502_PAGE_SIZE;
170         int xfered = 0;
171         u8 status_bytes[DS2502_STATUS_SIZE];
172         int i;
173         int ret;
174
175         if (min_page >= DS2502_PAGE_COUNT || max_page >= DS2502_PAGE_COUNT)
176                 return -EINVAL;
177
178         if (min_page == 0 && max_page == 0) {
179                 ret = ds2502_read_status_bytes(dev, status_bytes);
180                 if (ret)
181                         return ret;
182         } else {
183                 /* Dummy one to one page redirection */
184                 memset(status_bytes, 0xff, sizeof(status_bytes));
185         }
186
187         for (i = min_page; i <= max_page; i++) {
188                 int page;
189                 int pos;
190                 int bytes_in_page;
191                 int bytes_for_user;
192
193                 page = ds2502_indirect_page(dev, status_bytes, i);
194                 if (page < 0)
195                         return page;
196                 dev_dbg(dev, "page logical %d => physical %d\n", i, page);
197
198                 pos = page * DS2502_PAGE_SIZE;
199                 if (i == min_page)
200                         pos += offset % DS2502_PAGE_SIZE;
201
202                 bytes_in_page = DS2502_PAGE_SIZE - (pos % DS2502_PAGE_SIZE);
203
204                 if (i == max_page)
205                         bytes_for_user = count - xfered;
206                 else
207                         bytes_for_user = bytes_in_page;
208
209                 ret = ds2502_read(dev, DS2502_CMD_READ_GEN_CRC,
210                                   bytes_in_page, pos,
211                                   &buf[xfered], bytes_for_user);
212                 if (ret < 0)
213                         return ret;
214
215                 xfered += bytes_for_user;
216         }
217
218         return 0;
219 }
220
221 static int ds2502_probe(struct udevice *dev)
222 {
223         struct w1_device *w1;
224
225         w1 = dev_get_parent_platdata(dev);
226         w1->id = 0;
227         return 0;
228 }
229
230 static const struct w1_eeprom_ops ds2502_ops = {
231         .read_buf       = ds2502_read_buf,
232 };
233
234 static const struct udevice_id ds2502_id[] = {
235         { .compatible = "maxim,ds2502", .data = W1_FAMILY_DS2502 },
236         { },
237 };
238
239 U_BOOT_DRIVER(ds2502) = {
240         .name           = "ds2502",
241         .id             = UCLASS_W1_EEPROM,
242         .of_match       = ds2502_id,
243         .ops            = &ds2502_ops,
244         .probe          = ds2502_probe,
245 };