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