5、MTD原始设备与FLASH硬件驱动的对话 (一) .
发布时间:2020-12-15 18:01:47 所属栏目:百科 来源:网络整理
导读:看了Linux MTD源代码分析后对以MTD的分层结构以及各层的分工情况有了大致的了解,然而各层之间是如何进行对话的呢,对于这个问题,Linux MTD源代码分析上没有详细的去说明。 小弟抽空研究了一下,打算从下到上,在从上到下,分两条主线来研究一下MTD原始设备
看了<<Linux MTD源代码分析>>后对以MTD的分层结构以及各层的分工情况有了大致的了解,然而各层之间是如何进行对话的呢,对于这个问题,<<Linux MTD源代码分析>>上没有详细的去说明。
小弟抽空研究了一下,打算从下到上,在从上到下,分两条主线来研究一下MTD原始设备与FLASH硬件驱动的对话(MTD原始设备与更上层的对话留待以后再研究)。 以下是第一部分,从下到上的介绍FLASH硬件驱动与MTD原始设备是如何建立联系的。 1、首先从入口函数开始: static int s3c24xx_nand_probe(struct device *dev,int is_s3c2440) { ??? struct platform_device *pdev = to_platform_device(dev); ??? struct s3c2410_platform_nand *plat = to_nand_plat(dev); ??? //获取nand flash配置用结构体数据(dev.c中定义,详细见附录部分) ??? struct s3c2410_nand_info *info; ??? struct s3c2410_nand_mtd *nmtd; ??? struct s3c2410_nand_set *sets; ??? struct resource *res; ??? int err = 0; ??? int size; ??? int nr_sets; ??? int setno; ??? pr_debug("s3c2410_nand_probe(%p)/n",dev); ??? info = kmalloc(sizeof(*info),GFP_KERNEL); ??? if (info == NULL) { ??? ??? printk(KERN_ERR PFX "no memory for flash info/n"); ??? ??? err = -ENOMEM; ??? ??? goto exit_error; ??? } ??? memzero(info,sizeof(*info)); ??? dev_set_drvdata(dev,info);?? ?? ?? ?? ?? ?? //以后有用 ??? spin_lock_init(&info->controller.lock);?? ?? //初始化自旋锁 ??? init_waitqueue_head(&info->controller.wq);?? //初始化等待队列 ??? /* get the clock source and enable it */ ??? info->clk = clk_get(dev,"nand"); ??? if (IS_ERR(info->clk)) { ??? ??? printk(KERN_ERR PFX "failed to get clock"); ??? ??? err = -ENOENT; ??? ??? goto exit_error; ??? } ??? clk_use(info->clk); ??? clk_enable(info->clk); ??? /* allocate and map the resource */ ??? /* currently we assume we have the one resource */ ??? res? = pdev->resource;?? ?? ?? ?? ?? ?? ?? ?? //提取dev.c中定义的与设备相关的资源 ??? size = res->end - res->start + 1; ??? info->area = request_mem_region(res->start,size,pdev->name); ??? if (info->area == NULL) { ??? ??? printk(KERN_ERR PFX "cannot reserve register region/n"); ??? ??? err = -ENOENT; ??? ??? goto exit_error; ??? } ??? info->device???? = dev; ??? info->platform?? = plat;?? ?? ?? ?? ?? ?? ?? //保存好struct s3c2410_platform_nand结构数据 ??? info->regs?????? = ioremap(res->start,size);//映射nand flash用到的寄存器 ??? info->is_s3c2440 = is_s3c2440;?? ?? ?? ?? ?? ??? if (info->regs == NULL) { ??? ??? printk(KERN_ERR PFX "cannot reserve register region/n"); ??? ??? err = -EIO; ??? ??? goto exit_error; ??? }??? ??? ??? printk(KERN_INFO PFX "mapped registers at %p/n",info->regs); ??? /* initialise the hardware */ ??? err = s3c2410_nand_inithw(info,dev); ??? //初始化s3c2410 nand flash控制,主要是配置S3C2410_NFCONF寄存器 ??? if (err != 0) ??? ??? goto exit_error; ??? sets = (plat != NULL) ? plat->sets : NULL;?? ??? nr_sets = (plat != NULL) ? plat->nr_sets : 1; ?? ??? info->mtd_count = nr_sets; ??? //我的板上只有一块nand flash,配置信息见plat-sets,数目为1。 ??? /* allocate our information */ ??? size = nr_sets * sizeof(*info->mtds); ??? info->mtds = kmalloc(size,GFP_KERNEL); ??? if (info->mtds == NULL) { ??? ??? printk(KERN_ERR PFX "failed to allocate mtd storage/n"); ??? ??? err = -ENOMEM; ??? ??? goto exit_error; ??? } ??? memzero(info->mtds,size); ??? /* initialise all possible chips */ ??? nmtd = info->mtds; ??? for (setno = 0; setno < nr_sets; setno++,nmtd++) { ??? ??? pr_debug("initialising set %d (%p,info %p)/n", ??? ??? ??? ?setno,nmtd,info); ??? ??? ??? ??? s3c2410_nand_init_chip(info,sets); ??? ??? nmtd->scan_res = nand_scan(&nmtd->mtd, ??? ??? ??? ??? ??? ?? (sets) ? sets->nr_chips : 1);//为什么使用set->nr_chips(还没配置的东西)? ??? ??? if (nmtd->scan_res == 0) { ??? ??? ??? s3c2410_nand_add_partition(info,sets); ??? ??? } ??? ??? if (sets != NULL) ??? ??? ??? sets++; ??? } ??? ??? pr_debug("initialised ok/n"); ??? return 0; ?exit_error: ??? s3c2410_nand_remove(dev); ??? if (err == 0) ??? ??? err = -EINVAL; ??? return err; } //初始化代表一片flash的struct nand_chip结构 static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info, ??? ??? ??? ??? ?? struct s3c2410_nand_mtd *nmtd, ??? ??? ??? ??? ?? struct s3c2410_nand_set *set) { ??? struct nand_chip *chip = &nmtd->chip; ??? chip->IO_ADDR_R??? ?? = info->regs + S3C2410_NFDATA;?? //读地址 ??? chip->IO_ADDR_W??? = info->regs + S3C2410_NFDATA;?? ?? //写地址 ??? chip->hwcontrol??? = s3c2410_nand_hwcontrol;?? ??? chip->dev_ready??? = s3c2410_nand_devready;?? ?? ?? ?? //ready状态查询 ??? chip->write_buf??? = s3c2410_nand_write_buf;?? ?? ?? ? //写函数 ??? chip->read_buf???? = s3c2410_nand_read_buf;?? ?? ?? ?? //读函数 ??? chip->select_chip? = s3c2410_nand_select_chip;?? ?? ?? //片选函数 ??? chip->chip_delay?? = 50; ??? chip->priv??? ?? = nmtd; ??? chip->options??? ?? = 0; ??? chip->controller?? = &info->controller; ??? if (info->is_s3c2440) { ??? ??? chip->IO_ADDR_R??? ?= info->regs + S3C2440_NFDATA; ??? ??? chip->IO_ADDR_W? = info->regs + S3C2440_NFDATA; ??? ??? chip->hwcontrol? = s3c2440_nand_hwcontrol; ??? } ??? nmtd->info??? ?? = info; ??? nmtd->mtd.priv??? ?? = chip;?? ?? ?? ?? ??? //nand_scan函数中会调用struct nand_chip *this = mtd->priv取出该struct nand_chip结构 ??? nmtd->set??? ?? = set; ??? if (hardware_ecc) { ??? ??? chip->correct_data? = s3c2410_nand_correct_data; ??? ??? chip->enable_hwecc? = s3c2410_nand_enable_hwecc; ??? ??? chip->calculate_ecc = s3c2410_nand_calculate_ecc; ??? ??? chip->eccmode??? ??? = NAND_ECC_HW3_512; ??? ??? chip->autooob?????? = &nand_hw_eccoob; ??? ??? if (info->is_s3c2440) { ??? ??? ??? chip->enable_hwecc? = s3c2440_nand_enable_hwecc; ??? ??? ??? chip->calculate_ecc = s3c2440_nand_calculate_ecc; ??? ??? } ??? } else {?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ??? ??? chip->eccmode??? ??? = NAND_ECC_SOFT;?? ?? ?? //ECC的类型 ??????? } } /* command and control functions ?* ?* Note,these all use tglx's method of changing the IO_ADDR_W field ?* to make the code simpler,and use the nand layer's code to issue the ?* command and address sequences via the proper IO ports. ?* */ static void s3c2410_nand_hwcontrol(struct mtd_info *mtd,int cmd) { ??? struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); ??? struct nand_chip *chip = mtd->priv; ??? switch (cmd) { ??? case NAND_CTL_SETNCE: ??? case NAND_CTL_CLRNCE: ??? ??? printk(KERN_ERR "%s: called for NCE/n",__FUNCTION__); ??? ??? break; ??? case NAND_CTL_SETCLE: ??? ??? chip->IO_ADDR_W = info->regs + S3C2410_NFCMD;//写命令 ??? ??? break; ??? case NAND_CTL_SETALE: ??? ??? chip->IO_ADDR_W = info->regs + S3C2410_NFADDR;//写地址 ??? ??? break; ??? ??? /* NAND_CTL_CLRCLE: */ ??? ??? /* NAND_CTL_CLRALE: */ ??? default: ??? ??? chip->IO_ADDR_W = info->regs + S3C2410_NFDATA;//写数据 ??? ??? break; ??? } } /* s3c2410_nand_devready() ?* ?* returns 0 if the nand is busy,1 if it is ready */ static int s3c2410_nand_devready(struct mtd_info *mtd) { ??? struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); ??? ??? if (info->is_s3c2440) ??? ??? return readb(info->regs + S3C2440_NFSTAT) & S3C2440_NFSTAT_READY; ??? return readb(info->regs + S3C2410_NFSTAT) & S3C2410_NFSTAT_BUSY;//返回nand flash都忙标志 } static void s3c2410_nand_write_buf(struct mtd_info *mtd, ??? ??? ??? ??? ?? const u_char *buf,int len) { ??? struct nand_chip *this = mtd->priv; ??? writesb(this->IO_ADDR_W,buf,len);//写操作 } static void s3c2410_nand_read_buf(struct mtd_info *mtd,u_char *buf,int len) { ??? struct nand_chip *this = mtd->priv; ??? readsb(this->IO_ADDR_R,len);//读操作 } /* select chip */ /* ?* 根据chip都值设置nand flash都片选信号: ?* chip = -1 -- 禁用nand flash ?* chip !=-1 -- 选择对应的nand flash ?*/ static void s3c2410_nand_select_chip(struct mtd_info *mtd,int chip) { ??? struct s3c2410_nand_info *info; ??? struct s3c2410_nand_mtd *nmtd; ??? struct nand_chip *this = mtd->priv; ??? void __iomem *reg; ??? unsigned long cur; ??? unsigned long bit; ??? nmtd = this->priv; ??? info = nmtd->info; ??? bit = (info->is_s3c2440) ? S3C2440_NFCONT_nFCE : S3C2410_NFCONF_nFCE; ??? reg = info->regs+((info->is_s3c2440) ? S3C2440_NFCONT:S3C2410_NFCONF); ??? cur = readl(reg); ??? if (chip == -1) { ??? ??? cur |= bit; ??? } else { ??? ??? if (nmtd->set != NULL && chip > nmtd->set->nr_chips) { ??? ??? ??? printk(KERN_ERR PFX "chip %d out of range/n",chip); ??? ??? ??? return; ??? ??? } ??? ??? if (info->platform != NULL) { ??? ??? ??? if (info->platform->select_chip != NULL) ??? ??? ??? ??? (info->platform->select_chip)(nmtd->set,chip); ??? ??? } ??? ??? cur &= ~bit; ??? } ??? writel(cur,reg); } 注: ??? s3c2410_nand_init_chip填充struct nand_chip的一部分成员,nand_scan以通用nand flash的标准进行检测,并填充struct nand_chip的其它成员,必要时根据检测结果进行取舍。 int nand_scan (struct mtd_info *mtd,int maxchips) { ??? int i,nand_maf_id,nand_dev_id,busw,maf_id; ??? struct nand_chip *this = mtd->priv;?? ???? ? //取出struct nand_chip结构 ??? /* Get buswidth to select the correct functions*/ ??? busw = this->options & NAND_BUSWIDTH_16;???? //nand flash的位宽 ??? /* check for proper chip_delay setup,set 20us if not */ ??? if (!this->chip_delay)?? ?? ?? ?? ?? ?? ?? ??? ??? this->chip_delay = 20; ??? /* check,if a user supplied command function given */ ??? if (this->cmdfunc == NULL)?? ?? ?? ?? ?? ?? //填充命令函数 ??? ??? this->cmdfunc = nand_command; ??? /* check,if a user supplied wait function given */ ??? if (this->waitfunc == NULL)?? ?? ?? ?? ?? ?? //填充等待函数 ??? ??? this->waitfunc = nand_wait; ??? if (!this->select_chip)?? ?? ?? ?? ?? ?? ??? //s3c2410_nand_init_chip中已定义 ??? ??? this->select_chip = nand_select_chip; ??? if (!this->write_byte)?? ?? ?? ?? ?? ?? ?? ? //使用默认的 ??? ??? this->write_byte = busw ? nand_write_byte16 : nand_write_byte; ??? if (!this->read_byte)?? ?? ?? ?? ?? ?? ?? ?? //使用默认的 ??? ??? this->read_byte = busw ? nand_read_byte16 : nand_read_byte; ??? if (!this->write_word)?? ?? ?? ?? ?? ?? ?? ? //使用默认的 ??? ??? this->write_word = nand_write_word; ??? if (!this->read_word)?? ?? ?? ?? ?? ?? ?? ?? //使用默认的 ??? ??? this->read_word = nand_read_word; ??? if (!this->block_bad)?? ?? ?? ?? ?? ?? ?? ?? //使用默认的 ??? ??? this->block_bad = nand_block_bad; ??? if (!this->block_markbad)?? ?? ?? ?? ?? ?? ? //使用默认的 ??? ??? this->block_markbad = nand_default_block_markbad; ??? if (!this->write_buf)?? ?? ?? ?? ?? ?? ?? ?? //s3c2410_nand_init_chip中已定义 ??? ??? this->write_buf = busw ? nand_write_buf16 : nand_write_buf; ??? if (!this->read_buf)?? ?? ?? ?? ?? ?? ?? ??? //s3c2410_nand_init_chip中已定义 ??? ??? this->read_buf = busw ? nand_read_buf16 : nand_read_buf; ??? if (!this->verify_buf)?? ?? ?? ?? ?? ?? ?? ? //使用默认的 ??? ??? this->verify_buf = busw ? nand_verify_buf16 : nand_verify_buf; ??? if (!this->scan_bbt)?? ?? ?? ?? ?? ?? ?? ??? //使用默认的 ??? ??? this->scan_bbt = nand_default_bbt; ??? /* Select the device */ ??? this->select_chip(mtd,0);?? ??? //片选,可惜在s3c2410 nand flash控制器中此操作为空 ??? /* Send the command for reading device ID */ ??? this->cmdfunc (mtd,NAND_CMD_READID,0x00,-1);//发送读ID命令 ??? /* Read manufacturer and device IDs */ ??? nand_maf_id = this->read_byte(mtd);?? ?? ?? ?? //读取生产商ID ??? nand_dev_id = this->read_byte(mtd);?? ?? ?? ?? //读取设备ID ??? /* Print and store flash device information */ ??? for (i = 0; nand_flash_ids[i].name != NULL; i++) {?? //保存着nand flash资料的nand_flash_ids表在include/linux/mtd/nand_ids.c文件中,详细见附录 ??? ??? ??? ??? ??? ??? if (nand_dev_id != nand_flash_ids[i].id)??? //比较设备ID ??? ??? ??? continue; ??? ??? if (!mtd->name) mtd->name = nand_flash_ids[i].name;?? //填充设备名 ??? ??? this->chipsize = nand_flash_ids[i].chipsize << 20;??? //填充设备大小 ??? ??? ??? ??? /* New devices have all the information in additional id bytes */ ??? ??? if (!nand_flash_ids[i].pagesize) { ??? ??? ??? int extid; ??? ??? ??? /* The 3rd id byte contains non relevant data ATM */ ??? ??? ??? extid = this->read_byte(mtd); ??? ??? ??? /* The 4th id byte is the important one */ ??? ??? ??? extid = this->read_byte(mtd); ??? ??? ??? /* Calc pagesize */ ??? ??? ??? mtd->oobblock = 1024 << (extid & 0x3); ??? ??? ??? extid >>= 2; ??? ??? ??? /* Calc oobsize */ ??? ??? ??? mtd->oobsize = (8 << (extid & 0x03)) * (mtd->oobblock / 512); ??? ??? ??? extid >>= 2; ??? ??? ??? /* Calc blocksize. Blocksize is multiples of 64KiB */ ??? ??? ??? mtd->erasesize = (64 * 1024)? << (extid & 0x03); ??? ??? ??? extid >>= 2; ??? ??? ??? /* Get buswidth information */ ??? ??? ??? busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0; ??? ??? ??? ??? } else { ??? ??? ??? /* Old devices have this data hardcoded in the ??? ??? ??? ?* device id table */ ??? ??? ??? mtd->erasesize = nand_flash_ids[i].erasesize;?? //填充檫除单元大小(16k) ??? ??? ??? mtd->oobblock = nand_flash_ids[i].pagesize;?? ? //填充页大小(512) ??? ??? ??? mtd->oobsize = mtd->oobblock / 32;?? ?? ?? ?? ? //oob大小(512/32=16) ??? ??? ??? busw = nand_flash_ids[i].options & NAND_BUSWIDTH_16;//获取nand flash表中定义的位宽 ??? ??? } ??? ??? /* Try to identify manufacturer */?? ?? ?? ?? //比较生产商ID ??? ??? for (maf_id = 0; nand_manuf_ids[maf_id].id != 0x0; maf_id++) { ??? ??? ??? if (nand_manuf_ids[maf_id].id == nand_maf_id) ??? ??? ??? ??? break; ??? ??? } ??? ??? /* Check,if buswidth is correct. Hardware drivers should set ??? ??? ?* this correct ! */ ??? ??? /用户定义的位宽与芯片实际的位宽不一致,取消nand flash的片选 ??? ??? if (busw != (this->options & NAND_BUSWIDTH_16)) {??? ??? ??? ??? printk (KERN_INFO "NAND device: Manufacturer ID:" ??? ??? ??? ??? " 0x%02x,Chip ID: 0x%02x (%s %s)/n", ??? ??? ??? ??? nand_manuf_ids[maf_id].name,mtd->name); ??? ??? ??? printk (KERN_WARNING ??? ??? ??? ??? "NAND bus width %d instead %d bit/n", ??? ??? ??? ??? ??? (this->options & NAND_BUSWIDTH_16) ? 16 : 8, ??? ??? ??? ??? ??? busw ? 16 : 8); ??? ??? ??? this->select_chip(mtd,-1);//在s3c2410 nand flash控制器驱动中,此操作为空操作 ??? ??? ??? return 1;??? ??? ??? } ??? ??? ??? ??? /* Calculate the address shift from the page size */ ??? ??? //计算页、可檫除单元、nand flash大小的偏移值? ??? ??? this->page_shift = ffs(mtd->oobblock) - 1; ??? ??? this->bbt_erase_shift = this->phys_erase_shift = ffs(mtd->erasesize) - 1; ??? ??? this->chip_shift = ffs(this->chipsize) - 1; ??? ??? /* Set the bad block position */ ??? ??? //标注此nand flash为大页还是小页? ??? ??? this->badblockpos = mtd->oobblock > 512 ? ??? ??? ??? NAND_LARGE_BADBLOCK_POS : NAND_SMALL_BADBLOCK_POS; ??? ??? /* Get chip options,preserve non chip based options */ ??? ??? //用户没指定的选项从nand flash表中获取补上 ??? ??? this->options &= ~NAND_CHIPOPTIONS_MSK; ??? ??? this->options |= nand_flash_ids[i].options & NAND_CHIPOPTIONS_MSK; ??? ??? /* Set this as a default. Board drivers can override it,if neccecary */ ??? ??? this->options |= NAND_NO_AUTOINCR; ??? ??? /* Check if this is a not a samsung device. Do not clear the options ??? ??? ?* for chips which are not having an extended id. ??? ??? ?*/??? ??? ??? if (nand_maf_id != NAND_MFR_SAMSUNG && !nand_flash_ids[i].pagesize) ??? ??? ??? this->options &= ~NAND_SAMSUNG_LP_OPTIONS; ??? ??? ??? ??? /* Check for AND chips with 4 page planes */ ??? ??? if (this->options & NAND_4PAGE_ARRAY) ??? ??? ??? this->erase_cmd = multi_erase_cmd; ??? ??? else ??? ??? ??? this->erase_cmd = single_erase_cmd;?? ?? ??? ??? /* Do not replace user supplied command function ! */ ??? ??? if (mtd->oobblock > 512 && this->cmdfunc == nand_command) ??? ??? ??? this->cmdfunc = nand_command_lp; ??? ??? ??? ??? ??? ??? printk (KERN_INFO "NAND device: Manufacturer ID:" ??? ??? ??? " 0x%02x, ??? ??? ??? nand_manuf_ids[maf_id].name,nand_flash_ids[i].name); ??? ??? break; ??? }//好的,检测结束^_^ ??? if (!nand_flash_ids[i].name) {?? ?? ??? ??? printk (KERN_WARNING "No NAND device found!!!/n"); ??? ??? this->select_chip(mtd,-1); ??? ??? return 1; ??? } ??? //统计一下同种类型的nand flash有多少块(我板上只有一块) ??? for (i=1; i < maxchips; i++) { ??? ??? this->select_chip(mtd,i); ??? ??? /* Send the command for reading device ID */ ??? ??? this->cmdfunc (mtd,-1); ??? ??? /* Read manufacturer and device IDs */ ??? ??? if (nand_maf_id != this->read_byte(mtd) || ??? ??? ??? nand_dev_id != this->read_byte(mtd)) ??? ??? ??? break; ??? } ??? if (i > 1) ??? ??? printk(KERN_INFO "%d NAND chips detected/n",i); ??? ??? /* Allocate buffers,if neccecary */ ??? if (!this->oob_buf) { ??? ??? size_t len; ??? ??? //求出一个檫除单元64K中oob所占用的总空间 ??? ??? len = mtd->oobsize << (this->phys_erase_shift - this->page_shift); ??? ??? this->oob_buf = kmalloc (len,GFP_KERNEL); ??? ??? if (!this->oob_buf) { ??? ??? ??? printk (KERN_ERR "nand_scan(): Cannot allocate oob_buf/n"); ??? ??? ??? return -ENOMEM; ??? ??? } ??? ??? this->options |= NAND_OOBBUF_ALLOC;//oob空间已分配,置相应的标志位 ??? } ??? ??? if (!this->data_buf) { ??? ??? size_t len; ??? ??? len = mtd->oobblock + mtd->oobsize;//512+16=128 ??? ??? this->data_buf = kmalloc (len,GFP_KERNEL); ??? ??? if (!this->data_buf) { ??? ??? ??? if (this->options & NAND_OOBBUF_ALLOC) ??? ??? ??? ??? kfree (this->oob_buf); ??? ??? ??? printk (KERN_ERR "nand_scan(): Cannot allocate data_buf/n"); ??? ??? ??? return -ENOMEM; ??? ??? } ??? ??? this->options |= NAND_DATABUF_ALLOC;//数据空间已分配,置相应的标志位 ??? } ??? /* Store the number of chips and calc total size for mtd */ ??? this->numchips = i;//记录nand flash片数 ??? mtd->size = i * this->chipsize;//计算出nand flash总大小 ??? /* Convert chipsize to number of pages per chip -1. */ ??? this->pagemask = (this->chipsize >> this->page_shift) - 1;//(64M>>9)-1=128k-1=0x1ffff ??? /* Preset the internal oob buffer */ ??? //oob_buf全部置为0xff ??? memset(this->oob_buf,0xff,mtd->oobsize << (this->phys_erase_shift - this->page_shift)); ??? /* If no default placement scheme is given,select an ??? ?* appropriate one */ ??? if (!this->autooob) {?? //我们选用的是NAND_ECC_SOFT,autooob未设置 ??? ??? /* Select the appropriate default oob placement scheme for ??? ??? ?* placement agnostic filesystems */ ??? ??? switch (mtd->oobsize) { ??? ??? case 8: ??? ??? ??? this->autooob = &nand_oob_8; ??? ??? ??? break; ??? ??? case 16: ??? ??? ??? this->autooob = &nand_oob_16;//我们的nand flash属于这一类 ??? ??? ??? break; ??? ??? case 64: ??? ??? ??? this->autooob = &nand_oob_64; ??? ??? ??? break; ??? ??? default: ??? ??? ??? printk (KERN_WARNING "No oob scheme defined for oobsize %d/n", ??? ??? ??? ??? mtd->oobsize); ??? ??? ??? BUG(); ??? ??? } ??? } 注: ??? ECC的东西不是很懂,先跳过^_^ ? ??? /* The number of bytes available for the filesystem to place fs dependend ??? ?* oob data */ ??? mtd->oobavail = 0; ??? for (i = 0; this->autooob->oobfree[i][1]; i++) ??? ??? mtd->oobavail += this->autooob->oobfree[i][1]; ??? /* ??? ?* check ECC mode,default to software ??? ?* if 3byte/512byte hardware ECC is selected and we have 256 byte pagesize ??? ?* fallback to software ECC ??? */ ??? this->eccsize = 256;??? /* set default eccsize */??? ??? this->eccbytes = 3; ??? switch (this->eccmode) { ??? case NAND_ECC_HW12_2048: ??? ??? if (mtd->oobblock < 2048) { ??? ??? ??? printk(KERN_WARNING "2048 byte HW ECC not possible on %d byte page size,fallback to SW ECC/n", ??? ??? ??? ?????? mtd->oobblock); ??? ??? ??? this->eccmode = NAND_ECC_SOFT; ??? ??? ??? this->calculate_ecc = nand_calculate_ecc; ??? ??? ??? this->correct_data = nand_correct_data; ??? ??? } else ??? ??? ??? this->eccsize = 2048; ??? ??? break; ??? case NAND_ECC_HW3_512: ??? case NAND_ECC_HW6_512: ??? case NAND_ECC_HW8_512: ??? ??? if (mtd->oobblock == 256) { ??? ??? ??? printk (KERN_WARNING "512 byte HW ECC not possible on 256 Byte pagesize,fallback to SW ECC /n"); ??? ??? ??? this->eccmode = NAND_ECC_SOFT; ??? ??? ??? this->calculate_ecc = nand_calculate_ecc; ??? ??? ??? this->correct_data = nand_correct_data; ??? ??? } else ??? ??? ??? this->eccsize = 512; /* set eccsize to 512 */ ??? ??? break; ??? ??? ??? ??? case NAND_ECC_HW3_256: ??? ??? break; ??? ??? ??? case NAND_ECC_NONE: ??? ??? printk (KERN_WARNING "NAND_ECC_NONE selected by board driver. This is not recommended !!/n"); ??? ??? this->eccmode = NAND_ECC_NONE; ??? ??? break; ??? case NAND_ECC_SOFT:??? ??? ??? this->calculate_ecc = nand_calculate_ecc; ??? ??? this->correct_data = nand_correct_data; ??? ??? break; ??? default: ??? ??? printk (KERN_WARNING "Invalid NAND_ECC_MODE %d/n",this->eccmode); ??? ??? BUG();??? ??? }??? ??? /* Check hardware ecc function availability and adjust number of ecc bytes per ??? ?* calculation step ??? */ ??? switch (this->eccmode) { ??? case NAND_ECC_HW12_2048: ??? ??? this->eccbytes += 4; ??? case NAND_ECC_HW8_512: ??? ??? this->eccbytes += 2; ??? case NAND_ECC_HW6_512: ??? ??? this->eccbytes += 3; ??? case NAND_ECC_HW3_512: ??? case NAND_ECC_HW3_256: ??? ??? if (this->calculate_ecc && this->correct_data && this->enable_hwecc) ??? ??? ??? break; ??? ??? printk (KERN_WARNING "No ECC functions supplied,Hardware ECC not possible/n"); ??? ??? BUG();??? ??? } ??? ??? ??? mtd->eccsize = this->eccsize; ??? ??? /* Set the number of read / write steps for one page to ensure ECC generation */ ??? switch (this->eccmode) { ??? case NAND_ECC_HW12_2048: ??? ??? this->eccsteps = mtd->oobblock / 2048; ??? ??? break; ??? case NAND_ECC_HW3_512: ??? case NAND_ECC_HW6_512: ??? case NAND_ECC_HW8_512: ??? ??? this->eccsteps = mtd->oobblock / 512; ??? ??? break; ??? case NAND_ECC_HW3_256: ??? case NAND_ECC_SOFT:??? ??? ??? this->eccsteps = mtd->oobblock / 256; ??? ??? break; ??? ??? ??? case NAND_ECC_NONE: ??? ??? this->eccsteps = 1; ??? ??? break; ??? } ??? ??? /* Initialize state,waitqueue and spinlock */ ??? this->state = FL_READY; ??? init_waitqueue_head (&this->wq); ??? spin_lock_init (&this->chip_lock); ??? /* De-select the device */ ??? this->select_chip(mtd,-1); ??? /* Invalidate the pagebuffer reference */ ??? this->pagebuf = -1; ??? /* Fill in remaining MTD driver data */ ??? //填充mtd结构的其它部分 ??? mtd->type = MTD_NANDFLASH; ??? mtd->flags = MTD_CAP_NANDFLASH | MTD_ECC; ??? mtd->ecctype = MTD_ECC_SW; ??? mtd->erase = nand_erase; ??? mtd->point = NULL; ??? mtd->unpoint = NULL; ??? mtd->read = nand_read; ??? /* nand_read->nand_do_read_ecc->read_buf->s3c2410_nand_read_buf */ ??? mtd->write = nand_write; ??? /* nand_write->nand_write_ecc->nand_write_page->write_buf->s3c2410_nand_write_buf */ ??? mtd->read_ecc = nand_read_ecc; ??? mtd->write_ecc = nand_write_ecc; ??? mtd->read_oob = nand_read_oob; ??? mtd->write_oob = nand_write_oob; ??? mtd->readv = NULL; ??? mtd->writev = nand_writev; ??? mtd->writev_ecc = nand_writev_ecc; ??? mtd->sync = nand_sync; ??? mtd->lock = NULL; ??? mtd->unlock = NULL; ??? mtd->suspend = NULL; ??? mtd->resume = NULL; ??? mtd->block_isbad = nand_block_isbad; ??? mtd->block_markbad = nand_block_markbad; ??? /* and make the autooob the default one */ ??? memcpy(&mtd->oobinfo,this->autooob,sizeof(mtd->oobinfo)); ??? mtd->owner = THIS_MODULE; ??? ??? /* Check,if we should skip the bad block table scan */ ??? if (this->options & NAND_SKIP_BBTSCAN) ??? ??? return 0; ??? /* Build bad block table */ ??? return this->scan_bbt (mtd); } /** ?* nand_command - [DEFAULT] Send command to NAND device ?* @mtd:??? MTD device structure ?* @command:??? the command to be sent ?* @column:??? the column address for this command,-1 if none ?* @page_addr:??? the page address for this command,-1 if none ?* ?* Send command to NAND device. This function is used for small page ?* devices (256/512 Bytes per page) ?*/ static void nand_command (struct mtd_info *mtd,unsigned command,int column,int page_addr) { ??? register struct nand_chip *this = mtd->priv; ??? /* Begin command latch cycle */ ??? this->hwcontrol(mtd,NAND_CTL_SETCLE);??? //选择写入S3C2410_NFCMD寄存器 ??? /* ??? ?* Write out the command to the device. ??? ?*/ ??? if (command == NAND_CMD_SEQIN) { ??? ??? int readcmd; ??? ??? if (column >= mtd->oobblock) {??? ??? //读/写位置超出512,读oob_data ??? ??? ??? /* OOB area */ ??? ??? ??? column -= mtd->oobblock; ??? ??? ??? readcmd = NAND_CMD_READOOB; ??? ??? } else if (column < 256) {??? ??? ??? //读/写位置在前512,使用read0命令 ??? ??? ??? /* First 256 bytes --> READ0 */ ??? ??? ??? readcmd = NAND_CMD_READ0; ??? ??? } else {??? ??? ??? ??? ??? ??? ??? ? //读/写位置在后512,使用read1命令 ??? ??? ??? column -= 256; ??? ??? ??? readcmd = NAND_CMD_READ1; ??? ??? } ??? ??? this->write_byte(mtd,readcmd);??? ??? //写入具体命令 ??? } ??? this->write_byte(mtd,command); ??? /* Set ALE and clear CLE to start address cycle */ ??? /* 清楚CLE,锁存命令;置位ALE,开始传输地址 */ ??? this->hwcontrol(mtd,NAND_CTL_CLRCLE);??? ? //锁存命令 ??? if (column != -1 || page_addr != -1) { ??? ??? this->hwcontrol(mtd,NAND_CTL_SETALE);? //选择写入S3C2410_NFADDR寄存器 ??? ??? /* Serially input address */ ??? ??? if (column != -1) { ??? ??? ??? /* Adjust columns for 16 bit buswidth */ ??? ??? ??? if (this->options & NAND_BUSWIDTH_16) ??? ??? ??? ??? column >>= 1; ??? ??? ??? this->write_byte(mtd,column);??? ? //写入列地址 ??? ??? } ??? ??? if (page_addr != -1) {??? ??? ??? ????? //写入页地址(分三个字节写入) ??? ??? ??? this->write_byte(mtd,(unsigned char) (page_addr & 0xff)); ??? ??? ??? this->write_byte(mtd,(unsigned char) ((page_addr >> 8) & 0xff)); ??? ??? ??? /* One more address cycle for devices > 32MiB */ ??? ??? ??? if (this->chipsize > (32 << 20)) ??? ??? ??? ??? this->write_byte(mtd,(unsigned char) ((page_addr >> 16) & 0x0f)); ??? ??? } ??? ??? /* Latch in address */ ??? ??? /* 锁存地址 */ ??? ??? this->hwcontrol(mtd,NAND_CTL_CLRALE); ??? } ??? /* ??? ?* program and erase have their own busy handlers ??? ?* status and sequential in needs no delay ??? */ ??? switch (command) { ??? ??? ??? ??? case NAND_CMD_PAGEPROG: ??? case NAND_CMD_ERASE1: ??? case NAND_CMD_ERASE2: ??? case NAND_CMD_SEQIN: ??? case NAND_CMD_STATUS: ??? ??? return; ??? case NAND_CMD_RESET:?? ?? //复位操作 ??? ??? ??? ??? ??? ??? ??? ? // 等待nand flash become ready ???? ?? if (this->dev_ready)? //判断nand flash 是否busy(1:ready 0:busy) ??? ??? ??? break; ??? ??? udelay(this->chip_delay); ??? ??? this->hwcontrol(mtd,NAND_CTL_SETCLE); ??? ??? this->write_byte(mtd,NAND_CMD_STATUS); ??? ??? this->hwcontrol(mtd,NAND_CTL_CLRCLE); ??? ??? while ( !(this->read_byte(mtd) & NAND_STATUS_READY)); ??? ??? return; ??? /* This applies to read commands */??? ??? default: ??? ??? /* ??? ??? ?* If we don't have access to the busy pin,we apply the given ??? ??? ?* command delay ??? ??? */ ??? ??? if (!this->dev_ready) { ??? ??? ??? udelay (this->chip_delay);//稍作延迟 ??? ??? ??? return; ??? ??? }??? ??? } ??? /* Apply this short delay always to ensure that we do wait tWB in ??? ?* any case on any machine. */ ??? ndelay (100); ??? nand_wait_ready(mtd); } /* ?* Wait for the ready pin,after a command ?* The timeout is catched later. ?*/ static void nand_wait_ready(struct mtd_info *mtd) { ??? struct nand_chip *this = mtd->priv; ??? unsigned long??? timeo = jiffies + 2; ??? /* wait until command is processed or timeout occures */ ??? do { ??? ??? if (this->dev_ready(mtd))? ??? ??? //简单调用this->dev_ready(s3c2410_nand_devready)函数??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ???? 等待nand flash become ready ??? ??? ??? return; ??? ??? touch_softlockup_watchdog(); ??? } while (time_before(jiffies,timeo));??? } /** ?* nand_wait - [DEFAULT]? wait until the command is done ?* @mtd:??? MTD device structure ?* @this:??? NAND chip structure ?* @state:??? state to select the max. timeout value ?* ?* Wait for command done. This applies to erase and program only ?* Erase can take up to 400ms and program up to 20ms according to ?* general NAND and SmartMedia specs ?* */ /* 等待知道命令传输完成,适用于檫除和写入命令 */ static int nand_wait(struct mtd_info *mtd,struct nand_chip *this,int state) { ??? unsigned long??? timeo = jiffies; ??? int??? status; ??? ??? if (state == FL_ERASING) ??? ??? ?timeo += (HZ * 400) / 1000;//檫除操作的话,时间相对要长一些 ??? else ??? ??? ?timeo += (HZ * 20) / 1000; ??? /* Apply this short delay always to ensure that we do wait tWB in ??? ?* any case on any machine. */ ??? ndelay (100); ??? if ((state == FL_ERASING) && (this->options & NAND_IS_AND)) ??? ??? this->cmdfunc (mtd,NAND_CMD_STATUS_MULTI,-1,-1); ??? else??? ??? ??? this->cmdfunc (mtd,NAND_CMD_STATUS,-1); ??? while (time_before(jiffies,timeo)) {??? ??? ??? ??? /* Check,if we were interrupted */ ??? ??? if (this->state != state) ??? ??? ??? return 0; ??? ??? /* 等待nand flash become ready */ ??? ??? if (this->dev_ready) { ??? ??? ??? if (this->dev_ready(mtd)) ??? ??? ??? ??? break;??? ??? ??? } else { ??? ??? ??? if (this->read_byte(mtd) & NAND_STATUS_READY) ??? ??? ??? ??? break; ??? ??? } ??? ??? cond_resched(); ??? } ??? status = (int) this->read_byte(mtd); ??? return status; } /** ?* nand_block_bad - [DEFAULT] Read bad block marker from the chip ?* 检查nand flash中某一页是否为坏块 ?* @mtd:??? MTD device structure ?* @ofs:??? offset from device start ?* @getchip:??? 0,if the chip is already selected ?* ?* Check,if the block is bad. ?*/ static int nand_block_bad(struct mtd_info *mtd,loff_t ofs,int getchip) { ??? int page,chipnr,res = 0; ??? struct nand_chip *this = mtd->priv; ??? u16 bad; ??? if (getchip) { ??? ??? page = (int)(ofs >> this->page_shift); ??? ??? chipnr = (int)(ofs >> this->chip_shift); ??? ??? /* Grab the lock and see if the device is available */ ??? ??? nand_get_device (this,mtd,FL_READING); ??? ??? /* Select the NAND device */ ??? ??? this->select_chip(mtd,chipnr); ??? } else ??? ??? page = (int) ofs;??? ??? if (this->options & NAND_BUSWIDTH_16) { ??? ??? this->cmdfunc (mtd,NAND_CMD_READOOB,this->badblockpos & 0xFE,page & this->pagemask); ??? ??? bad = cpu_to_le16(this->read_word(mtd)); ??? ??? if (this->badblockpos & 0x1) ??? ??? ??? bad >>= 1; ??? ??? if ((bad & 0xFF) != 0xff) ??? ??? ??? res = 1; ??? } else { ??? ??? this->cmdfunc (mtd,this->badblockpos,page & this->pagemask); ??? ??? /* 发送读oob_data命令(oob_data的badblockpos (第6)位记录着坏块标志) */ ??? ??? if (this->read_byte(mtd) != 0xff)//坏块 ??? ??? ??? res = 1; ??? } ??? ??? ??? if (getchip) { ??? ??? /* Deselect and wake up anyone waiting on the device */ ??? ??? nand_release_device(mtd); ??? }??? ??? ??? return res; } /** ?* nand_default_block_markbad - [DEFAULT] mark a block bad ?* 标志坏块 ?* @mtd:??? MTD device structure ?* @ofs:??? offset from device start ?* ?* This is the default implementation,which can be overridden by ?* a hardware specific driver. */ static int nand_default_block_markbad(struct mtd_info *mtd,loff_t ofs) { ??? struct nand_chip *this = mtd->priv; ??? u_char buf[2] = {0,0}; ??? size_t??? retlen; ??? int block; ??? ??? /* Get block number */ ??? block = ((int) ofs) >> this->bbt_erase_shift; ??? if (this->bbt) ??? ??? this->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1); ??? /* ??? ?? 这个暂时不是很好说:内核维护一个标志bad block表,使用2bit来表示1block。 ??? ?? 这个表在开机的时候通过扫描nand flash每个block的头两页的oob数据来生成, ??? ?? 发现坏块后至相应的block标志位为非零(有时候至3,但有时候至1,还没搞明白有什么不同) ???? */ ??? /* Do we have a flash based bad block table ? */ ??? if (this->options & NAND_USE_FLASH_BBT)//samsun nand flash不属于这种,暂时不去研究,以后同 ??? ??? return nand_update_bbt (mtd,ofs); ??? ??? ??? /* We write two bytes,so we dont have to mess with 16 bit access */ ??? ofs += mtd->oobsize + (this->badblockpos & ~0x01);//??????????????? ??? return nand_write_oob (mtd,ofs,2,&retlen,buf); } /** ?* nand_verify_buf - [DEFAULT] Verify chip data against buffer ?* 检验nand flash与buffer的数据是否一致 ?* @mtd:??? MTD device structure ?* @buf:??? buffer containing the data to compare ?* @len:??? number of bytes to compare ?* ?* Default verify function for 8bit buswith ?*/ static int nand_verify_buf(struct mtd_info *mtd,const u_char *buf,int len) { ??? int i; ??? struct nand_chip *this = mtd->priv; ??? for (i=0; i<len; i++) ??? ??? if (buf[i] != readb(this->IO_ADDR_R)) ??? ??? ??? return -EFAULT; ??? return 0; } /** ?* nand_default_bbt - [NAND Interface] Select a default bad block table for the device ?* @mtd:??? MTD device structure ?* ?* This function selects the default bad block table ?* support for the device and calls the nand_scan_bbt function ?* */ int nand_default_bbt (struct mtd_info *mtd) { ??? struct nand_chip *this = mtd->priv; ??? ??? /* Default for AG-AND. We must use a flash based ??? ?* bad block table as the devices have factory marked ??? ?* _good_ blocks. Erasing those blocks leads to loss ??? ?* of the good / bad information,so we _must_ store ??? ?* this information in a good / bad table during ??? ?* startup ??? */ ??? if (this->options & NAND_IS_AND) { ??? ??? /* Use the default pattern descriptors */ ??? ??? if (!this->bbt_td) {??? ??? ??? ??? this->bbt_td = &bbt_main_descr; ??? ??? ??? this->bbt_md = &bbt_mirror_descr; ??? ??? }??? ??? ??? this->options |= NAND_USE_FLASH_BBT; ??? ??? return nand_scan_bbt (mtd,&agand_flashbased); ??? } ??? ??? ??? /* Is a flash based bad block table requested ? */ ??? if (this->options & NAND_USE_FLASH_BBT) { ??? ??? /* Use the default pattern descriptors */??? ??? ??? if (!this->bbt_td) {??? ??? ??? ??? this->bbt_td = &bbt_main_descr; ??? ??? ??? this->bbt_md = &bbt_mirror_descr; ??? ??? } ??? ??? if (!this->badblock_pattern) { ??? ??? ??? this->badblock_pattern = (mtd->oobblock > 512) ? ??? ??? ??? ??? &largepage_flashbased : &smallpage_flashbased; ??? ??? } ??? } else {?? ?? //samsun nand flash的坏块表不存在与nand flash里面,需要扫描来生成。 ??? ??? this->bbt_td = NULL; ??? ??? this->bbt_md = NULL; ??? ??? if (!this->badblock_pattern) { ??? ??? ??? this->badblock_pattern = (mtd->oobblock > 512) ? ??? ??? ??? ??? &largepage_memorybased : &smallpage_memorybased; ??? ??? } ??? } ??? return nand_scan_bbt (mtd,this->badblock_pattern); } /** ?* nand_scan_bbt - [NAND Interface] scan,find,read and maybe create bad block table(s) ?* @mtd:??? MTD device structure ?* @bd:??? ??? descriptor for the good/bad block search pattern ?* ?* The function checks,if a bad block table(s) is/are already ?* available. If not it scans the device for manufacturer ?* marked good / bad blocks and writes the bad block table(s) to ?* the selected place. ?* ?* The bad block table memory is allocated here. It must be freed ?* by calling the nand_free_bbt function. ?* */ int nand_scan_bbt (struct mtd_info *mtd,struct nand_bbt_descr *bd) { ??? struct nand_chip *this = mtd->priv; ??? int len,res = 0; ??? uint8_t *buf; ??? struct nand_bbt_descr *td = this->bbt_td; ??? struct nand_bbt_descr *md = this->bbt_md; ??? len = mtd->size >> (this->bbt_erase_shift + 2); ??? /* Allocate memory (2bit per block) */ ??? /* 2bit per block=(2/8)byte per block,所以上面要多右移2位 */ ??? this->bbt = kmalloc (len,GFP_KERNEL); ??? if (!this->bbt) { ??? ??? printk (KERN_ERR "nand_scan_bbt: Out of memory/n"); ??? ??? return -ENOMEM; ??? } ??? /* Clear the memory bad block table */ ??? memset (this->bbt,len); ??? /* If no primary table decriptor is given,scan the device ??? ?* to build a memory based bad block table ??? ?*/ ??? if (!td) { ??? ??? if ((res = nand_memory_bbt(mtd,bd))) { ??? ??? ??? printk (KERN_ERR "nand_bbt: Can't scan flash and build the RAM-based BBT/n"); ??? ??? ??? kfree (this->bbt); ??? ??? ??? this->bbt = NULL; ??? ??? } ??? ??? return res; ??? } ??? /* Allocate a temporary buffer for one eraseblock incl. oob */ ??? /* 分配1 block所需要的oob data空间 */ ??? len = (1 << this->bbt_erase_shift); ??? len += (len >> this->page_shift) * mtd->oobsize; ??? buf = kmalloc (len,GFP_KERNEL); ??? if (!buf) { ??? ??? printk (KERN_ERR "nand_bbt: Out of memory/n"); ??? ??? kfree (this->bbt); ??? ??? this->bbt = NULL; ??? ??? return -ENOMEM; ??? } ??? ??? //由于td、md均为NULL,一下函数基本不起作用,先不去研究它 ??? /* Is the bbt at a given page ? */ ??? if (td->options & NAND_BBT_ABSPAGE) { ??? ??? res = read_abs_bbts (mtd,td,md); ??? } else {??? ??? ??? /* Search the bad block table using a pattern in oob */ ??? ??? res = search_read_bbts (mtd,md); ??? }??? ??? if (res) ??? ??? res = check_create (mtd,bd); ??? ??? /* Prevent the bbt regions from erasing / writing */ ??? mark_bbt_region (mtd,td); ??? if (md) ??? ??? mark_bbt_region (mtd,md); ??? ??? kfree (buf); ??? return res; } /** ?* nand_memory_bbt - [GENERIC] create a memory based bad block table ?* @mtd:??? MTD device structure ?* @bd:??? ??? descriptor for the good/bad block search pattern ?* ?* The function creates a memory based bbt by scanning the device ?* for manufacturer / software marked good / bad blocks */ static inline int nand_memory_bbt (struct mtd_info *mtd,struct nand_bbt_descr *bd) { ??? struct nand_chip *this = mtd->priv; ??? bd->options &= ~NAND_BBT_SCANEMPTY; ??? //我们只需要扫描oob data,不需要扫描全部(512+16bytes的数据) ??? return create_bbt (mtd,this->data_buf,bd,-1); } /** ?* create_bbt - [GENERIC] Create a bad block table by scanning the device ?* @mtd:??? MTD device structure ?* @buf:??? temporary buffer ?* @bd:??? ??? descriptor for the good/bad block search pattern ?* @chip:??? create the table for a specific chip,-1 read all chips. ?*??? ??? Applies only if NAND_BBT_PERCHIP option is set ?* ?* Create a bad block table by scanning the device ?* for the given good/bad block identify pattern ?*/ static int create_bbt (struct mtd_info *mtd,uint8_t *buf,struct nand_bbt_descr *bd,int chip) { ??? struct nand_chip *this = mtd->priv; ??? int i,j,numblocks,len,scanlen; ??? int startblock; ??? loff_t from; ??? size_t readlen,ooblen; ??? printk (KERN_INFO "Scanning device for bad blocks/n"); ??? if (bd->options & NAND_BBT_SCANALLPAGES)//扫描所有都页 ??? ??? len = 1 << (this->bbt_erase_shift - this->page_shift);//求出每block所含的page数 ??? else { ??? ??? if (bd->options & NAND_BBT_SCAN2NDPAGE)//只检查2 page ??? ??? ??? len = 2; ??? ??? else??? ??? ??? ??? len = 1;//只检查1 page ??? } ??? if (!(bd->options & NAND_BBT_SCANEMPTY)) { ??? ??? /* We need only read few bytes from the OOB area */ ??? ??? /* 我们只需要检查OOB的某些数据 */ ??? ??? scanlen = ooblen = 0; ??? ??? readlen = bd->len; ??? } else { ??? ??? /* Full page content should be read */ ??? ??? /* 读取整页内容 */ ??? ??? scanlen??? = mtd->oobblock + mtd->oobsize; ??? ??? readlen = len * mtd->oobblock; ??? ??? ooblen = len * mtd->oobsize; ??? } ??? if (chip == -1) { ??? ??? /* Note that numblocks is 2 * (real numblocks) here,see i+=2 below as it ??? ??? ?* makes shifting and masking less painful */ ??? ??? /* 计算出nand flash所包含都block数目(注意这里总数目经过林乘2操作)*/ ??? ??? numblocks = mtd->size >> (this->bbt_erase_shift - 1); ??? ??? startblock = 0; ??? ??? from = 0; ??? } else { ??? ??? if (chip >= this->numchips) { ??? ??? ??? printk (KERN_WARNING "create_bbt(): chipnr (%d) > available chips (%d)/n", ??? ??? ??? ??? chip + 1,this->numchips); ??? ??? ??? return -EINVAL; ??? ??? } ??? ??? numblocks = this->chipsize >> (this->bbt_erase_shift - 1); ??? ??? startblock = chip * numblocks; ??? ??? numblocks += startblock; ??? ??? from = startblock << (this->bbt_erase_shift - 1); ??? } ??? ??? for (i = startblock; i < numblocks;) { ??? ??? int ret; ??? ??? ??? ??? if (bd->options & NAND_BBT_SCANEMPTY)??? ??? //整页数据读取 ??? ??? ??? if ((ret = nand_read_raw (mtd,from,readlen,ooblen))) ??? ??? ??? ??? return ret; ??? ??? for (j = 0; j < len; j++) { ??? ??? ??? if (!(bd->options & NAND_BBT_SCANEMPTY)) { ??? ??? ??? ??? size_t retlen; ??? ??? ??? ??? ??? ??? ??? ??? /* Read the full oob until read_oob is fixed to ??? ??? ??? ??? ?* handle single byte reads for 16 bit buswidth */ ??? ??? ??? ??? /* 读取当前页的oob区的所有数据 */ ??? ??? ??? ??? ret = mtd->read_oob(mtd,from + j * mtd->oobblock, ??? ??? ??? ??? ??? ??? ??? mtd->oobsize,buf); ??? ??? ??? ??? if (ret) ??? ??? ??? ??? ??? return ret; ??? ??? ??? ??? /* 检查oob data的bad block标志位,判断是否是坏块 */ ??? ??? ??? ??? if (check_short_pattern (buf,bd)) { ??? ??? ??? ??? ??? this->bbt[i >> 3] |= 0x03 << (i & 0x6); ??? ??? ??? ??? /* 注意:这里i=实际值*2。由于一个block的状态用2bit来表示,那么一个字节可以存放4个block的状态。 ??? ??? ??? ??? ?? 这里i>>3刚好是实际block/4,4个block的状态刚好存放在this->bbt所指向的一个字节里面 ??? ??? ??? ??? ?*/ ??? ??? ??? ??? ??? printk (KERN_WARNING "Bad eraseblock %d at 0x%08x/n", ??? ??? ??? ??? ??? ??? i >> 1,(unsigned int) from); ??? ??? ??? ??? ??? break; ??? ??? ??? ??? } ??? ??? ??? } else { ??? ??? ??? ??? if (check_pattern (&buf[j * scanlen],scanlen,mtd->oobblock,bd)) { ??? ??? ??? ??? ??? this->bbt[i >> 3] |= 0x03 << (i & 0x6); ??? ??? ??? ??? ??? printk (KERN_WARNING "Bad eraseblock %d at 0x%08x/n",(unsigned int) from); ??? ??? ??? ??? ??? break; ??? ??? ??? ??? } ??? ??? ??? } ??? ??? } ??? ??? i += 2;//更新block的序号 ??? ??? from += (1 << this->bbt_erase_shift);//更新nand flash的地址 ??? } ??? return 0; } /** ?* nand_release - [NAND Interface] Free resources held by the NAND device ?* @mtd:??? MTD device structure */ void nand_release (struct mtd_info *mtd) { ??? struct nand_chip *this = mtd->priv; #ifdef CONFIG_MTD_PARTITIONS ??? /* Deregister partitions */ ??? del_mtd_partitions (mtd); #endif ??? /* Deregister the device */ ??? del_mtd_device (mtd); ??? /* Free bad block table memory,if allocated */ ??? if (this->bbt) ??? ??? kfree (this->bbt); ??? /* Buffer allocated by nand_scan ? */ ??? if (this->options & NAND_OOBBUF_ALLOC) ??? ??? kfree (this->oob_buf); ??? /* Buffer allocated by nand_scan ? */ ??? if (this->options & NAND_DATABUF_ALLOC) ??? ??? kfree (this->data_buf); } 附录: /arch/arm/mach-s3c2410/dev.c文件: static struct mtd_partition partition_info[]={ ? [0]={ ???? name??? :"vivi", ???? size??? :0x20000, ???? offset? :0, ? },[1]={ ???? name??? :"param", ???? size??? :0x10000, ???? offset? :0x20000,[2]={ ???? name??? :"kernel", ???? size??? :0x1d0000, ???? offset? :0x30000,[3]={ ???? name??? :"root", ???? size??? :0x3c00000, ???? offset? :0x200000, ? } }; struct s3c2410_nand_set nandset={ ??? nr_partitions??? :4, ??? partitions??? ?? :partition_info, }; struct s3c2410_platform_nand superlpplatform={ ??? tacls???? :0, ??? twrph0??? :30, ??? twrph1??? :0, ??? sets????? :&nandset, ??? nr_sets?? :1, }; struct platform_device s3c_device_nand = { ??? .name??? ??? ? = "s3c2410-nand", ??? .id??? ??? ? = -1, ??? .num_resources??? ? = ARRAY_SIZE(s3c_nand_resource), ??? .resource??? ? = s3c_nand_resource, ??? .dev={ ??? ??? .platform_data=&superlpplatform ??? } }; nand_flash_ids表 /driver/mtd/nand/nand_ids.c文件: struct nand_flash_dev nand_flash_ids[] = { ................................................................................ ??? {"NAND 64MiB 3,3V 8-bit",??? 0x76,512,64,0x4000,0}, ................................................................................ }; 注: ??? 这里只列出常用的samsun 64M Nand Flash的资料,对应的信息请看该结构体的定义: struct nand_flash_dev { ??? char *name; ??? int id; ??? unsigned long pagesize; ??? unsigned long chipsize; ??? unsigned long erasesize; ??? unsigned long options; }; 可知该nand flash 设备ID号为0x76,页大小为512,大小为64(M),檫除单元大小为16(K)。? (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
相关内容
- ORA-38760: This database instance failed to turn on fla
- .net – 是否通过LINQ从XDocument读取的项目顺序,保证?
- ruby – 在markdown文件中编辑YAML Frontmatter
- ruby-on-rails – 无法在dockerized rails应用程序中安装gi
- C#中WPF ListView绑定数据的实例详解
- c# – “输入字符串格式不正确”.
- vb.net 教程 3-5 窗体编程 对话框3 ColorDialog & Font
- flex总结
- 组件提供的方法应单一、简单且不失全面
- 正则表达式不适用于jquery验证插件