Based on 1 normalized pattern(s): this file is free software you can redistribute it and or modify it under the terms of the gnu general public license as published by the free software foundation either version 2 or at your option any later version this file is distributed in the hope that it will be useful but without any warranty without even the implied warranty of merchantability or fitness for a particular purpose see the gnu general public license for more details you should have received a copy of the gnu general public license along with this file if not write to the free software foundation inc 59 temple place suite 330 boston ma 02111 1307 usa extracted by the scancode license scanner the SPDX license identifier GPL-2.0-or-later has been chosen to replace the boilerplate/reference in 2 file(s). Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Reviewed-by: Kate Stewart <kstewart@linuxfoundation.org> Reviewed-by: Allison Randal <allison@lohutok.net> Cc: linux-spdx@vger.kernel.org Link: https://lkml.kernel.org/r/20190520170858.735098607@linutronix.de Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
220 lines
6.0 KiB
C
220 lines
6.0 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* This file provides ECC correction for more than 1 bit per block of data,
|
|
* using binary BCH codes. It relies on the generic BCH library lib/bch.c.
|
|
*
|
|
* Copyright © 2011 Ivan Djelic <ivan.djelic@parrot.com>
|
|
*/
|
|
|
|
#include <linux/types.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/bitops.h>
|
|
#include <linux/mtd/mtd.h>
|
|
#include <linux/mtd/rawnand.h>
|
|
#include <linux/mtd/nand_bch.h>
|
|
#include <linux/bch.h>
|
|
|
|
/**
|
|
* struct nand_bch_control - private NAND BCH control structure
|
|
* @bch: BCH control structure
|
|
* @errloc: error location array
|
|
* @eccmask: XOR ecc mask, allows erased pages to be decoded as valid
|
|
*/
|
|
struct nand_bch_control {
|
|
struct bch_control *bch;
|
|
unsigned int *errloc;
|
|
unsigned char *eccmask;
|
|
};
|
|
|
|
/**
|
|
* nand_bch_calculate_ecc - [NAND Interface] Calculate ECC for data block
|
|
* @chip: NAND chip object
|
|
* @buf: input buffer with raw data
|
|
* @code: output buffer with ECC
|
|
*/
|
|
int nand_bch_calculate_ecc(struct nand_chip *chip, const unsigned char *buf,
|
|
unsigned char *code)
|
|
{
|
|
struct nand_bch_control *nbc = chip->ecc.priv;
|
|
unsigned int i;
|
|
|
|
memset(code, 0, chip->ecc.bytes);
|
|
encode_bch(nbc->bch, buf, chip->ecc.size, code);
|
|
|
|
/* apply mask so that an erased page is a valid codeword */
|
|
for (i = 0; i < chip->ecc.bytes; i++)
|
|
code[i] ^= nbc->eccmask[i];
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(nand_bch_calculate_ecc);
|
|
|
|
/**
|
|
* nand_bch_correct_data - [NAND Interface] Detect and correct bit error(s)
|
|
* @chip: NAND chip object
|
|
* @buf: raw data read from the chip
|
|
* @read_ecc: ECC from the chip
|
|
* @calc_ecc: the ECC calculated from raw data
|
|
*
|
|
* Detect and correct bit errors for a data byte block
|
|
*/
|
|
int nand_bch_correct_data(struct nand_chip *chip, unsigned char *buf,
|
|
unsigned char *read_ecc, unsigned char *calc_ecc)
|
|
{
|
|
struct nand_bch_control *nbc = chip->ecc.priv;
|
|
unsigned int *errloc = nbc->errloc;
|
|
int i, count;
|
|
|
|
count = decode_bch(nbc->bch, NULL, chip->ecc.size, read_ecc, calc_ecc,
|
|
NULL, errloc);
|
|
if (count > 0) {
|
|
for (i = 0; i < count; i++) {
|
|
if (errloc[i] < (chip->ecc.size*8))
|
|
/* error is located in data, correct it */
|
|
buf[errloc[i] >> 3] ^= (1 << (errloc[i] & 7));
|
|
/* else error in ecc, no action needed */
|
|
|
|
pr_debug("%s: corrected bitflip %u\n", __func__,
|
|
errloc[i]);
|
|
}
|
|
} else if (count < 0) {
|
|
pr_err("ecc unrecoverable error\n");
|
|
count = -EBADMSG;
|
|
}
|
|
return count;
|
|
}
|
|
EXPORT_SYMBOL(nand_bch_correct_data);
|
|
|
|
/**
|
|
* nand_bch_init - [NAND Interface] Initialize NAND BCH error correction
|
|
* @mtd: MTD block structure
|
|
*
|
|
* Returns:
|
|
* a pointer to a new NAND BCH control structure, or NULL upon failure
|
|
*
|
|
* Initialize NAND BCH error correction. Parameters @eccsize and @eccbytes
|
|
* are used to compute BCH parameters m (Galois field order) and t (error
|
|
* correction capability). @eccbytes should be equal to the number of bytes
|
|
* required to store m*t bits, where m is such that 2^m-1 > @eccsize*8.
|
|
*
|
|
* Example: to configure 4 bit correction per 512 bytes, you should pass
|
|
* @eccsize = 512 (thus, m=13 is the smallest integer such that 2^m-1 > 512*8)
|
|
* @eccbytes = 7 (7 bytes are required to store m*t = 13*4 = 52 bits)
|
|
*/
|
|
struct nand_bch_control *nand_bch_init(struct mtd_info *mtd)
|
|
{
|
|
struct nand_chip *nand = mtd_to_nand(mtd);
|
|
unsigned int m, t, eccsteps, i;
|
|
struct nand_bch_control *nbc = NULL;
|
|
unsigned char *erased_page;
|
|
unsigned int eccsize = nand->ecc.size;
|
|
unsigned int eccbytes = nand->ecc.bytes;
|
|
unsigned int eccstrength = nand->ecc.strength;
|
|
|
|
if (!eccbytes && eccstrength) {
|
|
eccbytes = DIV_ROUND_UP(eccstrength * fls(8 * eccsize), 8);
|
|
nand->ecc.bytes = eccbytes;
|
|
}
|
|
|
|
if (!eccsize || !eccbytes) {
|
|
pr_warn("ecc parameters not supplied\n");
|
|
goto fail;
|
|
}
|
|
|
|
m = fls(1+8*eccsize);
|
|
t = (eccbytes*8)/m;
|
|
|
|
nbc = kzalloc(sizeof(*nbc), GFP_KERNEL);
|
|
if (!nbc)
|
|
goto fail;
|
|
|
|
nbc->bch = init_bch(m, t, 0);
|
|
if (!nbc->bch)
|
|
goto fail;
|
|
|
|
/* verify that eccbytes has the expected value */
|
|
if (nbc->bch->ecc_bytes != eccbytes) {
|
|
pr_warn("invalid eccbytes %u, should be %u\n",
|
|
eccbytes, nbc->bch->ecc_bytes);
|
|
goto fail;
|
|
}
|
|
|
|
eccsteps = mtd->writesize/eccsize;
|
|
|
|
/* Check that we have an oob layout description. */
|
|
if (!mtd->ooblayout) {
|
|
pr_warn("missing oob scheme");
|
|
goto fail;
|
|
}
|
|
|
|
/* sanity checks */
|
|
if (8*(eccsize+eccbytes) >= (1 << m)) {
|
|
pr_warn("eccsize %u is too large\n", eccsize);
|
|
goto fail;
|
|
}
|
|
|
|
/*
|
|
* ecc->steps and ecc->total might be used by mtd->ooblayout->ecc(),
|
|
* which is called by mtd_ooblayout_count_eccbytes().
|
|
* Make sure they are properly initialized before calling
|
|
* mtd_ooblayout_count_eccbytes().
|
|
* FIXME: we should probably rework the sequencing in nand_scan_tail()
|
|
* to avoid setting those fields twice.
|
|
*/
|
|
nand->ecc.steps = eccsteps;
|
|
nand->ecc.total = eccsteps * eccbytes;
|
|
if (mtd_ooblayout_count_eccbytes(mtd) != (eccsteps*eccbytes)) {
|
|
pr_warn("invalid ecc layout\n");
|
|
goto fail;
|
|
}
|
|
|
|
nbc->eccmask = kmalloc(eccbytes, GFP_KERNEL);
|
|
nbc->errloc = kmalloc_array(t, sizeof(*nbc->errloc), GFP_KERNEL);
|
|
if (!nbc->eccmask || !nbc->errloc)
|
|
goto fail;
|
|
/*
|
|
* compute and store the inverted ecc of an erased ecc block
|
|
*/
|
|
erased_page = kmalloc(eccsize, GFP_KERNEL);
|
|
if (!erased_page)
|
|
goto fail;
|
|
|
|
memset(erased_page, 0xff, eccsize);
|
|
memset(nbc->eccmask, 0, eccbytes);
|
|
encode_bch(nbc->bch, erased_page, eccsize, nbc->eccmask);
|
|
kfree(erased_page);
|
|
|
|
for (i = 0; i < eccbytes; i++)
|
|
nbc->eccmask[i] ^= 0xff;
|
|
|
|
if (!eccstrength)
|
|
nand->ecc.strength = (eccbytes * 8) / fls(8 * eccsize);
|
|
|
|
return nbc;
|
|
fail:
|
|
nand_bch_free(nbc);
|
|
return NULL;
|
|
}
|
|
EXPORT_SYMBOL(nand_bch_init);
|
|
|
|
/**
|
|
* nand_bch_free - [NAND Interface] Release NAND BCH ECC resources
|
|
* @nbc: NAND BCH control structure
|
|
*/
|
|
void nand_bch_free(struct nand_bch_control *nbc)
|
|
{
|
|
if (nbc) {
|
|
free_bch(nbc->bch);
|
|
kfree(nbc->errloc);
|
|
kfree(nbc->eccmask);
|
|
kfree(nbc);
|
|
}
|
|
}
|
|
EXPORT_SYMBOL(nand_bch_free);
|
|
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_AUTHOR("Ivan Djelic <ivan.djelic@parrot.com>");
|
|
MODULE_DESCRIPTION("NAND software BCH ECC support");
|