Openwrt读取华邦winbond spi-nand Flash的硬件UniqueID、唯一UUID、FlashID

前言:

    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复位,否则会影响后续指令的执行。

a96bfa22a539e3258146ae820774ada7.png823d066f39a40a8aa8b8d6cd5bb9397f.png

实现方法:

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培训班,佐大可提供相关的技术支持。

本文章由作者:佐须之男 整理编辑,原文地址: Openwrt读取华邦winbond spi-nand Flash的硬件UniqueID、唯一UUID、FlashID
本站的文章和资源来自互联网或者站长的原创,按照 CC BY -NC -SA 3.0 CN协议发布和共享,转载或引用本站文章应遵循相同协议。如果有侵犯版权的资 源请尽快联系站长,我们会在24h内删除有争议的资源。欢迎大家多多交流,期待共同学习进步。

相关推荐