MTD NAND 驱动编程接口

作者:

Thomas Gleixner

简介

通用 NAND 驱动程序支持几乎所有基于 NAND 和 AG-AND 的芯片,并将它们连接到 Linux 内核的内存技术设备 (MTD) 子系统。

本文档提供给想要实现适用于 NAND 设备的板驱动程序或文件系统驱动程序的开发人员。

已知错误和假设

无。

文档提示

函数和结构文档是自动生成的。每个函数和结构成员都有一个简短的描述,并用 [XXX] 标识符标记。以下章节解释这些标识符的含义。

函数标识符 [XXX]

函数在简短的注释中用 [XXX] 标识符标记。标识符解释函数的使用和范围。使用以下标识符

  • [MTD 接口]

    这些函数提供 MTD 内核 API 的接口。它们不可替换,并提供完全独立于硬件的功能。

  • [NAND 接口]

    这些函数被导出并提供 NAND 内核 API 的接口。

  • [通用]

    通用函数不可替换,并提供完全独立于硬件的功能。

  • [默认]

    默认函数提供适用于大多数实现的硬件相关功能。如有必要,板驱动程序可以替换这些函数。这些函数通过 NAND 芯片描述结构中的指针调用。板驱动程序可以在调用 nand_scan() 之前设置应由板相关函数替换的函数。如果 nand_scan() 入口处的函数指针为 NULL,则该指针将设置为适用于检测到的芯片类型的默认函数。

结构成员标识符 [XXX]

结构成员在注释中用 [XXX] 标识符标记。标识符解释成员的使用和范围。使用以下标识符

  • [内部]

    这些成员仅供 NAND 驱动程序内部使用,不得修改。大多数这些值都是从芯片几何信息计算得出的,这些信息在 nand_scan() 期间进行评估。

  • [可替换]

    可替换成员保存可由板驱动程序提供的硬件相关函数。板驱动程序可以在调用 nand_scan() 之前设置应由板相关函数替换的函数。如果 nand_scan() 入口处的函数指针为 NULL,则该指针将设置为适用于检测到的芯片类型的默认函数。

  • [板特定]

    板特定成员保存必须由板驱动程序提供的硬件相关信息。板驱动程序必须在调用 nand_scan() 之前设置函数指针和数据字段。

  • [可选]

    可选成员可以保存与板驱动程序相关的信息。通用 NAND 驱动程序代码不使用此信息。

基本板驱动程序

对于大多数板来说,仅提供基本函数并填写 NAND 芯片描述结构中一些真正依赖于板的成员就足够了。

基本定义

至少您必须提供一个 nand_chip 结构和一个用于存储 ioremap 芯片地址的存储空间。您可以使用 kmalloc 分配 nand_chip 结构,也可以静态分配它。NAND 芯片结构嵌入了一个 mtd 结构,该结构将注册到 MTD 子系统。您可以使用 nand_to_mtd() 帮助程序从 nand_chip 指针中提取指向 mtd 结构的指针。

基于 Kmalloc 的示例

static struct mtd_info *board_mtd;
static void __iomem *baseaddr;

静态示例

static struct nand_chip board_chip;
static void __iomem *baseaddr;

分区定义

如果要将设备划分为分区,则定义适合您板的分区方案。

#define NUM_PARTITIONS 2
static struct mtd_partition partition_info[] = {
    { .name = "Flash partition 1",
      .offset =  0,
      .size =    8 * 1024 * 1024 },
    { .name = "Flash partition 2",
      .offset =  MTDPART_OFS_NEXT,
      .size =    MTDPART_SIZ_FULL },
};

硬件控制函数

硬件控制函数提供对 NAND 芯片控制引脚的访问。访问可以通过 GPIO 引脚或地址线完成。如果使用地址线,请确保满足时序要求。

基于 GPIO 的示例

static void board_hwcontrol(struct mtd_info *mtd, int cmd)
{
    switch(cmd){
        case NAND_CTL_SETCLE: /* Set CLE pin high */ break;
        case NAND_CTL_CLRCLE: /* Set CLE pin low */ break;
        case NAND_CTL_SETALE: /* Set ALE pin high */ break;
        case NAND_CTL_CLRALE: /* Set ALE pin low */ break;
        case NAND_CTL_SETNCE: /* Set nCE pin low */ break;
        case NAND_CTL_CLRNCE: /* Set nCE pin high */ break;
    }
}

基于地址线的示例。 假设 nCE 引脚由芯片选择解码器驱动。

static void board_hwcontrol(struct mtd_info *mtd, int cmd)
{
    struct nand_chip *this = mtd_to_nand(mtd);
    switch(cmd){
        case NAND_CTL_SETCLE: this->legacy.IO_ADDR_W |= CLE_ADRR_BIT;  break;
        case NAND_CTL_CLRCLE: this->legacy.IO_ADDR_W &= ~CLE_ADRR_BIT; break;
        case NAND_CTL_SETALE: this->legacy.IO_ADDR_W |= ALE_ADRR_BIT;  break;
        case NAND_CTL_CLRALE: this->legacy.IO_ADDR_W &= ~ALE_ADRR_BIT; break;
    }
}

设备就绪函数

如果硬件接口将 NAND 芯片的就绪忙引脚连接到 GPIO 或其他可访问的 I/O 引脚,则此函数用于读回引脚的状态。该函数没有参数,如果设备忙(R/B 引脚为低电平),则应返回 0,如果设备就绪(R/B 引脚为高电平),则应返回 1。如果硬件接口无法访问就绪忙引脚,则不能定义该函数,并且函数指针 this->legacy.dev_ready 设置为 NULL。

初始化函数

初始化函数分配内存并设置所有特定于板的参数和函数指针。设置好所有内容后,调用 nand_scan()。此函数尝试检测和识别芯片。如果找到芯片,则会相应地初始化所有内部数据字段。必须先将结构清零,然后再填写有关设备的必要信息。

static int __init board_init (void)
{
    struct nand_chip *this;
    int err = 0;

    /* Allocate memory for MTD device structure and private data */
    this = kzalloc(sizeof(struct nand_chip), GFP_KERNEL);
    if (!this) {
        printk ("Unable to allocate NAND MTD device structure.\n");
        err = -ENOMEM;
        goto out;
    }

    board_mtd = nand_to_mtd(this);

    /* map physical address */
    baseaddr = ioremap(CHIP_PHYSICAL_ADDRESS, 1024);
    if (!baseaddr) {
        printk("Ioremap to access NAND chip failed\n");
        err = -EIO;
        goto out_mtd;
    }

    /* Set address of NAND IO lines */
    this->legacy.IO_ADDR_R = baseaddr;
    this->legacy.IO_ADDR_W = baseaddr;
    /* Reference hardware control function */
    this->hwcontrol = board_hwcontrol;
    /* Set command delay time, see datasheet for correct value */
    this->legacy.chip_delay = CHIP_DEPENDEND_COMMAND_DELAY;
    /* Assign the device ready function, if available */
    this->legacy.dev_ready = board_dev_ready;
    this->eccmode = NAND_ECC_SOFT;

    /* Scan to find existence of the device */
    if (nand_scan (this, 1)) {
        err = -ENXIO;
        goto out_ior;
    }

    add_mtd_partitions(board_mtd, partition_info, NUM_PARTITIONS);
    goto out;

out_ior:
    iounmap(baseaddr);
out_mtd:
    kfree (this);
out:
    return err;
}
module_init(board_init);

退出函数

仅当驱动程序编译为模块时,才需要退出函数。它释放芯片驱动程序拥有的所有资源,并在 MTD 层中取消注册分区。

#ifdef MODULE
static void __exit board_cleanup (void)
{
    /* Unregister device */
    WARN_ON(mtd_device_unregister(board_mtd));
    /* Release resources */
    nand_cleanup(mtd_to_nand(board_mtd));

    /* unmap physical address */
    iounmap(baseaddr);

    /* Free the MTD device structure */
    kfree (mtd_to_nand(board_mtd));
}
module_exit(board_cleanup);
#endif

高级板驱动程序函数

本章介绍 NAND 驱动程序的高级功能。有关板驱动程序可以覆盖的函数列表,请参阅 nand_chip 结构的文档。

多芯片控制

NAND 驱动程序可以控制芯片阵列。因此,板级驱动程序必须提供自己的 `select_chip` 函数。此函数必须(取消)选择请求的芯片。在调用 `nand_scan()` 之前,必须设置 `nand_chip` 结构中的函数指针。`nand_scan()` 的 `maxchip` 参数定义了要扫描的最大芯片数量。请确保 `select_chip` 函数可以处理请求的芯片数量。

NAND 驱动程序将这些芯片连接成一个虚拟芯片,并将此虚拟芯片提供给 MTD 层。

注意:该驱动程序只能处理大小相同的线性芯片阵列。不支持扩展总线宽度的并行阵列。

基于 GPIO 的示例

static void board_select_chip (struct mtd_info *mtd, int chip)
{
    /* Deselect all chips, set all nCE pins high */
    GPIO(BOARD_NAND_NCE) |= 0xff;
    if (chip >= 0)
        GPIO(BOARD_NAND_NCE) &= ~ (1 << chip);
}

基于地址线的示例。 假设 nCE 引脚连接到地址解码器。

static void board_select_chip (struct mtd_info *mtd, int chip)
{
    struct nand_chip *this = mtd_to_nand(mtd);

    /* Deselect all chips */
    this->legacy.IO_ADDR_R &= ~BOARD_NAND_ADDR_MASK;
    this->legacy.IO_ADDR_W &= ~BOARD_NAND_ADDR_MASK;
    switch (chip) {
    case 0:
        this->legacy.IO_ADDR_R |= BOARD_NAND_ADDR_CHIP0;
        this->legacy.IO_ADDR_W |= BOARD_NAND_ADDR_CHIP0;
        break;
    ....
    case n:
        this->legacy.IO_ADDR_R |= BOARD_NAND_ADDR_CHIPn;
        this->legacy.IO_ADDR_W |= BOARD_NAND_ADDR_CHIPn;
        break;
    }
}

硬件 ECC 支持

函数和常量

NAND 驱动程序支持三种不同类型的硬件 ECC。

  • NAND_ECC_HW3_256

    硬件 ECC 生成器,每 256 字节提供 3 字节 ECC。

  • NAND_ECC_HW3_512

    硬件 ECC 生成器,每 512 字节提供 3 字节 ECC。

  • NAND_ECC_HW6_512

    硬件 ECC 生成器,每 512 字节提供 6 字节 ECC。

  • NAND_ECC_HW8_512

    硬件 ECC 生成器,每 512 字节提供 8 字节 ECC。

如果您的硬件生成器具有不同的功能,请将其添加到 `nand_base.c` 中的适当位置。

板级驱动程序必须提供以下函数:

  • `enable_hwecc`

    此函数在读取/写入芯片之前调用。在此函数中重置或初始化硬件生成器。调用该函数时会带有一个参数,该参数使您能够区分读取和写入操作。

  • `calculate_ecc`

    此函数在从芯片读取/写入后调用。将 ECC 从硬件传输到缓冲区。如果设置了 `NAND_HWECC_SYNDROME` 选项,则该函数仅在写入时调用。请参见下文。

  • `correct_data`

    如果出现 ECC 错误,则会调用此函数进行错误检测和纠正。如果错误可以纠正,则返回 1 或 2。如果错误无法纠正,则返回 -1。如果您的硬件生成器与 `nand_ecc` 软件生成器的默认算法匹配,则使用 `nand_ecc` 提供的纠正函数,而不是实现重复的代码。

带综合征计算的硬件 ECC

许多硬件 ECC 实现都提供 Reed-Solomon 代码,并在读取时计算错误综合征。在调用通用 Reed-Solomon 库中的错误纠正代码之前,必须将综合征转换为标准 Reed-Solomon 综合征。

ECC 字节必须紧接在数据字节之后放置,以便综合征生成器能够工作。这与软件 ECC 使用的通常布局相反。不再可能分离数据和带外区域。NAND 驱动程序代码处理此布局,并且 oob 区域中剩余的空闲字节由自动放置代码管理。在这种情况下,提供匹配的 oob 布局。有关实现参考,请参见 `rts_from4.c` 和 `diskonchip.c`。在这些情况下,我们还必须在 FLASH 上使用坏块表,因为 ECC 布局会干扰坏块标记位置。有关详细信息,请参见坏块表支持。

坏块表支持

大多数 NAND 芯片在备用区域的定义位置标记坏块。在任何情况下都不得擦除这些块,因为坏块信息将会丢失。可以通过读取块中第一页的备用区域来每次访问块时检查坏块标记。这很耗时,因此使用坏块表。

NAND 驱动程序支持各种类型的坏块表。

  • 每个设备

    坏块表包含设备的所有坏块信息,该设备可以由多个芯片组成。

  • 每个芯片

    每个芯片使用一个坏块表,其中包含此特定芯片的坏块信息。

  • 固定偏移量

    坏块表位于芯片(设备)中的固定偏移量处。这适用于各种 DiskOnChip 设备。

  • 自动放置

    坏块表自动放置并在芯片(设备)的末尾或开头检测到。

  • 镜像表

    坏块表在芯片(设备)上镜像,以便在不丢失数据的情况下更新坏块表。

`nand_scan()` 调用函数 `nand_default_bbt()`。`nand_default_bbt()` 根据 `nand_scan()` 检索的芯片信息选择适当的默认坏块表描述符。

标准策略是扫描设备以查找坏块,并构建一个基于 RAM 的坏块表,该表允许比始终在闪存芯片本身上检查坏块信息更快的访问速度。

基于闪存的表

可能需要或有必要将坏块表保存在 FLASH 中。对于 AG-AND 芯片,这是强制性的,因为它们没有出厂标记的坏块。它们具有出厂标记的好块。当擦除该块以重复使用时,会擦除标记模式。因此,如果在将模式写回芯片之前断电,则该块将丢失并添加到坏块中。因此,当第一次检测到芯片时,我们会扫描芯片以查找好块,并在擦除任何块之前将此信息存储在坏块表中。

存储表的块通过在内存坏块表中将其标记为坏块来防止意外访问。坏块表管理函数允许规避此保护。

激活基于 FLASH 的坏块表支持的最简单方法是在调用 `nand_scan()` 之前,在 nand 芯片结构的 `bbt_option` 字段中设置 `NAND_BBT_USE_FLASH` 选项。对于 AG-AND 芯片,默认情况下会这样做。这会激活 NAND 驱动程序的默认基于 FLASH 的坏块表功能。默认的坏块表选项为:

  • 每个芯片存储坏块表

  • 每个块使用 2 位

  • 自动放置在芯片末尾

  • 使用带有版本号的镜像表

  • 在芯片末尾保留 4 个块

用户定义的表

用户定义的表是通过填写 `nand_bbt_descr` 结构并在调用 `nand_scan()` 之前将指针存储在 `nand_chip` 结构成员 `bbt_td` 中来创建的。如果需要镜像表,则必须创建第二个结构,并且必须将指向该结构的指针存储在 `nand_chip` 结构内的 `bbt_md` 中。如果将 `bbt_md` 成员设置为 NULL,则仅使用主表,并且不会扫描镜像表。

`nand_bbt_descr` 结构中最重要的是 `options` 字段。这些选项定义了表的大部分属性。使用 `rawnand.h` 中的预定义常量来定义选项。

  • 每个块的位数

    支持的位数是 1、2、4、8。

  • 每个芯片的表

    设置常量 `NAND_BBT_PERCHIP` 选择为芯片阵列中的每个芯片管理坏块表。如果未设置此选项,则使用每个设备的坏块表。

  • 表位置是绝对的

    使用选项常量 `NAND_BBT_ABSPAGE` 并在 `pages` 字段中定义坏块表开始的绝对页码。如果您选择了每个芯片的坏块表,并且您有一个多芯片阵列,则必须为芯片阵列中的每个芯片提供起始页。注意:不会执行表识别模式的扫描,因此可以使字段 `pattern`、`veroffs`、`offs` 和 `len` 保持未初始化状态。

  • 表位置是自动检测到的

    该表可以位于芯片(设备)的第一个或最后一个好块中。设置 `NAND_BBT_LASTBLOCK` 将坏块表放置在芯片(设备)的末尾。坏块表通过存储在块中第一页的备用区域中的模式进行标记和识别,该块保存坏块表。将指向模式的指针存储在 `pattern` 字段中。此外,模式的长度必须存储在 `len` 中,并且备用区域中的偏移量必须在 `nand_bbt_descr` 结构的 `offs` 成员中给出。对于镜像坏块表,不同的模式是强制性的。

  • 表创建

    如果在扫描期间找不到表,请设置 `NAND_BBT_CREATE` 选项以启用表创建。通常,这仅在新发现芯片时执行一次。

  • 表写入支持

    设置 `NAND_BBT_WRITE` 选项以启用表写入支持。这允许在由于磨损而必须将块标记为坏块时更新坏块表。MTD 接口函数 `block_markbad` 正在调用坏块表的更新函数。如果启用了写入支持,则会在 FLASH 上更新表。

    注意:写入支持应仅为带有版本控制的镜像表启用。

  • 表版本控制

    设置 `NAND_BBT_VERSION` 选项以启用表版本控制。强烈建议对具有写入支持的镜像表启用此功能。它可以确保将丢失坏块表信息的风险降低到丢失应标记为坏块的一个磨损块的信息。版本存储在设备备用区域中的 4 个连续字节中。版本号的位置由坏块表描述符中的成员 `veroffs` 定义。

  • 在写入时保存块内容

    如果保存坏块表的块包含其他有用的信息,请设置选项 `NAND_BBT_SAVECONTENT`。当写入坏块表时,将读取整个块,更新坏块表,擦除该块,然后写回所有内容。如果未设置此选项,则仅写入坏块表,并且忽略和擦除该块中的其他所有内容。

  • 保留块的数量

    为了自动布局,必须为坏块表存储保留一些块。保留的块数在坏块表描述结构体的 maxblocks 成员中定义。为镜像表保留 4 个块应该是一个合理的数字。这也限制了扫描坏块表标识模式的块数。

备用区域(自动)布局

NAND 驱动程序实现了在备用区域中放置文件系统数据的不同可能性:

  • 由文件系统驱动程序定义的布局

  • 自动布局

默认布局函数是自动布局。NAND 驱动程序为各种芯片类型内置了默认布局方案。如果由于硬件 ECC 功能导致默认布局不适合,则板级驱动程序可以提供自己的布局方案。

文件系统驱动程序可以提供自己的布局方案,该方案将代替默认布局方案。

布局方案由 nand_oobinfo 结构体定义

struct nand_oobinfo {
    int useecc;
    int eccbytes;
    int eccpos[24];
    int oobfree[8][2];
};
  • useecc

    useecc 成员控制 ECC 和布局函数。头文件 include/mtd/mtd-abi.h 包含用于选择 ECC 和布局的常量。MTD_NANDECC_OFF 完全关闭 ECC。不推荐这样做,仅用于测试和诊断。MTD_NANDECC_PLACE 选择调用者定义的布局,MTD_NANDECC_AUTOPLACE 选择自动布局。

  • eccbytes

    eccbytes 成员定义每个页面的 ECC 字节数。

  • eccpos

    eccpos 数组保存备用区域中放置 ECC 代码的字节偏移量。

  • oobfree

    oobfree 数组定义备用区域中可用于自动布局的区域。信息以 {offset, size} 格式给出。offset 定义可用区域的起始位置,size 定义长度(以字节为单位)。可以定义多个区域。列表以 {0, 0} 条目结束。

由文件系统驱动程序定义的布局

调用函数提供一个指向 nand_oobinfo 结构的指针,该结构定义了 ECC 布局。对于写入操作,调用者必须提供一个备用区域缓冲区以及数据缓冲区。备用区域缓冲区大小为(页面数)*(备用区域大小)。对于读取操作,缓冲区大小为(页面数)*((备用区域大小)+(每个页面的 ECC 步数)* sizeof(int))。驱动程序将每个元组的 ECC 检查结果存储在备用缓冲区中。存储顺序为:

<spare data page 0><ecc result 0>...<ecc result n>

...

<spare data page n><ecc result 0>...<ecc result n>

这是 YAFFS1 使用的旧模式。

如果备用区域缓冲区为 NULL,则仅根据 nand_oobinfo 结构中给定的方案执行 ECC 布局。

自动布局

自动布局使用内置默认值将 ECC 字节放置在备用区域中。如果必须将文件系统数据存储/读取到备用区域,则调用函数必须提供一个缓冲区。每个页面的缓冲区大小由 nand_oobinfo 结构中的 oobfree 数组确定。

如果备用区域缓冲区为 NULL,则仅根据默认内置方案执行 ECC 布局。

备用区域自动布局默认方案

256 字节页面大小

偏移量

内容

注释

0x00

ECC 字节 0

错误校正码字节 0

0x01

ECC 字节 1

错误校正码字节 1

0x02

ECC 字节 2

错误校正码字节 2

0x03

自动放置 0

0x04

自动放置 1

0x05

坏块标记

如果此字节中的任何位为零,则此块为坏块。这仅适用于块中的第一个页面。在其余页面中,此字节保留。

0x06

自动放置 2

0x07

自动放置 3

512 字节页面大小

偏移量

内容

注释

0x00

ECC 字节 0

此页面中较低的 256 字节数据的错误校正码字节 0

0x01

ECC 字节 1

此页面中较低的 256 字节数据的错误校正码字节 1

0x02

ECC 字节 2

此页面中较低的 256 字节数据的错误校正码字节 2

0x03

ECC 字节 3

此页面中较高的 256 字节数据的错误校正码字节 0

0x04

保留

保留

0x05

坏块标记

如果此字节中的任何位为零,则此块为坏块。这仅适用于块中的第一个页面。在其余页面中,此字节保留。

0x06

ECC 字节 4

此页面中较高的 256 字节数据的错误校正码字节 1

0x07

ECC 字节 5

此页面中较高的 256 字节数据的错误校正码字节 2

0x08 - 0x0F

自动放置 0 - 7

2048 字节页面大小

偏移量

内容

注释

0x00

坏块标记

如果此字节中的任何位为零,则此块为坏块。这仅适用于块中的第一个页面。在其余页面中,此字节保留。

0x01

保留

保留

0x02-0x27

自动放置 0 - 37

0x28

ECC 字节 0

此页面中第一个 256 字节数据的错误校正码字节 0

0x29

ECC 字节 1

此页面中第一个 256 字节数据的错误校正码字节 1

0x2A

ECC 字节 2

此页面中第一个 256 字节数据的错误校正码字节 2

0x2B

ECC 字节 3

此页面中第二个 256 字节数据的错误校正码字节 0

0x2C

ECC 字节 4

此页面中第二个 256 字节数据的错误校正码字节 1

0x2D

ECC 字节 5

此页面中第二个 256 字节数据的错误校正码字节 2

0x2E

ECC 字节 6

此页面中第三个 256 字节数据的错误校正码字节 0

0x2F

ECC 字节 7

此页面中第三个 256 字节数据的错误校正码字节 1

0x30

ECC 字节 8

此页面中第三个 256 字节数据的错误校正码字节 2

0x31

ECC 字节 9

此页面中第四个 256 字节数据的错误校正码字节 0

0x32

ECC 字节 10

此页面中第四个 256 字节数据的错误校正码字节 1

0x33

ECC 字节 11

此页面中第四个 256 字节数据的错误校正码字节 2

0x34

ECC 字节 12

此页面中第五个 256 字节数据的错误校正码字节 0

0x35

ECC 字节 13

此页面中第五个 256 字节数据的错误校正码字节 1

0x36

ECC 字节 14

此页面中第五个 256 字节数据的错误校正码字节 2

0x37

ECC 字节 15

此页面中第六个 256 字节数据的错误校正码字节 0

0x38

ECC 字节 16

此页面中第六个 256 字节数据的错误校正码字节 1

0x39

ECC 字节 17

此页面中第六个 256 字节数据的错误校正码字节 2

0x3A

ECC 字节 18

此页面中第七个 256 字节数据的错误校正码字节 0

0x3B

ECC 字节 19

此页面中第七个 256 字节数据的错误校正码字节 1

0x3C

ECC 字节 20

此页面中第七个 256 字节数据的错误校正码字节 2

0x3D

ECC 字节 21

此页面中第八个 256 字节数据的错误校正码字节 0

0x3E

ECC 字节 22

此页面中第八个 256 字节数据的错误校正码字节 1

0x3F

ECC 字节 23

此页面中第八个 256 字节数据的错误校正码字节 2

文件系统支持

NAND 驱动程序通过 MTD 接口为文件系统提供所有必要的功能。

文件系统必须了解 NAND 的特性和限制。NAND 闪存的一个主要限制是,您不能像您希望的那样频繁地写入页面。在再次擦除之前,对页面的连续写入限制为 1-3 次,具体取决于制造商的规范。这同样适用于备用区域。

因此,NAND 感知文件系统必须以页面大小的块写入,或保持一个写入缓冲区以收集较小的写入,直到它们总和达到页面大小。可用的 NAND 感知文件系统:JFFS2、YAFFS。

用于存储文件系统数据的备用区域的使用由备用区域布局功能控制,该功能在前面的章节中进行了描述。

工具

MTD 项目提供了一些有用的工具来处理 NAND 闪存。

  • flasherase, flasheraseall:擦除和格式化 FLASH 分区

  • nandwrite:将文件系统映像写入 NAND 闪存

  • nanddump:转储 NAND 闪存分区的内容

这些工具了解 NAND 的限制。请使用这些工具,而不是抱怨由非 NAND 感知访问方法引起的错误。

常量

本章介绍可能与驱动程序开发人员相关的常量。

芯片选项常量

芯片 ID 表的常量

这些常量在 rawnand.h 中定义。它们被 OR 在一起以描述芯片功能

/* Buswitdh is 16 bit */
#define NAND_BUSWIDTH_16    0x00000002
/* Device supports partial programming without padding */
#define NAND_NO_PADDING     0x00000004
/* Chip has cache program function */
#define NAND_CACHEPRG       0x00000008
/* Chip has copy back function */
#define NAND_COPYBACK       0x00000010
/* AND Chip which has 4 banks and a confusing page / block
 * assignment. See Renesas datasheet for further information */
#define NAND_IS_AND     0x00000020
/* Chip has a array of 4 pages which can be read without
 * additional ready /busy waits */
#define NAND_4PAGE_ARRAY    0x00000040

运行时选项的常量

这些常量在 rawnand.h 中定义。它们被 OR 在一起以描述功能

/* The hw ecc generator provides a syndrome instead a ecc value on read
 * This can only work if we have the ecc bytes directly behind the
 * data bytes. Applies for DOC and AG-AND Renesas HW Reed Solomon generators */
#define NAND_HWECC_SYNDROME 0x00020000

ECC 选择常量

使用这些常量选择 ECC 算法

/* No ECC. Usage is not recommended ! */
#define NAND_ECC_NONE       0
/* Software ECC 3 byte ECC per 256 Byte data */
#define NAND_ECC_SOFT       1
/* Hardware ECC 3 byte ECC per 256 Byte data */
#define NAND_ECC_HW3_256    2
/* Hardware ECC 3 byte ECC per 512 Byte data */
#define NAND_ECC_HW3_512    3
/* Hardware ECC 6 byte ECC per 512 Byte data */
#define NAND_ECC_HW6_512    4
/* Hardware ECC 8 byte ECC per 512 Byte data */
#define NAND_ECC_HW8_512    6

结构体

本章包含 NAND 驱动程序中使用的结构体的自动生成文档,这些结构体可能与驱动程序开发人员相关。每个结构体成员都有一个简短的描述,并用 [XXX] 标识符标记。有关说明,请参阅“文档提示”一章。

struct nand_parameters

来自参数页面的 NAND 通用参数

定义:

struct nand_parameters {
    const char *model;
    bool supports_set_get_features;
    bool supports_read_cache;
    unsigned long set_feature_list[BITS_TO_LONGS(ONFI_FEATURE_NUMBER)];
    unsigned long get_feature_list[BITS_TO_LONGS(ONFI_FEATURE_NUMBER)];
    struct onfi_params *onfi;
};

成员

model

型号名称

supports_set_get_features

NAND 芯片支持设置/获取功能

supports_read_cache

NAND 芯片支持读取缓存操作

set_feature_list

可以设置的功能的位图

get_feature_list

可以获取的功能的位图

onfi

ONFI 特定参数

struct nand_id

NAND ID 结构体

定义:

struct nand_id {
    u8 data[NAND_MAX_ID_LEN];
    int len;
};

成员

data

包含 ID 字节的缓冲区。

len

ID 长度。

struct nand_ecc_step_info

ECC 引擎的 ECC 步进信息

定义:

struct nand_ecc_step_info {
    int stepsize;
    const int *strengths;
    int nstrengths;
};

成员

stepsize

每个 ECC 步进的数据字节数

strengths

支持的强度数组

nstrengths

支持的强度数量

struct nand_ecc_caps

ECC 引擎的功能

定义:

struct nand_ecc_caps {
    const struct nand_ecc_step_info *stepinfos;
    int nstepinfos;
    int (*calc_ecc_bytes)(int step_size, int strength);
};

成员

stepinfos

ECC 步进信息数组

nstepinfos

ECC 步进信息数量

calc_ecc_bytes

驱动程序用于计算每个步进 ECC 字节的回调函数

struct nand_ecc_ctrl

ECC 的控制结构

定义:

struct nand_ecc_ctrl {
    enum nand_ecc_engine_type engine_type;
    enum nand_ecc_placement placement;
    enum nand_ecc_algo algo;
    int steps;
    int size;
    int bytes;
    int total;
    int strength;
    int prepad;
    int postpad;
    unsigned int options;
    u8 *calc_buf;
    u8 *code_buf;
    void (*hwctl)(struct nand_chip *chip, int mode);
    int (*calculate)(struct nand_chip *chip, const uint8_t *dat, uint8_t *ecc_code);
    int (*correct)(struct nand_chip *chip, uint8_t *dat, uint8_t *read_ecc, uint8_t *calc_ecc);
    int (*read_page_raw)(struct nand_chip *chip, uint8_t *buf, int oob_required, int page);
    int (*write_page_raw)(struct nand_chip *chip, const uint8_t *buf, int oob_required, int page);
    int (*read_page)(struct nand_chip *chip, uint8_t *buf, int oob_required, int page);
    int (*read_subpage)(struct nand_chip *chip, uint32_t offs, uint32_t len, uint8_t *buf, int page);
    int (*write_subpage)(struct nand_chip *chip, uint32_t offset,uint32_t data_len, const uint8_t *data_buf, int oob_required, int page);
    int (*write_page)(struct nand_chip *chip, const uint8_t *buf, int oob_required, int page);
    int (*write_oob_raw)(struct nand_chip *chip, int page);
    int (*read_oob_raw)(struct nand_chip *chip, int page);
    int (*read_oob)(struct nand_chip *chip, int page);
    int (*write_oob)(struct nand_chip *chip, int page);
};

成员

engine_type

ECC 引擎类型

placement

OOB 字节放置

algo

ECC 算法

steps

每个页面的 ECC 步数

size

每个 ECC 步进的数据字节数

字节

每个步进的 ECC 字节数

total

每个页面的 ECC 字节总数

strength

每个 ECC 步进可纠正的最大位数

prepad

用于基于综合症的 ECC 生成器的填充信息

postpad

用于基于综合症的 ECC 生成器的填充信息

options

ECC 特定选项(请参阅上面定义的 NAND_ECC_XXX 标志)

calc_buf

用于计算 ECC 的缓冲区,大小为 oobsize。

code_buf

用于从闪存读取 ECC 的缓冲区,大小为 oobsize。

hwctl

控制硬件 ECC 生成器的函数。仅当硬件 ECC 可用时才必须提供

calculate

用于 ECC 计算或从 ECC 硬件读取的回调函数

correct

用于 ECC 校正的函数,与 ECC 生成器(软件/硬件)匹配。应返回一个正数,表示已校正的位翻转次数,如果位翻转次数超过 ECC 强度则返回 -EBADMSG,如果错误与校正没有直接关系则返回任何其他错误代码。如果返回 -EBADMSG,则应保持输入缓冲区不变。

read_page_raw

用于读取没有 ECC 的原始页面的函数。此函数应隐藏 ECC 控制器使用的特定布局,并且即使它们没有连续存储在 NAND 芯片上(例如,NAND_ECC_PLACEMENT_INTERLEAVED 交错带内和带外数据),也始终返回连续的带内和带外数据。

write_page_raw

用于写入没有 ECC 的原始页面的函数。此函数应隐藏 ECC 控制器使用的特定布局,并将传递的数据视为连续的带内和带外数据。ECC 控制器负责进行适当的转换以适应其特定布局(例如,NAND_ECC_PLACEMENT_INTERLEAVED 交错带内和带外数据)。

read_page

根据 ECC 生成器要求读取页面的函数;返回任何单个 ECC 步骤中校正的最大位翻转次数,-EIO 硬件错误

read_subpage

用于读取 ECC 覆盖的页面的部分的函数;返回与 read_page() 相同的值

write_subpage

用于写入 ECC 覆盖的页面的部分的函数。

write_page

根据 ECC 生成器要求写入页面的函数。

write_oob_raw

用于写入没有 ECC 的芯片 OOB 数据的函数

read_oob_raw

用于读取没有 ECC 的芯片 OOB 数据的函数

read_oob

用于读取芯片 OOB 数据的函数

write_oob

用于写入芯片 OOB 数据的函数

struct nand_sdr_timings

SDR NAND 芯片时序

定义:

struct nand_sdr_timings {
    u64 tBERS_max;
    u32 tCCS_min;
    u64 tPROG_max;
    u64 tR_max;
    u32 tALH_min;
    u32 tADL_min;
    u32 tALS_min;
    u32 tAR_min;
    u32 tCEA_max;
    u32 tCEH_min;
    u32 tCH_min;
    u32 tCHZ_max;
    u32 tCLH_min;
    u32 tCLR_min;
    u32 tCLS_min;
    u32 tCOH_min;
    u32 tCS_min;
    u32 tDH_min;
    u32 tDS_min;
    u32 tFEAT_max;
    u32 tIR_min;
    u32 tITC_max;
    u32 tRC_min;
    u32 tREA_max;
    u32 tREH_min;
    u32 tRHOH_min;
    u32 tRHW_min;
    u32 tRHZ_max;
    u32 tRLOH_min;
    u32 tRP_min;
    u32 tRR_min;
    u64 tRST_max;
    u32 tWB_max;
    u32 tWC_min;
    u32 tWH_min;
    u32 tWHR_min;
    u32 tWP_min;
    u32 tWW_min;
};

成员

tBERS_max

块擦除时间

tCCS_min

更改列设置时间

tPROG_max

页面编程时间

tR_max

页面读取时间

tALH_min

ALE 保持时间

tADL_min

ALE 到数据加载时间

tALS_min

ALE 设置时间

tAR_min

ALE 到 RE# 延迟

tCEA_max

CE# 访问时间

tCEH_min

CE# 高电平保持时间

tCH_min

CE# 保持时间

tCHZ_max

CE# 高电平到输出高阻抗

tCLH_min

CLE 保持时间

tCLR_min

CLE 到 RE# 延迟

tCLS_min

CLE 设置时间

tCOH_min

CE# 高电平到输出保持

tCS_min

CE# 设置时间

tDH_min

数据保持时间

tDS_min

数据设置时间

tFEAT_max

设置功能和获取功能的忙碌时间

tIR_min

输出高阻抗到 RE# 低电平

tITC_max

接口和定时模式更改时间

tRC_min

RE# 周期时间

tREA_max

RE# 访问时间

tREH_min

RE# 高电平保持时间

tRHOH_min

RE# 高电平到输出保持

tRHW_min

RE# 高电平到 WE# 低电平

tRHZ_max

RE# 高电平到输出高阻抗

tRLOH_min

RE# 低电平到输出保持

tRP_min

RE# 脉冲宽度

tRR_min

就绪到 RE# 低电平(仅限数据)

tRST_max

设备复位时间,从 R/B# 的下降沿到 R/B# 的上升沿测量。

tWB_max

WE# 高电平到 SR[6] 低电平

tWC_min

WE# 周期时间

tWH_min

WE# 高电平保持时间

tWHR_min

WE# 高电平到 RE# 低电平

tWP_min

WE# 脉冲宽度

tWW_min

WP# 过渡到 WE# 低电平

描述

此结构定义了 SDR NAND 芯片的时序要求。这些信息可以在每个 NAND 数据表中找到,并且时序的含义在 ONFI 规范中进行了描述:https://media-www.micron.com/-/media/client/onfi/specs/onfi_3_1_spec.pdf(第 4.15 章 时序参数)

所有这些时序均以皮秒表示。

struct nand_nvddr_timings

NV-DDR NAND 芯片时序

定义:

struct nand_nvddr_timings {
    u64 tBERS_max;
    u32 tCCS_min;
    u64 tPROG_max;
    u64 tR_max;
    u32 tAC_min;
    u32 tAC_max;
    u32 tADL_min;
    u32 tCAD_min;
    u32 tCAH_min;
    u32 tCALH_min;
    u32 tCALS_min;
    u32 tCAS_min;
    u32 tCEH_min;
    u32 tCH_min;
    u32 tCK_min;
    u32 tCS_min;
    u32 tDH_min;
    u32 tDQSCK_min;
    u32 tDQSCK_max;
    u32 tDQSD_min;
    u32 tDQSD_max;
    u32 tDQSHZ_max;
    u32 tDQSQ_max;
    u32 tDS_min;
    u32 tDSC_min;
    u32 tFEAT_max;
    u32 tITC_max;
    u32 tQHS_max;
    u32 tRHW_min;
    u32 tRR_min;
    u32 tRST_max;
    u32 tWB_max;
    u32 tWHR_min;
    u32 tWRCK_min;
    u32 tWW_min;
};

成员

tBERS_max

块擦除时间

tCCS_min

更改列设置时间

tPROG_max

页面编程时间

tR_max

页面读取时间

tAC_min

从 CLK 开始的 DQ[7:0] 的访问窗口

tAC_max

从 CLK 开始的 DQ[7:0] 的访问窗口

tADL_min

ALE 到数据加载时间

tCAD_min

命令、地址、数据延迟

tCAH_min

命令/地址 DQ 保持时间

tCALH_min

W/R_n、CLE 和 ALE 保持时间

tCALS_min

W/R_n、CLE 和 ALE 设置时间

tCAS_min

命令/地址 DQ 设置时间

tCEH_min

CE# 高电平保持时间

tCH_min

CE# 保持时间

tCK_min

平均时钟周期时间

tCS_min

CE# 设置时间

tDH_min

数据保持时间

tDQSCK_min

从 CLK 开始的 DQS 的访问窗口开始

tDQSCK_max

从 CLK 开始的 DQS 的访问窗口结束

tDQSD_min

由设备驱动的最小 W/R_n 低电平到 DQS/DQ

tDQSD_max

由设备驱动的最大 W/R_n 低电平到 DQS/DQ

tDQSHZ_max

由设备驱动的 W/R_n 高电平到 DQS/DQ 三态

tDQSQ_max

DQS-DQ 偏移,DQS 到最后一个有效的 DQ,每次访问

tDS_min

数据设置时间

tDSC_min

DQS 周期时间

tFEAT_max

设置功能和获取功能的忙碌时间

tITC_max

接口和定时模式更改时间

tQHS_max

数据保持偏移系数

tRHW_min

数据输出周期到命令、地址或数据输入周期

tRR_min

就绪到 RE# 低电平(仅限数据)

tRST_max

设备复位时间,从 R/B# 的下降沿到 R/B# 的上升沿测量。

tWB_max

WE# 高电平到 SR[6] 低电平

tWHR_min

WE# 高电平到 RE# 低电平

tWRCK_min

W/R_n 低电平到数据输出周期

tWW_min

WP# 过渡到 WE# 低电平

描述

此结构定义了 NV-DDR NAND 数据接口的时序要求。这些信息可以在每个 NAND 数据表中找到,并且时序的含义在 ONFI 规范中进行了描述:https://media-www.micron.com/-/media/client/onfi/specs/onfi_4_1_gold.pdf(第 4.18.2 章 NV-DDR)

所有这些时序均以皮秒表示。

enum nand_interface_type

NAND 接口类型

常量

NAND_SDR_IFACE

单数据速率接口

NAND_NVDDR_IFACE

双倍数据速率接口

struct nand_interface_config

NAND 接口时序

定义:

struct nand_interface_config {
    enum nand_interface_type type;
    struct nand_timings {
        unsigned int mode;
        union {
            struct nand_sdr_timings sdr;
            struct nand_nvddr_timings nvddr;
        };
    } timings;
};

成员

type

时序类型

timings

时序信息

timings.mode

规范中定义的时序模式

{unnamed_union}

匿名

timings.sdr

typeNAND_SDR_IFACE 时使用它。

timings.nvddr

typeNAND_NVDDR_IFACE 时使用它。

bool nand_interface_is_sdr(const struct nand_interface_config *conf)

获取接口类型

参数

const struct nand_interface_config *conf

数据接口

bool nand_interface_is_nvddr(const struct nand_interface_config *conf)

获取接口类型

参数

const struct nand_interface_config *conf

数据接口

const struct nand_sdr_timings *nand_get_sdr_timings(const struct nand_interface_config *conf)

从数据接口获取 SDR 时序

参数

const struct nand_interface_config *conf

数据接口

const struct nand_nvddr_timings *nand_get_nvddr_timings(const struct nand_interface_config *conf)

从数据接口获取 NV-DDR 时序

参数

const struct nand_interface_config *conf

数据接口

struct nand_op_cmd_instr

命令指令的定义

定义:

struct nand_op_cmd_instr {
    u8 opcode;
};

成员

opcode

在一个周期内发出的命令

struct nand_op_addr_instr

地址指令的定义

定义:

struct nand_op_addr_instr {
    unsigned int naddrs;
    const u8 *addrs;
};

成员

naddrs

addrs 数组的长度

addrs

包含要发出的地址周期的数组

struct nand_op_data_instr

数据指令的定义

定义:

struct nand_op_data_instr {
    unsigned int len;
    union {
        void *in;
        const void *out;
    } buf;
    bool force_8bit;
};

成员

len

要移动的数据字节数

buf

要填充的缓冲区

buf.in

从 NAND 芯片读取时要填充的缓冲区

buf.out

写入 NAND 芯片时要读取的缓冲区

force_8bit

强制 8 位访问

描述

请注意,“in”和“out”与 ONFI 规范相反,是从控制器角度来看的,因此“in”是从 NAND 芯片读取,“out”是写入 NAND 芯片。

struct nand_op_waitrdy_instr

等待就绪指令的定义

定义:

struct nand_op_waitrdy_instr {
    unsigned int timeout_ms;
};

成员

timeout_ms

等待就绪/忙引脚的最大延迟(以毫秒为单位)

enum nand_op_instr_type

所有指令类型的定义

常量

NAND_OP_CMD_INSTR

命令指令

NAND_OP_ADDR_INSTR

地址指令

NAND_OP_DATA_IN_INSTR

数据输入指令

NAND_OP_DATA_OUT_INSTR

数据输出指令

NAND_OP_WAITRDY_INSTR

等待就绪指令

struct nand_op_instr

指令对象

定义:

struct nand_op_instr {
    enum nand_op_instr_type type;
    union {
        struct nand_op_cmd_instr cmd;
        struct nand_op_addr_instr addr;
        struct nand_op_data_instr data;
        struct nand_op_waitrdy_instr waitrdy;
    } ctx;
    unsigned int delay_ns;
};

成员

type

指令类型

ctx

与指令关联的额外数据。您必须根据 **type** 使用适当的元素

ctx.cmd

如果 **type** 为 NAND_OP_CMD_INSTR,则使用此项

ctx.addr

如果 **type** 为 NAND_OP_ADDR_INSTR,则使用此项

ctx.data

如果 **type** 为 NAND_OP_DATA_IN_INSTRNAND_OP_DATA_OUT_INSTR,则使用此项

ctx.waitrdy

如果 **type** 为 NAND_OP_WAITRDY_INSTR,则使用此项

delay_ns

在总线上发出指令后,控制器应应用的延迟。大多数现代控制器都具有内部时序控制逻辑,在这种情况下,控制器驱动程序可以忽略此字段。

struct nand_subop

子操作

定义:

struct nand_subop {
    unsigned int cs;
    const struct nand_op_instr *instrs;
    unsigned int ninstrs;
    unsigned int first_instr_start_off;
    unsigned int last_instr_end_off;
};

成员

cs

为此 NAND 子操作选择的 CS 线

instrs

指令数组

ninstrs

instrs 数组的长度

first_instr_start_off

子操作的第一条指令的起始偏移量

last_instr_end_off

子操作的最后一条指令的结束偏移量(不包括在内)

描述

**first_instr_start_off** 和 **last_instr_end_off** 仅适用于数据或地址指令。

当 NAND 控制器无法按原样处理操作时,解析器会将其拆分为子操作,并将子操作传递给控制器驱动程序。

struct nand_op_parser_addr_constraints

地址指令的约束

定义:

struct nand_op_parser_addr_constraints {
    unsigned int maxcycles;
};

成员

maxcycles

控制器在单个步骤中可以发出的最大地址周期数

struct nand_op_parser_data_constraints

数据指令的约束

定义:

struct nand_op_parser_data_constraints {
    unsigned int maxlen;
};

成员

maxlen

控制器在单个步骤中可以处理的最大数据长度

struct nand_op_parser_pattern_elem

模式的一个元素

定义:

struct nand_op_parser_pattern_elem {
    enum nand_op_instr_type type;
    bool optional;
    union {
        struct nand_op_parser_addr_constraints addr;
        struct nand_op_parser_data_constraints data;
    } ctx;
};

成员

type

指令类型

optional

模式的此元素是可选的还是强制的

ctx

地址或数据约束

ctx.addr

地址约束(周期数)

ctx.data

数据约束(数据长度)

struct nand_op_parser_pattern

NAND 子操作模式描述符

定义:

struct nand_op_parser_pattern {
    const struct nand_op_parser_pattern_elem *elems;
    unsigned int nelems;
    int (*exec)(struct nand_chip *chip, const struct nand_subop *subop);
};

成员

elems

模式元素数组

nelems

elems 数组中模式元素的数量

exec

将发出子操作的函数

描述

模式是元素的列表,每个元素代表一条指令及其约束。模式本身由核心使用,以将 NAND 芯片操作与 NAND 控制器操作进行匹配。一旦找到 NAND 控制器操作模式和 NAND 芯片操作(或 NAND 操作的子集)之间的匹配项,则会调用 pattern ->exec() 钩子,以便控制器驱动程序可以在总线上发出操作。

控制器驱动程序应声明其支持的尽可能多的模式,并将此模式列表(在以下宏的帮助下创建)传递给 nand_op_parser_exec_op() 帮助程序。

struct nand_op_parser

NAND 控制器操作解析器描述符

定义:

struct nand_op_parser {
    const struct nand_op_parser_pattern *patterns;
    unsigned int npatterns;
};

成员

patterns

支持的模式数组

npatterns

patterns 数组的长度

描述

解析器描述符只是一个支持的模式数组,每次尝试执行 NAND 操作时(或尝试确定是否支持特定操作时),nand_op_parser_exec_op() 都会迭代该数组。

值得一提的是,将按照声明顺序测试模式,并将采用第一个匹配项,因此务必适当排序模式,以便将简单/低效的模式放在列表末尾。通常,这是放置单指令模式的位置。

struct nand_operation

NAND 操作描述符

定义:

struct nand_operation {
    unsigned int cs;
    bool deassert_wp;
    const struct nand_op_instr *instrs;
    unsigned int ninstrs;
};

成员

cs

为此 NAND 操作选择的 CS 线

deassert_wp

当操作需要取消置位 WP 引脚时设置为 true(擦除、编程等)

instrs

要执行的指令数组

ninstrs

instrs 数组的长度

描述

将传递给 chip->exec_op() 的实际操作结构。

struct nand_controller_ops

控制器操作

定义:

struct nand_controller_ops {
    int (*attach_chip)(struct nand_chip *chip);
    void (*detach_chip)(struct nand_chip *chip);
    int (*exec_op)(struct nand_chip *chip,const struct nand_operation *op, bool check_only);
    int (*setup_interface)(struct nand_chip *chip, int chipnr, const struct nand_interface_config *conf);
};

成员

attach_chip

此方法在 NAND 检测阶段之后,在设置了闪存 ID 和 MTD 字段(如擦除大小、页面大小和 OOB 大小)之后调用。如果 NAND 芯片或设备树提供了 ECC 要求,则可以使用。通常用于选择适当的 ECC 配置并分配关联的资源。此钩子是可选的。

detach_chip

释放 nand_controller_ops->attach_chip() 中分配/声明的所有资源。此钩子是可选的。

exec_op

执行 NAND 操作的特定于控制器的方法。此方法替换 chip->legacy.cmdfunc()、chip->legacy.{read,write}_{buf,byte,word}()、chip->legacy.dev_ready() 和 chip->legacy.waitfunc()。

setup_interface

设置数据接口和时序。如果 chipnr 设置为 NAND_DATA_IFACE_CHECK_ONLY,则表示不应应用配置,而仅应进行检查。此挂钩是可选的。

struct nand_controller

用于描述 NAND 控制器的结构

定义:

struct nand_controller {
    struct mutex lock;
    const struct nand_controller_ops *ops;
    struct {
        unsigned int data_only_read: 1;
        unsigned int cont_read: 1;
    } supported_op;
    bool controller_wp;
};

成员

lock

用于序列化对 NAND 控制器的访问的锁

ops

NAND 控制器操作。

supported_op

NAND 控制器已知支持的操作,仅在初始检查后由核心写入。

supported_op.data_only_read

控制器支持从总线上读取更多数据,而无需重新启动整个读取操作或更改列。

supported_op.cont_read

控制器支持顺序缓存读取。

controller_wp

控制器负责处理 WP 引脚。

struct nand_legacy

NAND 芯片的旧式字段/挂钩

定义:

struct nand_legacy {
    void __iomem *IO_ADDR_R;
    void __iomem *IO_ADDR_W;
    void (*select_chip)(struct nand_chip *chip, int cs);
    u8 (*read_byte)(struct nand_chip *chip);
    void (*write_byte)(struct nand_chip *chip, u8 byte);
    void (*write_buf)(struct nand_chip *chip, const u8 *buf, int len);
    void (*read_buf)(struct nand_chip *chip, u8 *buf, int len);
    void (*cmd_ctrl)(struct nand_chip *chip, int dat, unsigned int ctrl);
    void (*cmdfunc)(struct nand_chip *chip, unsigned command, int column, int page_addr);
    int (*dev_ready)(struct nand_chip *chip);
    int (*waitfunc)(struct nand_chip *chip);
    int (*block_bad)(struct nand_chip *chip, loff_t ofs);
    int (*block_markbad)(struct nand_chip *chip, loff_t ofs);
    int (*set_features)(struct nand_chip *chip, int feature_addr, u8 *subfeature_para);
    int (*get_features)(struct nand_chip *chip, int feature_addr, u8 *subfeature_para);
    int chip_delay;
    struct nand_controller dummy_controller;
};

成员

IO_ADDR_R

读取闪存设备 8 条 I/O 线的地址

IO_ADDR_W

写入闪存设备 8 条 I/O 线的地址

select_chip

选择/取消选择特定的目标/die

read_byte

从芯片读取一个字节

write_byte

在低 8 条 I/O 线上向芯片写入单个字节

write_buf

将缓冲区中的数据写入芯片

read_buf

将数据从芯片读取到缓冲区中

cmd_ctrl

用于控制 ALE/CLE/nCE 的硬件特定功能。也用于写入命令和地址

cmdfunc

用于向芯片写入命令的硬件特定功能。

dev_ready

用于访问设备就绪/忙碌线的硬件特定功能。如果设置为 NULL,则无法访问就绪/忙碌信息,并且就绪/忙碌信息从芯片状态寄存器读取。

waitfunc

用于等待就绪的硬件特定功能。

block_bad

使用 OOB 标记检查块是否损坏

block_markbad

标记一个坏块

set_features

设置 NAND 芯片特性

get_features

获取 NAND 芯片特性

chip_delay

用于将数据从阵列传输到读取寄存器 (tR) 的芯片相关延迟。

dummy_controller

用于只能控制单个芯片的驱动程序的虚拟控制器实现

描述

如果你正在看这个结构,那你已经错了。这些字段/挂钩都已弃用。

struct nand_chip_ops

NAND 芯片操作

定义:

struct nand_chip_ops {
    int (*suspend)(struct nand_chip *chip);
    void (*resume)(struct nand_chip *chip);
    int (*lock_area)(struct nand_chip *chip, loff_t ofs, uint64_t len);
    int (*unlock_area)(struct nand_chip *chip, loff_t ofs, uint64_t len);
    int (*setup_read_retry)(struct nand_chip *chip, int retry_mode);
    int (*choose_interface_config)(struct nand_chip *chip, struct nand_interface_config *iface);
};

成员

suspend

挂起操作

resume

恢复操作

lock_area

锁定操作

unlock_area

解锁操作

setup_read_retry

设置读取重试模式(主要用于 MLC NAND)

choose_interface_config

选择最佳的接口配置

struct nand_manufacturer

NAND 制造商结构

定义:

struct nand_manufacturer {
    const struct nand_manufacturer_desc *desc;
    void *priv;
};

成员

desc

制造商描述

priv

制造商驱动程序的私有信息

struct nand_secure_region

NAND 安全区域结构

定义:

struct nand_secure_region {
    u64 offset;
    u64 size;
};

成员

offset

安全区域的起始偏移量

size

安全区域的大小

struct nand_chip

NAND 私有闪存芯片数据

定义:

struct nand_chip {
    struct nand_device base;
    struct nand_id id;
    struct nand_parameters parameters;
    struct nand_manufacturer manufacturer;
    struct nand_chip_ops ops;
    struct nand_legacy legacy;
    unsigned int options;
    const struct nand_interface_config *current_interface_config;
    struct nand_interface_config *best_interface_config;
    unsigned int bbt_erase_shift;
    unsigned int bbt_options;
    unsigned int badblockpos;
    unsigned int badblockbits;
    struct nand_bbt_descr *bbt_td;
    struct nand_bbt_descr *bbt_md;
    struct nand_bbt_descr *badblock_pattern;
    u8 *bbt;
    unsigned int page_shift;
    unsigned int phys_erase_shift;
    unsigned int chip_shift;
    unsigned int pagemask;
    unsigned int subpagesize;
    u8 *data_buf;
    u8 *oob_poi;
    struct {
        unsigned int bitflips;
        int page;
    } pagecache;
    unsigned long buf_align;
    struct mutex lock;
    unsigned int suspended : 1;
    wait_queue_head_t resume_wq;
    int cur_cs;
    int read_retries;
    struct nand_secure_region *secure_regions;
    u8 nr_secure_regions;
    struct {
        bool ongoing;
        unsigned int first_page;
        unsigned int pause_page;
        unsigned int last_page;
    } cont_read;
    struct nand_controller *controller;
    struct nand_ecc_ctrl ecc;
    void *priv;
};

成员

base

继承自通用 NAND 设备

id

保存 NAND ID

parameters

以易于阅读的形式保存通用参数

manufacturer

制造商信息

ops

NAND 芯片操作

legacy

所有旧式字段/挂钩。如果你开发新的驱动程序,甚至不要尝试使用这些字段/挂钩,如果你正在修改使用这些字段/挂钩的现有驱动程序,你应该考虑重构该驱动程序并避免使用它们。

options

各种芯片选项。可以部分设置它们以通知 nand_scan 关于特殊功能。请参阅定义以了解更多说明。

current_interface_config

当前使用的 NAND 接口配置

best_interface_config

最适合 NAND 芯片和 NAND 控制器约束的最佳 NAND 接口配置。如果未设置,则必须使用默认的重置接口配置。

bbt_erase_shift

bbt 条目中的地址位数

bbt_options

坏块表特定选项。此处使用的所有选项都必须来自 bbm.h。默认情况下,这些选项将复制到相应的 nand_bbt_descr 中。

badblockpos

oob 区域中的坏块标记位置

badblockbits

良好块的坏块标记位置中的最小设置位数;即,当 badblockbits = 7 时,BBM = 11110111b 是好的

bbt_td

用于闪存查找的坏块表描述符

bbt_md

坏块表镜像描述符

badblock_pattern

用于初始坏块扫描的坏块扫描模式

bbt

坏块表指针

page_shift

页面中的地址位数(列地址位数)

phys_erase_shift

物理擦除块中的地址位数

chip_shift

一个芯片中的地址位数

pagemask

页面编号掩码 = (页面数 / 芯片数) - 1

subpagesize

保存子页面大小

data_buf

用于数据的缓冲区,大小为 (页面大小 + oobsize)

oob_poi

数据缓冲区覆盖的 OOB 区域的指针

pagecache

包含页面缓存相关字段的结构

pagecache.bitflips

缓存页面的位翻转次数

pagecache.page

当前缓存在缓存中的页面编号。-1 表示当前没有缓存页面

buf_align

平台所需的最小缓冲区对齐

lock

保护挂起字段的锁。也用于序列化对 NAND 设备的访问

suspended

当设备挂起时设置为 1,未挂起时设置为 0

resume_wq

如果 rawnand 处于挂起状态,则等待睡眠的等待队列。

cur_cs

当前选定的目标。-1 表示未选择任何目标,否则我们应该始终具有 cur_cs >= 0 && cur_cs < nanddev_ntargets()。NAND 控制器驱动程序不应修改此值,但允许读取它。

read_retries

支持的读取重试模式的数量

secure_regions

包含安全区域信息的结构

nr_secure_regions

安全区域的数量

cont_read

顺序页面读取内部

cont_read.ongoing

是否正在进行连续读取

cont_read.first_page

连续读取操作的开始

cont_read.pause_page

当前顺序缓存读取操作的结束

cont_read.last_page

连续读取操作的结束

controller

在多个独立设备之间共享的硬件控制器结构

ecc

ECC 控制器结构

priv

芯片私有数据

const struct nand_interface_config *nand_get_interface_config(struct nand_chip *chip)

检索 NAND 芯片的当前接口配置

参数

struct nand_chip *chip

NAND 芯片

struct nand_flash_dev

NAND 闪存设备 ID 结构

定义:

struct nand_flash_dev {
    char *name;
    union {
        struct {
            uint8_t mfr_id;
            uint8_t dev_id;
        };
        uint8_t id[NAND_MAX_ID_LEN];
    };
    unsigned int pagesize;
    unsigned int chipsize;
    unsigned int erasesize;
    unsigned int options;
    uint16_t id_len;
    uint16_t oobsize;
    struct {
        uint16_t strength_ds;
        uint16_t step_ds;
    } ecc;
};

成员

name

NAND 芯片的人可读名称

{unnamed_union}

匿名

{unnamed_struct}

匿名

mfr_id

完整芯片 ID 数组的制造商 ID 部分(与 id[0] 引用相同的内存地址)

dev_id

完整芯片 ID 数组的设备 ID 部分(与 id[1] 引用相同的内存地址)

id

完整设备 ID 数组

pagesize

NAND 页面的大小(以字节为单位);如果为 0,则实际页面大小(以及擦除块大小)由扩展 NAND 芯片 ID 数组确定)

chipsize

总芯片大小(以 MiB 为单位)

erasesize

擦除块大小(以字节为单位)(如果为 0,则从扩展 ID 确定)

options

存储各种芯片位选项

id_len

id 的有效长度。

oobsize

OOB 大小

ecc

来自数据手册的 ECC 可纠正性和步长信息。

ecc.strength_ds

来自数据手册的 ECC 可纠正性,与 nand_chip{} 中的 ecc_strength_ds 相同。

ecc.step_ds

ecc.strength_ds 所需的 ECC 步长,与 nand_chip{} 中的 ecc_step_ds 相同,也来自数据手册。例如,“每个 512 字节 4 位 ECC”可以使用 NAND_ECC_INFO(4, 512) 设置。

int nand_opcode_8bits(unsigned int command)

检查操作码的地址是否应该只在低 8 位上发送

参数

unsigned int command

要检查的操作码

void *nand_get_data_buf(struct nand_chip *chip)

获取内部页面缓冲区

参数

struct nand_chip *chip

NAND 芯片对象

描述

返回使缓存失效后预先分配的页面缓冲区。驱动程序不想分配自己的反弹缓冲区,并且仍然需要此类缓冲区进行特定操作(最常见的是仅读取 OOB 数据时),则应使用此函数。

请注意,永远不要在写入/write_oob 路径中调用此函数,因为核心可能已将要写出的数据放置在此缓冲区中。

返回

指向页面缓存缓冲区的指针

提供的公共函数

本章节包含导出的 NAND 内核 API 函数的自动生成文档。每个函数都有一个简短的描述,并用 [XXX] 标识符标记。有关说明,请参阅“文档提示”章节。

void nand_extract_bits(u8 *dst, unsigned int dst_off, const u8 *src, unsigned int src_off, unsigned int nbits)

将未对齐的位从一个缓冲区复制到另一个缓冲区

参数

u8 *dst

目标缓冲区

unsigned int dst_off

写入开始的位偏移量

const u8 *src

源缓冲区

unsigned int src_off

读取开始的位偏移量

unsigned int nbits

要从 src 复制到 dst 的位数

描述

将位从一个内存区域复制到另一个内存区域(允许重叠)。

void nand_select_target(struct nand_chip *chip, unsigned int cs)

选择一个 NAND 目标(又名管芯)

参数

struct nand_chip *chip

NAND 芯片对象

unsigned int cs

要选择的 CS 线。请注意,此 CS ID 始终来自芯片的 PoV,而不是控制器的 PoV

描述

选择一个 NAND 目标,以便对 chip 执行的进一步操作转到所选的 NAND 目标。

void nand_deselect_target(struct nand_chip *chip)

取消选择当前选定的目标

参数

struct nand_chip *chip

NAND 芯片对象

描述

取消选择当前选定的 NAND 目标。在取消选择目标后对 chip 执行的操作的结果是未定义的。

int nand_soft_waitrdy(struct nand_chip *chip, unsigned long timeout_ms)

轮询 STATUS 寄存器,直到 RDY 位设置为 1

参数

struct nand_chip *chip

NAND 芯片结构

unsigned long timeout_ms

超时时间,以毫秒为单位

描述

使用 ->exec_op() 轮询 STATUS 寄存器,直到 RDY 位变为 1。如果未在指定的超时时间内发生,则返回 -ETIMEDOUT。

此助手旨在当控制器无法访问 NAND R/B 引脚时使用。

请注意,从 ->exec_op() 实现中调用此助手意味着 ->exec_op() 必须是可重入的。

如果 NAND 芯片准备就绪,则返回 0,否则返回负错误。

int nand_gpio_waitrdy(struct nand_chip *chip, struct gpio_desc *gpiod, unsigned long timeout_ms)

轮询 R/B GPIO 引脚,直到准备就绪

参数

struct nand_chip *chip

NAND 芯片结构

struct gpio_desc *gpiod

R/B 引脚的 GPIO 描述符

unsigned long timeout_ms

超时时间,以毫秒为单位

描述

轮询 R/B GPIO 引脚,直到它准备就绪。如果未在指定的超时时间内发生,则返回 -ETIMEDOUT。

此助手旨在当控制器通过 GPIO 访问 NAND R/B 引脚时使用。

如果 R/B 引脚指示芯片已准备就绪,则返回 0,否则返回负错误。

int nand_read_page_op(struct nand_chip *chip, unsigned int page, unsigned int offset_in_page, void *buf, unsigned int len)

执行 READ PAGE 操作

参数

struct nand_chip *chip

NAND 芯片

unsigned int page

要读取的页

unsigned int offset_in_page

页内的偏移量

void *buf

用于存储数据的缓冲区

unsigned int len

缓冲区的长度

描述

此函数发出 READ PAGE 操作。此函数不选择/取消选择 CS 线。

成功返回 0,否则返回负错误代码。

int nand_change_read_column_op(struct nand_chip *chip, unsigned int offset_in_page, void *buf, unsigned int len, bool force_8bit)

执行 CHANGE READ COLUMN 操作

参数

struct nand_chip *chip

NAND 芯片

unsigned int offset_in_page

页内的偏移量

void *buf

用于存储数据的缓冲区

unsigned int len

缓冲区的长度

bool force_8bit

强制 8 位总线访问

描述

此函数发出 CHANGE READ COLUMN 操作。此函数不选择/取消选择 CS 线。

成功返回 0,否则返回负错误代码。

int nand_read_oob_op(struct nand_chip *chip, unsigned int page, unsigned int offset_in_oob, void *buf, unsigned int len)

执行 READ OOB 操作

参数

struct nand_chip *chip

NAND 芯片

unsigned int page

要读取的页

unsigned int offset_in_oob

OOB 区域内的偏移量

void *buf

用于存储数据的缓冲区

unsigned int len

缓冲区的长度

描述

此函数发出 READ OOB 操作。此函数不选择/取消选择 CS 线。

成功返回 0,否则返回负错误代码。

int nand_prog_page_begin_op(struct nand_chip *chip, unsigned int page, unsigned int offset_in_page, const void *buf, unsigned int len)

启动 PROG PAGE 操作

参数

struct nand_chip *chip

NAND 芯片

unsigned int page

要写入的页

unsigned int offset_in_page

页内的偏移量

const void *buf

包含要写入页的数据的缓冲区

unsigned int len

缓冲区的长度

描述

此函数发出 PROG PAGE 操作的前半部分。此函数不选择/取消选择 CS 线。

成功返回 0,否则返回负错误代码。

int nand_prog_page_end_op(struct nand_chip *chip)

结束 PROG PAGE 操作

参数

struct nand_chip *chip

NAND 芯片

描述

此函数发出 PROG PAGE 操作的后半部分。此函数不选择/取消选择 CS 线。

成功返回 0,否则返回负错误代码。

int nand_prog_page_op(struct nand_chip *chip, unsigned int page, unsigned int offset_in_page, const void *buf, unsigned int len)

执行完整的 PROG PAGE 操作

参数

struct nand_chip *chip

NAND 芯片

unsigned int page

要写入的页

unsigned int offset_in_page

页内的偏移量

const void *buf

包含要写入页的数据的缓冲区

unsigned int len

缓冲区的长度

描述

此函数执行完整的 PROG PAGE 操作。此函数不选择/取消选择 CS 线。

成功返回 0,否则返回负错误代码。

int nand_change_write_column_op(struct nand_chip *chip, unsigned int offset_in_page, const void *buf, unsigned int len, bool force_8bit)

执行 CHANGE WRITE COLUMN 操作

参数

struct nand_chip *chip

NAND 芯片

unsigned int offset_in_page

页内的偏移量

const void *buf

包含要发送到 NAND 的数据的缓冲区

unsigned int len

缓冲区的长度

bool force_8bit

强制 8 位总线访问

描述

此函数执行 CHANGE WRITE COLUMN 操作。此函数不选择/取消选择 CS 线。

成功返回 0,否则返回负错误代码。

int nand_readid_op(struct nand_chip *chip, u8 addr, void *buf, unsigned int len)

执行 READID 操作

参数

struct nand_chip *chip

NAND 芯片

u8 addr

在 READID 命令之后传递的地址周期

void *buf

用于存储 ID 的缓冲区

unsigned int len

缓冲区的长度

描述

此函数发送 READID 命令并读取 NAND 返回的 ID。此函数不选择/取消选择 CS 线。

成功返回 0,否则返回负错误代码。

int nand_status_op(struct nand_chip *chip, u8 *status)

执行 STATUS 操作

参数

struct nand_chip *chip

NAND 芯片

u8 *status

用于存储 NAND 状态的输出变量

描述

此函数发送 STATUS 命令并读取 NAND 返回的状态。此函数不选择/取消选择 CS 线。

成功返回 0,否则返回负错误代码。

int nand_exit_status_op(struct nand_chip *chip)

退出 STATUS 操作

参数

struct nand_chip *chip

NAND 芯片

描述

此函数发送 READ0 命令以取消 STATUS 命令的效果,从而避免在发送新的读取命令之前只读取状态。

此函数不选择/取消选择 CS 线。

成功返回 0,否则返回负错误代码。

int nand_erase_op(struct nand_chip *chip, unsigned int eraseblock)

执行擦除操作

参数

struct nand_chip *chip

NAND 芯片

unsigned int eraseblock

要擦除的块

描述

此函数发送 ERASE 命令并等待 NAND 准备就绪后返回。此函数不选择/取消选择 CS 线。

成功返回 0,否则返回负错误代码。

int nand_reset_op(struct nand_chip *chip)

执行复位操作

参数

struct nand_chip *chip

NAND 芯片

描述

此函数发送 RESET 命令并等待 NAND 准备就绪后返回。此函数不选择/取消选择 CS 线。

成功返回 0,否则返回负错误代码。

int nand_read_data_op(struct nand_chip *chip, void *buf, unsigned int len, bool force_8bit, bool check_only)

从 NAND 读取数据

参数

struct nand_chip *chip

NAND 芯片

void *buf

用于存储数据的缓冲区

unsigned int len

缓冲区的长度

bool force_8bit

强制 8 位总线访问

bool check_only

不实际运行该命令,仅检查控制器驱动程序是否支持它

描述

此函数在总线上执行原始数据读取。通常在启动另一个 NAND 操作(如 nand_read_page_op())后使用。此函数不选择/取消选择 CS 线。

成功返回 0,否则返回负错误代码。

int nand_write_data_op(struct nand_chip *chip, const void *buf, unsigned int len, bool force_8bit)

从 NAND 写入数据

参数

struct nand_chip *chip

NAND 芯片

const void *buf

包含要在总线上发送的数据的缓冲区

unsigned int len

缓冲区的长度

bool force_8bit

强制 8 位总线访问

描述

此函数在总线上执行原始数据写入。通常在启动另一个 NAND 操作(如 nand_write_page_begin_op())后使用。此函数不选择/取消选择 CS 线。

成功返回 0,否则返回负错误代码。

int nand_op_parser_exec_op(struct nand_chip *chip, const struct nand_op_parser *parser, const struct nand_operation *op, bool check_only)

exec_op 解析器

参数

struct nand_chip *chip

NAND 芯片

const struct nand_op_parser *parser

控制器驱动程序提供的模式描述

const struct nand_operation *op

要处理的 NAND 操作

bool check_only

为 true 时,该函数仅检查是否可以处理 op,但不执行该操作

描述

辅助函数,旨在简化仅支持有限指令序列的 NAND 控制器驱动程序的集成。支持的序列在 parser 中描述,如果 check_only 设置为 false,则框架会负责将 op 拆分为多个子操作,并将其传递回匹配模式的 ->exec() 回调。

NAND 控制器驱动程序应从其自身的 ->exec_op() 实现中调用此函数。

成功时返回 0,否则返回负错误代码。失败可能是由于不支持的操作(没有一个支持的模式能够处理请求的操作),或者由匹配模式的 ->exec() 挂钩返回的错误引起的。

unsigned int nand_subop_get_addr_start_off(const struct nand_subop *subop, unsigned int instr_idx)

获取地址数组中的起始偏移量

参数

const struct nand_subop *subop

整个子操作

unsigned int instr_idx

子操作内部的指令索引

描述

在驱动程序开发过程中,可能会倾向于直接使用地址指令的 ->addr.addrs 字段。这是错误的,因为地址指令可能会被拆分。

给定一个地址指令,返回要发出的第一个周期的偏移量。

unsigned int nand_subop_get_num_addr_cyc(const struct nand_subop *subop, unsigned int instr_idx)

获取要断言的剩余地址周期数

参数

const struct nand_subop *subop

整个子操作

unsigned int instr_idx

子操作内部的指令索引

描述

在驱动程序开发过程中,可能会倾向于直接使用数据指令的 ->addr->naddrs 字段。这是错误的,因为指令可能会被拆分。

给定一个地址指令,返回要发出的地址周期数。

unsigned int nand_subop_get_data_start_off(const struct nand_subop *subop, unsigned int instr_idx)

获取数据数组中的起始偏移量

参数

const struct nand_subop *subop

整个子操作

unsigned int instr_idx

子操作内部的指令索引

描述

在驱动程序开发过程中,可能会倾向于直接使用数据指令的 ->data->buf.{in,out} 字段。这是错误的,因为数据指令可能会被拆分。

给定一个数据指令,返回要开始的偏移量。

unsigned int nand_subop_get_data_len(const struct nand_subop *subop, unsigned int instr_idx)

获取要检索的字节数

参数

const struct nand_subop *subop

整个子操作

unsigned int instr_idx

子操作内部的指令索引

描述

在驱动程序开发过程中,可能会倾向于直接使用数据指令的 ->data->len 字段。这是错误的,因为数据指令可能会被拆分。

返回要发送/接收的数据块的长度。

int nand_reset(struct nand_chip *chip, int chipnr)

重置并初始化 NAND 设备

参数

struct nand_chip *chip

NAND 芯片

int chipnr

内部芯片 ID

描述

保存时序数据结构,然后应用 SDR 时序模式 0(有关详细信息,请参阅 nand_reset_interface),执行重置操作,然后恢复之前的时序。

成功返回 0,否则返回负错误代码。

int nand_check_erased_ecc_chunk(void *data, int datalen, void *ecc, int ecclen, void *extraoob, int extraooblen, int bitflips_threshold)

检查 ECC 块是否(几乎)只包含 0xff 数据

参数

void *data

要测试的数据缓冲区

int datalen

数据长度

void *ecc

ECC 缓冲区

int ecclen

ECC 长度

void *extraoob

额外的 OOB 缓冲区

int extraooblen

额外的 OOB 长度

int bitflips_threshold

最大位翻转数

描述

检查数据缓冲区及其关联的 ECC 和 OOB 数据是否仅包含 0xff 模式,这意味着底层区域已被擦除并准备好进行编程。 bitflips_threshold 指定将该区域视为未擦除之前的最大位翻转数。

返回小于或等于 bitflips_threshold 的正位翻转数,或超过阈值的位翻转数的 -ERROR_CODE。 如果成功,则用 0xff 填充传递的缓冲区。

注意

1/ ECC 算法在预定义的块大小上工作,这些块大小通常

与 NAND 页面大小不同。在修复位翻转时,ECC 引擎将报告每个块的错误数,并且 NAND 核心基础设施希望您返回整个页面的最大位翻转数。 这就是为什么您应该始终在单个块而不是整个页面上使用此函数的原因。在检查完每个块后,您应该相应地更新 max_bitflips 值。

2/ 在检查擦除页中的位翻转时,您不仅应该检查

有效负载数据,还应检查其关联的 ECC 数据,因为用户可能已将几乎所有位都编程为 1,但只有几个位除外。 在这种情况下,我们不应将该块视为已擦除,并且检查 ECC 字节可以防止这种情况。

3/ extraoob 参数是可选的,如果您的某些 OOB

数据受 ECC 引擎保护,则应使用此参数。 如果您支持子页面并希望将一些额外的 OOB 数据附加到 ECC 块,也可以使用此参数。

int nand_read_page_raw(struct nand_chip *chip, uint8_t *buf, int oob_required, int page)

[内部] 读取没有 ECC 的原始页面数据

参数

struct nand_chip *chip

nand 芯片信息结构

uint8_t *buf

存储读取数据的缓冲区

int oob_required

调用方需要读取到 chip->oob_poi 的 OOB 数据

int page

要读取的页码

描述

不适用于使用特殊 oob 布局的综合症计算 ECC 控制器。

int nand_monolithic_read_page_raw(struct nand_chip *chip, u8 *buf, int oob_required, int page)

原始模式下的整体页面读取

参数

struct nand_chip *chip

NAND 芯片信息结构

u8 *buf

存储读取数据的缓冲区

int oob_required

调用方需要读取到 chip->oob_poi 的 OOB 数据

int page

要读取的页码

描述

这是一个原始页面读取,即没有任何错误检测/纠正。单片式意味着我们请求将所有相关数据(主数据加上可能的 OOB 数据)加载到 NAND 缓存中,并以单个操作通过总线(从 NAND 芯片到 NAND 控制器)发送。 这是 nand_read_page_raw() 的替代方法,后者首先读取主数据,如果也请求了 OOB 数据,则在总线上读取更多数据。

int nand_read_page_hwecc_oob_first(struct nand_chip *chip, uint8_t *buf, int oob_required, int page)

硬件 ECC 页面读取,ECC 数据从 OOB 区域读取

参数

struct nand_chip *chip

nand 芯片信息结构

uint8_t *buf

存储读取数据的缓冲区

int oob_required

调用方需要读取到 chip->oob_poi 的 OOB 数据

int page

要读取的页码

描述

用于大页面芯片的硬件 ECC,它需要先从 OOB 中提取 ECC 数据,然后再读取实际数据。

int nand_read_oob_std(struct nand_chip *chip, int page)

[可替换] 最常用的 OOB 数据读取函数

参数

struct nand_chip *chip

nand 芯片信息结构

int page

要读取的页码

int nand_write_oob_std(struct nand_chip *chip, int page)

[可替换] 最常用的 OOB 数据写入函数

参数

struct nand_chip *chip

nand 芯片信息结构

int page

要写入的页码

int nand_write_page_raw(struct nand_chip *chip, const uint8_t *buf, int oob_required, int page)

[内部] 原始页面写入函数

参数

struct nand_chip *chip

nand 芯片信息结构

const uint8_t *buf

数据缓冲区

int oob_required

必须将 chip->oob_poi 写入 OOB

int page

要写入的页码

描述

不适用于使用特殊 oob 布局的综合症计算 ECC 控制器。

int nand_monolithic_write_page_raw(struct nand_chip *chip, const u8 *buf, int oob_required, int page)

原始模式下的单片式页面写入

参数

struct nand_chip *chip

NAND 芯片信息结构

const u8 *buf

要写入的数据缓冲区

int oob_required

必须将 chip->oob_poi 写入 OOB

int page

要写入的页码

描述

这是一个原始页面写入,即没有任何错误检测/纠正。单片式意味着我们请求将所有相关数据(主数据加上可能的 OOB 数据)通过总线发送,并在单个操作中有效地编程到 NAND 芯片阵列中。 这是 nand_write_page_raw() 的替代方法,后者首先发送主数据,然后通过在 NAND 总线上锁定更多数据周期来最终发送 OOB 数据,最后发送程序命令以同步 NAND 芯片缓存。

int rawnand_dt_parse_gpio_cs(struct device *dev, struct gpio_desc ***cs_array, unsigned int *ncs_array)

解析控制器的 gpio-cs 属性

参数

struct device *dev

要解析的设备。也用于管理分配。

struct gpio_desc ***cs_array

成功时分配的 GPIO 描述符指针数组

unsigned int *ncs_array

成功时更新的 cs_array 中的条目数。 成功时 返回 0,否则返回错误。

int nand_ecc_choose_conf(struct nand_chip *chip, const struct nand_ecc_caps *caps, int oobavail)

设置 ECC 强度和 ECC 步长

参数

struct nand_chip *chip

nand 芯片信息结构

const struct nand_ecc_caps *caps

ECC 引擎功能信息结构

int oobavail

ECC 引擎可以使用的 OOB 大小

描述

根据以下逻辑选择 ECC 配置。

  1. 如果 ECC 步长和 ECC 强度都已设置(通常由 DT 设置),则检查此控制器是否支持它。

  2. 如果用户提供了 nand-ecc-maximize 属性,则选择最大 ECC 强度。

  3. 否则,请尝试匹配与芯片要求最接近的 ECC 步长和 ECC 强度。如果可用的 OOB 大小不能满足芯片要求,则回退到最大 ECC 步长和 ECC 强度。

成功时,将设置选定的 ECC 设置。

int nand_scan_with_ids(struct nand_chip *chip, unsigned int maxchips, struct nand_flash_dev *ids)

[NAND 接口] 扫描 NAND 设备

参数

struct nand_chip *chip

NAND 芯片对象

unsigned int maxchips

要扫描的芯片数量。

struct nand_flash_dev *ids

可选的闪存 ID 表

描述

这将使用默认值填充所有未初始化的函数指针。读取闪存 ID,并用适当的值填充 mtd/芯片结构。

void nand_cleanup(struct nand_chip *chip)

[NAND 接口] 释放 NAND 设备持有的资源

参数

struct nand_chip *chip

NAND 芯片对象

提供的内部函数

本章包含 NAND 驱动程序内部函数的自动生成文档。每个函数都有一个简短的描述,该描述标有 [XXX] 标识符。有关说明,请参阅“文档提示”一章。标有 [DEFAULT] 的函数可能与板驱动程序开发人员相关。

void nand_release_device(struct nand_chip *chip)

[通用] 释放芯片

参数

struct nand_chip *chip

NAND 芯片对象

描述

释放芯片锁并唤醒等待设备上的任何人。

int nand_bbm_get_next_page(struct nand_chip *chip, int page)

获取坏块标记的下一页

参数

struct nand_chip *chip

NAND 芯片对象

int page

开始检查坏块标记使用的第一页

描述

返回一个整数,该整数对应于块内页面偏移量,用于存储坏块标记的页面。如果没有更多页面可用,则返回 -EINVAL。

int nand_block_bad(struct nand_chip *chip, loff_t ofs)

[默认] 从芯片读取坏块标记

参数

struct nand_chip *chip

NAND 芯片对象

loff_t ofs

设备起始偏移量

描述

检查块是否为坏块。

bool nand_region_is_secured(struct nand_chip *chip, loff_t offset, u64 size)

检查区域是否安全

参数

struct nand_chip *chip

NAND 芯片对象

loff_t offset

要检查的区域的偏移量

u64 size

要检查的区域的大小

描述

通过将偏移量和大小与从 DT 获取的安全区域列表进行比较来检查区域是否安全。如果区域安全则返回 true,否则返回 false。

void nand_get_device(struct nand_chip *chip)

[通用] 获取所选访问的芯片

参数

struct nand_chip *chip

NAND 芯片结构

描述

锁定设备及其控制器以进行独占访问

int nand_check_wp(struct nand_chip *chip)

[通用] 检查芯片是否写保护

参数

struct nand_chip *chip

NAND 芯片对象

描述

检查设备是否写保护。该函数期望设备已被选中。

uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob, size_t len, struct mtd_oob_ops *ops)

[内部] 将客户端缓冲区传输到 OOB

参数

struct nand_chip *chip

NAND 芯片对象

uint8_t *oob

OOB 数据缓冲区

size_t len

OOB 数据写入长度

struct mtd_oob_ops *ops

OOB 操作结构

int nand_do_write_oob(struct nand_chip *chip, loff_t to, struct mtd_oob_ops *ops)

[MTD 接口] NAND 写带外数据

参数

struct nand_chip *chip

NAND 芯片对象

loff_t to

要写入的偏移量

struct mtd_oob_ops *ops

OOB 操作描述结构

描述

NAND 写带外数据。

int nand_default_block_markbad(struct nand_chip *chip, loff_t ofs)

[默认] 通过坏块标记标记块为坏块

参数

struct nand_chip *chip

NAND 芯片对象

loff_t ofs

设备起始偏移量

描述

这是默认实现,可以被硬件特定的驱动程序覆盖。它提供了将坏块标记写入块的详细信息。

int nand_markbad_bbm(struct nand_chip *chip, loff_t ofs)

通过更新 BBM 来标记块

参数

struct nand_chip *chip

NAND 芯片对象

loff_t ofs

要标记为坏块的块的偏移量

int nand_block_markbad_lowlevel(struct nand_chip *chip, loff_t ofs)

标记一个坏块

参数

struct nand_chip *chip

NAND 芯片对象

loff_t ofs

设备起始偏移量

描述

此函数执行通用的 NAND 坏块标记步骤(即,坏块表和/或标记)。我们只允许硬件驱动程序指定如何将坏块标记写入 OOB (chip->legacy.block_markbad)。

我们按以下顺序尝试操作

  1. 擦除受影响的块,以允许干净地写入 OOB 标记

  2. 将坏块标记写入受影响块的 OOB 区域(除非存在标志 NAND_BBT_NO_OOB_BBM)

  3. 更新 BBT

请注意,我们保留在 (2) 或 (3) 中遇到的第一个错误,完成这些过程,并在最后转储错误。

int nand_block_isreserved(struct mtd_info *mtd, loff_t ofs)

[通用] 检查块是否标记为保留。

参数

struct mtd_info *mtd

MTD 设备结构

loff_t ofs

设备起始偏移量

描述

检查块是否标记为保留。

int nand_block_checkbad(struct nand_chip *chip, loff_t ofs, int allowbbt)

[通用] 检查块是否标记为坏块

参数

struct nand_chip *chip

NAND 芯片对象

loff_t ofs

设备起始偏移量

int allowbbt

1,如果允许访问 bbt 区域

描述

检查块是否为坏块。通过读取坏块表或调用扫描函数。

void panic_nand_wait(struct nand_chip *chip, unsigned long timeo)

[通用] 等待命令完成

参数

struct nand_chip *chip

NAND 芯片结构

unsigned long timeo

超时

描述

等待命令完成。这是在中断上下文中使用 nand_wait 的辅助函数。可能发生在 panic 状态,并且尝试通过 mtdoops 写入 oops 信息。

int nand_reset_interface(struct nand_chip *chip, int chipnr)

重置数据接口和时序

参数

struct nand_chip *chip

NAND 芯片

int chipnr

内部芯片 ID

描述

将数据接口和时序重置为 ONFI 模式 0。

成功返回 0,否则返回负错误代码。

int nand_setup_interface(struct nand_chip *chip, int chipnr)

设置最佳数据接口和时序

参数

struct nand_chip *chip

NAND 芯片

int chipnr

内部芯片 ID

描述

配置芯片和驱动程序支持的最佳数据接口和 NAND 时序。

成功返回 0,否则返回负错误代码。

int nand_choose_best_sdr_timings(struct nand_chip *chip, struct nand_interface_config *iface, struct nand_sdr_timings *spec_timings)

选择 NAND 控制器和 NAND 芯片都支持的最佳 SDR 时序

参数

struct nand_chip *chip

NAND 芯片

struct nand_interface_config *iface

接口配置(最终可以更新)

struct nand_sdr_timings *spec_timings

特定时序,当不符合 ONFI 规范时使用

描述

如果提供了特定时序,则使用它们。否则,从 ONFI 信息中检索支持的时序模式。

int nand_choose_best_nvddr_timings(struct nand_chip *chip, struct nand_interface_config *iface, struct nand_nvddr_timings *spec_timings)

选择 NAND 控制器和 NAND 芯片都支持的最佳 NVDDR 时序

参数

struct nand_chip *chip

NAND 芯片

struct nand_interface_config *iface

接口配置(最终可以更新)

struct nand_nvddr_timings *spec_timings

特定时序,当不符合 ONFI 规范时使用

描述

如果提供了特定时序,则使用它们。否则,从 ONFI 信息中检索支持的时序模式。

int nand_choose_best_timings(struct nand_chip *chip, struct nand_interface_config *iface)

选择 NAND 控制器和 NAND 芯片都支持的最佳 NVDDR 或 SDR 时序

参数

struct nand_chip *chip

NAND 芯片

struct nand_interface_config *iface

接口配置(最终可以更新)

描述

如果提供了特定时序,则使用它们。否则,从 ONFI 信息中检索支持的时序模式。

int nand_choose_interface_config(struct nand_chip *chip)

找到最佳数据接口和时序

参数

struct nand_chip *chip

NAND 芯片

描述

找到芯片和驱动程序支持的最佳数据接口和 NAND 时序。最终让 NAND 制造商驱动程序提出自己的一组时序。

在此函数之后,nand_chip->interface_config 将初始化为可用的最佳时序模式。

成功返回 0,否则返回负错误代码。

int nand_fill_column_cycles(struct nand_chip *chip, u8 *addrs, unsigned int offset_in_page)

填充地址的列周期

参数

struct nand_chip *chip

NAND 芯片

u8 *addrs

要填充的地址周期数组

unsigned int offset_in_page

页内的偏移量

描述

根据 NAND 总线宽度和页大小,填充 addrs 字段的第一个或前两个字节。

返回编码列所需的周期数,如果其中一个参数无效,则返回负错误代码。

int nand_read_param_page_op(struct nand_chip *chip, u8 page, void *buf, unsigned int len)

执行读取参数页操作

参数

struct nand_chip *chip

NAND 芯片

u8 page

要读取的参数页

void *buf

用于存储数据的缓冲区

unsigned int len

缓冲区的长度

描述

此函数发出读取参数页操作。此函数不选择/取消选择 CS 线。

成功返回 0,否则返回负错误代码。

int nand_set_features_op(struct nand_chip *chip, u8 feature, const void *data)

执行设置特性操作

参数

struct nand_chip *chip

NAND 芯片

u8 feature

特性 ID

const void *data

4 字节数据

描述

此函数发送设置特性命令,并等待 NAND 准备就绪后再返回。此函数不选择/取消选择 CS 线。

成功返回 0,否则返回负错误代码。

int nand_get_features_op(struct nand_chip *chip, u8 feature, void *data)

执行获取特性操作

参数

struct nand_chip *chip

NAND 芯片

u8 feature

特性 ID

void *data

4 字节数据

描述

此函数发送获取特性命令,并等待 NAND 准备就绪后再返回。此函数不选择/取消选择 CS 线。

成功返回 0,否则返回负错误代码。

struct nand_op_parser_ctx

解析器使用的上下文

定义:

struct nand_op_parser_ctx {
    const struct nand_op_instr *instrs;
    unsigned int ninstrs;
    struct nand_subop subop;
};

成员

instrs

必须处理的所有指令的数组

ninstrs

instrs 数组的长度

子操作

要传递给 NAND 控制器的子操作

描述

核心使用此结构将 NAND 操作拆分为可以由 NAND 控制器处理的子操作。

bool nand_op_parser_must_split_instr(const struct nand_op_parser_pattern_elem *pat, const struct nand_op_instr *instr, unsigned int *start_offset)

检查是否必须拆分指令

参数

const struct nand_op_parser_pattern_elem *pat

instr 匹配的解析器模式元素

const struct nand_op_instr *instr

指向要检查的指令的指针

unsigned int *start_offset

这是一个输入/输出参数。如果 instr 已经被拆分,则 start_offset 是开始的偏移量(地址周期或数据缓冲区中的偏移量)。相反,如果函数返回 true(即 instr 必须拆分),则此参数将更新以指向尚未处理的第一个数据/地址周期。

描述

一些 NAND 控制器是有限的,不能通过唯一的操作发送 X 个地址周期,也不能同时读取/写入超过 Y 个字节。在这种情况下,将不适合单个控制器操作的指令拆分为两个或多个块。

如果必须拆分指令,则返回 true,否则返回 false。 start_offset 参数也会更新为下一组指令必须开始的偏移量(如果是地址或数据指令)。

bool nand_op_parser_match_pat(const struct nand_op_parser_pattern *pat, struct nand_op_parser_ctx *ctx)

检查模式是否与解析器上下文中剩余的指令匹配

参数

const struct nand_op_parser_pattern *pat

要测试的模式

struct nand_op_parser_ctx *ctx

要与模式 pat 匹配的解析器上下文结构

描述

检查 pat 是否匹配 ctx 中剩余的指令集或子集。如果匹配,则返回 true,否则返回 false。当返回 true 时,ctx->subop 将更新为要传递给控制器驱动程序的指令集。

int nand_get_features(struct nand_chip *chip, int addr, u8 *subfeature_param)

执行 GET_FEATURE 的包装函数

参数

struct nand_chip *chip

NAND 芯片信息结构

int addr

特性地址

u8 *subfeature_param

子特性参数,一个 4 字节的数组

描述

成功返回 0,否则返回负的错误码。 如果操作无法处理,则返回 -ENOTSUPP。

int nand_set_features(struct nand_chip *chip, int addr, u8 *subfeature_param)

执行 SET_FEATURE 的包装函数

参数

struct nand_chip *chip

NAND 芯片信息结构

int addr

特性地址

u8 *subfeature_param

子特性参数,一个 4 字节的数组

描述

成功返回 0,否则返回负的错误码。 如果操作无法处理,则返回 -ENOTSUPP。

int nand_check_erased_buf(void *buf, int len, int bitflips_threshold)

检查缓冲区是否(几乎)只包含 0xff 数据

参数

void *buf

要测试的缓冲区

int len

缓冲区长度

int bitflips_threshold

最大位翻转数

描述

检查缓冲区是否只包含 0xff,这意味着底层区域已被擦除并准备好进行编程。 bitflips_threshold 指定在认为该区域未擦除之前允许的最大位翻转次数。 返回小于或等于 bitflips_threshold 的正数位翻转次数,或者对于超过阈值的位翻转返回 -ERROR_CODE。

注意

此函数的逻辑是从 memweight 实现中提取的,除了如果位翻转次数超过 bitflips_threshold 值,`nand_check_erased_buf` 函数会在测试整个缓冲区之前退出。

int nand_read_page_raw_notsupp(struct nand_chip *chip, u8 *buf, int oob_required, int page)

虚拟读取原始页函数

参数

struct nand_chip *chip

nand 芯片信息结构

u8 *buf

存储读取数据的缓冲区

int oob_required

调用方需要读取到 chip->oob_poi 的 OOB 数据

int page

要读取的页码

描述

无条件返回 -ENOTSUPP。

int nand_read_page_raw_syndrome(struct nand_chip *chip, uint8_t *buf, int oob_required, int page)

[内部] 读取没有 ECC 的原始页面数据

参数

struct nand_chip *chip

nand 芯片信息结构

uint8_t *buf

存储读取数据的缓冲区

int oob_required

调用方需要读取到 chip->oob_poi 的 OOB 数据

int page

要读取的页码

描述

即使不使用 OOB,我们也需要特殊的 oob 布局和处理。

int nand_read_page_swecc(struct nand_chip *chip, uint8_t *buf, int oob_required, int page)

[可替换] 基于软件 ECC 的页面读取函数

参数

struct nand_chip *chip

nand 芯片信息结构

uint8_t *buf

存储读取数据的缓冲区

int oob_required

调用方需要读取到 chip->oob_poi 的 OOB 数据

int page

要读取的页码

int nand_read_subpage(struct nand_chip *chip, uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi, int page)

[可替换] 基于 ECC 的子页读取函数

参数

struct nand_chip *chip

nand 芯片信息结构

uint32_t data_offs

请求数据在页面中的偏移量

uint32_t readlen

数据长度

uint8_t *bufpoi

存储读取数据的缓冲区

int page

要读取的页码

int nand_read_page_hwecc(struct nand_chip *chip, uint8_t *buf, int oob_required, int page)

[可替换] 基于硬件 ECC 的页面读取函数

参数

struct nand_chip *chip

nand 芯片信息结构

uint8_t *buf

存储读取数据的缓冲区

int oob_required

调用方需要读取到 chip->oob_poi 的 OOB 数据

int page

要读取的页码

描述

不适用于需要特殊 oob 布局的校验和计算 ECC 控制器。

int nand_read_page_syndrome(struct nand_chip *chip, uint8_t *buf, int oob_required, int page)

[可替换] 基于硬件 ECC 校验和的页面读取

参数

struct nand_chip *chip

nand 芯片信息结构

uint8_t *buf

存储读取数据的缓冲区

int oob_required

调用方需要读取到 chip->oob_poi 的 OOB 数据

int page

要读取的页码

描述

硬件生成器会自动计算错误校验和。 因此,我们需要特殊的 oob 布局和处理。

uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob, struct mtd_oob_ops *ops, size_t len)

[内部] 将 oob 传输到客户端缓冲区

参数

struct nand_chip *chip

NAND 芯片对象

uint8_t *oob

oob 目标地址

struct mtd_oob_ops *ops

OOB 操作结构

size_t len

要传输的 oob 大小

int nand_setup_read_retry(struct nand_chip *chip, int retry_mode)

[内部] 设置 READ RETRY 模式

参数

struct nand_chip *chip

NAND 芯片对象

int retry_mode

要使用的重试模式

描述

一些供应商提供了一个特殊的命令来移动 Vt 阈值,当页面中存在太多位翻转(即 ECC 错误)时使用。 设置新的阈值后,主机应重试读取该页面。

int nand_do_read_ops(struct nand_chip *chip, loff_t from, struct mtd_oob_ops *ops)

[内部] 使用 ECC 读取数据

参数

struct nand_chip *chip

NAND 芯片对象

loff_t from

读取起始偏移量

struct mtd_oob_ops *ops

OOB 操作结构

描述

内部函数。调用时需持有芯片锁。

int nand_read_oob_syndrome(struct nand_chip *chip, int page)

[可替换] 用于带校验位的硬件 ECC 的 OOB 数据读取函数

参数

struct nand_chip *chip

nand 芯片信息结构

int page

要读取的页码

int nand_write_oob_syndrome(struct nand_chip *chip, int page)

[可替换] 用于带校验位的硬件 ECC 的 OOB 数据写入函数 - 仅用于大页闪存

参数

struct nand_chip *chip

nand 芯片信息结构

int page

要写入的页码

int nand_do_read_oob(struct nand_chip *chip, loff_t from, struct mtd_oob_ops *ops)

[内部] NAND 带外读取

参数

struct nand_chip *chip

NAND 芯片对象

loff_t from

读取起始偏移量

struct mtd_oob_ops *ops

带外操作描述结构

描述

从备用区读取 NAND 带外数据。

int nand_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops)

[MTD 接口] NAND 读取数据和/或带外数据

参数

struct mtd_info *mtd

MTD 设备结构

loff_t from

读取起始偏移量

struct mtd_oob_ops *ops

OOB 操作描述结构

描述

NAND 读取数据和/或带外数据。

int nand_write_page_raw_notsupp(struct nand_chip *chip, const u8 *buf, int oob_required, int page)

虚拟原始页写入函数

参数

struct nand_chip *chip

nand 芯片信息结构

const u8 *buf

数据缓冲区

int oob_required

必须将 chip->oob_poi 写入 OOB

int page

要写入的页码

描述

无条件返回 -ENOTSUPP。

int nand_write_page_raw_syndrome(struct nand_chip *chip, const uint8_t *buf, int oob_required, int page)

[内部] 原始页面写入函数

参数

struct nand_chip *chip

nand 芯片信息结构

const uint8_t *buf

数据缓冲区

int oob_required

必须将 chip->oob_poi 写入 OOB

int page

要写入的页码

描述

即使未检查 ECC,我们也需要特殊的 OOB 布局和处理。

int nand_write_page_swecc(struct nand_chip *chip, const uint8_t *buf, int oob_required, int page)

[可替换] 基于软件 ECC 的页写入函数

参数

struct nand_chip *chip

nand 芯片信息结构

const uint8_t *buf

数据缓冲区

int oob_required

必须将 chip->oob_poi 写入 OOB

int page

要写入的页码

int nand_write_page_hwecc(struct nand_chip *chip, const uint8_t *buf, int oob_required, int page)

[可替换] 基于硬件 ECC 的页写入函数

参数

struct nand_chip *chip

nand 芯片信息结构

const uint8_t *buf

数据缓冲区

int oob_required

必须将 chip->oob_poi 写入 OOB

int page

要写入的页码

int nand_write_subpage_hwecc(struct nand_chip *chip, uint32_t offset, uint32_t data_len, const uint8_t *buf, int oob_required, int page)

[可替换] 基于硬件 ECC 的子页写入

参数

struct nand_chip *chip

nand 芯片信息结构

uint32_t offset

页内子页的列地址

uint32_t data_len

数据长度

const uint8_t *buf

数据缓冲区

int oob_required

必须将 chip->oob_poi 写入 OOB

int page

要写入的页码

int nand_write_page_syndrome(struct nand_chip *chip, const uint8_t *buf, int oob_required, int page)

[可替换] 基于硬件 ECC 校验位的页写入

参数

struct nand_chip *chip

nand 芯片信息结构

const uint8_t *buf

数据缓冲区

int oob_required

必须将 chip->oob_poi 写入 OOB

int page

要写入的页码

描述

硬件生成器会自动计算错误校验和。 因此,我们需要特殊的 oob 布局和处理。

int nand_write_page(struct nand_chip *chip, uint32_t offset, int data_len, const uint8_t *buf, int oob_required, int page, int raw)

写入一页

参数

struct nand_chip *chip

NAND 芯片描述符

uint32_t offset

页内地址偏移量

int data_len

要写入的实际数据长度

const uint8_t *buf

要写入的数据

int oob_required

必须将 chip->oob_poi 写入 OOB

int page

要写入的页码

int raw

使用 write_page 的 _raw 版本

int nand_do_write_ops(struct nand_chip *chip, loff_t to, struct mtd_oob_ops *ops)

[内部] 带 ECC 的 NAND 写入

参数

struct nand_chip *chip

NAND 芯片对象

loff_t to

要写入的偏移量

struct mtd_oob_ops *ops

带外操作描述结构

描述

带 ECC 的 NAND 写入。

int panic_nand_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const uint8_t *buf)

[MTD 接口] 带 ECC 的 NAND 写入

参数

struct mtd_info *mtd

MTD 设备结构

loff_t to

要写入的偏移量

size_t len

要写入的字节数

size_t *retlen

指向用于存储写入字节数的变量的指针

const uint8_t *buf

要写入的数据

描述

带 ECC 的 NAND 写入。在中断上下文中执行写入时使用,例如,当在 panic 时写入 oops 时,可能会被 mtdoops 调用。

int nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops)

[MTD 接口] NAND 写入数据和/或带外数据

参数

struct mtd_info *mtd

MTD 设备结构

loff_t to

要写入的偏移量

struct mtd_oob_ops *ops

OOB 操作描述结构

int nand_erase(struct mtd_info *mtd, struct erase_info *instr)

[MTD 接口] 擦除块

参数

struct mtd_info *mtd

MTD 设备结构

struct erase_info *instr

擦除指令

描述

擦除一个或多个块。

int nand_erase_nand(struct nand_chip *chip, struct erase_info *instr, int allowbbt)

[内部] 擦除块

参数

struct nand_chip *chip

NAND 芯片对象

struct erase_info *instr

擦除指令

int allowbbt

允许擦除 bbt 区域

描述

擦除一个或多个块。

void nand_sync(struct mtd_info *mtd)

[MTD 接口] 同步

参数

struct mtd_info *mtd

MTD 设备结构

描述

同步实际上是等待芯片就绪函数。

int nand_block_isbad(struct mtd_info *mtd, loff_t offs)

[MTD 接口] 检查偏移量处的块是否为坏块

参数

struct mtd_info *mtd

MTD 设备结构

loff_t offs

相对于 mtd 起始的偏移量

int nand_block_markbad(struct mtd_info *mtd, loff_t ofs)

[MTD 接口] 将给定偏移量处的块标记为坏块

参数

struct mtd_info *mtd

MTD 设备结构

loff_t ofs

相对于 mtd 起始的偏移量

int nand_suspend(struct mtd_info *mtd)

[MTD 接口] 暂停 NAND 闪存

参数

struct mtd_info *mtd

MTD 设备结构

描述

成功返回 0,否则返回负错误代码。

void nand_resume(struct mtd_info *mtd)

[MTD 接口] 恢复 NAND 闪存

参数

struct mtd_info *mtd

MTD 设备结构

void nand_shutdown(struct mtd_info *mtd)

[MTD 接口] 完成当前的 NAND 操作并阻止进一步操作

参数

struct mtd_info *mtd

MTD 设备结构

int nand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)

[MTD 接口] 锁定 NAND 闪存

参数

struct mtd_info *mtd

MTD 设备结构

loff_t ofs

偏移量字节地址

uint64_t len

要锁定的字节数(必须是块/页大小的倍数)

int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)

[MTD 接口] 解锁 NAND 闪存

参数

struct mtd_info *mtd

MTD 设备结构

loff_t ofs

偏移量字节地址

uint64_t len

要解锁的字节数(必须是块/页大小的倍数)

int nand_scan_ident(struct nand_chip *chip, unsigned int maxchips, struct nand_flash_dev *table)

扫描 NAND 设备

参数

struct nand_chip *chip

NAND 芯片对象

unsigned int maxchips

要扫描的芯片数量

struct nand_flash_dev *table

备用 NAND ID 表

描述

这是正常 nand_scan() 函数的第一阶段。它读取闪存 ID 并相应地设置 MTD 字段。

此辅助函数以前由需要调整一些与 ECC 相关的参数的控制器驱动程序直接调用,然后再调用 nand_scan_tail()。这种分离阻止了在此阶段进行动态分配,这很不方便,并且为了方便 ->init_ecc()/cleanup_ecc() 钩子而被禁止。

int nand_check_ecc_caps(struct nand_chip *chip, const struct nand_ecc_caps *caps, int oobavail)

检查预设 ECC 设置的合理性

参数

struct nand_chip *chip

nand 芯片信息结构

const struct nand_ecc_caps *caps

ECC 容量信息结构

int oobavail

ECC 引擎可以使用的 OOB 大小

描述

当 ECC 步长和强度已经设置时,检查它们是否受控制器支持,以及计算出的 ECC 字节是否适合芯片的 OOB 区域。如果成功,则设置计算出的 ECC 字节数。

int nand_match_ecc_req(struct nand_chip *chip, const struct nand_ecc_caps *caps, int oobavail)

以最少的 ECC 字节满足芯片的要求

参数

struct nand_chip *chip

nand 芯片信息结构

const struct nand_ecc_caps *caps

ECC 引擎功能信息结构

int oobavail

ECC 引擎可以使用的 OOB 大小

描述

如果提供了芯片的 ECC 要求,则尝试以最少的 ECC 字节(即以最大的 OOB 可用字节)来满足它。如果成功,则设置选定的 ECC 设置。

int nand_maximize_ecc(struct nand_chip *chip, const struct nand_ecc_caps *caps, int oobavail)

选择可用的最大 ECC 强度

参数

struct nand_chip *chip

nand 芯片信息结构

const struct nand_ecc_caps *caps

ECC 引擎功能信息结构

int oobavail

ECC 引擎可以使用的 OOB 大小

描述

选择控制器支持且适合芯片 OOB 区域的最大 ECC 强度。如果成功,则设置选定的 ECC 设置。

int nand_scan_tail(struct nand_chip *chip)

扫描 NAND 设备

参数

struct nand_chip *chip

NAND 芯片对象

描述

这是普通 nand_scan() 函数的第二阶段。它使用默认值填充所有未初始化的函数指针,并扫描坏块表(如果适用)。

int check_pattern(uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td)

[通用] 检查缓冲区中是否存在某个模式

参数

uint8_t *buf

要搜索的缓冲区

int len

要搜索的缓冲区长度

int paglen

页长度

struct nand_bbt_descr *td

搜索模式描述符

描述

检查给定位置是否存在某种模式。用于搜索坏块表和好/坏块标识符。

int check_short_pattern(uint8_t *buf, struct nand_bbt_descr *td)

[通用] 检查缓冲区中是否存在某个模式

参数

uint8_t *buf

要搜索的缓冲区

struct nand_bbt_descr *td

搜索模式描述符

描述

检查给定位置是否存在某种模式。用于搜索坏块表和好/坏块标识符。与 check_pattern 相同,但没有可选的空检查。

u32 add_marker_len(struct nand_bbt_descr *td)

计算数据区域中标记的长度

参数

struct nand_bbt_descr *td

用于计算的 BBT 描述符

描述

如果标记位于 OOB 区域,则长度将为 0。

int read_bbt(struct nand_chip *this, uint8_t *buf, int page, int num, struct nand_bbt_descr *td, int offs)

[通用] 从页面开始读取坏块表

参数

struct nand_chip *this

NAND 芯片对象

uint8_t *buf

临时缓冲区

int page

起始页

int num

要读取的 bbt 描述符的数量

struct nand_bbt_descr *td

bbt 描述表

int offs

表中的块号偏移量

描述

从页面开始读取坏块表。

int read_abs_bbt(struct nand_chip *this, uint8_t *buf, struct nand_bbt_descr *td, int chip)

[通用] 从给定页面开始读取坏块表

参数

struct nand_chip *this

NAND 芯片对象

uint8_t *buf

临时缓冲区

struct nand_bbt_descr *td

坏块表的描述符

int chip

读取特定芯片的表,-1 读取所有芯片;仅当设置 NAND_BBT_PERCHIP 选项时才适用

描述

从给定页面开始读取所有芯片的坏块表。我们假设 bbt 位是连续排列的。

int scan_read_oob(struct nand_chip *this, uint8_t *buf, loff_t offs, size_t len)

[通用] 扫描数据+OOB 区域到缓冲区

参数

struct nand_chip *this

NAND 芯片对象

uint8_t *buf

临时缓冲区

loff_t offs

要扫描的偏移量

size_t len

要读取的数据区域的长度

描述

从数据+OOB 扫描读取数据。可能会遍历多个页面,在 buf 中交错页面、OOB、页面、OOB...。完成传输并返回“最强”的 ECC 条件(错误或位翻转)。可能会在第一个(非 ECC)错误时退出。

void read_abs_bbts(struct nand_chip *this, uint8_t *buf, struct nand_bbt_descr *td, struct nand_bbt_descr *md)

[通用] 从给定页开始读取所有芯片的坏块表

参数

struct nand_chip *this

NAND 芯片对象

uint8_t *buf

临时缓冲区

struct nand_bbt_descr *td

坏块表的描述符

struct nand_bbt_descr *md

坏块表镜像的描述符

描述

从给定页开始读取所有芯片的坏块表。我们假设 bbt 位是连续的。

int create_bbt(struct nand_chip *this, uint8_t *buf, struct nand_bbt_descr *bd, int chip)

[通用] 通过扫描设备创建坏块表

参数

struct nand_chip *this

NAND 芯片对象

uint8_t *buf

临时缓冲区

struct nand_bbt_descr *bd

好/坏块搜索模式的描述符

int chip

为特定芯片创建表格,-1 读取所有芯片; 仅当设置了 NAND_BBT_PERCHIP 选项时才适用

描述

通过扫描设备以查找给定的好/坏块识别模式来创建坏块表。

int search_bbt(struct nand_chip *this, uint8_t *buf, struct nand_bbt_descr *td)

[通用] 扫描设备以查找特定的坏块表

参数

struct nand_chip *this

NAND 芯片对象

uint8_t *buf

临时缓冲区

struct nand_bbt_descr *td

坏块表的描述符

描述

通过搜索给定的标识模式来读取坏块表。搜索从设备的开头向上或从设备的结尾向下执行。搜索始终从块的开头开始。如果给出了 NAND_BBT_PERCHIP 选项,则为每个芯片搜索一个 bbt,其中包含该芯片的坏块信息。这是为某些 DOC 设备提供支持所必需的。

bbt 标识模式位于块中第一页的 oob 区域。

void search_read_bbts(struct nand_chip *this, uint8_t *buf, struct nand_bbt_descr *td, struct nand_bbt_descr *md)

[通用] 扫描设备以查找坏块表

参数

struct nand_chip *this

NAND 芯片对象

uint8_t *buf

临时缓冲区

struct nand_bbt_descr *td

坏块表的描述符

struct nand_bbt_descr *md

坏块表镜像的描述符

描述

搜索并读取坏块表。

int get_bbt_block(struct nand_chip *this, struct nand_bbt_descr *td, struct nand_bbt_descr *md, int chip)

获取适合存储 BBT 的第一个有效擦除块

参数

struct nand_chip *this

NAND 设备

struct nand_bbt_descr *td

BBT 描述

struct nand_bbt_descr *md

镜像 BBT 描述符

int chip

芯片选择器

描述

此函数返回一个正的块号,该块号指向适合存储 BBT 的有效擦除块(即在为 BBT 保留的范围内),如果所有块都已使用或标记为坏块,则返回 -ENOSPC。如果 td->pages[chip] 已经指向一个有效的块,我们将重用它,否则我们将搜索下一个有效的块。

void mark_bbt_block_bad(struct nand_chip *this, struct nand_bbt_descr *td, int chip, int block)

将为 BBT 保留的块之一标记为坏块

参数

struct nand_chip *this

NAND 设备

struct nand_bbt_descr *td

BBT 描述

int chip

芯片选择器

int block

要标记的 BBT 块

描述

为 BBT 保留的块可能会变为坏块。此函数是一个辅助函数,用于将此类块标记为坏块。它负责更新内存中的 BBT,使用坏块标记将块标记为坏块,并使关联的 td->pages[] 条目无效。

int write_bbt(struct nand_chip *this, uint8_t *buf, struct nand_bbt_descr *td, struct nand_bbt_descr *md, int chipsel)

[通用] (重新)写入坏块表

参数

struct nand_chip *this

NAND 芯片对象

uint8_t *buf

临时缓冲区

struct nand_bbt_descr *td

坏块表的描述符

struct nand_bbt_descr *md

坏块表镜像的描述符

int chipsel

特定芯片的选择器,-1 表示全部

描述

(重新)写入坏块表。

int nand_memory_bbt(struct nand_chip *this, struct nand_bbt_descr *bd)

[通用] 创建基于内存的坏块表

参数

struct nand_chip *this

NAND 芯片对象

struct nand_bbt_descr *bd

好/坏块搜索模式的描述符

描述

该函数通过扫描设备以查找制造商/软件标记的好/坏块来创建基于内存的 bbt。

int check_create(struct nand_chip *this, uint8_t *buf, struct nand_bbt_descr *bd)

[通用] 必要时创建并写入 bbt

参数

struct nand_chip *this

NAND 设备

uint8_t *buf

临时缓冲区

struct nand_bbt_descr *bd

好/坏块搜索模式的描述符

描述

该函数检查先前调用 read_bbt 的结果,并在必要时创建/更新 bbt。如果未找到芯片/设备的 bbt,则需要创建。如果缺少其中一个表或一个表的版本号小于另一个表,则需要更新。

int nand_update_bbt(struct nand_chip *this, loff_t offs)

更新坏块表

参数

struct nand_chip *this

NAND 设备

loff_t offs

新标记的块的偏移量

描述

该函数更新坏块表。

void mark_bbt_region(struct nand_chip *this, struct nand_bbt_descr *td)

[通用] 标记坏块表区域

参数

struct nand_chip *this

NAND 设备

struct nand_bbt_descr *td

坏块表描述符

描述

坏块表区域被标记为“坏”,以防止意外擦除/写入。这些区域由标记 0x02 标识。

void verify_bbt_descr(struct nand_chip *this, struct nand_bbt_descr *bd)

验证坏块描述。

参数

struct nand_chip *this

NAND 设备

struct nand_bbt_descr *bd

要验证的表。

描述

此函数对坏块描述表执行一些健全性检查。

int nand_scan_bbt(struct nand_chip *this, struct nand_bbt_descr *bd)

[NAND接口]扫描,查找,读取,并可能创建坏块表。

参数

struct nand_chip *this

NAND 设备

struct nand_bbt_descr *bd

好/坏块搜索模式的描述符

描述

该函数检查是否已存在坏块表。如果不存在,它会扫描设备中由制造商标记的好/坏块,并将坏块表写入选定的位置。

坏块表内存在此处分配。必须通过调用 `nand_free_bbt` 函数来释放。

int nand_create_badblock_pattern(struct nand_chip *this)

[内部]创建 BBT 描述符结构。

参数

struct nand_chip *this

要为其创建描述符的 NAND 芯片。

描述

此函数根据 this 的属性分配并初始化一个用于 BBM 检测的 `nand_bbt_descr`。新的描述符存储在 `this->badblock_pattern` 中。因此,传递给此函数时,`this->badblock_pattern` 应该为 NULL。

int nand_isreserved_bbt(struct nand_chip *this, loff_t offs)

[NAND接口]检查一个块是否被保留。

参数

struct nand_chip *this

NAND 芯片对象

loff_t offs

设备中的偏移量。

int nand_isbad_bbt(struct nand_chip *this, loff_t offs, int allowbbt)

[NAND接口]检查一个块是否为坏块。

参数

struct nand_chip *this

NAND 芯片对象

loff_t offs

设备中的偏移量。

int allowbbt

允许访问坏块表区域。

int nand_markbad_bbt(struct nand_chip *this, loff_t offs)

[NAND接口]在BBT中将一个块标记为坏块。

参数

struct nand_chip *this

NAND 芯片对象

loff_t offs

坏块的偏移量。

贡献者

以下人员为 NAND 驱动程序做出了贡献

  1. Steven J. Hillsjhill@realitydiluted.com

  2. David Woodhousedwmw2@infradead.org

  3. Thomas Gleixnertglx@linutronix.de

许多用户为测试提供了错误修复、改进和帮助。非常感谢。

以下人员为此文档做出了贡献

  1. Thomas Gleixnertglx@linutronix.de