w1-eeprom: Add support for Maxim DS2502 add only memory
authorMartin Fuzzey <martin.fuzzey@flowbird.group>
Wed, 24 Oct 2018 08:21:19 +0000 (10:21 +0200)
committerTom Rini <trini@konsulko.com>
Fri, 16 Nov 2018 21:51:55 +0000 (16:51 -0500)
Signed-off-by: Martin Fuzzey <martin.fuzzey@flowbird.group>
doc/device-tree-bindings/w1-eeprom/ds2502.txt [new file with mode: 0644]
drivers/w1-eeprom/Kconfig
drivers/w1-eeprom/Makefile
drivers/w1-eeprom/ds2502.c [new file with mode: 0644]
include/w1.h

diff --git a/doc/device-tree-bindings/w1-eeprom/ds2502.txt b/doc/device-tree-bindings/w1-eeprom/ds2502.txt
new file mode 100644 (file)
index 0000000..7f05fc4
--- /dev/null
@@ -0,0 +1,33 @@
+Maxim DS2502 driver device binding - one wire protocol add only memory from Maxim
+=======================
+
+This memory needs to be connected to a onewire bus, as a child node.
+The bus will read the device serial number and match this node with a found
+device on the bus
+Also check doc/device-tree-bindings/w1 for onewire bus drivers
+
+Driver:
+- drivers/w1-eeprom/ds2502.c
+
+Ds2502 device-tree node properties:
+Required:
+* compatible = "maxim,ds2502"
+
+Optional:
+* none
+
+Example:
+       eeprom1: eeprom@0 {
+               compatible = "maxim,ds2502";
+       };
+
+Example with parent bus:
+       onewire {
+               compatible = "fsl,imx53-owire";
+               reg = <0x63fa4000 0x4000>;
+
+               eeprom1: eeprom@0 {
+                       compatible = "maxim,ds2502";
+               };
+       };
+
index 4b7f3c4e0b84f29e0f61a9158a28b2f422fbc38f..34aca4b9001ab339089b135f0e920a27a0408b0d 100644 (file)
@@ -18,6 +18,19 @@ config W1_EEPROM_DS24XXX
        help
          Maxim DS24 EEPROMs 1-Wire EEPROM support
 
+config W1_EEPROM_DS2502
+       bool "Enable Maxim DS2502 Add-Only Memory support"
+       depends on W1
+       help
+         Maxim DS2502 1-Wire add-only memory support.
+         This device has 128 bytes of data memory, organized as 4 pages of
+         32 bytes and 8 out of band status bytes that may be used to redirect
+         pages, allowing data to be modified up to 4 times (by external
+         programming).
+
+         The device may be seen as a 32 byte memory, using the page redirection
+         or as a 128 byte memory, ignoring the page redirection.
+
 config W1_EEPROM_SANDBOX
        bool "Enable sandbox onewire EEPROM driver"
        depends on W1
index 03cc4c8ac8db79b56db9e00d6f21ace715773432..83f4008bb55f98c4e8815bb4f7b60954c1edb1a7 100644 (file)
@@ -1,5 +1,6 @@
 obj-$(CONFIG_W1_EEPROM) += w1-eeprom-uclass.o
 
 obj-$(CONFIG_W1_EEPROM_DS24XXX) += ds24xxx.o
+obj-$(CONFIG_W1_EEPROM_DS2502) += ds2502.o
 
 obj-$(CONFIG_W1_EEPROM_SANDBOX) += eep_sandbox.o
diff --git a/drivers/w1-eeprom/ds2502.c b/drivers/w1-eeprom/ds2502.c
new file mode 100644 (file)
index 0000000..76ca460
--- /dev/null
@@ -0,0 +1,244 @@
+// 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 <common.h>
+#include <dm.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_platdata(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,
+};
index 399177a12e34b5c651f0373905d35d950c3763ff..b958b1c92c2f3c3e39d6cbe913136c68d74d70ac 100644 (file)
@@ -12,6 +12,7 @@
 
 #define W1_FAMILY_DS24B33      0x23
 #define W1_FAMILY_DS2431       0x2d
+#define W1_FAMILY_DS2502       0x09
 #define W1_FAMILY_EEP_SANDBOX  0xfe
 
 struct w1_device {