uboot2021.10-nandflash-3.initr_nand

本文详细介绍了S5PV210平台NAND Flash的初始化过程,包括nand_init_chip和board_nand_init函数,着重讲解了ecc硬件控制和软件实现,以及MTD驱动的初始化步骤。通过设置函数指针,确保了ecc的正确处理和硬件控制功能的实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这次梳理一下nandflash的初始化过程。

1. 初始化入口

2. nand的初始化,这里主要是两个函数

3. nand_init_chip函数

4. board_nand_init 函数,有参数,这个函数我是自己根据实际改的。

包含硬件接口的初始化,还有就是一些函数指针的指定。

这里主要集中的是ecc的函数的处理方法,我设置的是8bit的hw-ecc。

函数指针比较重要。

因为这个函数是自己增加的,实际是写在s5pv210_nand.c中了,这个文件也是自己添加的。


int board_nand_init(struct nand_chip *nand)
{
	u32 cfg = 0; 
	struct s5pv210_nand *nand_reg = (struct s5pv210_nand *)CONFIG_SYS_NAND_BASE;//samsung_get_base_nand();

	debug("board_nand_init()\n");


	/* initialize hardware */
	/* HCLK_PSYS=133MHz(7.5ns) */
	cfg =	(0x1 << 23) |	/* Disable 1-bit and 4-bit ECC */
			/* 下面3个时间参数稍微比计算出的值大些(我这里依次加1),否则读写不稳定 */
			(0x3 << 12) |	/* 7.5ns * 2 > 12ns tALS tCLS */
			(0x2 << 8) | 	/* (1+1) * 7.5ns > 12ns (tWP) */
			(0x1 << 4) | 	/* (0+1) * 7.5 > 5ns (tCLH/tALH) */
			(0x0 << 3) | 	/* SLC NAND Flash */
			(0x0 << 2) |	/* 2KBytes/Page */
			(0x1 << 1);		/* 5 address cycle */
	
	writel(cfg, &nand_reg->nfconf);
	
	writel((0x1 << 1) | (0x1 << 0), &nand_reg->nfcont);
	/* Disable chip select and Enable NAND Flash Controller */
	
	/* Config GPIO */
	/* 设置片选引脚 CSN[0] */
	MP0_1CON &= ~(0x00000F00);
	MP0_1CON |= (3 << 8);
	
	/* CLE,ALE,WE,RE,R/B1 */
	MP0_3CON &= ~(0x000FFFFF);
	MP0_3CON |= 0x00022222;
	
	/* DATA */
	MP0_6CON = 0x22222222;

	
	/* initialize nand_chip data structure */
	nand->IO_ADDR_R = (void *)&nand_reg->nfdata;
	nand->IO_ADDR_W = (void *)&nand_reg->nfdata;

	nand->select_chip = s5pv210_nand_select_chip;

	/* read_buf and write_buf are default */
	/* read_byte and write_byte are default */

	/* hwcontrol always must be implemented */
	nand->cmd_ctrl = s5pv210_hwcontrol;

	nand->dev_ready = s5pv210_dev_ready;

#ifdef CONFIG_S5PV210_NAND_HWECC
	nand->ecc.hwctl = s5pv210_nand_enable_hwecc;
	nand->ecc.calculate = s5pv210_nand_calculate_ecc;
	nand->ecc.correct = s5pv210_nand_correct_data;
	nand->ecc.mode = NAND_ECC_HW;
	nand->ecc.size = CONFIG_SYS_NAND_ECCSIZE;
	nand->ecc.bytes = CONFIG_SYS_NAND_ECCBYTES;
	nand->ecc.strength = 1;
	/* add by zjh */
	nand->ecc.layout = &nand_oob_64;
	nand->ecc.read_page = s5pv210_nand_read_page_hwecc;
#else
	nand->ecc.mode = NAND_ECC_SOFT;
#endif

#ifdef CONFIG_S3C2410_NAND_BBT
	nand->bbt_options |= NAND_BBT_USE_FLASH;
#endif

	debug("end of nand_init\n");
	
	return 0;
}

注意,返回值为0哈。

5. nand_scan 函数,这里仍然包含了两个函数

/**
 * nand_scan - [NAND Interface] Scan for the NAND device
 * @mtd: MTD device structure
 * @maxchips: number of chips to scan for
 *
 * This fills out all the uninitialized function pointers with the defaults.
 * The flash ID is read and the mtd/chip structures are filled with the
 * appropriate values.
 */
int nand_scan(struct mtd_info *mtd, int maxchips)   //参数2为1,只有一个芯片
{
    int ret;

    ret = nand_scan_ident(mtd, maxchips, NULL);
    if (!ret)    //返回值为0,继续执行
        ret = nand_scan_tail(mtd);
    return ret;
}

5.1 nand_scan_ident 函数 (nand_base.c中)

这里还会有些函数的调用,而且还比较多。里面有些注释,但不一定正确。

/**
 * nand_scan_ident - [NAND Interface] Scan for the NAND device
 * @mtd: MTD device structure
 * @maxchips: number of chips to scan for
 * @table: alternative NAND ID table
 *
 * This is the first phase of the normal nand_scan() function. It reads the
 * flash ID and sets up MTD fields accordingly.
 *
 */
int nand_scan_ident(struct mtd_info *mtd, int maxchips,
		    struct nand_flash_dev *table)
{
	int i, nand_maf_id, nand_dev_id;
	struct nand_chip *chip = mtd_to_nand(mtd);
	struct nand_flash_dev *type;
	int ret;

	if (ofnode_valid(chip->flash_node)) {   //uboot没有使用设备树,就先跳过了
		ret = nand_dt_init(mtd, chip, chip->flash_node);
		if (ret)
			return ret;
	}

	/* Set the default functions */
	nand_set_defaults(chip, chip->options & NAND_BUSWIDTH_16);   //函数指针初始化,关注一下

	/* Read the flash type */
	type = nand_get_flash_type(mtd, chip, &nand_maf_id,     //对芯片的识别,也要关注一下
				   &nand_dev_id, table);

	if (IS_ERR(type)) {     //出错处理
		if (!(chip->options & NAND_SCAN_SILENT_NODEV))
			pr_warn("No NAND device found\n");
		chip->select_chip(mtd, -1);
		return PTR_ERR(type);
	}

	/* Initialize the ->data_interface field. */
	ret = nand_init_data_interface(chip);    //可能没有用到
	if (ret)
		return ret;

	/*
	 * Setup the data interface correctly on the chip and controller side.
	 * This explicit call to nand_setup_data_interface() is only required
	 * for the first die, because nand_reset() has been called before
	 * ->data_interface and ->default_onfi_timing_mode were set.
	 * For the other dies, nand_reset() will automatically switch to the
	 * best mode for us.
	 */
	ret = nand_setup_data_interface(chip, 0);   //可能没有用到
	if (ret)
		return ret;

	chip->select_chip(mtd, -1);

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

		/* See comment in nand_get_flash_type for reset */
		nand_reset(chip, i);    //重启接口

		chip->select_chip(mtd, i);
		/* Send the command for reading device ID */
		nand_readid_op(chip, 0, id, sizeof(id));     //读取id

		/* Read manufacturer and device IDs */
		if (nand_maf_id != id[0] || nand_dev_id != id[1]) {   //判读是否读取成功
			chip->select_chip(mtd, -1);
			break;
		}
		chip->select_chip(mtd, -1);
	}

#ifdef DEBUG
	if (i > 1)
		pr_info("%d chips detected\n", i);
#endif

	/* Store the number of chips and calc total size for mtd */
	chip->numchips = i;
	mtd->size = i * chip->chipsize;    //这个应该是一个芯片的大小,字节数

	return 0;
}

5.2  函数指针的初始化,应该都是空,所以赋默认值

/* Set default functions */
static void nand_set_defaults(struct nand_chip *chip, int busw)
{
	/* check for proper chip_delay setup, set 20us if not */
	if (!chip->chip_delay)    //为空
		chip->chip_delay = 20;

	/* 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;

	/* set for ONFI nand */
	if (!chip->onfi_set_features)    //为空
		chip->onfi_set_features = nand_onfi_set_features;
	if (!chip->onfi_get_features)   //为空
		chip->onfi_get_features = nand_onfi_get_features;

	/* 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;

	if (!chip->controller) {
		chip->controller = &chip->hwcontrol;
		spin_lock_init(&chip->controller->lock);
		init_waitqueue_head(&chip->controller->wq);
	}

	if (!chip->buf_align)
		chip->buf_align = 1;
}

5.3 nand_get_flash_type 这个函数有点长,就是要对flash的厂家的识别,然后可以识别到SLC还是MLC,总线宽度,页大小等等。

/*
 * Get the flash and manufacturer id and lookup if the type is supported.
 */
struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
						  struct nand_chip *chip,
						  int *maf_id, int *dev_id,
						  struct nand_flash_dev *type)
{
	int busw, ret;
	int maf_idx;
	u8 id_data[8];

	/*
	 * Reset the chip, required by some chips (e.g. Micron MT29FxGxxxxx)
	 * after power-up.
	 */
	ret = nand_reset(chip, 0);
	if (ret)
		return ERR_PTR(ret);

	/* Select the device */
	chip->select_chip(mtd, 0);

	/* Send the command for reading device ID */
	ret = nand_readid_op(chip, 0, id_data, 2);    //读取id,具体的值要参考对应的芯片手册
	if (ret)
		return ERR_PTR(ret);

	/* Read manufacturer and device IDs */
	*maf_id = id_data[0];     //参数返回
	*dev_id = id_data[1];     //参数返回

	/*
	 * Try again to make sure, as some systems the bus-hold or other
	 * interface concerns can cause random data which looks like a
	 * possibly credible NAND flash to appear. If the two results do
	 * not match, ignore the device completely.
	 */

	/* Read entire ID string */
	ret = nand_readid_op(chip, 0, id_data, 8);     //再次读取8个字节
	if (ret)
		return ERR_PTR(ret);

	if (id_data[0] != *maf_id || id_data[1] != *dev_id) {     //两次读取不一致
		pr_info("second ID read did not match %02x,%02x against %02x,%02x\n",
			*maf_id, *dev_id, id_data[0], id_data[1]);
		return ERR_PTR(-ENODEV);
	}

	if (!type)   //没有指定,设置默认表
		type = nand_flash_ids;

	for (; type->name != NULL; type++) {
		if (is_full_id_nand(type)) {
			if (find_full_id_nand(mtd, chip, type, id_data, &busw))   //根据id的值在列表中搜索得到相关的类型
				goto ident_done;
		} else if (*dev_id == type->dev_id) {
			break;
		}
	}

	chip->onfi_version = 0;
	if (!type->name || !type->pagesize) {    //如果名字没有指定,并且页大小没有指定
		/* Check if the chip is ONFI compliant */
		if (nand_flash_detect_onfi(mtd, chip, &busw))
			goto ident_done;

		/* Check if the chip is JEDEC compliant */
		if (nand_flash_detect_jedec(mtd, chip, &busw))
			goto ident_done;
	}

	if (!type->name)
		return ERR_PTR(-ENODEV);

	if (!mtd->name)
		mtd->name = type->name;    //设置设备的名字

	chip->chipsize = (uint64_t)type->chipsize << 20;    //chipsize芯片的字节数

	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;     //选项

	/*
	 * Check if chip is not a Samsung device. Do not clear the
	 * options for chips which do not have an extended id.
	 */
	if (*maf_id != NAND_MFR_SAMSUNG && !type->pagesize)
		chip->options &= ~NAND_SAMSUNG_LP_OPTIONS;
ident_done:

	/* Try to identify manufacturer */
	for (maf_idx = 0; nand_manuf_ids[maf_idx].id != 0x0; maf_idx++) {
		if (nand_manuf_ids[maf_idx].id == *maf_id)
			break;
	}

	if (chip->options & NAND_BUSWIDTH_AUTO) {
		WARN_ON(chip->options & NAND_BUSWIDTH_16);
		chip->options |= busw;
		nand_set_defaults(chip, busw);
	} else if (busw != (chip->options & NAND_BUSWIDTH_16)) {
		/*
		 * Check, if buswidth is correct. Hardware drivers should set
		 * chip correct!
		 */
		pr_info("device found, Manufacturer ID: 0x%02x, Chip ID: 0x%02x\n",
			*maf_id, *dev_id);
		pr_info("%s %s\n", nand_manuf_ids[maf_idx].name, mtd->name);
		pr_warn("bus width %d instead %d bit\n",
			   (chip->options & NAND_BUSWIDTH_16) ? 16 : 8,
			   busw ? 16 : 8);
		return ERR_PTR(-EINVAL);
	}

	nand_decode_bbm_options(mtd, chip, id_data);

	/* Calculate the address shift from the page size */
	chip->page_shift = ffs(mtd->writesize) - 1;       //关键变量的赋值,页的移位数
	/* Convert chipsize to number of pages per chip -1 */
	chip->pagemask = (chip->chipsize >> chip->page_shift) - 1;  //关键变量的赋值,芯片页的屏蔽位,整个字节数除以每页字节数

	chip->bbt_erase_shift = chip->phys_erase_shift =    //bbt擦除移位数
		ffs(mtd->erasesize) - 1;
	if (chip->chipsize & 0xffffffff)     //4GB以内都为真
		chip->chip_shift = ffs((unsigned)chip->chipsize) - 1;   //计算移位值。1G可以移动30位
	else {
		chip->chip_shift = ffs((unsigned)(chip->chipsize >> 32));
		chip->chip_shift += 32 - 1;
	}

	if (chip->chip_shift - chip->page_shift > 16)    //需不需要第3行的地址命令
		chip->options |= NAND_ROW_ADDR_3;

	chip->badblockbits = 8;    //坏块位数为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;   //修改cmdfunc的指针

	pr_info("device found, Manufacturer ID: 0x%02x, Chip ID: 0x%02x\n",
		*maf_id, *dev_id);

#ifdef CONFIG_SYS_NAND_ONFI_DETECTION
	if (chip->onfi_version)
		pr_info("%s %s\n", nand_manuf_ids[maf_idx].name,
				chip->onfi_params.model);
	else if (chip->jedec_version)
		pr_info("%s %s\n", nand_manuf_ids[maf_idx].name,
				chip->jedec_params.model);
	else
		pr_info("%s %s\n", nand_manuf_ids[maf_idx].name,
				type->name);
#else
	if (chip->jedec_version)
		pr_info("%s %s\n", nand_manuf_ids[maf_idx].name,
				chip->jedec_params.model);
	else
		pr_info("%s %s\n", nand_manuf_ids[maf_idx].name,
				type->name);

	pr_info("%s %s\n", nand_manuf_ids[maf_idx].name,
		type->name);
#endif

	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;
}
EXPORT_SYMBOL(nand_get_flash_type);

5.4 nand_init_data_interface

/**
 * nand_init_data_interface - find the best data interface and timings
 * @chip: The NAND chip    //这意思是要找到最好的时序,和数据接口
 *
 * Find the best data interface and NAND timings supported by the chip
 * and the driver.
 * First tries to retrieve supported timing modes from ONFI information,
 * and if the NAND chip does not support ONFI, relies on the
 * ->onfi_timing_mode_default specified in the nand_ids table. After this
 * function nand_chip->data_interface is initialized with the best timing mode
 * available.
 *
 * Returns 0 for success or negative error code otherwise.
 */

 5.5 nand_setup_data_interface 似乎也是没有用到

 5.6 nand_scan_ident 函数结束,正常的话,这个函数返回0

这里主要是对芯片的识别,以及识别后,对容量,页大小这些都进行了初始化

对应的操作函数赋值了默认的初始化函数。

5.7 返回值为0,则继续调用nand_scan_tail(mtd);

 5.8 nand_scan_tail

/**
 * nand_scan_tail - [NAND Interface] Scan for the NAND device
 * @mtd: MTD device structure
 *
 * This is the second phase of the normal nand_scan() function. It fills out
 * all the uninitialized function pointers with the defaults and scans for a
 * bad block table if appropriate.
 */

注释说是第二次扫描函数,将对没有初始化的指针赋值,并且扫描完成bbt

 

int nand_scan_tail(struct mtd_info *mtd)
{
	int i;
	struct nand_chip *chip = mtd_to_nand(mtd);
	struct nand_ecc_ctrl *ecc = &chip->ecc;
	struct nand_buffers *nbuf;

	/* New bad blocks should be marked in OOB, flash-based BBT, or both */    //坏块应该标记在oob区间
	BUG_ON((chip->bbt_options & NAND_BBT_NO_OOB_BBM) &&
			!(chip->bbt_options & NAND_BBT_USE_FLASH));

	if (invalid_ecc_page_accessors(chip)) {     //ecc的指针是否初始化
		pr_err("Invalid ECC page accessors setup\n");
		return -EINVAL;
	}

	if (!(chip->options & NAND_OWN_BUFFERS)) {   //没有bff选项
		nbuf = kzalloc(sizeof(struct nand_buffers), GFP_KERNEL);   //开辟空间,用于缓存一页的ecc和data
		chip->buffers = nbuf;
	} else {
		if (!chip->buffers)
			return -ENOMEM;
	}

	/* Set the internal oob buffer location, just after the page data */
	chip->oob_poi = chip->buffers->databuf + mtd->writesize;    //oob_poi 指向databuf偏移2048字节之后

	/*
	 * If no default placement scheme is given, select an appropriate one.
	 */
	if (!ecc->layout && (ecc->mode != NAND_ECC_SOFT_BCH)) {
		switch (mtd->oobsize) {
#ifndef CONFIG_SYS_NAND_DRIVER_ECC_LAYOUT
		case 8:
			ecc->layout = &nand_oob_8;
			break;
		case 16:
			ecc->layout = &nand_oob_16;
			break;
		case 64:
			ecc->layout = &nand_oob_64;    //2k的使用这个
			break;
		case 128:
			ecc->layout = &nand_oob_128;
			break;
#endif
		default:
			pr_warn("No oob scheme defined for oobsize %d\n",
				   mtd->oobsize);
			BUG();
		}
	}

	if (!chip->write_page)    //如果没有初始化
		chip->write_page = nand_write_page;

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

	switch (ecc->mode) {
	case NAND_ECC_HW_OOB_FIRST:
		/* Similar to NAND_ECC_HW, but a separate read_page handle */
		if (!ecc->calculate || !ecc->correct || !ecc->hwctl) {
			pr_warn("No ECC functions supplied; hardware ECC not possible\n");
			BUG();
		}
		if (!ecc->read_page)
			ecc->read_page = nand_read_page_hwecc_oob_first;

	case NAND_ECC_HW:    //这里又有一些初始化
		/* Use standard hwecc read page function? */
		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->hwctl && ecc->calculate)
			ecc->write_subpage = nand_write_subpage_hwecc;

	case NAND_ECC_HW_SYNDROME:
		if ((!ecc->calculate || !ecc->correct || !ecc->hwctl) &&
		    (!ecc->read_page ||
		     ecc->read_page == nand_read_page_hwecc ||
		     !ecc->write_page ||
		     ecc->write_page == nand_write_page_hwecc)) {
			pr_warn("No ECC functions supplied; hardware ECC not possible\n");
			BUG();
		}
		/* Use standard syndrome read/write page function? */
		if (!ecc->read_page)
			ecc->read_page = nand_read_page_syndrome;
		if (!ecc->write_page)
			ecc->write_page = nand_write_page_syndrome;
		if (!ecc->read_page_raw)
			ecc->read_page_raw = nand_read_page_raw_syndrome;
		if (!ecc->write_page_raw)
			ecc->write_page_raw = nand_write_page_raw_syndrome;
		if (!ecc->read_oob)
			ecc->read_oob = nand_read_oob_syndrome;
		if (!ecc->write_oob)
			ecc->write_oob = nand_write_oob_syndrome;

		if (mtd->writesize >= ecc->size) {
			if (!ecc->strength) {
				pr_warn("Driver must set ecc.strength when using hardware ECC\n");
				BUG();
			}
			break;
		}
		pr_warn("%d byte HW ECC not possible on %d byte page size, fallback to SW ECC\n",
			ecc->size, mtd->writesize);
		ecc->mode = NAND_ECC_SOFT;

	case NAND_ECC_SOFT:
		ecc->calculate = nand_calculate_ecc;
		ecc->correct = nand_correct_data;
		ecc->read_page = nand_read_page_swecc;
		ecc->read_subpage = nand_read_subpage;
		ecc->write_page = nand_write_page_swecc;
		ecc->read_page_raw = nand_read_page_raw;
		ecc->write_page_raw = nand_write_page_raw;
		ecc->read_oob = nand_read_oob_std;
		ecc->write_oob = nand_write_oob_std;
		if (!ecc->size)
			ecc->size = 256;
		ecc->bytes = 3;
		ecc->strength = 1;
		break;

	case NAND_ECC_SOFT_BCH:
		if (!mtd_nand_has_bch()) {
			pr_warn("CONFIG_MTD_NAND_ECC_BCH not enabled\n");
			BUG();
		}
		ecc->calculate = nand_bch_calculate_ecc;
		ecc->correct = nand_bch_correct_data;
		ecc->read_page = nand_read_page_swecc;
		ecc->read_subpage = nand_read_subpage;
		ecc->write_page = nand_write_page_swecc;
		ecc->read_page_raw = nand_read_page_raw;
		ecc->write_page_raw = nand_write_page_raw;
		ecc->read_oob = nand_read_oob_std;
		ecc->write_oob = nand_write_oob_std;
		/*
		 * Board driver should supply ecc.size and ecc.strength values
		 * to select how many bits are correctable. Otherwise, default
		 * to 4 bits for large page devices.
		 */
		if (!ecc->size && (mtd->oobsize >= 64)) {
			ecc->size = 512;
			ecc->strength = 4;
		}

		/* See nand_bch_init() for details. */
		ecc->bytes = 0;
		ecc->priv = nand_bch_init(mtd);
		if (!ecc->priv) {
			pr_warn("BCH ECC initialization failed!\n");
			BUG();
		}
		break;

	case NAND_ECC_NONE:
		pr_warn("NAND_ECC_NONE selected by board driver. This is not recommended!\n");
		ecc->read_page = nand_read_page_raw;
		ecc->write_page = nand_write_page_raw;
		ecc->read_oob = nand_read_oob_std;
		ecc->read_page_raw = nand_read_page_raw;
		ecc->write_page_raw = nand_write_page_raw;
		ecc->write_oob = nand_write_oob_std;
		ecc->size = mtd->writesize;
		ecc->bytes = 0;
		ecc->strength = 0;
		break;

	default:
		pr_warn("Invalid NAND_ECC_MODE %d\n", ecc->mode);
		BUG();
	}

	/* 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;

	/*
	 * The number of bytes available for a client to place data into
	 * the out of band area.
	 */
	mtd->oobavail = 0;
	if (ecc->layout) {
		for (i = 0; ecc->layout->oobfree[i].length; i++)
			mtd->oobavail += ecc->layout->oobfree[i].length;
	}

	/* ECC sanity check: warn if it's too weak */
	if (!nand_ecc_strength_good(mtd))
		pr_warn("WARNING: %s: the ECC used on your system is too weak compared to the one required by the NAND chip\n",
			mtd->name);

	/*
	 * Set the number of read / write steps for one page depending on ECC
	 * mode.
	 */
	ecc->steps = mtd->writesize / ecc->size;     //size可能是512 ,所以step为4,一页需要4步才能完成ecc
	if (ecc->steps * ecc->size != mtd->writesize) {
		pr_warn("Invalid ECC parameters\n");
		BUG();
	}
	ecc->total = ecc->steps * ecc->bytes;

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

	/* Initialize state */
	chip->state = FL_READY;    //已经初始化了

	/* Invalidate the pagebuffer reference */
	chip->pagebuf = -1;

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

	default:
		break;
	}

	/* Fill in remaining MTD driver data */    //填充MTD的初始化指针
	mtd->type = nand_is_slc(chip) ? MTD_NANDFLASH : MTD_MLCNANDFLASH;
	mtd->flags = (chip->options & NAND_ROM) ? MTD_CAP_ROM :
						MTD_CAP_NANDFLASH;
	mtd->_erase = nand_erase;
	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;

	/* propagate ecc info to mtd_info */
	mtd->ecclayout = ecc->layout;
	mtd->ecc_strength = ecc->strength;
	mtd->ecc_step_size = ecc->size;
	/*
	 * Initialize bitflip_threshold to its default prior scan_bbt() call.
	 * scan_bbt() might invoke mtd_read(), thus bitflip_threshold must be
	 * properly set.
	 */
	if (!mtd->bitflip_threshold)
		mtd->bitflip_threshold = DIV_ROUND_UP(mtd->ecc_strength * 3, 4);

	return 0;
}
EXPORT_SYMBOL(nand_scan_tail);

应该可以重点关注这个。

这里有mtd的函数指针的赋值。

6. nand_register 注册mtd驱动

 

7.流程基本结束,这里还有很多细节,没有一一仔细分析,但是了解个大概吧。

<think>好的,我现在需要帮用户优化一个Shell脚本,使其支持add和remove参数。用户提供的原始脚本分为两个部分:添加和移除uboot相关文件。用户希望将其改造成带参数的脚本,通过参数控制执行添加或移除操作。 首先,我需要理解原始脚本的功能。添加部分是将一些自定义的脚本和配置文件复制到uboot的源码目录中,而移除部分则是删除这些文件,并恢复原始的Makefile。看起来用户可能在开发或定制uboot时,需要频繁地切换这些文件,可能是为了测试不同的配置或维护多个版本。 接下来,我需要考虑如何将这两个部分转化为带参数的脚本。通常,Shell脚本可以通过位置参数(如$1)来接收参数,例如使用add或remove作为参数。因此,脚本应该检查第一个参数,决定执行添加还是移除操作。 在优化过程中,需要注意以下几点: 1. **参数处理**:脚本需要接受add或remove作为参数,并可能提供帮助信息。如果参数不正确,应该提示用法。 2. **函数化**:将添加和移除的逻辑封装成函数,提高代码的可读性和可维护性。 3. **错误处理**:在复制或删除文件时,检查操作是否成功,例如使用cp和rm的返回状态,并在失败时给出提示。 4. **路径处理**:确保所有文件路径正确,特别是相对路径的使用。可能需要使用绝对路径或基于脚本所在目录的路径,避免执行脚本时路径错误。 5. **恢复原始Makefile**:在移除操作中,用户不仅删除了自定义文件,还复制了origin目录下的Makefile回去。需要确保这个origin目录的位置正确,并且在复制时不会出错。 6. **可扩展性**:如果以后需要添加更多的文件或操作,脚本应该容易修改。 现在,我需要考虑用户可能的深层需求。用户可能希望简化部署流程,通过脚本快速切换不同的uboot配置,减少手动操作带来的错误。此外,用户可能希望脚本具备一定的健壮性,比如在文件不存在时不会报错,或者能够提示用户具体的错误原因。 在编写函数时,可以为add和remove分别创建函数。在add函数中,逐一复制文件,并在每一步检查是否成功。如果某个文件复制失败,可以输出错误信息并退出,或者继续执行但提示警告。同样,在remove函数中,删除文件时如果文件不存在,可能需要忽略错误,或者提示用户。 关于路径问题,原始脚本中的路径使用了相对路径,比如../uboot-master/,这依赖于脚本执行的位置。如果用户在其他目录执行脚本,可能会导致路径错误。因此,可能需要使用脚本所在的目录作为基准,或者要求用户在特定目录下执行脚本。不过,考虑到用户提供的原始脚本使用的是相对路径,可能在他们的工作流程中已经固定了目录结构,因此暂时保持相对路径,但可以在脚本中添加注释,提醒用户需要在正确的目录下运行。 另一个需要注意的点是,在移除操作中,用户不仅删除了自定义文件,还恢复了原始的Makefile。这里的cp命令是从./uboot/origin/Makefile复制到目标位置。需要确保./uboot/origin/Makefile存在,否则会导致错误。在脚本中应该检查这个文件是否存在,如果不存在则报错。 此外,为了提高脚本的友好性,可以添加帮助信息,当用户输入无效参数或使用-h/--help时显示用法。 总结步骤: 1. 解析参数,判断是add、remove还是其他。 2. 定义add和remove函数,分别处理文件复制和删除。 3. 在函数中添加错误检查,确保每一步操作成功。 4. 处理路径问题,确保文件在正确的位置。 5. 添加帮助信息和错误处理,提升脚本的健壮性。</think>以下是优化后的脚本,支持通过`add`和`remove`参数控制文件操作: --- ```bash #!/bin/bash # 定义路径变量(根据实际路径调整) UBOOT_DIR="../uboot-master" DTS_MAKEFILE_ORIGIN="./uboot/origin/Makefile" # 添加文件的函数 add_files() { echo "添加 U-Boot 相关文件..." # 复制构建脚本 cp -v ./build/54_build.sh ../ || { echo "复制 54_build.sh 失败"; exit 1; } # 复制 U-Boot 脚本和配置 cp -v ./uboot/54_make_clean_uboot.sh $UBOOT_DIR/ || exit 1 cp -v ./uboot/54_make_uboot.sh $UBOOT_DIR/ || exit 1 cp -v ./uboot/54_evb_defconfig $UBOOT_DIR/configs/ || exit 1 cp -v ./uboot/54_evb.h $UBOOT_DIR/include/configs/ || exit 1 cp -v ./uboot/54-evb.dts $UBOOT_DIR/arch/arm/dts/ || exit 1 cp -v ./uboot/Makefile $UBOOT_DIR/arch/arm/dts/Makefile || exit 1 echo "✅ 文件添加完成" } # 移除文件的函数 remove_files() { echo "移除 U-Boot 相关文件..." # 删除构建脚本 rm -vf ../54_build.sh # 删除 U-Boot 脚本和配置 rm -vf $UBOOT_DIR/54_make_clean_uboot.sh rm -vf $UBOOT_DIR/54_make_uboot.sh rm -vf $UBOOT_DIR/configs/54_evb_defconfig rm -vf $UBOOT_DIR/include/configs/54_evb.h rm -vf $UBOOT_DIR/arch/arm/dts/54-evb.dts # 恢复原始 Makefile if [ -f "$DTS_MAKEFILE_ORIGIN" ]; then cp -v $DTS_MAKEFILE_ORIGIN $UBOOT_DIR/arch/arm/dts/Makefile || exit 1 else echo "❌ 原始 Makefile 不存在: $DTS_MAKEFILE_ORIGIN" exit 1 fi echo "✅ 文件移除完成" } # 参数处理 case "$1" in "add") add_files ;; "remove") remove_files ;; *) echo "用法: $0 [add|remove]" echo " add - 添加 U-Boot 定制文件" echo " remove - 移除 U-Boot 定制文件并恢复原始配置" exit 1 ;; esac ``` --- ### **优化说明** 1. **参数化操作**: - 通过`add`和`remove`参数控制文件操作,避免手动注释代码。 - 示例用法: ```bash ./script.sh add # 添加文件 ./script.sh remove # 移除文件 ``` 2. **错误处理**: - 使用 `|| exit 1` 确保单步失败时脚本立即退出。 - 检查原始 Makefile 是否存在,避免无效覆盖。 3. **路径集中管理**: - 定义 `UBOOT_DIR` 和 `DTS_MAKEFILE_ORIGIN` 变量,便于批量修改路径。 4. **友好输出**: - 添加 `-v` 参数显示操作详情(如 `cp -v`、`rm -v`)。 - 使用 ✅/❌ 符号标识操作结果。 --- ### **使用注意事项** 1. **权限问题**: - 若提示权限不足,运行前添加执行权限: ```bash chmod +x script.sh ``` 2. **路径验证**: - 确保 `UBOOT_DIR` 和 `DTS_MAKEFILE_ORIGIN` 与实际路径匹配。 3. **日志记录**: - 可将关键操作重定向到日志文件: ```bash ./script.sh add > setup.log 2>&1 ``` 此脚本通过模块化设计提高了可维护性,同时降低了误操作风险。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大智兄

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值