1/***************************************************************************** 2* Copyright 2004 - 2009 Broadcom Corporation. All rights reserved. 3* 4* Unless you and Broadcom execute a separate written software license 5* agreement governing use of this software, this software is licensed to you 6* under the terms of the GNU General Public License version 2, available at 7* http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). 8* 9* Notwithstanding the above, under no circumstances may you combine this 10* software in any way with any other Broadcom software provided under a 11* license other than the GPL, without Broadcom's express prior written 12* consent. 13*****************************************************************************/ 14 15/* ---- Include Files ---------------------------------------------------- */ 16#include "nand_bcm_umi.h" 17 18/* ---- External Variable Declarations ----------------------------------- */ 19/* ---- External Function Prototypes ------------------------------------- */ 20/* ---- Public Variables ------------------------------------------------- */ 21/* ---- Private Constants and Types -------------------------------------- */ 22 23/* ---- Private Function Prototypes -------------------------------------- */ 24static int bcm_umi_bch_read_page_hwecc(struct mtd_info *mtd, 25 struct nand_chip *chip, uint8_t *buf, int page); 26static void bcm_umi_bch_write_page_hwecc(struct mtd_info *mtd, 27 struct nand_chip *chip, const uint8_t *buf); 28 29/* ---- Private Variables ------------------------------------------------ */ 30 31/* 32** nand_hw_eccoob 33** New oob placement block for use with hardware ecc generation. 34*/ 35static struct nand_ecclayout nand_hw_eccoob_512 = { 36 /* Reserve 5 for BI indicator */ 37 .oobfree = { 38#if (NAND_ECC_NUM_BYTES > 3) 39 {.offset = 0, .length = 2} 40#else 41 {.offset = 0, .length = 5}, 42 {.offset = 6, .length = 7} 43#endif 44 } 45}; 46 47/* 48** We treat the OOB for a 2K page as if it were 4 512 byte oobs, 49** except the BI is at byte 0. 50*/ 51static struct nand_ecclayout nand_hw_eccoob_2048 = { 52 /* Reserve 0 as BI indicator */ 53 .oobfree = { 54#if (NAND_ECC_NUM_BYTES > 10) 55 {.offset = 1, .length = 2}, 56#elif (NAND_ECC_NUM_BYTES > 7) 57 {.offset = 1, .length = 5}, 58 {.offset = 16, .length = 6}, 59 {.offset = 32, .length = 6}, 60 {.offset = 48, .length = 6} 61#else 62 {.offset = 1, .length = 8}, 63 {.offset = 16, .length = 9}, 64 {.offset = 32, .length = 9}, 65 {.offset = 48, .length = 9} 66#endif 67 } 68}; 69 70/* We treat the OOB for a 4K page as if it were 8 512 byte oobs, 71 * except the BI is at byte 0. */ 72static struct nand_ecclayout nand_hw_eccoob_4096 = { 73 /* Reserve 0 as BI indicator */ 74 .oobfree = { 75#if (NAND_ECC_NUM_BYTES > 10) 76 {.offset = 1, .length = 2}, 77 {.offset = 16, .length = 3}, 78 {.offset = 32, .length = 3}, 79 {.offset = 48, .length = 3}, 80 {.offset = 64, .length = 3}, 81 {.offset = 80, .length = 3}, 82 {.offset = 96, .length = 3}, 83 {.offset = 112, .length = 3} 84#else 85 {.offset = 1, .length = 5}, 86 {.offset = 16, .length = 6}, 87 {.offset = 32, .length = 6}, 88 {.offset = 48, .length = 6}, 89 {.offset = 64, .length = 6}, 90 {.offset = 80, .length = 6}, 91 {.offset = 96, .length = 6}, 92 {.offset = 112, .length = 6} 93#endif 94 } 95}; 96 97/* ---- Private Functions ------------------------------------------------ */ 98/* ==== Public Functions ================================================= */ 99 100/**************************************************************************** 101* 102* bcm_umi_bch_read_page_hwecc - hardware ecc based page read function 103* @mtd: mtd info structure 104* @chip: nand chip info structure 105* @buf: buffer to store read data 106* 107***************************************************************************/ 108static int bcm_umi_bch_read_page_hwecc(struct mtd_info *mtd, 109 struct nand_chip *chip, uint8_t * buf, 110 int page) 111{ 112 int sectorIdx = 0; 113 int eccsize = chip->ecc.size; 114 int eccsteps = chip->ecc.steps; 115 uint8_t *datap = buf; 116 uint8_t eccCalc[NAND_ECC_NUM_BYTES]; 117 int sectorOobSize = mtd->oobsize / eccsteps; 118 int stat; 119 120 for (sectorIdx = 0; sectorIdx < eccsteps; 121 sectorIdx++, datap += eccsize) { 122 if (sectorIdx > 0) { 123 /* Seek to page location within sector */ 124 chip->cmdfunc(mtd, NAND_CMD_RNDOUT, sectorIdx * eccsize, 125 -1); 126 } 127 128 /* Enable hardware ECC before reading the buf */ 129 nand_bcm_umi_bch_enable_read_hwecc(); 130 131 /* Read in data */ 132 bcm_umi_nand_read_buf(mtd, datap, eccsize); 133 134 /* Pause hardware ECC after reading the buf */ 135 nand_bcm_umi_bch_pause_read_ecc_calc(); 136 137 /* Read the OOB ECC */ 138 chip->cmdfunc(mtd, NAND_CMD_RNDOUT, 139 mtd->writesize + sectorIdx * sectorOobSize, -1); 140 nand_bcm_umi_bch_read_oobEcc(mtd->writesize, eccCalc, 141 NAND_ECC_NUM_BYTES, 142 chip->oob_poi + 143 sectorIdx * sectorOobSize); 144 145 /* Correct any ECC detected errors */ 146 stat = 147 nand_bcm_umi_bch_correct_page(datap, eccCalc, 148 NAND_ECC_NUM_BYTES); 149 150 /* Update Stats */ 151 if (stat < 0) { 152#if defined(NAND_BCM_UMI_DEBUG) 153 printk(KERN_WARNING "%s uncorr_err sectorIdx=%d\n", 154 __func__, sectorIdx); 155 printk(KERN_WARNING 156 "%s data %02x %02x %02x %02x " 157 "%02x %02x %02x %02x\n", 158 __func__, datap[0], datap[1], datap[2], datap[3], 159 datap[4], datap[5], datap[6], datap[7]); 160 printk(KERN_WARNING 161 "%s ecc %02x %02x %02x %02x " 162 "%02x %02x %02x %02x %02x %02x " 163 "%02x %02x %02x\n", 164 __func__, eccCalc[0], eccCalc[1], eccCalc[2], 165 eccCalc[3], eccCalc[4], eccCalc[5], eccCalc[6], 166 eccCalc[7], eccCalc[8], eccCalc[9], eccCalc[10], 167 eccCalc[11], eccCalc[12]); 168 BUG(); 169#endif 170 mtd->ecc_stats.failed++; 171 } else { 172#if defined(NAND_BCM_UMI_DEBUG) 173 if (stat > 0) { 174 printk(KERN_INFO 175 "%s %d correctable_errors detected\n", 176 __func__, stat); 177 } 178#endif 179 mtd->ecc_stats.corrected += stat; 180 } 181 } 182 return 0; 183} 184 185/**************************************************************************** 186* 187* bcm_umi_bch_write_page_hwecc - hardware ecc based page write function 188* @mtd: mtd info structure 189* @chip: nand chip info structure 190* @buf: data buffer 191* 192***************************************************************************/ 193static void bcm_umi_bch_write_page_hwecc(struct mtd_info *mtd, 194 struct nand_chip *chip, const uint8_t *buf) 195{ 196 int sectorIdx = 0; 197 int eccsize = chip->ecc.size; 198 int eccsteps = chip->ecc.steps; 199 const uint8_t *datap = buf; 200 uint8_t *oobp = chip->oob_poi; 201 int sectorOobSize = mtd->oobsize / eccsteps; 202 203 for (sectorIdx = 0; sectorIdx < eccsteps; 204 sectorIdx++, datap += eccsize, oobp += sectorOobSize) { 205 /* Enable hardware ECC before writing the buf */ 206 nand_bcm_umi_bch_enable_write_hwecc(); 207 bcm_umi_nand_write_buf(mtd, datap, eccsize); 208 nand_bcm_umi_bch_write_oobEcc(mtd->writesize, oobp, 209 NAND_ECC_NUM_BYTES); 210 } 211 212 bcm_umi_nand_write_buf(mtd, chip->oob_poi, mtd->oobsize); 213} 214