前言:
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内删除有争议的资源。欢迎大家多多交流,期待共同学习进步。