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

2023-10-19 01:30

本文主要是介绍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 0x20000mtd_debug write /dev/mtd0 0 0x20000 sample.binmtd_debug read /dev/mtd0 0 0x20000 bootstrap_2.bin
  • 也可以使用 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

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"

这篇关于Macronix MX25L25645G NOR Flash无法擦除问题分析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/236396

相关文章

Kotlin Map映射转换问题小结

《KotlinMap映射转换问题小结》文章介绍了Kotlin集合转换的多种方法,包括map(一对一转换)、mapIndexed(带索引)、mapNotNull(过滤null)、mapKeys/map... 目录Kotlin 集合转换:map、mapIndexed、mapNotNull、mapKeys、map

nginx中端口无权限的问题解决

《nginx中端口无权限的问题解决》当Nginx日志报错bind()to80failed(13:Permissiondenied)时,这通常是由于权限不足导致Nginx无法绑定到80端口,下面就来... 目录一、问题原因分析二、解决方案1. 以 root 权限运行 Nginx(不推荐)2. 为 Nginx

SpringBoot中六种批量更新Mysql的方式效率对比分析

《SpringBoot中六种批量更新Mysql的方式效率对比分析》文章比较了MySQL大数据量批量更新的多种方法,指出REPLACEINTO和ONDUPLICATEKEY效率最高但存在数据风险,MyB... 目录效率比较测试结构数据库初始化测试数据批量修改方案第一种 for第二种 case when第三种

解决1093 - You can‘t specify target table报错问题及原因分析

《解决1093-Youcan‘tspecifytargettable报错问题及原因分析》MySQL1093错误因UPDATE/DELETE语句的FROM子句直接引用目标表或嵌套子查询导致,... 目录报js错原因分析具体原因解决办法方法一:使用临时表方法二:使用JOIN方法三:使用EXISTS示例总结报错原

Windows环境下解决Matplotlib中文字体显示问题的详细教程

《Windows环境下解决Matplotlib中文字体显示问题的详细教程》本文详细介绍了在Windows下解决Matplotlib中文显示问题的方法,包括安装字体、更新缓存、配置文件设置及编码調整,并... 目录引言问题分析解决方案详解1. 检查系统已安装字体2. 手动添加中文字体(以SimHei为例)步骤

SpringSecurity整合redission序列化问题小结(最新整理)

《SpringSecurity整合redission序列化问题小结(最新整理)》文章详解SpringSecurity整合Redisson时的序列化问题,指出需排除官方Jackson依赖,通过自定义反序... 目录1. 前言2. Redission配置2.1 RedissonProperties2.2 Red

nginx 负载均衡配置及如何解决重复登录问题

《nginx负载均衡配置及如何解决重复登录问题》文章详解Nginx源码安装与Docker部署,介绍四层/七层代理区别及负载均衡策略,通过ip_hash解决重复登录问题,对nginx负载均衡配置及如何... 目录一:源码安装:1.配置编译参数2.编译3.编译安装 二,四层代理和七层代理区别1.二者混合使用举例

MySQL中的LENGTH()函数用法详解与实例分析

《MySQL中的LENGTH()函数用法详解与实例分析》MySQLLENGTH()函数用于计算字符串的字节长度,区别于CHAR_LENGTH()的字符长度,适用于多字节字符集(如UTF-8)的数据验证... 目录1. LENGTH()函数的基本语法2. LENGTH()函数的返回值2.1 示例1:计算字符串

Android kotlin中 Channel 和 Flow 的区别和选择使用场景分析

《Androidkotlin中Channel和Flow的区别和选择使用场景分析》Kotlin协程中,Flow是冷数据流,按需触发,适合响应式数据处理;Channel是热数据流,持续发送,支持... 目录一、基本概念界定FlowChannel二、核心特性对比数据生产触发条件生产与消费的关系背压处理机制生命周期

SQL Server配置管理器无法打开的四种解决方法

《SQLServer配置管理器无法打开的四种解决方法》本文总结了SQLServer配置管理器无法打开的四种解决方法,文中通过图文示例介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的... 目录方法一:桌面图标进入方法二:运行窗口进入检查版本号对照表php方法三:查找文件路径方法四:检查 S