📄 diskonchip.c
字号:
case NAND_ECC_READ: WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf); WriteDOC(DOC_ECC_EN, docptr, Mplus_ECCConf); break; case NAND_ECC_WRITE: WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf); WriteDOC(DOC_ECC_EN | DOC_ECC_RW, docptr, Mplus_ECCConf); break; }}/* This code is only called on write */static int doc200x_calculate_ecc(struct mtd_info *mtd, const u_char *dat, unsigned char *ecc_code){ struct nand_chip *this = mtd->priv; struct doc_priv *doc = this->priv; void __iomem *docptr = doc->virtadr; int i; int emptymatch = 1; /* flush the pipeline */ if (DoC_is_2000(doc)) { WriteDOC(doc->CDSNControl & ~CDSN_CTRL_FLASH_IO, docptr, CDSNControl); WriteDOC(0, docptr, 2k_CDSN_IO); WriteDOC(0, docptr, 2k_CDSN_IO); WriteDOC(0, docptr, 2k_CDSN_IO); WriteDOC(doc->CDSNControl, docptr, CDSNControl); } else if (DoC_is_MillenniumPlus(doc)) { WriteDOC(0, docptr, Mplus_NOP); WriteDOC(0, docptr, Mplus_NOP); WriteDOC(0, docptr, Mplus_NOP); } else { WriteDOC(0, docptr, NOP); WriteDOC(0, docptr, NOP); WriteDOC(0, docptr, NOP); } for (i = 0; i < 6; i++) { if (DoC_is_MillenniumPlus(doc)) ecc_code[i] = ReadDOC_(docptr, DoC_Mplus_ECCSyndrome0 + i); else ecc_code[i] = ReadDOC_(docptr, DoC_ECCSyndrome0 + i); if (ecc_code[i] != empty_write_ecc[i]) emptymatch = 0; } if (DoC_is_MillenniumPlus(doc)) WriteDOC(DOC_ECC_DIS, docptr, Mplus_ECCConf); else WriteDOC(DOC_ECC_DIS, docptr, ECCConf);#if 0 /* If emptymatch=1, we might have an all-0xff data buffer. Check. */ if (emptymatch) { /* Note: this somewhat expensive test should not be triggered often. It could be optimized away by examining the data in the writebuf routine, and remembering the result. */ for (i = 0; i < 512; i++) { if (dat[i] == 0xff) continue; emptymatch = 0; break; } } /* If emptymatch still =1, we do have an all-0xff data buffer. Return all-0xff ecc value instead of the computed one, so it'll look just like a freshly-erased page. */ if (emptymatch) memset(ecc_code, 0xff, 6);#endif return 0;}static int doc200x_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *isnull){ int i, ret = 0; struct nand_chip *this = mtd->priv; struct doc_priv *doc = this->priv; void __iomem *docptr = doc->virtadr; uint8_t calc_ecc[6]; volatile u_char dummy; int emptymatch = 1; /* flush the pipeline */ if (DoC_is_2000(doc)) { dummy = ReadDOC(docptr, 2k_ECCStatus); dummy = ReadDOC(docptr, 2k_ECCStatus); dummy = ReadDOC(docptr, 2k_ECCStatus); } else if (DoC_is_MillenniumPlus(doc)) { dummy = ReadDOC(docptr, Mplus_ECCConf); dummy = ReadDOC(docptr, Mplus_ECCConf); dummy = ReadDOC(docptr, Mplus_ECCConf); } else { dummy = ReadDOC(docptr, ECCConf); dummy = ReadDOC(docptr, ECCConf); dummy = ReadDOC(docptr, ECCConf); } /* Error occured ? */ if (dummy & 0x80) { for (i = 0; i < 6; i++) { if (DoC_is_MillenniumPlus(doc)) calc_ecc[i] = ReadDOC_(docptr, DoC_Mplus_ECCSyndrome0 + i); else calc_ecc[i] = ReadDOC_(docptr, DoC_ECCSyndrome0 + i); if (calc_ecc[i] != empty_read_syndrome[i]) emptymatch = 0; } /* If emptymatch=1, the read syndrome is consistent with an all-0xff data and stored ecc block. Check the stored ecc. */ if (emptymatch) { for (i = 0; i < 6; i++) { if (read_ecc[i] == 0xff) continue; emptymatch = 0; break; } } /* If emptymatch still =1, check the data block. */ if (emptymatch) { /* Note: this somewhat expensive test should not be triggered often. It could be optimized away by examining the data in the readbuf routine, and remembering the result. */ for (i = 0; i < 512; i++) { if (dat[i] == 0xff) continue; emptymatch = 0; break; } } /* If emptymatch still =1, this is almost certainly a freshly- erased block, in which case the ECC will not come out right. We'll suppress the error and tell the caller everything's OK. Because it is. */ if (!emptymatch) ret = doc_ecc_decode(rs_decoder, dat, calc_ecc); if (ret > 0) printk(KERN_ERR "doc200x_correct_data corrected %d errors\n", ret); } if (DoC_is_MillenniumPlus(doc)) WriteDOC(DOC_ECC_DIS, docptr, Mplus_ECCConf); else WriteDOC(DOC_ECC_DIS, docptr, ECCConf); if (no_ecc_failures && (ret == -EBADMSG)) { printk(KERN_ERR "suppressing ECC failure\n"); ret = 0; } return ret;}//u_char mydatabuf[528];/* The strange out-of-order .oobfree list below is a (possibly unneeded) * attempt to retain compatibility. It used to read: * .oobfree = { {8, 8} } * Since that leaves two bytes unusable, it was changed. But the following * scheme might affect existing jffs2 installs by moving the cleanmarker: * .oobfree = { {6, 10} } * jffs2 seems to handle the above gracefully, but the current scheme seems * safer. The only problem with it is that any code that parses oobfree must * be able to handle out-of-order segments. */static struct nand_ecclayout doc200x_oobinfo = { .eccbytes = 6, .eccpos = {0, 1, 2, 3, 4, 5}, .oobfree = {{8, 8}, {6, 2}}};/* Find the (I)NFTL Media Header, and optionally also the mirror media header. On sucessful return, buf will contain a copy of the media header for further processing. id is the string to scan for, and will presumably be either "ANAND" or "BNAND". If findmirror=1, also look for the mirror media header. The page #s of the found media headers are placed in mh0_page and mh1_page in the DOC private structure. */static int __init find_media_headers(struct mtd_info *mtd, u_char *buf, const char *id, int findmirror){ struct nand_chip *this = mtd->priv; struct doc_priv *doc = this->priv; unsigned offs; int ret; size_t retlen; for (offs = 0; offs < mtd->size; offs += mtd->erasesize) { ret = mtd->read(mtd, offs, mtd->writesize, &retlen, buf); if (retlen != mtd->writesize) continue; if (ret) { printk(KERN_WARNING "ECC error scanning DOC at 0x%x\n", offs); } if (memcmp(buf, id, 6)) continue; printk(KERN_INFO "Found DiskOnChip %s Media Header at 0x%x\n", id, offs); if (doc->mh0_page == -1) { doc->mh0_page = offs >> this->page_shift; if (!findmirror) return 1; continue; } doc->mh1_page = offs >> this->page_shift; return 2; } if (doc->mh0_page == -1) { printk(KERN_WARNING "DiskOnChip %s Media Header not found.\n", id); return 0; } /* Only one mediaheader was found. We want buf to contain a mediaheader on return, so we'll have to re-read the one we found. */ offs = doc->mh0_page << this->page_shift; ret = mtd->read(mtd, offs, mtd->writesize, &retlen, buf); if (retlen != mtd->writesize) { /* Insanity. Give up. */ printk(KERN_ERR "Read DiskOnChip Media Header once, but can't reread it???\n"); return 0; } return 1;}static inline int __init nftl_partscan(struct mtd_info *mtd, struct mtd_partition *parts){ struct nand_chip *this = mtd->priv; struct doc_priv *doc = this->priv; int ret = 0; u_char *buf; struct NFTLMediaHeader *mh; const unsigned psize = 1 << this->page_shift; int numparts = 0; unsigned blocks, maxblocks; int offs, numheaders; buf = kmalloc(mtd->writesize, GFP_KERNEL); if (!buf) { printk(KERN_ERR "DiskOnChip mediaheader kmalloc failed!\n"); return 0; } if (!(numheaders = find_media_headers(mtd, buf, "ANAND", 1))) goto out; mh = (struct NFTLMediaHeader *)buf; le16_to_cpus(&mh->NumEraseUnits); le16_to_cpus(&mh->FirstPhysicalEUN); le32_to_cpus(&mh->FormattedSize); printk(KERN_INFO " DataOrgID = %s\n" " NumEraseUnits = %d\n" " FirstPhysicalEUN = %d\n" " FormattedSize = %d\n" " UnitSizeFactor = %d\n", mh->DataOrgID, mh->NumEraseUnits, mh->FirstPhysicalEUN, mh->FormattedSize, mh->UnitSizeFactor); blocks = mtd->size >> this->phys_erase_shift; maxblocks = min(32768U, mtd->erasesize - psize); if (mh->UnitSizeFactor == 0x00) { /* Auto-determine UnitSizeFactor. The constraints are: - There can be at most 32768 virtual blocks. - There can be at most (virtual block size - page size) virtual blocks (because MediaHeader+BBT must fit in 1). */ mh->UnitSizeFactor = 0xff; while (blocks > maxblocks) { blocks >>= 1; maxblocks = min(32768U, (maxblocks << 1) + psize); mh->UnitSizeFactor--; } printk(KERN_WARNING "UnitSizeFactor=0x00 detected. Correct value is assumed to be 0x%02x.\n", mh->UnitSizeFactor); } /* NOTE: The lines below modify internal variables of the NAND and MTD layers; variables with have already been configured by nand_scan. Unfortunately, we didn't know before this point what these values should be. Thus, this code is somewhat dependant on the exact implementation of the NAND layer. */ if (mh->UnitSizeFactor != 0xff) { this->bbt_erase_shift += (0xff - mh->UnitSizeFactor); mtd->erasesize <<= (0xff - mh->UnitSizeFactor); printk(KERN_INFO "Setting virtual erase size to %d\n", mtd->erasesize); blocks = mtd->size >> this->bbt_erase_shift; maxblocks = min(32768U, mtd->erasesize - psize); } if (blocks > maxblocks) { printk(KERN_ERR "UnitSizeFactor of 0x%02x is inconsistent with device size. Aborting.\n", mh->UnitSizeFactor); goto out; } /* Skip past the media headers. */ offs = max(doc->mh0_page, doc->mh1_page); offs <<= this->page_shift; offs += mtd->erasesize; if (show_firmware_partition == 1) { parts[0].name = " DiskOnChip Firmware / Media Header partition"; parts[0].offset = 0; parts[0].size = offs; numparts = 1; } parts[numparts].name = " DiskOnChip BDTL partition"; parts[numparts].offset = offs; parts[numparts].size = (mh->NumEraseUnits - numheaders) << this->bbt_erase_shift; offs += parts[numparts].size; numparts++; if (offs < mtd->size) { parts[numparts].name = " DiskOnChip Remainder partition"; parts[numparts].offset = offs; parts[numparts].size = mtd->size - offs; numparts++; } ret = numparts; out: kfree(buf); return ret;}/* This is a stripped-down copy of the code in inftlmount.c */static inline int __init inftl_partscan(struct mtd_info *mtd, struct mtd_partition *parts){ struct nand_chip *this = mtd->priv; struct doc_priv *doc = this->priv; int ret = 0; u_char *buf; struct INFTLMediaHeader *mh; struct INFTLPartition *ip; int numparts = 0; int blocks; int vshift, lastvunit = 0; int i; int end = mtd->size; if (inftl_bbt_write) end -= (INFTL_BBT_RESERVED_BLOCKS << this->phys_erase_shift); buf = kmalloc(mtd->writesize, GFP_KERNEL); if (!buf) { printk(KERN_ERR "DiskOnChip mediaheader kmalloc failed!\n"); return 0; } if (!find_media_headers(mtd, buf, "BNAND", 0)) goto out; doc->mh1_page = doc->mh0_page + (4096 >> this->page_shift); mh = (struct INFTLMediaHeader *)buf; le32_to_cpus(&mh->NoOfBootImageBlocks); le32_to_cpus(&mh->NoOfBinaryPartitions); le32_to_cpus(&mh->NoOfBDTLPartitions); le32_to_cpus(&mh->BlockMultiplierBits); le32_to_cpus(&mh->FormatFlags); le32_to_cpus(&mh->PercentUsed); printk(KERN_INFO " bootRecordID = %s\n" " NoOfBootImageBlocks = %d\n" " NoOfBinaryPartitions = %d\n" " NoOfBDTLPartitions = %d\n" " BlockMultiplerBits = %d\n" " FormatFlgs = %d\n" " OsakVersion = %d.%d.%d.%d\n" " PercentUsed = %d\n", mh->bootRecordID, mh->NoOfBootImageBlocks, mh->NoOfBinaryPartitions, mh->NoOfBDTLPartitions, mh->BlockMultiplierBits, mh->FormatFlags, ((unsigned char *) &mh->OsakVersion)[0] & 0xf, ((unsigned char *) &mh->OsakVersion)[1] & 0xf, ((unsigned char *) &mh->OsakVersion)[2] & 0xf, ((unsigned char *) &mh->OsakVersion)[3] & 0xf, mh->PercentUsed); vshift = this->phys_erase_shift + mh->BlockMultiplierBits; blocks = mtd->size >> vshift; if (blocks > 32768) { printk(KERN_ERR "BlockMultiplierBits=%d is inconsistent with device size. Aborting.\n", mh->BlockMultiplierBits); goto out; } blocks = doc->chips_per_floor << (this->chip_shift - this->phys_erase_shift); if (inftl_bbt_write && (blocks > mtd->erasesize)) { printk(KERN_ERR "Writeable BBTs spanning more than one erase block are not yet supported. FIX ME!\n"); goto out; } /* Scan the partitions */ for (i = 0; (i < 4); i++) { ip = &(mh->Partitions[i]); le32_to_cpus(&ip->virtualUnits); le32_to_cpus(&ip->firstUnit); le32_to_cpus(&ip->lastUnit); le32_to_cpus(&ip->flags); le32_to_cpus(&ip->spareUnits); le32_to_cpus(&ip->Reserved0); printk(KERN_INFO " PARTITION[%d] ->\n" " virtualUnits = %d\n" " firstUnit = %d\n" " lastUnit = %d\n" " flags = 0x%x\n" " spareUnits = %d\n", i, ip->virtualUnits, ip->firstUnit, ip->lastUnit, ip->flags, ip->spareUnits); if ((show_firmware_partition == 1) && (i == 0) && (ip->firstUnit > 0)) { parts[0].name = " DiskOnChip IPL / Media Header partition"; parts[0].offset = 0; parts[0].size = mtd->erasesize * ip->firstUnit; numparts = 1; } if (ip->flags & INFTL_BINARY) parts[numparts].name = " DiskOnChip BDK partition"; else parts[numparts].name = " DiskOnChip BDTL partition"; parts[numparts].offset = ip->firstUnit << vshift; parts[numparts].size = (1 + ip->lastUnit - ip->firstUnit) << vshift; numparts++; if (ip->lastUnit > lastvunit) lastvunit = ip->lastUnit; if (ip->flags & INFTL_LAST) break; } lastvunit++; if ((lastvunit << vshift) < end) { parts[numparts].name = " DiskOnChip Remainder partition"; parts[numparts].offset = lastvunit << vshift; parts[numparts].size = end - parts[numparts].offset; numparts++; } ret = numparts; out: kfree(buf); return ret;}static int __init nftl_scan_bbt(struct mtd_info *mtd){ int ret, numparts; struct nand_chip *this = mtd->priv; struct doc_priv *doc = this->priv; struct mtd_partition parts[2];
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -