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