This patch adds support for reading the unique ID from ONFI-compliant NAND
flash devices.

The unique ID is a 32 bytes long identifier that is useful for device
identification and tracking.

Tested on SAM9G25-EK with Macronix MX60LF8G18AC flash.

Signed-off-by: Zixun LI <[email protected]>
---
 drivers/mtd/nand/raw/nand_base.c | 69 ++++++++++++++++++++++++++++++++
 include/linux/mtd/rawnand.h      |  8 ++++
 2 files changed, 77 insertions(+)

diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c
index 48e3685d995..30aa578e074 100644
--- a/drivers/mtd/nand/raw/nand_base.c
+++ b/drivers/mtd/nand/raw/nand_base.c
@@ -991,6 +991,75 @@ static int nand_onfi_set_timings(struct mtd_info *mtd, 
struct nand_chip *chip)
                                       tmode_param);
 }
 
+/**
+ * nand_onfi_read_unique_id - read the ONFI unique ID sequence
+ * @chip: NAND chip descriptor
+ * @buf: destination buffer
+ * @len: size of the destination buffer
+ *
+ * Issues the ONFI UNIQUE_ID command sequence, validates the unique ID and
+ * copies it into @buf.
+ *
+ * Return: 0 on success or negative error code otherwise.
+ */
+int nand_onfi_read_unique_id(struct nand_chip *chip, void *buf,
+                            unsigned int len)
+{
+       struct mtd_info *mtd;
+       unsigned int copy;
+       unsigned int byte;
+       unsigned int copy_len;
+       u8 raw_id[ONFI_UNIQUE_ID_LEN * 2];
+       u8 *out = buf;
+       int ret = -EIO;
+
+       if (!chip)
+               return -EINVAL;
+
+       if (len && !buf)
+               return -EINVAL;
+
+       if (len > ONFI_UNIQUE_ID_LEN)
+               return -EINVAL;
+
+       if (!chip->onfi_version)
+               return -EOPNOTSUPP;
+
+       mtd = nand_to_mtd(chip);
+       nand_get_device(mtd, FL_READING);
+       chip->select_chip(mtd, 0);
+
+       chip->cmdfunc(mtd, NAND_CMD_UNIQUE_ID, 0, -1);
+
+       for (copy = 0; copy < ONFI_UNIQUE_ID_COPIES; copy++) {
+               for (byte = 0; byte < ONFI_UNIQUE_ID_LEN * 2; byte++)
+                       raw_id[byte] = chip->read_byte(mtd);
+
+               for (byte = 0; byte < ONFI_UNIQUE_ID_LEN; byte++) {
+                       u8 lo = raw_id[byte];
+                       u8 hi = raw_id[byte + ONFI_UNIQUE_ID_LEN];
+
+                       if ((lo ^ hi) != 0xff)
+                               break;
+               }
+
+               if (byte == ONFI_UNIQUE_ID_LEN) {
+                       copy_len = min_t(unsigned int, len, ONFI_UNIQUE_ID_LEN);
+
+                       if (len)
+                               memcpy(out, raw_id, copy_len);
+
+                       ret = 0;
+                       goto out;
+               }
+       }
+
+out:
+       nand_release_device(mtd);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(nand_onfi_read_unique_id);
+
 /**
  * nand_setup_data_interface - Setup the best data interface and timings
  * @chip: The NAND chip
diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h
index 3e80b134063..87083c34aed 100644
--- a/include/linux/mtd/rawnand.h
+++ b/include/linux/mtd/rawnand.h
@@ -88,6 +88,7 @@ void nand_wait_ready(struct mtd_info *mtd);
 #define NAND_CMD_READID                0x90
 #define NAND_CMD_ERASE2                0xd0
 #define NAND_CMD_PARAM         0xec
+#define NAND_CMD_UNIQUE_ID     0xed
 #define NAND_CMD_GET_FEATURES  0xee
 #define NAND_CMD_SET_FEATURES  0xef
 #define NAND_CMD_RESET         0xff
@@ -409,6 +410,10 @@ struct onfi_ext_param_page {
         */
 } __packed;
 
+/* ONFI unique ID */
+#define ONFI_UNIQUE_ID_LEN 16
+#define ONFI_UNIQUE_ID_COPIES 16
+
 struct jedec_ecc_info {
        u8 ecc_bits;
        u8 codeword_size;
@@ -1385,4 +1390,7 @@ int nand_write_data_op(struct nand_chip *chip, const void 
*buf,
 /* Default extended ID decoding function */
 void nand_decode_ext_id(struct nand_chip *chip);
 
+int nand_onfi_read_unique_id(struct nand_chip *chip, void *buf,
+                            unsigned int len);
+
 #endif /* __LINUX_MTD_RAWNAND_H */
-- 
2.51.0

Reply via email to