NAND驱动分析--(二)
来源:互联网 发布:hadoop源码有多少 编辑:程序博客网 时间:2024/05/17 02:34
在上一篇nand驱动分析中,大概描述了nand flash驱动加载时的初始化流程,接下来对其调用的一些函数进行进一步的阐述。
首先,上一篇说到调用了fsl_elbc_chip_init()函数,此函数还是在drivers\mtd\nand\fsl_elbc_nand.c文件中被定义,其代码如下所示:
static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv){ struct fsl_lbc_ctrl *ctrl = priv->ctrl; struct fsl_lbc_regs __iomem *lbc = ctrl->regs; //将寄存器基址赋值给lbc struct nand_chip *chip = &priv->chip; //将priv结构的chip成员地址传递给此函数中的chip指针,这样就可以对priv结构中的chip成员进行一系列初始化 printk("eLBC Set Information for bank %d\n", priv->bank); priv->mtd.priv = chip; //将chip指针传递给主分区mtd中的私有数据,其类型为nand_chip priv->mtd.owner = THIS_MODULE; priv->fmr = in_be32(&lbc->fmr) & FMR_ECCM; /* 初始化nand_chip中的一些基本操作函数 */ chip->read_byte = fsl_elbc_read_byte; //读数据函数 chip->write_buf = fsl_elbc_write_buf; chip->read_buf = fsl_elbc_read_buf; chip->verify_buf = fsl_elbc_verify_buf; chip->select_chip = fsl_elbc_select_chip; chip->cmdfunc = fsl_elbc_cmdfunc; //写命令函数 chip->waitfunc = fsl_elbc_wait; //等待命令执行完毕函数 chip->bbt_td = &bbt_main_descr; chip->bbt_md = &bbt_mirror_descr; chip->options = NAND_NO_READRDY | NAND_NO_AUTOINCR | NAND_USE_FLASH_BBT; chip->controller = &elbc_fcm_ctrl->controller; chip->priv = priv; chip->ecc.read_page = fsl_elbc_read_page; chip->ecc.write_page = fsl_elbc_write_page; if ((in_be32(&lbc->bank[priv->bank].br) & BR_DECC) == BR_DECC_CHK_GEN) { chip->ecc.mode = NAND_ECC_HW; /* put in small page settings and adjust later if needed */ #if 0 chip->ecc.layout = (priv->fmr & FMR_ECCM) ? &fsl_elbc_oob_sp_eccm1 : &fsl_elbc_oob_sp_eccm0; #else chip->ecc.layout = (priv->fmr & FMR_ECCM) ? &fsl_elbc_oob_lp_eccm1 : &fsl_elbc_oob_lp_eccm0; #endif chip->ecc.size = 512; chip->ecc.bytes = 3; } else { chip->ecc.mode = NAND_ECC_SOFT; } chip->ecc.mode = NAND_ECC_NONE; return 0;}
以上代码的主要作用有两点:1、将主分区的mtd_info结构中的priv指针,指向nand_chip;2、初始化nand_chip结构体中的一些基本操作函数。
而在nand_chip结构体中最重要的一个操作函数是chip->cmdfunc,所以接下来以此函数进行分析,而其他的函数就不再进行详细分析。代码如下所示:
/* cmdfunc send commands to the FCM */static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command, int column, int page_addr){ struct nand_chip *chip = mtd->priv; struct fsl_elbc_mtd *priv = chip->priv; struct fsl_lbc_ctrl *ctrl = priv->ctrl; struct fsl_lbc_regs __iomem *lbc = ctrl->regs; elbc_fcm_ctrl->use_mdr = 0; /* clear the read buffer */ elbc_fcm_ctrl->read_bytes = 0; if (command != NAND_CMD_PAGEPROG) elbc_fcm_ctrl->index = 0; switch (command) //根据发送的命令来执行相应的操作 { /* READ0 and READ1 read the entire buffer to use hardware ECC. */ case NAND_CMD_READ1: column += 256; /* fall-through */ case NAND_CMD_READ0: //读数据命令 out_be32(&lbc->fbcr, 0); /* read entire page to enable ECC */ set_addr(mtd, 0, page_addr, 0); //设置要读取数据的首地址,即向elbc的块、页和列地址寄存器写值 elbc_fcm_ctrl->read_bytes = mtd->writesize + mtd->oobsize; //计算出每次读取字节的总数 elbc_fcm_ctrl->index += column; fsl_elbc_do_read(chip, 0); //向FCM模式的本地总线发送读取nand flash数据的指令 fsl_elbc_run_command(mtd); //使cpu运行刚才向elbc发送的指令 return; /* READOOB reads only the OOB because no ECC is performed. */ case NAND_CMD_READOOB: out_be32(&lbc->fbcr, mtd->oobsize - column); set_addr(mtd, column, page_addr, 1); elbc_fcm_ctrl->read_bytes = mtd->writesize + mtd->oobsize; fsl_elbc_do_read(chip, 1); fsl_elbc_run_command(mtd); return; /* READID must read all 5 possible bytes while CEB is active */ case NAND_CMD_READID: //读取芯片ID /* 向fir寄存器写入想要执行指令的步骤和顺序 */ out_be32(&lbc->fir, (FIR_OP_CM0 << FIR_OP0_SHIFT) | //执行fcr中第一条指令 (FIR_OP_UA << FIR_OP1_SHIFT) | //使用用户默认地址 (FIR_OP_RBW << FIR_OP2_SHIFT)); //等待并读取fbcr寄存器的值 out_be32(&lbc->fcr, NAND_CMD_READID << FCR_CMD0_SHIFT); //写入读取设备ID指令到fcr寄存器中的第一条指令的位置 /* 5 bytes for manuf, device and exts */ out_be32(&lbc->fbcr, 5); //向数据计数寄存器中写入要读取数据总数的值 elbc_fcm_ctrl->read_bytes = 5; elbc_fcm_ctrl->use_mdr = 1; elbc_fcm_ctrl->mdr = 0; //将FCM数据寄存器的值先清零 set_addr(mtd, 0, 0, 0); //设置地址,此时是读设备ID,所以地址为0 fsl_elbc_run_command(mtd); //开始运行fir以及fcr寄存器中的指令 return; /* 从此处可知,擦除命令分为两个步骤,NAND_CMD_ERASE1命令用于设置想要擦除内容的首地址,NAND_CMD_ERASE2命令用于擦除步骤1中设置的地址所指向的内容 */ /* ERASE1 stores the block and page address */ case NAND_CMD_ERASE1: //擦除命令1 set_addr(mtd, 0, page_addr, 0); //设置要擦除nand块的首地址 return; /* ERASE2 uses the block and page address from ERASE1 */ case NAND_CMD_ERASE2: //擦除命令2 out_be32(&lbc->fir, (FIR_OP_CM0 << FIR_OP0_SHIFT) | (FIR_OP_PA << FIR_OP1_SHIFT) | (FIR_OP_CM2 << FIR_OP2_SHIFT) | (FIR_OP_CW1 << FIR_OP3_SHIFT) | (FIR_OP_RS << FIR_OP4_SHIFT)); out_be32(&lbc->fcr, (NAND_CMD_ERASE1 << FCR_CMD0_SHIFT) | (NAND_CMD_STATUS << FCR_CMD1_SHIFT) | (NAND_CMD_ERASE2 << FCR_CMD2_SHIFT)); out_be32(&lbc->fbcr, 0); elbc_fcm_ctrl->read_bytes = 0; elbc_fcm_ctrl->use_mdr = 1; fsl_elbc_run_command(mtd); return; /* RESET without waiting for the ready line */ case NAND_CMD_RESET: //nand flash芯片复位操作 out_be32(&lbc->fir, FIR_OP_CM0 << FIR_OP0_SHIFT); out_be32(&lbc->fcr, NAND_CMD_RESET << FCR_CMD0_SHIFT); fsl_elbc_run_command(mtd); return; default: dev_err(priv->dev, "fsl_elbc_cmdfunc: error, unsupported command 0x%x.\n", command); }}
从上述代码可以看出,elbc是如何通过数据、地址总线向nand flash发送命令、地址等数据的。
操作nand flash的方法,如下所示:
eLBC_FIR和eLBC_FCR这两个寄存器是操作nand flash的关键。
1、eLBC_FIR寄存器,向此寄存器中写入一些操作码,即可实现相应的功能,其功能码如下所示:
/* FIR寄存器一共有8个可编程指令序列,而此指令为4bit位的操作码 */#define FIR_OP0 0xF0000000#define FIR_OP0_SHIFT 28#define FIR_OP1 0x0F000000#define FIR_OP1_SHIFT 24#define FIR_OP2 0x00F00000#define FIR_OP2_SHIFT 20#define FIR_OP3 0x000F0000#define FIR_OP3_SHIFT 16#define FIR_OP4 0x0000F000#define FIR_OP4_SHIFT 12#define FIR_OP5 0x00000F00#define FIR_OP5_SHIFT 8#define FIR_OP6 0x000000F0#define FIR_OP6_SHIFT 4#define FIR_OP7 0x0000000F#define FIR_OP7_SHIFT 0/* No operation and end of sequence */#define FIR_OP_NOP 0x0 /* Issue current column address */#define FIR_OP_CA 0x1 /* Issue current block+page address */ #define FIR_OP_PA 0x2 /* Issue user defined address */#define FIR_OP_UA 0x3 /* Issue command from FCR[CMD0] */ #define FIR_OP_CM0 0x4 /* Issue command from FCR[CMD1] */ #define FIR_OP_CM1 0x5 /* Issue command from FCR[CMD2] */ #define FIR_OP_CM2 0x6 /* Issue command from FCR[CMD3] */ #define FIR_OP_CM3 0x7 /* Write FBCR bytes from FCM buffer */ #define FIR_OP_WB 0x8 /* Write 1 or 2 bytes from MDR[AS] */ #define FIR_OP_WS 0x9 /* Read FBCR bytes to FCM buffer */ #define FIR_OP_RB 0xA /* Read 1 or 2 bytes to MDR[AS] */ #define FIR_OP_RS 0xB /* Wait then issue FCR[CMD0] */ #define FIR_OP_CW0 0xC /* Wait then issue FCR[CMD1] */ #define FIR_OP_CW1 0xD /* Wait then read FBCR bytes */#define FIR_OP_RBW 0xE /* Wait then read 1 or 2 bytes */#define FIR_OP_RSW 0xE
2、eLBC_FCR寄存器,而此寄存器中则主要根据nand芯片手册的一些操作命令来写入nand flash芯片的一些指令,如0x90表示读取芯片ID等。
最后再运行fsl_elbc_run_command(mtd);函数即可根据上面的两个寄存器的值来进行nand操作。
接下来就会执行到nand_scan (&priv->mtd, 1)函数,此函数用于扫描连接到elbc的外部nand flash,具体代码如下所示:
int nand_scan (struct mtd_info *mtd, int maxchips){ int ret; int i, j, nand_maf_id, nand_dev_id, busw; struct nand_chip *this = mtd->priv; /* Get buswidth to select the correct functions*/ busw = this->options & NAND_BUSWIDTH_16; /* 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) 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) this->write_buf = busw ? nand_write_buf16 : nand_write_buf; if (!this->read_buf) 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); /* Send the command for reading device ID */ this->cmdfunc (mtd, NAND_CMD_READID, 0x00, -1); //读取nand设备ID号 /* Read manufacturer and device IDs */ nand_maf_id = this->read_byte(mtd); //将读出的ID号赋值给nand_maf_id nand_dev_id = this->read_byte(mtd); //将读出的ID号赋值给nand_dev_id /* Print and store flash device information */ /* 轮询定义的芯片信息有没有与读出的设备ID一致的芯片 */ for (i = 0; nand_flash_ids[i].name != NULL; i++) { if (nand_dev_id != nand_flash_ids[i].id) continue; /* 查找到对应的芯片信息后,根据芯片信息将nand_chip结构体补充完整 */ 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); mtd->writesize = 1024 << (extid & 0x3); extid >>= 2; /* Calc oobsize */ mtd->oobsize = (8 << (extid & 0x01)) * (mtd->writesize >> 9); 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; mtd->oobblock = nand_flash_ids[i].pagesize; mtd->oobsize = mtd->oobblock / 32; busw = nand_flash_ids[i].options & NAND_BUSWIDTH_16; mtd->writesize = nand_flash_ids[i].pagesize; } /* Check, if buswidth is correct. Hardware drivers should set * this correct ! */ if (busw != (this->options & NAND_BUSWIDTH_16)) { printk (KERN_INFO "NAND device: Manufacturer ID:" " 0x%02x, Chip ID: 0x%02x (%s %s)\n", nand_maf_id, nand_dev_id, nand_manuf_ids[i].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); return 1; } /* Calculate the address shift from the page size */ this->page_shift = ffs(mtd->oobblock) - 1; /* Convert chipsize to number of pages per chip -1. */ this->pagemask = (this->chipsize >> this->page_shift) - 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 */ this->badblockpos = mtd->oobblock > 512 ? NAND_LARGE_BADBLOCK_POS : NAND_SMALL_BADBLOCK_POS; /* Get chip options, preserve non chip based options */ 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; /* Try to identify manufacturer */ for (j = 0; nand_manuf_ids[j].id != 0x0; j++) { if (nand_manuf_ids[j].id == nand_maf_id) break; } printk (KERN_INFO "NAND device: Manufacturer ID:" " 0x%02x, Chip ID: 0x%02x (%s %s)\n", nand_maf_id, nand_dev_id, nand_manuf_ids[j].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; } for (i=1; i < maxchips; i++) { this->select_chip(mtd, i); /* Send the command for reading device ID */ this->cmdfunc (mtd, NAND_CMD_READID, 0x00, -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; 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; } if (!this->data_buf) { size_t len; len = mtd->oobblock + mtd->oobsize; 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; mtd->size = i * this->chipsize; /* Convert chipsize to number of pages per chip -1. */ this->pagemask = (this->chipsize >> this->page_shift) - 1; /* Preset the internal oob buffer */ 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) { /* 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; break; case 64: this->autooob = &nand_oob_64_fsl; break; default: printk (KERN_WARNING "No oob scheme defined for oobsize %d\n", mtd->oobsize); BUG(); } } /* The number of bytes available for the filesystem to place fs dependend * oob data */ if (this->options & NAND_BUSWIDTH_16) { mtd->oobavail = mtd->oobsize - (this->autooob->eccbytes + 2); if (this->autooob->eccbytes & 0x01) mtd->oobavail--; } else mtd->oobavail = mtd->oobsize - (this->autooob->eccbytes + 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_info结构体,每一个分区都包含了一个mtd_info结构体,所以每一个分区都可以调用mtd_info结构体中的一些功能函数,对分区进行操作 */ 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; mtd->write = nand_write; 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; fsl_elbc_chip_init_tail(mtd); /* Build bad block table */ return this->scan_bbt (mtd);}
nand_scan函数的主要作用就是用于扫描外部nand flash,并与代码中已有的nand flash设备ID号进行匹配,如果匹配成功,则利用对应的nand设备信息将nand_chip结构体和mtd_info结构体填充完整。
到程序的最后,将调用add_mtd_partitions(&priv->mtd, p1020_partition_info, 3)函数来进行nand flash的分区,并将priv->mtd作为主分区,来将其它分区的mtd_info结构补充完整。其代码分析如下:
int add_mtd_partitions(struct mtd_info *master, const struct mtd_partition *parts, int nbparts){ struct mtd_part *slave; u_int32_t cur_offset = 0; int i; printk (KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n", nbparts, master->name); /* 根据nbparts参数来确定nand存储器将划分为几个分区 */ for (i = 0; i < nbparts; i++) { /* allocate the partition structure */ slave = kmalloc (sizeof(*slave), GFP_KERNEL); if (!slave) { printk ("memory allocation error while creating partitions for \"%s\"\n", master->name); del_mtd_partitions(master); return -ENOMEM; } memset(slave, 0, sizeof(*slave)); list_add(&slave->list, &mtd_partitions); /* set up the MTD object for this partition */ /* 根据主分区的mtd_info结构信息来初始化各个分区的mtd_info信息 */ slave->mtd.type = master->type; slave->mtd.flags = master->flags & ~parts[i].mask_flags; slave->mtd.size = parts[i].size; slave->mtd.oobblock = master->oobblock; slave->mtd.oobsize = master->oobsize; slave->mtd.ecctype = master->ecctype; slave->mtd.eccsize = master->eccsize; slave->mtd.name = parts[i].name; slave->mtd.bank_size = master->bank_size; slave->mtd.owner = master->owner; slave->mtd.read = part_read; slave->mtd.write = part_write; if(master->point && master->unpoint){ slave->mtd.point = part_point; slave->mtd.unpoint = part_unpoint; } if (master->read_ecc) slave->mtd.read_ecc = part_read_ecc; if (master->write_ecc) slave->mtd.write_ecc = part_write_ecc; if (master->read_oob) slave->mtd.read_oob = part_read_oob; if (master->write_oob) slave->mtd.write_oob = part_write_oob; if(master->read_user_prot_reg) slave->mtd.read_user_prot_reg = part_read_user_prot_reg; if(master->read_fact_prot_reg) slave->mtd.read_fact_prot_reg = part_read_fact_prot_reg; if(master->write_user_prot_reg) slave->mtd.write_user_prot_reg = part_write_user_prot_reg; if (master->sync) slave->mtd.sync = part_sync; if (!i && master->suspend && master->resume) { slave->mtd.suspend = part_suspend; slave->mtd.resume = part_resume; } if (master->writev) slave->mtd.writev = part_writev; if (master->readv) slave->mtd.readv = part_readv; if (master->writev_ecc) slave->mtd.writev_ecc = part_writev_ecc; if (master->readv_ecc) slave->mtd.readv_ecc = part_readv_ecc; if (master->lock) slave->mtd.lock = part_lock; if (master->unlock) slave->mtd.unlock = part_unlock; if (master->block_isbad) slave->mtd.block_isbad = part_block_isbad; if (master->block_markbad) slave->mtd.block_markbad = part_block_markbad; slave->mtd.erase = part_erase; slave->master = master; slave->offset = parts[i].offset; slave->index = i; if (slave->offset == MTDPART_OFS_APPEND) slave->offset = cur_offset; if (slave->offset == MTDPART_OFS_NXTBLK) { u_int32_t emask = master->erasesize-1; slave->offset = (cur_offset + emask) & ~emask; if (slave->offset != cur_offset) { printk(KERN_NOTICE "Moving partition %d: " "0x%08x -> 0x%08x\n", i, cur_offset, slave->offset); } } if (slave->mtd.size == MTDPART_SIZ_FULL) slave->mtd.size = master->size - slave->offset; cur_offset = slave->offset + slave->mtd.size; printk (KERN_NOTICE "0x%08x-0x%08x : \"%s\"\n", slave->offset, slave->offset + slave->mtd.size, slave->mtd.name); /* let's do some sanity checks */ if (slave->offset >= master->size) { /* let's register it anyway to preserve ordering */ slave->offset = 0; slave->mtd.size = 0; printk ("mtd: partition \"%s\" is out of reach -- disabled\n", parts[i].name); } if (slave->offset + slave->mtd.size > master->size) { slave->mtd.size = master->size - slave->offset; printk ("mtd: partition \"%s\" extends beyond the end of device \"%s\" -- size truncated to %#x\n", parts[i].name, master->name, slave->mtd.size); } if (master->numeraseregions>1) { /* Deal with variable erase size stuff */ int i; struct mtd_erase_region_info *regions = master->eraseregions; /* Find the first erase regions which is part of this partition. */ for (i=0; i < master->numeraseregions && slave->offset >= regions[i].offset; i++) ; for (i--; i < master->numeraseregions && slave->offset + slave->mtd.size > regions[i].offset; i++) { if (slave->mtd.erasesize < regions[i].erasesize) { slave->mtd.erasesize = regions[i].erasesize; } } } else { /* Single erase size */ slave->mtd.erasesize = master->erasesize; } if ((slave->mtd.flags & MTD_WRITEABLE) && (slave->offset % slave->mtd.erasesize)) { /* Doesn't start on a boundary of major erase size */ /* FIXME: Let it be writable if it is on a boundary of _minor_ erase size though */ slave->mtd.flags &= ~MTD_WRITEABLE; printk ("mtd: partition \"%s\" doesn't start on an erase block boundary -- force read-only\n", parts[i].name); } if ((slave->mtd.flags & MTD_WRITEABLE) && (slave->mtd.size % slave->mtd.erasesize)) { slave->mtd.flags &= ~MTD_WRITEABLE; printk ("mtd: partition \"%s\" doesn't end on an erase block -- force read-only\n", parts[i].name); } /* copy oobinfo from master */ memcpy(&slave->mtd.oobinfo, &master->oobinfo, sizeof(slave->mtd.oobinfo)); if(parts[i].mtdp) { /* store the object pointer (caller may or may not register it */ *parts[i].mtdp = &slave->mtd; slave->registered = 0; } else { /* register our partition */ /* 最后再根据从分区的mtd_info结构来注册分区 */ add_mtd_device(&slave->mtd); slave->registered = 1; } } return 0;}
所以此函数的主要作用就是向系统注册nand flash的分区。
此时,nand flash的硬件驱动层就分析完毕。下一篇文章将讲述mtd设备层。
- NAND驱动分析--(二)
- NAND驱动分析--(一)
- NAND驱动分析--(三)
- Linux NAND FLASH驱动分析(一)
- nand FLASH 驱动分析
- nand 驱动分析
- NAND FLASH 驱动分析
- nand flash驱动分析
- linux nand flash 驱动(二) --- 驱动实现
- 基于linux2.6.16的nand驱动开发(二)
- 基于linux2.6.16的nand驱动开发(二)
- 内核读写nand flash驱动分析(含注释)
- linux nand flash 驱动(一) --- 原理分析
- NAND FLASH学习笔记之MTD下nand flash驱动(二)
- NAND FLASH学习笔记之MTD下nand flash驱动(二)
- NAND控制器驱动程序分析(二)
- linux NAND驱动之二:NAND存储原理
- linux NAND驱动之二:NAND存储原理
- 【python 神经网络】BP神经网络python实现-iris数据集分类
- Jupyter Notebook 的快捷键
- Xcode单元测试
- 309. Best Time to Buy and Sell Stock with Cooldown
- <考试题> codevs 5251 WYW的数字金字塔
- NAND驱动分析--(二)
- mysql-----查询及索引知识点
- Java Web开发中前后端分离的技术方案和优缺点
- Test测试方法
- My97DatePicker时间控件使用
- [Leetcode] 416. Partition Equal Subset Sum 解题报告
- windows麦克风输入采集
- window mysql python安装及使用
- dokuwiki:安装与配置