Linux Mtd子系统5(基于Linux6.6)---nand驱动注册介绍
一、Nand_controller驱动的注册流程及功能说明
前面已经介绍了mtd驱动模型、nand驱动模型以及mtd与nand的关联、mtd与vfs之间的关联。主要介绍nand_chip(nand controller对应的逻辑抽象结构体)的注册接口。而针对nand_chip而言,其注册接口所实现的功能主要包括哪些呢?借助上一篇文章的图来进行说明。
- 建立mtd_info(该mtd_info为该nandflash芯片对应的master mtd_info)中访问nandflash相关的函数指针,主要用于指向nand驱动模型中通用接口(即nand_write、nand_write、nand_read_oob等接口);
- 实现nand_chip中ecc控制相关成员,即实现nand_chip->ecc变量,即实现(struct nand_ecc_ctrl类型的结构体变量,包括ecc计算接口、ecc纠错接口、读取一页接口、写一页数据接口、ecc字节数、单次进行ecc计算的数据字节数等等信息,该接口可由具体的nand_controller驱动自己赋值,无须nand驱动模型的注册函数实现,若具体的nand_controller驱动未设置,则由nand驱动模型的注册函数使用通用的接口(soft ecc接口));
- 实现nand_chip中通过nand_controller与nandflash进行命令通信的接口cmd_ctrl(该接口因不同nand_controller而不同,因此针对不同的nand_controller,需要实现对应的cmd_ctrl);
- 实现nand_chip中与nand flash进行通信的命令接口cmdfunc,该接口主要调用cmd_ctrl,实现将相关操作命令下发给nandflash(如读取nandflash id、读写数据、读写oob数据等命令);
- 实现nand_chip中的read_byte、read_word、read_buf、write_buf、block_bad等接口,这些接口实现读写flash的功能,同时read_byte、read_word、read_buf、write_buf接口也主要是由上述2中读写一页数据的接口调用;
- 实现坏块表的建立操作;
- 获取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中成员变量的设置,主要实现的功能如下:
- 调用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等等接口);
- 调用nand_get_flash_type接口,获取nandflash相关的参数(若page size、block size、oob size、flash size等信息),该接口主要通过如下操作实现参数的获取:
- 通过读取flash的设备id信息,并从全局变量nand_flash_ids中查找是否存在该设备id对应的定义参数,若存在,则获取page size、block size、oob size、flash size等等信息;若不存在,则进入步骤b;
- 通过调用nand_flash_detect_onfi接口,判断flash是否支持onfi规范,若支持则读取onfi相关的参数,从而获取page size、block size、oob size、flash size等等信息;若未获取到,则进入步骤c;
- 则根据设备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;
}
该接口实现的功能如下:
- 若nand_chip->ecc.layout 未设置,则根据ecc类型,设置默认的oob信息;
- 依据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算法,默认接口也有所不同);
- 设置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等接口;
- 若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实现的流程如下:
- 调用nand_get_device接口获取nand_chip的执行权(若当前nand_chip不在ready状态(即nand_chip再执行其他的读、写或者擦除等操作),则将该进程加入到等待队列中,并把该进程调度出去);
- 获取nand_chip的执行权后,则调用nand_do_write_ops执行写操作;
- nand_do_write_ops接口中判断flash处于写保护,则返回失败;
- 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_ident
和 nand_scan_tail
是该过程中的两个关键接口。
nand_scan_ident
nand_scan_ident
函数的作用是扫描并识别连接到 NAND 控制器的设备。它会完成以下任务:
- 设备识别: 通过发送读取命令(通常是读取 ID 命令)来获取 NAND Flash 的 ID 信息,包括 NAND Flash 的制造商、型号、尺寸、页大小、块大小等。
- 参数设置: 根据识别出的 ID 信息,
nand_scan_ident
会设置nand_chip
结构中的参数(如页大小、块大小等),以便后续的操作。
具体实现流程如下:
- 向 NAND Flash 发送命令以读取 ID。
- 解析 NAND Flash 返回的 ID 数据。
- 根据 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 的初始化函数: 通过该函数进一步配置硬件接口、内存映射等。
具体实现流程如下:
- 初始化硬件控制器。
- 配置坏块管理。
- 设置相关的驱动参数,如 I/O 操作函数。
- 完成设备的注册和准备工作。
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
通常包含了几个字段,如mode
、strength
和steps
,这些字段指定了使用哪种 ECC 算法及其强度(如 BCH 或 RS 编码)。nand_chip->ecc
接口提供了相应的操作函数,用于执行 ECC 操作,如nand_enable_ecc
和nand_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 驱动注册流程:
nand_scan_ident
用于扫描并识别 NAND 设备,获取设备信息并设置nand_chip
的参数。nand_scan_tail
负责后续的硬件初始化、坏块管理和驱动配置等任务。
-
通用抽象接口与
nand_chip
关联:nand_chip->ecc
接口为 NAND Flash 提供了 ECC 功能的抽象,使得错误检测和纠正操作可以通过统一接口进行管理。nand_chip
接口提供了与 NAND 控制器硬件直接交互的操作,通用抽象接口通过这些操作函数来管理不同类型的 NAND 控制器和设备。