]>
Commit | Line | Data |
---|---|---|
266dead2 LHC |
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, | |
1fbb938d | 25 | struct nand_chip *chip, uint8_t *buf, int oob_required, int page); |
266dead2 | 26 | static void bcm_umi_bch_write_page_hwecc(struct mtd_info *mtd, |
1fbb938d | 27 | struct nand_chip *chip, const uint8_t *buf, int oob_required); |
266dead2 LHC |
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 | |
1fbb938d | 106 | * @oob_required: caller expects OOB data read to chip->oob_poi |
266dead2 LHC |
107 | * |
108 | ***************************************************************************/ | |
109 | static int bcm_umi_bch_read_page_hwecc(struct mtd_info *mtd, | |
110 | struct nand_chip *chip, uint8_t * buf, | |
1fbb938d | 111 | int oob_required, int page) |
266dead2 LHC |
112 | { |
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; | |
3f91e94f | 120 | unsigned int max_bitflips = 0; |
266dead2 LHC |
121 | |
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; | |
3f91e94f | 182 | max_bitflips = max_t(unsigned int, max_bitflips, stat); |
266dead2 LHC |
183 | } |
184 | } | |
3f91e94f | 185 | return max_bitflips; |
266dead2 LHC |
186 | } |
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 | |
1fbb938d | 194 | * @oob_required: must write chip->oob_poi to OOB |
266dead2 LHC |
195 | * |
196 | ***************************************************************************/ | |
197 | static void bcm_umi_bch_write_page_hwecc(struct mtd_info *mtd, | |
1fbb938d | 198 | struct nand_chip *chip, const uint8_t *buf, int oob_required) |
266dead2 LHC |
199 | { |
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 | } |