/***************************************************************************** * Copyright 2004 - 2009 Broadcom Corporation. All rights reserved. * * Unless you and Broadcom execute a separate written software license * agreement governing use of this software, this software is licensed to you * under the terms of the GNU General Public License version 2, available at * http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). * * Notwithstanding the above, under no circumstances may you combine this * software in any way with any other Broadcom software provided under a * license other than the GPL, without Broadcom's express prior written * consent. *****************************************************************************/ /* ---- Include Files ---------------------------------------------------- */ #include "nand_bcm_umi.h" /* ---- External Variable Declarations ----------------------------------- */ /* ---- External Function Prototypes ------------------------------------- */ /* ---- Public Variables ------------------------------------------------- */ /* ---- Private Constants and Types -------------------------------------- */ /* ---- Private Function Prototypes -------------------------------------- */ static int bcm_umi_bch_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int oob_required, int page); static int bcm_umi_bch_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf, int oob_required); /* ---- Private Variables ------------------------------------------------ */ /* ** nand_hw_eccoob ** New oob placement block for use with hardware ecc generation. */ static struct nand_ecclayout nand_hw_eccoob_512 = { /* Reserve 5 for BI indicator */ .oobfree = { #if (NAND_ECC_NUM_BYTES > 3) {.offset = 0, .length = 2} #else {.offset = 0, .length = 5}, {.offset = 6, .length = 7} #endif } }; /* ** We treat the OOB for a 2K page as if it were 4 512 byte oobs, ** except the BI is at byte 0. */ static struct nand_ecclayout nand_hw_eccoob_2048 = { /* Reserve 0 as BI indicator */ .oobfree = { #if (NAND_ECC_NUM_BYTES > 10) {.offset = 1, .length = 2}, #elif (NAND_ECC_NUM_BYTES > 7) {.offset = 1, .length = 5}, {.offset = 16, .length = 6}, {.offset = 32, .length = 6}, {.offset = 48, .length = 6} #else {.offset = 1, .length = 8}, {.offset = 16, .length = 9}, {.offset = 32, .length = 9}, {.offset = 48, .length = 9} #endif } }; /* We treat the OOB for a 4K page as if it were 8 512 byte oobs, * except the BI is at byte 0. */ static struct nand_ecclayout nand_hw_eccoob_4096 = { /* Reserve 0 as BI indicator */ .oobfree = { #if (NAND_ECC_NUM_BYTES > 10) {.offset = 1, .length = 2}, {.offset = 16, .length = 3}, {.offset = 32, .length = 3}, {.offset = 48, .length = 3}, {.offset = 64, .length = 3}, {.offset = 80, .length = 3}, {.offset = 96, .length = 3}, {.offset = 112, .length = 3} #else {.offset = 1, .length = 5}, {.offset = 16, .length = 6}, {.offset = 32, .length = 6}, {.offset = 48, .length = 6}, {.offset = 64, .length = 6}, {.offset = 80, .length = 6}, {.offset = 96, .length = 6}, {.offset = 112, .length = 6} #endif } }; /* ---- Private Functions ------------------------------------------------ */ /* ==== Public Functions ================================================= */ /**************************************************************************** * * bcm_umi_bch_read_page_hwecc - hardware ecc based page read function * @mtd: mtd info structure * @chip: nand chip info structure * @buf: buffer to store read data * @oob_required: caller expects OOB data read to chip->oob_poi * ***************************************************************************/ static int bcm_umi_bch_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, uint8_t * buf, int oob_required, int page) { int sectorIdx = 0; int eccsize = chip->ecc.size; int eccsteps = chip->ecc.steps; uint8_t *datap = buf; uint8_t eccCalc[NAND_ECC_NUM_BYTES]; int sectorOobSize = mtd->oobsize / eccsteps; int stat; unsigned int max_bitflips = 0; for (sectorIdx = 0; sectorIdx < eccsteps; sectorIdx++, datap += eccsize) { if (sectorIdx > 0) { /* Seek to page location within sector */ chip->cmdfunc(mtd, NAND_CMD_RNDOUT, sectorIdx * eccsize, -1); } /* Enable hardware ECC before reading the buf */ nand_bcm_umi_bch_enable_read_hwecc(); /* Read in data */ bcm_umi_nand_read_buf(mtd, datap, eccsize); /* Pause hardware ECC after reading the buf */ nand_bcm_umi_bch_pause_read_ecc_calc(); /* Read the OOB ECC */ chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize + sectorIdx * sectorOobSize, -1); nand_bcm_umi_bch_read_oobEcc(mtd->writesize, eccCalc, NAND_ECC_NUM_BYTES, chip->oob_poi + sectorIdx * sectorOobSize); /* Correct any ECC detected errors */ stat = nand_bcm_umi_bch_correct_page(datap, eccCalc, NAND_ECC_NUM_BYTES); /* Update Stats */ if (stat < 0) { #if defined(NAND_BCM_UMI_DEBUG) printk(KERN_WARNING "%s uncorr_err sectorIdx=%d\n", __func__, sectorIdx); printk(KERN_WARNING "%s data %02x %02x %02x %02x " "%02x %02x %02x %02x\n", __func__, datap[0], datap[1], datap[2], datap[3], datap[4], datap[5], datap[6], datap[7]); printk(KERN_WARNING "%s ecc %02x %02x %02x %02x " "%02x %02x %02x %02x %02x %02x " "%02x %02x %02x\n", __func__, eccCalc[0], eccCalc[1], eccCalc[2], eccCalc[3], eccCalc[4], eccCalc[5], eccCalc[6], eccCalc[7], eccCalc[8], eccCalc[9], eccCalc[10], eccCalc[11], eccCalc[12]); BUG(); #endif mtd->ecc_stats.failed++; } else { #if defined(NAND_BCM_UMI_DEBUG) if (stat > 0) { printk(KERN_INFO "%s %d correctable_errors detected\n", __func__, stat); } #endif mtd->ecc_stats.corrected += stat; max_bitflips = max_t(unsigned int, max_bitflips, stat); } } return max_bitflips; } /**************************************************************************** * * bcm_umi_bch_write_page_hwecc - hardware ecc based page write function * @mtd: mtd info structure * @chip: nand chip info structure * @buf: data buffer * @oob_required: must write chip->oob_poi to OOB * ***************************************************************************/ static int bcm_umi_bch_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf, int oob_required) { int sectorIdx = 0; int eccsize = chip->ecc.size; int eccsteps = chip->ecc.steps; const uint8_t *datap = buf; uint8_t *oobp = chip->oob_poi; int sectorOobSize = mtd->oobsize / eccsteps; for (sectorIdx = 0; sectorIdx < eccsteps; sectorIdx++, datap += eccsize, oobp += sectorOobSize) { /* Enable hardware ECC before writing the buf */ nand_bcm_umi_bch_enable_write_hwecc(); bcm_umi_nand_write_buf(mtd, datap, eccsize); nand_bcm_umi_bch_write_oobEcc(mtd->writesize, oobp, NAND_ECC_NUM_BYTES); } bcm_umi_nand_write_buf(mtd, chip->oob_poi, mtd->oobsize); return 0; }