blob: 51f6bd2e65b4de3655451dcd95b5a189e20463e1 [file] [log] [blame]
Balamanikandan Gunasundarfbf192f2022-10-25 16:21:02 +05301// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright 2017 ATMEL
4 * Copyright 2017 Free Electrons
5 *
6 * Author: Boris Brezillon <boris.brezillon@free-electrons.com>
7 *
8 * Derived from the atmel_nand.c driver which contained the following
9 * copyrights:
10 *
11 * Copyright 2003 Rick Bronson
12 *
13 * Derived from drivers/mtd/nand/autcpu12.c (removed in v3.8)
14 * Copyright 2001 Thomas Gleixner (gleixner@autronix.de)
15 *
16 * Derived from drivers/mtd/spia.c (removed in v3.8)
17 * Copyright 2000 Steven J. Hill (sjhill@cotw.com)
18 *
19 * Add Hardware ECC support for AT91SAM9260 / AT91SAM9263
20 * Richard Genoud (richard.genoud@gmail.com), Adeneo Copyright 2007
21 *
22 * Derived from Das U-Boot source code
23 * (u-boot-1.1.5/board/atmel/at91sam9263ek/nand.c)
24 * Copyright 2006 ATMEL Rousset, Lacressonniere Nicolas
25 *
26 * Add Programmable Multibit ECC support for various AT91 SoC
27 * Copyright 2012 ATMEL, Hong Xu
28 *
29 * Add Nand Flash Controller support for SAMA5 SoC
30 * Copyright 2013 ATMEL, Josh Wu (josh.wu@atmel.com)
31 *
32 * The PMECC is an hardware assisted BCH engine, which means part of the
33 * ECC algorithm is left to the software. The hardware/software repartition
34 * is explained in the "PMECC Controller Functional Description" chapter in
35 * Atmel datasheets, and some of the functions in this file are directly
36 * implementing the algorithms described in the "Software Implementation"
37 * sub-section.
38 *
39 * TODO: it seems that the software BCH implementation in lib/bch.c is already
40 * providing some of the logic we are implementing here. It would be smart
41 * to expose the needed lib/bch.c helpers/functions and re-use them here.
42 */
43#include <linux/iopoll.h>
44#include <linux/mtd/rawnand.h>
45#include <dm/of_access.h>
46#include <dm/ofnode.h>
47#include <asm/io.h>
48#include "pmecc.h"
49#include <dm/uclass.h>
50#include <dm/device_compat.h>
51#include <dm/devres.h>
52#include <linux/ioport.h>
53
54/* Galois field dimension */
55#define PMECC_GF_DIMENSION_13 13
56#define PMECC_GF_DIMENSION_14 14
57
58/* Primitive Polynomial used by PMECC */
59#define PMECC_GF_13_PRIMITIVE_POLY 0x201b
60#define PMECC_GF_14_PRIMITIVE_POLY 0x4443
61
62#define PMECC_LOOKUP_TABLE_SIZE_512 0x2000
63#define PMECC_LOOKUP_TABLE_SIZE_1024 0x4000
64
65/* Time out value for reading PMECC status register */
66#define PMECC_MAX_TIMEOUT_MS 100
67
68/* PMECC Register Definitions */
69#define ATMEL_PMECC_CFG 0x0
70#define PMECC_CFG_BCH_STRENGTH(x) (x)
71#define PMECC_CFG_BCH_STRENGTH_MASK GENMASK(2, 0)
72#define PMECC_CFG_SECTOR512 (0 << 4)
73#define PMECC_CFG_SECTOR1024 BIT(4)
74#define PMECC_CFG_NSECTORS(x) ((fls(x) - 1) << 8)
75#define PMECC_CFG_READ_OP (0 << 12)
76#define PMECC_CFG_WRITE_OP BIT(12)
77#define PMECC_CFG_SPARE_ENABLE BIT(16)
78#define PMECC_CFG_AUTO_ENABLE BIT(20)
79
80#define ATMEL_PMECC_SAREA 0x4
81#define ATMEL_PMECC_SADDR 0x8
82#define ATMEL_PMECC_EADDR 0xc
83
84#define ATMEL_PMECC_CLK 0x10
85#define PMECC_CLK_133MHZ (2 << 0)
86
87#define ATMEL_PMECC_CTRL 0x14
88#define PMECC_CTRL_RST BIT(0)
89#define PMECC_CTRL_DATA BIT(1)
90#define PMECC_CTRL_USER BIT(2)
91#define PMECC_CTRL_ENABLE BIT(4)
92#define PMECC_CTRL_DISABLE BIT(5)
93
94#define ATMEL_PMECC_SR 0x18
95#define PMECC_SR_BUSY BIT(0)
96#define PMECC_SR_ENABLE BIT(4)
97
98#define ATMEL_PMECC_IER 0x1c
99#define ATMEL_PMECC_IDR 0x20
100#define ATMEL_PMECC_IMR 0x24
101#define ATMEL_PMECC_ISR 0x28
102#define PMECC_ERROR_INT BIT(0)
103
104#define ATMEL_PMECC_ECC(sector, n) \
105 ((((sector) + 1) * 0x40) + (n))
106
107#define ATMEL_PMECC_REM(sector, n) \
108 ((((sector) + 1) * 0x40) + ((n) * 4) + 0x200)
109
110/* PMERRLOC Register Definitions */
111#define ATMEL_PMERRLOC_ELCFG 0x0
112#define PMERRLOC_ELCFG_SECTOR_512 (0 << 0)
113#define PMERRLOC_ELCFG_SECTOR_1024 BIT(0)
114#define PMERRLOC_ELCFG_NUM_ERRORS(n) ((n) << 16)
115
116#define ATMEL_PMERRLOC_ELPRIM 0x4
117#define ATMEL_PMERRLOC_ELEN 0x8
118#define ATMEL_PMERRLOC_ELDIS 0xc
119#define PMERRLOC_DISABLE BIT(0)
120
121#define ATMEL_PMERRLOC_ELSR 0x10
122#define PMERRLOC_ELSR_BUSY BIT(0)
123
124#define ATMEL_PMERRLOC_ELIER 0x14
125#define ATMEL_PMERRLOC_ELIDR 0x18
126#define ATMEL_PMERRLOC_ELIMR 0x1c
127#define ATMEL_PMERRLOC_ELISR 0x20
128#define PMERRLOC_ERR_NUM_MASK GENMASK(12, 8)
129#define PMERRLOC_CALC_DONE BIT(0)
130
131#define ATMEL_PMERRLOC_SIGMA(x) (((x) * 0x4) + 0x28)
132
133#define ATMEL_PMERRLOC_EL(offs, x) (((x) * 0x4) + (offs))
134
135struct atmel_pmecc_gf_tables {
136 u16 *alpha_to;
137 u16 *index_of;
138};
139
140struct atmel_pmecc_caps {
141 const int *strengths;
142 int nstrengths;
143 int el_offset;
144 bool correct_erased_chunks;
145};
146
147struct atmel_pmecc_user_conf_cache {
148 u32 cfg;
149 u32 sarea;
150 u32 saddr;
151 u32 eaddr;
152};
153
154struct atmel_pmecc_user {
155 struct atmel_pmecc_user_conf_cache cache;
156 struct atmel_pmecc *pmecc;
157 const struct atmel_pmecc_gf_tables *gf_tables;
158 int eccbytes;
159 s16 *partial_syn;
160 s16 *si;
161 s16 *lmu;
162 s16 *smu;
163 s32 *mu;
164 s32 *dmu;
165 s32 *delta;
166 u32 isr;
167};
168
169/* Serialize table access */
170DEFINE_MUTEX(pmecc_gf_tables_lock);
171static const struct atmel_pmecc_gf_tables *pmecc_gf_tables_512;
172static const struct atmel_pmecc_gf_tables *pmecc_gf_tables_1024;
173
174static inline int deg(unsigned int poly)
175{
176 /* polynomial degree is the most-significant bit index */
177 return fls(poly) - 1;
178}
179
180static int atmel_pmecc_build_gf_tables(int mm, unsigned int poly,
181 struct atmel_pmecc_gf_tables *gf_tables)
182{
183 unsigned int i, x = 1;
184 const unsigned int k = BIT(deg(poly));
185 unsigned int nn = BIT(mm) - 1;
186
187 /* primitive polynomial must be of degree m */
188 if (k != (1u << mm))
189 return -EINVAL;
190
191 for (i = 0; i < nn; i++) {
192 gf_tables->alpha_to[i] = x;
193 gf_tables->index_of[x] = i;
194 if (i && (x == 1))
195 /* polynomial is not primitive (a^i=1 with 0<i<2^m-1) */
196 return -EINVAL;
197 x <<= 1;
198 if (x & k)
199 x ^= poly;
200 }
201 gf_tables->alpha_to[nn] = 1;
202 gf_tables->index_of[0] = 0;
203
204 return 0;
205}
206
207static const struct atmel_pmecc_gf_tables *
208atmel_pmecc_create_gf_tables(const struct atmel_pmecc_user_req *req)
209{
210 struct atmel_pmecc_gf_tables *gf_tables;
211 unsigned int poly, degree, table_size;
212 int ret;
213
214 if (req->ecc.sectorsize == 512) {
215 degree = PMECC_GF_DIMENSION_13;
216 poly = PMECC_GF_13_PRIMITIVE_POLY;
217 table_size = PMECC_LOOKUP_TABLE_SIZE_512;
218 } else {
219 degree = PMECC_GF_DIMENSION_14;
220 poly = PMECC_GF_14_PRIMITIVE_POLY;
221 table_size = PMECC_LOOKUP_TABLE_SIZE_1024;
222 }
223
224 gf_tables = kzalloc(sizeof(*gf_tables) +
225 (2 * table_size * sizeof(u16)),
226 GFP_KERNEL);
227 if (!gf_tables)
228 return ERR_PTR(-ENOMEM);
229
230 gf_tables->alpha_to = (void *)(gf_tables + 1);
231 gf_tables->index_of = gf_tables->alpha_to + table_size;
232
233 ret = atmel_pmecc_build_gf_tables(degree, poly, gf_tables);
234 if (ret) {
235 kfree(gf_tables);
236 return ERR_PTR(ret);
237 }
238
239 return gf_tables;
240}
241
242static const struct atmel_pmecc_gf_tables *
243atmel_pmecc_get_gf_tables(const struct atmel_pmecc_user_req *req)
244{
245 const struct atmel_pmecc_gf_tables **gf_tables, *ret;
246
247 mutex_lock(&pmecc_gf_tables_lock);
248 if (req->ecc.sectorsize == 512)
249 gf_tables = &pmecc_gf_tables_512;
250 else
251 gf_tables = &pmecc_gf_tables_1024;
252
253 ret = *gf_tables;
254
255 if (!ret) {
256 ret = atmel_pmecc_create_gf_tables(req);
257 if (!IS_ERR(ret))
258 *gf_tables = ret;
259 }
260 mutex_unlock(&pmecc_gf_tables_lock);
261
262 return ret;
263}
264
265static int atmel_pmecc_prepare_user_req(struct atmel_pmecc *pmecc,
266 struct atmel_pmecc_user_req *req)
267{
268 int i, max_eccbytes, eccbytes = 0, eccstrength = 0;
269
270 if (req->pagesize <= 0 || req->oobsize <= 0 || req->ecc.bytes <= 0)
271 return -EINVAL;
272
273 if (req->ecc.ooboffset >= 0 &&
274 req->ecc.ooboffset + req->ecc.bytes > req->oobsize)
275 return -EINVAL;
276
277 if (req->ecc.sectorsize == ATMEL_PMECC_SECTOR_SIZE_AUTO) {
278 if (req->ecc.strength != ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH)
279 return -EINVAL;
280
281 if (req->pagesize > 512)
282 req->ecc.sectorsize = 1024;
283 else
284 req->ecc.sectorsize = 512;
285 }
286
287 if (req->ecc.sectorsize != 512 && req->ecc.sectorsize != 1024)
288 return -EINVAL;
289
290 if (req->pagesize % req->ecc.sectorsize)
291 return -EINVAL;
292
293 req->ecc.nsectors = req->pagesize / req->ecc.sectorsize;
294
295 max_eccbytes = req->ecc.bytes;
296
297 for (i = 0; i < pmecc->caps->nstrengths; i++) {
298 int nbytes, strength = pmecc->caps->strengths[i];
299
300 if (req->ecc.strength != ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH &&
301 strength < req->ecc.strength)
302 continue;
303
304 nbytes = DIV_ROUND_UP(strength * fls(8 * req->ecc.sectorsize),
305 8);
306 nbytes *= req->ecc.nsectors;
307
308 if (nbytes > max_eccbytes)
309 break;
310
311 eccstrength = strength;
312 eccbytes = nbytes;
313
314 if (req->ecc.strength != ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH)
315 break;
316 }
317
318 if (!eccstrength)
319 return -EINVAL;
320
321 req->ecc.bytes = eccbytes;
322 req->ecc.strength = eccstrength;
323
324 if (req->ecc.ooboffset < 0)
325 req->ecc.ooboffset = req->oobsize - eccbytes;
326
327 return 0;
328}
329
330struct atmel_pmecc_user *
331atmel_pmecc_create_user(struct atmel_pmecc *pmecc,
332 struct atmel_pmecc_user_req *req)
333{
334 struct atmel_pmecc_user *user;
335 const struct atmel_pmecc_gf_tables *gf_tables;
336 int strength, size, ret;
337
338 ret = atmel_pmecc_prepare_user_req(pmecc, req);
339 if (ret)
340 return ERR_PTR(ret);
341
342 size = sizeof(*user);
343 size = ALIGN(size, sizeof(u16));
344 /* Reserve space for partial_syn, si and smu */
345 size += ((2 * req->ecc.strength) + 1) * sizeof(u16) *
346 (2 + req->ecc.strength + 2);
347 /* Reserve space for lmu. */
348 size += (req->ecc.strength + 1) * sizeof(u16);
349 /* Reserve space for mu, dmu and delta. */
350 size = ALIGN(size, sizeof(s32));
351 size += (req->ecc.strength + 1) * sizeof(s32) * 3;
352
353 user = kzalloc(size, GFP_KERNEL);
354 if (!user)
355 return ERR_PTR(-ENOMEM);
356
357 user->pmecc = pmecc;
358
359 user->partial_syn = (s16 *)PTR_ALIGN(user + 1, sizeof(u16));
360 user->si = user->partial_syn + ((2 * req->ecc.strength) + 1);
361 user->lmu = user->si + ((2 * req->ecc.strength) + 1);
362 user->smu = user->lmu + (req->ecc.strength + 1);
363 user->mu = (s32 *)PTR_ALIGN(user->smu +
364 (((2 * req->ecc.strength) + 1) *
365 (req->ecc.strength + 2)),
366 sizeof(s32));
367 user->dmu = user->mu + req->ecc.strength + 1;
368 user->delta = user->dmu + req->ecc.strength + 1;
369
370 gf_tables = atmel_pmecc_get_gf_tables(req);
371 if (IS_ERR(gf_tables)) {
372 kfree(user);
373 return ERR_CAST(gf_tables);
374 }
375
376 user->gf_tables = gf_tables;
377
378 user->eccbytes = req->ecc.bytes / req->ecc.nsectors;
379
380 for (strength = 0; strength < pmecc->caps->nstrengths; strength++) {
381 if (pmecc->caps->strengths[strength] == req->ecc.strength)
382 break;
383 }
384
385 user->cache.cfg = PMECC_CFG_BCH_STRENGTH(strength) |
386 PMECC_CFG_NSECTORS(req->ecc.nsectors);
387
388 if (req->ecc.sectorsize == 1024)
389 user->cache.cfg |= PMECC_CFG_SECTOR1024;
390
391 user->cache.sarea = req->oobsize - 1;
392 user->cache.saddr = req->ecc.ooboffset;
393 user->cache.eaddr = req->ecc.ooboffset + req->ecc.bytes - 1;
394
395 return user;
396}
397EXPORT_SYMBOL_GPL(atmel_pmecc_create_user);
398
399void atmel_pmecc_destroy_user(struct atmel_pmecc_user *user)
400{
401 kfree(user);
402}
403EXPORT_SYMBOL_GPL(atmel_pmecc_destroy_user);
404
405static int get_strength(struct atmel_pmecc_user *user)
406{
407 const int *strengths = user->pmecc->caps->strengths;
408
409 return strengths[user->cache.cfg & PMECC_CFG_BCH_STRENGTH_MASK];
410}
411
412static int get_sectorsize(struct atmel_pmecc_user *user)
413{
414 return user->cache.cfg & PMECC_CFG_SECTOR1024 ? 1024 : 512;
415}
416
417static void atmel_pmecc_gen_syndrome(struct atmel_pmecc_user *user, int sector)
418{
419 int strength = get_strength(user);
420 u32 value;
421 int i;
422
423 /* Fill odd syndromes */
424 for (i = 0; i < strength; i++) {
425 value = readl_relaxed(user->pmecc->regs.base +
426 ATMEL_PMECC_REM(sector, i / 2));
427 if (i & 1)
428 value >>= 16;
429
430 user->partial_syn[(2 * i) + 1] = value;
431 }
432}
433
434static void atmel_pmecc_substitute(struct atmel_pmecc_user *user)
435{
436 int degree = get_sectorsize(user) == 512 ? 13 : 14;
437 int cw_len = BIT(degree) - 1;
438 int strength = get_strength(user);
439 s16 *alpha_to = (s16 *)user->gf_tables->alpha_to;
440 s16 *index_of = (s16 *)user->gf_tables->index_of;
441 s16 *partial_syn = user->partial_syn;
442 s16 *si;
443 int i, j;
444
445 /*
446 * si[] is a table that holds the current syndrome value,
447 * an element of that table belongs to the field
448 */
449 si = user->si;
450
451 memset(&si[1], 0, sizeof(s16) * ((2 * strength) - 1));
452
453 /* Computation 2t syndromes based on S(x) */
454 /* Odd syndromes */
455 for (i = 1; i < 2 * strength; i += 2) {
456 for (j = 0; j < degree; j++) {
457 if (partial_syn[i] & BIT(j))
458 si[i] = alpha_to[i * j] ^ si[i];
459 }
460 }
461 /* Even syndrome = (Odd syndrome) ** 2 */
462 for (i = 2, j = 1; j <= strength; i = ++j << 1) {
463 if (si[j] == 0) {
464 si[i] = 0;
465 } else {
466 s16 tmp;
467
468 tmp = index_of[si[j]];
469 tmp = (tmp * 2) % cw_len;
470 si[i] = alpha_to[tmp];
471 }
472 }
473}
474
475static void atmel_pmecc_get_sigma(struct atmel_pmecc_user *user)
476{
477 s16 *lmu = user->lmu;
478 s16 *si = user->si;
479 s32 *mu = user->mu;
480 s32 *dmu = user->dmu;
481 s32 *delta = user->delta;
482 int degree = get_sectorsize(user) == 512 ? 13 : 14;
483 int cw_len = BIT(degree) - 1;
484 int strength = get_strength(user);
485 int num = 2 * strength + 1;
486 s16 *index_of = (s16 *)user->gf_tables->index_of;
487 s16 *alpha_to = (s16 *)user->gf_tables->alpha_to;
488 int i, j, k;
489 u32 dmu_0_count, tmp;
490 s16 *smu = user->smu;
491
492 /* index of largest delta */
493 int ro;
494 int largest;
495 int diff;
496
497 dmu_0_count = 0;
498
499 /* First Row */
500
501 /* Mu */
502 mu[0] = -1;
503
504 memset(smu, 0, sizeof(s16) * num);
505 smu[0] = 1;
506
507 /* discrepancy set to 1 */
508 dmu[0] = 1;
509 /* polynom order set to 0 */
510 lmu[0] = 0;
511 delta[0] = (mu[0] * 2 - lmu[0]) >> 1;
512
513 /* Second Row */
514
515 /* Mu */
516 mu[1] = 0;
517 /* Sigma(x) set to 1 */
518 memset(&smu[num], 0, sizeof(s16) * num);
519 smu[num] = 1;
520
521 /* discrepancy set to S1 */
522 dmu[1] = si[1];
523
524 /* polynom order set to 0 */
525 lmu[1] = 0;
526
527 delta[1] = (mu[1] * 2 - lmu[1]) >> 1;
528
529 /* Init the Sigma(x) last row */
530 memset(&smu[(strength + 1) * num], 0, sizeof(s16) * num);
531
532 for (i = 1; i <= strength; i++) {
533 mu[i + 1] = i << 1;
534 /* Begin Computing Sigma (Mu+1) and L(mu) */
535 /* check if discrepancy is set to 0 */
536 if (dmu[i] == 0) {
537 dmu_0_count++;
538
539 tmp = ((strength - (lmu[i] >> 1) - 1) / 2);
540 if ((strength - (lmu[i] >> 1) - 1) & 0x1)
541 tmp += 2;
542 else
543 tmp += 1;
544
545 if (dmu_0_count == tmp) {
546 for (j = 0; j <= (lmu[i] >> 1) + 1; j++)
547 smu[(strength + 1) * num + j] =
548 smu[i * num + j];
549
550 lmu[strength + 1] = lmu[i];
551 return;
552 }
553
554 /* copy polynom */
555 for (j = 0; j <= lmu[i] >> 1; j++)
556 smu[(i + 1) * num + j] = smu[i * num + j];
557
558 /* copy previous polynom order to the next */
559 lmu[i + 1] = lmu[i];
560 } else {
561 ro = 0;
562 largest = -1;
563 /* find largest delta with dmu != 0 */
564 for (j = 0; j < i; j++) {
565 if ((dmu[j]) && (delta[j] > largest)) {
566 largest = delta[j];
567 ro = j;
568 }
569 }
570
571 /* compute difference */
572 diff = (mu[i] - mu[ro]);
573
574 /* Compute degree of the new smu polynomial */
575 if ((lmu[i] >> 1) > ((lmu[ro] >> 1) + diff))
576 lmu[i + 1] = lmu[i];
577 else
578 lmu[i + 1] = ((lmu[ro] >> 1) + diff) * 2;
579
580 /* Init smu[i+1] with 0 */
581 for (k = 0; k < num; k++)
582 smu[(i + 1) * num + k] = 0;
583
584 /* Compute smu[i+1] */
585 for (k = 0; k <= lmu[ro] >> 1; k++) {
586 s16 a, b, c;
587
588 if (!(smu[ro * num + k] && dmu[i]))
589 continue;
590
591 a = index_of[dmu[i]];
592 b = index_of[dmu[ro]];
593 c = index_of[smu[ro * num + k]];
594 tmp = a + (cw_len - b) + c;
595 a = alpha_to[tmp % cw_len];
596 smu[(i + 1) * num + (k + diff)] = a;
597 }
598
599 for (k = 0; k <= lmu[i] >> 1; k++)
600 smu[(i + 1) * num + k] ^= smu[i * num + k];
601 }
602
603 /* End Computing Sigma (Mu+1) and L(mu) */
604 /* In either case compute delta */
605 delta[i + 1] = (mu[i + 1] * 2 - lmu[i + 1]) >> 1;
606
607 /* Do not compute discrepancy for the last iteration */
608 if (i >= strength)
609 continue;
610
611 for (k = 0; k <= (lmu[i + 1] >> 1); k++) {
612 tmp = 2 * (i - 1);
613 if (k == 0) {
614 dmu[i + 1] = si[tmp + 3];
615 } else if (smu[(i + 1) * num + k] && si[tmp + 3 - k]) {
616 s16 a, b, c;
617
618 a = index_of[smu[(i + 1) * num + k]];
619 b = si[2 * (i - 1) + 3 - k];
620 c = index_of[b];
621 tmp = a + c;
622 tmp %= cw_len;
623 dmu[i + 1] = alpha_to[tmp] ^ dmu[i + 1];
624 }
625 }
626 }
627}
628
629static int atmel_pmecc_err_location(struct atmel_pmecc_user *user)
630{
631 int sector_size = get_sectorsize(user);
632 int degree = sector_size == 512 ? 13 : 14;
633 struct atmel_pmecc *pmecc = user->pmecc;
634 int strength = get_strength(user);
635 int ret, roots_nbr, i, err_nbr = 0;
636 int num = (2 * strength) + 1;
637 s16 *smu = user->smu;
638 u32 val;
639
640 writel(PMERRLOC_DISABLE, pmecc->regs.errloc + ATMEL_PMERRLOC_ELDIS);
641
642 for (i = 0; i <= user->lmu[strength + 1] >> 1; i++) {
643 writel_relaxed(smu[(strength + 1) * num + i],
644 pmecc->regs.errloc + ATMEL_PMERRLOC_SIGMA(i));
645 err_nbr++;
646 }
647
648 val = (err_nbr - 1) << 16;
649 if (sector_size == 1024)
650 val |= 1;
651
652 writel(val, pmecc->regs.errloc + ATMEL_PMERRLOC_ELCFG);
653 writel((sector_size * 8) + (degree * strength),
654 pmecc->regs.errloc + ATMEL_PMERRLOC_ELEN);
655
656 ret = readl_relaxed_poll_timeout(pmecc->regs.errloc +
657 ATMEL_PMERRLOC_ELISR,
658 val, val & PMERRLOC_CALC_DONE,
659 PMECC_MAX_TIMEOUT_MS * 1000);
660 if (ret) {
661 dev_err(pmecc->dev,
662 "PMECC: Timeout to calculate error location.\n");
663 return ret;
664 }
665
666 roots_nbr = (val & PMERRLOC_ERR_NUM_MASK) >> 8;
667 /* Number of roots == degree of smu hence <= cap */
668 if (roots_nbr == user->lmu[strength + 1] >> 1)
669 return err_nbr - 1;
670
671 /*
672 * Number of roots does not match the degree of smu
673 * unable to correct error.
674 */
675 return -EBADMSG;
676}
677
678int atmel_pmecc_correct_sector(struct atmel_pmecc_user *user, int sector,
679 void *data, void *ecc)
680{
681 struct atmel_pmecc *pmecc = user->pmecc;
682 int sectorsize = get_sectorsize(user);
683 int eccbytes = user->eccbytes;
684 int i, nerrors;
685
686 if (!(user->isr & BIT(sector)))
687 return 0;
688
689 atmel_pmecc_gen_syndrome(user, sector);
690 atmel_pmecc_substitute(user);
691 atmel_pmecc_get_sigma(user);
692
693 nerrors = atmel_pmecc_err_location(user);
694 if (nerrors < 0)
695 return nerrors;
696
697 for (i = 0; i < nerrors; i++) {
698 const char *area;
699 int byte, bit;
700 u32 errpos;
701 u8 *ptr;
702
703 errpos = readl_relaxed(pmecc->regs.errloc +
704 ATMEL_PMERRLOC_EL(pmecc->caps->el_offset, i));
705 errpos--;
706
707 byte = errpos / 8;
708 bit = errpos % 8;
709
710 if (byte < sectorsize) {
711 ptr = data + byte;
712 area = "data";
713 } else if (byte < sectorsize + eccbytes) {
714 ptr = ecc + byte - sectorsize;
715 area = "ECC";
716 } else {
717 dev_dbg(pmecc->dev,
718 "Invalid errpos value (%d, max is %d)\n",
719 errpos, (sectorsize + eccbytes) * 8);
720 return -EINVAL;
721 }
722
723 dev_dbg(pmecc->dev,
724 "Bit flip in %s area, byte %d: 0x%02x -> 0x%02x\n",
725 area, byte, *ptr, (unsigned int)(*ptr ^ BIT(bit)));
726
727 *ptr ^= BIT(bit);
728 }
729
730 return nerrors;
731}
732EXPORT_SYMBOL_GPL(atmel_pmecc_correct_sector);
733
734bool atmel_pmecc_correct_erased_chunks(struct atmel_pmecc_user *user)
735{
736 return user->pmecc->caps->correct_erased_chunks;
737}
738EXPORT_SYMBOL_GPL(atmel_pmecc_correct_erased_chunks);
739
740void atmel_pmecc_get_generated_eccbytes(struct atmel_pmecc_user *user,
741 int sector, void *ecc)
742{
743 struct atmel_pmecc *pmecc = user->pmecc;
744 u8 *ptr = ecc;
745 int i;
746
747 for (i = 0; i < user->eccbytes; i++)
748 ptr[i] = readb_relaxed(pmecc->regs.base +
749 ATMEL_PMECC_ECC(sector, i));
750}
751EXPORT_SYMBOL_GPL(atmel_pmecc_get_generated_eccbytes);
752
753void atmel_pmecc_reset(struct atmel_pmecc *pmecc)
754{
755 writel(PMECC_CTRL_RST, pmecc->regs.base + ATMEL_PMECC_CTRL);
756 writel(PMECC_CTRL_DISABLE, pmecc->regs.base + ATMEL_PMECC_CTRL);
757}
758EXPORT_SYMBOL_GPL(atmel_pmecc_reset);
759
760int atmel_pmecc_enable(struct atmel_pmecc_user *user, int op)
761{
762 struct atmel_pmecc *pmecc = user->pmecc;
763 u32 cfg;
764
765 if (op != NAND_ECC_READ && op != NAND_ECC_WRITE) {
766 dev_err(pmecc->dev, "Bad ECC operation!");
767 return -EINVAL;
768 }
769
770 mutex_lock(&user->pmecc->lock);
771
772 cfg = user->cache.cfg;
773 if (op == NAND_ECC_WRITE)
774 cfg |= PMECC_CFG_WRITE_OP;
775 else
776 cfg |= PMECC_CFG_AUTO_ENABLE;
777
778 writel(cfg, pmecc->regs.base + ATMEL_PMECC_CFG);
779 writel(user->cache.sarea, pmecc->regs.base + ATMEL_PMECC_SAREA);
780 writel(user->cache.saddr, pmecc->regs.base + ATMEL_PMECC_SADDR);
781 writel(user->cache.eaddr, pmecc->regs.base + ATMEL_PMECC_EADDR);
782
783 writel(PMECC_CTRL_ENABLE, pmecc->regs.base + ATMEL_PMECC_CTRL);
784 writel(PMECC_CTRL_DATA, pmecc->regs.base + ATMEL_PMECC_CTRL);
785
786 return 0;
787}
788EXPORT_SYMBOL_GPL(atmel_pmecc_enable);
789
790void atmel_pmecc_disable(struct atmel_pmecc_user *user)
791{
792 atmel_pmecc_reset(user->pmecc);
793 mutex_unlock(&user->pmecc->lock);
794}
795EXPORT_SYMBOL_GPL(atmel_pmecc_disable);
796
797int atmel_pmecc_wait_rdy(struct atmel_pmecc_user *user)
798{
799 struct atmel_pmecc *pmecc = user->pmecc;
800 u32 status;
801 int ret;
802
803 ret = readl_relaxed_poll_timeout(pmecc->regs.base +
804 ATMEL_PMECC_SR,
805 status, !(status & PMECC_SR_BUSY),
806 PMECC_MAX_TIMEOUT_MS * 1000);
807 if (ret) {
808 dev_err(pmecc->dev,
809 "Timeout while waiting for PMECC ready.\n");
810 return ret;
811 }
812
813 user->isr = readl_relaxed(pmecc->regs.base + ATMEL_PMECC_ISR);
814
815 return 0;
816}
817EXPORT_SYMBOL_GPL(atmel_pmecc_wait_rdy);
818
Balamanikandan Gunasundarfbf192f2022-10-25 16:21:02 +0530819static struct atmel_pmecc *
820atmel_pmecc_create(struct udevice *dev,
821 const struct atmel_pmecc_caps *caps,
822 int pmecc_res_idx, int errloc_res_idx,
823 int timing_res_idx)
824{
825 struct atmel_pmecc *pmecc;
826 struct resource res;
827
828 pmecc = devm_kzalloc(dev, sizeof(*pmecc), GFP_KERNEL);
829 if (!pmecc)
830 return ERR_PTR(-ENOMEM);
831
832 pmecc->caps = caps;
833 pmecc->dev = dev;
834 mutex_init(&pmecc->lock);
835
836 ofnode_read_resource(dev->node_, 0, &res);
837 pmecc->regs.base = (void *)res.start;
838 ofnode_read_resource(dev->node_, 1, &res);
839 pmecc->regs.errloc = (void *)res.start;
840
841 pmecc->regs.timing = 0;
842
843 /* Disable all interrupts before registering the PMECC handler. */
844 writel(0xffffffff, pmecc->regs.base + ATMEL_PMECC_IDR);
845 atmel_pmecc_reset(pmecc);
846
847 return pmecc;
848}
849
850static void devm_atmel_pmecc_put(struct udevice *dev, void *res)
851{
852}
853
854static struct atmel_pmecc *atmel_pmecc_get_by_node(struct udevice *dev)
855{
856 struct atmel_pmecc *pmecc, **ptr;
857 int ret;
858
859 pmecc = dev_get_plat(dev);
860 if (!pmecc) {
861 ret = -EPROBE_DEFER;
862 goto err_put_device;
863 }
864
865 ptr = devres_alloc(devm_atmel_pmecc_put, sizeof(*ptr), GFP_KERNEL);
866 if (!ptr) {
867 ret = -ENOMEM;
868 goto err_put_device;
869 }
870
871 *ptr = pmecc;
872
873 devres_add(dev, ptr);
874
875 return pmecc;
876
877err_put_device:
878 return ERR_PTR(ret);
879}
880
881static const int atmel_pmecc_strengths[] = { 2, 4, 8, 12, 24, 32 };
882
883static struct atmel_pmecc_caps at91sam9g45_caps = {
884 .strengths = atmel_pmecc_strengths,
885 .nstrengths = 5,
886 .el_offset = 0x8c,
887};
888
889static struct atmel_pmecc_caps sama5d4_caps = {
890 .strengths = atmel_pmecc_strengths,
891 .nstrengths = 5,
892 .el_offset = 0x8c,
893 .correct_erased_chunks = true,
894};
895
896static struct atmel_pmecc_caps sama5d2_caps = {
897 .strengths = atmel_pmecc_strengths,
898 .nstrengths = 6,
899 .el_offset = 0xac,
900 .correct_erased_chunks = true,
901};
902
903struct atmel_pmecc *devm_atmel_pmecc_get(struct udevice *userdev)
904{
905 struct atmel_pmecc *pmecc;
906 struct ofnode_phandle_args args;
907 struct udevice *pdev;
908 int ret;
909
910 if (!userdev)
911 return ERR_PTR(-EINVAL);
912
913 ret = ofnode_parse_phandle_with_args(userdev->node_,
914 "ecc-engine",
915 NULL, 0, 0, &args);
916 ret = uclass_get_device_by_ofnode(UCLASS_MTD, args.node, &pdev);
917 if (ret)
918 return NULL;
919
920 pmecc = atmel_pmecc_get_by_node(pdev);
921
922 /* TODO:
923 * Support old DT bindings
924 */
925
926 return pmecc;
927}
928EXPORT_SYMBOL(devm_atmel_pmecc_get);
929
930static const struct udevice_id atmel_pmecc_match[] = {
931 { .compatible = "atmel,at91sam9g45-pmecc", (ulong)&at91sam9g45_caps },
932 { .compatible = "atmel,sama5d4-pmecc", (ulong)&sama5d4_caps },
933 { .compatible = "atmel,sama5d2-pmecc", (ulong)&sama5d2_caps },
934 { /* sentinel */ }
935};
936
937static int atmel_pmecc_probe(struct udevice *dev)
938{
939 const struct atmel_pmecc_caps *caps;
940 struct atmel_pmecc *pmecc;
941
942 caps = (struct atmel_pmecc_caps *)dev_get_driver_data(dev);
943 if (!caps) {
944 dev_err(dev, "Invalid caps\n");
945 return -EINVAL;
946 }
947
948 pmecc = atmel_pmecc_create(dev, caps, 0, 1, 2);
949 if (IS_ERR(pmecc))
950 return PTR_ERR(pmecc);
951
952 dev->plat_ = pmecc;
953
954 return 0;
955}
956
957U_BOOT_DRIVER(atmel_pmecc) = {
958 .name = "atmel-pmecc",
959 .id = UCLASS_MTD,
960 .of_match = atmel_pmecc_match,
961 .probe = atmel_pmecc_probe,
962};