OpenWrt读取SPI-NAND协议Flash芯片UniqueID详解(以华邦为例)

Flash芯片一般都有一个出厂时由制造商设定的Unique ID,唯一ID。获取到可以用来进行各类加密识别认证,作为设备唯一ID的一种。
本文以华邦品牌的flash芯片为例(W25N01GV、W25M02GV),如何在Linux下读取该ID。

阅读芯片手册,了解读取步骤

一般Unique ID信息都存放在otp区域里,otp区域是芯片上一块特殊的区域,读取前需要进行模式切换,具体切换流程需要阅读芯片手册,每个厂家都不一样。

在目录里,找到读取的相关页面

阅读文档可以得知,winbond这款芯片otp区域有十页,其中第一页就存放的Unique ID。
读取需要修改状态寄存器的OTP-E位,且读取完成后需要复位。否则会影响正常区域的读写。

下面给出patch

diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c
old mode 100644
new mode 100755
--- a/drivers/mtd/nand/spi/core.c
+++ b/drivers/mtd/nand/spi/core.c
@@ -48,6 +48,33 @@ static int spinand_read_status(struct spinand_device *spinand, u8 *status)
 	return spinand_read_reg_op(spinand, REG_STATUS, status);
 }
 
+static int spinand_read_status_reg2(struct spinand_device *spinand, u8 *status)
+{
+	return spinand_read_reg_op(spinand, REG_CFG, status);
+}
+
+static int spinand_read_write_status_reg2(struct spinand_device *spinand, int otp_en_flag)
+{
+	u8 val = 0;
+
+	spinand_read_status_reg2(spinand, &val);
+
+	if (otp_en_flag == 0)
+		val &= CFG_OTP_DISABLE;
+	else
+		val |= CFG_OTP_ENABLE;
+
+	spinand_write_reg_op(spinand, REG_CFG, val);
+
+	// reset val
+	val = 0;
+	spinand_read_status_reg2(spinand, &val);
+
+	return 0;
+}
+
 static int spinand_get_cfg(struct spinand_device *spinand, u8 *cfg)
 {
 	struct nand_device *nand = spinand_to_nand(spinand);
@@ -1048,6 +1075,105 @@ static const struct mtd_ooblayout_ops spinand_noecc_ooblayout = {
 	.free = spinand_noecc_ooblayout_free,
 };
 
+static int spinand_unique_id_read(void *priv, u8 *buf, int readlen) {
+	int ret;
+	u8 status;
+	struct spinand_device *spinand = (struct spinand_device *)priv;
+	struct device *dev = &spinand->spimem->spi->dev;
+	u32 addr[5]= {0x00,0x00,0x00,0x00,0x00};
+	int addrlen = 5;
+
+	typedef struct nand_pos my_pos;
+	my_pos pos;
+	typedef struct nand_page_io_req my_req;
+	my_req req;
+
+	if(addrlen != sizeof(struct nand_addr)/sizeof(unsigned int)) {
+		dev_err(dev, "Must provide correct addr(length) for spinand calibration\n");
+		return -EINVAL;
+	}
+
+
+	if (ret)
+		return ret;
+
+	/* We should store our golden data in first target because
+	 * we can't switch target at this moment.
+	 */
+	pos = (my_pos){
+		.target = 0,
+		.lun = *addr,
+		.plane = *(addr+1),
+		.eraseblock = *(addr+2),
+		.page = *(addr+3),
+	};
+
+	req = (my_req){
+		.type = NAND_PAGE_READ,
+		.pos = pos,
+		.dataoffs = *(addr+4),
+		.datalen = readlen,
+		.databuf.in = buf,
+		.mode = MTD_OPS_AUTO_OOB,
+	};
+
+	ret = spinand_load_page_op(spinand, &req);
+	if (ret)
+		return ret;
+
+	ret = spinand_wait(spinand, &status);
+	if (ret < 0)
+		return ret;
+
+	{
+		//struct spi_mem_op op = SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, buf, readlen);
+		struct spi_mem_op op = SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, buf, readlen);
+		ret = spi_mem_exec_op(spinand->spimem, &op);
+	}
+
+	return 0;
+}
+
+static int spi_nand_unique_id(struct spinand_device *spinand)
+{
+	int ret = 0;
+	u8 *buf;
+	int readlen = 32;
+
+	buf = kzalloc(readlen, GFP_KERNEL);
+	if(!buf){
+		printk("%s-%d; ERROR - kzalloc func: Insufficient memory allocation failed;\n", __func__, __LINE__);
+		return -ENOMEM;
+	}
+
+	// set Status Register-2, open OTP mode
+	spinand_read_write_status_reg2(spinand, 1);
+
+	spinand_unique_id_read(spinand, buf, readlen);
+
+	// copy spinand->uid from buf
+	memcpy(spinand->uid, buf, sizeof(spinand->uid));
+
+	// reset Status Register-2, close OTP mode
+	spinand_read_write_status_reg2(spinand, 0);
+
+	kfree(buf);
+
+	return 0;
+}
+
 static int spinand_init(struct spinand_device *spinand)
 {
 	struct device *dev = &spinand->spimem->spi->dev;
@@ -1094,6 +1220,16 @@ static int spinand_init(struct spinand_device *spinand)
 	if (ret)
 		goto err_free_bufs;
 
+	// init spinand->uid
+	memset(spinand->uid, 0, sizeof(spinand->uid));
+	// try read flash-chip unique ID
+	if(spi_nand_unique_id(spinand) == 0){
+		// sync uniqiue id
+		mtd->chip_uid = spinand->uid;
+	}
+
 	ret = spinand_upd_cfg(spinand, CFG_OTP_ENABLE, 0);
 	if (ret)
 		goto err_free_bufs;
diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h
old mode 100644
new mode 100755
index fabd98fe69ad2eeeed2e0b4bec0c5f39a7534320..61531db9ae2c4cd886a1e5863ed7146b8ed48337
--- a/include/linux/mtd/spinand.h
+++ b/include/linux/mtd/spinand.h
@@ -155,6 +155,7 @@
 #define CFG_OTP_ENABLE		BIT(6)
 #define CFG_ECC_ENABLE		BIT(4)
 #define CFG_QUAD_ENABLE		BIT(0)
+#define CFG_OTP_DISABLE		(~(BIT(6)))
 
 /* status register */
 #define REG_STATUS		0xc0
@@ -361,6 +362,14 @@ struct spinand_dirmap {
 	struct spi_mem_dirmap_desc *rdesc;
 };
 
+/*
+ * SPINAND unique ID length and number of repetitions. The full unique ID is the
+ * manufacturer ID (1B) plus the unique device ID (16B). Also count the '-'
+ * between both IDs and the '\0' at the end in the 'STRING_LEN'.
+ */
+#define SPINAND_UNIQUEID_LEN       16
+
 /**
  * struct spinand_device - SPI NAND device instance
  * @base: NAND device instance
@@ -386,6 +395,7 @@ struct spinand_dirmap {
  *		the stack
  * @manufacturer: SPI NAND manufacturer information
  * @priv: manufacturer private data
+ * @uid: Unique ID of the flash chip (add by IKUAI)
  */
 struct spinand_device {
 	struct nand_device base;
@@ -414,6 +424,9 @@ struct spinand_device {
 	u8 *scratchbuf;
 	const struct spinand_manufacturer *manufacturer;
 	void *priv;
+	u8 uid[SPINAND_UNIQUEID_LEN];
 };
 
 /**

代码存在优化空间,且可以使用linux内核原生的自带寄存器更新函数 spinand_upd_cfg 实现,只是该函数传参有坑,需要注意。

物联沃分享整理
物联沃-IOTWORD物联网 » OpenWrt读取SPI-NAND协议Flash芯片UniqueID详解(以华邦为例)

发表评论