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, | |
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 | ***************************************************************************/ | |
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 | ***************************************************************************/ | |
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 | } |