18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright 2017 ATMEL 48c2ecf20Sopenharmony_ci * Copyright 2017 Free Electrons 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Author: Boris Brezillon <boris.brezillon@free-electrons.com> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Derived from the atmel_nand.c driver which contained the following 98c2ecf20Sopenharmony_ci * copyrights: 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * Copyright 2003 Rick Bronson 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * Derived from drivers/mtd/nand/autcpu12.c (removed in v3.8) 148c2ecf20Sopenharmony_ci * Copyright 2001 Thomas Gleixner (gleixner@autronix.de) 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * Derived from drivers/mtd/spia.c (removed in v3.8) 178c2ecf20Sopenharmony_ci * Copyright 2000 Steven J. Hill (sjhill@cotw.com) 188c2ecf20Sopenharmony_ci * 198c2ecf20Sopenharmony_ci * Add Hardware ECC support for AT91SAM9260 / AT91SAM9263 208c2ecf20Sopenharmony_ci * Richard Genoud (richard.genoud@gmail.com), Adeneo Copyright 2007 218c2ecf20Sopenharmony_ci * 228c2ecf20Sopenharmony_ci * Derived from Das U-Boot source code 238c2ecf20Sopenharmony_ci * (u-boot-1.1.5/board/atmel/at91sam9263ek/nand.c) 248c2ecf20Sopenharmony_ci * Copyright 2006 ATMEL Rousset, Lacressonniere Nicolas 258c2ecf20Sopenharmony_ci * 268c2ecf20Sopenharmony_ci * Add Programmable Multibit ECC support for various AT91 SoC 278c2ecf20Sopenharmony_ci * Copyright 2012 ATMEL, Hong Xu 288c2ecf20Sopenharmony_ci * 298c2ecf20Sopenharmony_ci * Add Nand Flash Controller support for SAMA5 SoC 308c2ecf20Sopenharmony_ci * Copyright 2013 ATMEL, Josh Wu (josh.wu@atmel.com) 318c2ecf20Sopenharmony_ci * 328c2ecf20Sopenharmony_ci * The PMECC is an hardware assisted BCH engine, which means part of the 338c2ecf20Sopenharmony_ci * ECC algorithm is left to the software. The hardware/software repartition 348c2ecf20Sopenharmony_ci * is explained in the "PMECC Controller Functional Description" chapter in 358c2ecf20Sopenharmony_ci * Atmel datasheets, and some of the functions in this file are directly 368c2ecf20Sopenharmony_ci * implementing the algorithms described in the "Software Implementation" 378c2ecf20Sopenharmony_ci * sub-section. 388c2ecf20Sopenharmony_ci * 398c2ecf20Sopenharmony_ci * TODO: it seems that the software BCH implementation in lib/bch.c is already 408c2ecf20Sopenharmony_ci * providing some of the logic we are implementing here. It would be smart 418c2ecf20Sopenharmony_ci * to expose the needed lib/bch.c helpers/functions and re-use them here. 428c2ecf20Sopenharmony_ci */ 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci#include <linux/genalloc.h> 458c2ecf20Sopenharmony_ci#include <linux/iopoll.h> 468c2ecf20Sopenharmony_ci#include <linux/module.h> 478c2ecf20Sopenharmony_ci#include <linux/mtd/rawnand.h> 488c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 498c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 508c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 518c2ecf20Sopenharmony_ci#include <linux/slab.h> 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci#include "pmecc.h" 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci/* Galois field dimension */ 568c2ecf20Sopenharmony_ci#define PMECC_GF_DIMENSION_13 13 578c2ecf20Sopenharmony_ci#define PMECC_GF_DIMENSION_14 14 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci/* Primitive Polynomial used by PMECC */ 608c2ecf20Sopenharmony_ci#define PMECC_GF_13_PRIMITIVE_POLY 0x201b 618c2ecf20Sopenharmony_ci#define PMECC_GF_14_PRIMITIVE_POLY 0x4443 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci#define PMECC_LOOKUP_TABLE_SIZE_512 0x2000 648c2ecf20Sopenharmony_ci#define PMECC_LOOKUP_TABLE_SIZE_1024 0x4000 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci/* Time out value for reading PMECC status register */ 678c2ecf20Sopenharmony_ci#define PMECC_MAX_TIMEOUT_MS 100 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci/* PMECC Register Definitions */ 708c2ecf20Sopenharmony_ci#define ATMEL_PMECC_CFG 0x0 718c2ecf20Sopenharmony_ci#define PMECC_CFG_BCH_STRENGTH(x) (x) 728c2ecf20Sopenharmony_ci#define PMECC_CFG_BCH_STRENGTH_MASK GENMASK(2, 0) 738c2ecf20Sopenharmony_ci#define PMECC_CFG_SECTOR512 (0 << 4) 748c2ecf20Sopenharmony_ci#define PMECC_CFG_SECTOR1024 (1 << 4) 758c2ecf20Sopenharmony_ci#define PMECC_CFG_NSECTORS(x) ((fls(x) - 1) << 8) 768c2ecf20Sopenharmony_ci#define PMECC_CFG_READ_OP (0 << 12) 778c2ecf20Sopenharmony_ci#define PMECC_CFG_WRITE_OP (1 << 12) 788c2ecf20Sopenharmony_ci#define PMECC_CFG_SPARE_ENABLE BIT(16) 798c2ecf20Sopenharmony_ci#define PMECC_CFG_AUTO_ENABLE BIT(20) 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci#define ATMEL_PMECC_SAREA 0x4 828c2ecf20Sopenharmony_ci#define ATMEL_PMECC_SADDR 0x8 838c2ecf20Sopenharmony_ci#define ATMEL_PMECC_EADDR 0xc 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci#define ATMEL_PMECC_CLK 0x10 868c2ecf20Sopenharmony_ci#define PMECC_CLK_133MHZ (2 << 0) 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci#define ATMEL_PMECC_CTRL 0x14 898c2ecf20Sopenharmony_ci#define PMECC_CTRL_RST BIT(0) 908c2ecf20Sopenharmony_ci#define PMECC_CTRL_DATA BIT(1) 918c2ecf20Sopenharmony_ci#define PMECC_CTRL_USER BIT(2) 928c2ecf20Sopenharmony_ci#define PMECC_CTRL_ENABLE BIT(4) 938c2ecf20Sopenharmony_ci#define PMECC_CTRL_DISABLE BIT(5) 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci#define ATMEL_PMECC_SR 0x18 968c2ecf20Sopenharmony_ci#define PMECC_SR_BUSY BIT(0) 978c2ecf20Sopenharmony_ci#define PMECC_SR_ENABLE BIT(4) 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci#define ATMEL_PMECC_IER 0x1c 1008c2ecf20Sopenharmony_ci#define ATMEL_PMECC_IDR 0x20 1018c2ecf20Sopenharmony_ci#define ATMEL_PMECC_IMR 0x24 1028c2ecf20Sopenharmony_ci#define ATMEL_PMECC_ISR 0x28 1038c2ecf20Sopenharmony_ci#define PMECC_ERROR_INT BIT(0) 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci#define ATMEL_PMECC_ECC(sector, n) \ 1068c2ecf20Sopenharmony_ci ((((sector) + 1) * 0x40) + (n)) 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci#define ATMEL_PMECC_REM(sector, n) \ 1098c2ecf20Sopenharmony_ci ((((sector) + 1) * 0x40) + ((n) * 4) + 0x200) 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci/* PMERRLOC Register Definitions */ 1128c2ecf20Sopenharmony_ci#define ATMEL_PMERRLOC_ELCFG 0x0 1138c2ecf20Sopenharmony_ci#define PMERRLOC_ELCFG_SECTOR_512 (0 << 0) 1148c2ecf20Sopenharmony_ci#define PMERRLOC_ELCFG_SECTOR_1024 (1 << 0) 1158c2ecf20Sopenharmony_ci#define PMERRLOC_ELCFG_NUM_ERRORS(n) ((n) << 16) 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci#define ATMEL_PMERRLOC_ELPRIM 0x4 1188c2ecf20Sopenharmony_ci#define ATMEL_PMERRLOC_ELEN 0x8 1198c2ecf20Sopenharmony_ci#define ATMEL_PMERRLOC_ELDIS 0xc 1208c2ecf20Sopenharmony_ci#define PMERRLOC_DISABLE BIT(0) 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci#define ATMEL_PMERRLOC_ELSR 0x10 1238c2ecf20Sopenharmony_ci#define PMERRLOC_ELSR_BUSY BIT(0) 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci#define ATMEL_PMERRLOC_ELIER 0x14 1268c2ecf20Sopenharmony_ci#define ATMEL_PMERRLOC_ELIDR 0x18 1278c2ecf20Sopenharmony_ci#define ATMEL_PMERRLOC_ELIMR 0x1c 1288c2ecf20Sopenharmony_ci#define ATMEL_PMERRLOC_ELISR 0x20 1298c2ecf20Sopenharmony_ci#define PMERRLOC_ERR_NUM_MASK GENMASK(12, 8) 1308c2ecf20Sopenharmony_ci#define PMERRLOC_CALC_DONE BIT(0) 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci#define ATMEL_PMERRLOC_SIGMA(x) (((x) * 0x4) + 0x28) 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci#define ATMEL_PMERRLOC_EL(offs, x) (((x) * 0x4) + (offs)) 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_cistruct atmel_pmecc_gf_tables { 1378c2ecf20Sopenharmony_ci u16 *alpha_to; 1388c2ecf20Sopenharmony_ci u16 *index_of; 1398c2ecf20Sopenharmony_ci}; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistruct atmel_pmecc_caps { 1428c2ecf20Sopenharmony_ci const int *strengths; 1438c2ecf20Sopenharmony_ci int nstrengths; 1448c2ecf20Sopenharmony_ci int el_offset; 1458c2ecf20Sopenharmony_ci bool correct_erased_chunks; 1468c2ecf20Sopenharmony_ci}; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cistruct atmel_pmecc { 1498c2ecf20Sopenharmony_ci struct device *dev; 1508c2ecf20Sopenharmony_ci const struct atmel_pmecc_caps *caps; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci struct { 1538c2ecf20Sopenharmony_ci void __iomem *base; 1548c2ecf20Sopenharmony_ci void __iomem *errloc; 1558c2ecf20Sopenharmony_ci } regs; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci struct mutex lock; 1588c2ecf20Sopenharmony_ci}; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_cistruct atmel_pmecc_user_conf_cache { 1618c2ecf20Sopenharmony_ci u32 cfg; 1628c2ecf20Sopenharmony_ci u32 sarea; 1638c2ecf20Sopenharmony_ci u32 saddr; 1648c2ecf20Sopenharmony_ci u32 eaddr; 1658c2ecf20Sopenharmony_ci}; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_cistruct atmel_pmecc_user { 1688c2ecf20Sopenharmony_ci struct atmel_pmecc_user_conf_cache cache; 1698c2ecf20Sopenharmony_ci struct atmel_pmecc *pmecc; 1708c2ecf20Sopenharmony_ci const struct atmel_pmecc_gf_tables *gf_tables; 1718c2ecf20Sopenharmony_ci int eccbytes; 1728c2ecf20Sopenharmony_ci s16 *partial_syn; 1738c2ecf20Sopenharmony_ci s16 *si; 1748c2ecf20Sopenharmony_ci s16 *lmu; 1758c2ecf20Sopenharmony_ci s16 *smu; 1768c2ecf20Sopenharmony_ci s32 *mu; 1778c2ecf20Sopenharmony_ci s32 *dmu; 1788c2ecf20Sopenharmony_ci s32 *delta; 1798c2ecf20Sopenharmony_ci u32 isr; 1808c2ecf20Sopenharmony_ci}; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(pmecc_gf_tables_lock); 1838c2ecf20Sopenharmony_cistatic const struct atmel_pmecc_gf_tables *pmecc_gf_tables_512; 1848c2ecf20Sopenharmony_cistatic const struct atmel_pmecc_gf_tables *pmecc_gf_tables_1024; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_cistatic inline int deg(unsigned int poly) 1878c2ecf20Sopenharmony_ci{ 1888c2ecf20Sopenharmony_ci /* polynomial degree is the most-significant bit index */ 1898c2ecf20Sopenharmony_ci return fls(poly) - 1; 1908c2ecf20Sopenharmony_ci} 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_cistatic int atmel_pmecc_build_gf_tables(int mm, unsigned int poly, 1938c2ecf20Sopenharmony_ci struct atmel_pmecc_gf_tables *gf_tables) 1948c2ecf20Sopenharmony_ci{ 1958c2ecf20Sopenharmony_ci unsigned int i, x = 1; 1968c2ecf20Sopenharmony_ci const unsigned int k = BIT(deg(poly)); 1978c2ecf20Sopenharmony_ci unsigned int nn = BIT(mm) - 1; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci /* primitive polynomial must be of degree m */ 2008c2ecf20Sopenharmony_ci if (k != (1u << mm)) 2018c2ecf20Sopenharmony_ci return -EINVAL; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci for (i = 0; i < nn; i++) { 2048c2ecf20Sopenharmony_ci gf_tables->alpha_to[i] = x; 2058c2ecf20Sopenharmony_ci gf_tables->index_of[x] = i; 2068c2ecf20Sopenharmony_ci if (i && (x == 1)) 2078c2ecf20Sopenharmony_ci /* polynomial is not primitive (a^i=1 with 0<i<2^m-1) */ 2088c2ecf20Sopenharmony_ci return -EINVAL; 2098c2ecf20Sopenharmony_ci x <<= 1; 2108c2ecf20Sopenharmony_ci if (x & k) 2118c2ecf20Sopenharmony_ci x ^= poly; 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci gf_tables->alpha_to[nn] = 1; 2148c2ecf20Sopenharmony_ci gf_tables->index_of[0] = 0; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci return 0; 2178c2ecf20Sopenharmony_ci} 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cistatic const struct atmel_pmecc_gf_tables * 2208c2ecf20Sopenharmony_ciatmel_pmecc_create_gf_tables(const struct atmel_pmecc_user_req *req) 2218c2ecf20Sopenharmony_ci{ 2228c2ecf20Sopenharmony_ci struct atmel_pmecc_gf_tables *gf_tables; 2238c2ecf20Sopenharmony_ci unsigned int poly, degree, table_size; 2248c2ecf20Sopenharmony_ci int ret; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci if (req->ecc.sectorsize == 512) { 2278c2ecf20Sopenharmony_ci degree = PMECC_GF_DIMENSION_13; 2288c2ecf20Sopenharmony_ci poly = PMECC_GF_13_PRIMITIVE_POLY; 2298c2ecf20Sopenharmony_ci table_size = PMECC_LOOKUP_TABLE_SIZE_512; 2308c2ecf20Sopenharmony_ci } else { 2318c2ecf20Sopenharmony_ci degree = PMECC_GF_DIMENSION_14; 2328c2ecf20Sopenharmony_ci poly = PMECC_GF_14_PRIMITIVE_POLY; 2338c2ecf20Sopenharmony_ci table_size = PMECC_LOOKUP_TABLE_SIZE_1024; 2348c2ecf20Sopenharmony_ci } 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci gf_tables = kzalloc(sizeof(*gf_tables) + 2378c2ecf20Sopenharmony_ci (2 * table_size * sizeof(u16)), 2388c2ecf20Sopenharmony_ci GFP_KERNEL); 2398c2ecf20Sopenharmony_ci if (!gf_tables) 2408c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci gf_tables->alpha_to = (void *)(gf_tables + 1); 2438c2ecf20Sopenharmony_ci gf_tables->index_of = gf_tables->alpha_to + table_size; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci ret = atmel_pmecc_build_gf_tables(degree, poly, gf_tables); 2468c2ecf20Sopenharmony_ci if (ret) { 2478c2ecf20Sopenharmony_ci kfree(gf_tables); 2488c2ecf20Sopenharmony_ci return ERR_PTR(ret); 2498c2ecf20Sopenharmony_ci } 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci return gf_tables; 2528c2ecf20Sopenharmony_ci} 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_cistatic const struct atmel_pmecc_gf_tables * 2558c2ecf20Sopenharmony_ciatmel_pmecc_get_gf_tables(const struct atmel_pmecc_user_req *req) 2568c2ecf20Sopenharmony_ci{ 2578c2ecf20Sopenharmony_ci const struct atmel_pmecc_gf_tables **gf_tables, *ret; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci mutex_lock(&pmecc_gf_tables_lock); 2608c2ecf20Sopenharmony_ci if (req->ecc.sectorsize == 512) 2618c2ecf20Sopenharmony_ci gf_tables = &pmecc_gf_tables_512; 2628c2ecf20Sopenharmony_ci else 2638c2ecf20Sopenharmony_ci gf_tables = &pmecc_gf_tables_1024; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci ret = *gf_tables; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci if (!ret) { 2688c2ecf20Sopenharmony_ci ret = atmel_pmecc_create_gf_tables(req); 2698c2ecf20Sopenharmony_ci if (!IS_ERR(ret)) 2708c2ecf20Sopenharmony_ci *gf_tables = ret; 2718c2ecf20Sopenharmony_ci } 2728c2ecf20Sopenharmony_ci mutex_unlock(&pmecc_gf_tables_lock); 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci return ret; 2758c2ecf20Sopenharmony_ci} 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_cistatic int atmel_pmecc_prepare_user_req(struct atmel_pmecc *pmecc, 2788c2ecf20Sopenharmony_ci struct atmel_pmecc_user_req *req) 2798c2ecf20Sopenharmony_ci{ 2808c2ecf20Sopenharmony_ci int i, max_eccbytes, eccbytes = 0, eccstrength = 0; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci if (req->pagesize <= 0 || req->oobsize <= 0 || req->ecc.bytes <= 0) 2838c2ecf20Sopenharmony_ci return -EINVAL; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci if (req->ecc.ooboffset >= 0 && 2868c2ecf20Sopenharmony_ci req->ecc.ooboffset + req->ecc.bytes > req->oobsize) 2878c2ecf20Sopenharmony_ci return -EINVAL; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci if (req->ecc.sectorsize == ATMEL_PMECC_SECTOR_SIZE_AUTO) { 2908c2ecf20Sopenharmony_ci if (req->ecc.strength != ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH) 2918c2ecf20Sopenharmony_ci return -EINVAL; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci if (req->pagesize > 512) 2948c2ecf20Sopenharmony_ci req->ecc.sectorsize = 1024; 2958c2ecf20Sopenharmony_ci else 2968c2ecf20Sopenharmony_ci req->ecc.sectorsize = 512; 2978c2ecf20Sopenharmony_ci } 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci if (req->ecc.sectorsize != 512 && req->ecc.sectorsize != 1024) 3008c2ecf20Sopenharmony_ci return -EINVAL; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci if (req->pagesize % req->ecc.sectorsize) 3038c2ecf20Sopenharmony_ci return -EINVAL; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci req->ecc.nsectors = req->pagesize / req->ecc.sectorsize; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci max_eccbytes = req->ecc.bytes; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci for (i = 0; i < pmecc->caps->nstrengths; i++) { 3108c2ecf20Sopenharmony_ci int nbytes, strength = pmecc->caps->strengths[i]; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci if (req->ecc.strength != ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH && 3138c2ecf20Sopenharmony_ci strength < req->ecc.strength) 3148c2ecf20Sopenharmony_ci continue; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci nbytes = DIV_ROUND_UP(strength * fls(8 * req->ecc.sectorsize), 3178c2ecf20Sopenharmony_ci 8); 3188c2ecf20Sopenharmony_ci nbytes *= req->ecc.nsectors; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci if (nbytes > max_eccbytes) 3218c2ecf20Sopenharmony_ci break; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci eccstrength = strength; 3248c2ecf20Sopenharmony_ci eccbytes = nbytes; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci if (req->ecc.strength != ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH) 3278c2ecf20Sopenharmony_ci break; 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci if (!eccstrength) 3318c2ecf20Sopenharmony_ci return -EINVAL; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci req->ecc.bytes = eccbytes; 3348c2ecf20Sopenharmony_ci req->ecc.strength = eccstrength; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci if (req->ecc.ooboffset < 0) 3378c2ecf20Sopenharmony_ci req->ecc.ooboffset = req->oobsize - eccbytes; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci return 0; 3408c2ecf20Sopenharmony_ci} 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_cistruct atmel_pmecc_user * 3438c2ecf20Sopenharmony_ciatmel_pmecc_create_user(struct atmel_pmecc *pmecc, 3448c2ecf20Sopenharmony_ci struct atmel_pmecc_user_req *req) 3458c2ecf20Sopenharmony_ci{ 3468c2ecf20Sopenharmony_ci struct atmel_pmecc_user *user; 3478c2ecf20Sopenharmony_ci const struct atmel_pmecc_gf_tables *gf_tables; 3488c2ecf20Sopenharmony_ci int strength, size, ret; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci ret = atmel_pmecc_prepare_user_req(pmecc, req); 3518c2ecf20Sopenharmony_ci if (ret) 3528c2ecf20Sopenharmony_ci return ERR_PTR(ret); 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci size = sizeof(*user); 3558c2ecf20Sopenharmony_ci size = ALIGN(size, sizeof(u16)); 3568c2ecf20Sopenharmony_ci /* Reserve space for partial_syn, si and smu */ 3578c2ecf20Sopenharmony_ci size += ((2 * req->ecc.strength) + 1) * sizeof(u16) * 3588c2ecf20Sopenharmony_ci (2 + req->ecc.strength + 2); 3598c2ecf20Sopenharmony_ci /* Reserve space for lmu. */ 3608c2ecf20Sopenharmony_ci size += (req->ecc.strength + 1) * sizeof(u16); 3618c2ecf20Sopenharmony_ci /* Reserve space for mu, dmu and delta. */ 3628c2ecf20Sopenharmony_ci size = ALIGN(size, sizeof(s32)); 3638c2ecf20Sopenharmony_ci size += (req->ecc.strength + 1) * sizeof(s32) * 3; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci user = kzalloc(size, GFP_KERNEL); 3668c2ecf20Sopenharmony_ci if (!user) 3678c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci user->pmecc = pmecc; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci user->partial_syn = (s16 *)PTR_ALIGN(user + 1, sizeof(u16)); 3728c2ecf20Sopenharmony_ci user->si = user->partial_syn + ((2 * req->ecc.strength) + 1); 3738c2ecf20Sopenharmony_ci user->lmu = user->si + ((2 * req->ecc.strength) + 1); 3748c2ecf20Sopenharmony_ci user->smu = user->lmu + (req->ecc.strength + 1); 3758c2ecf20Sopenharmony_ci user->mu = (s32 *)PTR_ALIGN(user->smu + 3768c2ecf20Sopenharmony_ci (((2 * req->ecc.strength) + 1) * 3778c2ecf20Sopenharmony_ci (req->ecc.strength + 2)), 3788c2ecf20Sopenharmony_ci sizeof(s32)); 3798c2ecf20Sopenharmony_ci user->dmu = user->mu + req->ecc.strength + 1; 3808c2ecf20Sopenharmony_ci user->delta = user->dmu + req->ecc.strength + 1; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci gf_tables = atmel_pmecc_get_gf_tables(req); 3838c2ecf20Sopenharmony_ci if (IS_ERR(gf_tables)) { 3848c2ecf20Sopenharmony_ci kfree(user); 3858c2ecf20Sopenharmony_ci return ERR_CAST(gf_tables); 3868c2ecf20Sopenharmony_ci } 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci user->gf_tables = gf_tables; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci user->eccbytes = req->ecc.bytes / req->ecc.nsectors; 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci for (strength = 0; strength < pmecc->caps->nstrengths; strength++) { 3938c2ecf20Sopenharmony_ci if (pmecc->caps->strengths[strength] == req->ecc.strength) 3948c2ecf20Sopenharmony_ci break; 3958c2ecf20Sopenharmony_ci } 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci user->cache.cfg = PMECC_CFG_BCH_STRENGTH(strength) | 3988c2ecf20Sopenharmony_ci PMECC_CFG_NSECTORS(req->ecc.nsectors); 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci if (req->ecc.sectorsize == 1024) 4018c2ecf20Sopenharmony_ci user->cache.cfg |= PMECC_CFG_SECTOR1024; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci user->cache.sarea = req->oobsize - 1; 4048c2ecf20Sopenharmony_ci user->cache.saddr = req->ecc.ooboffset; 4058c2ecf20Sopenharmony_ci user->cache.eaddr = req->ecc.ooboffset + req->ecc.bytes - 1; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci return user; 4088c2ecf20Sopenharmony_ci} 4098c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(atmel_pmecc_create_user); 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_civoid atmel_pmecc_destroy_user(struct atmel_pmecc_user *user) 4128c2ecf20Sopenharmony_ci{ 4138c2ecf20Sopenharmony_ci kfree(user); 4148c2ecf20Sopenharmony_ci} 4158c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(atmel_pmecc_destroy_user); 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_cistatic int get_strength(struct atmel_pmecc_user *user) 4188c2ecf20Sopenharmony_ci{ 4198c2ecf20Sopenharmony_ci const int *strengths = user->pmecc->caps->strengths; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci return strengths[user->cache.cfg & PMECC_CFG_BCH_STRENGTH_MASK]; 4228c2ecf20Sopenharmony_ci} 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_cistatic int get_sectorsize(struct atmel_pmecc_user *user) 4258c2ecf20Sopenharmony_ci{ 4268c2ecf20Sopenharmony_ci return user->cache.cfg & PMECC_CFG_SECTOR1024 ? 1024 : 512; 4278c2ecf20Sopenharmony_ci} 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_cistatic void atmel_pmecc_gen_syndrome(struct atmel_pmecc_user *user, int sector) 4308c2ecf20Sopenharmony_ci{ 4318c2ecf20Sopenharmony_ci int strength = get_strength(user); 4328c2ecf20Sopenharmony_ci u32 value; 4338c2ecf20Sopenharmony_ci int i; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci /* Fill odd syndromes */ 4368c2ecf20Sopenharmony_ci for (i = 0; i < strength; i++) { 4378c2ecf20Sopenharmony_ci value = readl_relaxed(user->pmecc->regs.base + 4388c2ecf20Sopenharmony_ci ATMEL_PMECC_REM(sector, i / 2)); 4398c2ecf20Sopenharmony_ci if (i & 1) 4408c2ecf20Sopenharmony_ci value >>= 16; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci user->partial_syn[(2 * i) + 1] = value; 4438c2ecf20Sopenharmony_ci } 4448c2ecf20Sopenharmony_ci} 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_cistatic void atmel_pmecc_substitute(struct atmel_pmecc_user *user) 4478c2ecf20Sopenharmony_ci{ 4488c2ecf20Sopenharmony_ci int degree = get_sectorsize(user) == 512 ? 13 : 14; 4498c2ecf20Sopenharmony_ci int cw_len = BIT(degree) - 1; 4508c2ecf20Sopenharmony_ci int strength = get_strength(user); 4518c2ecf20Sopenharmony_ci s16 *alpha_to = user->gf_tables->alpha_to; 4528c2ecf20Sopenharmony_ci s16 *index_of = user->gf_tables->index_of; 4538c2ecf20Sopenharmony_ci s16 *partial_syn = user->partial_syn; 4548c2ecf20Sopenharmony_ci s16 *si; 4558c2ecf20Sopenharmony_ci int i, j; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci /* 4588c2ecf20Sopenharmony_ci * si[] is a table that holds the current syndrome value, 4598c2ecf20Sopenharmony_ci * an element of that table belongs to the field 4608c2ecf20Sopenharmony_ci */ 4618c2ecf20Sopenharmony_ci si = user->si; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci memset(&si[1], 0, sizeof(s16) * ((2 * strength) - 1)); 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci /* Computation 2t syndromes based on S(x) */ 4668c2ecf20Sopenharmony_ci /* Odd syndromes */ 4678c2ecf20Sopenharmony_ci for (i = 1; i < 2 * strength; i += 2) { 4688c2ecf20Sopenharmony_ci for (j = 0; j < degree; j++) { 4698c2ecf20Sopenharmony_ci if (partial_syn[i] & BIT(j)) 4708c2ecf20Sopenharmony_ci si[i] = alpha_to[i * j] ^ si[i]; 4718c2ecf20Sopenharmony_ci } 4728c2ecf20Sopenharmony_ci } 4738c2ecf20Sopenharmony_ci /* Even syndrome = (Odd syndrome) ** 2 */ 4748c2ecf20Sopenharmony_ci for (i = 2, j = 1; j <= strength; i = ++j << 1) { 4758c2ecf20Sopenharmony_ci if (si[j] == 0) { 4768c2ecf20Sopenharmony_ci si[i] = 0; 4778c2ecf20Sopenharmony_ci } else { 4788c2ecf20Sopenharmony_ci s16 tmp; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci tmp = index_of[si[j]]; 4818c2ecf20Sopenharmony_ci tmp = (tmp * 2) % cw_len; 4828c2ecf20Sopenharmony_ci si[i] = alpha_to[tmp]; 4838c2ecf20Sopenharmony_ci } 4848c2ecf20Sopenharmony_ci } 4858c2ecf20Sopenharmony_ci} 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_cistatic void atmel_pmecc_get_sigma(struct atmel_pmecc_user *user) 4888c2ecf20Sopenharmony_ci{ 4898c2ecf20Sopenharmony_ci s16 *lmu = user->lmu; 4908c2ecf20Sopenharmony_ci s16 *si = user->si; 4918c2ecf20Sopenharmony_ci s32 *mu = user->mu; 4928c2ecf20Sopenharmony_ci s32 *dmu = user->dmu; 4938c2ecf20Sopenharmony_ci s32 *delta = user->delta; 4948c2ecf20Sopenharmony_ci int degree = get_sectorsize(user) == 512 ? 13 : 14; 4958c2ecf20Sopenharmony_ci int cw_len = BIT(degree) - 1; 4968c2ecf20Sopenharmony_ci int strength = get_strength(user); 4978c2ecf20Sopenharmony_ci int num = 2 * strength + 1; 4988c2ecf20Sopenharmony_ci s16 *index_of = user->gf_tables->index_of; 4998c2ecf20Sopenharmony_ci s16 *alpha_to = user->gf_tables->alpha_to; 5008c2ecf20Sopenharmony_ci int i, j, k; 5018c2ecf20Sopenharmony_ci u32 dmu_0_count, tmp; 5028c2ecf20Sopenharmony_ci s16 *smu = user->smu; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci /* index of largest delta */ 5058c2ecf20Sopenharmony_ci int ro; 5068c2ecf20Sopenharmony_ci int largest; 5078c2ecf20Sopenharmony_ci int diff; 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci dmu_0_count = 0; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci /* First Row */ 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci /* Mu */ 5148c2ecf20Sopenharmony_ci mu[0] = -1; 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci memset(smu, 0, sizeof(s16) * num); 5178c2ecf20Sopenharmony_ci smu[0] = 1; 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci /* discrepancy set to 1 */ 5208c2ecf20Sopenharmony_ci dmu[0] = 1; 5218c2ecf20Sopenharmony_ci /* polynom order set to 0 */ 5228c2ecf20Sopenharmony_ci lmu[0] = 0; 5238c2ecf20Sopenharmony_ci delta[0] = (mu[0] * 2 - lmu[0]) >> 1; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci /* Second Row */ 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci /* Mu */ 5288c2ecf20Sopenharmony_ci mu[1] = 0; 5298c2ecf20Sopenharmony_ci /* Sigma(x) set to 1 */ 5308c2ecf20Sopenharmony_ci memset(&smu[num], 0, sizeof(s16) * num); 5318c2ecf20Sopenharmony_ci smu[num] = 1; 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci /* discrepancy set to S1 */ 5348c2ecf20Sopenharmony_ci dmu[1] = si[1]; 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci /* polynom order set to 0 */ 5378c2ecf20Sopenharmony_ci lmu[1] = 0; 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci delta[1] = (mu[1] * 2 - lmu[1]) >> 1; 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci /* Init the Sigma(x) last row */ 5428c2ecf20Sopenharmony_ci memset(&smu[(strength + 1) * num], 0, sizeof(s16) * num); 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci for (i = 1; i <= strength; i++) { 5458c2ecf20Sopenharmony_ci mu[i + 1] = i << 1; 5468c2ecf20Sopenharmony_ci /* Begin Computing Sigma (Mu+1) and L(mu) */ 5478c2ecf20Sopenharmony_ci /* check if discrepancy is set to 0 */ 5488c2ecf20Sopenharmony_ci if (dmu[i] == 0) { 5498c2ecf20Sopenharmony_ci dmu_0_count++; 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci tmp = ((strength - (lmu[i] >> 1) - 1) / 2); 5528c2ecf20Sopenharmony_ci if ((strength - (lmu[i] >> 1) - 1) & 0x1) 5538c2ecf20Sopenharmony_ci tmp += 2; 5548c2ecf20Sopenharmony_ci else 5558c2ecf20Sopenharmony_ci tmp += 1; 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci if (dmu_0_count == tmp) { 5588c2ecf20Sopenharmony_ci for (j = 0; j <= (lmu[i] >> 1) + 1; j++) 5598c2ecf20Sopenharmony_ci smu[(strength + 1) * num + j] = 5608c2ecf20Sopenharmony_ci smu[i * num + j]; 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci lmu[strength + 1] = lmu[i]; 5638c2ecf20Sopenharmony_ci return; 5648c2ecf20Sopenharmony_ci } 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci /* copy polynom */ 5678c2ecf20Sopenharmony_ci for (j = 0; j <= lmu[i] >> 1; j++) 5688c2ecf20Sopenharmony_ci smu[(i + 1) * num + j] = smu[i * num + j]; 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci /* copy previous polynom order to the next */ 5718c2ecf20Sopenharmony_ci lmu[i + 1] = lmu[i]; 5728c2ecf20Sopenharmony_ci } else { 5738c2ecf20Sopenharmony_ci ro = 0; 5748c2ecf20Sopenharmony_ci largest = -1; 5758c2ecf20Sopenharmony_ci /* find largest delta with dmu != 0 */ 5768c2ecf20Sopenharmony_ci for (j = 0; j < i; j++) { 5778c2ecf20Sopenharmony_ci if ((dmu[j]) && (delta[j] > largest)) { 5788c2ecf20Sopenharmony_ci largest = delta[j]; 5798c2ecf20Sopenharmony_ci ro = j; 5808c2ecf20Sopenharmony_ci } 5818c2ecf20Sopenharmony_ci } 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci /* compute difference */ 5848c2ecf20Sopenharmony_ci diff = (mu[i] - mu[ro]); 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci /* Compute degree of the new smu polynomial */ 5878c2ecf20Sopenharmony_ci if ((lmu[i] >> 1) > ((lmu[ro] >> 1) + diff)) 5888c2ecf20Sopenharmony_ci lmu[i + 1] = lmu[i]; 5898c2ecf20Sopenharmony_ci else 5908c2ecf20Sopenharmony_ci lmu[i + 1] = ((lmu[ro] >> 1) + diff) * 2; 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci /* Init smu[i+1] with 0 */ 5938c2ecf20Sopenharmony_ci for (k = 0; k < num; k++) 5948c2ecf20Sopenharmony_ci smu[(i + 1) * num + k] = 0; 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci /* Compute smu[i+1] */ 5978c2ecf20Sopenharmony_ci for (k = 0; k <= lmu[ro] >> 1; k++) { 5988c2ecf20Sopenharmony_ci s16 a, b, c; 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci if (!(smu[ro * num + k] && dmu[i])) 6018c2ecf20Sopenharmony_ci continue; 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci a = index_of[dmu[i]]; 6048c2ecf20Sopenharmony_ci b = index_of[dmu[ro]]; 6058c2ecf20Sopenharmony_ci c = index_of[smu[ro * num + k]]; 6068c2ecf20Sopenharmony_ci tmp = a + (cw_len - b) + c; 6078c2ecf20Sopenharmony_ci a = alpha_to[tmp % cw_len]; 6088c2ecf20Sopenharmony_ci smu[(i + 1) * num + (k + diff)] = a; 6098c2ecf20Sopenharmony_ci } 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci for (k = 0; k <= lmu[i] >> 1; k++) 6128c2ecf20Sopenharmony_ci smu[(i + 1) * num + k] ^= smu[i * num + k]; 6138c2ecf20Sopenharmony_ci } 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci /* End Computing Sigma (Mu+1) and L(mu) */ 6168c2ecf20Sopenharmony_ci /* In either case compute delta */ 6178c2ecf20Sopenharmony_ci delta[i + 1] = (mu[i + 1] * 2 - lmu[i + 1]) >> 1; 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci /* Do not compute discrepancy for the last iteration */ 6208c2ecf20Sopenharmony_ci if (i >= strength) 6218c2ecf20Sopenharmony_ci continue; 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci for (k = 0; k <= (lmu[i + 1] >> 1); k++) { 6248c2ecf20Sopenharmony_ci tmp = 2 * (i - 1); 6258c2ecf20Sopenharmony_ci if (k == 0) { 6268c2ecf20Sopenharmony_ci dmu[i + 1] = si[tmp + 3]; 6278c2ecf20Sopenharmony_ci } else if (smu[(i + 1) * num + k] && si[tmp + 3 - k]) { 6288c2ecf20Sopenharmony_ci s16 a, b, c; 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci a = index_of[smu[(i + 1) * num + k]]; 6318c2ecf20Sopenharmony_ci b = si[2 * (i - 1) + 3 - k]; 6328c2ecf20Sopenharmony_ci c = index_of[b]; 6338c2ecf20Sopenharmony_ci tmp = a + c; 6348c2ecf20Sopenharmony_ci tmp %= cw_len; 6358c2ecf20Sopenharmony_ci dmu[i + 1] = alpha_to[tmp] ^ dmu[i + 1]; 6368c2ecf20Sopenharmony_ci } 6378c2ecf20Sopenharmony_ci } 6388c2ecf20Sopenharmony_ci } 6398c2ecf20Sopenharmony_ci} 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_cistatic int atmel_pmecc_err_location(struct atmel_pmecc_user *user) 6428c2ecf20Sopenharmony_ci{ 6438c2ecf20Sopenharmony_ci int sector_size = get_sectorsize(user); 6448c2ecf20Sopenharmony_ci int degree = sector_size == 512 ? 13 : 14; 6458c2ecf20Sopenharmony_ci struct atmel_pmecc *pmecc = user->pmecc; 6468c2ecf20Sopenharmony_ci int strength = get_strength(user); 6478c2ecf20Sopenharmony_ci int ret, roots_nbr, i, err_nbr = 0; 6488c2ecf20Sopenharmony_ci int num = (2 * strength) + 1; 6498c2ecf20Sopenharmony_ci s16 *smu = user->smu; 6508c2ecf20Sopenharmony_ci u32 val; 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci writel(PMERRLOC_DISABLE, pmecc->regs.errloc + ATMEL_PMERRLOC_ELDIS); 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci for (i = 0; i <= user->lmu[strength + 1] >> 1; i++) { 6558c2ecf20Sopenharmony_ci writel_relaxed(smu[(strength + 1) * num + i], 6568c2ecf20Sopenharmony_ci pmecc->regs.errloc + ATMEL_PMERRLOC_SIGMA(i)); 6578c2ecf20Sopenharmony_ci err_nbr++; 6588c2ecf20Sopenharmony_ci } 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci val = (err_nbr - 1) << 16; 6618c2ecf20Sopenharmony_ci if (sector_size == 1024) 6628c2ecf20Sopenharmony_ci val |= 1; 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci writel(val, pmecc->regs.errloc + ATMEL_PMERRLOC_ELCFG); 6658c2ecf20Sopenharmony_ci writel((sector_size * 8) + (degree * strength), 6668c2ecf20Sopenharmony_ci pmecc->regs.errloc + ATMEL_PMERRLOC_ELEN); 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci ret = readl_relaxed_poll_timeout(pmecc->regs.errloc + 6698c2ecf20Sopenharmony_ci ATMEL_PMERRLOC_ELISR, 6708c2ecf20Sopenharmony_ci val, val & PMERRLOC_CALC_DONE, 0, 6718c2ecf20Sopenharmony_ci PMECC_MAX_TIMEOUT_MS * 1000); 6728c2ecf20Sopenharmony_ci if (ret) { 6738c2ecf20Sopenharmony_ci dev_err(pmecc->dev, 6748c2ecf20Sopenharmony_ci "PMECC: Timeout to calculate error location.\n"); 6758c2ecf20Sopenharmony_ci return ret; 6768c2ecf20Sopenharmony_ci } 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci roots_nbr = (val & PMERRLOC_ERR_NUM_MASK) >> 8; 6798c2ecf20Sopenharmony_ci /* Number of roots == degree of smu hence <= cap */ 6808c2ecf20Sopenharmony_ci if (roots_nbr == user->lmu[strength + 1] >> 1) 6818c2ecf20Sopenharmony_ci return err_nbr - 1; 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci /* 6848c2ecf20Sopenharmony_ci * Number of roots does not match the degree of smu 6858c2ecf20Sopenharmony_ci * unable to correct error. 6868c2ecf20Sopenharmony_ci */ 6878c2ecf20Sopenharmony_ci return -EBADMSG; 6888c2ecf20Sopenharmony_ci} 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ciint atmel_pmecc_correct_sector(struct atmel_pmecc_user *user, int sector, 6918c2ecf20Sopenharmony_ci void *data, void *ecc) 6928c2ecf20Sopenharmony_ci{ 6938c2ecf20Sopenharmony_ci struct atmel_pmecc *pmecc = user->pmecc; 6948c2ecf20Sopenharmony_ci int sectorsize = get_sectorsize(user); 6958c2ecf20Sopenharmony_ci int eccbytes = user->eccbytes; 6968c2ecf20Sopenharmony_ci int i, nerrors; 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci if (!(user->isr & BIT(sector))) 6998c2ecf20Sopenharmony_ci return 0; 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci atmel_pmecc_gen_syndrome(user, sector); 7028c2ecf20Sopenharmony_ci atmel_pmecc_substitute(user); 7038c2ecf20Sopenharmony_ci atmel_pmecc_get_sigma(user); 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci nerrors = atmel_pmecc_err_location(user); 7068c2ecf20Sopenharmony_ci if (nerrors < 0) 7078c2ecf20Sopenharmony_ci return nerrors; 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci for (i = 0; i < nerrors; i++) { 7108c2ecf20Sopenharmony_ci const char *area; 7118c2ecf20Sopenharmony_ci int byte, bit; 7128c2ecf20Sopenharmony_ci u32 errpos; 7138c2ecf20Sopenharmony_ci u8 *ptr; 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci errpos = readl_relaxed(pmecc->regs.errloc + 7168c2ecf20Sopenharmony_ci ATMEL_PMERRLOC_EL(pmecc->caps->el_offset, i)); 7178c2ecf20Sopenharmony_ci errpos--; 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci byte = errpos / 8; 7208c2ecf20Sopenharmony_ci bit = errpos % 8; 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci if (byte < sectorsize) { 7238c2ecf20Sopenharmony_ci ptr = data + byte; 7248c2ecf20Sopenharmony_ci area = "data"; 7258c2ecf20Sopenharmony_ci } else if (byte < sectorsize + eccbytes) { 7268c2ecf20Sopenharmony_ci ptr = ecc + byte - sectorsize; 7278c2ecf20Sopenharmony_ci area = "ECC"; 7288c2ecf20Sopenharmony_ci } else { 7298c2ecf20Sopenharmony_ci dev_dbg(pmecc->dev, 7308c2ecf20Sopenharmony_ci "Invalid errpos value (%d, max is %d)\n", 7318c2ecf20Sopenharmony_ci errpos, (sectorsize + eccbytes) * 8); 7328c2ecf20Sopenharmony_ci return -EINVAL; 7338c2ecf20Sopenharmony_ci } 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci dev_dbg(pmecc->dev, 7368c2ecf20Sopenharmony_ci "Bit flip in %s area, byte %d: 0x%02x -> 0x%02x\n", 7378c2ecf20Sopenharmony_ci area, byte, *ptr, (unsigned int)(*ptr ^ BIT(bit))); 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci *ptr ^= BIT(bit); 7408c2ecf20Sopenharmony_ci } 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci return nerrors; 7438c2ecf20Sopenharmony_ci} 7448c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(atmel_pmecc_correct_sector); 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_cibool atmel_pmecc_correct_erased_chunks(struct atmel_pmecc_user *user) 7478c2ecf20Sopenharmony_ci{ 7488c2ecf20Sopenharmony_ci return user->pmecc->caps->correct_erased_chunks; 7498c2ecf20Sopenharmony_ci} 7508c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(atmel_pmecc_correct_erased_chunks); 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_civoid atmel_pmecc_get_generated_eccbytes(struct atmel_pmecc_user *user, 7538c2ecf20Sopenharmony_ci int sector, void *ecc) 7548c2ecf20Sopenharmony_ci{ 7558c2ecf20Sopenharmony_ci struct atmel_pmecc *pmecc = user->pmecc; 7568c2ecf20Sopenharmony_ci u8 *ptr = ecc; 7578c2ecf20Sopenharmony_ci int i; 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci for (i = 0; i < user->eccbytes; i++) 7608c2ecf20Sopenharmony_ci ptr[i] = readb_relaxed(pmecc->regs.base + 7618c2ecf20Sopenharmony_ci ATMEL_PMECC_ECC(sector, i)); 7628c2ecf20Sopenharmony_ci} 7638c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(atmel_pmecc_get_generated_eccbytes); 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_civoid atmel_pmecc_reset(struct atmel_pmecc *pmecc) 7668c2ecf20Sopenharmony_ci{ 7678c2ecf20Sopenharmony_ci writel(PMECC_CTRL_RST, pmecc->regs.base + ATMEL_PMECC_CTRL); 7688c2ecf20Sopenharmony_ci writel(PMECC_CTRL_DISABLE, pmecc->regs.base + ATMEL_PMECC_CTRL); 7698c2ecf20Sopenharmony_ci} 7708c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(atmel_pmecc_reset); 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ciint atmel_pmecc_enable(struct atmel_pmecc_user *user, int op) 7738c2ecf20Sopenharmony_ci{ 7748c2ecf20Sopenharmony_ci struct atmel_pmecc *pmecc = user->pmecc; 7758c2ecf20Sopenharmony_ci u32 cfg; 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci if (op != NAND_ECC_READ && op != NAND_ECC_WRITE) { 7788c2ecf20Sopenharmony_ci dev_err(pmecc->dev, "Bad ECC operation!"); 7798c2ecf20Sopenharmony_ci return -EINVAL; 7808c2ecf20Sopenharmony_ci } 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci mutex_lock(&user->pmecc->lock); 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci cfg = user->cache.cfg; 7858c2ecf20Sopenharmony_ci if (op == NAND_ECC_WRITE) 7868c2ecf20Sopenharmony_ci cfg |= PMECC_CFG_WRITE_OP; 7878c2ecf20Sopenharmony_ci else 7888c2ecf20Sopenharmony_ci cfg |= PMECC_CFG_AUTO_ENABLE; 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci writel(cfg, pmecc->regs.base + ATMEL_PMECC_CFG); 7918c2ecf20Sopenharmony_ci writel(user->cache.sarea, pmecc->regs.base + ATMEL_PMECC_SAREA); 7928c2ecf20Sopenharmony_ci writel(user->cache.saddr, pmecc->regs.base + ATMEL_PMECC_SADDR); 7938c2ecf20Sopenharmony_ci writel(user->cache.eaddr, pmecc->regs.base + ATMEL_PMECC_EADDR); 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci writel(PMECC_CTRL_ENABLE, pmecc->regs.base + ATMEL_PMECC_CTRL); 7968c2ecf20Sopenharmony_ci writel(PMECC_CTRL_DATA, pmecc->regs.base + ATMEL_PMECC_CTRL); 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci return 0; 7998c2ecf20Sopenharmony_ci} 8008c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(atmel_pmecc_enable); 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_civoid atmel_pmecc_disable(struct atmel_pmecc_user *user) 8038c2ecf20Sopenharmony_ci{ 8048c2ecf20Sopenharmony_ci atmel_pmecc_reset(user->pmecc); 8058c2ecf20Sopenharmony_ci mutex_unlock(&user->pmecc->lock); 8068c2ecf20Sopenharmony_ci} 8078c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(atmel_pmecc_disable); 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ciint atmel_pmecc_wait_rdy(struct atmel_pmecc_user *user) 8108c2ecf20Sopenharmony_ci{ 8118c2ecf20Sopenharmony_ci struct atmel_pmecc *pmecc = user->pmecc; 8128c2ecf20Sopenharmony_ci u32 status; 8138c2ecf20Sopenharmony_ci int ret; 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci ret = readl_relaxed_poll_timeout(pmecc->regs.base + 8168c2ecf20Sopenharmony_ci ATMEL_PMECC_SR, 8178c2ecf20Sopenharmony_ci status, !(status & PMECC_SR_BUSY), 0, 8188c2ecf20Sopenharmony_ci PMECC_MAX_TIMEOUT_MS * 1000); 8198c2ecf20Sopenharmony_ci if (ret) { 8208c2ecf20Sopenharmony_ci dev_err(pmecc->dev, 8218c2ecf20Sopenharmony_ci "Timeout while waiting for PMECC ready.\n"); 8228c2ecf20Sopenharmony_ci return ret; 8238c2ecf20Sopenharmony_ci } 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_ci user->isr = readl_relaxed(pmecc->regs.base + ATMEL_PMECC_ISR); 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci return 0; 8288c2ecf20Sopenharmony_ci} 8298c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(atmel_pmecc_wait_rdy); 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_cistatic struct atmel_pmecc *atmel_pmecc_create(struct platform_device *pdev, 8328c2ecf20Sopenharmony_ci const struct atmel_pmecc_caps *caps, 8338c2ecf20Sopenharmony_ci int pmecc_res_idx, int errloc_res_idx) 8348c2ecf20Sopenharmony_ci{ 8358c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 8368c2ecf20Sopenharmony_ci struct atmel_pmecc *pmecc; 8378c2ecf20Sopenharmony_ci struct resource *res; 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci pmecc = devm_kzalloc(dev, sizeof(*pmecc), GFP_KERNEL); 8408c2ecf20Sopenharmony_ci if (!pmecc) 8418c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ci pmecc->caps = caps; 8448c2ecf20Sopenharmony_ci pmecc->dev = dev; 8458c2ecf20Sopenharmony_ci mutex_init(&pmecc->lock); 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, pmecc_res_idx); 8488c2ecf20Sopenharmony_ci pmecc->regs.base = devm_ioremap_resource(dev, res); 8498c2ecf20Sopenharmony_ci if (IS_ERR(pmecc->regs.base)) 8508c2ecf20Sopenharmony_ci return ERR_CAST(pmecc->regs.base); 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, errloc_res_idx); 8538c2ecf20Sopenharmony_ci pmecc->regs.errloc = devm_ioremap_resource(dev, res); 8548c2ecf20Sopenharmony_ci if (IS_ERR(pmecc->regs.errloc)) 8558c2ecf20Sopenharmony_ci return ERR_CAST(pmecc->regs.errloc); 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci /* Disable all interrupts before registering the PMECC handler. */ 8588c2ecf20Sopenharmony_ci writel(0xffffffff, pmecc->regs.base + ATMEL_PMECC_IDR); 8598c2ecf20Sopenharmony_ci atmel_pmecc_reset(pmecc); 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci return pmecc; 8628c2ecf20Sopenharmony_ci} 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_cistatic void devm_atmel_pmecc_put(struct device *dev, void *res) 8658c2ecf20Sopenharmony_ci{ 8668c2ecf20Sopenharmony_ci struct atmel_pmecc **pmecc = res; 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci put_device((*pmecc)->dev); 8698c2ecf20Sopenharmony_ci} 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_cistatic struct atmel_pmecc *atmel_pmecc_get_by_node(struct device *userdev, 8728c2ecf20Sopenharmony_ci struct device_node *np) 8738c2ecf20Sopenharmony_ci{ 8748c2ecf20Sopenharmony_ci struct platform_device *pdev; 8758c2ecf20Sopenharmony_ci struct atmel_pmecc *pmecc, **ptr; 8768c2ecf20Sopenharmony_ci int ret; 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_ci pdev = of_find_device_by_node(np); 8798c2ecf20Sopenharmony_ci if (!pdev) 8808c2ecf20Sopenharmony_ci return ERR_PTR(-EPROBE_DEFER); 8818c2ecf20Sopenharmony_ci pmecc = platform_get_drvdata(pdev); 8828c2ecf20Sopenharmony_ci if (!pmecc) { 8838c2ecf20Sopenharmony_ci ret = -EPROBE_DEFER; 8848c2ecf20Sopenharmony_ci goto err_put_device; 8858c2ecf20Sopenharmony_ci } 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci ptr = devres_alloc(devm_atmel_pmecc_put, sizeof(*ptr), GFP_KERNEL); 8888c2ecf20Sopenharmony_ci if (!ptr) { 8898c2ecf20Sopenharmony_ci ret = -ENOMEM; 8908c2ecf20Sopenharmony_ci goto err_put_device; 8918c2ecf20Sopenharmony_ci } 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci *ptr = pmecc; 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci devres_add(userdev, ptr); 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci return pmecc; 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_cierr_put_device: 9008c2ecf20Sopenharmony_ci put_device(&pdev->dev); 9018c2ecf20Sopenharmony_ci return ERR_PTR(ret); 9028c2ecf20Sopenharmony_ci} 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_cistatic const int atmel_pmecc_strengths[] = { 2, 4, 8, 12, 24, 32 }; 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_cistatic struct atmel_pmecc_caps at91sam9g45_caps = { 9078c2ecf20Sopenharmony_ci .strengths = atmel_pmecc_strengths, 9088c2ecf20Sopenharmony_ci .nstrengths = 5, 9098c2ecf20Sopenharmony_ci .el_offset = 0x8c, 9108c2ecf20Sopenharmony_ci}; 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_cistatic struct atmel_pmecc_caps sama5d4_caps = { 9138c2ecf20Sopenharmony_ci .strengths = atmel_pmecc_strengths, 9148c2ecf20Sopenharmony_ci .nstrengths = 5, 9158c2ecf20Sopenharmony_ci .el_offset = 0x8c, 9168c2ecf20Sopenharmony_ci .correct_erased_chunks = true, 9178c2ecf20Sopenharmony_ci}; 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_cistatic struct atmel_pmecc_caps sama5d2_caps = { 9208c2ecf20Sopenharmony_ci .strengths = atmel_pmecc_strengths, 9218c2ecf20Sopenharmony_ci .nstrengths = 6, 9228c2ecf20Sopenharmony_ci .el_offset = 0xac, 9238c2ecf20Sopenharmony_ci .correct_erased_chunks = true, 9248c2ecf20Sopenharmony_ci}; 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_cistatic const struct of_device_id atmel_pmecc_legacy_match[] = { 9278c2ecf20Sopenharmony_ci { .compatible = "atmel,sama5d4-nand", &sama5d4_caps }, 9288c2ecf20Sopenharmony_ci { .compatible = "atmel,sama5d2-nand", &sama5d2_caps }, 9298c2ecf20Sopenharmony_ci { /* sentinel */ } 9308c2ecf20Sopenharmony_ci}; 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_cistruct atmel_pmecc *devm_atmel_pmecc_get(struct device *userdev) 9338c2ecf20Sopenharmony_ci{ 9348c2ecf20Sopenharmony_ci struct atmel_pmecc *pmecc; 9358c2ecf20Sopenharmony_ci struct device_node *np; 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci if (!userdev) 9388c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ci if (!userdev->of_node) 9418c2ecf20Sopenharmony_ci return NULL; 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci np = of_parse_phandle(userdev->of_node, "ecc-engine", 0); 9448c2ecf20Sopenharmony_ci if (np) { 9458c2ecf20Sopenharmony_ci pmecc = atmel_pmecc_get_by_node(userdev, np); 9468c2ecf20Sopenharmony_ci of_node_put(np); 9478c2ecf20Sopenharmony_ci } else { 9488c2ecf20Sopenharmony_ci /* 9498c2ecf20Sopenharmony_ci * Support old DT bindings: in this case the PMECC iomem 9508c2ecf20Sopenharmony_ci * resources are directly defined in the user pdev at position 9518c2ecf20Sopenharmony_ci * 1 and 2. Extract all relevant information from there. 9528c2ecf20Sopenharmony_ci */ 9538c2ecf20Sopenharmony_ci struct platform_device *pdev = to_platform_device(userdev); 9548c2ecf20Sopenharmony_ci const struct atmel_pmecc_caps *caps; 9558c2ecf20Sopenharmony_ci const struct of_device_id *match; 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_ci /* No PMECC engine available. */ 9588c2ecf20Sopenharmony_ci if (!of_property_read_bool(userdev->of_node, 9598c2ecf20Sopenharmony_ci "atmel,has-pmecc")) 9608c2ecf20Sopenharmony_ci return NULL; 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_ci caps = &at91sam9g45_caps; 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci /* Find the caps associated to the NAND dev node. */ 9658c2ecf20Sopenharmony_ci match = of_match_node(atmel_pmecc_legacy_match, 9668c2ecf20Sopenharmony_ci userdev->of_node); 9678c2ecf20Sopenharmony_ci if (match && match->data) 9688c2ecf20Sopenharmony_ci caps = match->data; 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ci pmecc = atmel_pmecc_create(pdev, caps, 1, 2); 9718c2ecf20Sopenharmony_ci } 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_ci return pmecc; 9748c2ecf20Sopenharmony_ci} 9758c2ecf20Sopenharmony_ciEXPORT_SYMBOL(devm_atmel_pmecc_get); 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_cistatic const struct of_device_id atmel_pmecc_match[] = { 9788c2ecf20Sopenharmony_ci { .compatible = "atmel,at91sam9g45-pmecc", &at91sam9g45_caps }, 9798c2ecf20Sopenharmony_ci { .compatible = "atmel,sama5d4-pmecc", &sama5d4_caps }, 9808c2ecf20Sopenharmony_ci { .compatible = "atmel,sama5d2-pmecc", &sama5d2_caps }, 9818c2ecf20Sopenharmony_ci { /* sentinel */ } 9828c2ecf20Sopenharmony_ci}; 9838c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, atmel_pmecc_match); 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_cistatic int atmel_pmecc_probe(struct platform_device *pdev) 9868c2ecf20Sopenharmony_ci{ 9878c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 9888c2ecf20Sopenharmony_ci const struct atmel_pmecc_caps *caps; 9898c2ecf20Sopenharmony_ci struct atmel_pmecc *pmecc; 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_ci caps = of_device_get_match_data(&pdev->dev); 9928c2ecf20Sopenharmony_ci if (!caps) { 9938c2ecf20Sopenharmony_ci dev_err(dev, "Invalid caps\n"); 9948c2ecf20Sopenharmony_ci return -EINVAL; 9958c2ecf20Sopenharmony_ci } 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ci pmecc = atmel_pmecc_create(pdev, caps, 0, 1); 9988c2ecf20Sopenharmony_ci if (IS_ERR(pmecc)) 9998c2ecf20Sopenharmony_ci return PTR_ERR(pmecc); 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, pmecc); 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_ci return 0; 10048c2ecf20Sopenharmony_ci} 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_cistatic struct platform_driver atmel_pmecc_driver = { 10078c2ecf20Sopenharmony_ci .driver = { 10088c2ecf20Sopenharmony_ci .name = "atmel-pmecc", 10098c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(atmel_pmecc_match), 10108c2ecf20Sopenharmony_ci }, 10118c2ecf20Sopenharmony_ci .probe = atmel_pmecc_probe, 10128c2ecf20Sopenharmony_ci}; 10138c2ecf20Sopenharmony_cimodule_platform_driver(atmel_pmecc_driver); 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 10168c2ecf20Sopenharmony_ciMODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>"); 10178c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("PMECC engine driver"); 10188c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:atmel_pmecc"); 1019