Linux Mtd子系统6

Linux Mtd子系统6(基于Linux6.6)---nand controller驱动开发介绍

一、nand controller驱动开发流程说明

  1. 创建一个platform driver驱动,以便实现nand_controller芯片相关私有数据与驱动的分离(关于platform模型的使用方式以及好处,请参考我之前写的文章)。
  2. 在platform的probe接口中,开始进行struct nand_chip类型变量内存空间的申请及初始化,需要设置的内容如下:
    1. 设置nand_chip的IO_ADDR_R、IO_ADDR_W地址,该地址主要用于nanflash数据的读写操作(如在nand_chip->read_buf、nand_chip->write_buf等接口中);
    2. 设置nand_chip->ecc变量,主要设置ecc及其相关的读写接口,若该nand_controller支持硬件ecc,则需要根据platform_device中传递的resource信息,设置硬件ecc类型,并设置ecc使能hwctl、ecc校验接口calculate、ecc纠错接口correct、read_page_raw、write_page_raw、read_page、read_subpage、write_subpage、write_page、write_oob_raw、read_oob_raw、read_oob、write_oob等函数指针,并设置每页数据执行ecc校验的次数、单次进行数据ecc计算的字节数、产生的ecc字节数等(如ti omap2的nand chip驱动,针对硬件bch4、bch8,其设置的接口为omap_elm_correct_data、omap3_calculate_ecc_bch、omap_read_page_bch、omap_write_page_bch、omap3_enable_hwecc_bch、nand_chip.ecc.size、nand_chip.ecc.mode、nand_chip.ecc.strength、nand.ecc.bytes);
    3. 设置与nandflash进行命令通信的函数指针,针对ti omap2的nand chip驱动而言,其设置如下nand.cmd_ctrl  = omap_hwcontrol;
    4. 设置与nanflash进行数据通信的接口以及坏块标记以及芯片选择相关的接口,这主要涉及到如下一些函数指针的设置read_byte、read_word、write_buf、read_buf、block_bad、block_markbad、select_chip、block_bad、erase_cmd、scan_bbt、errstat等接口(这些接口不是需要全部设置的,在ti omap2的驱动中,仅设置了dev_ready、read_buf、write_buf等接口,而对其他接口并没有设置,那这些接口在哪里设置呢?在上一章我们介绍的nand_scan_identnand_scan_tail接口中,针对nand_chip中未进行初始化的函数指针,会设置nand驱动模块定义的通用接口)。
    5. 设置oob中存储ecc的字节数以及每一个ecc字节在oob中的偏移位置;
    6. 调用nand_scan_ident、nand_scan_tail接口,完成nand_chip中page size、block size、erase size、nand flash size等参数的设置,并针对nand_chip中尚未设置的函数指针的设置,并完成该nand_flash对应的mtd_info类型变量中flash参数的设置以及其对应的mtd接口的设置(针对这些内容已在上一篇文章中介绍,此处不再赘述)
    7. 调用mtd_device_parse_register接口,完成该nandflash各分区的对应的mtd_info的注册(若没有对nandflash分区,则将该master mtd_info注册至系统中,也就是完成mtd_info对应的额块设备、字符设备的注册等等,这些内容已在上几篇文章中说明)。

        完成以上内容即可完成nand_controller的驱动开发。

具体的举例说明:

以下是开发 Linux NAND 控制器驱动的一般流程:

1. 硬件分析与文档研究

在开始开发之前,需要充分了解硬件平台和 NAND 控制器的规格,包括:

  • 控制器硬件接口(如:SPI, parallel NAND)
  • NAND 存储设备的特性(如:页大小、块大小、OOB 大小、ECC 支持等)
  • 控制器的寄存器映射和功能
  • 控制器的时序要求、时钟管理、DMA 支持等

这一步的核心是根据硬件文档、数据手册,或硬件描述文件(如 DTS 文件)深入了解硬件特性,并为后续开发做准备。

2. 建立基本的驱动框架

首先需要为 NAND 控制器驱动建立一个基本框架。这个框架包括:

  • 驱动文件:在 Linux 中,NAND 驱动通常会放在 drivers/mtd/nand 目录下。可以根据目标硬件建立新的子目录。
  • 结构体定义:定义与硬件相关的结构体(如 nand_chipnand_controller),这些结构体将用于存储硬件信息和控制寄存器。
  • 驱动初始化:编写驱动的初始化函数,设置基本的硬件配置,如控制器寄存器、时钟源、引脚配置等。

3. 配置 NAND 控制器的硬件寄存器

NAND 控制器通常会有一些寄存器,用于配置控制器的时序、读取和写入操作,或者启动 NAND Flash 操作(如擦除、编程、读取等)。这一步需要通过 I/O 寄存器访问 API(如 readl()writel())来编写硬件寄存器的初始化代码。

在这个阶段,通常需要关注以下几个方面:

  • 设置时序控制:控制器可能需要一些特定的时序信号,如 R/B 信号、CE 信号等。
  • 配置 ECC(错误检测与校正):大多数 NAND 控制器会集成硬件 ECC 引擎,开发时需要设置 ECC 校验模式。
  • 配置中断:如果 NAND 控制器支持中断处理,可以设置中断服务程序来处理操作完成后的通知。

4. 实现低级读写操作

在这一步中,需要实现与 NAND Flash 交互的低级操作,最基本的操作包括:

  • 页读操作:从 NAND Flash 的指定页面读取数据。
  • 页写操作:向 NAND Flash 的指定页面写入数据。
  • 块擦除操作:对整个块进行擦除。

这些操作通常需要通过编写相应的函数来完成,驱动中会涉及到的常见操作包括:

  • 向控制器发送命令(如读取命令、写入命令、擦除命令)。
  • 通过轮询或中断方式检查命令是否完成。
  • 处理 ECC 校验和错误恢复(如果启用了 ECC)。

示例代码:

static int nand_read_page(struct nand_chip *chip, uint32_t page, uint8_t *buf)
{
    // 设置命令寄存器
    writel(NAND_CMD_READ_PAGE, chip->regs + NAND_CMD_REG);
    writel(page, chip->regs + NAND_PAGE_REG);
    
    // 启动 NAND 读取过程
    writel(NAND_CMD_START, chip->regs + NAND_START_REG);
    
    // 等待 NAND 读取完成
    while (readl(chip->regs + NAND_STATUS_REG) & NAND_STATUS_BUSY)
        ;
    
    // 将数据读取到缓冲区
    memcpy(buf, (void *)chip->regs + NAND_DATA_REG, chip->page_size);
    
    return 0;
}

5. 实现 NAND 驱动的高级接口

Linux 内核的 NAND 驱动框架是基于 nand_chip 结构体的,通过实现标准的 NAND 操作接口来提供给更高层的调用。常见的接口包括:

  • read:实现从 NAND 设备读取数据。
  • write:实现向 NAND 设备写入数据。
  • erase:实现擦除操作。
  • block_isbadblock_markbad:实现坏块管理。

例如,NAND 驱动中的 nand_read 函数负责调用低级读操作并处理错误:

int nand_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, uint8_t *buf)
{
    struct nand_chip *chip = mtd_to_nand(mtd);
    int page = from / chip->page_size;
    int offset = from % chip->page_size;
    
    // 调用硬件的读接口
    nand_read_page(chip, page, buf);
    
    *retlen = len;
    return 0;
}

6. 坏块管理

NAND 存储设备会有坏块,这些坏块在制造过程中可能出现故障,因此需要管理坏块的检测和标记。通常,NAND 驱动会在 OOB 区域或指定位置存储坏块信息。

  • 坏块检测:通过读取 OOB 区域的特定位置来检测坏块。
  • 坏块标记:将坏块信息写入 OOB 区域,并在系统中标记该块为坏块,避免再次使用。

代码示例:

int nand_block_isbad(struct nand_chip *chip, uint32_t block)
{
    uint8_t oob_buf[chip->oob_size];
    
    // 读取 OOB 区域
    if (nand_read_oob(chip, block, oob_buf)) {
        return -1; // 读取失败
    }

    // 检查 OOB 区域的坏块标记
    if (oob_buf[NAND_BAD_BLOCK_POS] == NAND_BAD_BLOCK_MARKER)
        return 1; // 坏块
    return 0; // 正常块
}

int nand_block_markbad(struct nand_chip *chip, uint32_t block)
{
    uint8_t oob_buf[chip->oob_size];
    
    // 读取 OOB 区域
    if (nand_read_oob(chip, block, oob_buf))
        return -1;

    // 标记为坏块
    oob_buf[NAND_BAD_BLOCK_POS] = NAND_BAD_BLOCK_MARKER;
    
    // 写回 OOB 区域
    return nand_write_oob(chip, block, oob_buf);
}

7. 中断处理与错误恢复

NAND 控制器通常支持中断机制,用于通知操作完成。编写中断处理程序,以便在操作完成后正确处理数据或启动下一步操作。

同时,需要处理常见的错误情况,如:

  • NAND Flash 读取或写入超时
  • ECC 校验失败
  • 坏块管理和跳过坏块

8. 集成与测试

  • 集成驱动:将驱动集成到 Linux 内核,并将驱动与硬件进行绑定。
  • 功能测试:进行常规的读取、写入、擦除操作,验证是否正常工作。
  • 性能测试:根据需要对性能进行测试,优化驱动代码。
  • 回归测试:确保不会对其他功能产生负面影响。

9. 提交与维护

  • 将开发好的 NAND 控制器驱动提交到 Linux 内核树中,通常提交到 drivers/mtd/nand 目录。
  • 在开发过程中,继续修复可能出现的 bug,提升性能,并跟进硬件平台的升级或变化。

二、Nandflash适配注意事项说明

2.1、Nandflash参数设置说明

       在进行开发板的适配时,若需要适配的nandflash不支持onfi规范,则需要在变量struct nand_flash_dev nand_flash_ids[] 中检测下所使用的的nandflash相关的参数是否已在定义中,若没有添加即可,添加规则如下图所示。

 drivers/mtd/nand/raw/nand_ids.c

/*
 * The chip ID list:
 *    name, device ID, page size, chip size in MiB, eraseblock size, options
 *
 * If page size and eraseblock size are 0, the sizes are taken from the
 * extended chip ID.
 */
struct nand_flash_dev nand_flash_ids[] = {
	/*
	 * Some incompatible NAND chips share device ID's and so must be
	 * listed by full ID. We list them first so that we can easily identify
	 * the most specific match.
	 */
	{"TC58NVG0S3E 1G 3.3V 8-bit",
		{ .id = {0x98, 0xd1, 0x90, 0x15, 0x76, 0x14, 0x01, 0x00} },
		  SZ_2K, SZ_128, SZ_128K, 0, 8, 64, NAND_ECC_INFO(1, SZ_512), },
	{"TC58NVG2S0F 4G 3.3V 8-bit",
		{ .id = {0x98, 0xdc, 0x90, 0x26, 0x76, 0x15, 0x01, 0x08} },
		  SZ_4K, SZ_512, SZ_256K, 0, 8, 224, NAND_ECC_INFO(4, SZ_512) },
	{"TC58NVG2S0H 4G 3.3V 8-bit",
		{ .id = {0x98, 0xdc, 0x90, 0x26, 0x76, 0x16, 0x08, 0x00} },
		  SZ_4K, SZ_512, SZ_256K, 0, 8, 256, NAND_ECC_INFO(8, SZ_512) },
	{"TC58NVG3S0F 8G 3.3V 8-bit",
		{ .id = {0x98, 0xd3, 0x90, 0x26, 0x76, 0x15, 0x02, 0x08} },
		  SZ_4K, SZ_1K, SZ_256K, 0, 8, 232, NAND_ECC_INFO(4, SZ_512) },
	{"TC58NVG5D2 32G 3.3V 8-bit",
		{ .id = {0x98, 0xd7, 0x94, 0x32, 0x76, 0x56, 0x09, 0x00} },
		  SZ_8K, SZ_4K, SZ_1M, 0, 8, 640, NAND_ECC_INFO(40, SZ_1K) },
	{"TC58NVG6D2 64G 3.3V 8-bit",
		{ .id = {0x98, 0xde, 0x94, 0x82, 0x76, 0x56, 0x04, 0x20} },
		  SZ_8K, SZ_8K, SZ_2M, 0, 8, 640, NAND_ECC_INFO(40, SZ_1K) },
	{"SDTNQGAMA 64G 3.3V 8-bit",
		{ .id = {0x45, 0xde, 0x94, 0x93, 0x76, 0x57} },
		  SZ_16K, SZ_8K, SZ_4M, 0, 6, 1280, NAND_ECC_INFO(40, SZ_1K) },
	{"SDTNRGAMA 64G 3.3V 8-bit",
		{ .id = {0x45, 0xde, 0x94, 0x93, 0x76, 0x50} },
		  SZ_16K, SZ_8K, SZ_4M, 0, 6, 1280, NAND_ECC_INFO(40, SZ_1K) },
	{"H27UCG8T2ATR-BC 64G 3.3V 8-bit",
		{ .id = {0xad, 0xde, 0x94, 0xda, 0x74, 0xc4} },
		  SZ_8K, SZ_8K, SZ_2M, NAND_NEED_SCRAMBLING, 6, 640,
		  NAND_ECC_INFO(40, SZ_1K) },
	{"H27UCG8T2ETR-BC 64G 3.3V 8-bit",
		{ .id = {0xad, 0xde, 0x14, 0xa7, 0x42, 0x4a} },
		  SZ_16K, SZ_8K, SZ_4M, NAND_NEED_SCRAMBLING, 6, 1664,
		  NAND_ECC_INFO(40, SZ_1K) },
	{"TH58NVG2S3HBAI4 4G 3.3V 8-bit",
		{ .id = {0x98, 0xdc, 0x91, 0x15, 0x76} },
		  SZ_2K, SZ_512, SZ_128K, 0, 5, 128, NAND_ECC_INFO(8, SZ_512) },
	{"TH58NVG3S0HBAI4 8G 3.3V 8-bit",
		{ .id = {0x98, 0xd3, 0x91, 0x26, 0x76} },
		  SZ_4K, SZ_1K, SZ_256K, 0, 5, 256, NAND_ECC_INFO(8, SZ_512)},

	LEGACY_ID_NAND("NAND 4MiB 5V 8-bit",   0x6B, 4, SZ_8K, SP_OPTIONS),
	LEGACY_ID_NAND("NAND 4MiB 3,3V 8-bit", 0xE3, 4, SZ_8K, SP_OPTIONS),
	LEGACY_ID_NAND("NAND 4MiB 3,3V 8-bit", 0xE5, 4, SZ_8K, SP_OPTIONS),
	LEGACY_ID_NAND("NAND 8MiB 3,3V 8-bit", 0xD6, 8, SZ_8K, SP_OPTIONS),
	LEGACY_ID_NAND("NAND 8MiB 3,3V 8-bit", 0xE6, 8, SZ_8K, SP_OPTIONS),

	LEGACY_ID_NAND("NAND 16MiB 1,8V 8-bit",  0x33, 16, SZ_16K, SP_OPTIONS),
	LEGACY_ID_NAND("NAND 16MiB 3,3V 8-bit",  0x73, 16, SZ_16K, SP_OPTIONS),
	LEGACY_ID_NAND("NAND 16MiB 1,8V 16-bit", 0x43, 16, SZ_16K, SP_OPTIONS16),
	LEGACY_ID_NAND("NAND 16MiB 3,3V 16-bit", 0x53, 16, SZ_16K, SP_OPTIONS16),

	LEGACY_ID_NAND("NAND 32MiB 1,8V 8-bit",  0x35, 32, SZ_16K, SP_OPTIONS),
	LEGACY_ID_NAND("NAND 32MiB 3,3V 8-bit",  0x75, 32, SZ_16K, SP_OPTIONS),
	LEGACY_ID_NAND("NAND 32MiB 1,8V 16-bit", 0x45, 32, SZ_16K, SP_OPTIONS16),
	LEGACY_ID_NAND("NAND 32MiB 3,3V 16-bit", 0x55, 32, SZ_16K, SP_OPTIONS16),

	LEGACY_ID_NAND("NAND 64MiB 1,8V 8-bit",  0x36, 64, SZ_16K, SP_OPTIONS),
	LEGACY_ID_NAND("NAND 64MiB 3,3V 8-bit",  0x76, 64, SZ_16K, SP_OPTIONS),
	LEGACY_ID_NAND("NAND 64MiB 1,8V 16-bit", 0x46, 64, SZ_16K, SP_OPTIONS16),
	LEGACY_ID_NAND("NAND 64MiB 3,3V 16-bit", 0x56, 64, SZ_16K, SP_OPTIONS16),

	LEGACY_ID_NAND("NAND 128MiB 1,8V 8-bit",  0x78, 128, SZ_16K, SP_OPTIONS),
	LEGACY_ID_NAND("NAND 128MiB 1,8V 8-bit",  0x39, 128, SZ_16K, SP_OPTIONS),
	LEGACY_ID_NAND("NAND 128MiB 3,3V 8-bit",  0x79, 128, SZ_16K, SP_OPTIONS),
	LEGACY_ID_NAND("NAND 128MiB 1,8V 16-bit", 0x72, 128, SZ_16K, SP_OPTIONS16),
	LEGACY_ID_NAND("NAND 128MiB 1,8V 16-bit", 0x49, 128, SZ_16K, SP_OPTIONS16),
	LEGACY_ID_NAND("NAND 128MiB 3,3V 16-bit", 0x74, 128, SZ_16K, SP_OPTIONS16),
	LEGACY_ID_NAND("NAND 128MiB 3,3V 16-bit", 0x59, 128, SZ_16K, SP_OPTIONS16),

	LEGACY_ID_NAND("NAND 256MiB 3,3V 8-bit", 0x71, 256, SZ_16K, SP_OPTIONS),

	/*
	 * These are the new chips with large page size. Their page size and
	 * eraseblock size are determined from the extended ID bytes.
	 */

	/* 512 Megabit */
	EXTENDED_ID_NAND("NAND 64MiB 1,8V 8-bit",  0xA2,  64, LP_OPTIONS),
	EXTENDED_ID_NAND("NAND 64MiB 1,8V 8-bit",  0xA0,  64, LP_OPTIONS),
	EXTENDED_ID_NAND("NAND 64MiB 3,3V 8-bit",  0xF2,  64, LP_OPTIONS),
	EXTENDED_ID_NAND("NAND 64MiB 3,3V 8-bit",  0xD0,  64, LP_OPTIONS),
	EXTENDED_ID_NAND("NAND 64MiB 3,3V 8-bit",  0xF0,  64, LP_OPTIONS),
	EXTENDED_ID_NAND("NAND 64MiB 1,8V 16-bit", 0xB2,  64, LP_OPTIONS16),
	EXTENDED_ID_NAND("NAND 64MiB 1,8V 16-bit", 0xB0,  64, LP_OPTIONS16),
	EXTENDED_ID_NAND("NAND 64MiB 3,3V 16-bit", 0xC2,  64, LP_OPTIONS16),
	EXTENDED_ID_NAND("NAND 64MiB 3,3V 16-bit", 0xC0,  64, LP_OPTIONS16),

	/* 1 Gigabit */
	EXTENDED_ID_NAND("NAND 128MiB 1,8V 8-bit",  0xA1, 128, LP_OPTIONS),
	EXTENDED_ID_NAND("NAND 128MiB 3,3V 8-bit",  0xF1, 128, LP_OPTIONS),
	EXTENDED_ID_NAND("NAND 128MiB 3,3V 8-bit",  0xD1, 128, LP_OPTIONS),
	EXTENDED_ID_NAND("NAND 128MiB 1,8V 16-bit", 0xB1, 128, LP_OPTIONS16),
	EXTENDED_ID_NAND("NAND 128MiB 3,3V 16-bit", 0xC1, 128, LP_OPTIONS16),
	EXTENDED_ID_NAND("NAND 128MiB 1,8V 16-bit", 0xAD, 128, LP_OPTIONS16),

	/* 2 Gigabit */
	EXTENDED_ID_NAND("NAND 256MiB 1,8V 8-bit",  0xAA, 256, LP_OPTIONS),
	EXTENDED_ID_NAND("NAND 256MiB 3,3V 8-bit",  0xDA, 256, LP_OPTIONS),
	EXTENDED_ID_NAND("NAND 256MiB 1,8V 16-bit", 0xBA, 256, LP_OPTIONS16),
	EXTENDED_ID_NAND("NAND 256MiB 3,3V 16-bit", 0xCA, 256, LP_OPTIONS16),

	/* 4 Gigabit */
	EXTENDED_ID_NAND("NAND 512MiB 1,8V 8-bit",  0xAC, 512, LP_OPTIONS),
	EXTENDED_ID_NAND("NAND 512MiB 3,3V 8-bit",  0xDC, 512, LP_OPTIONS),
	EXTENDED_ID_NAND("NAND 512MiB 1,8V 16-bit", 0xBC, 512, LP_OPTIONS16),
	EXTENDED_ID_NAND("NAND 512MiB 3,3V 16-bit", 0xCC, 512, LP_OPTIONS16),

	/* 8 Gigabit */
	EXTENDED_ID_NAND("NAND 1GiB 1,8V 8-bit",  0xA3, 1024, LP_OPTIONS),
	EXTENDED_ID_NAND("NAND 1GiB 3,3V 8-bit",  0xD3, 1024, LP_OPTIONS),
	EXTENDED_ID_NAND("NAND 1GiB 1,8V 16-bit", 0xB3, 1024, LP_OPTIONS16),
	EXTENDED_ID_NAND("NAND 1GiB 3,3V 16-bit", 0xC3, 1024, LP_OPTIONS16),

	/* 16 Gigabit */
	EXTENDED_ID_NAND("NAND 2GiB 1,8V 8-bit",  0xA5, 2048, LP_OPTIONS),
	EXTENDED_ID_NAND("NAND 2GiB 3,3V 8-bit",  0xD5, 2048, LP_OPTIONS),
	EXTENDED_ID_NAND("NAND 2GiB 1,8V 16-bit", 0xB5, 2048, LP_OPTIONS16),
	EXTENDED_ID_NAND("NAND 2GiB 3,3V 16-bit", 0xC5, 2048, LP_OPTIONS16),

	/* 32 Gigabit */
	EXTENDED_ID_NAND("NAND 4GiB 1,8V 8-bit",  0xA7, 4096, LP_OPTIONS),
	EXTENDED_ID_NAND("NAND 4GiB 3,3V 8-bit",  0xD7, 4096, LP_OPTIONS),
	EXTENDED_ID_NAND("NAND 4GiB 1,8V 16-bit", 0xB7, 4096, LP_OPTIONS16),
	EXTENDED_ID_NAND("NAND 4GiB 3,3V 16-bit", 0xC7, 4096, LP_OPTIONS16),

	/* 64 Gigabit */
	EXTENDED_ID_NAND("NAND 8GiB 1,8V 8-bit",  0xAE, 8192, LP_OPTIONS),
	EXTENDED_ID_NAND("NAND 8GiB 3,3V 8-bit",  0xDE, 8192, LP_OPTIONS),
	EXTENDED_ID_NAND("NAND 8GiB 1,8V 16-bit", 0xBE, 8192, LP_OPTIONS16),
	EXTENDED_ID_NAND("NAND 8GiB 3,3V 16-bit", 0xCE, 8192, LP_OPTIONS16),

	/* 128 Gigabit */
	EXTENDED_ID_NAND("NAND 16GiB 1,8V 8-bit",  0x1A, 16384, LP_OPTIONS),
	EXTENDED_ID_NAND("NAND 16GiB 3,3V 8-bit",  0x3A, 16384, LP_OPTIONS),
	EXTENDED_ID_NAND("NAND 16GiB 1,8V 16-bit", 0x2A, 16384, LP_OPTIONS16),
	EXTENDED_ID_NAND("NAND 16GiB 3,3V 16-bit", 0x4A, 16384, LP_OPTIONS16),

	/* 256 Gigabit */
	EXTENDED_ID_NAND("NAND 32GiB 1,8V 8-bit",  0x1C, 32768, LP_OPTIONS),
	EXTENDED_ID_NAND("NAND 32GiB 3,3V 8-bit",  0x3C, 32768, LP_OPTIONS),
	EXTENDED_ID_NAND("NAND 32GiB 1,8V 16-bit", 0x2C, 32768, LP_OPTIONS16),
	EXTENDED_ID_NAND("NAND 32GiB 3,3V 16-bit", 0x4C, 32768, LP_OPTIONS16),

	/* 512 Gigabit */
	EXTENDED_ID_NAND("NAND 64GiB 1,8V 8-bit",  0x1E, 65536, LP_OPTIONS),
	EXTENDED_ID_NAND("NAND 64GiB 3,3V 8-bit",  0x3E, 65536, LP_OPTIONS),
	EXTENDED_ID_NAND("NAND 64GiB 1,8V 16-bit", 0x2E, 65536, LP_OPTIONS16),
	EXTENDED_ID_NAND("NAND 64GiB 3,3V 16-bit", 0x4E, 65536, LP_OPTIONS16),

	{NULL}
};

若nandflash支持onfi接口,则无须在nand_flash_ids中设置相关参数,由nand驱动模型中的nand_flash_detect_onfi函数即可通过与nandflash通信,获取writesize、erasesize、oobsize、chipsize等信息即可。

2.2、Nandflash对应分区属性设置

在 Linux 中,NAND Flash 分区的属性设置通常是通过 MTD (Memory Technology Devices) 子系统来管理的。MTD 子系统提供了对 Flash 存储设备的访问接口,包括 NAND、NOR Flash 等。要正确配置 NAND Flash 的分区属性,你需要为每个分区设置相应的特性、大小、偏移量以及存储方式。以下是如何在 Linux 中设置 NAND Flash 分区属性的详细步骤。

1. 理解 NAND Flash 和 MTD 分区

MTD 设备通过分区和设备节点来管理 Flash 存储。每个 MTD 设备可以被划分为多个分区,每个分区可以指定大小、偏移量、存储特性等。

NAND Flash 在 MTD 中的分区通常包括:

  • Block size:每个擦除块的大小(NAND Flash 的擦除单位,通常为 128KB、256KB、512KB 或 2MB 等)。
  • Page size:每个页的大小(通常为 2KB、4KB、8KB、16KB 等)。
  • OOB (Out-Of-Band) 区:用于存储冗余数据,如坏块标记和 ECC 校验。

2. MTD 分区配置

在 Linux 中,NAND Flash 的分区通常是通过设备树(DTS)或命令行(如 mtd 工具)进行配置的。

设备树配置 (DTS)

在设备树中,我们可以通过 mtd 节点来配置 NAND Flash 设备和其分区。分区配置通常是通过在设备树文件中定义 mtd 分区来实现的。

示例:

/ {
    nand: nand@0 {
        compatible = "foo,nand-controller";
        reg = <0x0 0x10000000 0x0 0x1000000>;
        #address-cells = <1>;
        #size-cells = <1>;
        status = "okay";

        mtd-names = "nand0";
        
        mtd@0 {
            label = "boot";
            reg = <0x0 0x400000>;    // 分区的起始地址和大小
            partition-size = <0x400000>;
            read-only;
        };

        mtd@1 {
            label = "rootfs";
            reg = <0x400000 0x6000000>;  // 起始地址和大小
            partition-size = <0x6000000>;
        };
    };
};

在这个示例中:

  • mtd@0mtd@1 是定义的两个分区,分别用于 bootrootfs
  • reg 定义了分区的起始地址和大小。
  • read-only 表示该分区为只读。

通过 mtd 命令行工具配置

可以通过 mtd 工具在命令行中配置 NAND Flash 分区。mtd 提供了许多有用的命令,如:

  • mtd_debug 用于调试
  • flash_erase 用于擦除特定的 Flash 区域
  • mtdinfo 用于显示 NAND Flash 设备的基本信息
  • nandwrite 用于将数据写入 NAND Flash

配置 NAND 分区的命令通常是通过 U-Boot 或根文件系统中的脚本来完成的,或者在内核中使用 nand 设备驱动进行分区。

修改内核配置 (使用 CONFIG_MTD 配置项)

在内核配置中,需要启用 MTD 和 NAND 支持。你可以通过 make menuconfigmake xconfig 工具配置内核选项:

Device Drivers  --->
    [*] Memory Technology Device (MTD) support  --->
        [*] NAND Flash support
        [*]  NAND device driver
        [*]   NAND MTD device support

启用上述选项后,内核将支持 NAND Flash 存储和相关的 MTD 分区。

3. 配置分区的属性

在分区中,你可能需要配置以下几个关键属性:

  • 大小 (size):分区的大小,通常以字节为单位。例如,4MB、8MB、16MB 等。
  • 偏移量 (offset):分区的起始地址。此值通常基于 Flash 存储的起始位置。
  • 类型 (type):分区的类型,例如 bootrootfsuserdata 等。
  • 只读/读写权限 (read-only):如果设置为只读,则该分区不能被写入。
  • 坏块标记 (bad-block handling):NAND 存储常常有坏块,需要在分区中跳过或标记它们。

4. 坏块管理

由于 NAND Flash 的物理特性,NAND 存储通常有坏块,在分区时需要考虑坏块管理。内核的 MTD 子系统有内建的坏块管理机制,它会自动处理坏块。可以通过以下方式来启用坏块检测和处理:

自动检测和跳过坏块

内核的 MTD 子系统会在操作时自动检测坏块并跳过,但你也可以在 mtd 配置中使用坏块管理特性:

&nand {
    bad-block-pos = <0x200>;
};

在这个配置中,bad-block-pos 定义了坏块标记的位置,通常是 OOB 区域中的某个位置。坏块标记通常会存储在 OOB 区域的特定位置,内核会根据这些标记来跳过坏块。

手动标记坏块

如果在开发过程中需要手动标记坏块,可以使用 flash_erasenandwrite 等工具进行坏块标记。

5. 在用户空间使用分区

在 Linux 启动后,MTD 子系统将以块设备的形式挂载 NAND Flash 分区。这些分区通常会出现在 /dev/mtdblockX/dev/mtdX 下,具体取决于内核配置。

例如,如果你在设备树中定义了 mtd@0 分区,内核启动时该分区会被挂载为 /dev/mtdblock0。你可以通过常见的文件系统工具(如 mount)或 MTD 特定工具来操作这些分区。

mount -t jffs2 /dev/mtdblock0 /mnt

2.3、Nandflash的oob设置

在 Linux 中,NAND Flash 的 OOB (Out-Of-Band) 区域 是一个存储冗余数据的区域,通常用于存储坏块标记、ECC 校验数据、标识符以及其他元数据。OOB 区域的具体用途和配置方式可能依赖于 NAND Flash 控制器的硬件特性、内核驱动以及使用的文件系统。

在 MTD (Memory Technology Device) 子系统中,OOB 区域的配置和管理通常是由 NAND 驱动程序处理的。下面是有关如何在 Linux 中配置和使用 NAND Flash 的 OOB 区域的一些重要细节。

1. OOB 区域的作用

NAND Flash 通常由多个页组成,每个页都包含两个区域:

  • 数据区:存储实际的数据内容。
  • OOB 区域:存储附加信息,如坏块标记、ECC 校验码、页标识符等。

每个页通常分为两个部分:数据部分和 OOB 部分。OOB 区域通常位于页的末尾,其大小和位置依赖于 NAND Flash 的具体型号。

OOB 区域的常见用途包括:

  • 坏块标记:用于标记 NAND Flash 中的坏块。
  • ECC (Error Correction Code) 校验码:用于纠正由于 NAND Flash 的物理特性导致的错误。
  • 分页标识符和元数据:用于存储一些文件系统和驱动程序所需的元数据信息。

2. 如何配置 OOB 区域

在 Linux 中,OOB 区域的设置通常由 NAND Flash 驱动(如 nand 驱动或 mtd 驱动)控制。在内核中,OOB 区域的大小通常在驱动初始化时配置。通常情况下,OOB 区域的大小是固定的,但它可以通过设备树(DTS)或内核配置进行微调。

OOB 大小的配置

NAND Flash 的 OOB 区域的大小通常由 NAND 芯片的硬件规格决定,不同型号的 NAND Flash 的 OOB 区域大小可能不同。Linux 内核中的 NAND 驱动程序通常会根据芯片的特性来计算和设置 OOB 区域的大小。

在驱动程序中,OOB 区域的大小可以通过 nand_chip->oob_poinand_chip->oobsize 等参数来进行管理。oobsize 是表示 OOB 区域大小的一个重要参数。

设备树配置

在设备树中,你可以指定 NAND 控制器的某些属性,包括 OOB 区域的设置。以下是一个配置设备树的例子,其中配置了 NAND Flash 的 OOB 区域:

dts

&nand {
    status = "okay";
    reg = <0x0 0x20000000 0x0 0x1000000>;
    compatible = "vendor,nand-flash";
    nand-ecc-mode = "hw";
    nand-oobsize = <224>;  // 设置 OOB 大小为 224 字节
    nand-page-size = <4096>; // 设置页大小为 4KB
};

在这个例子中,nand-oobsize 参数用于设置 OOB 区域的大小。请注意,不同的 NAND 控制器或芯片可能对 OOB 区域的配置要求不同,因此具体的配置需要参考芯片的数据手册。

内核代码中的 OOB 配置

在 Linux 内核代码中,OOB 区域的配置通常通过 mtd 子系统和相关的 NAND 驱动来完成。例如,nand_base.c 驱动程序负责初始化 NAND Flash 芯片及其特性。

内核中用于配置 NAND 驱动的代码可能如下所示:

static struct nand_chip nand_chip = {
    .ecc.mode = NAND_ECC_HW,
    .ecc.size = 512,
    .oobsize = 224,   // 配置 OOB 大小为 224 字节
    .page_shift = 12, // 页大小为 4KB (2^12)
    .page_size = 4096,
};

在这个例子中,oobsize 指定了 OOB 区域的大小。

3. 使用 OOB 区域

OOB 区域通常不直接由文件系统来访问,而是由底层的 NAND 驱动和文件系统共同管理。例如:

  • 坏块管理:MTD 驱动会在 NAND Flash 中自动标记坏块,并在访问时跳过坏块。坏块信息通常存储在 OOB 区域中。
  • ECC 校验:NAND 驱动程序可以使用 OOB 区域来存储 ECC 校验码,以便在读取数据时对数据进行纠错。

在 Linux 文件系统(如 JFFS2UBIFS)中,通常会与 NAND 驱动紧密配合,利用 OOB 区域的元数据来执行错误检测和修正、坏块管理等操作。

4. 读取和写入 OOB 区域

操作 OOB 区域的最常见方式是通过 NAND 驱动提供的 API。例如,使用 nand_write_oob()nand_read_oob() 函数来写入和读取 OOB 区域的数据。

读取 OOB 数据

要读取 OOB 区域的数据,可以使用 nand_read_oob() 函数。下面是一个示例:

int nand_read_oob(struct nand_chip *chip, unsigned int page, int block)
{
    int ret;
    unsigned char oob_data[chip->oobsize];
    
    ret = chip->read_oob(chip, page, block, oob_data);
    if (ret) {
        pr_err("Failed to read OOB data from page %u\n", page);
        return ret;
    }
    
    pr_info("OOB data: %*ph\n", chip->oobsize, oob_data);
    return 0;
}

在此代码中,read_oob 函数会从指定页面读取 OOB 区域的数据。

写入 OOB 数据

同样的,nand_write_oob() 可以用来将数据写入 OOB 区域:

int nand_write_oob(struct nand_chip *chip, unsigned int page, int block, unsigned char *oob_data)
{
    int ret;
    
    ret = chip->write_oob(chip, page, block, oob_data);
    if (ret) {
        pr_err("Failed to write OOB data to page %u\n", page);
        return ret;
    }
    
    pr_info("OOB data written successfully to page %u\n", page);
    return 0;
}

这段代码演示了如何将数据写入 OOB 区域。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值