blob: 5914bb32e0014e189cd42aafd34017698222dd27 [file] [log] [blame]
Leo (Hao) Chen266dead2009-10-09 19:13:08 -07001/*****************************************************************************
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,
Brian Norris1fbb9382012-05-02 10:14:55 -070025 struct nand_chip *chip, uint8_t *buf, int oob_required, int page);
Leo (Hao) Chen266dead2009-10-09 19:13:08 -070026static void bcm_umi_bch_write_page_hwecc(struct mtd_info *mtd,
Brian Norris1fbb9382012-05-02 10:14:55 -070027 struct nand_chip *chip, const uint8_t *buf, int oob_required);
Leo (Hao) Chen266dead2009-10-09 19:13:08 -070028
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
Brian Norris1fbb9382012-05-02 10:14:55 -0700106* @oob_required: caller expects OOB data read to chip->oob_poi
Leo (Hao) Chen266dead2009-10-09 19:13:08 -0700107*
108***************************************************************************/
109static int bcm_umi_bch_read_page_hwecc(struct mtd_info *mtd,
110 struct nand_chip *chip, uint8_t * buf,
Brian Norris1fbb9382012-05-02 10:14:55 -0700111 int oob_required, int page)
Leo (Hao) Chen266dead2009-10-09 19:13:08 -0700112{
113 int sectorIdx = 0;
114 int eccsize = chip->ecc.size;
115 int eccsteps = chip->ecc.steps;
116 uint8_t *datap = buf;
117 uint8_t eccCalc[NAND_ECC_NUM_BYTES];
118 int sectorOobSize = mtd->oobsize / eccsteps;
119 int stat;
Mike Dunn3f91e942012-04-25 12:06:09 -0700120 unsigned int max_bitflips = 0;
Leo (Hao) Chen266dead2009-10-09 19:13:08 -0700121
122 for (sectorIdx = 0; sectorIdx < eccsteps;
123 sectorIdx++, datap += eccsize) {
124 if (sectorIdx > 0) {
125 /* Seek to page location within sector */
126 chip->cmdfunc(mtd, NAND_CMD_RNDOUT, sectorIdx * eccsize,
127 -1);
128 }
129
130 /* Enable hardware ECC before reading the buf */
131 nand_bcm_umi_bch_enable_read_hwecc();
132
133 /* Read in data */
134 bcm_umi_nand_read_buf(mtd, datap, eccsize);
135
136 /* Pause hardware ECC after reading the buf */
137 nand_bcm_umi_bch_pause_read_ecc_calc();
138
139 /* Read the OOB ECC */
140 chip->cmdfunc(mtd, NAND_CMD_RNDOUT,
141 mtd->writesize + sectorIdx * sectorOobSize, -1);
142 nand_bcm_umi_bch_read_oobEcc(mtd->writesize, eccCalc,
143 NAND_ECC_NUM_BYTES,
144 chip->oob_poi +
145 sectorIdx * sectorOobSize);
146
147 /* Correct any ECC detected errors */
148 stat =
149 nand_bcm_umi_bch_correct_page(datap, eccCalc,
150 NAND_ECC_NUM_BYTES);
151
152 /* Update Stats */
153 if (stat < 0) {
154#if defined(NAND_BCM_UMI_DEBUG)
155 printk(KERN_WARNING "%s uncorr_err sectorIdx=%d\n",
156 __func__, sectorIdx);
157 printk(KERN_WARNING
158 "%s data %02x %02x %02x %02x "
159 "%02x %02x %02x %02x\n",
160 __func__, datap[0], datap[1], datap[2], datap[3],
161 datap[4], datap[5], datap[6], datap[7]);
162 printk(KERN_WARNING
163 "%s ecc %02x %02x %02x %02x "
164 "%02x %02x %02x %02x %02x %02x "
165 "%02x %02x %02x\n",
166 __func__, eccCalc[0], eccCalc[1], eccCalc[2],
167 eccCalc[3], eccCalc[4], eccCalc[5], eccCalc[6],
168 eccCalc[7], eccCalc[8], eccCalc[9], eccCalc[10],
169 eccCalc[11], eccCalc[12]);
170 BUG();
171#endif
172 mtd->ecc_stats.failed++;
173 } else {
174#if defined(NAND_BCM_UMI_DEBUG)
175 if (stat > 0) {
176 printk(KERN_INFO
177 "%s %d correctable_errors detected\n",
178 __func__, stat);
179 }
180#endif
181 mtd->ecc_stats.corrected += stat;
Mike Dunn3f91e942012-04-25 12:06:09 -0700182 max_bitflips = max_t(unsigned int, max_bitflips, stat);
Leo (Hao) Chen266dead2009-10-09 19:13:08 -0700183 }
184 }
Mike Dunn3f91e942012-04-25 12:06:09 -0700185 return max_bitflips;
Leo (Hao) Chen266dead2009-10-09 19:13:08 -0700186}
187
188/****************************************************************************
189*
190* bcm_umi_bch_write_page_hwecc - hardware ecc based page write function
191* @mtd: mtd info structure
192* @chip: nand chip info structure
193* @buf: data buffer
Brian Norris1fbb9382012-05-02 10:14:55 -0700194* @oob_required: must write chip->oob_poi to OOB
Leo (Hao) Chen266dead2009-10-09 19:13:08 -0700195*
196***************************************************************************/
197static void bcm_umi_bch_write_page_hwecc(struct mtd_info *mtd,
Brian Norris1fbb9382012-05-02 10:14:55 -0700198 struct nand_chip *chip, const uint8_t *buf, int oob_required)
Leo (Hao) Chen266dead2009-10-09 19:13:08 -0700199{
200 int sectorIdx = 0;
201 int eccsize = chip->ecc.size;
202 int eccsteps = chip->ecc.steps;
203 const uint8_t *datap = buf;
204 uint8_t *oobp = chip->oob_poi;
205 int sectorOobSize = mtd->oobsize / eccsteps;
206
207 for (sectorIdx = 0; sectorIdx < eccsteps;
208 sectorIdx++, datap += eccsize, oobp += sectorOobSize) {
209 /* Enable hardware ECC before writing the buf */
210 nand_bcm_umi_bch_enable_write_hwecc();
211 bcm_umi_nand_write_buf(mtd, datap, eccsize);
212 nand_bcm_umi_bch_write_oobEcc(mtd->writesize, oobp,
213 NAND_ECC_NUM_BYTES);
214 }
215
216 bcm_umi_nand_write_buf(mtd, chip->oob_poi, mtd->oobsize);
217}