u-boot: NAND 驱动简介
文章目录
- 1. 前言
- 2. NAND 初始化
- 3. 访问 NAND 设备
- 3.1 查看 NAND 设备信息
- 3.1.1 查看 NAND 设备基本信息
- 3.1.2 查看 NAND 设备 MTD 分区
- 3.1.3 查看 NAND 设备坏块
- 3.2 NAND 擦除操作
- 3.3 NAND 写操作
- 3.4 NAND 读操作
- 3.5 其它 NAND 操作
1. 前言
限于作者能力水平,本文可能存在谬误,因此而给读者带来的损失,作者不做任何承诺。
2. NAND 初始化
下面以 Micron MT29F2G08AAD 型号的 Nand Flash 为例,说明 ARMv7 架构下 U-Boot 对 Nand Flash 设备的初始化过程。首先,可以将相关代码划分为 U-Boot NAND 驱动硬件无关通用部分 和 硬件相关的 NAND FLASH 以及其 控制器 驱动 两部分。另外,抽象层次上,U-Boot 将 Nor Flash,Nand Flash 等类型设备,统一抽象为 MTD(Memory Technology Device) 类型设备,对这些类型设备的访问,都是通过 MTD 接口来间接进行。
board_init_r() /* common/board_r.c *//* 特定 板型 初始化:这里重点关注 NAND 控制器 的 初始化 */board_init() /* board/myirtech/myd_c335x/myd_c335x.c */...gpmc_init();/* putting a blanket check on GPMC based on ZeBu for now */gpmc_cfg = (struct gpmc *)GPMC_BASE;/* NAND 控制器 的一些寄存器配置 */...initr_nand()nand_init() /* drivers/mtd/nand/nand.c */nand_init_chip()struct mtd_info *mtd;#ifndef CONFIG_DM_NANDstruct nand_chip *nand = &nand_chip[i];ulong base_addr = base_address[i];#endif...mtd = &nand_info[i]; /* NAND 设备 的 MTD 数据对象 */mtd->priv = nand; /* MTD NAND 设备数据 */nand->IO_ADDR_R = nand->IO_ADDR_W = (void __iomem *)base_addr; /* NAND 设备 IO 地址空间 *//* 1. 特定 板型 的 NAND 初始化 */if (board_nand_init(nand)) /* drivers/mtd/nand/omap_gpmc.c */return;/* 2. 扫描识别并配置 NAND 控制器 上 挂接的 NAND 设备 */if (nand_scan(mtd, maxchips))return;/* 3. 注册 扫描到的、挂接在 NAND 控制器 上 NAND 设备 */nand_register(i);/* 1. 特定 板型 的 NAND 初始化 */
board_nand_init(nand).../** xloader/Uboot's gpmc configuration would have configured GPMC for* nand type of memory. The following logic scans and latches on to the* first CS with NAND type memory.* TBD: need to make this logic generic to handle multiple CS NAND* devices.*/while (cs < GPMC_MAX_CS) {/* Check if NAND type is set */if ((readl(&gpmc_cfg->cs[cs].config1) & 0xC00) == 0x800) {/* Found it!! */break;}cs++;}...nand->IO_ADDR_R = (void __iomem *)&gpmc_cfg->cs[cs].nand_dat;nand->IO_ADDR_W = (void __iomem *)&gpmc_cfg->cs[cs].nand_cmd;...nand->priv = &omap_nand_info[cs];nand->cmd_ctrl = omap_nand_hwcontrol;nand->options |= NAND_NO_PADDING | NAND_CACHEPRG;nand->chip_delay = 100;nand->ecc.layout = &omap_ecclayout; /* NAND ECC 数据 layout */...nand->options &= ~NAND_BUSWIDTH_16; /* 8 位数据宽度 */.../* select ECC scheme */
#if defined(CONFIG_NAND_OMAP_ECCSCHEME)/* NAND ECC 模式选择 */err = omap_select_ecc_scheme(nand, CONFIG_NAND_OMAP_ECCSCHEME,CONFIG_SYS_NAND_PAGE_SIZE, CONFIG_SYS_NAND_OOBSIZE);...switch (ecc_scheme) {...case OMAP_ECC_BCH8_CODE_HW: /* ECC 使用 硬件 BCH8 码 */#ifdef CONFIG_NAND_OMAP_ELM.../* intialize ELM for ECC error detection */elm_init(); /* ECC 错误检测硬件模块 ELM 初始化 */.../* populate ecc specific fields */nand->ecc.mode = NAND_ECC_HW; /* 硬件 ECC */nand->ecc.strength = 8;nand->ecc.size = SECTOR_BYTES;nand->ecc.bytes = 14;/* ECC 操作接口 */nand->ecc.hwctl = omap_enable_hwecc;nand->ecc.correct = omap_correct_data_bch;nand->ecc.calculate = omap_calculate_ecc;nand->ecc.read_page = omap_read_page_bch;...#else...#endif...}...info->ecc_scheme = ecc_scheme; /* OMAP_ECC_BCH8_CODE_HW */return 0;
#else...
#endif#ifdef CONFIG_NAND_OMAP_GPMC_PREFETCHnand->read_buf = omap_nand_read_prefetch;
#else...
#endifnand->dev_ready = omap_dev_ready;return 0;/* 2. 扫描识别并配置 NAND 控制器 上 挂接的 NAND 设备 */
nand_scan(mtd, maxchips) /* drivers/mtd/nand/nand_base.c */.../* 扫描识别 NAND 设备 和 参数, 设置操作接口 等 */ret = nand_scan_ident(mtd, maxchips, NULL);...struct nand_chip *chip = mtd->priv;struct nand_flash_dev *type;/* Set the default functions */nand_set_defaults(chip, chip->options & NAND_BUSWIDTH_16); /* 设置 NAND 设备缺省操作接口(读写等) */.../* check, if a user supplied command function given */if (chip->cmdfunc == NULL)chip->cmdfunc = nand_command;/* check, if a user supplied wait function given */if (chip->waitfunc == NULL)chip->waitfunc = nand_wait;if (!chip->select_chip)chip->select_chip = nand_select_chip;.../* If called twice, pointers that depend on busw may need to be reset */if (!chip->read_byte || chip->read_byte == nand_read_byte)chip->read_byte = busw ? nand_read_byte16 : nand_read_byte;if (!chip->read_word)chip->read_word = nand_read_word;if (!chip->block_bad)chip->block_bad = nand_block_bad; /* 坏块 判定接口 */if (!chip->block_markbad)chip->block_markbad = nand_default_block_markbad;if (!chip->write_buf || chip->write_buf == nand_write_buf)chip->write_buf = busw ? nand_write_buf16 : nand_write_buf;if (!chip->write_byte || chip->write_byte == nand_write_byte)chip->write_byte = busw ? nand_write_byte16 : nand_write_byte;if (!chip->read_buf || chip->read_buf == nand_read_buf)chip->read_buf = busw ? nand_read_buf16 : nand_read_buf;if (!chip->scan_bbt)chip->scan_bbt = nand_default_bbt; /* 坏块 选择/建立 接口 */.../* Read the flash type *//* 识别 NAND 设备类型 */type = nand_get_flash_type(mtd, chip, &nand_maf_id,&nand_dev_id, table);...u8 id_data[8];/* Select the device */chip->select_chip(mtd, 0);/** Reset the chip, required by some chips (e.g. Micron MT29FxGxxxxx)* after power-up.*/chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); /* 复位 NAND 设备 ID *//* Send the command for reading device ID */chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); /* 发送读取 NAND 设备 ID 命令 *//* Read manufacturer and device IDs */*maf_id = chip->read_byte(mtd); /* 读取 NAND 设备 制造商 ID */*dev_id = chip->read_byte(mtd); /* 读取 NAND 设备 ID */chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); /* 读取 NAND 芯片 ID *//* Read entire ID string */for (i = 0; i < 8; i++)id_data[i] = chip->read_byte(mtd);...if (!type)type = nand_flash_ids; /* 预定义的 NAND 设备列表: drivers/mtd/nand/nand_ids.c *//** 对比 读取到的 NAND 设备 ID 和 预定义表 nand_flash_ids[], * 看是否能找到匹配的表项.*/for (; type->name != NULL; type++) {if (is_full_id_nand(type)) { /* 对于 全 ID 标识的设备, 进行全 ID 匹配 */if (find_full_id_nand(mtd, chip, type, id_data, &busw)) /* 如果 是 设备全 ID 匹配, */goto ident_done; /* 完成识别工作 */} else if (*dev_id == type->dev_id) { /* 如果 不是 设备全 ID 匹配, 而是 设备 ID 匹配 */break; /* 做进一步的识别工作(如符合 ONFI/JEDEC 规范的设备匹配工作) */}}/* 可能满足 ONFI/JEDEC 规范 的 设备 识别 */chip->onfi_version = 0;if (!type->name || !type->pagesize) {/* Check if the chip is ONFI compliant */if (nand_flash_detect_onfi(mtd, chip, &busw)) /* 识别到 符合 ONFI 接口规范 的 NAND 设备 */goto ident_done; /* 完成识别工作 *//* Check if the chip is JEDEC compliant */if (nand_flash_detect_jedec(mtd, chip, &busw)) /* 识别到 符合 JEDEC 接口规范 的 NAND 设备 */goto ident_done;}/* * 目前无法识别设备:* . 无法从预定义 NAND 芯片列表 nand_flash_ids[] 匹配设备* . 设备不符合 ONFI/JEDEC 接口规范*/if (!type->name)return ERR_PTR(-ENODEV);if (!mtd->name)mtd->name = type->name; /* 设置 MTD NAND 设备名称 *//* 设置 NAND 设备容量 */chip->chipsize = (uint64_t)type->chipsize << 20;/* 设置 NAND 的 page size, OOB size, erase size */if (!type->pagesize && chip->init_size) {/* Set the pagesize, oobsize, erasesize by the driver */busw = chip->init_size(mtd, chip, id_data);} else if (!type->pagesize) {/* Decode parameters from extended ID */nand_decode_ext_id(mtd, chip, id_data, &busw);} else {nand_decode_id(mtd, chip, type, id_data, &busw);}/* Get chip options */chip->options |= type->options; /* 设置 NAND 设备选项 */...ident_done:.../* 坏块管理相关配置 */nand_decode_bbm_options(mtd, chip, id_data);int maf_id = id_data[0];/* Set the bad block position */if (mtd->writesize > 512 || (chip->options & NAND_BUSWIDTH_16))chip->badblockpos = NAND_LARGE_BADBLOCK_POS;elsechip->badblockpos = NAND_SMALL_BADBLOCK_POS;/** Bad block marker is stored in the last page of each block on Samsung* and Hynix MLC devices; stored in first two pages of each block on* Micron devices with 2KiB pages and on SLC Samsung, Hynix, Toshiba,* AMD/Spansion, and Macronix. All others scan only the first page.*/if (!nand_is_slc(chip) &&(maf_id == NAND_MFR_SAMSUNG ||maf_id == NAND_MFR_HYNIX))/* 三星 和 海力士 的 非 SLC 类型设备, 坏块标记存储在每个 block 的最后一个 page */chip->bbt_options |= NAND_BBT_SCANLASTPAGE;else if ((nand_is_slc(chip) &&(maf_id == NAND_MFR_SAMSUNG ||maf_id == NAND_MFR_HYNIX ||maf_id == NAND_MFR_TOSHIBA ||maf_id == NAND_MFR_AMD ||maf_id == NAND_MFR_MACRONIX)) ||(mtd->writesize == 2048 &&maf_id == NAND_MFR_MICRON))/* 一些厂家的 SLC 类型设备,坏块标记存储在 block 的 第1 和 第2 个 page */chip->bbt_options |= NAND_BBT_SCAN2NDPAGE;...chip->badblockbits = 8;chip->erase = single_erase; /* 设置 擦除 接口 *//* Do not replace user supplied command function! */if (mtd->writesize > 512 && chip->cmdfunc == nand_command)chip->cmdfunc = nand_command_lp; /* 覆盖命令为 大 page 接口 *//* 报告设备信息 (需要开启 CONFIG_MTD_DEBUG) */pr_info("device found, Manufacturer ID: 0x%02x, Chip ID: 0x%02x\n",*maf_id, *dev_id);...pr_info("%d MiB, %s, erase size: %d KiB, page size: %d, OOB size: %d\n",(int)(chip->chipsize >> 20), nand_is_slc(chip) ? "SLC" : "MLC",mtd->erasesize >> 10, mtd->writesize, mtd->oobsize);return type; /* 返回是被的 NAND 设备类型 *//* 通过 NAND 控制器,选择 NAND 芯片 */chip->select_chip(mtd, -1);/* Store the number of chips and calc total size for mtd */chip->numchips = i;mtd->size = i * chip->chipsize; /* 记录 MTD NAND 设备容量 */return 0;if (!ret)/** 扫描识别收尾工作: * 从 前面 扫描识别到的 NAND 设备 和 参数,* 为 NAND 设备建立缓冲,设置 NAND 设备对应的 MTD NAND 设备对象。*/ret = nand_scan_tail(mtd);int i;struct nand_chip *chip = mtd->priv;struct nand_ecc_ctrl *ecc = &chip->ecc;struct nand_buffers *nbuf;/* 为 NAND 设备 创建 缓冲: ECC, 数据 */if (!(chip->options & NAND_OWN_BUFFERS)) {nbuf = kzalloc(sizeof(struct nand_buffers), GFP_KERNEL);chip->buffers = nbuf;} else {if (!chip->buffers)return -ENOMEM;}/* Set the internal oob buffer location, just after the page data *//* 和 NAND 页面存储一样, OOB 缓冲 紧邻 page 缓冲之后 */chip->oob_poi = chip->buffers->databuf + mtd->writesize;...switch (ecc->mode) {...case NAND_ECC_HW: /* 设置没有配置的 ECC 接口 */if (!ecc->read_page)ecc->read_page = nand_read_page_hwecc;if (!ecc->write_page)ecc->write_page = nand_write_page_hwecc;if (!ecc->read_page_raw)ecc->read_page_raw = nand_read_page_raw;if (!ecc->write_page_raw)ecc->write_page_raw = nand_write_page_raw;if (!ecc->read_oob)ecc->read_oob = nand_read_oob_std;if (!ecc->write_oob)ecc->write_oob = nand_write_oob_std;if (!ecc->read_subpage)ecc->read_subpage = nand_read_subpage;if (!ecc->write_subpage)ecc->write_subpage = nand_write_subpage_hwecc;...}/* 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;// 一些其它 ECC 相关配置.../* Initialize state */chip->state = FL_READY; /* NAND 设备标记为 READY 状态 */.../* Fill in remaining MTD driver data */mtd->type = nand_is_slc(chip) ? MTD_NANDFLASH : MTD_MLCNANDFLASH; /* 设置 MTD NAND 设备类型: SLC 或 MLC */mtd->flags = (chip->options & NAND_ROM) ? MTD_CAP_ROM :MTD_CAP_NANDFLASH; /* MTD NAND 读写属性设置:只读、可读写 *//* 设置 MTD NAND 设备接口: 调用 NAND 设备接口 */mtd->_erase = nand_erase;mtd->_read = nand_read;mtd->_write = nand_write;mtd->_panic_write = panic_nand_write;mtd->_read_oob = nand_read_oob;mtd->_write_oob = nand_write_oob;mtd->_sync = nand_sync;mtd->_lock = NULL;mtd->_unlock = NULL;mtd->_block_isreserved = nand_block_isreserved;mtd->_block_isbad = nand_block_isbad;mtd->_block_markbad = nand_block_markbad;mtd->writebufsize = mtd->writesize;// MTD NAND ECC 相关设置...return 0;return ret;/* 3. 注册 扫描到的、挂接在 NAND 控制器 上 NAND 设备 */
nand_register(i);struct mtd_info *mtd;mtd = get_nand_dev_by_index(devnum);sprintf(dev_name[devnum], "nand%d", devnum);mtd->name = dev_name[devnum];#ifdef CONFIG_MTD_DEVICE/** Add MTD device so that we can reference it later* via the mtdcore infrastructure (e.g. ubi).*/add_mtd_device(mtd);
#endiftotal_nand_size += mtd->size / 1024;if (nand_curr_device == -1)nand_curr_device = devnum; /* 设置当前 NAND 设备编号 */return 0;
3. 访问 NAND 设备
3.1 查看 NAND 设备信息
3.1.1 查看 NAND 设备基本信息
U-Boot 提供一些 nand info 命令,可以查看 NAND 设备信息:
# nand infoDevice 0: nand0, sector size 128 KiBPage size 2048 bOOB size 64 bErase size 131072 bsubpagesize 512 boptions 0x4000000cbbt options 0x 8000
从上面看到,Nand Flash 设备:
o page 是 2KB 大小
o page 后跟的 OOB(Spare area) 是 64 Bytes
o 擦除 size 是 128KB,也就是 block size
3.1.2 查看 NAND 设备 MTD 分区
前面有说过,U-Boot 将 NAND 抽象为 MTD 设备进行访问,通过 mtdparts 命令,可以查看 NAND 设备的分区信息:
# mtdpartsdevice nand0 <nand.0>, # parts = 11#: name size offset mask_flags0: NAND.SPL 0x00020000 0x00000000 01: NAND.SPL.backup1 0x00020000 0x00020000 02: NAND.SPL.backup2 0x00020000 0x00040000 03: NAND.SPL.backup3 0x00020000 0x00060000 04: NAND.u-boot-spl-os 0x00040000 0x00080000 05: NAND.u-boot 0x00100000 0x000c0000 06: NAND.u-boot-env 0x00020000 0x001c0000 07: NAND.u-boot-env.backup10x00020000 0x001e0000 08: NAND.kernel 0x00800000 0x00200000 09: NAND.rootfs 0x0d600000 0x00a00000 0
10: NAND.userdata 0x02000000 0x0e000000 0active partition: nand0,0 - (NAND.SPL) 0x00020000 @ 0x00000000defaults:
mtdids : nand0=nand.0
mtdparts: mtdparts=nand.0:128k(NAND.SPL),128k(NAND.SPL.backup1),128k(NAND.SPL.backup2),128k(NAND.SPL.backup3),256k(NAND.u-boot-spl-os),1m(NAND.u-boot),128k(NAND.u-boot-env),128k(NAND.u-boot-env.backup1),8m(NAND.kernel),214m(NAND.rootfs),-(NAND.userdata)
3.1.3 查看 NAND 设备坏块
# nand badDevice 0 bad blocks:03e40000086c0000
发现了两个坏块,数据标记了它们的字节偏移位置。
3.2 NAND 擦除操作
通过 nand erase 命令,可以对 NAND 发起对 NAND 的擦除操作:
# nand erase c0000 100000
其中参数 c0000 是要擦除的起始位置,相对于 NAND 设备开始位置的字节偏移;100000 是要擦除的长度字节数。这两个参数都是十六进制数字。来看一下擦除过程的具体实现:
do_nand() /* cmd/nand.c */...nand_info_t *nand;...int dev = nand_curr_device; /* 默认选择当前 NAND 设备进行操作 */......cmd = argv[1]; /* "erase" */...nand = get_nand_dev_by_index(dev); /* 获取 NAND 设备 */if (strncmp(cmd, "erase", 5) == 0 || strncmp(cmd, "scrub", 5) == 0) {nand_erase_options_t opts;......printf("\nNAND %s: ", cmd); /* "NAND erase: " *//* 解析 擦除起始位置 和 长度 参数 到 @off 和 @size */if (mtd_arg_off_size(argc - o, argv + o, &dev, &off, &size,&maxsize, MTD_DEV_TYPE_NAND,nand->size) != 0)return 1;/* 切换到 要操作 的 目标 NAND 设备 */if (set_dev(dev))return 1;nand = get_nand_dev_by_index(dev);memset(&opts, 0, sizeof(opts));opts.offset = off;opts.length = size;...ret = nand_erase_opts(nand, &opts); /* 擦除操作 */...}ret = nand_erase_opts(nand, &opts); /* drivers/mtd/nand/nand_util.c */.../** @erase_length:要擦除的 block 数目* @erased_length: 已经擦除的 block 数目*/unsigned long erase_length, erased_length; /* in blocks */...erase_length = lldiv(opts->length + meminfo->erasesize - 1,meminfo->erasesize); /* 向上对齐到 erase size (block 大小) */...for (erased_length = 0;erased_length < erase_length;erase.addr += meminfo->erasesize) {...if (!opts->scrub) {/* 检查位于 @ofs 位置的 block 是不是坏块 */int ret = mtd_block_isbad(meminfo, erase.addr);if (ret > 0) { /* 坏块 */...if (!opts->spread)erased_length++; /* 非 nand erase.spread 命令, 坏块也计入擦除 block 数目 */continue; /* 跳过坏块: 坏块不做擦除动作,擦除坏块是非法操作 */}}erased_length++; /* 已擦除 block 数目 +1 */result = mtd_erase(meminfo, &erase); /* 擦除当前块 */...return mtd->_erase(mtd, instr);nand_erase()return nand_erase_nand(mtd, instr, 0);...struct nand_chip *chip = mtd->priv; /* MTD 转入 NAND 层操作 */.../* Grab the lock and see if the device is available */nand_get_device(mtd, FL_ERASING);/* Shift to get first page */page = (int)(instr->addr >> chip->page_shift); /* 擦除位置转换为 page 位置 */chipnr = (int)(instr->addr >> chip->chip_shift);/* Calculate pages in each block */pages_per_block = 1 << (chip->phys_erase_shift - chip->page_shift);/* Select the NAND device */chip->select_chip(mtd, chipnr);/* Loop through the pages */len = instr->len;instr->state = MTD_ERASING;while (len) {...status = chip->erase(mtd, page & chip->pagemask);single_erase()struct nand_chip *chip = mtd->priv;/* Send commands to erase a block *//* 发送 page 擦除操作命令 */chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page);chip->cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1);/* 等待擦除操作完成 */return chip->waitfunc(mtd, chip);/* Increment page address and decrement length *//* 移向下一个 page */len -= (1ULL << chip->phys_erase_shift);page += pages_per_block;...}instr->state = MTD_ERASE_DONE;...}
3.3 NAND 写操作
假定以命令 nand write 0x82000000 c0000 ${filesize} 发起写操作。写操作基本流程和擦除操作差不多,来看细节:
do_nand()...if (strncmp(cmd, "read", 4) == 0 || strncmp(cmd, "write", 5) == 0) {...addr = (ulong)simple_strtoul(argv[2], NULL, 16); // 0x82000000read = strncmp(cmd, "read", 4) == 0; /* 1 = read, 0 = write */printf("\nNAND %s: ", read ? "read" : "write");s = strchr(cmd, '.');if (s && !strcmp(s, ".raw")) { // nand write.raw...} else {// @off = 0xc0000// @size = ${filesize}if (mtd_arg_off_size(argc - 3, argv + 3, &dev, &off,&size, &maxsize,MTD_DEV_TYPE_NAND,nand->size) != 0)return 1;if (set_dev(dev))return 1;...rwsize = size;if (!s || !strcmp(s, ".jffs2") ||!strcmp(s, ".e") || !strcmp(s, ".i")) {if (read)ret = nand_read_skip_bad(nand, off, &rwsize,NULL, maxsize,(u_char *)addr); /* 读取,会自动跳过坏块 */elseret = nand_write_skip_bad(nand, off, &rwsize,NULL, maxsize,(u_char *)addr,WITH_WR_VERIFY); /* 写入,会自动跳过坏块 */}}nand = get_nand_dev_by_index(dev);}nand_write_skip_bad()...size_t left_to_write = *length; /* 要写入的长度, 字节数 */...int need_skip;blocksize = nand->erasesize;/* 检查写入过程中, 要跳过的坏块数目 */need_skip = check_skip_len(nand, offset, *length, &used_for_write);...while (left_to_write > 0) {size_t block_offset = offset & (nand->erasesize - 1); /* 计算 block 内偏移 */.../* 写入时跳过坏块 */if (nand_block_isbad(nand, offset & ~(nand->erasesize - 1))) {printf("Skip bad block 0x%08llx\n",offset & ~(nand->erasesize - 1));offset += nand->erasesize - block_offset;continue;}/* 调整 写入 大小 */if (left_to_write < (blocksize - block_offset))write_size = left_to_write;elsewrite_size = blocksize - block_offset;/* 写入 block *//* * 注意前后的两个 nand_write() 不是同一个函数: * . 前一个 nand_write() 是 MTD 驱动层的 接口* . 后一个是 NAND 驱动层的 接口*/rval = nand_write(nand, offset, &truncated_write_size, p_buffer); /* drivers/mtd/mtcore.c */mtd_write()mtd->_write(mtd, to, len, retlen, buf);nand_write() /* drivers/mtd/nand/nand_base.c */struct mtd_oob_ops ops;nand_get_device(mtd, FL_WRITING);memset(&ops, 0, sizeof(ops));ops.len = len;ops.datbuf = (uint8_t *)buf;ops.mode = MTD_OPS_PLACE_OOB;ret = nand_do_write_ops(mtd, to, &ops); /* NAND 设备写操作 */*retlen = ops.retlen;nand_release_device(mtd);return ret;/* 写入后,再读回来验证一下数据是否正确 */if ((flags & WITH_WR_VERIFY) && !rval)rval = nand_verify(nand, offset,truncated_write_size, p_buffer);offset += write_size;p_buffer += write_size;...left_to_write -= write_size;}ret = nand_do_write_ops(mtd, to, &ops); /* NAND 设备写操作 */...struct nand_chip *chip = mtd->priv;uint32_t writelen = ops->len;......while (1) {int bytes = mtd->writesize; /* 按 page 写入 */...uint8_t *wbuf = buf; /* 数据 */...if (unlikely(oob)) {...} else {/* We still need to erase leftover OOB data */memset(chip->oob_poi, 0xff, mtd->oobsize); /* 没有指定 OOB 数据, 则将 Spare area 全部填充 0xff */}ret = chip->write_page(mtd, chip, column, bytes, wbuf,oob_required, page, cached,(ops->mode == MTD_OPS_RAW)); /* 写入一个 page 的数据 */nand_write_page()...chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page);if (unlikely(raw))status = chip->ecc.write_page_raw(mtd, chip, buf, oob_required);else if (subpage)status = chip->ecc.write_subpage(mtd, chip, offset, data_len, buf, oob_required);else /* 这里只考虑这一种情形 */status = chip->ecc.write_page(mtd, chip, buf, oob_required);nand_write_page_hwecc()int i, eccsize = chip->ecc.size;int eccbytes = chip->ecc.bytes;int eccsteps = chip->ecc.steps;uint8_t *ecc_calc = chip->buffers->ecccalc;const uint8_t *p = buf; /* 要写入的数据 */uint32_t *eccpos = chip->ecc.layout->eccpos;/* 写数据到 NAND, 每次写 @eccsize 个字节 (@eccsize 是计算 ECC 的单元) */for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {chip->ecc.hwctl(mtd, NAND_ECC_WRITE);chip->write_buf(mtd, p, eccsize); /* 将数据写入到 NAND */chip->ecc.calculate(mtd, p, &ecc_calc[i]); /* 计算数据 @p 的 ECC, 记录到 ecc_calc[i] */}for (i = 0; i < chip->ecc.total; i++)chip->oob_poi[eccpos[i]] = ecc_calc[i];chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); /* 将 ECC 数据写入到 Spare area */return 0;/* 写完一个 page */cached = 0;if (!cached || !NAND_HAS_CACHEPROG(chip)) {chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);status = chip->waitfunc(mtd, chip);} else {...}return 0;...writelen -= bytes;if (!writelen)break;...}...
3.4 NAND 读操作
可以通过 nand read 0x82000000 200000 800000 发起对 NAND 的读操作:将 NAND 从 0x200000 位置开始的 0x800000 个字节,读取到内存地址 0x82000000 开始的位置。NAND 读操作入口函数为 nand_read_skip_bad() ,其逻辑和 nand_write_skip_bad() 非常相似,在此不再赘述。值得一提的是,nand_read_skip_bad() 读操作,会跳过坏块。
3.5 其它 NAND 操作
nand device // 输出当前 NAND 设备信息
nand device <dev> // 切换到 NAND 设备 dev
nand dump[.oob] <offset> // 导出 NAND page 和 OOB, 带 .oob 后缀仅导出 OOB
......相关文章:
u-boot: NAND 驱动简介
文章目录 1. 前言2. NAND 初始化3. 访问 NAND 设备3.1 查看 NAND 设备信息3.1.1 查看 NAND 设备基本信息3.1.2 查看 NAND 设备 MTD 分区3.1.3 查看 NAND 设备坏块 3.2 NAND 擦除操作3.3 NAND 写操作3.4 NAND 读操作3.5 其它 NAND 操作 1. 前言 限于作者能力水平,本…...
史上最全的大数据开发八股文【自己的吐血总结】
自我介绍 我本硕都是双非计算机专业,从研一下开始学习大数据开发的相关知识,从找实习到秋招,我投递过100公司,拿到过10的offer,包括滴滴、字节、蚂蚁、携程、蔚来、去哪儿等大厂(岗位都是大数据开发&#…...
数据库学习案例20240304-mysql数据库案例总结(碎片,统计信息)
1 表中的碎片 在InnoDB中删除行的时候,这些行只是被标记为“已删除”,而不是真正从物理存储上进行了删除,因而存储空间也没有真正被释放回收。InnoDB的Purge线程会异步地来清理这些没用的索引键和行。但是依然没有把这些释放出来的空间还给操…...
【小白友好】LeetCode 删除并获得点数
基础题 打家劫舍https://leetcode.cn/problems/house-robber/ 小白解法 删除nums[i]就会使得所有nums[i]-1和nums[i]1的值都消失,手写了几个,发现找来找去不方便,还不如先排个序,然后这样nums[i]-1和nums[i]和nums[i]1就能靠在…...
c#委托、lambda、事件
Lambda Lambda表达式是一种匿名函数,Lambda表达式通常以箭头“>”分隔左侧的输入和右侧的输出。 (parameter_list) > { statement_block } parameter_list 是由一个或多个参数组成的逗号分隔列表,每个参数都包括类型和名称,可以为空。…...
每日一练——9×9乘法表
#include<stdio.h>int main() {int i 0; //乘数定义for (i 1; i < 9; i) //循环1到9 {int j 0;//被乘数定义for (j 1; j < i; j) //循环被乘数1到9{printf("%d*%d%2d ", i, j, i * j); 乘法}printf("\n"); 换行} return 0; }...
大白话解析LevelDB:ShardedLRUCache
文章目录 Cache 接口定义ShardedLRUCache 的实现ShardedLRUCache 的构造函数ShardedLRUCache::Insert(const Slice& key, void* value, size_t charge, void (\*deleter)(const Slice& key, void* value))ShardedLRUCache::Lookup(const Slice& key)ShardedLRUCach…...
GDOI2024游记
Day0 中午一点钟从学校出发去东莞,大概坐了一个多小时车,两点半多到酒店。住的八方精选酒店(ljh说他们住九方精选酒店,乐),说的是景区酒店,但打开外窗,近处是简陋的阳台,…...
学编程怎么样才能更快入手,编程怎么简单易学
学编程怎么样才能更快入手,编程怎么简单易学 一、前言 对于初学编程建议先从简单入手,然后再学习其他复杂的编程语言。 今天给大家分享的中文编程开发语言工具 进度条构件的用法。 编程入门视频教程链接 https://edu.csdn.net/course/detail/39036 …...
Android 通知--判断通知是否有跳转
一. 从应用层来分析 在 Android 中,可以通过 PendingIntent 来实现有跳转的通知和没有跳转的通知的区别。具体来说,有跳转的通知会设置一个 PendingIntent,当用户点击通知时会触发该 PendingIntent,打开指定的界面或执行特…...
【计算机网络】IO多路转接之poll
文章目录 一、poll函数接口二、socket就绪条件三、poll的优点四、poll的缺点五、poll使用案例--只读取数据的server服务器1.err.hpp2.log.hpp3.sock.hpp4.pollServer.hpp5.main.cc 一、poll函数接口 #include <poll.h> int poll(struct pollfd *fds, nfds_t nfds, int t…...
性能比较:in和exists
当在Hive SQL中使用NOT IN和NOT EXISTS时,性能差异主要取决于底层数据的组织方式、数据量大小、索引的使用情况以及具体查询的复杂程度。下面是对这两种方法的性能分析: 1. NOT IN:- 工作原理:NOT IN子查询会逐个比较主查询中的值…...
【Java设计模式】五、建造者模式
文章目录 1、建造者模式2、案例:共享单车的创建3、其他用途 1、建造者模式 某个对象的构建复杂将复杂的对象的创建 和 属性赋值所分离,使得同样的构建过程可以创建不同的表示建造的过程和细节调用者不需要知道,只需要通过构建者去进行操作 …...
nginx代理minio教程 避坑过的教程 避开SignatureDoesNotMatch
本次教程使用的是单机minio进行演示,集群minio也和这个差不多。 按照这个教程,可以避开nginx代理minio之后,只能访问文件,但是通过预签名url上传文件就会报SignatureDoesNotMatch的坑 暂定如下: 你已经下载好miniom…...
Linux进程详细介绍
文章目录 Linux进程1、计算机体系结构和操作系统管理1.1、计算机体系结构 -- 硬件1.2、操作系统(Operator System) -- 软件 2、进程2.1、进程基本概念2.2、进程标识符2.2.1、获取当前进程标识符和当前进程的父进程标识符2.2.2、通过系统调用创建进程 -- …...
2024年3月产品认证基础考试简答题及答案
产品认证基础 46.产品认证的工厂检查有哪几种路线?各有什么优缺点? 答案:两种常用的检查路线: 1.按照要素或过程检查 按照认证规则规定的工厂应满足的要素要求(包括质量保证能力要求),结合部…...
嵌入式蓝桥杯做题总结
第十二届省赛 按键代码 ——自认为比较巧妙,定时器3被设置为10ms进入一次中断,代替了HAL_Delay(10)的方法消抖; 运用状态机机思想实现检测多个按键检测——且分为两个状态,其中一个状态PB1和PB2的按键不…...
Spring Boot 常用注解大全
以下是Spring Boot中常用的注解及其详细解释以及相应的代码示例: SpringBootApplication: 这个注解用于标识一个Spring Boot应用的主类。它整合了 Configuration,EnableAutoConfiguration 和 ComponentScan。 SpringBootApplication public class Demo…...
(MATLAB)第十二章-数列与极限
目录 12.1 数列 12.1.1 数列求和 1. 累计求和函数sum() 2. 忽略NaN累计求和函数 nansum() 3. 求此元素位置之前的元素和函数cumsum() 4. 求梯形累计和函数cumtrapz() 12.1.2 数列求积 1. 元素连续相乘函数 prod() 2. 求累计积函数 cumprod() 3. 阶乘函数 ffactorial(n…...
OJ输入问题+准备
写在之前: 发现题目输入是这样的: 我的问题:如何通过空格分割这些输入的字符串并分别保存!!(C语言scanf好解决一点但我选择C....) C引入了ostringstream、istringstream、stringstream这三个类…...
React第五十七节 Router中RouterProvider使用详解及注意事项
前言 在 React Router v6.4 中,RouterProvider 是一个核心组件,用于提供基于数据路由(data routers)的新型路由方案。 它替代了传统的 <BrowserRouter>,支持更强大的数据加载和操作功能(如 loader 和…...
汽车生产虚拟实训中的技能提升与生产优化
在制造业蓬勃发展的大背景下,虚拟教学实训宛如一颗璀璨的新星,正发挥着不可或缺且日益凸显的关键作用,源源不断地为企业的稳健前行与创新发展注入磅礴强大的动力。就以汽车制造企业这一极具代表性的行业主体为例,汽车生产线上各类…...
Java - Mysql数据类型对应
Mysql数据类型java数据类型备注整型INT/INTEGERint / java.lang.Integer–BIGINTlong/java.lang.Long–––浮点型FLOATfloat/java.lang.FloatDOUBLEdouble/java.lang.Double–DECIMAL/NUMERICjava.math.BigDecimal字符串型CHARjava.lang.String固定长度字符串VARCHARjava.lang…...
Qwen3-Embedding-0.6B深度解析:多语言语义检索的轻量级利器
第一章 引言:语义表示的新时代挑战与Qwen3的破局之路 1.1 文本嵌入的核心价值与技术演进 在人工智能领域,文本嵌入技术如同连接自然语言与机器理解的“神经突触”——它将人类语言转化为计算机可计算的语义向量,支撑着搜索引擎、推荐系统、…...
[10-3]软件I2C读写MPU6050 江协科技学习笔记(16个知识点)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16...
第一篇:Agent2Agent (A2A) 协议——协作式人工智能的黎明
AI 领域的快速发展正在催生一个新时代,智能代理(agents)不再是孤立的个体,而是能够像一个数字团队一样协作。然而,当前 AI 生态系统的碎片化阻碍了这一愿景的实现,导致了“AI 巴别塔问题”——不同代理之间…...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一)
宇树机器人多姿态起立控制强化学习框架论文解析 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一) 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化…...
C++.OpenGL (10/64)基础光照(Basic Lighting)
基础光照(Basic Lighting) 冯氏光照模型(Phong Lighting Model) #mermaid-svg-GLdskXwWINxNGHso {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-GLdskXwWINxNGHso .error-icon{fill:#552222;}#mermaid-svg-GLd…...
华为OD机试-最短木板长度-二分法(A卷,100分)
此题是一个最大化最小值的典型例题, 因为搜索范围是有界的,上界最大木板长度补充的全部木料长度,下界最小木板长度; 即left0,right10^6; 我们可以设置一个候选值x(mid),将木板的长度全部都补充到x,如果成功…...
通过 Ansible 在 Windows 2022 上安装 IIS Web 服务器
拓扑结构 这是一个用于通过 Ansible 部署 IIS Web 服务器的实验室拓扑。 前提条件: 在被管理的节点上安装WinRm 准备一张自签名的证书 开放防火墙入站tcp 5985 5986端口 准备自签名证书 PS C:\Users\azureuser> $cert New-SelfSignedCertificate -DnsName &…...
