这次梳理一下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驱动