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 -------------------------------------- */
24 static int bcm_umi_bch_read_page_hwecc(struct mtd_info *mtd,
25 	struct nand_chip *chip, uint8_t *buf, int page);
26 static 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 */
35 static 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 */
51 static 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. */
72 static 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 ***************************************************************************/
bcm_umi_bch_read_page_hwecc(struct mtd_info * mtd,struct nand_chip * chip,uint8_t * buf,int page)108 static 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 ***************************************************************************/
bcm_umi_bch_write_page_hwecc(struct mtd_info * mtd,struct nand_chip * chip,const uint8_t * buf)193 static 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