MTD设备驱动--NAND flash
前面的文章MTD设备驱动(http://blog.csdn.net/paomadi/article/details/9262307)讲了mtd设备的架构组织 其中讲述了调用int add_mtd_partitions(struct mtd_info *master,const struct mtd_partition *parts,int nbparts)? 该函数一个作用是mtd_info slave来继承mtd_info master的属性方法 那么mtd_info master的属性方法在哪里设置? 针对nand flash则通过以下步骤来设置 一.分配一个nand_chip,并初始化其成员函数 struct nand_chip { void __iomem *IO_ADDR_R; void __iomem *IO_ADDR_W; uint8_t (*read_byte)(struct mtd_info *mtd); //读一个字节 u16 (*read_word)(struct mtd_info *mtd); //读一个字 void (*write_buf)(struct mtd_info *mtd,const uint8_t *buf,int len); //写缓冲区 void (*read_buf)(struct mtd_info *mtd,uint8_t *buf,int len); //读缓冲区 int (*verify_buf)(struct mtd_info *mtd,int len); //校验缓冲区 void (*select_chip)(struct mtd_info *mtd,int chip); //选择芯片 int (*block_bad)(struct mtd_info *mtd,loff_t ofs,int getchip); //坏块 int (*block_markbad)(struct mtd_info *mtd,loff_t ofs); //标记坏块 void (*cmd_ctrl)(struct mtd_info *mtd,int dat,unsigned int ctrl); //控制命令 int (*init_size)(struct mtd_info *mtd,struct nand_chip *this,u8 *id_data); //初始化大小 int (*dev_ready)(struct mtd_info *mtd); //设备准备好 void (*cmdfunc)(struct mtd_info *mtd,unsigned command,int column,int page_addr); int(*waitfunc)(struct mtd_info *mtd,struct nand_chip *this); void (*erase_cmd)(struct mtd_info *mtd,int page); //擦除,命令 int (*scan_bbt)(struct mtd_info *mtd); //扫描bbt区 int (*errstat)(struct mtd_info *mtd,int state,int status,int page); int (*write_page)(struct mtd_info *mtd,struct nand_chip *chip,int page,int cached,int raw); //页写 int chip_delay; unsigned int options; //选项 int page_shift; int phys_erase_shift; int bbt_erase_shift; int chip_shift; int numchips; //芯片个数 uint64_t chipsize; //芯片容量 int pagemask; int pagebuf; int subpagesize; uint8_t cellinfo; int badblockpos; int badblockbits; int onfi_version; struct nand_onfi_params onfi_params; flstate_t state; uint8_t *oob_poi; struct nand_hw_control *controller; struct nand_ecclayout *ecclayout; struct nand_ecc_ctrl ecc; struct nand_buffers *buffers; struct nand_hw_control hwcontrol; struct mtd_oob_ops ops; uint8_t *bbt; struct nand_bbt_descr *bbt_td; struct nand_bbt_descr *bbt_md; struct nand_bbt_descr *badblock_pattern; void *priv; }; 二.分配mtd_info结构体 初始化几个变量一般是owner模块所有者(THIS_MODULE),priv私有数据(一般选择nand_chip对象或包含nand_chip对象结构体对象),name(名字) 三.调用nand_scan函数,将第二步分配的mtd_info对象和nand flash的芯片个数传递进来 nand_scan int nand_scan(struct mtd_info *mtd,int maxchips) { int ret; if (!mtd->owner && caller_is_module()) { printk(KERN_CRIT "%s called with NULL mtd->owner!n",__func__); BUG(); } ret = nand_scan_ident(mtd,maxchips,NULL); //读取flash ID并建立MTD相应字段 if (!ret) ret = nand_scan_tail(mtd); //填充未初始化的功能函数默认函数并扫描坏块表 return ret; } EXPORT_SYMBOL(nand_scan); nand_scan函数分成两大块来完成nand_scan_ident和nand_scan_tail 1.nand_scan_ident读取flash ID并建立MTD相应字段 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; //私有数据中获取nand_chip struct nand_flash_dev *type; busw = chip->options & NAND_BUSWIDTH_16; //获取总线宽度 nand_set_defaults(chip,busw); //设置nand_chip默认方法函数 type = nand_get_flash_type(mtd,chip,&nand_maf_id,&nand_dev_id,table); //获取nand flash设备类型 if (IS_ERR(type)) { if (!(chip->options & NAND_SCAN_SILENT_NODEV)) printk(KERN_WARNING "No NAND device found.n"); chip->select_chip(mtd,-1); return PTR_ERR(type); } for (i = 1; i < maxchips; i++) { //芯片个数 chip->select_chip(mtd,i); //调用select_chip方法选中芯片 chip->cmdfunc(mtd,NAND_CMD_RESET,-1,-1); //调用NAND_CMD_RESET命令重启设备 chip->cmdfunc(mtd,NAND_CMD_READID,0x00,-1); //调用NAND_CMD_READID命令读取设备ID if (nand_maf_id != chip->read_byte(mtd) || nand_dev_id != chip->read_byte(mtd)) //读厂商ID和设备ID break; } if (i > 1) printk(KERN_INFO "%d NAND chips detectedn",i); chip->numchips = i; //芯片数 mtd->size = i * chip->chipsize; //MTD设备总容量 return 0; } EXPORT_SYMBOL(nand_scan_ident); 1.1nand_set_defaults设置一些默认方法 static void nand_set_defaults(struct nand_chip *chip,int busw) { if (!chip->chip_delay) chip->chip_delay = 20; if (chip->cmdfunc == NULL) chip->cmdfunc = nand_command; //nand命令 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->verify_buf) chip->verify_buf = busw ? nand_verify_buf16 : nand_verify_buf; //校验缓冲区 if (!chip->scan_bbt) chip->scan_bbt = nand_default_bbt; //扫描bbt if (!chip->controller) { chip->controller = &chip->hwcontrol; spin_lock_init(&chip->controller->lock); init_waitqueue_head(&chip->controller->wq); } } 1.2nand_get_flash_type获取nand flash类型 static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,int busw,int *maf_id,int *dev_id,struct nand_flash_dev *type) { int i,maf_idx; u8 id_data[8]; int ret; chip->select_chip(mtd,0); //片选芯片 chip->cmdfunc(mtd,-1); //重启芯片 chip->cmdfunc(mtd,-1); //读芯片ID *maf_id = chip->read_byte(mtd); //读工厂id *dev_id = chip->read_byte(mtd); //读设备id chip->cmdfunc(mtd,-1); //读芯片ID for (i = 0; i < 2; i++) id_data[i] = chip->read_byte(mtd); //读取芯片ID if (id_data[0] != *maf_id || id_data[1] != *dev_id) { //比较ID值 printk(KERN_INFO "%s: second ID read did not match %02x,%02x against %02x,%02xn",__func__,*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 (*dev_id == type->id) break; chip->onfi_version = 0; if (!type->name || !type->pagesize) { //检测是否onfi接口标准 ret = nand_flash_detect_onfi(mtd,busw); if (ret) goto ident_done; } chip->cmdfunc(mtd,-1); //读芯片ID for (i = 0; i < 8; i++) //读取全部id信息 id_data[i] = chip->read_byte(mtd); if (!type->name) return ERR_PTR(-ENODEV); if (!mtd->name) mtd->name = type->name; //设置名字 chip->chipsize = (uint64_t)type->chipsize << 20; //获取芯片容量 if (!type->pagesize && chip->init_size) { busw = chip->init_size(mtd,id_data); //初始化芯片容量 } else if (!type->pagesize) { int extid; chip->cellinfo = id_data[2]; extid = id_data[3]; if (id_data[0] == id_data[6] && id_data[1] == id_data[7] && id_data[0] == NAND_MFR_SAMSUNG && (chip->cellinfo & NAND_CI_CELLTYPE_MSK) && id_data[5] != 0x00) { mtd->writesize = 2048 << (extid & 0x03); extid >>= 2; switch (extid & 0x03) { case 1: mtd->oobsize = 128; break; case 2: mtd->oobsize = 218; break; case 3: mtd->oobsize = 400; break; default: mtd->oobsize = 436; break; } extid >>= 2; mtd->erasesize = (128 * 1024) << (((extid >> 1) & 0x04) | (extid & 0x03)); busw = 0; } else { mtd->writesize = 1024 << (extid & 0x03); extid >>= 2; mtd->oobsize = (8 << (extid & 0x01)) * (mtd->writesize >> 9); extid >>= 2; mtd->erasesize = (64 * 1024) << (extid & 0x03); extid >>= 2; busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0; } } else { mtd->erasesize = type->erasesize; //擦除尺寸 mtd->writesize = type->pagesize; //页尺寸 mtd->oobsize = mtd->writesize / 32; //oob区尺寸 busw = type->options & NAND_BUSWIDTH_16; //数据宽度 if (*maf_id == NAND_MFR_AMD && id_data[4] != 0x00 && id_data[5] == 0x00 && id_data[6] == 0x00 && id_data[7] == 0x00 && mtd->writesize == 512) { mtd->erasesize = 128 * 1024; mtd->erasesize <<= ((id_data[3] & 0x03) << 1); } } chip->options &= ~NAND_CHIPOPTIONS_MSK; chip->options |= type->options & NAND_CHIPOPTIONS_MSK; if (*maf_id != NAND_MFR_SAMSUNG && !type->pagesize) chip->options &= ~NAND_SAMSUNG_LP_OPTIONS; ident_done: chip->options |= NAND_NO_AUTOINCR; for (maf_idx = 0; nand_manuf_ids[maf_idx].id != 0x0; maf_idx++) { if (nand_manuf_ids[maf_idx].id == *maf_id) break; } if (busw != (chip->options & NAND_BUSWIDTH_16)) { printk(KERN_INFO "NAND device: Manufacturer ID: 0x%02x,Chip ID: 0x%02x (%s %s)n",nand_manuf_ids[maf_idx].name,mtd->name); printk(KERN_WARNING "NAND bus width %d instead %d bitn",(chip->options & NAND_BUSWIDTH_16) ? 16 : 8,busw ? 16 : 8); return ERR_PTR(-EINVAL); } chip->page_shift = ffs(mtd->writesize) - 1; chip->pagemask = (chip->chipsize >> chip->page_shift) - 1; chip->bbt_erase_shift = chip->phys_erase_shift = ffs(mtd->erasesize) - 1; if (chip->chipsize & 0xffffffff) chip->chip_shift = ffs((unsigned)chip->chipsize) - 1; else { chip->chip_shift = ffs((unsigned)(chip->chipsize >> 32)); chip->chip_shift += 32 - 1; } chip->badblockbits = 8; if (mtd->writesize > 512 || (busw & NAND_BUSWIDTH_16)) //设置坏块位置 chip->badblockpos = NAND_LARGE_BADBLOCK_POS; else chip->badblockpos = NAND_SMALL_BADBLOCK_POS; if ((chip->cellinfo & NAND_CI_CELLTYPE_MSK) && (*maf_id == NAND_MFR_SAMSUNG || *maf_id == NAND_MFR_HYNIX)) chip->options |= NAND_BBT_SCANLASTPAGE; else if ((!(chip->cellinfo & NAND_CI_CELLTYPE_MSK) && (*maf_id == NAND_MFR_SAMSUNG || *maf_id == NAND_MFR_HYNIX || *maf_id == NAND_MFR_TOSHIBA || *maf_id == NAND_MFR_AMD)) || (mtd->writesize == 2048 && *maf_id == NAND_MFR_MICRON)) chip->options |= NAND_BBT_SCAN2NDPAGE; if (!(busw & NAND_BUSWIDTH_16) && *maf_id == NAND_MFR_STMICRO && mtd->writesize == 2048) { chip->options |= NAND_BBT_SCANBYTE1AND6; chip->badblockpos = 0; } if (chip->options & NAND_4PAGE_ARRAY) chip->erase_cmd = multi_erase_cmd; else chip->erase_cmd = single_erase_cmd; if (mtd->writesize > 512 && chip->cmdfunc == nand_command) chip->cmdfunc = nand_command_lp; printk(KERN_INFO "NAND device: Maf ID: 0x%02x,Chip ID: 0x%02x (%s,%s)n erasesize: 0x%x,writesize: %d,oobsize: %dn",chip->onfi_version ? type->name : chip->onfi_params.model,mtd->erasesize,mtd->writesize,mtd->oobsize); return type; } 2.nand_scan_tail填充未初始化的功能函数默认函数并扫描坏块表 int nand_scan_tail(struct mtd_info *mtd) { int i; struct nand_chip *chip = mtd->priv; if (!(chip->options & NAND_OWN_BUFFERS)) chip->buffers = kmalloc(sizeof(*chip->buffers),GFP_KERNEL); if (!chip->buffers) return -ENOMEM; chip->oob_poi = chip->buffers->databuf + mtd->writesize; //计算oob位置 if (!chip->ecc.layout) { //设置ecc区布局 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: printk(KERN_WARNING "No oob scheme defined for oobsize %dn",mtd->oobsize); BUG(); } } if (!chip->write_page) //没有页写方法 chip->write_page = nand_write_page; //指定默认的页写方法 switch (chip->ecc.mode) { case NAND_ECC_HW_OOB_FIRST: if (!chip->ecc.calculate || !chip->ecc.correct || !chip->ecc.hwctl) { printk(KERN_WARNING "No ECC functions supplied; Hardware ECC not possiblen"); BUG(); } if (!chip->ecc.read_page) chip->ecc.read_page = nand_read_page_hwecc_oob_first; case NAND_ECC_HW: 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)) { printk(KERN_WARNING "No ECC functions supplied; Hardware ECC not possiblen"); BUG(); } 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) break; printk(KERN_WARNING "%d byte HW ECC not possible on %d byte page size,fallback to SW ECCn",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; break; case NAND_ECC_NONE: printk(KERN_WARNING "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; break; default: printk(KERN_WARNING "Invalid NAND_ECC_MODE %dn",chip->ecc.mode); BUG(); } 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; chip->ecc.steps = mtd->writesize / chip->ecc.size; if (chip->ecc.steps * chip->ecc.size != mtd->writesize) { printk(KERN_WARNING "Invalid ecc parametersn"); BUG(); } chip->ecc.total = chip->ecc.steps * chip->ecc.bytes; 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; chip->state = FL_READY; //初始化状态 chip->select_chip(mtd,-1); //选中芯片 chip->pagebuf = -1; mtd->type = MTD_NANDFLASH; //MTD设备类型 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; //读oob区 mtd->write_oob = nand_write_oob; //写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; mtd->ecclayout = chip->ecc.layout; //ecc布局 if (chip->options & NAND_SKIP_BBTSCAN) return 0; return chip->scan_bbt(mtd); //扫描,标记坏块 } EXPORT_SYMBOL(nand_scan_tail); 四.nand_scan完了也就设置好mtd_info master的属性及方法了,那么就可以调用add_mtd_partitions添加mtd的分区了 例如: static struct mtd_partition xxx_nand_partitions_bs128[] = { /* All the partition sizes are listed in terms of NAND block size */ { .name = "U-Boot",.offset = 0,/* Offset = 0x0 */ .size = 18 * SZ_128K,.mask_flags = MTD_WRITEABLE,/* force read-only */ },{ .name = "U-Boot Env",.offset = MTDPART_OFS_APPEND,/* Offset = 0x240000 */ .size = 2 * SZ_128K,},{ .name = "U-Boot Logo",/* Offset = 0x280000 */ .size = 24 * SZ_128K,{ .name = "Kernel",/* Offset = 0x580000 */ .size = 34 * SZ_128K,{ .name = "File System",/* Offset = 0x6C0000 */ .size = 1601 * SZ_128K,{ .name = "Reserved",/* Offset = 0xCEE0000 */ .size = MTDPART_SIZ_FULL,}; (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |