X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fmtd%2Finftlmount.c;h=64cc97ca7bd310ce0ea2690006dd2bdcd4332298;hb=9bf4aaab3e101692164d49b7ca357651eb691cb6;hp=042c4eeadf8ec6100b978a9e1b310fc25702306e;hpb=db216c3d5e4c040e557a50f8f5d35d5c415e8c1c;p=linux-2.6.git diff --git a/drivers/mtd/inftlmount.c b/drivers/mtd/inftlmount.c index 042c4eead..64cc97ca7 100644 --- a/drivers/mtd/inftlmount.c +++ b/drivers/mtd/inftlmount.c @@ -8,7 +8,7 @@ * Author: Fabrice Bellard (fabrice.bellard@netgem.com) * Copyright (C) 2000 Netgem S.A. * - * $Id: inftlmount.c,v 1.11 2003/06/23 07:39:21 dwmw2 Exp $ + * $Id: inftlmount.c,v 1.14 2004/08/09 13:57:42 dwmw2 Exp $ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -41,7 +41,7 @@ #include #include -char inftlmountrev[]="$Revision: 1.11 $"; +char inftlmountrev[]="$Revision: 1.14 $"; /* * find_boot_record: Find the INFTL Media Header and its Spare copy which @@ -54,14 +54,13 @@ static int find_boot_record(struct INFTLrecord *inftl) { struct inftl_unittail h1; //struct inftl_oob oob; - unsigned int i, block, boot_record_count = 0; + unsigned int i, block; u8 buf[SECTORSIZE]; struct INFTLMediaHeader *mh = &inftl->MediaHdr; struct INFTLPartition *ip; - int retlen; + size_t retlen; - DEBUG(MTD_DEBUG_LEVEL3, "INFTL: find_boot_record(inftl=0x%x)\n", - (int)inftl); + DEBUG(MTD_DEBUG_LEVEL3, "INFTL: find_boot_record(inftl=%p)\n", inftl); /* * Assume logical EraseSize == physical erasesize for starting the @@ -72,7 +71,6 @@ static int find_boot_record(struct INFTLrecord *inftl) inftl->nb_blocks = inftl->mbd.mtd->size / inftl->EraseSize; inftl->MediaUnit = BLOCK_NIL; - inftl->SpareMediaUnit = BLOCK_NIL; /* Search for a valid boot record */ for (block = 0; block < inftl->nb_blocks; block++) { @@ -82,8 +80,11 @@ static int find_boot_record(struct INFTLrecord *inftl) * Check for BNAND header first. Then whinge if it's found * but later checks fail. */ - if ((ret = MTD_READ(inftl->mbd.mtd, block * inftl->EraseSize, - SECTORSIZE, &retlen, buf))) { + ret = MTD_READ(inftl->mbd.mtd, block * inftl->EraseSize, + SECTORSIZE, &retlen, buf); + /* We ignore ret in case the ECC of the MediaHeader is invalid + (which is apparently acceptable) */ + if (retlen != SECTORSIZE) { static int warncount = 5; if (warncount) { @@ -114,36 +115,28 @@ static int find_boot_record(struct INFTLrecord *inftl) continue; } - if (boot_record_count) { - /* - * We've already processed one. So we just check if - * this one is the same as the first one we found. - */ - if (memcmp(mh, buf, sizeof(struct INFTLMediaHeader))) { - printk(KERN_WARNING "INFTL: Media Headers at " - "0x%x and 0x%x disagree.\n", - inftl->MediaUnit * inftl->EraseSize, - block * inftl->EraseSize); - return -1; - } - if (boot_record_count == 1) - inftl->SpareMediaUnit = block; - - /* - * Mark this boot record (INFTL MediaHeader) block as - * reserved. - */ - inftl->PUtable[block] = BLOCK_RESERVED; - - boot_record_count++; - continue; - } /* * This is the first we've seen. * Copy the media header structure into place. */ memcpy(mh, buf, sizeof(struct INFTLMediaHeader)); + + /* Read the spare media header at offset 4096 */ + MTD_READ(inftl->mbd.mtd, block * inftl->EraseSize + 4096, + SECTORSIZE, &retlen, buf); + if (retlen != SECTORSIZE) { + printk(KERN_WARNING "INFTL: Unable to read spare " + "Media Header\n"); + return -1; + } + /* Check if this one is the same as the first one we found. */ + if (memcmp(mh, buf, sizeof(struct INFTLMediaHeader))) { + printk(KERN_WARNING "INFTL: Primary and spare Media " + "Headers disagree.\n"); + return -1; + } + mh->NoOfBootImageBlocks = le32_to_cpu(mh->NoOfBootImageBlocks); mh->NoOfBinaryPartitions = le32_to_cpu(mh->NoOfBinaryPartitions); mh->NoOfBDTLPartitions = le32_to_cpu(mh->NoOfBDTLPartitions); @@ -197,8 +190,9 @@ static int find_boot_record(struct INFTLrecord *inftl) "UnitSizeFactor 0x%02x is experimental\n", mh->BlockMultiplierBits); inftl->EraseSize = inftl->mbd.mtd->erasesize << - (0xff - mh->BlockMultiplierBits); + mh->BlockMultiplierBits; inftl->nb_blocks = inftl->mbd.mtd->size / inftl->EraseSize; + block >>= mh->BlockMultiplierBits; } /* Scan the partitions */ @@ -293,7 +287,7 @@ static int find_boot_record(struct INFTLrecord *inftl) inftl->PUtable = kmalloc(inftl->nb_blocks * sizeof(u16), GFP_KERNEL); if (!inftl->PUtable) { printk(KERN_WARNING "INFTL: allocation of PUtable " - "failed (%d bytes)\n", + "failed (%zd bytes)\n", inftl->nb_blocks * sizeof(u16)); return -ENOMEM; } @@ -302,7 +296,7 @@ static int find_boot_record(struct INFTLrecord *inftl) if (!inftl->VUtable) { kfree(inftl->PUtable); printk(KERN_WARNING "INFTL: allocation of VUtable " - "failed (%d bytes)\n", + "failed (%zd bytes)\n", inftl->nb_blocks * sizeof(u16)); return -ENOMEM; } @@ -317,34 +311,23 @@ static int find_boot_record(struct INFTLrecord *inftl) /* Mark this boot record (NFTL MediaHeader) block as reserved */ inftl->PUtable[block] = BLOCK_RESERVED; -#if 0 /* Read Bad Erase Unit Table and modify PUtable[] accordingly */ for (i = 0; i < inftl->nb_blocks; i++) { - if ((i & (SECTORSIZE - 1)) == 0) { - /* read one sector for every SECTORSIZE of blocks */ - if ((ret = MTD_READECC(inftl->mbd.mtd, - block * inftl->EraseSize + i + SECTORSIZE, - SECTORSIZE, &retlen, buf, - (char *)&oob, NULL)) < 0) { - printk(KERN_WARNING "INFTL: read of " - "bad sector table failed " - "(err %d)\n", ret); - kfree(inftl->VUtable); - kfree(inftl->PUtable); - return -1; - } + int physblock; + /* If any of the physical eraseblocks are bad, don't + use the unit. */ + for (physblock = 0; physblock < inftl->EraseSize; physblock += inftl->mbd.mtd->erasesize) { + if (inftl->mbd.mtd->block_isbad(inftl->mbd.mtd, i * inftl->EraseSize + physblock)) + inftl->PUtable[i] = BLOCK_RESERVED; } - /* Mark the Bad Erase Unit as RESERVED in PUtable */ - if (buf[i & (SECTORSIZE - 1)] != 0xff) - inftl->PUtable[i] = BLOCK_RESERVED; } -#endif inftl->MediaUnit = block; - boot_record_count++; + return 0; } - - return boot_record_count ? 0 : -1; + + /* Not found. */ + return -1; } static int memcmpb(void *a, int c, int n) @@ -364,28 +347,22 @@ static int memcmpb(void *a, int c, int n) static int check_free_sectors(struct INFTLrecord *inftl, unsigned int address, int len, int check_oob) { - int i, retlen; - u8 buf[SECTORSIZE]; + u8 buf[SECTORSIZE + inftl->mbd.mtd->oobsize]; + size_t retlen; + int i; - DEBUG(MTD_DEBUG_LEVEL3, "INFTL: check_free_sectors(inftl=0x%x," - "address=0x%x,len=%d,check_oob=%d)\n", (int)inftl, + DEBUG(MTD_DEBUG_LEVEL3, "INFTL: check_free_sectors(inftl=%p," + "address=0x%x,len=%d,check_oob=%d)\n", inftl, address, len, check_oob); for (i = 0; i < len; i += SECTORSIZE) { - /* - * We want to read the sector without ECC check here since a - * free sector does not have ECC syndrome on it yet. - */ - if (MTD_READ(inftl->mbd.mtd, address, SECTORSIZE, &retlen, buf) < 0) + if (MTD_READECC(inftl->mbd.mtd, address, SECTORSIZE, &retlen, buf, &buf[SECTORSIZE], &inftl->oobinfo) < 0) return -1; if (memcmpb(buf, 0xff, SECTORSIZE) != 0) return -1; if (check_oob) { - if (MTD_READOOB(inftl->mbd.mtd, address, - inftl->mbd.mtd->oobsize, &retlen, buf) < 0) - return -1; - if (memcmpb(buf, 0xff, inftl->mbd.mtd->oobsize) != 0) + if (memcmpb(buf + SECTORSIZE, 0xff, inftl->mbd.mtd->oobsize) != 0) return -1; } address += SECTORSIZE; @@ -402,52 +379,62 @@ static int check_free_sectors(struct INFTLrecord *inftl, unsigned int address, * Return: 0 when succeed, -1 on error. * * ToDo: 1. Is it neceressary to check_free_sector after erasing ?? - * 2. UnitSizeFactor != 0xFF */ int INFTL_formatblock(struct INFTLrecord *inftl, int block) { - int retlen; + size_t retlen; struct inftl_unittail uci; struct erase_info *instr = &inftl->instr; + int physblock; - DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_formatblock(inftl=0x%x," - "block=%d)\n", (int)inftl, block); + DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_formatblock(inftl=%p," + "block=%d)\n", inftl, block); memset(instr, 0, sizeof(struct erase_info)); + /* FIXME: Shouldn't we be setting the 'discarded' flag to zero + _first_? */ + /* Use async erase interface, test return code */ instr->addr = block * inftl->EraseSize; - instr->len = inftl->EraseSize; - MTD_ERASE(inftl->mbd.mtd, instr); + instr->len = inftl->mbd.mtd->erasesize; + /* Erase one physical eraseblock at a time, even though the NAND api + allows us to group them. This way we if we have a failure, we can + mark only the failed block in the bbt. */ + for (physblock = 0; physblock < inftl->EraseSize; physblock += instr->len, instr->addr += instr->len) { + MTD_ERASE(inftl->mbd.mtd, instr); + + if (instr->state == MTD_ERASE_FAILED) { + printk(KERN_WARNING "INFTL: error while formatting block %d\n", + block); + goto fail; + } - if (instr->state == MTD_ERASE_FAILED) { /* - * Could not format, FixMe: We should update the BadUnitTable - * both in memory and on disk. - */ - printk(KERN_WARNING "INFTL: error while formatting block %d\n", - block); - return -1; + * Check the "freeness" of Erase Unit before updating metadata. + * FixMe: is this check really necessary? Since we have check the + * return code after the erase operation. + */ + if (check_free_sectors(inftl, instr->addr, instr->len, 1) != 0) + goto fail; } - /* - * Check the "freeness" of Erase Unit before updating metadata. - * FixMe: is this check really necessary? Since we have check the - * return code after the erase operation. - */ - if (check_free_sectors(inftl, instr->addr, inftl->EraseSize, 1) != 0) - return -1; - uci.EraseMark = cpu_to_le16(ERASE_MARK); uci.EraseMark1 = cpu_to_le16(ERASE_MARK); uci.Reserved[0] = 0; uci.Reserved[1] = 0; uci.Reserved[2] = 0; uci.Reserved[3] = 0; - if (MTD_WRITEOOB(inftl->mbd.mtd, block * inftl->EraseSize + SECTORSIZE * 2 + + instr->addr = block * inftl->EraseSize + SECTORSIZE * 2; + if (MTD_WRITEOOB(inftl->mbd.mtd, instr->addr + 8, 8, &retlen, (char *)&uci) < 0) - return -1; + goto fail; return 0; +fail: + /* could not format, update the bad block table (caller is responsible + for setting the PUtable to BLOCK_RESERVED on failure) */ + inftl->mbd.mtd->block_markbad(inftl->mbd.mtd, instr->addr); + return -1; } /* @@ -472,7 +459,6 @@ static void format_chain(struct INFTLrecord *inftl, unsigned int first_block) if (INFTL_formatblock(inftl, block) < 0) { /* * Cannot format !!!! Mark it as Bad Unit, - * FixMe: update the BadUnitTable on disk. */ inftl->PUtable[block] = BLOCK_RESERVED; } else { @@ -565,10 +551,11 @@ int INFTL_mount(struct INFTLrecord *s) int chain_length, do_format_chain; struct inftl_unithead1 h0; struct inftl_unittail h1; - int i, retlen; + size_t retlen; + int i; u8 *ANACtable, ANAC; - DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_mount(inftl=0x%x)\n", (int)s); + DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_mount(inftl=%p)\n", s); /* Search for INFTL MediaHeader and Spare INFTL Media Header */ if (find_boot_record(s) < 0) {