u-boot nand flash read/write debug(2018-08-01)

本文详细记录了解决NAND Flash读写错误的过程,包括分析读写操作的代码流程,发现并修正ECC校验配置错误,最终实现稳定的数据读写。
AI助手已提取文章相关产品:

机器感知

一个专注于SLAM、机器视觉、Linux 等相关技术文章分享的公众号
 

nand read 30000000 0 10000

NAND read: device 0 offset 0x0, size 0x10000
entry nand_read=mtd_read
entry mtd_read and call mtd->_read
entry nand_read
entry nand_do_read_ops
need_skip = 0
NAND read from offset 0 failed -74
 0 bytes read: ERROR
出错代码位置:
if (!need_skip) {
        rval = nand_read(nand, offset, length, buffer);
        if (!rval || rval == -EUCLEAN)
            return 0;

        *length = 0;
        printf("NAND read from offset %llx failed %d\n",
            offset, rval);
        return rval;
    }
一个设置项:nand->ecc.mode = NAND_ECC_SOFT;
所影响的代码:
case NAND_ECC_SOFT:
        ecc->calculate = nand_calculate_ecc;
        ecc->correct = nand_correct_data;
        ecc->read_page = nand_read_page_swecc;
        ecc->read_subpage = nand_read_subpage;
        ecc->write_page = nand_write_page_swecc;
        ecc->read_page_raw = nand_read_page_raw;/********/
        ecc->write_page_raw = nand_write_page_raw;
        ecc->read_oob = nand_read_oob_std;
        ecc->write_oob = nand_write_oob_std;
        if (!ecc->size)
            ecc->size = 256;
        ecc->bytes = 3;
        ecc->strength = 1;
        break;
case NAND_ECC_NONE:
        pr_warn("NAND_ECC_NONE selected by board driver. This is not recommended!\n");
        ecc->read_page = nand_read_page_raw;
        ecc->write_page = nand_write_page_raw;
        ecc->read_oob = nand_read_oob_std;
        ecc->read_page_raw = nand_read_page_raw;
        ecc->write_page_raw = nand_write_page_raw;
        ecc->write_oob = nand_write_oob_std;
        ecc->size = mtd->writesize;
        ecc->bytes = 0;
        ecc->strength = 0;
        break;
读失败可能是没加片选操作chip->select_chip(mtd, 0);
片选命令不需要再加了,在读命令发出前,前段代码有发送片选命令

现在的问题是可以正确读出nandflash中的数据,但是总是提示读错误,不知为何!

终于找到原因了:
是ecc校验有问题,u-boot中默认支持的ecc页大小与我的板子上的不符合,具体如下

    /*
     * Check ECC mode, default to software if 3byte/512byte hardware ECC is
     * selected and we have 256 byte pagesize fallback to software ECC
     */
..nand_scant_tail:
    switch (ecc->mode) {...}位于nand_base.c中

在此语句上添加ecc->mode=NAND_ECC_NONE,即使用ecc校验,重新编译下载后,完美成功。
测试结果:其中读结束返回的大小为bytes字节,而cmp返回的是字数words,即4个字节
u-boot-2015.10 # nand read 30080000 0 80000

NAND read: device 0 offset 0x0, size 0x80000
entry nand_read=mtd_read
entry mtd_read and call mtd->_read
entry nand_read
entry nand_do_read_ops
oob=0,ret=0,ecc_fail=0,max_bitflips=0
nand_read return value=0
mtd_read:ret_code=0,ecc_strength=0
need_skip = 0,rval=0
 524288 bytes read: OK
u-boot-2015.10 # nand read 30000000 0 80000

NAND read: device 0 offset 0x0, size 0x80000
entry nand_read=mtd_read
entry mtd_read and call mtd->_read
entry nand_read
entry nand_do_read_ops
oob=0,ret=0,ecc_fail=0,max_bitflips=0
nand_read return value=0
mtd_read:ret_code=0,ecc_strength=0
need_skip = 0,rval=0
 524288 bytes read: OK
u-boot-2015.10 # cmp 30000000 30080000 7ffff
word at 0x30080000 (0xea000014) != word at 0x30100000 (0x7b8c33ce)
Total of 131072 word(s) were the same
nand write操作:

NAND write: device 0 offset 0xfffff00, size 0x100
Attempt to write non page-aligned data
 0 bytes written: ERROR


u-boot-2015.10 # nand write 30000000 f000000 800

NAND write: device 0 offset 0xf000000, size 0x800
entry nand_read=mtd_read
entry mtd_read and call mtd->_read
entry nand_read
entry nand_do_read_ops
oob=0,ret=0,ecc_fail=0,max_bitflips=0
nand_read return value=0
mtd_read:ret_code=0,ecc_strength=0
NAND write to offset f000000 failed -5
 0 bytes written: ERROR
从下面的输出并结合代码可以看出:之所以会出现读操作,并不是刚开始认为的写操作函数变成了
读操作函数,而是写操作中有数据校验这个功能,所以需要重新读出数据进行对比,因此有了读操作
的信息输出。
Flash: 2 MiB
NAND:  set mtd->_read = nand_read<872222912>
set mtd->_write = nand_write<872219876>
256 MiB

u-boot-2015.10 # nand write 30100000 f000000 800

NAND write: device 0 offset 0xf000000, size 0x800
entry mtd_write and call mtd->_write
entry nand_read=mtd_read
entry mtd_read and call mtd->_read
entry nand_read
entry nand_do_read_ops
oob=0,ret=0,ecc_fail=0,max_bitflips=0
nand_read return value=0
mtd_read:ret_code=0,ecc_strength=0
NAND write to offset f000000 failed -5
 0 bytes written: ERROR
现在可以确定了确实是写函数有问题了。
    if (!chip->read_byte || chip->read_byte == nand_read_byte)
        chip->read_byte = busw ? nand_read_byte16 : nand_read_byte;
    if (!chip->read_word)
        chip->read_word = nand_read_word;
    if (!chip->block_bad)
        chip->block_bad = nand_block_bad;
    if (!chip->block_markbad)
        chip->block_markbad = nand_default_block_markbad;
    if (!chip->write_buf || chip->write_buf == nand_write_buf)
        chip->write_buf = busw ? nand_write_buf16 : nand_write_buf;
    if (!chip->write_byte || chip->write_byte == nand_write_byte)
        chip->write_byte = busw ? nand_write_byte16 : nand_write_byte;
    if (!chip->read_buf || chip->read_buf == nand_read_buf)
        chip->read_buf = busw ? nand_read_buf16 : nand_read_buf;
写操作技术路线:
chip->write_page = nand_write_page;
    |->if (unlikely(raw))
        status = chip->ecc.write_page_raw(mtd, chip, buf,oob_required);
目前看没有什么问题,可是没有发现发送写命令的操作!
通过查看代码,基本可以确定,确实没有发送写命令。
include/linux/mtd/nand.h:#define NAND_CMD_SEQIN        0x80
找到了,源代码是有发送写命令的,看来问题还在别处:
    chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page);
    if (unlikely(raw))
        status = chip->ecc.write_page_raw(mtd, chip, buf,
                            oob_required);
对比1.1.6的代码,和2015.10的还是有一些差别的

接下来检查一下硬件相关的操作,即发送写指令是否正确。
    /* hwcontrol always must be implemented */
    nand->cmd_ctrl = s3c24x0_hwcontrol;

您可能感兴趣的与本文相关内容

<think>我们正在分析U-Boot的源码风格,特别是为了编写Flash驱动。根据引用[1]和[2],我们可以总结出以下几点: 1. **代码分层与模块化**:U-Boot的代码结构通常按照功能模块划分,例如驱动代码放在`drivers`目录下,Flash驱动则位于`drivers/mtd`或`drivers/flash`目录中。每个驱动模块应尽量保持独立,通过清晰的接口与其它模块交互。 2. **严格的编码规范**: - 使用C语言编写,遵循ANSI C标准。 - 变量和函数命名采用小写字母加下划线的形式(例如`flash_init`)。 - 全局变量和函数需在头文件中声明,并添加必要的注释说明其作用。 3. **硬件抽象层(HAL)**:U-Boot通过硬件抽象层来屏蔽底层硬件差异。编写Flash驱动时,需要实现特定的接口函数,例如: - `flash_init()`: 初始化Flash设备 - `flash_erase()`: 擦除指定扇区 - `flash_write()`: 写入数据 4. **条件编译与配置**:U-Boot大量使用宏定义(如`CONFIG_SPI_FLASH`)来启用或禁用特定功能。新增驱动时,需在相应的Kconfig文件中添加配置选项,并通过`#ifdef`进行条件编译[^1]。 5. **寄存器操作规范**:直接操作硬件寄存器时,需使用U-Boot提供的读写宏(例如`readl()`/`writel()`),并避免直接使用魔数(Magic Number),而应定义清晰的寄存器位掩码(如`#define FLASH_CR_ENABLE (1 << 0)`)。 6. **错误处理**:函数应返回标准错误码(定义在`include/errno.h`中),并在操作失败时记录错误信息(使用`debug()`或`printf()`输出调试信息)。 7. **文档与注释**:每个驱动文件开头应包含详细注释,说明驱动支持的硬件型号、作者、实现原理等;关键代码段需添加行内注释解释复杂逻辑[^1]。 ### Flash驱动编写示例 以下是简化版SPI Flash驱动的结构: ```c #include <common.h> #include <spi.h> #include <spi_flash.h> /* 定义Flash命令 (根据具体芯片手册定义) */ #define CMD_READ_ID 0x9F /* 实现读写函数 */ static int spi_flash_read(struct spi_flash *flash, u32 offset, size_t len, void *buf) { /* 实现读取逻辑 */ ... return 0; /* 成功返回0 */ } /* 注册驱动到U-Boot框架 */ struct spi_flash *spi_flash_probe(unsigned int bus, unsigned int cs) { struct spi_flash *flash; flash = spi_flash_alloc(bus, cs); if (!flash) return NULL; flash->read = spi_flash_read; /* 绑定读函数 */ ... return flash; } ``` ### 关键注意事项 - **避免阻塞操作**:Flash擦除/写入耗时较长,需通过状态轮询或中断实现异步操作,避免阻塞系统启动。 - **缓存管理**:部分Flash驱动需实现缓存机制(如NOR Flash的写缓冲),确保数据一致性。 - **设备树支持**:新驱动应支持从设备树(Device Tree)获取配置参数,而非硬编码。 §§相关问题§§ 1. U-BootFlash驱动的初始化流程是怎样的? 2. 如何为新的SPI Flash芯片添加驱动? 3. U-Boot驱动开发中如何调试硬件访问问题?</think>### U-Boot源码风格分析及Flash驱动编写规范 U-Boot源码采用高度模块化、分层设计的风格,遵循严格的编码规范。以下是核心要点: #### 1. **代码组织结构** - **目录分层清晰** Flash驱动位于`drivers/mtd/`或`drivers/flash/`目录,例如: ``` u-boot/ ├── drivers/ │ ├── mtd/ # MTD子系统 │ │ ├── spi/ # SPI Flash驱动 │ │ └── nand/ # NAND Flash驱动 │ └── flash/ # 传统NOR Flash驱动 ``` - **硬件抽象层(HAL)** 驱动需通过`struct flash_info`定义设备参数,例如: ```c struct flash_info { const char *name; // 设备型号 uint32_t id; // JEDEC ID uint32_t sector_size; uint32_t size; // 总容量 ... }; ``` #### 2. **编码规范** - **命名规则** - 全局函数:`flash_operation()`(前缀+动词) - 局部变量:`sector_offset`(小写+下划线) - 宏定义:`FLASH_DEBUG_ENABLE`(大写+下划线) - **注释要求** ```c /* * flash_erase() - 擦除指定扇区 * @base_addr: Flash基地址 * @sector: 扇区编号 * 返回值: 0成功,-1失败 */ int flash_erase(ulong base_addr, uint sector) { ... } ``` #### 3. **驱动接口实现** Flash驱动必须实现以下核心操作: ```c // 在 include/flash.h 中定义接口 struct flash_ops { int (*erase)(struct flash_info *info, uint sector); int (*write)(struct flash_info *info, ulong dest, uchar *src, uint len); int (*read)(struct flash_info *info, ulong src, uchar *dest, uint len); }; // 驱动实现示例 (drivers/mtd/spi/spi_flash.c) static int spi_flash_erase(struct flash_info *info, uint sector) { /* 1. 发送擦除指令 */ spi_write_cmd(CMD_ERASE_SECTOR, sector); /* 2. 等待操作完成 */ while (spi_read_status() & STATUS_BUSY); ... } ``` #### 4. **硬件访问规范** - **寄存器操作** 使用位掩码宏定义,禁止直接写魔数: ```c #define FLASH_CR_ENABLE (1 << 0) writel(FLASH_CR_ENABLE, FLASH_BASE + REG_CR); // 正确 writel(0x1, FLASH_BASE + 0x100); // 禁止 ``` - **内存屏障** 跨架构操作需添加屏障: ```c void flash_write_buffer(uchar *data) { memcpy(dest, data, len); mb(); // 内存屏障确保数据同步 } ``` #### 5. **配置与移植** - **Kconfig配置** 在`drivers/mtd/Kconfig`中添加: ```kconfig config SPI_FLASH_NEWCHIP bool "Support NewFlash Chip" depends on SPI_FLASH help Enable this for NewFlash devices ``` - **设备树绑定** 驱动需解析设备树节点: ```dts &spi0 { newflash: flash@0 { compatible = "vendor,newflash"; spi-max-frequency = <50000000>; }; }; ``` ### 编写建议 1. **参考现有驱动**:如`drivers/mtd/spi/spi_flash_macronix.c` 2. **严格遵循分层**: - 硬件操作层(SPI/I2C时序) - Flash指令层(擦除/写入协议) - MTD抽象层(对接U-Boot存储系统) 3. **添加调试支持**: ```c #ifdef DEBUG printf("Erase sector %d\n", sector); #endif ``` > 关键原则:**保持硬件相关代码集中,通过抽象接口对接上层**,确保驱动可维护性[^1]。 --- ###
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值