blob: 6b17e744a6926547e189c3d5065347f5a939b3d1 [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Sergey Lapin77e524c2008-10-31 12:28:43 +01002/*
3 * (C) Copyright 2007-2008
Stelian Pop5ee0c7f2011-11-01 00:00:39 +01004 * Stelian Pop <stelian@popies.net>
Sergey Lapin77e524c2008-10-31 12:28:43 +01005 * Lead Tech Design <www.leadtechdesign.com>
6 *
7 * (C) Copyright 2006 ATMEL Rousset, Lacressonniere Nicolas
8 *
Wu, Joshfd3091d2012-08-23 00:05:36 +00009 * Add Programmable Multibit ECC support for various AT91 SoC
10 * (C) Copyright 2012 ATMEL, Hong Xu
Sergey Lapin77e524c2008-10-31 12:28:43 +010011 */
12
13#include <common.h>
Simon Glass0f2af882020-05-10 11:40:05 -060014#include <log.h>
Andreas Bießmanna4c24d32013-11-29 12:13:45 +010015#include <asm/gpio.h>
Sergey Lapin77e524c2008-10-31 12:28:43 +010016#include <asm/arch/gpio.h>
Simon Glass9bc15642020-02-03 07:36:16 -070017#include <dm/device_compat.h>
Simon Glassd66c5f72020-02-03 07:36:15 -070018#include <dm/devres.h>
Simon Glass4dcacfc2020-05-10 11:40:13 -060019#include <linux/bitops.h>
Simon Glassc06c1be2020-05-10 11:40:08 -060020#include <linux/bug.h>
Simon Glassdbd79542020-05-10 11:40:11 -060021#include <linux/delay.h>
Simon Glassbdd5f812023-09-14 18:21:46 -060022#include <linux/printk.h>
Sergey Lapin77e524c2008-10-31 12:28:43 +010023
Wu, Josh4e87b3152013-07-03 11:11:48 +080024#include <malloc.h>
Sergey Lapin77e524c2008-10-31 12:28:43 +010025#include <nand.h>
Wu, Joshfd3091d2012-08-23 00:05:36 +000026#include <watchdog.h>
Heiko Schocherfd683382014-10-31 08:31:01 +010027#include <linux/mtd/nand_ecc.h>
Tom Rini3bde7e22021-09-22 14:50:35 -040028#include <linux/mtd/rawnand.h>
Sergey Lapin77e524c2008-10-31 12:28:43 +010029
Nikolay Petukhove6015ca2010-03-19 10:49:27 +050030#ifdef CONFIG_ATMEL_NAND_HWECC
31
32/* Register access macros */
33#define ecc_readl(add, reg) \
Andre Renaudcf44b942016-05-05 07:28:14 -060034 readl(add + ATMEL_ECC_##reg)
Nikolay Petukhove6015ca2010-03-19 10:49:27 +050035#define ecc_writel(add, reg, value) \
Andre Renaudcf44b942016-05-05 07:28:14 -060036 writel((value), add + ATMEL_ECC_##reg)
Nikolay Petukhove6015ca2010-03-19 10:49:27 +050037
38#include "atmel_nand_ecc.h" /* Hardware ECC registers */
39
Wu, Joshfd3091d2012-08-23 00:05:36 +000040#ifdef CONFIG_ATMEL_NAND_HW_PMECC
41
42struct atmel_nand_host {
43 struct pmecc_regs __iomem *pmecc;
44 struct pmecc_errloc_regs __iomem *pmerrloc;
45 void __iomem *pmecc_rom_base;
46
47 u8 pmecc_corr_cap;
48 u16 pmecc_sector_size;
49 u32 pmecc_index_table_offset;
Wu, Josh1f5c0892015-01-16 11:54:46 +080050 u32 pmecc_version;
Wu, Joshfd3091d2012-08-23 00:05:36 +000051
52 int pmecc_bytes_per_sector;
53 int pmecc_sector_number;
54 int pmecc_degree; /* Degree of remainders */
55 int pmecc_cw_len; /* Length of codeword */
56
57 /* lookup table for alpha_to and index_of */
58 void __iomem *pmecc_alpha_to;
59 void __iomem *pmecc_index_of;
60
61 /* data for pmecc computation */
Wu, Josh4e87b3152013-07-03 11:11:48 +080062 int16_t *pmecc_smu;
63 int16_t *pmecc_partial_syn;
64 int16_t *pmecc_si;
65 int16_t *pmecc_lmu; /* polynomal order */
66 int *pmecc_mu;
67 int *pmecc_dmu;
68 int *pmecc_delta;
Wu, Joshfd3091d2012-08-23 00:05:36 +000069};
70
71static struct atmel_nand_host pmecc_host;
72static struct nand_ecclayout atmel_pmecc_oobinfo;
73
74/*
75 * Return number of ecc bytes per sector according to sector size and
76 * correction capability
77 *
78 * Following table shows what at91 PMECC supported:
79 * Correction Capability Sector_512_bytes Sector_1024_bytes
80 * ===================== ================ =================
81 * 2-bits 4-bytes 4-bytes
82 * 4-bits 7-bytes 7-bytes
83 * 8-bits 13-bytes 14-bytes
84 * 12-bits 20-bytes 21-bytes
85 * 24-bits 39-bytes 42-bytes
Josh Wuce764952015-11-24 16:34:01 +080086 * 32-bits 52-bytes 56-bytes
Wu, Joshfd3091d2012-08-23 00:05:36 +000087 */
88static int pmecc_get_ecc_bytes(int cap, int sector_size)
89{
90 int m = 12 + sector_size / 512;
91 return (m * cap + 7) / 8;
92}
93
94static void pmecc_config_ecc_layout(struct nand_ecclayout *layout,
95 int oobsize, int ecc_len)
96{
97 int i;
98
99 layout->eccbytes = ecc_len;
100
101 /* ECC will occupy the last ecc_len bytes continuously */
102 for (i = 0; i < ecc_len; i++)
103 layout->eccpos[i] = oobsize - ecc_len + i;
104
105 layout->oobfree[0].offset = 2;
106 layout->oobfree[0].length =
107 oobsize - ecc_len - layout->oobfree[0].offset;
108}
109
110static void __iomem *pmecc_get_alpha_to(struct atmel_nand_host *host)
111{
112 int table_size;
113
114 table_size = host->pmecc_sector_size == 512 ?
115 PMECC_INDEX_TABLE_SIZE_512 : PMECC_INDEX_TABLE_SIZE_1024;
116
117 /* the ALPHA lookup table is right behind the INDEX lookup table. */
118 return host->pmecc_rom_base + host->pmecc_index_table_offset +
119 table_size * sizeof(int16_t);
120}
121
Wu, Josh4e87b3152013-07-03 11:11:48 +0800122static void pmecc_data_free(struct atmel_nand_host *host)
123{
124 free(host->pmecc_partial_syn);
125 free(host->pmecc_si);
126 free(host->pmecc_lmu);
127 free(host->pmecc_smu);
128 free(host->pmecc_mu);
129 free(host->pmecc_dmu);
130 free(host->pmecc_delta);
131}
132
133static int pmecc_data_alloc(struct atmel_nand_host *host)
134{
135 const int cap = host->pmecc_corr_cap;
136 int size;
137
138 size = (2 * cap + 1) * sizeof(int16_t);
139 host->pmecc_partial_syn = malloc(size);
140 host->pmecc_si = malloc(size);
141 host->pmecc_lmu = malloc((cap + 1) * sizeof(int16_t));
142 host->pmecc_smu = malloc((cap + 2) * size);
143
144 size = (cap + 1) * sizeof(int);
145 host->pmecc_mu = malloc(size);
146 host->pmecc_dmu = malloc(size);
147 host->pmecc_delta = malloc(size);
148
149 if (host->pmecc_partial_syn &&
150 host->pmecc_si &&
151 host->pmecc_lmu &&
152 host->pmecc_smu &&
153 host->pmecc_mu &&
154 host->pmecc_dmu &&
155 host->pmecc_delta)
156 return 0;
157
158 /* error happened */
159 pmecc_data_free(host);
160 return -ENOMEM;
161
162}
163
Wu, Joshfd3091d2012-08-23 00:05:36 +0000164static void pmecc_gen_syndrome(struct mtd_info *mtd, int sector)
165{
Scott Wood17fed142016-05-30 13:57:56 -0500166 struct nand_chip *nand_chip = mtd_to_nand(mtd);
167 struct atmel_nand_host *host = nand_get_controller_data(nand_chip);
Wu, Joshfd3091d2012-08-23 00:05:36 +0000168 int i;
169 uint32_t value;
170
171 /* Fill odd syndromes */
172 for (i = 0; i < host->pmecc_corr_cap; i++) {
Wu, Joshb31868f2014-06-24 18:18:06 +0800173 value = pmecc_readl(host->pmecc, rem_port[sector].rem[i / 2]);
Wu, Joshfd3091d2012-08-23 00:05:36 +0000174 if (i & 1)
175 value >>= 16;
176 value &= 0xffff;
177 host->pmecc_partial_syn[(2 * i) + 1] = (int16_t)value;
178 }
179}
180
181static void pmecc_substitute(struct mtd_info *mtd)
182{
Scott Wood17fed142016-05-30 13:57:56 -0500183 struct nand_chip *nand_chip = mtd_to_nand(mtd);
184 struct atmel_nand_host *host = nand_get_controller_data(nand_chip);
Wu, Joshfd3091d2012-08-23 00:05:36 +0000185 int16_t __iomem *alpha_to = host->pmecc_alpha_to;
186 int16_t __iomem *index_of = host->pmecc_index_of;
187 int16_t *partial_syn = host->pmecc_partial_syn;
188 const int cap = host->pmecc_corr_cap;
189 int16_t *si;
190 int i, j;
191
192 /* si[] is a table that holds the current syndrome value,
193 * an element of that table belongs to the field
194 */
195 si = host->pmecc_si;
196
197 memset(&si[1], 0, sizeof(int16_t) * (2 * cap - 1));
198
199 /* Computation 2t syndromes based on S(x) */
200 /* Odd syndromes */
201 for (i = 1; i < 2 * cap; i += 2) {
202 for (j = 0; j < host->pmecc_degree; j++) {
203 if (partial_syn[i] & (0x1 << j))
204 si[i] = readw(alpha_to + i * j) ^ si[i];
205 }
206 }
207 /* Even syndrome = (Odd syndrome) ** 2 */
208 for (i = 2, j = 1; j <= cap; i = ++j << 1) {
209 if (si[j] == 0) {
210 si[i] = 0;
211 } else {
212 int16_t tmp;
213
214 tmp = readw(index_of + si[j]);
215 tmp = (tmp * 2) % host->pmecc_cw_len;
216 si[i] = readw(alpha_to + tmp);
217 }
218 }
219}
220
221/*
222 * This function defines a Berlekamp iterative procedure for
223 * finding the value of the error location polynomial.
224 * The input is si[], initialize by pmecc_substitute().
225 * The output is smu[][].
226 *
227 * This function is written according to chip datasheet Chapter:
228 * Find the Error Location Polynomial Sigma(x) of Section:
229 * Programmable Multibit ECC Control (PMECC).
230 */
231static void pmecc_get_sigma(struct mtd_info *mtd)
232{
Scott Wood17fed142016-05-30 13:57:56 -0500233 struct nand_chip *nand_chip = mtd_to_nand(mtd);
234 struct atmel_nand_host *host = nand_get_controller_data(nand_chip);
Wu, Joshfd3091d2012-08-23 00:05:36 +0000235
236 int16_t *lmu = host->pmecc_lmu;
237 int16_t *si = host->pmecc_si;
238 int *mu = host->pmecc_mu;
239 int *dmu = host->pmecc_dmu; /* Discrepancy */
240 int *delta = host->pmecc_delta; /* Delta order */
241 int cw_len = host->pmecc_cw_len;
242 const int16_t cap = host->pmecc_corr_cap;
243 const int num = 2 * cap + 1;
244 int16_t __iomem *index_of = host->pmecc_index_of;
245 int16_t __iomem *alpha_to = host->pmecc_alpha_to;
246 int i, j, k;
247 uint32_t dmu_0_count, tmp;
248 int16_t *smu = host->pmecc_smu;
249
250 /* index of largest delta */
251 int ro;
252 int largest;
253 int diff;
254
255 /* Init the Sigma(x) */
Bin Meng455ef432018-10-08 02:27:44 -0700256 memset(smu, 0, sizeof(int16_t) * num * (cap + 2));
Wu, Joshfd3091d2012-08-23 00:05:36 +0000257
258 dmu_0_count = 0;
259
260 /* First Row */
261
262 /* Mu */
263 mu[0] = -1;
264
265 smu[0] = 1;
266
267 /* discrepancy set to 1 */
268 dmu[0] = 1;
269 /* polynom order set to 0 */
270 lmu[0] = 0;
271 /* delta[0] = (mu[0] * 2 - lmu[0]) >> 1; */
272 delta[0] = -1;
273
274 /* Second Row */
275
276 /* Mu */
277 mu[1] = 0;
278 /* Sigma(x) set to 1 */
279 smu[num] = 1;
280
281 /* discrepancy set to S1 */
282 dmu[1] = si[1];
283
284 /* polynom order set to 0 */
285 lmu[1] = 0;
286
287 /* delta[1] = (mu[1] * 2 - lmu[1]) >> 1; */
288 delta[1] = 0;
289
290 for (i = 1; i <= cap; i++) {
291 mu[i + 1] = i << 1;
292 /* Begin Computing Sigma (Mu+1) and L(mu) */
293 /* check if discrepancy is set to 0 */
294 if (dmu[i] == 0) {
295 dmu_0_count++;
296
297 tmp = ((cap - (lmu[i] >> 1) - 1) / 2);
298 if ((cap - (lmu[i] >> 1) - 1) & 0x1)
299 tmp += 2;
300 else
301 tmp += 1;
302
303 if (dmu_0_count == tmp) {
304 for (j = 0; j <= (lmu[i] >> 1) + 1; j++)
305 smu[(cap + 1) * num + j] =
306 smu[i * num + j];
307
308 lmu[cap + 1] = lmu[i];
309 return;
310 }
311
312 /* copy polynom */
313 for (j = 0; j <= lmu[i] >> 1; j++)
314 smu[(i + 1) * num + j] = smu[i * num + j];
315
316 /* copy previous polynom order to the next */
317 lmu[i + 1] = lmu[i];
318 } else {
319 ro = 0;
320 largest = -1;
321 /* find largest delta with dmu != 0 */
322 for (j = 0; j < i; j++) {
323 if ((dmu[j]) && (delta[j] > largest)) {
324 largest = delta[j];
325 ro = j;
326 }
327 }
328
329 /* compute difference */
330 diff = (mu[i] - mu[ro]);
331
332 /* Compute degree of the new smu polynomial */
333 if ((lmu[i] >> 1) > ((lmu[ro] >> 1) + diff))
334 lmu[i + 1] = lmu[i];
335 else
336 lmu[i + 1] = ((lmu[ro] >> 1) + diff) * 2;
337
338 /* Init smu[i+1] with 0 */
339 for (k = 0; k < num; k++)
340 smu[(i + 1) * num + k] = 0;
341
342 /* Compute smu[i+1] */
343 for (k = 0; k <= lmu[ro] >> 1; k++) {
344 int16_t a, b, c;
345
346 if (!(smu[ro * num + k] && dmu[i]))
347 continue;
348 a = readw(index_of + dmu[i]);
349 b = readw(index_of + dmu[ro]);
350 c = readw(index_of + smu[ro * num + k]);
351 tmp = a + (cw_len - b) + c;
352 a = readw(alpha_to + tmp % cw_len);
353 smu[(i + 1) * num + (k + diff)] = a;
354 }
355
356 for (k = 0; k <= lmu[i] >> 1; k++)
357 smu[(i + 1) * num + k] ^= smu[i * num + k];
358 }
359
360 /* End Computing Sigma (Mu+1) and L(mu) */
361 /* In either case compute delta */
362 delta[i + 1] = (mu[i + 1] * 2 - lmu[i + 1]) >> 1;
363
364 /* Do not compute discrepancy for the last iteration */
365 if (i >= cap)
366 continue;
367
368 for (k = 0; k <= (lmu[i + 1] >> 1); k++) {
369 tmp = 2 * (i - 1);
370 if (k == 0) {
371 dmu[i + 1] = si[tmp + 3];
372 } else if (smu[(i + 1) * num + k] && si[tmp + 3 - k]) {
373 int16_t a, b, c;
374 a = readw(index_of +
375 smu[(i + 1) * num + k]);
376 b = si[2 * (i - 1) + 3 - k];
377 c = readw(index_of + b);
378 tmp = a + c;
379 tmp %= cw_len;
380 dmu[i + 1] = readw(alpha_to + tmp) ^
381 dmu[i + 1];
382 }
383 }
384 }
385}
386
387static int pmecc_err_location(struct mtd_info *mtd)
388{
Scott Wood17fed142016-05-30 13:57:56 -0500389 struct nand_chip *nand_chip = mtd_to_nand(mtd);
390 struct atmel_nand_host *host = nand_get_controller_data(nand_chip);
Wu, Joshfd3091d2012-08-23 00:05:36 +0000391 const int cap = host->pmecc_corr_cap;
392 const int num = 2 * cap + 1;
393 int sector_size = host->pmecc_sector_size;
394 int err_nbr = 0; /* number of error */
395 int roots_nbr; /* number of roots */
396 int i;
397 uint32_t val;
398 int16_t *smu = host->pmecc_smu;
399 int timeout = PMECC_MAX_TIMEOUT_US;
400
Wu, Joshb31868f2014-06-24 18:18:06 +0800401 pmecc_writel(host->pmerrloc, eldis, PMERRLOC_DISABLE);
Wu, Joshfd3091d2012-08-23 00:05:36 +0000402
403 for (i = 0; i <= host->pmecc_lmu[cap + 1] >> 1; i++) {
Wu, Joshb31868f2014-06-24 18:18:06 +0800404 pmecc_writel(host->pmerrloc, sigma[i],
405 smu[(cap + 1) * num + i]);
Wu, Joshfd3091d2012-08-23 00:05:36 +0000406 err_nbr++;
407 }
408
409 val = PMERRLOC_ELCFG_NUM_ERRORS(err_nbr - 1);
410 if (sector_size == 1024)
411 val |= PMERRLOC_ELCFG_SECTOR_1024;
412
Wu, Joshb31868f2014-06-24 18:18:06 +0800413 pmecc_writel(host->pmerrloc, elcfg, val);
414 pmecc_writel(host->pmerrloc, elen,
415 sector_size * 8 + host->pmecc_degree * cap);
Wu, Joshfd3091d2012-08-23 00:05:36 +0000416
417 while (--timeout) {
Wu, Joshb31868f2014-06-24 18:18:06 +0800418 if (pmecc_readl(host->pmerrloc, elisr) & PMERRLOC_CALC_DONE)
Wu, Joshfd3091d2012-08-23 00:05:36 +0000419 break;
Stefan Roese80877fa2022-09-02 14:10:46 +0200420 schedule();
Wu, Joshfd3091d2012-08-23 00:05:36 +0000421 udelay(1);
422 }
423
424 if (!timeout) {
Sean Andersondfff1c12020-09-15 10:44:49 -0400425 dev_err(mtd->dev,
426 "Timeout to calculate PMECC error location\n");
Wu, Joshfd3091d2012-08-23 00:05:36 +0000427 return -1;
428 }
429
Wu, Joshb31868f2014-06-24 18:18:06 +0800430 roots_nbr = (pmecc_readl(host->pmerrloc, elisr) & PMERRLOC_ERR_NUM_MASK)
Wu, Joshfd3091d2012-08-23 00:05:36 +0000431 >> 8;
432 /* Number of roots == degree of smu hence <= cap */
433 if (roots_nbr == host->pmecc_lmu[cap + 1] >> 1)
434 return err_nbr - 1;
435
436 /* Number of roots does not match the degree of smu
437 * unable to correct error */
438 return -1;
439}
440
441static void pmecc_correct_data(struct mtd_info *mtd, uint8_t *buf, uint8_t *ecc,
442 int sector_num, int extra_bytes, int err_nbr)
443{
Scott Wood17fed142016-05-30 13:57:56 -0500444 struct nand_chip *nand_chip = mtd_to_nand(mtd);
445 struct atmel_nand_host *host = nand_get_controller_data(nand_chip);
Wu, Joshfd3091d2012-08-23 00:05:36 +0000446 int i = 0;
447 int byte_pos, bit_pos, sector_size, pos;
448 uint32_t tmp;
449 uint8_t err_byte;
450
451 sector_size = host->pmecc_sector_size;
452
453 while (err_nbr) {
Wu, Joshb31868f2014-06-24 18:18:06 +0800454 tmp = pmecc_readl(host->pmerrloc, el[i]) - 1;
Wu, Joshfd3091d2012-08-23 00:05:36 +0000455 byte_pos = tmp / 8;
456 bit_pos = tmp % 8;
457
458 if (byte_pos >= (sector_size + extra_bytes))
459 BUG(); /* should never happen */
460
461 if (byte_pos < sector_size) {
462 err_byte = *(buf + byte_pos);
463 *(buf + byte_pos) ^= (1 << bit_pos);
464
465 pos = sector_num * host->pmecc_sector_size + byte_pos;
Sean Andersondfff1c12020-09-15 10:44:49 -0400466 dev_dbg(mtd->dev,
467 "Bit flip in data area, byte_pos: %d, bit_pos: %d, 0x%02x -> 0x%02x\n",
Wu, Joshfd3091d2012-08-23 00:05:36 +0000468 pos, bit_pos, err_byte, *(buf + byte_pos));
469 } else {
470 /* Bit flip in OOB area */
471 tmp = sector_num * host->pmecc_bytes_per_sector
472 + (byte_pos - sector_size);
473 err_byte = ecc[tmp];
474 ecc[tmp] ^= (1 << bit_pos);
475
476 pos = tmp + nand_chip->ecc.layout->eccpos[0];
Sean Andersondfff1c12020-09-15 10:44:49 -0400477 dev_dbg(mtd->dev,
478 "Bit flip in OOB, oob_byte_pos: %d, bit_pos: %d, 0x%02x -> 0x%02x\n",
Wu, Joshfd3091d2012-08-23 00:05:36 +0000479 pos, bit_pos, err_byte, ecc[tmp]);
480 }
481
482 i++;
483 err_nbr--;
484 }
485
486 return;
487}
488
489static int pmecc_correction(struct mtd_info *mtd, u32 pmecc_stat, uint8_t *buf,
490 u8 *ecc)
491{
Scott Wood17fed142016-05-30 13:57:56 -0500492 struct nand_chip *nand_chip = mtd_to_nand(mtd);
493 struct atmel_nand_host *host = nand_get_controller_data(nand_chip);
Kai Stuhlemmer (ebee Engineering)bd4d5992021-05-21 11:52:06 +0300494 int i, err_nbr;
495 u8 *buf_pos, *ecc_pos;
Wu, Joshfd3091d2012-08-23 00:05:36 +0000496
Wu, Joshfd3091d2012-08-23 00:05:36 +0000497 for (i = 0; i < host->pmecc_sector_number; i++) {
498 err_nbr = 0;
499 if (pmecc_stat & 0x1) {
500 buf_pos = buf + i * host->pmecc_sector_size;
501
502 pmecc_gen_syndrome(mtd, i);
503 pmecc_substitute(mtd);
504 pmecc_get_sigma(mtd);
505
506 err_nbr = pmecc_err_location(mtd);
Kai Stuhlemmer (ebee Engineering)bd4d5992021-05-21 11:52:06 +0300507 if (err_nbr >= 0) {
508 pmecc_correct_data(mtd, buf_pos, ecc, i,
509 host->pmecc_bytes_per_sector,
510 err_nbr);
511 } else if (host->pmecc_version < PMECC_VERSION_SAMA5D4) {
512 ecc_pos = ecc + i * host->pmecc_bytes_per_sector;
513
514 err_nbr = nand_check_erased_ecc_chunk(
515 buf_pos, host->pmecc_sector_size,
516 ecc_pos, host->pmecc_bytes_per_sector,
517 NULL, 0, host->pmecc_corr_cap);
518 }
519
520 if (err_nbr < 0) {
Sean Andersondfff1c12020-09-15 10:44:49 -0400521 dev_err(mtd->dev, "PMECC: Too many errors\n");
Wu, Joshfd3091d2012-08-23 00:05:36 +0000522 mtd->ecc_stats.failed++;
Scott Wood52ab7ce2016-05-30 13:57:58 -0500523 return -EBADMSG;
Wu, Joshfd3091d2012-08-23 00:05:36 +0000524 }
Kai Stuhlemmer (ebee Engineering)bd4d5992021-05-21 11:52:06 +0300525
526 mtd->ecc_stats.corrected += err_nbr;
Wu, Joshfd3091d2012-08-23 00:05:36 +0000527 }
528 pmecc_stat >>= 1;
529 }
530
531 return 0;
532}
533
534static int atmel_nand_pmecc_read_page(struct mtd_info *mtd,
Sergey Lapin3a38a552013-01-14 03:46:50 +0000535 struct nand_chip *chip, uint8_t *buf, int oob_required, int page)
Wu, Joshfd3091d2012-08-23 00:05:36 +0000536{
Scott Wood17fed142016-05-30 13:57:56 -0500537 struct atmel_nand_host *host = nand_get_controller_data(chip);
Wu, Joshfd3091d2012-08-23 00:05:36 +0000538 int eccsize = chip->ecc.size;
539 uint8_t *oob = chip->oob_poi;
540 uint32_t *eccpos = chip->ecc.layout->eccpos;
541 uint32_t stat;
542 int timeout = PMECC_MAX_TIMEOUT_US;
543
544 pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_RST);
545 pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_DISABLE);
546 pmecc_writel(host->pmecc, cfg, ((pmecc_readl(host->pmecc, cfg))
547 & ~PMECC_CFG_WRITE_OP) | PMECC_CFG_AUTO_ENABLE);
548
549 pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_ENABLE);
550 pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_DATA);
551
552 chip->read_buf(mtd, buf, eccsize);
553 chip->read_buf(mtd, oob, mtd->oobsize);
554
555 while (--timeout) {
556 if (!(pmecc_readl(host->pmecc, sr) & PMECC_SR_BUSY))
557 break;
Stefan Roese80877fa2022-09-02 14:10:46 +0200558 schedule();
Wu, Joshfd3091d2012-08-23 00:05:36 +0000559 udelay(1);
560 }
561
562 if (!timeout) {
Sean Andersondfff1c12020-09-15 10:44:49 -0400563 dev_err(mtd->dev, "Timeout to read PMECC page\n");
Wu, Joshfd3091d2012-08-23 00:05:36 +0000564 return -1;
565 }
566
567 stat = pmecc_readl(host->pmecc, isr);
568 if (stat != 0)
569 if (pmecc_correction(mtd, stat, buf, &oob[eccpos[0]]) != 0)
Scott Wood52ab7ce2016-05-30 13:57:58 -0500570 return -EBADMSG;
Wu, Joshfd3091d2012-08-23 00:05:36 +0000571
572 return 0;
573}
574
Sergey Lapin3a38a552013-01-14 03:46:50 +0000575static int atmel_nand_pmecc_write_page(struct mtd_info *mtd,
576 struct nand_chip *chip, const uint8_t *buf,
Scott Wood46e13102016-05-30 13:57:57 -0500577 int oob_required, int page)
Wu, Joshfd3091d2012-08-23 00:05:36 +0000578{
Scott Wood17fed142016-05-30 13:57:56 -0500579 struct atmel_nand_host *host = nand_get_controller_data(chip);
Wu, Joshfd3091d2012-08-23 00:05:36 +0000580 uint32_t *eccpos = chip->ecc.layout->eccpos;
581 int i, j;
582 int timeout = PMECC_MAX_TIMEOUT_US;
583
584 pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_RST);
585 pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_DISABLE);
586
587 pmecc_writel(host->pmecc, cfg, (pmecc_readl(host->pmecc, cfg) |
588 PMECC_CFG_WRITE_OP) & ~PMECC_CFG_AUTO_ENABLE);
589
590 pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_ENABLE);
591 pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_DATA);
592
593 chip->write_buf(mtd, (u8 *)buf, mtd->writesize);
594
595 while (--timeout) {
596 if (!(pmecc_readl(host->pmecc, sr) & PMECC_SR_BUSY))
597 break;
Stefan Roese80877fa2022-09-02 14:10:46 +0200598 schedule();
Wu, Joshfd3091d2012-08-23 00:05:36 +0000599 udelay(1);
600 }
601
602 if (!timeout) {
Sean Andersondfff1c12020-09-15 10:44:49 -0400603 dev_err(mtd->dev,
604 "Timeout to read PMECC status, fail to write PMECC in oob\n");
Sergey Lapin3a38a552013-01-14 03:46:50 +0000605 goto out;
Wu, Joshfd3091d2012-08-23 00:05:36 +0000606 }
607
608 for (i = 0; i < host->pmecc_sector_number; i++) {
609 for (j = 0; j < host->pmecc_bytes_per_sector; j++) {
610 int pos;
611
612 pos = i * host->pmecc_bytes_per_sector + j;
613 chip->oob_poi[eccpos[pos]] =
Wu, Joshb31868f2014-06-24 18:18:06 +0800614 pmecc_readb(host->pmecc, ecc_port[i].ecc[j]);
Wu, Joshfd3091d2012-08-23 00:05:36 +0000615 }
616 }
617 chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
Sergey Lapin3a38a552013-01-14 03:46:50 +0000618out:
619 return 0;
Wu, Joshfd3091d2012-08-23 00:05:36 +0000620}
621
622static void atmel_pmecc_core_init(struct mtd_info *mtd)
623{
Scott Wood17fed142016-05-30 13:57:56 -0500624 struct nand_chip *nand_chip = mtd_to_nand(mtd);
625 struct atmel_nand_host *host = nand_get_controller_data(nand_chip);
Wu, Joshfd3091d2012-08-23 00:05:36 +0000626 uint32_t val = 0;
627 struct nand_ecclayout *ecc_layout;
628
629 pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_RST);
630 pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_DISABLE);
631
632 switch (host->pmecc_corr_cap) {
633 case 2:
634 val = PMECC_CFG_BCH_ERR2;
635 break;
636 case 4:
637 val = PMECC_CFG_BCH_ERR4;
638 break;
639 case 8:
640 val = PMECC_CFG_BCH_ERR8;
641 break;
642 case 12:
643 val = PMECC_CFG_BCH_ERR12;
644 break;
645 case 24:
646 val = PMECC_CFG_BCH_ERR24;
647 break;
Josh Wuce764952015-11-24 16:34:01 +0800648 case 32:
649 val = PMECC_CFG_BCH_ERR32;
650 break;
Wu, Joshfd3091d2012-08-23 00:05:36 +0000651 }
652
653 if (host->pmecc_sector_size == 512)
654 val |= PMECC_CFG_SECTOR512;
655 else if (host->pmecc_sector_size == 1024)
656 val |= PMECC_CFG_SECTOR1024;
657
658 switch (host->pmecc_sector_number) {
659 case 1:
660 val |= PMECC_CFG_PAGE_1SECTOR;
661 break;
662 case 2:
663 val |= PMECC_CFG_PAGE_2SECTORS;
664 break;
665 case 4:
666 val |= PMECC_CFG_PAGE_4SECTORS;
667 break;
668 case 8:
669 val |= PMECC_CFG_PAGE_8SECTORS;
670 break;
671 }
672
673 val |= (PMECC_CFG_READ_OP | PMECC_CFG_SPARE_DISABLE
674 | PMECC_CFG_AUTO_DISABLE);
675 pmecc_writel(host->pmecc, cfg, val);
676
677 ecc_layout = nand_chip->ecc.layout;
678 pmecc_writel(host->pmecc, sarea, mtd->oobsize - 1);
679 pmecc_writel(host->pmecc, saddr, ecc_layout->eccpos[0]);
680 pmecc_writel(host->pmecc, eaddr,
681 ecc_layout->eccpos[ecc_layout->eccbytes - 1]);
682 /* See datasheet about PMECC Clock Control Register */
683 pmecc_writel(host->pmecc, clk, PMECC_CLK_133MHZ);
684 pmecc_writel(host->pmecc, idr, 0xff);
685 pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_ENABLE);
686}
687
Wu, Josh3b4c7f62013-07-04 15:36:23 +0800688#ifdef CONFIG_SYS_NAND_ONFI_DETECTION
689/*
Wu, Josh3b4c7f62013-07-04 15:36:23 +0800690 * pmecc_choose_ecc - Get ecc requirement from ONFI parameters. If
691 * pmecc_corr_cap or pmecc_sector_size is 0, then set it as
692 * ONFI ECC parameters.
693 * @host: point to an atmel_nand_host structure.
694 * if host->pmecc_corr_cap is 0 then set it as the ONFI ecc_bits.
695 * if host->pmecc_sector_size is 0 then set it as the ONFI sector_size.
696 * @chip: point to an nand_chip structure.
697 * @cap: store the ONFI ECC correct bits capbility
698 * @sector_size: in how many bytes that ONFI require to correct @ecc_bits
699 *
700 * Return 0 if success. otherwise return the error code.
701 */
702static int pmecc_choose_ecc(struct atmel_nand_host *host,
703 struct nand_chip *chip,
704 int *cap, int *sector_size)
705{
706 /* Get ECC requirement from ONFI parameters */
707 *cap = *sector_size = 0;
708 if (chip->onfi_version) {
Josh Wuc90cc682016-01-25 14:06:33 +0800709 *cap = chip->ecc_strength_ds;
710 *sector_size = chip->ecc_step_ds;
Masahiro Yamadaf8a5d512017-10-18 00:10:48 +0900711 pr_debug("ONFI params, minimum required ECC: %d bits in %d bytes\n",
Josh Wuc90cc682016-01-25 14:06:33 +0800712 *cap, *sector_size);
Wu, Josh3b4c7f62013-07-04 15:36:23 +0800713 }
Josh Wuc90cc682016-01-25 14:06:33 +0800714
Wu, Josh3b4c7f62013-07-04 15:36:23 +0800715 if (*cap == 0 && *sector_size == 0) {
Josh Wuc90cc682016-01-25 14:06:33 +0800716 /* Non-ONFI compliant */
Sean Andersondfff1c12020-09-15 10:44:49 -0400717 dev_info(chip->mtd.dev,
718 "NAND chip is not ONFI compliant, assume ecc_bits is 2 in 512 bytes\n");
Wu, Josh3b4c7f62013-07-04 15:36:23 +0800719 *cap = 2;
720 *sector_size = 512;
721 }
722
723 /* If head file doesn't specify then use the one in ONFI parameters */
724 if (host->pmecc_corr_cap == 0) {
725 /* use the most fitable ecc bits (the near bigger one ) */
726 if (*cap <= 2)
727 host->pmecc_corr_cap = 2;
728 else if (*cap <= 4)
729 host->pmecc_corr_cap = 4;
730 else if (*cap <= 8)
731 host->pmecc_corr_cap = 8;
732 else if (*cap <= 12)
733 host->pmecc_corr_cap = 12;
734 else if (*cap <= 24)
735 host->pmecc_corr_cap = 24;
736 else
Josh Wuce764952015-11-24 16:34:01 +0800737#ifdef CONFIG_SAMA5D2
738 host->pmecc_corr_cap = 32;
739#else
740 host->pmecc_corr_cap = 24;
741#endif
Wu, Josh3b4c7f62013-07-04 15:36:23 +0800742 }
743 if (host->pmecc_sector_size == 0) {
744 /* use the most fitable sector size (the near smaller one ) */
745 if (*sector_size >= 1024)
746 host->pmecc_sector_size = 1024;
747 else if (*sector_size >= 512)
748 host->pmecc_sector_size = 512;
749 else
750 return -EINVAL;
751 }
752 return 0;
753}
754#endif
755
Josh Wuf259ad22014-11-10 15:24:00 +0800756#if defined(NO_GALOIS_TABLE_IN_ROM)
757static uint16_t *pmecc_galois_table;
758static inline int deg(unsigned int poly)
759{
760 /* polynomial degree is the most-significant bit index */
761 return fls(poly) - 1;
762}
763
764static int build_gf_tables(int mm, unsigned int poly,
765 int16_t *index_of, int16_t *alpha_to)
766{
767 unsigned int i, x = 1;
768 const unsigned int k = 1 << deg(poly);
769 unsigned int nn = (1 << mm) - 1;
770
771 /* primitive polynomial must be of degree m */
772 if (k != (1u << mm))
773 return -EINVAL;
774
775 for (i = 0; i < nn; i++) {
776 alpha_to[i] = x;
777 index_of[x] = i;
778 if (i && (x == 1))
779 /* polynomial is not primitive (a^i=1 with 0<i<2^m-1) */
780 return -EINVAL;
781 x <<= 1;
782 if (x & k)
783 x ^= poly;
784 }
785
786 alpha_to[nn] = 1;
787 index_of[0] = 0;
788
789 return 0;
790}
791
792static uint16_t *create_lookup_table(int sector_size)
793{
794 int degree = (sector_size == 512) ?
795 PMECC_GF_DIMENSION_13 :
796 PMECC_GF_DIMENSION_14;
797 unsigned int poly = (sector_size == 512) ?
798 PMECC_GF_13_PRIMITIVE_POLY :
799 PMECC_GF_14_PRIMITIVE_POLY;
800 int table_size = (sector_size == 512) ?
801 PMECC_INDEX_TABLE_SIZE_512 :
802 PMECC_INDEX_TABLE_SIZE_1024;
803
804 int16_t *addr = kzalloc(2 * table_size * sizeof(uint16_t), GFP_KERNEL);
805 if (addr && build_gf_tables(degree, poly, addr, addr + table_size))
806 return NULL;
807
808 return (uint16_t *)addr;
809}
810#endif
811
Wu, Joshfd3091d2012-08-23 00:05:36 +0000812static int atmel_pmecc_nand_init_params(struct nand_chip *nand,
813 struct mtd_info *mtd)
814{
815 struct atmel_nand_host *host;
816 int cap, sector_size;
817
Scott Wood17fed142016-05-30 13:57:56 -0500818 host = &pmecc_host;
819 nand_set_controller_data(nand, host);
Wu, Joshfd3091d2012-08-23 00:05:36 +0000820
821 nand->ecc.mode = NAND_ECC_HW;
822 nand->ecc.calculate = NULL;
823 nand->ecc.correct = NULL;
824 nand->ecc.hwctl = NULL;
825
Wu, Josh3b4c7f62013-07-04 15:36:23 +0800826#ifdef CONFIG_SYS_NAND_ONFI_DETECTION
827 host->pmecc_corr_cap = host->pmecc_sector_size = 0;
828
829#ifdef CONFIG_PMECC_CAP
830 host->pmecc_corr_cap = CONFIG_PMECC_CAP;
831#endif
832#ifdef CONFIG_PMECC_SECTOR_SIZE
833 host->pmecc_sector_size = CONFIG_PMECC_SECTOR_SIZE;
834#endif
835 /* Get ECC requirement of ONFI parameters. And if CONFIG_PMECC_CAP or
836 * CONFIG_PMECC_SECTOR_SIZE not defined, then use ecc_bits, sector_size
837 * from ONFI.
838 */
839 if (pmecc_choose_ecc(host, nand, &cap, &sector_size)) {
Sean Andersondfff1c12020-09-15 10:44:49 -0400840 dev_err(mtd->dev,
841 "Required ECC %d bits in %d bytes not supported!\n",
Josh Wudaf40882016-01-25 14:06:34 +0800842 cap, sector_size);
Wu, Josh3b4c7f62013-07-04 15:36:23 +0800843 return -EINVAL;
844 }
845
846 if (cap > host->pmecc_corr_cap)
Sean Andersondfff1c12020-09-15 10:44:49 -0400847 dev_info(mtd->dev,
848 "WARNING: Using different ecc correct bits(%d bit) from Nand ONFI ECC reqirement (%d bit).\n",
849 host->pmecc_corr_cap, cap);
Wu, Josh3b4c7f62013-07-04 15:36:23 +0800850 if (sector_size < host->pmecc_sector_size)
Sean Andersondfff1c12020-09-15 10:44:49 -0400851 dev_info(mtd->dev,
852 "WARNING: Using different ecc correct sector size (%d bytes) from Nand ONFI ECC reqirement (%d bytes).\n",
853 host->pmecc_sector_size, sector_size);
Wu, Josh3b4c7f62013-07-04 15:36:23 +0800854#else /* CONFIG_SYS_NAND_ONFI_DETECTION */
855 host->pmecc_corr_cap = CONFIG_PMECC_CAP;
856 host->pmecc_sector_size = CONFIG_PMECC_SECTOR_SIZE;
857#endif
858
859 cap = host->pmecc_corr_cap;
860 sector_size = host->pmecc_sector_size;
861
862 /* TODO: need check whether cap & sector_size is validate */
Josh Wuf259ad22014-11-10 15:24:00 +0800863#if defined(NO_GALOIS_TABLE_IN_ROM)
864 /*
865 * As pmecc_rom_base is the begin of the gallois field table, So the
866 * index offset just set as 0.
867 */
868 host->pmecc_index_table_offset = 0;
869#else
Wu, Joshb45c9492013-07-03 11:11:45 +0800870 if (host->pmecc_sector_size == 512)
871 host->pmecc_index_table_offset = ATMEL_PMECC_INDEX_OFFSET_512;
872 else
873 host->pmecc_index_table_offset = ATMEL_PMECC_INDEX_OFFSET_1024;
Josh Wuf259ad22014-11-10 15:24:00 +0800874#endif
Wu, Joshfd3091d2012-08-23 00:05:36 +0000875
Masahiro Yamadaf8a5d512017-10-18 00:10:48 +0900876 pr_debug("Initialize PMECC params, cap: %d, sector: %d\n",
877 cap, sector_size);
Wu, Joshfd3091d2012-08-23 00:05:36 +0000878
879 host->pmecc = (struct pmecc_regs __iomem *) ATMEL_BASE_PMECC;
880 host->pmerrloc = (struct pmecc_errloc_regs __iomem *)
881 ATMEL_BASE_PMERRLOC;
Josh Wuf259ad22014-11-10 15:24:00 +0800882#if defined(NO_GALOIS_TABLE_IN_ROM)
883 pmecc_galois_table = create_lookup_table(host->pmecc_sector_size);
884 if (!pmecc_galois_table) {
Sean Andersondfff1c12020-09-15 10:44:49 -0400885 dev_err(mtd->dev, "out of memory\n");
Josh Wuf259ad22014-11-10 15:24:00 +0800886 return -ENOMEM;
887 }
888
889 host->pmecc_rom_base = (void __iomem *)pmecc_galois_table;
890#else
Wu, Joshfd3091d2012-08-23 00:05:36 +0000891 host->pmecc_rom_base = (void __iomem *) ATMEL_BASE_ROM;
Josh Wuf259ad22014-11-10 15:24:00 +0800892#endif
Wu, Joshfd3091d2012-08-23 00:05:36 +0000893
894 /* ECC is calculated for the whole page (1 step) */
895 nand->ecc.size = mtd->writesize;
896
897 /* set ECC page size and oob layout */
898 switch (mtd->writesize) {
899 case 2048:
900 case 4096:
Wu, Joshf9f69b12013-10-18 17:46:31 +0800901 case 8192:
Wu, Josh89bdc8e2013-08-23 15:09:05 +0800902 host->pmecc_degree = (sector_size == 512) ?
903 PMECC_GF_DIMENSION_13 : PMECC_GF_DIMENSION_14;
Wu, Joshfd3091d2012-08-23 00:05:36 +0000904 host->pmecc_cw_len = (1 << host->pmecc_degree) - 1;
905 host->pmecc_sector_number = mtd->writesize / sector_size;
906 host->pmecc_bytes_per_sector = pmecc_get_ecc_bytes(
907 cap, sector_size);
908 host->pmecc_alpha_to = pmecc_get_alpha_to(host);
909 host->pmecc_index_of = host->pmecc_rom_base +
910 host->pmecc_index_table_offset;
911
912 nand->ecc.steps = 1;
913 nand->ecc.bytes = host->pmecc_bytes_per_sector *
914 host->pmecc_sector_number;
Wu, Joshf9f69b12013-10-18 17:46:31 +0800915
916 if (nand->ecc.bytes > MTD_MAX_ECCPOS_ENTRIES_LARGE) {
Sean Andersondfff1c12020-09-15 10:44:49 -0400917 dev_err(mtd->dev,
918 "too large eccpos entries. max support ecc.bytes is %d\n",
919 MTD_MAX_ECCPOS_ENTRIES_LARGE);
Wu, Joshf9f69b12013-10-18 17:46:31 +0800920 return -EINVAL;
921 }
922
Josh Wu5d3256c2016-01-25 14:06:35 +0800923 if (nand->ecc.bytes > mtd->oobsize - PMECC_OOB_RESERVED_BYTES) {
Sean Andersondfff1c12020-09-15 10:44:49 -0400924 dev_err(mtd->dev, "No room for ECC bytes\n");
Wu, Joshfd3091d2012-08-23 00:05:36 +0000925 return -EINVAL;
926 }
927 pmecc_config_ecc_layout(&atmel_pmecc_oobinfo,
928 mtd->oobsize,
929 nand->ecc.bytes);
930 nand->ecc.layout = &atmel_pmecc_oobinfo;
931 break;
932 case 512:
933 case 1024:
934 /* TODO */
Sean Andersondfff1c12020-09-15 10:44:49 -0400935 dev_err(mtd->dev,
936 "Unsupported page size for PMECC, use Software ECC\n");
Wu, Joshfd3091d2012-08-23 00:05:36 +0000937 default:
938 /* page size not handled by HW ECC */
939 /* switching back to soft ECC */
940 nand->ecc.mode = NAND_ECC_SOFT;
941 nand->ecc.read_page = NULL;
942 nand->ecc.postpad = 0;
943 nand->ecc.prepad = 0;
944 nand->ecc.bytes = 0;
945 return 0;
946 }
947
Wu, Josh4e87b3152013-07-03 11:11:48 +0800948 /* Allocate data for PMECC computation */
949 if (pmecc_data_alloc(host)) {
Sean Andersondfff1c12020-09-15 10:44:49 -0400950 dev_err(mtd->dev,
951 "Cannot allocate memory for PMECC computation!\n");
Wu, Josh4e87b3152013-07-03 11:11:48 +0800952 return -ENOMEM;
953 }
954
Boris BREZILLONd7915f42014-09-02 10:23:09 +0200955 nand->options |= NAND_NO_SUBPAGE_WRITE;
Wu, Joshfd3091d2012-08-23 00:05:36 +0000956 nand->ecc.read_page = atmel_nand_pmecc_read_page;
957 nand->ecc.write_page = atmel_nand_pmecc_write_page;
Sergey Lapin3a38a552013-01-14 03:46:50 +0000958 nand->ecc.strength = cap;
Wu, Joshfd3091d2012-08-23 00:05:36 +0000959
Wu, Josh1f5c0892015-01-16 11:54:46 +0800960 /* Check the PMECC ip version */
961 host->pmecc_version = pmecc_readl(host->pmerrloc, version);
Sean Andersondfff1c12020-09-15 10:44:49 -0400962 dev_dbg(mtd->dev, "PMECC IP version is: %x\n", host->pmecc_version);
Wu, Josh1f5c0892015-01-16 11:54:46 +0800963
Wu, Joshfd3091d2012-08-23 00:05:36 +0000964 atmel_pmecc_core_init(mtd);
965
966 return 0;
967}
968
969#else
970
Nikolay Petukhove6015ca2010-03-19 10:49:27 +0500971/* oob layout for large page size
972 * bad block info is on bytes 0 and 1
973 * the bytes have to be consecutives to avoid
974 * several NAND_CMD_RNDOUT during read
975 */
976static struct nand_ecclayout atmel_oobinfo_large = {
977 .eccbytes = 4,
978 .eccpos = {60, 61, 62, 63},
979 .oobfree = {
980 {2, 58}
981 },
982};
983
984/* oob layout for small page size
985 * bad block info is on bytes 4 and 5
986 * the bytes have to be consecutives to avoid
987 * several NAND_CMD_RNDOUT during read
988 */
989static struct nand_ecclayout atmel_oobinfo_small = {
990 .eccbytes = 4,
991 .eccpos = {0, 1, 2, 3},
992 .oobfree = {
993 {6, 10}
994 },
995};
996
997/*
998 * Calculate HW ECC
999 *
1000 * function called after a write
1001 *
1002 * mtd: MTD block structure
1003 * dat: raw data (unused)
1004 * ecc_code: buffer for ECC
1005 */
1006static int atmel_nand_calculate(struct mtd_info *mtd,
1007 const u_char *dat, unsigned char *ecc_code)
1008{
Nikolay Petukhove6015ca2010-03-19 10:49:27 +05001009 unsigned int ecc_value;
1010
1011 /* get the first 2 ECC bytes */
Tom Riniddaaf072022-11-12 17:36:46 -05001012 ecc_value = ecc_readl(ATMEL_BASE_ECC, PR);
Nikolay Petukhove6015ca2010-03-19 10:49:27 +05001013
1014 ecc_code[0] = ecc_value & 0xFF;
1015 ecc_code[1] = (ecc_value >> 8) & 0xFF;
1016
1017 /* get the last 2 ECC bytes */
Tom Riniddaaf072022-11-12 17:36:46 -05001018 ecc_value = ecc_readl(ATMEL_BASE_ECC, NPR) & ATMEL_ECC_NPARITY;
Nikolay Petukhove6015ca2010-03-19 10:49:27 +05001019
1020 ecc_code[2] = ecc_value & 0xFF;
1021 ecc_code[3] = (ecc_value >> 8) & 0xFF;
1022
1023 return 0;
1024}
1025
1026/*
1027 * HW ECC read page function
1028 *
1029 * mtd: mtd info structure
1030 * chip: nand chip info structure
1031 * buf: buffer to store read data
Sergey Lapin3a38a552013-01-14 03:46:50 +00001032 * oob_required: caller expects OOB data read to chip->oob_poi
Nikolay Petukhove6015ca2010-03-19 10:49:27 +05001033 */
Sergey Lapin3a38a552013-01-14 03:46:50 +00001034static int atmel_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip,
1035 uint8_t *buf, int oob_required, int page)
Nikolay Petukhove6015ca2010-03-19 10:49:27 +05001036{
1037 int eccsize = chip->ecc.size;
1038 int eccbytes = chip->ecc.bytes;
1039 uint32_t *eccpos = chip->ecc.layout->eccpos;
1040 uint8_t *p = buf;
1041 uint8_t *oob = chip->oob_poi;
1042 uint8_t *ecc_pos;
1043 int stat;
1044
1045 /* read the page */
1046 chip->read_buf(mtd, p, eccsize);
1047
1048 /* move to ECC position if needed */
1049 if (eccpos[0] != 0) {
1050 /* This only works on large pages
1051 * because the ECC controller waits for
1052 * NAND_CMD_RNDOUTSTART after the
1053 * NAND_CMD_RNDOUT.
1054 * anyway, for small pages, the eccpos[0] == 0
1055 */
1056 chip->cmdfunc(mtd, NAND_CMD_RNDOUT,
1057 mtd->writesize + eccpos[0], -1);
1058 }
1059
1060 /* the ECC controller needs to read the ECC just after the data */
1061 ecc_pos = oob + eccpos[0];
1062 chip->read_buf(mtd, ecc_pos, eccbytes);
1063
1064 /* check if there's an error */
1065 stat = chip->ecc.correct(mtd, p, oob, NULL);
1066
1067 if (stat < 0)
1068 mtd->ecc_stats.failed++;
1069 else
1070 mtd->ecc_stats.corrected += stat;
1071
1072 /* get back to oob start (end of page) */
1073 chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize, -1);
1074
1075 /* read the oob */
1076 chip->read_buf(mtd, oob, mtd->oobsize);
1077
1078 return 0;
1079}
1080
1081/*
1082 * HW ECC Correction
1083 *
1084 * function called after a read
1085 *
1086 * mtd: MTD block structure
1087 * dat: raw data read from the chip
1088 * read_ecc: ECC from the chip (unused)
1089 * isnull: unused
1090 *
1091 * Detect and correct a 1 bit error for a page
1092 */
1093static int atmel_nand_correct(struct mtd_info *mtd, u_char *dat,
1094 u_char *read_ecc, u_char *isnull)
1095{
Scott Wood17fed142016-05-30 13:57:56 -05001096 struct nand_chip *nand_chip = mtd_to_nand(mtd);
Wu, Josh1586d1b2012-08-23 00:05:35 +00001097 unsigned int ecc_status;
Nikolay Petukhove6015ca2010-03-19 10:49:27 +05001098 unsigned int ecc_word, ecc_bit;
1099
1100 /* get the status from the Status Register */
Tom Riniddaaf072022-11-12 17:36:46 -05001101 ecc_status = ecc_readl(ATMEL_BASE_ECC, SR);
Nikolay Petukhove6015ca2010-03-19 10:49:27 +05001102
1103 /* if there's no error */
1104 if (likely(!(ecc_status & ATMEL_ECC_RECERR)))
1105 return 0;
1106
1107 /* get error bit offset (4 bits) */
Tom Riniddaaf072022-11-12 17:36:46 -05001108 ecc_bit = ecc_readl(ATMEL_BASE_ECC, PR) & ATMEL_ECC_BITADDR;
Nikolay Petukhove6015ca2010-03-19 10:49:27 +05001109 /* get word address (12 bits) */
Tom Riniddaaf072022-11-12 17:36:46 -05001110 ecc_word = ecc_readl(ATMEL_BASE_ECC, PR) & ATMEL_ECC_WORDADDR;
Nikolay Petukhove6015ca2010-03-19 10:49:27 +05001111 ecc_word >>= 4;
1112
1113 /* if there are multiple errors */
1114 if (ecc_status & ATMEL_ECC_MULERR) {
1115 /* check if it is a freshly erased block
1116 * (filled with 0xff) */
1117 if ((ecc_bit == ATMEL_ECC_BITADDR)
1118 && (ecc_word == (ATMEL_ECC_WORDADDR >> 4))) {
1119 /* the block has just been erased, return OK */
1120 return 0;
1121 }
1122 /* it doesn't seems to be a freshly
1123 * erased block.
1124 * We can't correct so many errors */
Sean Andersondfff1c12020-09-15 10:44:49 -04001125 dev_warn(mtd->dev,
1126 "multiple errors detected. Unable to correct.\n");
Scott Wood52ab7ce2016-05-30 13:57:58 -05001127 return -EBADMSG;
Nikolay Petukhove6015ca2010-03-19 10:49:27 +05001128 }
1129
1130 /* if there's a single bit error : we can correct it */
1131 if (ecc_status & ATMEL_ECC_ECCERR) {
1132 /* there's nothing much to do here.
1133 * the bit error is on the ECC itself.
1134 */
Sean Andersondfff1c12020-09-15 10:44:49 -04001135 dev_warn(mtd->dev,
1136 "one bit error on ECC code. Nothing to correct\n");
Nikolay Petukhove6015ca2010-03-19 10:49:27 +05001137 return 0;
1138 }
1139
Sean Andersondfff1c12020-09-15 10:44:49 -04001140 dev_warn(mtd->dev,
1141 "one bit error on data. (word offset in the page : 0x%x bit offset : 0x%x)\n",
1142 ecc_word, ecc_bit);
Nikolay Petukhove6015ca2010-03-19 10:49:27 +05001143 /* correct the error */
1144 if (nand_chip->options & NAND_BUSWIDTH_16) {
1145 /* 16 bits words */
1146 ((unsigned short *) dat)[ecc_word] ^= (1 << ecc_bit);
1147 } else {
1148 /* 8 bits words */
1149 dat[ecc_word] ^= (1 << ecc_bit);
1150 }
Sean Andersondfff1c12020-09-15 10:44:49 -04001151 dev_warn(mtd->dev, "error corrected\n");
Nikolay Petukhove6015ca2010-03-19 10:49:27 +05001152 return 1;
1153}
1154
1155/*
1156 * Enable HW ECC : unused on most chips
1157 */
1158static void atmel_nand_hwctl(struct mtd_info *mtd, int mode)
1159{
1160}
Wu, Josh6cded6d2012-08-23 00:05:34 +00001161
1162int atmel_hwecc_nand_init_param(struct nand_chip *nand, struct mtd_info *mtd)
1163{
1164 nand->ecc.mode = NAND_ECC_HW;
1165 nand->ecc.calculate = atmel_nand_calculate;
1166 nand->ecc.correct = atmel_nand_correct;
1167 nand->ecc.hwctl = atmel_nand_hwctl;
1168 nand->ecc.read_page = atmel_nand_read_page;
1169 nand->ecc.bytes = 4;
Andre Renaudeaf23212016-05-05 07:28:15 -06001170 nand->ecc.strength = 4;
Wu, Josh6cded6d2012-08-23 00:05:34 +00001171
1172 if (nand->ecc.mode == NAND_ECC_HW) {
1173 /* ECC is calculated for the whole page (1 step) */
1174 nand->ecc.size = mtd->writesize;
1175
1176 /* set ECC page size and oob layout */
1177 switch (mtd->writesize) {
1178 case 512:
1179 nand->ecc.layout = &atmel_oobinfo_small;
Tom Riniddaaf072022-11-12 17:36:46 -05001180 ecc_writel(ATMEL_BASE_ECC, MR,
Wu, Josh6cded6d2012-08-23 00:05:34 +00001181 ATMEL_ECC_PAGESIZE_528);
1182 break;
1183 case 1024:
1184 nand->ecc.layout = &atmel_oobinfo_large;
Tom Riniddaaf072022-11-12 17:36:46 -05001185 ecc_writel(ATMEL_BASE_ECC, MR,
Wu, Josh6cded6d2012-08-23 00:05:34 +00001186 ATMEL_ECC_PAGESIZE_1056);
1187 break;
1188 case 2048:
1189 nand->ecc.layout = &atmel_oobinfo_large;
Tom Riniddaaf072022-11-12 17:36:46 -05001190 ecc_writel(ATMEL_BASE_ECC, MR,
Wu, Josh6cded6d2012-08-23 00:05:34 +00001191 ATMEL_ECC_PAGESIZE_2112);
1192 break;
1193 case 4096:
1194 nand->ecc.layout = &atmel_oobinfo_large;
Tom Riniddaaf072022-11-12 17:36:46 -05001195 ecc_writel(ATMEL_BASE_ECC, MR,
Wu, Josh6cded6d2012-08-23 00:05:34 +00001196 ATMEL_ECC_PAGESIZE_4224);
1197 break;
1198 default:
1199 /* page size not handled by HW ECC */
1200 /* switching back to soft ECC */
1201 nand->ecc.mode = NAND_ECC_SOFT;
1202 nand->ecc.calculate = NULL;
1203 nand->ecc.correct = NULL;
1204 nand->ecc.hwctl = NULL;
1205 nand->ecc.read_page = NULL;
1206 nand->ecc.postpad = 0;
1207 nand->ecc.prepad = 0;
1208 nand->ecc.bytes = 0;
1209 break;
1210 }
1211 }
1212
1213 return 0;
1214}
1215
Wu, Joshfd3091d2012-08-23 00:05:36 +00001216#endif /* CONFIG_ATMEL_NAND_HW_PMECC */
1217
1218#endif /* CONFIG_ATMEL_NAND_HWECC */
Nikolay Petukhove6015ca2010-03-19 10:49:27 +05001219
Jean-Christophe PLAGNIOL-VILLARDc9539ba2009-03-22 10:22:34 +01001220static void at91_nand_hwcontrol(struct mtd_info *mtd,
Sergey Lapin77e524c2008-10-31 12:28:43 +01001221 int cmd, unsigned int ctrl)
1222{
Scott Wood17fed142016-05-30 13:57:56 -05001223 struct nand_chip *this = mtd_to_nand(mtd);
Sergey Lapin77e524c2008-10-31 12:28:43 +01001224
1225 if (ctrl & NAND_CTRL_CHANGE) {
1226 ulong IO_ADDR_W = (ulong) this->IO_ADDR_W;
Tom Rinib4213492022-11-12 17:36:51 -05001227 IO_ADDR_W &= ~(CFG_SYS_NAND_MASK_ALE
1228 | CFG_SYS_NAND_MASK_CLE);
Sergey Lapin77e524c2008-10-31 12:28:43 +01001229
1230 if (ctrl & NAND_CLE)
Tom Rinib4213492022-11-12 17:36:51 -05001231 IO_ADDR_W |= CFG_SYS_NAND_MASK_CLE;
Sergey Lapin77e524c2008-10-31 12:28:43 +01001232 if (ctrl & NAND_ALE)
Tom Rinib4213492022-11-12 17:36:51 -05001233 IO_ADDR_W |= CFG_SYS_NAND_MASK_ALE;
Sergey Lapin77e524c2008-10-31 12:28:43 +01001234
Tom Rinib4213492022-11-12 17:36:51 -05001235#ifdef CFG_SYS_NAND_ENABLE_PIN
1236 at91_set_gpio_value(CFG_SYS_NAND_ENABLE_PIN,
Wenyou Yangd4aab6b2017-03-23 12:55:21 +08001237 !(ctrl & NAND_NCE));
michael4b5cef72011-03-14 21:16:38 +00001238#endif
Sergey Lapin77e524c2008-10-31 12:28:43 +01001239 this->IO_ADDR_W = (void *) IO_ADDR_W;
1240 }
1241
1242 if (cmd != NAND_CMD_NONE)
1243 writeb(cmd, this->IO_ADDR_W);
1244}
1245
Tom Rinib4213492022-11-12 17:36:51 -05001246#ifdef CFG_SYS_NAND_READY_PIN
Jean-Christophe PLAGNIOL-VILLARDc9539ba2009-03-22 10:22:34 +01001247static int at91_nand_ready(struct mtd_info *mtd)
Sergey Lapin77e524c2008-10-31 12:28:43 +01001248{
Tom Rinib4213492022-11-12 17:36:51 -05001249 return at91_get_gpio_value(CFG_SYS_NAND_READY_PIN);
Sergey Lapin77e524c2008-10-31 12:28:43 +01001250}
Jean-Christophe PLAGNIOL-VILLARDc9539ba2009-03-22 10:22:34 +01001251#endif
Sergey Lapin77e524c2008-10-31 12:28:43 +01001252
Bo Shen9415b872014-03-03 14:47:16 +08001253#ifdef CONFIG_SPL_BUILD
1254/* The following code is for SPL */
Scott Wood2c1b7e12016-05-30 13:57:55 -05001255static struct mtd_info *mtd;
Bo Shen9415b872014-03-03 14:47:16 +08001256static struct nand_chip nand_chip;
1257
1258static int nand_command(int block, int page, uint32_t offs, u8 cmd)
1259{
Scott Wood17fed142016-05-30 13:57:56 -05001260 struct nand_chip *this = mtd_to_nand(mtd);
Bo Shen9415b872014-03-03 14:47:16 +08001261 int page_addr = page + block * CONFIG_SYS_NAND_PAGE_COUNT;
1262 void (*hwctrl)(struct mtd_info *mtd, int cmd,
1263 unsigned int ctrl) = this->cmd_ctrl;
1264
Scott Wood2c1b7e12016-05-30 13:57:55 -05001265 while (!this->dev_ready(mtd))
Bo Shen9415b872014-03-03 14:47:16 +08001266 ;
1267
1268 if (cmd == NAND_CMD_READOOB) {
1269 offs += CONFIG_SYS_NAND_PAGE_SIZE;
1270 cmd = NAND_CMD_READ0;
1271 }
1272
Scott Wood2c1b7e12016-05-30 13:57:55 -05001273 hwctrl(mtd, cmd, NAND_CTRL_CLE | NAND_CTRL_CHANGE);
Bo Shen9415b872014-03-03 14:47:16 +08001274
Brian Norris67675222014-05-06 00:46:17 +05301275 if ((this->options & NAND_BUSWIDTH_16) && !nand_opcode_8bits(cmd))
Bo Shen9415b872014-03-03 14:47:16 +08001276 offs >>= 1;
1277
Scott Wood2c1b7e12016-05-30 13:57:55 -05001278 hwctrl(mtd, offs & 0xff, NAND_CTRL_ALE | NAND_CTRL_CHANGE);
1279 hwctrl(mtd, (offs >> 8) & 0xff, NAND_CTRL_ALE);
1280 hwctrl(mtd, (page_addr & 0xff), NAND_CTRL_ALE);
1281 hwctrl(mtd, ((page_addr >> 8) & 0xff), NAND_CTRL_ALE);
Bo Shen9415b872014-03-03 14:47:16 +08001282#ifdef CONFIG_SYS_NAND_5_ADDR_CYCLE
Scott Wood2c1b7e12016-05-30 13:57:55 -05001283 hwctrl(mtd, (page_addr >> 16) & 0x0f, NAND_CTRL_ALE);
Bo Shen9415b872014-03-03 14:47:16 +08001284#endif
Scott Wood2c1b7e12016-05-30 13:57:55 -05001285 hwctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
Bo Shen9415b872014-03-03 14:47:16 +08001286
Scott Wood2c1b7e12016-05-30 13:57:55 -05001287 hwctrl(mtd, NAND_CMD_READSTART, NAND_CTRL_CLE | NAND_CTRL_CHANGE);
1288 hwctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
Bo Shen9415b872014-03-03 14:47:16 +08001289
Scott Wood2c1b7e12016-05-30 13:57:55 -05001290 while (!this->dev_ready(mtd))
Bo Shen9415b872014-03-03 14:47:16 +08001291 ;
1292
1293 return 0;
1294}
1295
1296static int nand_is_bad_block(int block)
1297{
Scott Wood17fed142016-05-30 13:57:56 -05001298 struct nand_chip *this = mtd_to_nand(mtd);
Bo Shen9415b872014-03-03 14:47:16 +08001299
1300 nand_command(block, 0, CONFIG_SYS_NAND_BAD_BLOCK_POS, NAND_CMD_READOOB);
1301
1302 if (this->options & NAND_BUSWIDTH_16) {
1303 if (readw(this->IO_ADDR_R) != 0xffff)
1304 return 1;
1305 } else {
1306 if (readb(this->IO_ADDR_R) != 0xff)
1307 return 1;
1308 }
1309
1310 return 0;
1311}
1312
1313#ifdef CONFIG_SPL_NAND_ECC
Tom Rinib4213492022-11-12 17:36:51 -05001314static int nand_ecc_pos[] = CFG_SYS_NAND_ECCPOS;
Bo Shen9415b872014-03-03 14:47:16 +08001315#define ECCSTEPS (CONFIG_SYS_NAND_PAGE_SIZE / \
Tom Rinib4213492022-11-12 17:36:51 -05001316 CFG_SYS_NAND_ECCSIZE)
1317#define ECCTOTAL (ECCSTEPS * CFG_SYS_NAND_ECCBYTES)
Bo Shen9415b872014-03-03 14:47:16 +08001318
1319static int nand_read_page(int block, int page, void *dst)
1320{
Scott Wood17fed142016-05-30 13:57:56 -05001321 struct nand_chip *this = mtd_to_nand(mtd);
Bo Shen9415b872014-03-03 14:47:16 +08001322 u_char ecc_calc[ECCTOTAL];
1323 u_char ecc_code[ECCTOTAL];
1324 u_char oob_data[CONFIG_SYS_NAND_OOBSIZE];
Tom Rinib4213492022-11-12 17:36:51 -05001325 int eccsize = CFG_SYS_NAND_ECCSIZE;
1326 int eccbytes = CFG_SYS_NAND_ECCBYTES;
Bo Shen9415b872014-03-03 14:47:16 +08001327 int eccsteps = ECCSTEPS;
1328 int i;
1329 uint8_t *p = dst;
1330 nand_command(block, page, 0, NAND_CMD_READ0);
1331
1332 for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
1333 if (this->ecc.mode != NAND_ECC_SOFT)
Scott Wood2c1b7e12016-05-30 13:57:55 -05001334 this->ecc.hwctl(mtd, NAND_ECC_READ);
1335 this->read_buf(mtd, p, eccsize);
1336 this->ecc.calculate(mtd, p, &ecc_calc[i]);
Bo Shen9415b872014-03-03 14:47:16 +08001337 }
Scott Wood2c1b7e12016-05-30 13:57:55 -05001338 this->read_buf(mtd, oob_data, CONFIG_SYS_NAND_OOBSIZE);
Bo Shen9415b872014-03-03 14:47:16 +08001339
1340 for (i = 0; i < ECCTOTAL; i++)
1341 ecc_code[i] = oob_data[nand_ecc_pos[i]];
1342
1343 eccsteps = ECCSTEPS;
1344 p = dst;
1345
1346 for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)
Scott Wood2c1b7e12016-05-30 13:57:55 -05001347 this->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]);
Bo Shen9415b872014-03-03 14:47:16 +08001348
1349 return 0;
1350}
Heiko Schocher4ff0c372014-10-31 08:31:02 +01001351
1352int spl_nand_erase_one(int block, int page)
1353{
Scott Wood17fed142016-05-30 13:57:56 -05001354 struct nand_chip *this = mtd_to_nand(mtd);
Heiko Schocher4ff0c372014-10-31 08:31:02 +01001355 void (*hwctrl)(struct mtd_info *mtd, int cmd,
1356 unsigned int ctrl) = this->cmd_ctrl;
1357 int page_addr;
1358
1359 if (nand_chip.select_chip)
Scott Wood2c1b7e12016-05-30 13:57:55 -05001360 nand_chip.select_chip(mtd, 0);
Heiko Schocher4ff0c372014-10-31 08:31:02 +01001361
1362 page_addr = page + block * CONFIG_SYS_NAND_PAGE_COUNT;
Scott Wood2c1b7e12016-05-30 13:57:55 -05001363 hwctrl(mtd, NAND_CMD_ERASE1, NAND_CTRL_CLE | NAND_CTRL_CHANGE);
Heiko Schocher4ff0c372014-10-31 08:31:02 +01001364 /* Row address */
Scott Wood2c1b7e12016-05-30 13:57:55 -05001365 hwctrl(mtd, (page_addr & 0xff), NAND_CTRL_ALE | NAND_CTRL_CHANGE);
1366 hwctrl(mtd, ((page_addr >> 8) & 0xff),
Heiko Schocher4ff0c372014-10-31 08:31:02 +01001367 NAND_CTRL_ALE | NAND_CTRL_CHANGE);
1368#ifdef CONFIG_SYS_NAND_5_ADDR_CYCLE
1369 /* One more address cycle for devices > 128MiB */
Scott Wood2c1b7e12016-05-30 13:57:55 -05001370 hwctrl(mtd, (page_addr >> 16) & 0x0f,
Heiko Schocher4ff0c372014-10-31 08:31:02 +01001371 NAND_CTRL_ALE | NAND_CTRL_CHANGE);
1372#endif
Scott Wood2c1b7e12016-05-30 13:57:55 -05001373 hwctrl(mtd, NAND_CMD_ERASE2, NAND_CTRL_CLE | NAND_CTRL_CHANGE);
Heiko Schocher4ff0c372014-10-31 08:31:02 +01001374
Scott Wood2c1b7e12016-05-30 13:57:55 -05001375 while (!this->dev_ready(mtd))
Heiko Schocher4ff0c372014-10-31 08:31:02 +01001376 ;
1377
1378 nand_deselect();
1379
1380 return 0;
1381}
Bo Shen9415b872014-03-03 14:47:16 +08001382#else
1383static int nand_read_page(int block, int page, void *dst)
1384{
Scott Wood17fed142016-05-30 13:57:56 -05001385 struct nand_chip *this = mtd_to_nand(mtd);
Bo Shen9415b872014-03-03 14:47:16 +08001386
1387 nand_command(block, page, 0, NAND_CMD_READ0);
Scott Wood2c1b7e12016-05-30 13:57:55 -05001388 atmel_nand_pmecc_read_page(mtd, this, dst, 0, page);
Bo Shen9415b872014-03-03 14:47:16 +08001389
1390 return 0;
1391}
1392#endif /* CONFIG_SPL_NAND_ECC */
1393
Bo Shen9415b872014-03-03 14:47:16 +08001394int at91_nand_wait_ready(struct mtd_info *mtd)
1395{
Scott Wood17fed142016-05-30 13:57:56 -05001396 struct nand_chip *this = mtd_to_nand(mtd);
Bo Shen9415b872014-03-03 14:47:16 +08001397
1398 udelay(this->chip_delay);
1399
Heiko Schocherae2af0a2014-10-31 08:31:03 +01001400 return 1;
Bo Shen9415b872014-03-03 14:47:16 +08001401}
1402
1403int board_nand_init(struct nand_chip *nand)
1404{
1405 int ret = 0;
1406
1407 nand->ecc.mode = NAND_ECC_SOFT;
1408#ifdef CONFIG_SYS_NAND_DBW_16
1409 nand->options = NAND_BUSWIDTH_16;
1410 nand->read_buf = nand_read_buf16;
1411#else
1412 nand->read_buf = nand_read_buf;
1413#endif
1414 nand->cmd_ctrl = at91_nand_hwcontrol;
Tom Rinib4213492022-11-12 17:36:51 -05001415#ifdef CFG_SYS_NAND_READY_PIN
Bo Shen9415b872014-03-03 14:47:16 +08001416 nand->dev_ready = at91_nand_ready;
1417#else
1418 nand->dev_ready = at91_nand_wait_ready;
1419#endif
1420 nand->chip_delay = 20;
David Dueckab7ec112015-03-20 10:52:49 +01001421#ifdef CONFIG_SYS_NAND_USE_FLASH_BBT
1422 nand->bbt_options |= NAND_BBT_USE_FLASH;
1423#endif
Bo Shen9415b872014-03-03 14:47:16 +08001424
1425#ifdef CONFIG_ATMEL_NAND_HWECC
1426#ifdef CONFIG_ATMEL_NAND_HW_PMECC
Scott Wood2c1b7e12016-05-30 13:57:55 -05001427 ret = atmel_pmecc_nand_init_params(nand, mtd);
Bo Shen9415b872014-03-03 14:47:16 +08001428#endif
1429#endif
1430
1431 return ret;
1432}
1433
1434void nand_init(void)
1435{
Boris Brezillon3b5f8842016-06-15 20:56:10 +02001436 mtd = nand_to_mtd(&nand_chip);
Scott Wood2c1b7e12016-05-30 13:57:55 -05001437 mtd->writesize = CONFIG_SYS_NAND_PAGE_SIZE;
1438 mtd->oobsize = CONFIG_SYS_NAND_OOBSIZE;
Tom Rinib4213492022-11-12 17:36:51 -05001439 nand_chip.IO_ADDR_R = (void __iomem *)CFG_SYS_NAND_BASE;
1440 nand_chip.IO_ADDR_W = (void __iomem *)CFG_SYS_NAND_BASE;
Bo Shen9415b872014-03-03 14:47:16 +08001441 board_nand_init(&nand_chip);
1442
1443#ifdef CONFIG_SPL_NAND_ECC
1444 if (nand_chip.ecc.mode == NAND_ECC_SOFT) {
1445 nand_chip.ecc.calculate = nand_calculate_ecc;
1446 nand_chip.ecc.correct = nand_correct_data;
1447 }
1448#endif
1449
1450 if (nand_chip.select_chip)
Scott Wood2c1b7e12016-05-30 13:57:55 -05001451 nand_chip.select_chip(mtd, 0);
Bo Shen9415b872014-03-03 14:47:16 +08001452}
1453
1454void nand_deselect(void)
1455{
1456 if (nand_chip.select_chip)
Scott Wood2c1b7e12016-05-30 13:57:55 -05001457 nand_chip.select_chip(mtd, -1);
Bo Shen9415b872014-03-03 14:47:16 +08001458}
1459
Ladislav Michlc6a42002017-04-16 15:31:59 +02001460#include "nand_spl_loaders.c"
1461
Bo Shen9415b872014-03-03 14:47:16 +08001462#else
1463
Tom Rinib4213492022-11-12 17:36:51 -05001464#ifndef CFG_SYS_NAND_BASE_LIST
1465#define CFG_SYS_NAND_BASE_LIST { CFG_SYS_NAND_BASE }
Nikolay Petukhove6015ca2010-03-19 10:49:27 +05001466#endif
Wu, Josh6cded6d2012-08-23 00:05:34 +00001467static struct nand_chip nand_chip[CONFIG_SYS_MAX_NAND_DEVICE];
Tom Rinib4213492022-11-12 17:36:51 -05001468static ulong base_addr[CONFIG_SYS_MAX_NAND_DEVICE] = CFG_SYS_NAND_BASE_LIST;
Wu, Josh6cded6d2012-08-23 00:05:34 +00001469
1470int atmel_nand_chip_init(int devnum, ulong base_addr)
1471{
1472 int ret;
Wu, Josh6cded6d2012-08-23 00:05:34 +00001473 struct nand_chip *nand = &nand_chip[devnum];
Scott Wood17fed142016-05-30 13:57:56 -05001474 struct mtd_info *mtd = nand_to_mtd(nand);
Wu, Josh6cded6d2012-08-23 00:05:34 +00001475
Wu, Josh6cded6d2012-08-23 00:05:34 +00001476 nand->IO_ADDR_R = nand->IO_ADDR_W = (void __iomem *)base_addr;
Nikolay Petukhove6015ca2010-03-19 10:49:27 +05001477
Bo Shenbb5a12f2013-08-28 14:54:26 +00001478#ifdef CONFIG_NAND_ECC_BCH
1479 nand->ecc.mode = NAND_ECC_SOFT_BCH;
1480#else
Sergey Lapin77e524c2008-10-31 12:28:43 +01001481 nand->ecc.mode = NAND_ECC_SOFT;
Bo Shenbb5a12f2013-08-28 14:54:26 +00001482#endif
Sergey Lapin77e524c2008-10-31 12:28:43 +01001483#ifdef CONFIG_SYS_NAND_DBW_16
1484 nand->options = NAND_BUSWIDTH_16;
1485#endif
Jean-Christophe PLAGNIOL-VILLARDc9539ba2009-03-22 10:22:34 +01001486 nand->cmd_ctrl = at91_nand_hwcontrol;
Tom Rinib4213492022-11-12 17:36:51 -05001487#ifdef CFG_SYS_NAND_READY_PIN
Jean-Christophe PLAGNIOL-VILLARDc9539ba2009-03-22 10:22:34 +01001488 nand->dev_ready = at91_nand_ready;
1489#endif
Wu, Joshf9f69b12013-10-18 17:46:31 +08001490 nand->chip_delay = 75;
David Dueckab7ec112015-03-20 10:52:49 +01001491#ifdef CONFIG_SYS_NAND_USE_FLASH_BBT
1492 nand->bbt_options |= NAND_BBT_USE_FLASH;
1493#endif
Sergey Lapin77e524c2008-10-31 12:28:43 +01001494
Wu, Josh6cded6d2012-08-23 00:05:34 +00001495 ret = nand_scan_ident(mtd, CONFIG_SYS_NAND_MAX_CHIPS, NULL);
1496 if (ret)
1497 return ret;
Nikolay Petukhove6015ca2010-03-19 10:49:27 +05001498
1499#ifdef CONFIG_ATMEL_NAND_HWECC
Wu, Joshfd3091d2012-08-23 00:05:36 +00001500#ifdef CONFIG_ATMEL_NAND_HW_PMECC
1501 ret = atmel_pmecc_nand_init_params(nand, mtd);
1502#else
Wu, Josh6cded6d2012-08-23 00:05:34 +00001503 ret = atmel_hwecc_nand_init_param(nand, mtd);
Wu, Joshfd3091d2012-08-23 00:05:36 +00001504#endif
Wu, Josh6cded6d2012-08-23 00:05:34 +00001505 if (ret)
1506 return ret;
1507#endif
Nikolay Petukhove6015ca2010-03-19 10:49:27 +05001508
Wu, Josh6cded6d2012-08-23 00:05:34 +00001509 ret = nand_scan_tail(mtd);
1510 if (!ret)
Scott Wood2c1b7e12016-05-30 13:57:55 -05001511 nand_register(devnum, mtd);
Nikolay Petukhove6015ca2010-03-19 10:49:27 +05001512
Wu, Josh6cded6d2012-08-23 00:05:34 +00001513 return ret;
1514}
Nikolay Petukhove6015ca2010-03-19 10:49:27 +05001515
Wu, Josh6cded6d2012-08-23 00:05:34 +00001516void board_nand_init(void)
1517{
1518 int i;
1519 for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++)
1520 if (atmel_nand_chip_init(i, base_addr[i]))
Sean Andersondfff1c12020-09-15 10:44:49 -04001521 log_err("atmel_nand: Fail to initialize #%d chip", i);
Sergey Lapin77e524c2008-10-31 12:28:43 +01001522}
Bo Shen9415b872014-03-03 14:47:16 +08001523#endif /* CONFIG_SPL_BUILD */