blob: cc609b1b5249b56b65ca44f2bcf71a1d99c72afe [file] [log] [blame]
Sergey Lapin10794322008-10-31 12:28:43 +01001/*
2 * (C) Copyright 2007-2008
Stelian Popc9e798d2011-11-01 00:00:39 +01003 * Stelian Pop <stelian@popies.net>
Sergey Lapin10794322008-10-31 12:28:43 +01004 * 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>
Jean-Christophe PLAGNIOL-VILLARD74c076d2009-03-22 10:22:34 +010028#include <asm/arch/hardware.h>
Sergey Lapin10794322008-10-31 12:28:43 +010029#include <asm/arch/gpio.h>
30#include <asm/arch/at91_pio.h>
31
32#include <nand.h>
33
Nikolay Petukhov7c27b7b2010-03-19 10:49:27 +050034#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 */
49static 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 */
62static 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 */
79static int atmel_nand_calculate(struct mtd_info *mtd,
80 const u_char *dat, unsigned char *ecc_code)
81{
Nikolay Petukhov7c27b7b2010-03-19 10:49:27 +050082 unsigned int ecc_value;
83
84 /* get the first 2 ECC bytes */
85 ecc_value = ecc_readl(CONFIG_SYS_NAND_ECC_BASE, PR);
86
87 ecc_code[0] = ecc_value & 0xFF;
88 ecc_code[1] = (ecc_value >> 8) & 0xFF;
89
90 /* get the last 2 ECC bytes */
91 ecc_value = ecc_readl(CONFIG_SYS_NAND_ECC_BASE, NPR) & ATMEL_ECC_NPARITY;
92
93 ecc_code[2] = ecc_value & 0xFF;
94 ecc_code[3] = (ecc_value >> 8) & 0xFF;
95
96 return 0;
97}
98
99/*
100 * HW ECC read page function
101 *
102 * mtd: mtd info structure
103 * chip: nand chip info structure
104 * buf: buffer to store read data
105 */
106static int atmel_nand_read_page(struct mtd_info *mtd,
107 struct nand_chip *chip, uint8_t *buf, int page)
108{
109 int eccsize = chip->ecc.size;
110 int eccbytes = chip->ecc.bytes;
111 uint32_t *eccpos = chip->ecc.layout->eccpos;
112 uint8_t *p = buf;
113 uint8_t *oob = chip->oob_poi;
114 uint8_t *ecc_pos;
115 int stat;
116
117 /* read the page */
118 chip->read_buf(mtd, p, eccsize);
119
120 /* move to ECC position if needed */
121 if (eccpos[0] != 0) {
122 /* This only works on large pages
123 * because the ECC controller waits for
124 * NAND_CMD_RNDOUTSTART after the
125 * NAND_CMD_RNDOUT.
126 * anyway, for small pages, the eccpos[0] == 0
127 */
128 chip->cmdfunc(mtd, NAND_CMD_RNDOUT,
129 mtd->writesize + eccpos[0], -1);
130 }
131
132 /* the ECC controller needs to read the ECC just after the data */
133 ecc_pos = oob + eccpos[0];
134 chip->read_buf(mtd, ecc_pos, eccbytes);
135
136 /* check if there's an error */
137 stat = chip->ecc.correct(mtd, p, oob, NULL);
138
139 if (stat < 0)
140 mtd->ecc_stats.failed++;
141 else
142 mtd->ecc_stats.corrected += stat;
143
144 /* get back to oob start (end of page) */
145 chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize, -1);
146
147 /* read the oob */
148 chip->read_buf(mtd, oob, mtd->oobsize);
149
150 return 0;
151}
152
153/*
154 * HW ECC Correction
155 *
156 * function called after a read
157 *
158 * mtd: MTD block structure
159 * dat: raw data read from the chip
160 * read_ecc: ECC from the chip (unused)
161 * isnull: unused
162 *
163 * Detect and correct a 1 bit error for a page
164 */
165static int atmel_nand_correct(struct mtd_info *mtd, u_char *dat,
166 u_char *read_ecc, u_char *isnull)
167{
168 struct nand_chip *nand_chip = mtd->priv;
Wu, Joshae797942012-08-23 00:05:35 +0000169 unsigned int ecc_status;
Nikolay Petukhov7c27b7b2010-03-19 10:49:27 +0500170 unsigned int ecc_word, ecc_bit;
171
172 /* get the status from the Status Register */
173 ecc_status = ecc_readl(CONFIG_SYS_NAND_ECC_BASE, SR);
174
175 /* if there's no error */
176 if (likely(!(ecc_status & ATMEL_ECC_RECERR)))
177 return 0;
178
179 /* get error bit offset (4 bits) */
180 ecc_bit = ecc_readl(CONFIG_SYS_NAND_ECC_BASE, PR) & ATMEL_ECC_BITADDR;
181 /* get word address (12 bits) */
182 ecc_word = ecc_readl(CONFIG_SYS_NAND_ECC_BASE, PR) & ATMEL_ECC_WORDADDR;
183 ecc_word >>= 4;
184
185 /* if there are multiple errors */
186 if (ecc_status & ATMEL_ECC_MULERR) {
187 /* check if it is a freshly erased block
188 * (filled with 0xff) */
189 if ((ecc_bit == ATMEL_ECC_BITADDR)
190 && (ecc_word == (ATMEL_ECC_WORDADDR >> 4))) {
191 /* the block has just been erased, return OK */
192 return 0;
193 }
194 /* it doesn't seems to be a freshly
195 * erased block.
196 * We can't correct so many errors */
197 printk(KERN_WARNING "atmel_nand : multiple errors detected."
198 " Unable to correct.\n");
199 return -EIO;
200 }
201
202 /* if there's a single bit error : we can correct it */
203 if (ecc_status & ATMEL_ECC_ECCERR) {
204 /* there's nothing much to do here.
205 * the bit error is on the ECC itself.
206 */
207 printk(KERN_WARNING "atmel_nand : one bit error on ECC code."
208 " Nothing to correct\n");
209 return 0;
210 }
211
212 printk(KERN_WARNING "atmel_nand : one bit error on data."
213 " (word offset in the page :"
214 " 0x%x bit offset : 0x%x)\n",
215 ecc_word, ecc_bit);
216 /* correct the error */
217 if (nand_chip->options & NAND_BUSWIDTH_16) {
218 /* 16 bits words */
219 ((unsigned short *) dat)[ecc_word] ^= (1 << ecc_bit);
220 } else {
221 /* 8 bits words */
222 dat[ecc_word] ^= (1 << ecc_bit);
223 }
224 printk(KERN_WARNING "atmel_nand : error corrected\n");
225 return 1;
226}
227
228/*
229 * Enable HW ECC : unused on most chips
230 */
231static void atmel_nand_hwctl(struct mtd_info *mtd, int mode)
232{
233}
Wu, Joshfe2185e2012-08-23 00:05:34 +0000234
235int atmel_hwecc_nand_init_param(struct nand_chip *nand, struct mtd_info *mtd)
236{
237 nand->ecc.mode = NAND_ECC_HW;
238 nand->ecc.calculate = atmel_nand_calculate;
239 nand->ecc.correct = atmel_nand_correct;
240 nand->ecc.hwctl = atmel_nand_hwctl;
241 nand->ecc.read_page = atmel_nand_read_page;
242 nand->ecc.bytes = 4;
243
244 if (nand->ecc.mode == NAND_ECC_HW) {
245 /* ECC is calculated for the whole page (1 step) */
246 nand->ecc.size = mtd->writesize;
247
248 /* set ECC page size and oob layout */
249 switch (mtd->writesize) {
250 case 512:
251 nand->ecc.layout = &atmel_oobinfo_small;
252 ecc_writel(CONFIG_SYS_NAND_ECC_BASE, MR,
253 ATMEL_ECC_PAGESIZE_528);
254 break;
255 case 1024:
256 nand->ecc.layout = &atmel_oobinfo_large;
257 ecc_writel(CONFIG_SYS_NAND_ECC_BASE, MR,
258 ATMEL_ECC_PAGESIZE_1056);
259 break;
260 case 2048:
261 nand->ecc.layout = &atmel_oobinfo_large;
262 ecc_writel(CONFIG_SYS_NAND_ECC_BASE, MR,
263 ATMEL_ECC_PAGESIZE_2112);
264 break;
265 case 4096:
266 nand->ecc.layout = &atmel_oobinfo_large;
267 ecc_writel(CONFIG_SYS_NAND_ECC_BASE, MR,
268 ATMEL_ECC_PAGESIZE_4224);
269 break;
270 default:
271 /* page size not handled by HW ECC */
272 /* switching back to soft ECC */
273 nand->ecc.mode = NAND_ECC_SOFT;
274 nand->ecc.calculate = NULL;
275 nand->ecc.correct = NULL;
276 nand->ecc.hwctl = NULL;
277 nand->ecc.read_page = NULL;
278 nand->ecc.postpad = 0;
279 nand->ecc.prepad = 0;
280 nand->ecc.bytes = 0;
281 break;
282 }
283 }
284
285 return 0;
286}
287
Nikolay Petukhov7c27b7b2010-03-19 10:49:27 +0500288#endif
289
Jean-Christophe PLAGNIOL-VILLARD74c076d2009-03-22 10:22:34 +0100290static void at91_nand_hwcontrol(struct mtd_info *mtd,
Sergey Lapin10794322008-10-31 12:28:43 +0100291 int cmd, unsigned int ctrl)
292{
293 struct nand_chip *this = mtd->priv;
294
295 if (ctrl & NAND_CTRL_CHANGE) {
296 ulong IO_ADDR_W = (ulong) this->IO_ADDR_W;
Jean-Christophe PLAGNIOL-VILLARD74c076d2009-03-22 10:22:34 +0100297 IO_ADDR_W &= ~(CONFIG_SYS_NAND_MASK_ALE
298 | CONFIG_SYS_NAND_MASK_CLE);
Sergey Lapin10794322008-10-31 12:28:43 +0100299
300 if (ctrl & NAND_CLE)
Jean-Christophe PLAGNIOL-VILLARD74c076d2009-03-22 10:22:34 +0100301 IO_ADDR_W |= CONFIG_SYS_NAND_MASK_CLE;
Sergey Lapin10794322008-10-31 12:28:43 +0100302 if (ctrl & NAND_ALE)
Jean-Christophe PLAGNIOL-VILLARD74c076d2009-03-22 10:22:34 +0100303 IO_ADDR_W |= CONFIG_SYS_NAND_MASK_ALE;
Sergey Lapin10794322008-10-31 12:28:43 +0100304
michael67a490d2011-03-14 21:16:38 +0000305#ifdef CONFIG_SYS_NAND_ENABLE_PIN
Jean-Christophe PLAGNIOL-VILLARD74c076d2009-03-22 10:22:34 +0100306 at91_set_gpio_value(CONFIG_SYS_NAND_ENABLE_PIN,
307 !(ctrl & NAND_NCE));
michael67a490d2011-03-14 21:16:38 +0000308#endif
Sergey Lapin10794322008-10-31 12:28:43 +0100309 this->IO_ADDR_W = (void *) IO_ADDR_W;
310 }
311
312 if (cmd != NAND_CMD_NONE)
313 writeb(cmd, this->IO_ADDR_W);
314}
315
Jean-Christophe PLAGNIOL-VILLARD74c076d2009-03-22 10:22:34 +0100316#ifdef CONFIG_SYS_NAND_READY_PIN
317static int at91_nand_ready(struct mtd_info *mtd)
Sergey Lapin10794322008-10-31 12:28:43 +0100318{
Jean-Christophe PLAGNIOL-VILLARD74c076d2009-03-22 10:22:34 +0100319 return at91_get_gpio_value(CONFIG_SYS_NAND_READY_PIN);
Sergey Lapin10794322008-10-31 12:28:43 +0100320}
Jean-Christophe PLAGNIOL-VILLARD74c076d2009-03-22 10:22:34 +0100321#endif
Sergey Lapin10794322008-10-31 12:28:43 +0100322
Wu, Joshfe2185e2012-08-23 00:05:34 +0000323#ifndef CONFIG_SYS_NAND_BASE_LIST
324#define CONFIG_SYS_NAND_BASE_LIST { CONFIG_SYS_NAND_BASE }
Nikolay Petukhov7c27b7b2010-03-19 10:49:27 +0500325#endif
Wu, Joshfe2185e2012-08-23 00:05:34 +0000326static struct nand_chip nand_chip[CONFIG_SYS_MAX_NAND_DEVICE];
327static ulong base_addr[CONFIG_SYS_MAX_NAND_DEVICE] = CONFIG_SYS_NAND_BASE_LIST;
328
329int atmel_nand_chip_init(int devnum, ulong base_addr)
330{
331 int ret;
332 struct mtd_info *mtd = &nand_info[devnum];
333 struct nand_chip *nand = &nand_chip[devnum];
334
335 mtd->priv = nand;
336 nand->IO_ADDR_R = nand->IO_ADDR_W = (void __iomem *)base_addr;
Nikolay Petukhov7c27b7b2010-03-19 10:49:27 +0500337
Sergey Lapin10794322008-10-31 12:28:43 +0100338 nand->ecc.mode = NAND_ECC_SOFT;
339#ifdef CONFIG_SYS_NAND_DBW_16
340 nand->options = NAND_BUSWIDTH_16;
341#endif
Jean-Christophe PLAGNIOL-VILLARD74c076d2009-03-22 10:22:34 +0100342 nand->cmd_ctrl = at91_nand_hwcontrol;
343#ifdef CONFIG_SYS_NAND_READY_PIN
344 nand->dev_ready = at91_nand_ready;
345#endif
Sergey Lapin10794322008-10-31 12:28:43 +0100346 nand->chip_delay = 20;
347
Wu, Joshfe2185e2012-08-23 00:05:34 +0000348 ret = nand_scan_ident(mtd, CONFIG_SYS_NAND_MAX_CHIPS, NULL);
349 if (ret)
350 return ret;
Nikolay Petukhov7c27b7b2010-03-19 10:49:27 +0500351
352#ifdef CONFIG_ATMEL_NAND_HWECC
Wu, Joshfe2185e2012-08-23 00:05:34 +0000353 ret = atmel_hwecc_nand_init_param(nand, mtd);
354 if (ret)
355 return ret;
Nikolay Petukhov7c27b7b2010-03-19 10:49:27 +0500356#endif
357
Wu, Joshfe2185e2012-08-23 00:05:34 +0000358 ret = nand_scan_tail(mtd);
359 if (!ret)
360 nand_register(devnum);
361
362 return ret;
363}
364
365void board_nand_init(void)
366{
367 int i;
368 for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++)
369 if (atmel_nand_chip_init(i, base_addr[i]))
370 printk(KERN_ERR "atmel_nand: Fail to initialize #%d chip",
371 i);
Sergey Lapin10794322008-10-31 12:28:43 +0100372}