Linux Mtd子系统5

Linux Mtd子系统5(基于Linux6.6)---nand驱动注册介绍

一、Nand_controller驱动的注册流程及功能说明

 前面已经介绍了mtd驱动模型、nand驱动模型以及mtd与nand的关联、mtd与vfs之间的关联。主要介绍nand_chip(nand controller对应的逻辑抽象结构体)的注册接口。而针对nand_chip而言,其注册接口所实现的功能主要包括哪些呢?借助上一篇文章的图来进行说明。

  1. 建立mtd_info(该mtd_info为该nandflash芯片对应的master mtd_info)中访问nandflash相关的函数指针,主要用于指向nand驱动模型中通用接口(即nand_write、nand_write、nand_read_oob等接口);
  2. 实现nand_chip中ecc控制相关成员,即实现nand_chip->ecc变量,即实现(struct nand_ecc_ctrl类型的结构体变量,包括ecc计算接口、ecc纠错接口、读取一页接口、写一页数据接口、ecc字节数、单次进行ecc计算的数据字节数等等信息,该接口可由具体的nand_controller驱动自己赋值,无须nand驱动模型的注册函数实现,若具体的nand_controller驱动未设置,则由nand驱动模型的注册函数使用通用的接口(soft ecc接口));
  3. 实现nand_chip中通过nand_controller与nandflash进行命令通信的接口cmd_ctrl(该接口因不同nand_controller而不同,因此针对不同的nand_controller,需要实现对应的cmd_ctrl);
  4. 实现nand_chip中与nand flash进行通信的命令接口cmdfunc,该接口主要调用cmd_ctrl,实现将相关操作命令下发给nandflash(如读取nandflash id、读写数据、读写oob数据等命令);
  5. 实现nand_chip中的read_byte、read_word、read_buf、write_buf、block_bad等接口,这些接口实现读写flash的功能,同时read_byte、read_word、read_buf、write_buf接口也主要是由上述2中读写一页数据的接口调用;
  6. 实现坏块表的建立操作;
  7. 获取nandflash的页大小、块大小、flash大小等参数大小,并赋值给nand_chip中具体的参数中。

nand_chip的注册大致即实现以上这些功能,完成上述功能后,也就实现了mtd_info->_read/_write--->nand_chip->ecc->read_page/write_page/…--->nand_chip->cmdfunc/read_buf/write_buf接口间的调用逻辑的关联,从而即可实现通过mtd_info中的函数指针即可完成调用nand_chip->read_buf/write_buf,实现与nandflash的通信。

而针对nand_chip的注册,nand驱动模型提供了两个接口nand_scan_ident、nand_scan_tail,通过这两个接口即完成了上述所说的功能,下面我们分析下这两个接口。

1.1、Nand_scan_ident接口分析

 drivers/mtd/nand/raw/nand_base.c

static int nand_scan_ident(struct nand_chip *chip, unsigned int maxchips,
			   struct nand_flash_dev *table)
{
	struct mtd_info *mtd = nand_to_mtd(chip);
	struct nand_memory_organization *memorg;
	int nand_maf_id, nand_dev_id;
	unsigned int i;
	int ret;

	memorg = nanddev_get_memorg(&chip->base);

	/* Assume all dies are deselected when we enter nand_scan_ident(). */
	chip->cur_cs = -1;

	mutex_init(&chip->lock);
	init_waitqueue_head(&chip->resume_wq);

	/* Enforce the right timings for reset/detection */
	chip->current_interface_config = nand_get_reset_interface_config();

	ret = rawnand_dt_init(chip);
	if (ret)
		return ret;

	if (!mtd->name && mtd->dev.parent)
		mtd->name = dev_name(mtd->dev.parent);

	/* Set the default functions */
	nand_set_defaults(chip);

	ret = nand_legacy_check_hooks(chip);
	if (ret)
		return ret;

	memorg->ntargets = maxchips;

	/* Read the flash type */
	ret = nand_detect(chip, table);
	if (ret) {
		if (!(chip->options & NAND_SCAN_SILENT_NODEV))
			pr_warn("No NAND device found\n");
		nand_deselect_target(chip);
		return ret;
	}

	nand_maf_id = chip->id.data[0];
	nand_dev_id = chip->id.data[1];

	nand_deselect_target(chip);

	/* Check for a chip array */
	for (i = 1; i < maxchips; i++) {
		u8 id[2];

		/* See comment in nand_get_flash_type for reset */
		ret = nand_reset(chip, i);
		if (ret)
			break;

		nand_select_target(chip, i);
		/* Send the command for reading device ID */
		ret = nand_readid_op(chip, 0, id, sizeof(id));
		if (ret)
			break;
		/* Read manufacturer and device IDs */
		if (nand_maf_id != id[0] || nand_dev_id != id[1]) {
			nand_deselect_target(chip);
			break;
		}
		nand_deselect_target(chip);
	}
	if (i > 1)
		pr_info("%d chips detected\n", i);

	/* Store the number of chips and calc total size for mtd */
	memorg->ntargets = i;
	mtd->size = i * nanddev_target_size(&chip->base);

	return 0;
}

该接口主要调用nand_set_defaults、nand_get_flash_type接口实现nand_chip中成员变量的设置,主要实现的功能如下:

  1. 调用nand_set_defaults接口,设置nand_chip中读写flash的接口(若未设置,则使用通用的接口,如nand_command、nand_wait、nand_read_byte、nand_read_byte16、nand_read_word、nand_read_buf、nand_write_buf、nand_block_bad等等接口);
  2. 调用nand_get_flash_type接口,获取nandflash相关的参数(若page size、block size、oob size、flash size等信息),该接口主要通过如下操作实现参数的获取:
    1. 通过读取flash的设备id信息,并从全局变量nand_flash_ids中查找是否存在该设备id对应的定义参数,若存在,则获取page size、block size、oob size、flash size等等信息;若不存在,则进入步骤b;
    2. 通过调用nand_flash_detect_onfi接口,判断flash是否支持onfi规范,若支持则读取onfi相关的参数,从而获取page size、block size、oob size、flash size等等信息;若未获取到,则进入步骤c;
    3. 则根据设备id、扩展id,设置对应的page size、block size、oob size、flash size等等信息;

1.2、Nand_scan_tail接口分析

drivers/mtd/nand/raw/nand_base.c

/**
 * nand_scan_tail - Scan for the NAND device
 * @chip: NAND chip object
 *
 * This is the second phase of the normal nand_scan() function. It fills out
 * all the uninitialized function pointers with the defaults and scans for a
 * bad block table if appropriate.
 */
static int nand_scan_tail(struct nand_chip *chip)
{
	struct mtd_info *mtd = nand_to_mtd(chip);
	struct nand_ecc_ctrl *ecc = &chip->ecc;
	int ret, i;

	/* New bad blocks should be marked in OOB, flash-based BBT, or both */
	if (WARN_ON((chip->bbt_options & NAND_BBT_NO_OOB_BBM) &&
		   !(chip->bbt_options & NAND_BBT_USE_FLASH))) {
		return -EINVAL;
	}

	chip->data_buf = kmalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL);
	if (!chip->data_buf)
		return -ENOMEM;

	/*
	 * FIXME: some NAND manufacturer drivers expect the first die to be
	 * selected when manufacturer->init() is called. They should be fixed
	 * to explictly select the relevant die when interacting with the NAND
	 * chip.
	 */
	nand_select_target(chip, 0);
	ret = nand_manufacturer_init(chip);
	nand_deselect_target(chip);
	if (ret)
		goto err_free_buf;

	/* Set the internal oob buffer location, just after the page data */
	chip->oob_poi = chip->data_buf + mtd->writesize;

	/*
	 * If no default placement scheme is given, select an appropriate one.
	 */
	if (!mtd->ooblayout &&
	    !(ecc->engine_type == NAND_ECC_ENGINE_TYPE_SOFT &&
	      ecc->algo == NAND_ECC_ALGO_BCH) &&
	    !(ecc->engine_type == NAND_ECC_ENGINE_TYPE_SOFT &&
	      ecc->algo == NAND_ECC_ALGO_HAMMING)) {
		switch (mtd->oobsize) {
		case 8:
		case 16:
			mtd_set_ooblayout(mtd, nand_get_small_page_ooblayout());
			break;
		case 64:
		case 128:
			mtd_set_ooblayout(mtd,
					  nand_get_large_page_hamming_ooblayout());
			break;
		default:
			/*
			 * Expose the whole OOB area to users if ECC_NONE
			 * is passed. We could do that for all kind of
			 * ->oobsize, but we must keep the old large/small
			 * page with ECC layout when ->oobsize <= 128 for
			 * compatibility reasons.
			 */
			if (ecc->engine_type == NAND_ECC_ENGINE_TYPE_NONE) {
				mtd_set_ooblayout(mtd,
						  nand_get_large_page_ooblayout());
				break;
			}

			WARN(1, "No oob scheme defined for oobsize %d\n",
				mtd->oobsize);
			ret = -EINVAL;
			goto err_nand_manuf_cleanup;
		}
	}

	/*
	 * Check ECC mode, default to software if 3byte/512byte hardware ECC is
	 * selected and we have 256 byte pagesize fallback to software ECC
	 */

	switch (ecc->engine_type) {
	case NAND_ECC_ENGINE_TYPE_ON_HOST:
		ret = nand_set_ecc_on_host_ops(chip);
		if (ret)
			goto err_nand_manuf_cleanup;

		if (mtd->writesize >= ecc->size) {
			if (!ecc->strength) {
				WARN(1, "Driver must set ecc.strength when using hardware ECC\n");
				ret = -EINVAL;
				goto err_nand_manuf_cleanup;
			}
			break;
		}
		pr_warn("%d byte HW ECC not possible on %d byte page size, fallback to SW ECC\n",
			ecc->size, mtd->writesize);
		ecc->engine_type = NAND_ECC_ENGINE_TYPE_SOFT;
		ecc->algo = NAND_ECC_ALGO_HAMMING;
		fallthrough;

	case NAND_ECC_ENGINE_TYPE_SOFT:
		ret = nand_set_ecc_soft_ops(chip);
		if (ret)
			goto err_nand_manuf_cleanup;
		break;

	case NAND_ECC_ENGINE_TYPE_ON_DIE:
		if (!ecc->read_page || !ecc->write_page) {
			WARN(1, "No ECC functions supplied; on-die ECC not possible\n");
			ret = -EINVAL;
			goto err_nand_manuf_cleanup;
		}
		if (!ecc->read_oob)
			ecc->read_oob = nand_read_oob_std;
		if (!ecc->write_oob)
			ecc->write_oob = nand_write_oob_std;
		break;

	case NAND_ECC_ENGINE_TYPE_NONE:
		pr_warn("NAND_ECC_ENGINE_TYPE_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;

	default:
		WARN(1, "Invalid NAND_ECC_MODE %d\n", ecc->engine_type);
		ret = -EINVAL;
		goto err_nand_manuf_cleanup;
	}

	if (ecc->correct || ecc->calculate) {
		ecc->calc_buf = kmalloc(mtd->oobsize, GFP_KERNEL);
		ecc->code_buf = kmalloc(mtd->oobsize, GFP_KERNEL);
		if (!ecc->calc_buf || !ecc->code_buf) {
			ret = -ENOMEM;
			goto err_nand_manuf_cleanup;
		}
	}

	/* For many systems, the standard OOB write also works for raw */
	if (!ecc->read_oob_raw)
		ecc->read_oob_raw = ecc->read_oob;
	if (!ecc->write_oob_raw)
		ecc->write_oob_raw = ecc->write_oob;

	/* propagate ecc info to mtd_info */
	mtd->ecc_strength = ecc->strength;
	mtd->ecc_step_size = ecc->size;

	/*
	 * Set the number of read / write steps for one page depending on ECC
	 * mode.
	 */
	if (!ecc->steps)
		ecc->steps = mtd->writesize / ecc->size;
	if (ecc->steps * ecc->size != mtd->writesize) {
		WARN(1, "Invalid ECC parameters\n");
		ret = -EINVAL;
		goto err_nand_manuf_cleanup;
	}

	if (!ecc->total) {
		ecc->total = ecc->steps * ecc->bytes;
		chip->base.ecc.ctx.total = ecc->total;
	}

	if (ecc->total > mtd->oobsize) {
		WARN(1, "Total number of ECC bytes exceeded oobsize\n");
		ret = -EINVAL;
		goto err_nand_manuf_cleanup;
	}

	/*
	 * The number of bytes available for a client to place data into
	 * the out of band area.
	 */
	ret = mtd_ooblayout_count_freebytes(mtd);
	if (ret < 0)
		ret = 0;

	mtd->oobavail = ret;

	/* ECC sanity check: warn if it's too weak */
	if (!nand_ecc_is_strong_enough(&chip->base))
		pr_warn("WARNING: %s: the ECC used on your system (%db/%dB) is too weak compared to the one required by the NAND chip (%db/%dB)\n",
			mtd->name, chip->ecc.strength, chip->ecc.size,
			nanddev_get_ecc_requirements(&chip->base)->strength,
			nanddev_get_ecc_requirements(&chip->base)->step_size);

	/* Allow subpage writes up to ecc.steps. Not possible for MLC flash */
	if (!(chip->options & NAND_NO_SUBPAGE_WRITE) && nand_is_slc(chip)) {
		switch (ecc->steps) {
		case 2:
			mtd->subpage_sft = 1;
			break;
		case 4:
		case 8:
		case 16:
			mtd->subpage_sft = 2;
			break;
		}
	}
	chip->subpagesize = mtd->writesize >> mtd->subpage_sft;

	/* Invalidate the pagebuffer reference */
	chip->pagecache.page = -1;

	/* Large page NAND with SOFT_ECC should support subpage reads */
	switch (ecc->engine_type) {
	case NAND_ECC_ENGINE_TYPE_SOFT:
		if (chip->page_shift > 9)
			chip->options |= NAND_SUBPAGE_READ;
		break;

	default:
		break;
	}

	ret = nanddev_init(&chip->base, &rawnand_ops, mtd->owner);
	if (ret)
		goto err_nand_manuf_cleanup;

	/* Adjust the MTD_CAP_ flags when NAND_ROM is set. */
	if (chip->options & NAND_ROM)
		mtd->flags = MTD_CAP_ROM;

	/* Fill in remaining MTD driver data */
	mtd->_erase = nand_erase;
	mtd->_point = NULL;
	mtd->_unpoint = NULL;
	mtd->_panic_write = panic_nand_write;
	mtd->_read_oob = nand_read_oob;
	mtd->_write_oob = nand_write_oob;
	mtd->_sync = nand_sync;
	mtd->_lock = nand_lock;
	mtd->_unlock = nand_unlock;
	mtd->_suspend = nand_suspend;
	mtd->_resume = nand_resume;
	mtd->_reboot = nand_shutdown;
	mtd->_block_isreserved = nand_block_isreserved;
	mtd->_block_isbad = nand_block_isbad;
	mtd->_block_markbad = nand_block_markbad;
	mtd->_max_bad_blocks = nanddev_mtd_max_bad_blocks;

	/*
	 * Initialize bitflip_threshold to its default prior scan_bbt() call.
	 * scan_bbt() might invoke mtd_read(), thus bitflip_threshold must be
	 * properly set.
	 */
	if (!mtd->bitflip_threshold)
		mtd->bitflip_threshold = DIV_ROUND_UP(mtd->ecc_strength * 3, 4);

	/* Find the fastest data interface for this chip */
	ret = nand_choose_interface_config(chip);
	if (ret)
		goto err_nanddev_cleanup;

	/* Enter fastest possible mode on all dies. */
	for (i = 0; i < nanddev_ntargets(&chip->base); i++) {
		ret = nand_setup_interface(chip, i);
		if (ret)
			goto err_free_interface_config;
	}

	rawnand_late_check_supported_ops(chip);

	/*
	 * Look for secure regions in the NAND chip. These regions are supposed
	 * to be protected by a secure element like Trustzone. So the read/write
	 * accesses to these regions will be blocked in the runtime by this
	 * driver.
	 */
	ret = of_get_nand_secure_regions(chip);
	if (ret)
		goto err_free_interface_config;

	/* Check, if we should skip the bad block table scan */
	if (chip->options & NAND_SKIP_BBTSCAN)
		return 0;

	/* Build bad block table */
	ret = nand_create_bbt(chip);
	if (ret)
		goto err_free_secure_regions;

	return 0;

err_free_secure_regions:
	kfree(chip->secure_regions);

err_free_interface_config:
	kfree(chip->best_interface_config);

err_nanddev_cleanup:
	nanddev_cleanup(&chip->base);

err_nand_manuf_cleanup:
	nand_manufacturer_cleanup(chip);

err_free_buf:
	kfree(chip->data_buf);
	kfree(ecc->code_buf);
	kfree(ecc->calc_buf);

	return ret;
}

该接口实现的功能如下:

  1. 若nand_chip->ecc.layout 未设置,则根据ecc类型,设置默认的oob信息;
  2. 依据nand_chip->ecc的类型,并确认是否需要设置ecc中calculate、correct、read_page、read_subpage、write_page、read_page_raw、write_page_raw、read_oob、write_oob接口(若nand_controller驱动未设置这些函数指针,则设置这几个接口,针对不同的ecc算法,默认接口也有所不同);
  3. 设置mtd_info中的函数指针,主要涉及_erase、_read、_write、_panic_write、_read_oob、_write_oob、_sync、_suspend、_resume、_block_isbad、_block_markbad等接口,并设置mtd_info的type、flags、ecclayout、ecc_strength等接口;
  4. 若nand_chip设置了bbt标志,则调用chip->scan_bbt,建立坏块表。

以上便为nand_scan_ident、nand_scan_tail接口的实现流程,说白了也就是设置mtd_info的_erase、_read、_write等接口,以及nand_chip的ecc中的接口、nand_chip的函数指针等等内容,最终完成本章开头的逻辑关联图。支持mtd_info、nand_chip的所有成员变量也已完成了设置。

那至此时,是否已经算是完成了mtd_info、nand_chip的所有关联了呢?

还差最后一步,那就是需要进行master mtd_info、slaver mtd_info的关联,这一步需要调用mtd_device_parse_register接口。这才完成了mtd_info、nand_chip的关联,也就建立了如下的关联图:

二、Vand驱动模型中通用接口说明

nand驱动模型提供的通用接口,主要用于抽象针对所有nand controller以及所有nand flash的读写接口等信息,在 Linux 中,NAND 驱动模型提供的通用接口是为了抽象和统一与 NAND 控制器和 NAND Flash 存储设备进行交互的操作。这些接口使得不同的 NAND 控制器和不同的 NAND Flash 设备能够通过统一的方式进行操作,从而简化了对不同硬件平台的支持。下面详细介绍 NAND 驱动模型提供的几类主要通用接口:

1. NAND Flash 擦除接口 (Erase Interface)

NAND Flash 的擦除操作通常是以块为单位进行的,即擦除 NAND Flash 中的一个块(block)。擦除操作是 NAND 存储中一个非常重要的过程,通常在每次写入数据前需要进行擦除。

接口示例:

  • int nand_erase_blk(struct nand_chip *chip, int page)
    该函数用于擦除一个块。在擦除时,传入的是页面索引或者块的标识,nand_chip 是指向 NAND 控制器芯片的指针。

  • 擦除过程:

    • 先通过 nand_chip 发送擦除命令到硬件。
    • 确保擦除操作完成,通常通过查询状态寄存器或使用超时机制来确认擦除完成。
  • 相关实现:

  • int nand_erase(struct nand_chip *chip, struct nand_blk *blk)
    {
        // 向 NAND 发送擦除命令
        // 确保块擦除操作完成
        ...
    }
    

2. NAND Flash 读写接口 (Read/Write Interface)

NAND Flash 的读写操作是最基本的操作之一。NAND 驱动模型提供了抽象的接口来执行读取和写入操作。操作的基本单位是页(page),在大多数 NAND Flash 中,一页通常是 2KB 到 16KB 的大小。

读接口:

  • int nand_read_page(struct nand_chip *chip, unsigned int page, void *buf) 该接口用于从指定的页面读取数据。读取时,传入页面编号以及缓冲区。

写接口:

  • int nand_write_page(struct nand_chip *chip, unsigned int page, const void *buf) 该接口用于将数据写入指定的页面。

  • 实现过程:

    • 读取操作: 使用 nand_chip 提供的函数,将数据从 NAND Flash 的指定页面读取到缓冲区。
    • 写入操作: 在写入之前,通常需要先进行擦除操作,因为 NAND Flash 不能覆盖写入。
  • 相关实现:

  • int nand_read_page(struct nand_chip *chip, unsigned int page, void *buf)
    {
        // 发送读取命令
        // 等待数据返回
        // 将数据读到 buf
        ...
    }
    
    int nand_write_page(struct nand_chip *chip, unsigned int page, const void *buf)
    {
        // 发送写入命令
        // 将数据写入 NAND Flash 页面
        ...
    }
    

3. NAND Flash OOB (Out-Of-Band) 区域读写接口 (OOB Read/Write Interface)

OOB 区域是 NAND Flash 中的一部分数据存储区域,通常用于存储错误校验信息(ECC 数据)、坏块标记或其他辅助数据。Linux 的 NAND 驱动提供了独立的 OOB 读写接口。

OOB 读接口:

  • int nand_read_oob(struct nand_chip *chip, unsigned int page, void *buf) 该接口用于读取指定页面的 OOB 区域。读取的 OOB 区域通常包含 ECC 信息、坏块标记等。

OOB 写接口:

  • int nand_write_oob(struct nand_chip *chip, unsigned int page, const void *buf) 该接口用于向指定页面的 OOB 区域写入数据。OOB 区域的写入通常在页面写入时一并完成。

  • 实现过程:

    • 通过 nand_chip 发送 OOB 相关的命令,读取或写入相应的 OOB 区域。
  • 相关实现:

  • int nand_read_oob(struct nand_chip *chip, unsigned int page, void *buf)
    {
        // 读取指定页面的 OOB 数据
        ...
    }
    
    int nand_write_oob(struct nand_chip *chip, unsigned int page, const void *buf)
    {
        // 写入指定页面的 OOB 数据
        ...
    }
    

4. NAND Flash 坏块检测及标记接口 (Bad Block Detection and Marking Interface)

坏块管理是 NAND Flash 操作中非常重要的一部分。由于 NAND Flash 的特性,某些存储块可能会随着时间的推移而发生故障,导致无法正常读写。Linux NAND 驱动提供了坏块检测和标记的接口,用于发现并标记坏块,以避免在未来的读写操作中使用它们。

坏块检测接口:

  • int nand_block_isbad(struct nand_chip *chip, unsigned int block) 该接口用于检测指定的块是否是坏块。通常情况下,通过读取该块的 OOB 区域中的坏块标记来判断。

坏块标记接口:

  • int nand_block_markbad(struct nand_chip *chip, unsigned int block) 该接口用于标记指定的块为坏块。通常会通过修改该块的 OOB 区域中的坏块标记来实现。

  • 实现过程:

    • 检测坏块: 读取 OOB 区域或执行特定的读取操作,如果读取失败,则判断该块为坏块。
    • 标记坏块: 在 OOB 区域中写入坏块标记,或者直接通过控制寄存器进行标记。
  • 相关实现:

int nand_block_isbad(struct nand_chip *chip, unsigned int block)
{
    // 检查块的 OOB 区域是否标记为坏块
    ...
}

int nand_block_markbad(struct nand_chip *chip, unsigned int block)
{
    // 在 OOB 区域标记坏块
    ...
}

以nand_write为例进行简要说明,nand_write实现的流程如下:

  1. 调用nand_get_device接口获取nand_chip的执行权(若当前nand_chip不在ready状态(即nand_chip再执行其他的读、写或者擦除等操作),则将该进程加入到等待队列中,并把该进程调度出去);
  2. 获取nand_chip的执行权后,则调用nand_do_write_ops执行写操作;
    1. nand_do_write_ops接口中判断flash处于写保护,则返回失败;
    2. nand_do_write_ops通过调用chip->write_page进行写操作(即nand_write_page接口);而nand_write_page则会调用nand_chip->ecc->write_page接口;而在nand_chip->ecc->write_page接口则会调用nand-chip->write_buff进行写操作。

以上变为nand_write接口的写操作流程,总结一下,nand驱动模型中通用接口的调用逻辑如下所示(nand_write的调用也遵循下面的调用流程),nand驱动模块的通用抽象接口通过nand_chip->ecc、nand_chip接口的调用关系,关联在一起。

三、总结

在 Linux 中,NAND 驱动的注册和相关接口的实现流程涉及多个阶段,包括对 NAND Flash 设备的识别、初始化、配置以及驱动层次结构的建立。以下是对 nand_chip 注册流程的总结以及驱动模型的通用抽象接口的说明。

3.1、nand_chip 注册流程

NAND 驱动注册的流程主要包括以下步骤:nand_scan_identnand_scan_tail 是该过程中的两个关键接口。

nand_scan_ident

nand_scan_ident 函数的作用是扫描并识别连接到 NAND 控制器的设备。它会完成以下任务:

  • 设备识别: 通过发送读取命令(通常是读取 ID 命令)来获取 NAND Flash 的 ID 信息,包括 NAND Flash 的制造商、型号、尺寸、页大小、块大小等。
  • 参数设置: 根据识别出的 ID 信息,nand_scan_ident 会设置 nand_chip 结构中的参数(如页大小、块大小等),以便后续的操作。

具体实现流程如下:

  1. 向 NAND Flash 发送命令以读取 ID。
  2. 解析 NAND Flash 返回的 ID 数据。
  3. 根据 ID 数据初始化 nand_chip 结构的相关字段,如页大小、块数量、ECC 方案等。
int nand_scan_ident(struct nand_chip *chip, int max_chips)
{
    // 发送读取 ID 命令
    // 获取 NAND Flash ID 信息
    // 根据 ID 设置相关参数
    // 例:设置页大小、块大小等
    ...
}

nand_scan_tail

nand_scan_tail 函数负责完成 NAND 驱动的后续配置工作,它会在 nand_scan_ident 之后调用。主要任务包括:

  • 初始化 NAND 控制器: 为了支持后续的读写操作,nand_scan_tail 会完成一些硬件初始化,如配置 ECC、设置读写操作等。
  • 坏块管理初始化:nand_scan_tail 中,坏块管理的相关数据结构会被初始化,以便在后续的操作中正确处理坏块。
  • 调用 nand_chip 的初始化函数: 通过该函数进一步配置硬件接口、内存映射等。

具体实现流程如下:

  1. 初始化硬件控制器。
  2. 配置坏块管理。
  3. 设置相关的驱动参数,如 I/O 操作函数。
  4. 完成设备的注册和准备工作。
int nand_scan_tail(struct nand_chip *chip)
{
    // 配置坏块管理
    // 初始化硬件控制器
    // 设置 I/O 操作函数
    // 完成设备注册
    ...
}

3.2、NAND 驱动模型的通用抽象接口

Linux 中的 NAND 驱动通过通用抽象接口(如 MTD 接口)与硬件和上层应用程序进行交互。通用抽象接口提供了统一的操作模型,便于多种不同的 NAND 控制器进行统一管理。

通用抽象接口与 nand_chip->ecc 接口的关联

nand_chip 中,ecc 接口用于定义 NAND 驱动的错误检测和纠正算法。ECC 是 NAND Flash 存储设备中至关重要的功能,因为 NAND Flash 存储的每个单元在多次写入后容易发生错误,ECC 用于检测并修复这些错误。

  • nand_chip->ecc 通常包含了几个字段,如 modestrengthsteps,这些字段指定了使用哪种 ECC 算法及其强度(如 BCH 或 RS 编码)。
  • nand_chip->ecc 接口提供了相应的操作函数,用于执行 ECC 操作,如 nand_enable_eccnand_correct_data 等。

nand_chip->ecc 与通用抽象接口的关联体现在如下几个方面:

  • 操作抽象: 通用抽象接口通过 nand_chip->ecc 提供对 ECC 的调用,使得上层应用或中间层可以通过统一的接口进行错误检测和纠正,而不需要关心底层具体的硬件实现。
  • 统一管理: Linux 的 NAND 驱动通过 ecc 接口在 NAND 驱动和硬件控制器之间建立了一个通用的 ECC 管理框架,这样不同的 NAND 控制器可以通过同一套接口进行管理。

通用抽象接口与 nand_chip 接口的关联

nand_chip 接口定义了与 NAND 控制器硬件直接交互的操作,例如读取、写入、擦除等。这些操作由不同的硬件控制器驱动程序提供具体实现,而 Linux 内核通过抽象层将这些操作统一为通用接口。

  • nand_chip 接口 定义了 NAND 操作的核心功能,包括:

    • read_byte(): 从 NAND Flash 读取一个字节。
    • write_byte(): 向 NAND Flash 写入一个字节。
    • erase_block(): 擦除 NAND Flash 中的一个块。
    • ecc(): 处理 ECC 相关操作。

    这些操作通常会通过 MTD 子系统 提供的通用抽象接口来调用。

  • 通用抽象接口 通常由 MTD 子系统提供,通过 nand_chip 实现的函数指针来调用。例如,nand_read_page()nand_write_page() 等函数将调用 nand_chip 中定义的硬件操作函数来完成实际的读写。

3.3、小结

  • NAND 驱动注册流程:

    1. nand_scan_ident 用于扫描并识别 NAND 设备,获取设备信息并设置 nand_chip 的参数。
    2. nand_scan_tail 负责后续的硬件初始化、坏块管理和驱动配置等任务。
  • 通用抽象接口与 nand_chip 关联:

    • nand_chip->ecc 接口为 NAND Flash 提供了 ECC 功能的抽象,使得错误检测和纠正操作可以通过统一接口进行管理。
    • nand_chip 接口提供了与 NAND 控制器硬件直接交互的操作,通用抽象接口通过这些操作函数来管理不同类型的 NAND 控制器和设备。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值