From patchwork Thu Aug 12 10:51:17 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Dong, Chuanxiao" X-Patchwork-Id: 61557 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from bombadil.infradead.org (bombadil.infradead.org [18.85.46.34]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 372FEB70DE for ; Thu, 12 Aug 2010 20:57:22 +1000 (EST) Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.72 #1 (Red Hat Linux)) id 1OjVPy-0004el-HN; Thu, 12 Aug 2010 10:53:50 +0000 Received: from mga02.intel.com ([134.134.136.20]) by bombadil.infradead.org with esmtp (Exim 4.72 #1 (Red Hat Linux)) id 1OjVPu-0004bI-Nu for linux-mtd@lists.infradead.org; Thu, 12 Aug 2010 10:53:48 +0000 Received: from orsmga001.jf.intel.com ([10.7.209.18]) by orsmga101.jf.intel.com with ESMTP; 12 Aug 2010 03:53:46 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="4.55,357,1278313200"; d="scan'208,223";a="646778487" Received: from unknown (HELO intel.com) ([172.16.120.184]) by orsmga001.jf.intel.com with ESMTP; 12 Aug 2010 03:53:45 -0700 Date: Thu, 12 Aug 2010 18:51:17 +0800 From: "Chuanxiao.Dong" To: linux-mtd@lists.infradead.org, dwmw2@infradead.org Subject: [PATCH v2 2/6]nand/denali: Add bad block management for MRST Message-ID: <20100812105117.GB11634@intel.com> MIME-Version: 1.0 Content-Disposition: inline User-Agent: Mutt/1.5.19 (2009-01-05) X-CRM114-Version: 20090807-BlameThorstenAndJenny ( TRE 0.7.6 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20100812_065347_288092_160CE386 X-CRM114-Status: GOOD ( 37.27 ) X-Spam-Score: -2.3 (--) X-Spam-Report: SpamAssassin version 3.3.1 on bombadil.infradead.org summary: Content analysis details: (-2.3 points) pts rule name description ---- ---------------------- -------------------------------------------------- -2.3 RCVD_IN_DNSWL_MED RBL: Sender listed at http://www.dnswl.org/, medium trust [134.134.136.20 listed in list.dnswl.org] -0.0 T_RP_MATCHES_RCVD Envelope sender domain matches handover relay domain X-BeenThere: linux-mtd@lists.infradead.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: Linux MTD discussion mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: linux-mtd-bounces@lists.infradead.org Errors-To: linux-mtd-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org From 675731420dabad00dc4337c3e89613c606f52906 Mon Sep 17 00:00:00 2001 From: Chuanxiao Dong Date: Thu, 12 Aug 2010 17:56:30 +0800 Subject: [PATCH 2/6] nand/denali: Add bad block management for MRST Denali controller in MRST uses 15bits or 8bits ECC correction according to the size of NAND chip OOB area. Both of these two corrections will consume most of the OOB bytes. There are no enough bytes for MTD bad block management to use for some kind of NAND chip. On the other side, when use MLC NAND, OOB area is not a trust area to store BBT. So here design a new bad block mangement for denali controller. Driver will scan available NAND blocks to create a memery BBT for the first time and store it in the last 8(default) blocks. The next time driver only need to read BBT from these blocks out and create a memery BBT. Also add a kernel config to let user choose how many blocks to store BBT for MRST. Signed-off-by: Chuanxiao Dong --- drivers/mtd/nand/Kconfig | 26 +++- drivers/mtd/nand/denali.c | 484 ++++++++++++++++++++++++++++++++++++++++++++- drivers/mtd/nand/denali.h | 16 ++ 3 files changed, 523 insertions(+), 3 deletions(-) diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 8b4b67c..6aece56 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -56,7 +56,7 @@ config MTD_NAND_DENALI help Enable the driver for NAND flash on Intel Moorestown, using the Denali NAND controller core. - + config MTD_NAND_DENALI_SCRATCH_REG_ADDR hex "Denali NAND size scratch register address" default "0xFF108018" @@ -68,6 +68,30 @@ config MTD_NAND_DENALI_SCRATCH_REG_ADDR scratch register here to enable this feature. On Intel Moorestown boards, the scratch register is at 0xFF108018. +choice + prompt "Compile for" + depends on MTD_NAND_DENALI + default MRST_NAND_CONTROLLER + +config MRST_NAND_CONTROLLER + bool "MRST NAND controller" + help + +config CE4100_NAND_CONTROLLER + bool "CE4100 NAND controller" + help + +endchoice + +config MTD_NAND_DENALI_BBT_BLKNUM + int "blocknums to store BBT -- MRST platform will use this" + default 8 + range 2 10 + depends on MTD_NAND_DENALI && MRST_NAND_CONTROLLER + help + This parameter defines the blocknums which is used to store Bad + Block Table. + config MTD_NAND_EDB7312 tristate "Support for Cirrus Logic EBD7312 evaluation board" depends on ARCH_EDB7312 diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c index dd32d04..a7d71e8 100644 --- a/drivers/mtd/nand/denali.c +++ b/drivers/mtd/nand/denali.c @@ -473,6 +473,9 @@ static void find_valid_banks(struct denali_nand_info *denali) static void detect_partition_feature(struct denali_nand_info *denali) { + void __iomem *scratch_reg; + int8_t shift; + /* For MRST platform, denali->fwblks represent the * number of blocks firmware is taken, * FW is in protect partition and MTD driver has no @@ -493,6 +496,22 @@ static void detect_partition_feature(struct denali_nand_info *denali) denali->fwblks = SPECTRA_START_BLOCK; } else denali->fwblks = SPECTRA_START_BLOCK; + + /* For MRST platform, denali->manageblks represent the + * number of blocks firmware can manage. + * */ + scratch_reg = ioremap_nocache(SCRATCH_REG_ADDR, SCRATCH_REG_SIZE); + if (!scratch_reg) + dev_err(&denali->dev->dev, + "denali: scrach reg ioremap failed\n"); + else { + shift = (int8_t)ioread8(scratch_reg); + if (shift > 0) + denali->manageblks = 1 << shift; + else + denali->manageblks = 0; + iounmap(scratch_reg); + } } static uint16_t denali_nand_timing_set(struct denali_nand_info *denali) @@ -546,8 +565,6 @@ static uint16_t denali_nand_timing_set(struct denali_nand_info *denali) find_valid_banks(denali); - detect_partition_feature(denali); - /* If the user specified to override the default timings * with a specific ONFI mode, we apply those changes here. */ @@ -1324,6 +1341,438 @@ static void denali_cmdfunc(struct mtd_info *mtd, unsigned int cmd, int col, } } +/** + * check_bbt_block: when reading the block which is contained + * BBT infromation, we do a check here. If reading failed, suppose + * device is power off during drive writing this block. So just + * erase this block and reture with special value. + * + * return value: + * 0: no error happend, read success; + * 1: read failed, erase success; + * -EIO: read failed, erase failed; + * other: param wrong + */ +static int check_bbt_block(struct mtd_info *mtd, loff_t from, + uint32_t len, uint8_t *buf) +{ + uint32_t ret, retlen = 0; + memset(buf, 0, len); + ret = mtd->read(mtd, from, len, &retlen, buf); + /* If read failed, driver should erase this block + * If erase failed, mark this block bad + * */ + if (ret == -EBADMSG) { + /* this Block was bad for some reason + * erase it right now*/ + struct erase_info ei; + memset(&ei, 0, sizeof(struct erase_info)); + ei.mtd = mtd; + ei.addr = from; + ei.len = mtd->erasesize; + ret = mtd->erase(mtd, &ei); + if (ret != -EIO && ret != -EINVAL) + ret = 1; + } else if (ret == -EUCLEAN) + ret = 0; + + return ret; +} + +/* + * write_bbt_to_blk: write BBT information to the specified block + * + * return value: + * 0: no error happend, write success; + * 1: write failed; + * other: system error; + * */ +static int write_bbt_to_blk(struct mtd_info *mtd) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); + struct denali_bbt_info *ptr = &denali->bbtinfo; + struct nand_chip *chip = &denali->nand; + struct erase_info ei; + struct device *dev = &denali->dev->dev; + uint32_t *bbtblk, len, blk; + uint8_t *buf, *bbt = NULL; + uint32_t ret, i, retlen; + int write_done; + loff_t to; +retry: + if (denali->availbbtblk <= 0) { + dev_err(dev, "No available block reserved for storing BBT\n"); + ret = 1; + return ret; + } + ptr->bbtlen = 4 * mtd->ecc_stats.badblocks; + ptr->serial++; + len = HEAD_LEN + ptr->bbtlen; + if (len % mtd->writesize) + len = (len / mtd->writesize + 1) * mtd->writesize; + + buf = kzalloc(len, GFP_KERNEL); + if (buf == NULL) { + ret = -ENOMEM; + return ret; + } + + /* Get bad block number form memery bbt */ + bbt = chip->bbt; + bbtblk = (uint32_t *)&buf[HEAD_LEN]; + for (i = 0; i < denali->totalblks / 4; i++, bbt++) { + blk = *bbt; + if (!blk) + continue; + if (blk & 0x03) + *bbtblk++ = i * 4; + if ((blk >> 2) & 0x03) + *bbtblk++ = 1 + i * 4; + if ((blk >> 4) & 0x03) + *bbtblk++ = 2 + i * 4; + if ((blk >> 6) & 0x03) + *bbtblk++ = 3 + i * 4; + } + + /* Add BBT basic information as BBT format said + * and write BBT information to specified blocks. + * Each block contains the same BBT + * */ + write_done = 0; + buf[0] = 'B'; + buf[1] = 'B'; + buf[2] = 'T'; + buf[3] = '0'; + *(uint32_t *)(buf + 4) = ptr->bbtlen; + *(uint32_t *)(buf + 8) = ptr->serial; + for (i = denali->bbtstartblk; i < denali->totalblks; i++) { + /* skip Bad Block */ + if (chip->bbt[i>>2] & (0x3<<((i&3)<<1))) + continue; + to = (loff_t)i * mtd->erasesize; + memset(&ei, 0, sizeof(struct erase_info)); + ei.mtd = mtd; + ei.addr = to; + ei.len = mtd->erasesize; + ret = mtd->erase(mtd, &ei); + if (ret) { + /* If erase failed, mark this block bad + * and go to top to try to write again */ + chip->bbt[i>>2] |= 0x1 << ((i&3)<<1); + mtd->ecc_stats.badblocks++; + denali->availbbtblk--; + kfree(buf); + dev_info(&denali->dev->dev, "erase bbtblk failed!!" + "found new bad block %d", i); + goto retry; + } + ret = mtd->write(mtd, to, len, &retlen, buf); + if (ret || retlen != len) { + /* If write failed, mark this block bad + * and go to top to try to write again */ + chip->bbt[i>>2] |= 0x1 << ((i&3)<<1); + mtd->ecc_stats.badblocks++; + denali->availbbtblk--; + kfree(buf); + dev_info(&denali->dev->dev, "write bbtblk failed!!" + "found new bad block %d", i); + goto retry; + } + write_done++; + } + + kfree(buf); + + /* If at lease one block is written successfully, + * return with success*/ + if (write_done) + ret = 0; + else { + if (denali->availbbtblk > 0) + BUG(); + ret = 1; + } + + return ret; +} + +/* + * create_bbt: scan NAND and create memery bbt + * + * Driver will scan the first 3 pages in each block. + * The first 2 bytes in OOB area in the 3 pages will + * identify whether the current block is bad. + * */ +static int create_bbt(struct mtd_info *mtd) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); + struct nand_chip *chip = &denali->nand; + struct device *dev = &denali->dev->dev; + struct mtd_oob_ops ops; + uint32_t startblk, endblk, i, j, pagecnt = 3, + skip = denali->bbtskipbytes; + int checkbyte, ret = 0; + uint8_t *buf = NULL; + loff_t from = 0; + + if (denali->manageblks == 0) { + dev_err(dev, "Never be here. The whole nand" + " should be managed by FTL\n"); + BUG(); + } + + /* we skip protected partition because + * there is no permission for MTD driver + * to access, also skip kboot part, since + * this part bad block is managed by FTL + * */ + startblk = denali->manageblks; + endblk = denali->totalblks; + + buf = kzalloc(mtd->oobsize, GFP_KERNEL); + if (buf == NULL) { + ret = -ENOMEM; + return ret; + } + + memset(&ops, 0, sizeof(struct mtd_oob_ops)); + ops.mode = MTD_OOB_PLACE; + ops.ooblen = mtd->oobsize; + ops.oobbuf = buf; + ops.ooboffs = 0; + ops.oobretlen = 0; + ops.datbuf = NULL; + for (i = startblk; i < endblk; i++) { + from = (loff_t)i * mtd->erasesize; + for (j = 0; j < pagecnt; j++) { + ret = mtd->read_oob(mtd, from, &ops); + if (ret) + goto read_oob_failed; + for (checkbyte = 0; + checkbyte < skip; + checkbyte++) + if (ops.oobbuf[checkbyte] != 0xff) { + chip->bbt[i>>2] |= 0x3 << ((i&3)<<1); + mtd->ecc_stats.badblocks++; + goto next_scan; + } + from += mtd->writesize; + } +next_scan: + continue; + } +read_oob_failed: + kfree(buf); + return ret; +} + +/* + * denali_scan_bbt - To creat a memery BBT if the BBT information + * is already stored in the blocks driver reserved. + * + * If NAND contains BBT information, denali_scan_bbt will create + * chip->bbt by reading BBT out. + * If NAND doesn't contain, suppose this NAND to be a new NAND. + * + * BBT is stored in the end 8(default) blocks of NAND chip. Each + * block contains the same BBT information. That means NAND chip + * contain 8 copys of a BBT information. If these blocks are all + * bad, driver will return with failure. + * + * Format of BBT defines below: + * -------------------------------------------------------------- + * | magic | length | serial | reserved | bad block | + * | number | | number | | number | + * | (4bytes) | (4bytes) | (4bytes) | (32bytes) | information | + * -------------------------------------------------------------- + * @ magic number is: 'B''B''T''0' to identify whether this block contains + * BBT information. + * @ length: represent the length of "bad block number information" part. + * @ serial number: represent the version of BBT. each time update the + * BBT will increase this value by 1. + * @ reserved: no use right now. + * @ bad block number information: store bad block number, each number + * is a 32bits value. + * */ +static int denali_scan_bbt(struct mtd_info *mtd) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); + struct nand_chip *chip = &denali->nand; + struct denali_bbt_info *ptr = &denali->bbtinfo; + struct device *dev = &denali->dev->dev; + uint32_t len = denali->totalblks; + uint32_t serial; + uint64_t mtdsize; + uint32_t i, badblk, get_bbt = 0, ret = 0; + uint8_t bbt_w = 0,/* a flag represent whether write BBT to NAND*/ + *buf = NULL; + loff_t from = 0; + /* let mtd know the BBT blocks + * after handle BBT, resett mtd size back*/ + mtdsize = mtd->size; + mtd->size = chip->numchips * chip->chipsize; + /* alloc 2 bit for each data block + * do the same as kernel did */ + if (len % 4) + len = len/4 + 1; + else + len = len/4; + chip->bbt = NULL; + chip->bbt = kzalloc(len, GFP_KERNEL); + if (chip->bbt == NULL) { + ret = -ENOMEM; + goto failed_alloc_bbt; + } + + /* alloc buffer + * suppose the BBT size is the maximum value */ + len = denali->totalblks * 4 + HEAD_LEN; + if (len % mtd->writesize) + len = (len/mtd->writesize + 1) * mtd->writesize; + buf = kzalloc(len, GFP_KERNEL); + if (buf == NULL) { + ret = -ENOMEM; + goto failed_alloc_buf; + } + + /* check whether if there is already a bbt in NAND */ + for (i = denali->bbtstartblk; i < denali->totalblks; i++) { + from = (loff_t)i * mtd->erasesize; + ret = check_bbt_block(mtd, from, len, buf); + if (ret == -EIO) { + dev_notice(dev, "bbt start block %d is BAD", i); + /* mark the storing BBT block to be bad */ + chip->bbt[i>>2] |= 0x01 << ((i&0x03)<<1); + denali->availbbtblk--; + mtd->ecc_stats.badblocks++; + continue; + } else if (ret == 1) { + dev_notice(dev, "bbt start block %d is ERASED," + " start NEXT", i); + continue; + } else if (ret) { + dev_err(dev, "ERROR param in check_bbt_block" + " ret %d", ret); + goto failed_tail; + } + /* If read successed, store BBT basic information + * If there are different serial number, the block + * contained the biggest serial number must have + * the latest BBT information. + * so here just update BBT basice information + * only if found a bigger serial number.*/ + if (buf[0] == 'B' && buf[1] == 'B' && + buf[2] == 'T' && buf[3] == '0') { + serial = *(uint32_t *)(buf + 8); + if (serial > ptr->serial) { + ptr->version = i; + ptr->bbtlen = *(uint32_t *)(buf + 4); + ptr->serial = serial; + } + get_bbt = 1; + } + memset(buf, 0, mtd->writesize); + } + + if (denali->availbbtblk <= 0) { + /* all the blocks stored BBT are bad + * driver thought this is a bad NAND chip + * and return with a failure + * */ + ret = 1; + goto failed_tail; + } + + if (get_bbt) { + /* Found NAND contains available BBT information + * and will read it out. + * needn't check because we have already checked + * above*/ + len = ptr->bbtlen + HEAD_LEN; + if (len % mtd->writesize) + len = (len/mtd->writesize + 1) * mtd->writesize; + from = (loff_t)ptr->version * mtd->erasesize; + check_bbt_block(mtd, from, len, buf); + if (ptr->serial != *(uint32_t *)(buf + 8) || + ptr->bbtlen != *(uint32_t *)(buf + 4)) { + dev_err(dev, "BUG when get BBT form block %d", + ptr->version); + BUG(); + } + for (i = HEAD_LEN; i < ptr->bbtlen + HEAD_LEN; i += 4) { + badblk = *(uint32_t *)(buf + i); + if (!(chip->bbt[badblk >> 2] & + (0x3 << ((badblk & 0x3) << 1)))) { + chip->bbt[badblk >> 2] |= + 0x03 << ((badblk & 0x3) << 1); + mtd->ecc_stats.badblocks++; + } + } + } else { + /* If NAND doesn't contain any BBT information, + * driver create memery BBT by scaning NAND itself + * */ + ret = create_bbt(mtd); + if (ret) + goto failed_tail; + ptr->version = 0; + ptr->serial = 0; + ptr->bbtlen = 0; + bbt_w = 1; /* set to write BBT to NAND */ + } + + /* If found new bad block or driver scaned NAND itself, + * write BBT information back to NAND + * */ + if (ptr->bbtlen < mtd->ecc_stats.badblocks * 4) + ret = write_bbt_to_blk(mtd); + else if (ptr->bbtlen > mtd->ecc_stats.badblocks * 4) { + dev_err(dev, "bbtlen can't be large than badblocks number"); + BUG(); + } + + if (!ret) { + kfree(buf); + mtd->size = mtdsize; + return ret; + } + +failed_tail: + kfree(buf); +failed_alloc_buf: + kfree(chip->bbt); +failed_alloc_bbt: + mtd->size = mtdsize; + return ret; +} + +/* + * denali_block_markbad: To mark a block as bad. + * Update memery bbt and BBT nand information + * */ +static int denali_block_markbad(struct mtd_info *mtd, loff_t ofs) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); + struct nand_chip *chip = &denali->nand; + uint32_t block = ofs >> chip->bbt_erase_shift; + uint64_t mtdsize; + int ret = 0; + /* let mtd know the BBT blocks */ + mtdsize = mtd->size; + mtd->size = chip->numchips * chip->chipsize; + + chip->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1); + mtd->ecc_stats.badblocks++; + /* update the flash based BBT */ + ret = write_bbt_to_blk(mtd); + if (ret) + mtd->ecc_stats.badblocks--; + + /* after handle BBT, resett mtd size back*/ + mtd->size = mtdsize; + return ret; +} + /* stubs for ECC functions not used by the NAND core */ static int denali_ecc_calculate(struct mtd_info *mtd, const uint8_t *data, uint8_t *ecc_code) @@ -1641,6 +2090,37 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) denali->nand.phys_erase_shift; denali->blksperchip = denali->totalblks / denali->nand.numchips; + if (denali->platform == INTEL_MRST) { + denali->bbtblks = BBT_BLKNUM; + denali->availbbtblk = denali->bbtblks; + denali->bbtstartblk = denali->totalblks - denali->bbtblks; + detect_partition_feature(denali); + denali->nand.bbt_td = NULL; + denali->nand.bbt_md = NULL; + denali->nand.options &= ~(NAND_USE_FLASH_BBT); + if (denali->manageblks) { + /* represent FW just manages a part of the whole NAND, + * and the reset of the NAND need MTD driver to care, + * so NAND driver need to manage BBT. BBT is hidden by + * NAND driver, user can not access BBT blocks. + * */ + denali->mtd.size -= denali->mtd.erasesize * + denali->bbtblks; + denali->nand.options &= ~(NAND_SKIP_BBTSCAN); + } + /* otherwise represent FW manages the whole NAND and there must + * has a FTL layer, FTL layer will manage the whole NAND, + * so MTD driver needn't care the BBT + * and just report the whole NAND size + * in this situation, user can only use those filesystem base on + * block device, e.g.ext3. NAND filesystem can't be used, e.g. + * YAFFS/YAFFS2 and ubifs. + * */ + denali->nand.options |= NAND_NO_SUBPAGE_WRITE; + denali->nand.scan_bbt = denali_scan_bbt; + denali->nand.block_markbad = denali_block_markbad; + } + /* These functions are required by the NAND core framework, otherwise, * the NAND core will assert. However, we don't need them, so we'll stub * them out. */ diff --git a/drivers/mtd/nand/denali.h b/drivers/mtd/nand/denali.h index 3918bcb..05f7415 100644 --- a/drivers/mtd/nand/denali.h +++ b/drivers/mtd/nand/denali.h @@ -714,6 +714,7 @@ #define LLD_MAX_FLASH_BANKS 4 #define DENALI_BUF_SIZE (NAND_MAX_PAGESIZE + NAND_MAX_OOBSIZE) +#define BBT_BLKNUM CONFIG_MTD_NAND_DENALI_BBT_BLKNUM struct nand_buf { int head; @@ -724,6 +725,14 @@ struct nand_buf { #define INTEL_CE4100 1 #define INTEL_MRST 2 +#define HEAD_LEN 44 + +/*denali_bbt_info: To store basic BBT information*/ +struct denali_bbt_info { + uint32_t version; + uint32_t bbtlen; + uint32_t serial; +}; struct denali_nand_info { struct mtd_info mtd; @@ -748,9 +757,16 @@ struct denali_nand_info { uint32_t devnum; /* represent how many nands connected */ uint32_t fwblks; /* represent how many blocks FW used */ + uint32_t manageblks; /* how many blocks managed by FW */ uint32_t totalblks; uint32_t blksperchip; + uint32_t bbtstartblk; + uint32_t bbtblks; uint32_t bbtskipbytes; + + /*used for BBT management*/ + struct denali_bbt_info bbtinfo; + uint32_t availbbtblk; /* how many available blocks can store BBT */ }; #endif /*_LLD_NAND_*/