前言:
Flash芯片一般都有一个出厂时由制造商设定的Unique ID,唯一ID。获取到可以用来进行各类加密识别认证,作为设备唯一ID的一种。
本文以华邦品牌的flash芯片为例(W25N01GV、W25M02GV),讲解如何在Linux、OpenWrt等环境下下读取 Flash ID。
背景知识:
一般来说Unique ID信息存放在otp区域里,otp区域是芯片上一块特殊的区域,读取前需要进行模式切换,具体切换流程需要阅读芯片手册,每家厂家的操作方法都不一样。
本文以winbond 华邦是spi nand Flash为例讲解,阅读文档可以得知,winbond这款芯片otp区域有十页,其中第一页就存放的Unique ID。读取需要修改状态寄存器的OTP-E位,且需要在读取完成后对OTP-E复位,否则会影响后续指令的执行。


实现方法:
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];
};
/**本补丁针对的是5.x内核,6.x内核需要修改flash相关的api。对OpenWrt开发感兴趣的网友,可以参加佐大的OpenWrt培训班,佐大可提供相关的技术支持。
本站的文章和资源来自互联网或者站长的原创,按照 CC BY -NC -SA 3.0 CN协议发布和共享,转载或引用本站文章应遵循相同协议。如果有侵犯版权的资 源请尽快联系站长,我们会在24h内删除有争议的资源。欢迎大家多多交流,期待共同学习进步。










