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

