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
  • 也可以使用 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) 函数中
Macronix MX25L25645G NOR Flash无法擦除问题分析_第1张图片
MEMERASE64 这个宏是为了操作 大于 4Gib大小的flash才会使用的。我们这里不考虑。

走到了内核中 mtd_erase

/linux/drivers/mtd/mtdcore.c
Macronix MX25L25645G NOR Flash无法擦除问题分析_第2张图片
会调用 master->_erase(master, &adjinstr) 这个操作。这个函数的定义在

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

spi_nor_scan 中
Macronix MX25L25645G NOR Flash无法擦除问题分析_第3张图片
Macronix MX25L25645G NOR Flash无法擦除问题分析_第4张图片
对比SAM9X60 EK Demo板,发现走的是 spi_nor_has_uniform_erase 这个else 分支
使用的是 spi_nor_erase_sector 按扇区来擦除
在驱动中添加了打印信息,对于 MX25L25645G 这个NOR来说,驱动选用的擦除、写、读命令以及对应的地址分别是:
Macronix MX25L25645G NOR Flash无法擦除问题分析_第5张图片
Macronix MX25L25645G NOR Flash无法擦除问题分析_第6张图片
Macronix MX25L25645G NOR Flash无法擦除问题分析_第7张图片

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

/linux/drivers/mtd/spi-nor/core.c
spi_nor_erase_sector
Macronix MX25L25645G NOR Flash无法擦除问题分析_第8张图片
传入的 nor->addr_width = [4] ,擦除操作时,根据flash数据手册,op→addr.buswidth应该等于1,但是等于4了,导致数据帧组装错误,flash没有识别这个指令,从而没有擦除成功。

对代码进行了修改,代码中提交的修改记录:
Macronix MX25L25645G NOR Flash无法擦除问题分析_第9张图片
详细分析下上面的代码:

op->addr.buswidth 是怎么来的呢?
Macronix MX25L25645G NOR Flash无法擦除问题分析_第10张图片
Macronix MX25L25645G NOR Flash无法擦除问题分析_第11张图片
我们这里设置的为0,但是下面这个函数也会对 buswidth 进行赋值。

spi_nor_spimem_setup_op(nor, &op, nor->write_proto);
Macronix MX25L25645G NOR Flash无法擦除问题分析_第12张图片
Macronix MX25L25645G NOR Flash无法擦除问题分析_第13张图片
加打印,看一下传入的 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

Macronix MX25L25645G NOR Flash无法擦除问题分析_第14张图片
感觉初始化并不是这样定义的。
Macronix MX25L25645G NOR Flash无法擦除问题分析_第15张图片
Macronix MX25L25645G NOR Flash无法擦除问题分析_第16张图片
在调试过程中,使用了Microchip SAM9X60 EK 评估板作为对比。评估板使用的QSPI NOR Flash型号是 SST26VF064B.

在这里插入图片描述

++++++++++++ 擦除操作,将P4K和Demo版本进行对比传入的 nor->write_proto ++++++++++++++
Macronix MX25L25645G NOR Flash无法擦除问题分析_第17张图片
Macronix MX25L25645G NOR Flash无法擦除问题分析_第18张图片
整个流程:
发送写使能(指令 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
Macronix MX25L25645G NOR Flash无法擦除问题分析_第19张图片
在这个地方被重新赋值:
spi_nor_select_pp
Macronix MX25L25645G NOR Flash无法擦除问题分析_第20张图片
对比spi_nor_select_read 和 spi_nor_select_pp。
Macronix MX25L25645G NOR Flash无法擦除问题分析_第21张图片
可以看到由于 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的读能力

Macronix MX25L25645G NOR Flash无法擦除问题分析_第22张图片
flash的写能力
Macronix MX25L25645G NOR Flash无法擦除问题分析_第23张图片
追 shared_mask
Macronix MX25L25645G NOR Flash无法擦除问题分析_第24张图片
继续追 params->hwcaps.mask
spi_nor_info_init_params 函数中会对 hwcaps.mask 进行赋值
Macronix MX25L25645G NOR Flash无法擦除问题分析_第25张图片
所以这就是内核的兼容性问题。得需要将 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"

你可能感兴趣的:(嵌入式Linux驱动开发,嵌入式硬件,驱动开发)