• Macronix MX25L25645G NOR Flash无法擦除问题分析


    1. 问题现象描述

       处理器使用的 SAM9X60, 使用的内核版本是 5.10.80,在调试 Macronix MX25L25645G NOR Flash时,发现flash驱动加载成功后,使用 mtd_debug 工具 erase flash时,擦除一整片flash区域时,命令执行速度很快,而且命令执行完没有报错,但是最后发现flash并没有真正被擦除。

    • mtd_debug 擦写命令
    mtd_debug erase /dev/mtd0 0 0x20000
     
    mtd_debug write /dev/mtd0 0 0x20000 sample.bin
     
    mtd_debug read /dev/mtd0 0 0x20000 bootstrap_2.bin
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 也可以使用 flashcp 和 flash_erase 命令, flashcp 会先擦除 flash 然后再写入数据。
    root@sam9x60ek:/tmp# flashcp -v sample.bin /dev/mtd3
    Erasing blocks: 1/1 (100%)
    Writing data: 1k/0k (100%)
    Verifying data: 1k/0k (1%)File does not seem to match flash data. First mismatch at 0x00000000-0x00000400
    
    • 1
    • 2
    • 3
    • 4

    2. 代码走读分析

    2.1 探索 SPI erase 过程

       用户层的mtd_debug erase , flash_ease等命令到内核驱动层进行分析。

       查看 flashcp 的源代码,其采用的方式是通过 ioctl (dev_fd,MEMERASE,&erase) 方式来进行擦除操作;
       查看 flash_erase的源代码,其调用了libc库中的 mtd_erase API 进行擦除操作, mtd_erase API 最终会调用 ioctl (dev_fd,MEMERASE,&erase) 方式来进行擦除操作。
    两者本质是一样的。

       基于5.10.80 内核版本

    1)ioctl (dev_fd,MEMERASE,&erase)
      Path: /linux/drivers/mtd/mtdchar.c
    static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg) 函数中
    在这里插入图片描述
    MEMERASE64 这个宏是为了操作 大于 4Gib大小的flash才会使用的。我们这里不考虑。

    走到了内核中 mtd_erase

    /linux/drivers/mtd/mtdcore.c
    在这里插入图片描述
    会调用 master->_erase(master, &adjinstr) 这个操作。这个函数的定义在

    /linux/drivers/mtd/spi-nor/core.c

    spi_nor_scan 中
    在这里插入图片描述
    在这里插入图片描述
    对比SAM9X60 EK Demo板,发现走的是 spi_nor_has_uniform_erase 这个else 分支
    使用的是 spi_nor_erase_sector 按扇区来擦除
    在驱动中添加了打印信息,对于 MX25L25645G 这个NOR来说,驱动选用的擦除、写、读命令以及对应的地址分别是:
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    2.2. SPI 擦除失败的根本原因分析。

    /linux/drivers/mtd/spi-nor/core.c
    spi_nor_erase_sector
    在这里插入图片描述
    传入的 nor->addr_width = [4] ,擦除操作时,根据flash数据手册,op→addr.buswidth应该等于1,但是等于4了,导致数据帧组装错误,flash没有识别这个指令,从而没有擦除成功。

    对代码进行了修改,代码中提交的修改记录:
    在这里插入图片描述
    详细分析下上面的代码:

    op->addr.buswidth 是怎么来的呢?
    在这里插入图片描述
    在这里插入图片描述
    我们这里设置的为0,但是下面这个函数也会对 buswidth 进行赋值。

    spi_nor_spimem_setup_op(nor, &op, nor->write_proto);
    在这里插入图片描述
    在这里插入图片描述
    加打印,看一下传入的 nor->write_proto 参数是多少,以及这个参数是在哪里初始化的?op.addr.buswidth 在 进入spi_nor_spimem_setup_op这个函数前后的值是多少?
    在这里插入图片描述
    nor→write_proto

    spi_nor_scan
    -> spi_nor_init_params
    -> spi_nor_info_init_params
    -> spi_nor_default_setup
    -> spi_nor_select_pp

    在这里插入图片描述
    感觉初始化并不是这样定义的。
    在这里插入图片描述
    在这里插入图片描述
    在调试过程中,使用了Microchip SAM9X60 EK 评估板作为对比。评估板使用的QSPI NOR Flash型号是 SST26VF064B.

    在这里插入图片描述

    ++++++++++++ 擦除操作,将P4K和Demo版本进行对比传入的 nor->write_proto ++++++++++++++
    在这里插入图片描述
    在这里插入图片描述
    整个流程:
    发送写使能(指令 06) -》 发送擦除命令 (指令 dc, (4 Byte Address Command, BE4B (block erase 64KB) )
    -》读取状态寄存器(指令05,WIP标志反映命令是否执行成功)-》关闭写使能(指令04)

    nor->write_proto 被定义的地方如下:

    /linux/drivers/mtd/spi-nor/core.c

    最先开始初始时,spi_nor_scan 中定义的是 SNOR_PROTO_1_1_1
    在这里插入图片描述
    在这个地方被重新赋值:
    spi_nor_select_pp
    在这里插入图片描述
    对比spi_nor_select_read 和 spi_nor_select_pp。
    在这里插入图片描述
    可以看到由于 shared_mask 不同造成后后面 best_match 选择不一样,Demo板选择的PP 模式是 1-1-1,我们的板子选择的PP 模式是 1-4-4

    shared_mask
    我们的板子: 0x5039b (0101 0000 0011 1001 1011)
    Demo板: 0x1039b (0001 0000 0011 1001 1011)

    这个对照下面:

    https://elixir.bootlin.com/linux/v5.10.80/source/include/linux/mtd/spi-nor.h#L220

    flash的读能力

    在这里插入图片描述
    flash的写能力
    在这里插入图片描述
    追 shared_mask
    在这里插入图片描述
    继续追 params->hwcaps.mask
    spi_nor_info_init_params 函数中会对 hwcaps.mask 进行赋值
    在这里插入图片描述
    所以这就是内核的兼容性问题。得需要将 Write 和 erase 剥离出来。

    3. 我们板子Linux内核启动过程中SPI Flash初始化Log

    *** heat [atmel_qspi_exec_op] [419] 
    *** heat [atmel_qspi_set_cfg] [328] op->addr.buswidth = [0]
    *** heat [atmel_qspi_set_cfg] [329] op->addr.nbytes = [0]
    *** heat [atmel_qspi_set_cfg] [408] iar = [0x0] icr = [0x9f] ifr = [0x90]    0x9f 命令是读取SPI Flash的Chip ID
    ******** heat func [macronix_default_init] enter 
    *** heat [spi_nor_spimem_read_data] [199] nor->read_opcode = [5a]
    *** heat [spi_nor_spimem_read_data] [200] nor->addr_width = [3]
    *** heat [atmel_qspi_exec_op] [419] 
    *** heat [atmel_qspi_set_cfg] [328] op->addr.buswidth = [1]
    *** heat [atmel_qspi_set_cfg] [329] op->addr.nbytes = [3]
    *** heat [atmel_qspi_set_cfg] [408] iar = [0x0] icr = [0x5a] ifr = [0x810b0]    0x5A 命令是读取 SFDP ( Serial Flash Discoverable Parameter (SFDP)) 能力集
    *** heat [spi_nor_spimem_read_data] [199] nor->read_opcode = [5a]
    *** heat [spi_nor_spimem_read_data] [200] nor->addr_width = [3]
    *** heat [atmel_qspi_exec_op] [419] 
    *** heat [atmel_qspi_set_cfg] [328] op->addr.buswidth = [1]
    *** heat [atmel_qspi_set_cfg] [329] op->addr.nbytes = [3]
    *** heat [atmel_qspi_set_cfg] [408] iar = [0x10] icr = [0x5a] ifr = [0x810b0]
    *** heat [spi_nor_spimem_read_data] [199] nor->read_opcode = [5a]
    *** heat [spi_nor_spimem_read_data] [200] nor->addr_width = [3]
    *** heat [atmel_qspi_exec_op] [419] 
    *** heat [atmel_qspi_set_cfg] [328] op->addr.buswidth = [1]
    *** heat [atmel_qspi_set_cfg] [329] op->addr.nbytes = [3]
    *** heat [atmel_qspi_set_cfg] [408] iar = [0x30] icr = [0x5a] ifr = [0x810b0]
    *** heat [spi_nor_spimem_read_data] [199] nor->read_opcode = [5a]
    *** heat [spi_nor_spimem_read_data] [200] nor->addr_width = [3]
    *** heat [atmel_qspi_exec_op] [419] 
    *** heat [atmel_qspi_set_cfg] [328] op->addr.buswidth = [1]
    *** heat [atmel_qspi_set_cfg] [329] op->addr.nbytes = [3]
    *** heat [atmel_qspi_set_cfg] [408] iar = [0xc0] icr = [0x5a] ifr = [0x810b0]
    ******** heat func [spi_nor_select_erase] [2450] wanted_size = 65536 
    ******** heat func [spi_nor_select_erase] [2469] erase->opcode = dc 
    *** heat [atmel_qspi_exec_op] [419] 
    *** heat [atmel_qspi_set_cfg] [328] op->addr.buswidth = [0]
    *** heat [atmel_qspi_set_cfg] [329] op->addr.nbytes = [0]
    *** heat [atmel_qspi_set_cfg] [408] iar = [0x0] icr = [0x5] ifr = [0x90]   0x5 命令是读取 Flash的 Status Reg
    ******** heat func [spi_nor_sr1_bit6_quad_enable] enter, ret = 0, bouncebuf = 64 
    ******** heat func [spi_nor_sr1_bit6_quad_enable] enter , line [1808]
    ******** heat func [macronix_set_4byte_addr_mode] enter 
    *** heat [atmel_qspi_exec_op] [419] 
    *** heat [atmel_qspi_set_cfg] [328] op->addr.buswidth = [0]
    *** heat [atmel_qspi_set_cfg] [329] op->addr.nbytes = [0]
    *** heat [atmel_qspi_set_cfg] [408] iar = [0x0] icr = [0x6] ifr = [0x10]  0x6 命令是 打开写使能
    ******** heat func [spi_nor_set_4byte_addr_mode] enter , enable=[1]
    *** heat [atmel_qspi_exec_op] [419] 
    *** heat [atmel_qspi_set_cfg] [328] op->addr.buswidth = [0]
    *** heat [atmel_qspi_set_cfg] [329] op->addr.nbytes = [0]
    *** heat [atmel_qspi_set_cfg] [408] iar = [0x0] icr = [0xb7] ifr = [0x10] 0xb7 命令是 使能4Byte address
    *** heat [atmel_qspi_exec_op] [419] 
    *** heat [atmel_qspi_set_cfg] [328] op->addr.buswidth = [0]
    *** heat [atmel_qspi_set_cfg] [329] op->addr.nbytes = [0]
    *** heat [atmel_qspi_set_cfg] [408] iar = [0x0] icr = [0x4] ifr = [0x10] 0x4 命令是 关闭写使能
    spi-nor spi0.0: mx25l25635e (32768 Kbytes)
    spi-nor spi0.0: mtd .name = spi0, .size = 0x2000000 (32MiB), .erasesize = 0x00010000 (64KiB) .numeraseregions = 0
    5 fixed-partitions partitions found on MTD device spi0
    Creating 5 MTD partitions on "spi0":
    0x000000000000-0x000000020000 : "at91bootstrap"
    0x000000020000-0x000000040000 : "env"
    0x000000040000-0x000000100000 : "u-boot"
    0x000000100000-0x000001000000 : "system"
    0x000001000000-0x000002000000 : "data"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
  • 相关阅读:
    《斯坦福数据挖掘教程·第三版》读书笔记(英文版) Chapter 2 MapReduce and the New Software Stack
    如何使用JMeter操作Elasticsearch
    文心一言 VS 讯飞星火 VS chatgpt (101)-- 算法导论9.3 7题
    Java老人护理上门服务类型系统小程序APP源码
    elementUI+springboot实现导入文件到后端并解析excel(进阶)
    【NOWCODER】- Python:列表(三)
    STM32MP157F-DK2 使用体验
    利用Redis实现向量相似度搜索:解决文本、图像和音频之间的相似度匹配问题
    JAVA IO——文件拷贝
    根目录/ 空间不够,扩容,导致web页面无法加载问题
  • 原文地址:https://blog.csdn.net/weixin_40407893/article/details/132811128