]>
Commit | Line | Data |
---|---|---|
1079432e SL |
1 | /* |
2 | * (C) Copyright 2007-2008 | |
3 | * Stelian Pop <[email protected]> | |
4 | * Lead Tech Design <www.leadtechdesign.com> | |
5 | * | |
6 | * (C) Copyright 2006 ATMEL Rousset, Lacressonniere Nicolas | |
7 | * | |
8 | * See file CREDITS for list of people who contributed to this | |
9 | * project. | |
10 | * | |
11 | * This program is free software; you can redistribute it and/or | |
12 | * modify it under the terms of the GNU General Public License as | |
13 | * published by the Free Software Foundation; either version 2 of | |
14 | * the License, or (at your option) any later version. | |
15 | * | |
16 | * This program is distributed in the hope that it will be useful, | |
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
19 | * GNU General Public License for more details. | |
20 | * | |
21 | * You should have received a copy of the GNU General Public License | |
22 | * along with this program; if not, write to the Free Software | |
23 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, | |
24 | * MA 02111-1307 USA | |
25 | */ | |
26 | ||
27 | #include <common.h> | |
74c076d6 | 28 | #include <asm/arch/hardware.h> |
1079432e SL |
29 | #include <asm/arch/gpio.h> |
30 | #include <asm/arch/at91_pio.h> | |
31 | ||
32 | #include <nand.h> | |
33 | ||
7c27b7b1 NP |
34 | #ifdef CONFIG_ATMEL_NAND_HWECC |
35 | ||
36 | /* Register access macros */ | |
37 | #define ecc_readl(add, reg) \ | |
38 | readl(AT91_BASE_SYS + add + ATMEL_ECC_##reg) | |
39 | #define ecc_writel(add, reg, value) \ | |
40 | writel((value), AT91_BASE_SYS + add + ATMEL_ECC_##reg) | |
41 | ||
42 | #include "atmel_nand_ecc.h" /* Hardware ECC registers */ | |
43 | ||
44 | /* oob layout for large page size | |
45 | * bad block info is on bytes 0 and 1 | |
46 | * the bytes have to be consecutives to avoid | |
47 | * several NAND_CMD_RNDOUT during read | |
48 | */ | |
49 | static struct nand_ecclayout atmel_oobinfo_large = { | |
50 | .eccbytes = 4, | |
51 | .eccpos = {60, 61, 62, 63}, | |
52 | .oobfree = { | |
53 | {2, 58} | |
54 | }, | |
55 | }; | |
56 | ||
57 | /* oob layout for small page size | |
58 | * bad block info is on bytes 4 and 5 | |
59 | * the bytes have to be consecutives to avoid | |
60 | * several NAND_CMD_RNDOUT during read | |
61 | */ | |
62 | static struct nand_ecclayout atmel_oobinfo_small = { | |
63 | .eccbytes = 4, | |
64 | .eccpos = {0, 1, 2, 3}, | |
65 | .oobfree = { | |
66 | {6, 10} | |
67 | }, | |
68 | }; | |
69 | ||
70 | /* | |
71 | * Calculate HW ECC | |
72 | * | |
73 | * function called after a write | |
74 | * | |
75 | * mtd: MTD block structure | |
76 | * dat: raw data (unused) | |
77 | * ecc_code: buffer for ECC | |
78 | */ | |
79 | static int atmel_nand_calculate(struct mtd_info *mtd, | |
80 | const u_char *dat, unsigned char *ecc_code) | |
81 | { | |
82 | struct nand_chip *nand_chip = mtd->priv; | |
83 | unsigned int ecc_value; | |
84 | ||
85 | /* get the first 2 ECC bytes */ | |
86 | ecc_value = ecc_readl(CONFIG_SYS_NAND_ECC_BASE, PR); | |
87 | ||
88 | ecc_code[0] = ecc_value & 0xFF; | |
89 | ecc_code[1] = (ecc_value >> 8) & 0xFF; | |
90 | ||
91 | /* get the last 2 ECC bytes */ | |
92 | ecc_value = ecc_readl(CONFIG_SYS_NAND_ECC_BASE, NPR) & ATMEL_ECC_NPARITY; | |
93 | ||
94 | ecc_code[2] = ecc_value & 0xFF; | |
95 | ecc_code[3] = (ecc_value >> 8) & 0xFF; | |
96 | ||
97 | return 0; | |
98 | } | |
99 | ||
100 | /* | |
101 | * HW ECC read page function | |
102 | * | |
103 | * mtd: mtd info structure | |
104 | * chip: nand chip info structure | |
105 | * buf: buffer to store read data | |
106 | */ | |
107 | static int atmel_nand_read_page(struct mtd_info *mtd, | |
108 | struct nand_chip *chip, uint8_t *buf, int page) | |
109 | { | |
110 | int eccsize = chip->ecc.size; | |
111 | int eccbytes = chip->ecc.bytes; | |
112 | uint32_t *eccpos = chip->ecc.layout->eccpos; | |
113 | uint8_t *p = buf; | |
114 | uint8_t *oob = chip->oob_poi; | |
115 | uint8_t *ecc_pos; | |
116 | int stat; | |
117 | ||
118 | /* read the page */ | |
119 | chip->read_buf(mtd, p, eccsize); | |
120 | ||
121 | /* move to ECC position if needed */ | |
122 | if (eccpos[0] != 0) { | |
123 | /* This only works on large pages | |
124 | * because the ECC controller waits for | |
125 | * NAND_CMD_RNDOUTSTART after the | |
126 | * NAND_CMD_RNDOUT. | |
127 | * anyway, for small pages, the eccpos[0] == 0 | |
128 | */ | |
129 | chip->cmdfunc(mtd, NAND_CMD_RNDOUT, | |
130 | mtd->writesize + eccpos[0], -1); | |
131 | } | |
132 | ||
133 | /* the ECC controller needs to read the ECC just after the data */ | |
134 | ecc_pos = oob + eccpos[0]; | |
135 | chip->read_buf(mtd, ecc_pos, eccbytes); | |
136 | ||
137 | /* check if there's an error */ | |
138 | stat = chip->ecc.correct(mtd, p, oob, NULL); | |
139 | ||
140 | if (stat < 0) | |
141 | mtd->ecc_stats.failed++; | |
142 | else | |
143 | mtd->ecc_stats.corrected += stat; | |
144 | ||
145 | /* get back to oob start (end of page) */ | |
146 | chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize, -1); | |
147 | ||
148 | /* read the oob */ | |
149 | chip->read_buf(mtd, oob, mtd->oobsize); | |
150 | ||
151 | return 0; | |
152 | } | |
153 | ||
154 | /* | |
155 | * HW ECC Correction | |
156 | * | |
157 | * function called after a read | |
158 | * | |
159 | * mtd: MTD block structure | |
160 | * dat: raw data read from the chip | |
161 | * read_ecc: ECC from the chip (unused) | |
162 | * isnull: unused | |
163 | * | |
164 | * Detect and correct a 1 bit error for a page | |
165 | */ | |
166 | static int atmel_nand_correct(struct mtd_info *mtd, u_char *dat, | |
167 | u_char *read_ecc, u_char *isnull) | |
168 | { | |
169 | struct nand_chip *nand_chip = mtd->priv; | |
170 | unsigned int ecc_status, ecc_parity, ecc_mode; | |
171 | unsigned int ecc_word, ecc_bit; | |
172 | ||
173 | /* get the status from the Status Register */ | |
174 | ecc_status = ecc_readl(CONFIG_SYS_NAND_ECC_BASE, SR); | |
175 | ||
176 | /* if there's no error */ | |
177 | if (likely(!(ecc_status & ATMEL_ECC_RECERR))) | |
178 | return 0; | |
179 | ||
180 | /* get error bit offset (4 bits) */ | |
181 | ecc_bit = ecc_readl(CONFIG_SYS_NAND_ECC_BASE, PR) & ATMEL_ECC_BITADDR; | |
182 | /* get word address (12 bits) */ | |
183 | ecc_word = ecc_readl(CONFIG_SYS_NAND_ECC_BASE, PR) & ATMEL_ECC_WORDADDR; | |
184 | ecc_word >>= 4; | |
185 | ||
186 | /* if there are multiple errors */ | |
187 | if (ecc_status & ATMEL_ECC_MULERR) { | |
188 | /* check if it is a freshly erased block | |
189 | * (filled with 0xff) */ | |
190 | if ((ecc_bit == ATMEL_ECC_BITADDR) | |
191 | && (ecc_word == (ATMEL_ECC_WORDADDR >> 4))) { | |
192 | /* the block has just been erased, return OK */ | |
193 | return 0; | |
194 | } | |
195 | /* it doesn't seems to be a freshly | |
196 | * erased block. | |
197 | * We can't correct so many errors */ | |
198 | printk(KERN_WARNING "atmel_nand : multiple errors detected." | |
199 | " Unable to correct.\n"); | |
200 | return -EIO; | |
201 | } | |
202 | ||
203 | /* if there's a single bit error : we can correct it */ | |
204 | if (ecc_status & ATMEL_ECC_ECCERR) { | |
205 | /* there's nothing much to do here. | |
206 | * the bit error is on the ECC itself. | |
207 | */ | |
208 | printk(KERN_WARNING "atmel_nand : one bit error on ECC code." | |
209 | " Nothing to correct\n"); | |
210 | return 0; | |
211 | } | |
212 | ||
213 | printk(KERN_WARNING "atmel_nand : one bit error on data." | |
214 | " (word offset in the page :" | |
215 | " 0x%x bit offset : 0x%x)\n", | |
216 | ecc_word, ecc_bit); | |
217 | /* correct the error */ | |
218 | if (nand_chip->options & NAND_BUSWIDTH_16) { | |
219 | /* 16 bits words */ | |
220 | ((unsigned short *) dat)[ecc_word] ^= (1 << ecc_bit); | |
221 | } else { | |
222 | /* 8 bits words */ | |
223 | dat[ecc_word] ^= (1 << ecc_bit); | |
224 | } | |
225 | printk(KERN_WARNING "atmel_nand : error corrected\n"); | |
226 | return 1; | |
227 | } | |
228 | ||
229 | /* | |
230 | * Enable HW ECC : unused on most chips | |
231 | */ | |
232 | static void atmel_nand_hwctl(struct mtd_info *mtd, int mode) | |
233 | { | |
234 | } | |
235 | #endif | |
236 | ||
74c076d6 | 237 | static void at91_nand_hwcontrol(struct mtd_info *mtd, |
1079432e SL |
238 | int cmd, unsigned int ctrl) |
239 | { | |
240 | struct nand_chip *this = mtd->priv; | |
241 | ||
242 | if (ctrl & NAND_CTRL_CHANGE) { | |
243 | ulong IO_ADDR_W = (ulong) this->IO_ADDR_W; | |
74c076d6 JCPV |
244 | IO_ADDR_W &= ~(CONFIG_SYS_NAND_MASK_ALE |
245 | | CONFIG_SYS_NAND_MASK_CLE); | |
1079432e SL |
246 | |
247 | if (ctrl & NAND_CLE) | |
74c076d6 | 248 | IO_ADDR_W |= CONFIG_SYS_NAND_MASK_CLE; |
1079432e | 249 | if (ctrl & NAND_ALE) |
74c076d6 | 250 | IO_ADDR_W |= CONFIG_SYS_NAND_MASK_ALE; |
1079432e | 251 | |
67a490d6 | 252 | #ifdef CONFIG_SYS_NAND_ENABLE_PIN |
74c076d6 JCPV |
253 | at91_set_gpio_value(CONFIG_SYS_NAND_ENABLE_PIN, |
254 | !(ctrl & NAND_NCE)); | |
67a490d6 | 255 | #endif |
1079432e SL |
256 | this->IO_ADDR_W = (void *) IO_ADDR_W; |
257 | } | |
258 | ||
259 | if (cmd != NAND_CMD_NONE) | |
260 | writeb(cmd, this->IO_ADDR_W); | |
261 | } | |
262 | ||
74c076d6 JCPV |
263 | #ifdef CONFIG_SYS_NAND_READY_PIN |
264 | static int at91_nand_ready(struct mtd_info *mtd) | |
1079432e | 265 | { |
74c076d6 | 266 | return at91_get_gpio_value(CONFIG_SYS_NAND_READY_PIN); |
1079432e | 267 | } |
74c076d6 | 268 | #endif |
1079432e SL |
269 | |
270 | int board_nand_init(struct nand_chip *nand) | |
271 | { | |
7c27b7b1 NP |
272 | #ifdef CONFIG_ATMEL_NAND_HWECC |
273 | static int chip_nr = 0; | |
274 | struct mtd_info *mtd; | |
275 | #endif | |
276 | ||
1079432e SL |
277 | nand->ecc.mode = NAND_ECC_SOFT; |
278 | #ifdef CONFIG_SYS_NAND_DBW_16 | |
279 | nand->options = NAND_BUSWIDTH_16; | |
280 | #endif | |
74c076d6 JCPV |
281 | nand->cmd_ctrl = at91_nand_hwcontrol; |
282 | #ifdef CONFIG_SYS_NAND_READY_PIN | |
283 | nand->dev_ready = at91_nand_ready; | |
284 | #endif | |
1079432e SL |
285 | nand->chip_delay = 20; |
286 | ||
7c27b7b1 NP |
287 | #ifdef CONFIG_ATMEL_NAND_HWECC |
288 | nand->ecc.mode = NAND_ECC_HW; | |
289 | nand->ecc.calculate = atmel_nand_calculate; | |
290 | nand->ecc.correct = atmel_nand_correct; | |
291 | nand->ecc.hwctl = atmel_nand_hwctl; | |
292 | nand->ecc.read_page = atmel_nand_read_page; | |
293 | nand->ecc.bytes = 4; | |
294 | #endif | |
295 | ||
296 | #ifdef CONFIG_ATMEL_NAND_HWECC | |
297 | mtd = &nand_info[chip_nr++]; | |
298 | mtd->priv = nand; | |
299 | ||
300 | /* Detect NAND chips */ | |
245eb900 | 301 | if (nand_scan_ident(mtd, 1, NULL)) { |
7c27b7b1 NP |
302 | printk(KERN_WARNING "NAND Flash not found !\n"); |
303 | return -ENXIO; | |
304 | } | |
305 | ||
306 | if (nand->ecc.mode == NAND_ECC_HW) { | |
307 | /* ECC is calculated for the whole page (1 step) */ | |
308 | nand->ecc.size = mtd->writesize; | |
309 | ||
310 | /* set ECC page size and oob layout */ | |
311 | switch (mtd->writesize) { | |
312 | case 512: | |
313 | nand->ecc.layout = &atmel_oobinfo_small; | |
314 | ecc_writel(CONFIG_SYS_NAND_ECC_BASE, MR, ATMEL_ECC_PAGESIZE_528); | |
315 | break; | |
316 | case 1024: | |
317 | nand->ecc.layout = &atmel_oobinfo_large; | |
318 | ecc_writel(CONFIG_SYS_NAND_ECC_BASE, MR, ATMEL_ECC_PAGESIZE_1056); | |
319 | break; | |
320 | case 2048: | |
321 | nand->ecc.layout = &atmel_oobinfo_large; | |
322 | ecc_writel(CONFIG_SYS_NAND_ECC_BASE, MR, ATMEL_ECC_PAGESIZE_2112); | |
323 | break; | |
324 | case 4096: | |
325 | nand->ecc.layout = &atmel_oobinfo_large; | |
326 | ecc_writel(CONFIG_SYS_NAND_ECC_BASE, MR, ATMEL_ECC_PAGESIZE_4224); | |
327 | break; | |
328 | default: | |
329 | /* page size not handled by HW ECC */ | |
330 | /* switching back to soft ECC */ | |
331 | nand->ecc.mode = NAND_ECC_SOFT; | |
332 | nand->ecc.calculate = NULL; | |
333 | nand->ecc.correct = NULL; | |
334 | nand->ecc.hwctl = NULL; | |
335 | nand->ecc.read_page = NULL; | |
336 | nand->ecc.postpad = 0; | |
337 | nand->ecc.prepad = 0; | |
338 | nand->ecc.bytes = 0; | |
339 | break; | |
340 | } | |
341 | } | |
342 | #endif | |
343 | ||
1079432e SL |
344 | return 0; |
345 | } |