Flash芯片一般都有一个出厂时由制造商设定的Unique ID,唯一ID。获取到可以用来进行各类加密识别认证,作为设备唯一ID的一种。
本文以华邦品牌的flash芯片为例(W25N01GV、W25M02GV),如何在Linux下读取该ID。
阅读芯片手册,了解读取步骤
一般Unique ID信息都存放在otp区域里,otp区域是芯片上一块特殊的区域,读取前需要进行模式切换,具体切换流程需要阅读芯片手册,每个厂家都不一样。
在目录里,找到读取的相关页面
阅读文档可以得知,winbond这款芯片otp区域有十页,其中第一页就存放的Unique ID。
读取需要修改状态寄存器的OTP-E位,且读取完成后需要复位。否则会影响正常区域的读写。
下面给出patch文章来源:https://www.toymoban.com/news/detail-687129.html
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 实现,只是该函数传参有坑,需要注意。文章来源地址https://www.toymoban.com/news/detail-687129.html
到了这里,关于Openwrt读取spi-nand协议Flash芯片UniqueID(华邦为例)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!