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这三个类…...
Python爬虫实战:研究MechanicalSoup库相关技术
一、MechanicalSoup 库概述 1.1 库简介 MechanicalSoup 是一个 Python 库,专为自动化交互网站而设计。它结合了 requests 的 HTTP 请求能力和 BeautifulSoup 的 HTML 解析能力,提供了直观的 API,让我们可以像人类用户一样浏览网页、填写表单和提交请求。 1.2 主要功能特点…...
从零实现富文本编辑器#5-编辑器选区模型的状态结构表达
先前我们总结了浏览器选区模型的交互策略,并且实现了基本的选区操作,还调研了自绘选区的实现。那么相对的,我们还需要设计编辑器的选区表达,也可以称为模型选区。编辑器中应用变更时的操作范围,就是以模型选区为基准来…...
Oracle查询表空间大小
1 查询数据库中所有的表空间以及表空间所占空间的大小 SELECTtablespace_name,sum( bytes ) / 1024 / 1024 FROMdba_data_files GROUP BYtablespace_name; 2 Oracle查询表空间大小及每个表所占空间的大小 SELECTtablespace_name,file_id,file_name,round( bytes / ( 1024 …...
多场景 OkHttpClient 管理器 - Android 网络通信解决方案
下面是一个完整的 Android 实现,展示如何创建和管理多个 OkHttpClient 实例,分别用于长连接、普通 HTTP 请求和文件下载场景。 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas…...

DAY 47
三、通道注意力 3.1 通道注意力的定义 # 新增:通道注意力模块(SE模块) class ChannelAttention(nn.Module):"""通道注意力模块(Squeeze-and-Excitation)"""def __init__(self, in_channels, reduction_rat…...

MMaDA: Multimodal Large Diffusion Language Models
CODE : https://github.com/Gen-Verse/MMaDA Abstract 我们介绍了一种新型的多模态扩散基础模型MMaDA,它被设计用于在文本推理、多模态理解和文本到图像生成等不同领域实现卓越的性能。该方法的特点是三个关键创新:(i) MMaDA采用统一的扩散架构…...
MVC 数据库
MVC 数据库 引言 在软件开发领域,Model-View-Controller(MVC)是一种流行的软件架构模式,它将应用程序分为三个核心组件:模型(Model)、视图(View)和控制器(Controller)。这种模式有助于提高代码的可维护性和可扩展性。本文将深入探讨MVC架构与数据库之间的关系,以…...

自然语言处理——循环神经网络
自然语言处理——循环神经网络 循环神经网络应用到基于机器学习的自然语言处理任务序列到类别同步的序列到序列模式异步的序列到序列模式 参数学习和长程依赖问题基于门控的循环神经网络门控循环单元(GRU)长短期记忆神经网络(LSTM)…...
Device Mapper 机制
Device Mapper 机制详解 Device Mapper(简称 DM)是 Linux 内核中的一套通用块设备映射框架,为 LVM、加密磁盘、RAID 等提供底层支持。本文将详细介绍 Device Mapper 的原理、实现、内核配置、常用工具、操作测试流程,并配以详细的…...

【分享】推荐一些办公小工具
1、PDF 在线转换 https://smallpdf.com/cn/pdf-tools 推荐理由:大部分的转换软件需要收费,要么功能不齐全,而开会员又用不了几次浪费钱,借用别人的又不安全。 这个网站它不需要登录或下载安装。而且提供的免费功能就能满足日常…...