Lines Matching +full:nand +full:- +full:ecc +full:- +full:strength

1 // SPDX-License-Identifier: GPL-2.0
6 * Author: Boris Brezillon <boris.brezillon@free-electrons.com>
13 * Derived from drivers/mtd/nand/autcpu12.c (removed in v3.8)
19 * Add Hardware ECC support for AT91SAM9260 / AT91SAM9263
22 * Derived from Das U-Boot source code
23 * (u-boot-1.1.5/board/atmel/at91sam9263ek/nand.c)
26 * Add Programmable Multibit ECC support for various AT91 SoC
29 * Add Nand Flash Controller support for SAMA5 SoC
33 * ECC algorithm is left to the software. The hardware/software repartition
37 * sub-section.
41 * to expose the needed lib/bch.c helpers/functions and re-use them here.
75 #define PMECC_CFG_NSECTORS(x) ((fls(x) - 1) << 8)
188 /* polynomial degree is the most-significant bit index */ in deg()
189 return fls(poly) - 1; in deg()
197 unsigned int nn = BIT(mm) - 1; in atmel_pmecc_build_gf_tables()
201 return -EINVAL; in atmel_pmecc_build_gf_tables()
204 gf_tables->alpha_to[i] = x; in atmel_pmecc_build_gf_tables()
205 gf_tables->index_of[x] = i; in atmel_pmecc_build_gf_tables()
207 /* polynomial is not primitive (a^i=1 with 0<i<2^m-1) */ in atmel_pmecc_build_gf_tables()
208 return -EINVAL; in atmel_pmecc_build_gf_tables()
213 gf_tables->alpha_to[nn] = 1; in atmel_pmecc_build_gf_tables()
214 gf_tables->index_of[0] = 0; in atmel_pmecc_build_gf_tables()
226 if (req->ecc.sectorsize == 512) { in atmel_pmecc_create_gf_tables()
240 return ERR_PTR(-ENOMEM); in atmel_pmecc_create_gf_tables()
242 gf_tables->alpha_to = (void *)(gf_tables + 1); in atmel_pmecc_create_gf_tables()
243 gf_tables->index_of = gf_tables->alpha_to + table_size; in atmel_pmecc_create_gf_tables()
260 if (req->ecc.sectorsize == 512) in atmel_pmecc_get_gf_tables()
282 if (req->pagesize <= 0 || req->oobsize <= 0 || req->ecc.bytes <= 0) in atmel_pmecc_prepare_user_req()
283 return -EINVAL; in atmel_pmecc_prepare_user_req()
285 if (req->ecc.ooboffset >= 0 && in atmel_pmecc_prepare_user_req()
286 req->ecc.ooboffset + req->ecc.bytes > req->oobsize) in atmel_pmecc_prepare_user_req()
287 return -EINVAL; in atmel_pmecc_prepare_user_req()
289 if (req->ecc.sectorsize == ATMEL_PMECC_SECTOR_SIZE_AUTO) { in atmel_pmecc_prepare_user_req()
290 if (req->ecc.strength != ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH) in atmel_pmecc_prepare_user_req()
291 return -EINVAL; in atmel_pmecc_prepare_user_req()
293 if (req->pagesize > 512) in atmel_pmecc_prepare_user_req()
294 req->ecc.sectorsize = 1024; in atmel_pmecc_prepare_user_req()
296 req->ecc.sectorsize = 512; in atmel_pmecc_prepare_user_req()
299 if (req->ecc.sectorsize != 512 && req->ecc.sectorsize != 1024) in atmel_pmecc_prepare_user_req()
300 return -EINVAL; in atmel_pmecc_prepare_user_req()
302 if (req->pagesize % req->ecc.sectorsize) in atmel_pmecc_prepare_user_req()
303 return -EINVAL; in atmel_pmecc_prepare_user_req()
305 req->ecc.nsectors = req->pagesize / req->ecc.sectorsize; in atmel_pmecc_prepare_user_req()
307 max_eccbytes = req->ecc.bytes; in atmel_pmecc_prepare_user_req()
309 for (i = 0; i < pmecc->caps->nstrengths; i++) { in atmel_pmecc_prepare_user_req()
310 int nbytes, strength = pmecc->caps->strengths[i]; in atmel_pmecc_prepare_user_req() local
312 if (req->ecc.strength != ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH && in atmel_pmecc_prepare_user_req()
313 strength < req->ecc.strength) in atmel_pmecc_prepare_user_req()
316 nbytes = DIV_ROUND_UP(strength * fls(8 * req->ecc.sectorsize), in atmel_pmecc_prepare_user_req()
318 nbytes *= req->ecc.nsectors; in atmel_pmecc_prepare_user_req()
323 eccstrength = strength; in atmel_pmecc_prepare_user_req()
326 if (req->ecc.strength != ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH) in atmel_pmecc_prepare_user_req()
331 return -EINVAL; in atmel_pmecc_prepare_user_req()
333 req->ecc.bytes = eccbytes; in atmel_pmecc_prepare_user_req()
334 req->ecc.strength = eccstrength; in atmel_pmecc_prepare_user_req()
336 if (req->ecc.ooboffset < 0) in atmel_pmecc_prepare_user_req()
337 req->ecc.ooboffset = req->oobsize - eccbytes; in atmel_pmecc_prepare_user_req()
348 int strength, size, ret; in atmel_pmecc_create_user() local
357 size += ((2 * req->ecc.strength) + 1) * sizeof(u16) * in atmel_pmecc_create_user()
358 (2 + req->ecc.strength + 2); in atmel_pmecc_create_user()
360 size += (req->ecc.strength + 1) * sizeof(u16); in atmel_pmecc_create_user()
363 size += (req->ecc.strength + 1) * sizeof(s32) * 3; in atmel_pmecc_create_user()
367 return ERR_PTR(-ENOMEM); in atmel_pmecc_create_user()
369 user->pmecc = pmecc; in atmel_pmecc_create_user()
371 user->partial_syn = (s16 *)PTR_ALIGN(user + 1, sizeof(u16)); in atmel_pmecc_create_user()
372 user->si = user->partial_syn + ((2 * req->ecc.strength) + 1); in atmel_pmecc_create_user()
373 user->lmu = user->si + ((2 * req->ecc.strength) + 1); in atmel_pmecc_create_user()
374 user->smu = user->lmu + (req->ecc.strength + 1); in atmel_pmecc_create_user()
375 user->mu = (s32 *)PTR_ALIGN(user->smu + in atmel_pmecc_create_user()
376 (((2 * req->ecc.strength) + 1) * in atmel_pmecc_create_user()
377 (req->ecc.strength + 2)), in atmel_pmecc_create_user()
379 user->dmu = user->mu + req->ecc.strength + 1; in atmel_pmecc_create_user()
380 user->delta = user->dmu + req->ecc.strength + 1; in atmel_pmecc_create_user()
388 user->gf_tables = gf_tables; in atmel_pmecc_create_user()
390 user->eccbytes = req->ecc.bytes / req->ecc.nsectors; in atmel_pmecc_create_user()
392 for (strength = 0; strength < pmecc->caps->nstrengths; strength++) { in atmel_pmecc_create_user()
393 if (pmecc->caps->strengths[strength] == req->ecc.strength) in atmel_pmecc_create_user()
397 user->cache.cfg = PMECC_CFG_BCH_STRENGTH(strength) | in atmel_pmecc_create_user()
398 PMECC_CFG_NSECTORS(req->ecc.nsectors); in atmel_pmecc_create_user()
400 if (req->ecc.sectorsize == 1024) in atmel_pmecc_create_user()
401 user->cache.cfg |= PMECC_CFG_SECTOR1024; in atmel_pmecc_create_user()
403 user->cache.sarea = req->oobsize - 1; in atmel_pmecc_create_user()
404 user->cache.saddr = req->ecc.ooboffset; in atmel_pmecc_create_user()
405 user->cache.eaddr = req->ecc.ooboffset + req->ecc.bytes - 1; in atmel_pmecc_create_user()
419 const int *strengths = user->pmecc->caps->strengths; in get_strength()
421 return strengths[user->cache.cfg & PMECC_CFG_BCH_STRENGTH_MASK]; in get_strength()
426 return user->cache.cfg & PMECC_CFG_SECTOR1024 ? 1024 : 512; in get_sectorsize()
431 int strength = get_strength(user); in atmel_pmecc_gen_syndrome() local
436 for (i = 0; i < strength; i++) { in atmel_pmecc_gen_syndrome()
437 value = readl_relaxed(user->pmecc->regs.base + in atmel_pmecc_gen_syndrome()
442 user->partial_syn[(2 * i) + 1] = value; in atmel_pmecc_gen_syndrome()
449 int cw_len = BIT(degree) - 1; in atmel_pmecc_substitute()
450 int strength = get_strength(user); in atmel_pmecc_substitute() local
451 s16 *alpha_to = user->gf_tables->alpha_to; in atmel_pmecc_substitute()
452 s16 *index_of = user->gf_tables->index_of; in atmel_pmecc_substitute()
453 s16 *partial_syn = user->partial_syn; in atmel_pmecc_substitute()
461 si = user->si; in atmel_pmecc_substitute()
463 memset(&si[1], 0, sizeof(s16) * ((2 * strength) - 1)); in atmel_pmecc_substitute()
467 for (i = 1; i < 2 * strength; i += 2) { in atmel_pmecc_substitute()
474 for (i = 2, j = 1; j <= strength; i = ++j << 1) { in atmel_pmecc_substitute()
489 s16 *lmu = user->lmu; in atmel_pmecc_get_sigma()
490 s16 *si = user->si; in atmel_pmecc_get_sigma()
491 s32 *mu = user->mu; in atmel_pmecc_get_sigma()
492 s32 *dmu = user->dmu; in atmel_pmecc_get_sigma()
493 s32 *delta = user->delta; in atmel_pmecc_get_sigma()
495 int cw_len = BIT(degree) - 1; in atmel_pmecc_get_sigma()
496 int strength = get_strength(user); in atmel_pmecc_get_sigma() local
497 int num = 2 * strength + 1; in atmel_pmecc_get_sigma()
498 s16 *index_of = user->gf_tables->index_of; in atmel_pmecc_get_sigma()
499 s16 *alpha_to = user->gf_tables->alpha_to; in atmel_pmecc_get_sigma()
502 s16 *smu = user->smu; in atmel_pmecc_get_sigma()
514 mu[0] = -1; in atmel_pmecc_get_sigma()
523 delta[0] = (mu[0] * 2 - lmu[0]) >> 1; in atmel_pmecc_get_sigma()
539 delta[1] = (mu[1] * 2 - lmu[1]) >> 1; in atmel_pmecc_get_sigma()
542 memset(&smu[(strength + 1) * num], 0, sizeof(s16) * num); in atmel_pmecc_get_sigma()
544 for (i = 1; i <= strength; i++) { in atmel_pmecc_get_sigma()
551 tmp = ((strength - (lmu[i] >> 1) - 1) / 2); in atmel_pmecc_get_sigma()
552 if ((strength - (lmu[i] >> 1) - 1) & 0x1) in atmel_pmecc_get_sigma()
559 smu[(strength + 1) * num + j] = in atmel_pmecc_get_sigma()
562 lmu[strength + 1] = lmu[i]; in atmel_pmecc_get_sigma()
574 largest = -1; in atmel_pmecc_get_sigma()
584 diff = (mu[i] - mu[ro]); in atmel_pmecc_get_sigma()
606 tmp = a + (cw_len - b) + c; in atmel_pmecc_get_sigma()
617 delta[i + 1] = (mu[i + 1] * 2 - lmu[i + 1]) >> 1; in atmel_pmecc_get_sigma()
620 if (i >= strength) in atmel_pmecc_get_sigma()
624 tmp = 2 * (i - 1); in atmel_pmecc_get_sigma()
627 } else if (smu[(i + 1) * num + k] && si[tmp + 3 - k]) { in atmel_pmecc_get_sigma()
631 b = si[2 * (i - 1) + 3 - k]; in atmel_pmecc_get_sigma()
645 struct atmel_pmecc *pmecc = user->pmecc; in atmel_pmecc_err_location()
646 int strength = get_strength(user); in atmel_pmecc_err_location() local
648 int num = (2 * strength) + 1; in atmel_pmecc_err_location()
649 s16 *smu = user->smu; in atmel_pmecc_err_location()
652 writel(PMERRLOC_DISABLE, pmecc->regs.errloc + ATMEL_PMERRLOC_ELDIS); in atmel_pmecc_err_location()
654 for (i = 0; i <= user->lmu[strength + 1] >> 1; i++) { in atmel_pmecc_err_location()
655 writel_relaxed(smu[(strength + 1) * num + i], in atmel_pmecc_err_location()
656 pmecc->regs.errloc + ATMEL_PMERRLOC_SIGMA(i)); in atmel_pmecc_err_location()
660 val = (err_nbr - 1) << 16; in atmel_pmecc_err_location()
664 writel(val, pmecc->regs.errloc + ATMEL_PMERRLOC_ELCFG); in atmel_pmecc_err_location()
665 writel((sector_size * 8) + (degree * strength), in atmel_pmecc_err_location()
666 pmecc->regs.errloc + ATMEL_PMERRLOC_ELEN); in atmel_pmecc_err_location()
668 ret = readl_relaxed_poll_timeout(pmecc->regs.errloc + in atmel_pmecc_err_location()
673 dev_err(pmecc->dev, in atmel_pmecc_err_location()
680 if (roots_nbr == user->lmu[strength + 1] >> 1) in atmel_pmecc_err_location()
681 return err_nbr - 1; in atmel_pmecc_err_location()
687 return -EBADMSG; in atmel_pmecc_err_location()
691 void *data, void *ecc) in atmel_pmecc_correct_sector() argument
693 struct atmel_pmecc *pmecc = user->pmecc; in atmel_pmecc_correct_sector()
695 int eccbytes = user->eccbytes; in atmel_pmecc_correct_sector()
698 if (!(user->isr & BIT(sector))) in atmel_pmecc_correct_sector()
715 errpos = readl_relaxed(pmecc->regs.errloc + in atmel_pmecc_correct_sector()
716 ATMEL_PMERRLOC_EL(pmecc->caps->el_offset, i)); in atmel_pmecc_correct_sector()
717 errpos--; in atmel_pmecc_correct_sector()
726 ptr = ecc + byte - sectorsize; in atmel_pmecc_correct_sector()
727 area = "ECC"; in atmel_pmecc_correct_sector()
729 dev_dbg(pmecc->dev, in atmel_pmecc_correct_sector()
732 return -EINVAL; in atmel_pmecc_correct_sector()
735 dev_dbg(pmecc->dev, in atmel_pmecc_correct_sector()
736 "Bit flip in %s area, byte %d: 0x%02x -> 0x%02x\n", in atmel_pmecc_correct_sector()
748 return user->pmecc->caps->correct_erased_chunks; in atmel_pmecc_correct_erased_chunks()
753 int sector, void *ecc) in atmel_pmecc_get_generated_eccbytes() argument
755 struct atmel_pmecc *pmecc = user->pmecc; in atmel_pmecc_get_generated_eccbytes()
756 u8 *ptr = ecc; in atmel_pmecc_get_generated_eccbytes()
759 for (i = 0; i < user->eccbytes; i++) in atmel_pmecc_get_generated_eccbytes()
760 ptr[i] = readb_relaxed(pmecc->regs.base + in atmel_pmecc_get_generated_eccbytes()
767 writel(PMECC_CTRL_RST, pmecc->regs.base + ATMEL_PMECC_CTRL); in atmel_pmecc_reset()
768 writel(PMECC_CTRL_DISABLE, pmecc->regs.base + ATMEL_PMECC_CTRL); in atmel_pmecc_reset()
774 struct atmel_pmecc *pmecc = user->pmecc; in atmel_pmecc_enable()
778 dev_err(pmecc->dev, "Bad ECC operation!"); in atmel_pmecc_enable()
779 return -EINVAL; in atmel_pmecc_enable()
782 mutex_lock(&user->pmecc->lock); in atmel_pmecc_enable()
784 cfg = user->cache.cfg; in atmel_pmecc_enable()
790 writel(cfg, pmecc->regs.base + ATMEL_PMECC_CFG); in atmel_pmecc_enable()
791 writel(user->cache.sarea, pmecc->regs.base + ATMEL_PMECC_SAREA); in atmel_pmecc_enable()
792 writel(user->cache.saddr, pmecc->regs.base + ATMEL_PMECC_SADDR); in atmel_pmecc_enable()
793 writel(user->cache.eaddr, pmecc->regs.base + ATMEL_PMECC_EADDR); in atmel_pmecc_enable()
795 writel(PMECC_CTRL_ENABLE, pmecc->regs.base + ATMEL_PMECC_CTRL); in atmel_pmecc_enable()
796 writel(PMECC_CTRL_DATA, pmecc->regs.base + ATMEL_PMECC_CTRL); in atmel_pmecc_enable()
804 atmel_pmecc_reset(user->pmecc); in atmel_pmecc_disable()
805 mutex_unlock(&user->pmecc->lock); in atmel_pmecc_disable()
811 struct atmel_pmecc *pmecc = user->pmecc; in atmel_pmecc_wait_rdy()
815 ret = readl_relaxed_poll_timeout(pmecc->regs.base + in atmel_pmecc_wait_rdy()
820 dev_err(pmecc->dev, in atmel_pmecc_wait_rdy()
825 user->isr = readl_relaxed(pmecc->regs.base + ATMEL_PMECC_ISR); in atmel_pmecc_wait_rdy()
835 struct device *dev = &pdev->dev; in atmel_pmecc_create()
841 return ERR_PTR(-ENOMEM); in atmel_pmecc_create()
843 pmecc->caps = caps; in atmel_pmecc_create()
844 pmecc->dev = dev; in atmel_pmecc_create()
845 mutex_init(&pmecc->lock); in atmel_pmecc_create()
848 pmecc->regs.base = devm_ioremap_resource(dev, res); in atmel_pmecc_create()
849 if (IS_ERR(pmecc->regs.base)) in atmel_pmecc_create()
850 return ERR_CAST(pmecc->regs.base); in atmel_pmecc_create()
853 pmecc->regs.errloc = devm_ioremap_resource(dev, res); in atmel_pmecc_create()
854 if (IS_ERR(pmecc->regs.errloc)) in atmel_pmecc_create()
855 return ERR_CAST(pmecc->regs.errloc); in atmel_pmecc_create()
858 writel(0xffffffff, pmecc->regs.base + ATMEL_PMECC_IDR); in atmel_pmecc_create()
868 put_device((*pmecc)->dev); in devm_atmel_pmecc_put()
880 return ERR_PTR(-EPROBE_DEFER); in atmel_pmecc_get_by_node()
883 ret = -EPROBE_DEFER; in atmel_pmecc_get_by_node()
889 ret = -ENOMEM; in atmel_pmecc_get_by_node()
900 put_device(&pdev->dev); in atmel_pmecc_get_by_node()
927 { .compatible = "atmel,sama5d4-nand", &sama5d4_caps },
928 { .compatible = "atmel,sama5d2-nand", &sama5d2_caps },
938 return ERR_PTR(-EINVAL); in devm_atmel_pmecc_get()
940 if (!userdev->of_node) in devm_atmel_pmecc_get()
943 np = of_parse_phandle(userdev->of_node, "ecc-engine", 0); in devm_atmel_pmecc_get()
958 if (!of_property_read_bool(userdev->of_node, in devm_atmel_pmecc_get()
959 "atmel,has-pmecc")) in devm_atmel_pmecc_get()
964 /* Find the caps associated to the NAND dev node. */ in devm_atmel_pmecc_get()
966 userdev->of_node); in devm_atmel_pmecc_get()
967 if (match && match->data) in devm_atmel_pmecc_get()
968 caps = match->data; in devm_atmel_pmecc_get()
978 { .compatible = "atmel,at91sam9g45-pmecc", &at91sam9g45_caps },
979 { .compatible = "atmel,sama5d4-pmecc", &sama5d4_caps },
980 { .compatible = "atmel,sama5d2-pmecc", &sama5d2_caps },
987 struct device *dev = &pdev->dev; in atmel_pmecc_probe()
991 caps = of_device_get_match_data(&pdev->dev); in atmel_pmecc_probe()
994 return -EINVAL; in atmel_pmecc_probe()
1008 .name = "atmel-pmecc",
1016 MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>");