NAND FLASH学习笔记之MTD下nand flash驱动(五)
2014-02-11 15:16
411 查看
转载请注明出处:http://blog.csdn.net/wang_zheng_kai
驱动中probe函数的分析
其中探测函数代码执行流程为:
在probe函数中主要是完成了NAND芯片级的初始化,主要有以下几个作用:
-分配nand_chip内存,根据目标板及NAND控制器初始化nand_chip中成员函数(若未初始化则使用nand_base.c中的默认函数),将mtd_info中的priv指向nand_chip(或板相关私有结构),设置ecc模式及处理函数
-以mtd_info为参数调用nand_scan_ident()和
nand_scan_tail()函数,探测NAND
Flash并进行初始化。
nand_scan_ident()会读取nand芯片ID,并根据mtd->priv即nand_chip中成员初始化mtd_info
nand_scan_tail()进行了ECC的设置和剩下的MTD驱动函数的初始化。
-若有分区,则以mtd_info和mtd_partition为参数调用add_mtd_partitions()添加分区信息
nand_scan_ident()
首先初始化mtd_info中一个重要的指针priv,使这个指针指向nand_chip变量;
然后调用了同文件下的nand_set_defaults()和nand_get_flash_type(),nand_set_defaults()函数对struct nand_chip结构体的函数指针进行了赋值。在此函数中cmdfunc映射到了nand_command,nand_get_flash_type()读取了厂商和设备ID进行匹配,并对struct
nand_chip结构体的变量进行初始化操作。
nand_scan_tail()
1、初始化oob区、ecc校验相关参数和函数指针。
2、初始化MTD驱动接口函数。
3、调用nand_bbt()创建坏块表。
probe程序代码分析如下:
nand_scan_ident()程序代码分析:
驱动中probe函数的分析
其中探测函数代码执行流程为:
在probe函数中主要是完成了NAND芯片级的初始化,主要有以下几个作用:
-分配nand_chip内存,根据目标板及NAND控制器初始化nand_chip中成员函数(若未初始化则使用nand_base.c中的默认函数),将mtd_info中的priv指向nand_chip(或板相关私有结构),设置ecc模式及处理函数
-以mtd_info为参数调用nand_scan_ident()和
nand_scan_tail()函数,探测NAND
Flash并进行初始化。
nand_scan_ident()会读取nand芯片ID,并根据mtd->priv即nand_chip中成员初始化mtd_info
nand_scan_tail()进行了ECC的设置和剩下的MTD驱动函数的初始化。
-若有分区,则以mtd_info和mtd_partition为参数调用add_mtd_partitions()添加分区信息
nand_scan_ident()
首先初始化mtd_info中一个重要的指针priv,使这个指针指向nand_chip变量;
然后调用了同文件下的nand_set_defaults()和nand_get_flash_type(),nand_set_defaults()函数对struct nand_chip结构体的函数指针进行了赋值。在此函数中cmdfunc映射到了nand_command,nand_get_flash_type()读取了厂商和设备ID进行匹配,并对struct
nand_chip结构体的变量进行初始化操作。
nand_scan_tail()
1、初始化oob区、ecc校验相关参数和函数指针。
2、初始化MTD驱动接口函数。
3、调用nand_bbt()创建坏块表。
probe程序代码分析如下:
NAND驱动的代码分析(二) ----------probe程序的分析 Joe static int jz4780_nand_probe(struct platform_device *pdev) { int ret = 0; int bank = 0; int i = 0, j = 0, k = 0, m = 0; int eccpos_start; struct nand_chip *chip; struct mtd_info *mtd; struct jz4780_nand *nand; struct jz4780_nand_platform_data *pdata; nand_flash_if_t *nand_if; 《1》 /* * sanity check主要是获取平台设备信息设备的相关参数 */ pdata = dev_get_platdata(&pdev->dev);//获取平台设备的数据 if (!pdata) { dev_err(&pdev->dev, "Failed to get platform_data.\n"); return -ENXIO; } nand = kzalloc(sizeof(struct jz4780_nand), GFP_KERNEL);//为设备分配内存空间 if (!nand) { dev_err(&pdev->dev, "Failed to allocate jz4780_nand.\n"); return -ENOMEM; } nand->pdev = pdev; nand->pdata = pdata; platform_set_drvdata(pdev, nand);//将nand保存为平台总线的私有数据,将nand设备的数据信息传递到系统平台设备中去 nand->num_nand_flash_if = pdata->num_nand_flash_if;//所用nand flash的片数 nand->xfer_type = pdata->xfer_type; nand->ecc_type = pdata->ecc_type; 《2》 /* * request GPEMC banks获取每一个bank的相关参数(busy,protect,timeout..)申请cs_gpio引脚 */ for (i = 0; i < nand->num_nand_flash_if; i++, j = i) { nand_if = &pdata->nand_flash_if_table[i]; nand->nand_flash_if_table[i] = nand_if; bank = nand_if->bank; ret = gpemc_request_cs(&pdev->dev, &nand_if->cs, bank); if (ret) { dev_err(&pdev->dev, "Failed to request busy" " gpio irq for bank%d\n", bank); goto err_free_busy_irq; } } 《3》 /* * request busy GPIO interrupt申请busy_gpio引脚 */ switch (nand->xfer_type) { case NAND_XFER_CPU_IRQ: case NAND_XFER_DMA_IRQ://中断方式读取数据 for (i = 0; i < nand->num_nand_flash_if; i++, k = i) { nand_if = &pdata->nand_flash_if_table[i]; if (nand_if->busy_gpio < 0) continue; ret = request_busy_irq(nand_if); if (ret) { dev_err(&pdev->dev, "Failed to request busy" " gpio irq for bank%d\n", bank); goto err_free_busy_irq; } } break; case NAND_XFER_CPU_POLL: case NAND_XFER_DMA_POLL://poll机制轮询读取数据(类似与底半部方式) for (i = 0; i < nand->num_nand_flash_if; i++, k = i) { nand_if = &pdata->nand_flash_if_table[i]; if (nand_if->busy_gpio < 0) continue; ret = request_busy_poll(nand_if); if (ret) { dev_err(&pdev->dev, "Failed to request busy" " gpio irq for bank%d\n", bank); goto err_free_busy_irq; } } nand->busy_poll = 1; break; default: WARN(1, "Unsupport transfer type.\n"); BUG(); break; } 《4》 /* * request WP GPIO//申请写保护引脚 */ for (i = 0; i < nand->num_nand_flash_if; i++, m = i) { nand_if = &pdata->nand_flash_if_table[i]; if (nand_if->wp_gpio < 0) continue; if (!gpio_is_valid(nand_if->wp_gpio)) { dev_err(&pdev->dev, "Invalid wp GPIO:%d\n", nand_if->wp_gpio); ret = -EINVAL; goto err_free_wp_gpio; } bank = nand_if->bank; ret = gpio_request(nand_if->wp_gpio, label_wp_gpio[bank]); if (ret) { dev_err(&pdev->dev, "Failed to request wp GPIO:%d\n", nand_if->wp_gpio); goto err_free_wp_gpio; } gpio_direction_output(nand_if->wp_gpio, 0);//设定gipo为输出模式 /* Write protect disabled by default */ jz4780_nand_enable_wp(nand_if, 0); //disable 写保护 } 《5》 /* * NAND flash devices support list override */ nand->nand_flash_table = pdata->nand_flash_table ? pdata->nand_flash_table : builtin_nand_flash_table; nand->num_nand_flash = pdata->nand_flash_table ? pdata->num_nand_flash : ARRAY_SIZE(builtin_nand_flash_table); /* * attach to MTD subsystem 链接MTD的子系统-----涉及到如何传输数据 * struct nand_chip是一个与NAND芯片密切相关的结构体,主要包含三方面内 容: *指向一些操作NAND芯片的函数的指针; *表示NAND芯片特性的成员变量,主要有: *与ecc,oob和bbt (bad block table)相关的一些结构体,对于坏块及坏块管理 */ chip = &nand->chip; chip->chip_delay = MAX_RB_DELAY_US; chip->cmdfunc = jz4780_nand_command; chip->dev_ready = jz4780_nand_dev_is_ready; chip->select_chip = jz4780_nand_select_chip; chip->cmd_ctrl = jz4780_nand_cmd_ctrl; chip->onfi_get_features = jz4780_nand_onfi_get_features; chip->onfi_set_features = jz4780_nand_onfi_set_features; switch (nand->xfer_type) { case NAND_XFER_DMA_IRQ: case NAND_XFER_DMA_POLL: /* * DMA transfer DMA方式传输数据 */ ret = jz4780_nand_request_dma(nand); if (ret) { dev_err(&pdev->dev, "Failed to request DMA channel.\n"); goto err_free_wp_gpio; } chip->read_buf = jz4780_nand_read_buf;//将芯片中的数据读到缓冲区中 chip->write_buf = jz4780_nand_write_buf;//将缓冲区中的数据写入芯片 nand->use_dma = 1; break; case NAND_XFER_CPU_IRQ: case NAND_XFER_CPU_POLL: /* * CPU transfer CPU方式传输数据 */ chip->read_buf = jz4780_nand_cpu_read_buf; chip->write_buf = jz4780_nand_cpu_write_buf; jz4780_nand_write_buf 和 jz4780_nand_read_buf:这是两个最基本的操作函数,其功能,就是往你的Nand Flash的控制器中的FIFO读写数据。一般情况下,是MTD上层的操作,比如要读取一页的数据,那么在发送完相关的读命令和等待时间之后,就会调用到你底层的read_buf,去Nand Flash的FIFO中,一点点把我们要的数据,读取出来,放到我们制定的内存的缓存中去。写操作也是类似,将我们内存中的数据,写到Nand Flash的FIFO中去。 break; default: WARN(1, "Unsupport transfer type.\n"); BUG(); break; } mtd = &nand->mtd;//填充mtd_info结构体相关信息 mtd->priv = chip;//把指向struct nand_chip结构体的指针赋给struct mtd_info的priv成员变量, //因为MTD Core中很多函数之间的调用都只传递struct mtd_info,它需要通过priv成员变量得到struct nand_chip。 mtd->name = dev_name(&pdev->dev); mtd->owner = THIS_MODULE; /* * if you use u-boot BBT creation code,specifying * this flag will let the kernel fish out the BBT * from the NAND, and also skip the full NAND scan * that can take 1/2s or so. little things... */ if (pdata->flash_bbt) {//当flashbbt=1的时候系统在启动的时候将跳过对bbt(bad block table)的扫描 chip->bbt_options |= NAND_BBT_USE_FLASH; chip->options |= NAND_SKIP_BBTSCAN; } /* * nand_base handle subpage write by fill space * where are outside of the subpage with 0xff, * that make things totally wrong, so disable it. */ chip->options |= NAND_NO_SUBPAGE_WRITE; /* * for relocation */ nand->gpemc_enable_nand_flash = gpemc_enable_nand_flash; nand->nand_wait_ready = nand_wait_ready; nand->gpio_get_value = gpio_get_value; nand->wait_for_completion_timeout = wait_for_completion_timeout; nand->msecs_to_jiffies = msecs_to_jiffies; nand->printk = printk; nand->udelay = __udelay; nand->ndelay = __ndelay; 《6》 /* * Detect NAND flash chips 侦测NAND flash chips */ /* step1. relax bank timings to scan 定时扫描空闲的bank*/ for (bank = 0; bank < nand->num_nand_flash_if; bank++) { nand_if = nand->nand_flash_if_table[bank]; gpemc_relax_bank_timing(&nand_if->cs); } if (nand_scan_ident(mtd, nand->num_nand_flash_if, nand->nand_flash_table)) { ret = -ENXIO; dev_err(&pdev->dev, "Failed to detect NAND flash.\n"); goto err_dma_release_channel; } /* * post configure bank timing by detected NAND device 通过侦测的NAND设备配置bank时序寄存器 */ /* step1. match NAND chip information */ nand->curr_nand_flash_info = jz4780_nand_match_nand_chip_info(nand); if (!nand->curr_nand_flash_info) { ret = -ENODEV; goto err_dma_release_channel; } /* * step2. preinitialize NAND flash 预初始化NAND闪存 */ ret = jz4780_nand_pre_init(nand); if (ret) { dev_err(&nand->pdev->dev, "Failed to" " preinitialize NAND chip.\n"); goto err_dma_release_channel; } /* step3. replace NAND command function with large page version */// 用对页面的操作命令函数替代NAND的功能命令函数 if (mtd->writesize > 512) chip->cmdfunc = jz4780_nand_command_lp; /* step4. configure bank timings */ //配置BANK的时序 switch (nand->curr_nand_flash_info->type) { case BANK_TYPE_NAND: for (bank = 0; bank < nand->num_nand_flash_if; bank++) { nand_if = nand->nand_flash_if_table[bank]; gpemc_fill_timing_from_nand(&nand_if->cs, &nand->curr_nand_flash_info-> nand_timing.common_nand_timing);//对bank时序进行赋值操作 ret = gpemc_config_bank_timing(&nand_if->cs);//对bank时序寄存器进行配置 if (ret) { dev_err(&pdev->dev, "Failed to configure timings for bank%d\n" , nand_if->bank); goto err_dma_release_channel; } } break; case BANK_TYPE_TOGGLE: for (bank = 0; bank < nand->num_nand_flash_if; bank++) { nand_if = nand->nand_flash_if_table[bank]; gpemc_fill_timing_from_toggle(&nand_if->cs, &nand->curr_nand_flash_info-> nand_timing.toggle_nand_timing); ret = gpemc_config_bank_timing(&nand_if->cs); if (ret) { dev_err(&pdev->dev, "Failed to configure timings for bank%d\n" , nand_if->bank); goto err_dma_release_channel; } } break; default: WARN(1, "Unsupported NAND type.\n"); BUG(); break; } 《7》 /* * initialize ECC control//初始化NAND ECC控制器 */ /* step1. configure ECC step */ switch (nand->ecc_type) { case NAND_ECC_TYPE_SW: //软件实现ECC检测 /* * valid ECC configuration ? */ if (nand->curr_nand_flash_info-> ecc_step.data_size % 8 || nand->curr_nand_flash_info-> ecc_step.ecc_bits % 8) { ret = -EINVAL; dev_err(&nand->pdev->dev, "Failed when configure ECC," " ECC size, and ECC bits must be a multiple of 8.\n");//ECC的大小和ECC位大小必须是8位 goto err_dma_release_channel; } chip->ecc.mode = NAND_ECC_SOFT_BCH;//ECC算法的模式为软件BCH模式 chip->ecc.size = nand->curr_nand_flash_info->ecc_step.data_size; chip->ecc.bytes = (fls(8 * chip->ecc.size) * (nand->curr_nand_flash_info->ecc_step.ecc_bits) + 7) / 8; break; case NAND_ECC_TYPE_HW: //硬件实现ECC的检测,选取的算法同样是BCH nand->bch_req.dev = &nand->pdev->dev; nand->bch_req.complete = jz4780_nand_bch_req_complete; nand->bch_req.ecc_level = nand->curr_nand_flash_info->ecc_step.ecc_bits; nand->bch_req.blksz = nand->curr_nand_flash_info->ecc_step.data_size; nand->bch_req.errrept_data = kzalloc(MAX_ERRREPT_DATA_SIZE, GFP_KERNEL); if (!nand->bch_req.errrept_data) { dev_err(&pdev->dev, "Failed to allocate ECC errrept_data buffer\n"); ret = -ENOMEM; goto err_dma_release_channel; } init_completion(&nand->bch_req_done); chip->ecc.mode = NAND_ECC_HW; chip->ecc.calculate = jz4780_nand_ecc_calculate_bch; chip->ecc.correct = jz4780_nand_ecc_correct_bch; chip->ecc.hwctl = jz4780_nand_ecc_hwctl; chip->ecc.size = nand->curr_nand_flash_info->ecc_step.data_size; chip->ecc.bytes = bch_ecc_bits_to_bytes( nand->curr_nand_flash_info->ecc_step.ecc_bits); chip->ecc.strength = nand->bch_req.ecc_level; break; default : WARN(1, "Unsupported ECC type.\n"); BUG(); break; } /* /* step2. generate ECC layout *///产生出ECC的布局 /* * eccbytes = eccsteps * eccbytes_prestep;//计算ECC的字节数 */ nand->ecclayout.eccbytes = mtd->writesize / chip->ecc.size * chip->ecc.bytes; //判断是否ECC字节数超出OOB的空间大小 if (mtd->oobsize < (nand->ecclayout.eccbytes + chip->badblockpos + 2)) { WARN(1, "ECC codes are out of OOB area.\n"); BUG(); } /* * ECC codes are right aligned ECC码为右对齐 * start position = oobsize - eccbytes 起始位置的计算 */ eccpos_start = mtd->oobsize - nand->ecclayout.eccbytes; //ECC码的起始位置 for (bank = 0; bank < nand->ecclayout.eccbytes; bank++) nand->ecclayout.eccpos[bank] = eccpos_start + bank; nand->ecclayout.oobfree->offset = chip->badblockpos + 2; nand->ecclayout.oobfree->length = mtd->oobsize - (nand->ecclayout.eccbytes + chip->badblockpos + 2); chip->ecc.layout = &nand->ecclayout; 《8》 /* * second phase NAND scan //第二阶段NAND扫描 */ if (nand_scan_tail(mtd)) { ret = -ENXIO; goto err_free_ecc; } #ifdef CONFIG_DEBUG_FS nand->debugfs_entry = jz4780_nand_debugfs_init(nand); if (IS_ERR(nand->debugfs_entry)) { dev_err(&pdev->dev, "Failed to register debugfs entry.\n"); ret = PTR_ERR(nand->debugfs_entry); goto err_free_ecc; } #endif /* * relocate hot functions to TCSM */ if (pdata->try_to_reloc_hot) { ret = jz4780_nand_reloc_hot_to_tcsm(nand); if (ret) { dev_err(&pdev->dev, "Failed to relocate hot functions.\n"); goto err_debugfs_remove; } } 《9》 /* * MTD register */ ret = mtd_device_parse_register(mtd, NULL, NULL, pdata->part_table, pdata->num_part); if (ret) { dev_err(&pdev->dev, "Failed to add MTD device\n"); goto err_unreloc_hot; } dev_info(&pdev->dev, "Successfully registered JZ4780 SoC NAND controller driver.\n"); return 0; err_unreloc_hot: if (pdata->try_to_reloc_hot) jz4780_nand_unreloc_hot_from_tcsm(nand); err_debugfs_remove: #ifdef CONFIG_DEBUG_FS debugfs_remove_recursive(nand->debugfs_entry); #endif err_free_ecc: if (pdata->ecc_type == NAND_ECC_TYPE_HW) kfree(nand->bch_req.errrept_data); err_dma_release_channel: if (nand->xfer_type == NAND_XFER_DMA_IRQ || nand->xfer_type == NAND_XFER_DMA_POLL) dma_release_channel(nand->dma_pipe_nand.chan); err_free_wp_gpio: for (bank = 0; bank < m; bank++) { nand_if = &pdata->nand_flash_if_table[bank]; if (nand_if->wp_gpio < 0) continue; gpio_free(nand_if->wp_gpio); } err_free_busy_irq: for (bank = 0; bank < k; bank++) { nand_if = &pdata->nand_flash_if_table[bank]; if (nand_if->busy_gpio < 0) continue; if (pdata->xfer_type == NAND_XFER_CPU_IRQ || pdata->xfer_type ==NAND_XFER_DMA_IRQ) free_irq(nand_if->busy_irq, nand_if); gpio_free(nand_if->busy_gpio); } err_release_cs: for (bank = 0; bank < j; bank++) { nand_if = &pdata->nand_flash_if_table[bank]; gpemc_release_cs(&nand_if->cs); } kfree(nand); return ret; }
nand_scan_ident()程序代码分析:
int nand_scan_ident(struct mtd_info *mtd, int maxchips,struct nand_flash_dev *table) { int i, busw, nand_maf_id, nand_dev_id; struct nand_chip *chip = mtd->priv; // mtd->priv在probe函数中被初始化为数据结构nand_chip的变量nand_chip[i],所以这里的this指针指向的就是变量nand_chip[i]。 struct nand_flash_dev *type; /* Get buswidth to select the correct functions */ busw = chip->options & NAND_BUSWIDTH_16; //位宽设置,options bit1设置为0是busw为0,表示位宽为8。options会在该函数后续被初始化为nand_flash_ids[i].options。如果用户需要配置扩展功能只能在nand_flash_ids[i].options配置。 /* Set the default functions */ nand_set_defaults(chip, busw); //nand_set_defaults()函数对struct nand_chip结构体的函数指针进行了赋值。在此函数中cmdfunc映射到了nand_command, /* 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; if (!chip->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 = busw ? nand_write_buf16 : nand_write_buf; if (!chip->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); } } /* Read the flash type */ type = nand_get_flash_type(mtd, chip, busw, &nand_maf_id, &nand_dev_id, table); //nand_get_flash_type()读取了厂商和设备ID,并对struct nand_chip结构体的变量进行初始化操作 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); } chip->select_chip(mtd, -1); /* Check for a chip array */ for (i = 1; i < maxchips; i++) { chip->select_chip(mtd, i); /* See comment in nand_get_flash_type for reset */ chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); /* Send the command for reading device ID */ chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); /* Read manufacturer and device IDs */ if (nand_maf_id != chip->read_byte(mtd) || nand_dev_id != chip->read_byte(mtd)) { chip->select_chip(mtd, -1); break; } chip->select_chip(mtd, -1); } if (i > 1) pr_info("%d NAND chips detected\n", i); /* Store the number of chips and calc total size for mtd */ chip->numchips = i; mtd->size = i * chip->chipsize; return 0; } EXPORT_SYMBOL(nand_scan_ident);
nand_scan_tail()代码分析:
int nand_scan_tail(struct mtd_info *mtd) { int i; struct nand_chip *chip = mtd->priv; /* New bad blocks should be marked in OOB, flash-based BBT, or both */ BUG_ON((chip->bbt_options & NAND_BBT_NO_OOB_BBM) && !(chip->bbt_options & NAND_BBT_USE_FLASH)); if (!(chip->options & NAND_OWN_BUFFERS)) chip->buffers = kmalloc(sizeof(*chip->buffers), GFP_KERNEL); if (!chip->buffers) return -ENOMEM; /* Set the internal oob buffer location, just after the page data */ chip->oob_poi = chip->buffers->databuf + mtd->writesize;//设置缓存位置,仅仅需要在数据页后面就行 * If no default placement scheme is given, select an appropriate one.如果没有预设的方案,就选择合适的一个 */ if (!chip->ecc.layout && (chip->ecc.mode != NAND_ECC_SOFT_BCH)) { switch (mtd->oobsize) { case 8: chip->ecc.layout = &nand_oob_8; break; case 16: chip->ecc.layout = &nand_oob_16; break; case 64: chip->ecc.layout = &nand_oob_64; break; case 128: chip->ecc.layout = &nand_oob_128; break; default: pr_warn("No oob scheme defined for oobsize %d\n", mtd->oobsize); BUG(); } } if (!chip->write_page) chip->write_page = nand_write_page; /* set for ONFI nand *///设置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; //对chip结构题的ecc相关的初始化 /* * Check ECC mode, default to software if 3byte/512byte hardware ECC is * selected and we have 256 byte pagesize fallback to software ECC *///如果硬件检测选择的是3/512那么我们默认软件ECC模式,我们每页有256bytes为ECC备用 // /*以下都是对chip赋值,对应nand_chip中的函数*/ /*Nand_scan是在初始化nand的时候对nand进行的一步非常好重要的操作, *在nand_scan中会对我们所写的关于特定芯片的读写函数重载到nand_chip结构中去, *并会将mtd_info结构体中的函数用nand的函数来重载,实现了mtd到底层驱动的联系。 *并且在nand_scan函数中会通过读取nand芯片的设备号和厂家号自动在芯片列表中寻找相应的型号和参数,并将其注册进去。*/ switch (chip->ecc.mode) { case NAND_ECC_HW_OOB_FIRST: /* Similar to NAND_ECC_HW, but a separate read_page handle */ if (!chip->ecc.calculate || !chip->ecc.correct || !chip->ecc.hwctl) { pr_warn("No ECC functions supplied; " "hardware ECC not possible\n"); BUG(); } if (!chip->ecc.read_page) chip->ecc.read_page = nand_read_page_hwecc_oob_first; case NAND_ECC_HW: /* Use standard hwecc read page function? */ if (!chip->ecc.read_page) chip->ecc.read_page = nand_read_page_hwecc; if (!chip->ecc.write_page) chip->ecc.write_page = nand_write_page_hwecc; if (!chip->ecc.read_page_raw) chip->ecc.read_page_raw = nand_read_page_raw; if (!chip->ecc.write_page_raw) chip->ecc.write_page_raw = nand_write_page_raw; if (!chip->ecc.read_oob) chip->ecc.read_oob = nand_read_oob_std; if (!chip->ecc.write_oob) chip->ecc.write_oob = nand_write_oob_std; case NAND_ECC_HW_SYNDROME: if ((!chip->ecc.calculate || !chip->ecc.correct || !chip->ecc.hwctl) && (!chip->ecc.read_page || chip->ecc.read_page == nand_read_page_hwecc || !chip->ecc.write_page || chip->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 (!chip->ecc.read_page) chip->ecc.read_page = nand_read_page_syndrome; if (!chip->ecc.write_page) chip->ecc.write_page = nand_write_page_syndrome; if (!chip->ecc.read_page_raw) chip->ecc.read_page_raw = nand_read_page_raw_syndrome; if (!chip->ecc.write_page_raw) chip->ecc.write_page_raw = nand_write_page_raw_syndrome; if (!chip->ecc.read_oob) chip->ecc.read_oob = nand_read_oob_syndrome; if (!chip->ecc.write_oob) chip->ecc.write_oob = nand_write_oob_syndrome; if (mtd->writesize >= chip->ecc.size) { if (!chip->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", chip->ecc.size, mtd->writesize); chip->ecc.mode = NAND_ECC_SOFT; case NAND_ECC_SOFT: chip->ecc.calculate = nand_calculate_ecc; chip->ecc.correct = nand_correct_data; chip->ecc.read_page = nand_read_page_swecc; chip->ecc.read_subpage = nand_read_subpage; chip->ecc.write_page = nand_write_page_swecc; chip->ecc.read_page_raw = nand_read_page_raw; chip->ecc.write_page_raw = nand_write_page_raw; chip->ecc.read_oob = nand_read_oob_std; chip->ecc.write_oob = nand_write_oob_std; if (!chip->ecc.size) chip->ecc.size = 256; chip->ecc.bytes = 3; chip->ecc.strength = 1; break; case NAND_ECC_SOFT_BCH: if (!mtd_nand_has_bch()) { pr_warn("CONFIG_MTD_ECC_BCH not enabled\n"); BUG(); } chip->ecc.calculate = nand_bch_calculate_ecc; chip->ecc.correct = nand_bch_correct_data; chip->ecc.read_page = nand_read_page_swecc; chip->ecc.read_subpage = nand_read_subpage; chip->ecc.write_page = nand_write_page_swecc; chip->ecc.read_page_raw = nand_read_page_raw; chip->ecc.write_page_raw = nand_write_page_raw; chip->ecc.read_oob = nand_read_oob_std; chip->ecc.write_oob = nand_write_oob_std; /* * Board driver should supply ecc.size and ecc.bytes values to * select how many bits are correctable; see nand_bch_init() * for details. Otherwise, default to 4 bits for large pag * devices. */ if (!chip->ecc.size && (mtd->oobsize >= 64)) { chip->ecc.size = 512; chip->ecc.bytes = 7; }//初始化NAND BCH 纠错 chip->ecc.priv = nand_bch_init(mtd, chip->ecc.size, chip->ecc.bytes, &chip->ecc.layout);//建立坏块表 if (!chip->ecc.priv) { pr_warn("BCH ECC initialization failed!\n"); BUG(); } //Driver must set ecc.strength when using hardware ECC chip->ecc.strength = chip->ecc.bytes * 8 / fls(8 * chip->ecc.size); break; case NAND_ECC_NONE: pr_warn("NAND_ECC_NONE selected by board driver. " "This is not recommended!\n"); chip->ecc.read_page = nand_read_page_raw; chip->ecc.write_page = nand_write_page_raw; chip->ecc.read_oob = nand_read_oob_std; chip->ecc.read_page_raw = nand_read_page_raw; chip->ecc.write_page_raw = nand_write_page_raw; chip->ecc.write_oob = nand_write_oob_std; chip->ecc.size = mtd->writesize; chip->ecc.bytes = 0; chip->ecc.strength = 0; break; default: pr_warn("Invalid NAND_ECC_MODE %d\n", chip->ecc.mode); BUG(); } /* For many systems, the standard OOB write also works for raw */ if (!chip->ecc.read_oob_raw) chip->ecc.read_oob_raw = chip->ecc.read_oob; if (!chip->ecc.write_oob_raw) chip->ecc.write_oob_raw = chip->ecc.write_oob; /* * The number of bytes available for a client to place data into * the out of band area. */ chip->ecc.layout->oobavail = 0; for (i = 0; chip->ecc.layout->oobfree[i].length && i < ARRAY_SIZE(chip->ecc.layout->oobfree); i++) chip->ecc.layout->oobavail += chip->ecc.layout->oobfree[i].length; mtd->oobavail = chip->ecc.layout->oobavail; /* * Set the number of read / write steps for one page depending on ECC * mode. */ chip->ecc.steps = mtd->writesize / chip->ecc.size; if (chip->ecc.steps * chip->ecc.size != mtd->writesize) { pr_warn("Invalid ECC parameters\n"); BUG(); } chip->ecc.total = chip->ecc.steps * chip->ecc.bytes; //subpage相关的初始化 /* Allow subpage writes up to ecc.steps. Not possible for MLC flash */ if (!(chip->options & NAND_NO_SUBPAGE_WRITE) && !(chip->cellinfo & NAND_CI_CELLTYPE_MSK)) { switch (chip->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 */ if ((chip->ecc.mode == NAND_ECC_SOFT) && (chip->page_shift > 9)) chip->options |= NAND_SUBPAGE_READ; //初始化剩余的mtd_info结构题 /* Fill in remaining MTD driver data */ mtd->type = MTD_NANDFLASH; mtd->flags = (chip->options & NAND_ROM) ? MTD_CAP_ROM : MTD_CAP_NANDFLASH; mtd->_erase = nand_erase; mtd->_point = NULL; mtd->_unpoint = NULL; 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->_suspend = nand_suspend; mtd->_resume = nand_resume; mtd->_block_isbad = nand_block_isbad; mtd->_block_markbad = nand_block_markbad; mtd->writebufsize = mtd->writesize; //把chip中的ecc信息传递给mtd结构题,初始化mtd_info /* propagate ecc info to mtd_info */ mtd->ecclayout = chip->ecc.layout; mtd->ecc_strength = chip->ecc.strength; /* * 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 = mtd->ecc_strength; /* Check, if we should skip the bad block table scan */ //判断是否跳过坏块表检测 if (chip->options & NAND_SKIP_BBTSCAN) return 0; /* Build bad block table *///扫描并建立坏块表 return chip->scan_bbt(mtd); } EXPORT_SYMBOL(nand_scan_tail);
相关文章推荐
- NAND FLASH学习笔记之MTD下nand flash驱动(二)————几个重要文件和几个重要的结构体
- NAND FLASH学习笔记之MTD下nand flash驱动(二)
- NAND FLASH学习笔记之MTD下nand flash驱动(四)
- NAND FLASH学习笔记之MTD下nand flash驱动(三)
- NAND FLASH学习笔记之MTD下nand flash驱动(六)
- NAND FLASH学习笔记之MTD下nand flash驱动(一)
- NAND FLASH学习笔记之MTD下nand flash驱动(七)---我的调试
- STM32学习笔记——FSMC 驱动大容量NAND FLASH [复制链接]
- 学习笔记 --- LINUX MTD设备之NANDFLASH驱动分析
- 学习笔记 --- LINUX MTD设备之NORFLASH驱动分析
- linux字符设备驱动学习笔记(一):简单的字符设备驱动
- 我的内核学习笔记9:Intel内部看门狗iTCO_wdt驱动
- RT-Thread 学习笔记(十三)--- 开启基于RTGUI的LCD显示功能(3)<触屏屏驱动移植和测试>
- Linux驱动学习笔记之一——高精度定时器2
- 学习笔记 --- LINUX LCD显示原理与驱动分析
- Linux 驱动学习笔记2 -- Timer
- AM335x(TQ335x)学习笔记——触摸屏驱动编写
- Android深度探索:HAL与驱动开发学习笔记--内存管理(学习重点)
- 【Java学习笔记】54:CentOS下安装MySQL的JDBC驱动并尝试连接
- STM32学习笔记之 USB驱动usb_endp.c文件和usb_istr.c文件分析