18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * This file provides ECC correction for more than 1 bit per block of data, 48c2ecf20Sopenharmony_ci * using binary BCH codes. It relies on the generic BCH library lib/bch.c. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright © 2011 Ivan Djelic <ivan.djelic@parrot.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/types.h> 108c2ecf20Sopenharmony_ci#include <linux/kernel.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/slab.h> 138c2ecf20Sopenharmony_ci#include <linux/bitops.h> 148c2ecf20Sopenharmony_ci#include <linux/mtd/mtd.h> 158c2ecf20Sopenharmony_ci#include <linux/mtd/rawnand.h> 168c2ecf20Sopenharmony_ci#include <linux/mtd/nand_bch.h> 178c2ecf20Sopenharmony_ci#include <linux/bch.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci/** 208c2ecf20Sopenharmony_ci * struct nand_bch_control - private NAND BCH control structure 218c2ecf20Sopenharmony_ci * @bch: BCH control structure 228c2ecf20Sopenharmony_ci * @errloc: error location array 238c2ecf20Sopenharmony_ci * @eccmask: XOR ecc mask, allows erased pages to be decoded as valid 248c2ecf20Sopenharmony_ci */ 258c2ecf20Sopenharmony_cistruct nand_bch_control { 268c2ecf20Sopenharmony_ci struct bch_control *bch; 278c2ecf20Sopenharmony_ci unsigned int *errloc; 288c2ecf20Sopenharmony_ci unsigned char *eccmask; 298c2ecf20Sopenharmony_ci}; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci/** 328c2ecf20Sopenharmony_ci * nand_bch_calculate_ecc - [NAND Interface] Calculate ECC for data block 338c2ecf20Sopenharmony_ci * @chip: NAND chip object 348c2ecf20Sopenharmony_ci * @buf: input buffer with raw data 358c2ecf20Sopenharmony_ci * @code: output buffer with ECC 368c2ecf20Sopenharmony_ci */ 378c2ecf20Sopenharmony_ciint nand_bch_calculate_ecc(struct nand_chip *chip, const unsigned char *buf, 388c2ecf20Sopenharmony_ci unsigned char *code) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci struct nand_bch_control *nbc = chip->ecc.priv; 418c2ecf20Sopenharmony_ci unsigned int i; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci memset(code, 0, chip->ecc.bytes); 448c2ecf20Sopenharmony_ci bch_encode(nbc->bch, buf, chip->ecc.size, code); 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci /* apply mask so that an erased page is a valid codeword */ 478c2ecf20Sopenharmony_ci for (i = 0; i < chip->ecc.bytes; i++) 488c2ecf20Sopenharmony_ci code[i] ^= nbc->eccmask[i]; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci return 0; 518c2ecf20Sopenharmony_ci} 528c2ecf20Sopenharmony_ciEXPORT_SYMBOL(nand_bch_calculate_ecc); 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci/** 558c2ecf20Sopenharmony_ci * nand_bch_correct_data - [NAND Interface] Detect and correct bit error(s) 568c2ecf20Sopenharmony_ci * @chip: NAND chip object 578c2ecf20Sopenharmony_ci * @buf: raw data read from the chip 588c2ecf20Sopenharmony_ci * @read_ecc: ECC from the chip 598c2ecf20Sopenharmony_ci * @calc_ecc: the ECC calculated from raw data 608c2ecf20Sopenharmony_ci * 618c2ecf20Sopenharmony_ci * Detect and correct bit errors for a data byte block 628c2ecf20Sopenharmony_ci */ 638c2ecf20Sopenharmony_ciint nand_bch_correct_data(struct nand_chip *chip, unsigned char *buf, 648c2ecf20Sopenharmony_ci unsigned char *read_ecc, unsigned char *calc_ecc) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci struct nand_bch_control *nbc = chip->ecc.priv; 678c2ecf20Sopenharmony_ci unsigned int *errloc = nbc->errloc; 688c2ecf20Sopenharmony_ci int i, count; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci count = bch_decode(nbc->bch, NULL, chip->ecc.size, read_ecc, calc_ecc, 718c2ecf20Sopenharmony_ci NULL, errloc); 728c2ecf20Sopenharmony_ci if (count > 0) { 738c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) { 748c2ecf20Sopenharmony_ci if (errloc[i] < (chip->ecc.size*8)) 758c2ecf20Sopenharmony_ci /* error is located in data, correct it */ 768c2ecf20Sopenharmony_ci buf[errloc[i] >> 3] ^= (1 << (errloc[i] & 7)); 778c2ecf20Sopenharmony_ci /* else error in ecc, no action needed */ 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci pr_debug("%s: corrected bitflip %u\n", __func__, 808c2ecf20Sopenharmony_ci errloc[i]); 818c2ecf20Sopenharmony_ci } 828c2ecf20Sopenharmony_ci } else if (count < 0) { 838c2ecf20Sopenharmony_ci pr_err("ecc unrecoverable error\n"); 848c2ecf20Sopenharmony_ci count = -EBADMSG; 858c2ecf20Sopenharmony_ci } 868c2ecf20Sopenharmony_ci return count; 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ciEXPORT_SYMBOL(nand_bch_correct_data); 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci/** 918c2ecf20Sopenharmony_ci * nand_bch_init - [NAND Interface] Initialize NAND BCH error correction 928c2ecf20Sopenharmony_ci * @mtd: MTD block structure 938c2ecf20Sopenharmony_ci * 948c2ecf20Sopenharmony_ci * Returns: 958c2ecf20Sopenharmony_ci * a pointer to a new NAND BCH control structure, or NULL upon failure 968c2ecf20Sopenharmony_ci * 978c2ecf20Sopenharmony_ci * Initialize NAND BCH error correction. Parameters @eccsize and @eccbytes 988c2ecf20Sopenharmony_ci * are used to compute BCH parameters m (Galois field order) and t (error 998c2ecf20Sopenharmony_ci * correction capability). @eccbytes should be equal to the number of bytes 1008c2ecf20Sopenharmony_ci * required to store m*t bits, where m is such that 2^m-1 > @eccsize*8. 1018c2ecf20Sopenharmony_ci * 1028c2ecf20Sopenharmony_ci * Example: to configure 4 bit correction per 512 bytes, you should pass 1038c2ecf20Sopenharmony_ci * @eccsize = 512 (thus, m=13 is the smallest integer such that 2^m-1 > 512*8) 1048c2ecf20Sopenharmony_ci * @eccbytes = 7 (7 bytes are required to store m*t = 13*4 = 52 bits) 1058c2ecf20Sopenharmony_ci */ 1068c2ecf20Sopenharmony_cistruct nand_bch_control *nand_bch_init(struct mtd_info *mtd) 1078c2ecf20Sopenharmony_ci{ 1088c2ecf20Sopenharmony_ci struct nand_chip *nand = mtd_to_nand(mtd); 1098c2ecf20Sopenharmony_ci unsigned int m, t, eccsteps, i; 1108c2ecf20Sopenharmony_ci struct nand_bch_control *nbc = NULL; 1118c2ecf20Sopenharmony_ci unsigned char *erased_page; 1128c2ecf20Sopenharmony_ci unsigned int eccsize = nand->ecc.size; 1138c2ecf20Sopenharmony_ci unsigned int eccbytes = nand->ecc.bytes; 1148c2ecf20Sopenharmony_ci unsigned int eccstrength = nand->ecc.strength; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci if (!eccbytes && eccstrength) { 1178c2ecf20Sopenharmony_ci eccbytes = DIV_ROUND_UP(eccstrength * fls(8 * eccsize), 8); 1188c2ecf20Sopenharmony_ci nand->ecc.bytes = eccbytes; 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci if (!eccsize || !eccbytes) { 1228c2ecf20Sopenharmony_ci pr_warn("ecc parameters not supplied\n"); 1238c2ecf20Sopenharmony_ci goto fail; 1248c2ecf20Sopenharmony_ci } 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci m = fls(1+8*eccsize); 1278c2ecf20Sopenharmony_ci t = (eccbytes*8)/m; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci nbc = kzalloc(sizeof(*nbc), GFP_KERNEL); 1308c2ecf20Sopenharmony_ci if (!nbc) 1318c2ecf20Sopenharmony_ci goto fail; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci nbc->bch = bch_init(m, t, 0, false); 1348c2ecf20Sopenharmony_ci if (!nbc->bch) 1358c2ecf20Sopenharmony_ci goto fail; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci /* verify that eccbytes has the expected value */ 1388c2ecf20Sopenharmony_ci if (nbc->bch->ecc_bytes != eccbytes) { 1398c2ecf20Sopenharmony_ci pr_warn("invalid eccbytes %u, should be %u\n", 1408c2ecf20Sopenharmony_ci eccbytes, nbc->bch->ecc_bytes); 1418c2ecf20Sopenharmony_ci goto fail; 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci eccsteps = mtd->writesize/eccsize; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci /* Check that we have an oob layout description. */ 1478c2ecf20Sopenharmony_ci if (!mtd->ooblayout) { 1488c2ecf20Sopenharmony_ci pr_warn("missing oob scheme"); 1498c2ecf20Sopenharmony_ci goto fail; 1508c2ecf20Sopenharmony_ci } 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci /* sanity checks */ 1538c2ecf20Sopenharmony_ci if (8*(eccsize+eccbytes) >= (1 << m)) { 1548c2ecf20Sopenharmony_ci pr_warn("eccsize %u is too large\n", eccsize); 1558c2ecf20Sopenharmony_ci goto fail; 1568c2ecf20Sopenharmony_ci } 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci /* 1598c2ecf20Sopenharmony_ci * ecc->steps and ecc->total might be used by mtd->ooblayout->ecc(), 1608c2ecf20Sopenharmony_ci * which is called by mtd_ooblayout_count_eccbytes(). 1618c2ecf20Sopenharmony_ci * Make sure they are properly initialized before calling 1628c2ecf20Sopenharmony_ci * mtd_ooblayout_count_eccbytes(). 1638c2ecf20Sopenharmony_ci * FIXME: we should probably rework the sequencing in nand_scan_tail() 1648c2ecf20Sopenharmony_ci * to avoid setting those fields twice. 1658c2ecf20Sopenharmony_ci */ 1668c2ecf20Sopenharmony_ci nand->ecc.steps = eccsteps; 1678c2ecf20Sopenharmony_ci nand->ecc.total = eccsteps * eccbytes; 1688c2ecf20Sopenharmony_ci nand->base.ecc.ctx.total = nand->ecc.total; 1698c2ecf20Sopenharmony_ci if (mtd_ooblayout_count_eccbytes(mtd) != (eccsteps*eccbytes)) { 1708c2ecf20Sopenharmony_ci pr_warn("invalid ecc layout\n"); 1718c2ecf20Sopenharmony_ci goto fail; 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci nbc->eccmask = kzalloc(eccbytes, GFP_KERNEL); 1758c2ecf20Sopenharmony_ci nbc->errloc = kmalloc_array(t, sizeof(*nbc->errloc), GFP_KERNEL); 1768c2ecf20Sopenharmony_ci if (!nbc->eccmask || !nbc->errloc) 1778c2ecf20Sopenharmony_ci goto fail; 1788c2ecf20Sopenharmony_ci /* 1798c2ecf20Sopenharmony_ci * compute and store the inverted ecc of an erased ecc block 1808c2ecf20Sopenharmony_ci */ 1818c2ecf20Sopenharmony_ci erased_page = kmalloc(eccsize, GFP_KERNEL); 1828c2ecf20Sopenharmony_ci if (!erased_page) 1838c2ecf20Sopenharmony_ci goto fail; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci memset(erased_page, 0xff, eccsize); 1868c2ecf20Sopenharmony_ci bch_encode(nbc->bch, erased_page, eccsize, nbc->eccmask); 1878c2ecf20Sopenharmony_ci kfree(erased_page); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci for (i = 0; i < eccbytes; i++) 1908c2ecf20Sopenharmony_ci nbc->eccmask[i] ^= 0xff; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci if (!eccstrength) 1938c2ecf20Sopenharmony_ci nand->ecc.strength = (eccbytes * 8) / fls(8 * eccsize); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci return nbc; 1968c2ecf20Sopenharmony_cifail: 1978c2ecf20Sopenharmony_ci nand_bch_free(nbc); 1988c2ecf20Sopenharmony_ci return NULL; 1998c2ecf20Sopenharmony_ci} 2008c2ecf20Sopenharmony_ciEXPORT_SYMBOL(nand_bch_init); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci/** 2038c2ecf20Sopenharmony_ci * nand_bch_free - [NAND Interface] Release NAND BCH ECC resources 2048c2ecf20Sopenharmony_ci * @nbc: NAND BCH control structure 2058c2ecf20Sopenharmony_ci */ 2068c2ecf20Sopenharmony_civoid nand_bch_free(struct nand_bch_control *nbc) 2078c2ecf20Sopenharmony_ci{ 2088c2ecf20Sopenharmony_ci if (nbc) { 2098c2ecf20Sopenharmony_ci bch_free(nbc->bch); 2108c2ecf20Sopenharmony_ci kfree(nbc->errloc); 2118c2ecf20Sopenharmony_ci kfree(nbc->eccmask); 2128c2ecf20Sopenharmony_ci kfree(nbc); 2138c2ecf20Sopenharmony_ci } 2148c2ecf20Sopenharmony_ci} 2158c2ecf20Sopenharmony_ciEXPORT_SYMBOL(nand_bch_free); 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 2188c2ecf20Sopenharmony_ciMODULE_AUTHOR("Ivan Djelic <ivan.djelic@parrot.com>"); 2198c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("NAND software BCH ECC support"); 220