[backport from linux commit 02f8c6aee8df3cdc935e9bdd4f2d020306035dbe] This patch merges the BCH ECC algorithm from the 3.0 Linux kernel. This enables U-Boot to support modern NAND flash chips that require more than 1-bit of ECC in software. Signed-off-by: Christian Hitz <christian.hitz@aizo.com> Cc: Scott Wood <scottwood@freescale.com> Signed-off-by: Scott Wood <scottwood@freescale.com>master
parent
4d2aee2b36
commit
4c6de8560c
@ -0,0 +1,236 @@ |
|||||||
|
/*
|
||||||
|
* 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> |
||||||
|
* |
||||||
|
* 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. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <common.h> |
||||||
|
/*#include <asm/io.h>*/ |
||||||
|
#include <linux/types.h> |
||||||
|
|
||||||
|
#include <linux/bitops.h> |
||||||
|
#include <linux/mtd/mtd.h> |
||||||
|
#include <linux/mtd/nand.h> |
||||||
|
#include <linux/mtd/nand_bch.h> |
||||||
|
#include <linux/bch.h> |
||||||
|
#include <malloc.h> |
||||||
|
|
||||||
|
/**
|
||||||
|
* struct nand_bch_control - private NAND BCH control structure |
||||||
|
* @bch: BCH control structure |
||||||
|
* @ecclayout: private ecc layout for this BCH configuration |
||||||
|
* @errloc: error location array |
||||||
|
* @eccmask: XOR ecc mask, allows erased pages to be decoded as valid |
||||||
|
*/ |
||||||
|
struct nand_bch_control { |
||||||
|
struct bch_control *bch; |
||||||
|
struct nand_ecclayout ecclayout; |
||||||
|
unsigned int *errloc; |
||||||
|
unsigned char *eccmask; |
||||||
|
}; |
||||||
|
|
||||||
|
/**
|
||||||
|
* nand_bch_calculate_ecc - [NAND Interface] Calculate ECC for data block |
||||||
|
* @mtd: MTD block structure |
||||||
|
* @buf: input buffer with raw data |
||||||
|
* @code: output buffer with ECC |
||||||
|
*/ |
||||||
|
int nand_bch_calculate_ecc(struct mtd_info *mtd, const unsigned char *buf, |
||||||
|
unsigned char *code) |
||||||
|
{ |
||||||
|
const struct nand_chip *chip = mtd->priv; |
||||||
|
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; |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* nand_bch_correct_data - [NAND Interface] Detect and correct bit error(s) |
||||||
|
* @mtd: MTD block structure |
||||||
|
* @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 mtd_info *mtd, unsigned char *buf, |
||||||
|
unsigned char *read_ecc, unsigned char *calc_ecc) |
||||||
|
{ |
||||||
|
const struct nand_chip *chip = mtd->priv; |
||||||
|
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 */ |
||||||
|
|
||||||
|
MTDDEBUG(MTD_DEBUG_LEVEL0, "%s: corrected bitflip %u\n", |
||||||
|
__func__, errloc[i]); |
||||||
|
} |
||||||
|
} else if (count < 0) { |
||||||
|
printk(KERN_ERR "ecc unrecoverable error\n"); |
||||||
|
count = -1; |
||||||
|
} |
||||||
|
return count; |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* nand_bch_init - [NAND Interface] Initialize NAND BCH error correction |
||||||
|
* @mtd: MTD block structure |
||||||
|
* @eccsize: ecc block size in bytes |
||||||
|
* @eccbytes: ecc length in bytes |
||||||
|
* @ecclayout: output default layout |
||||||
|
* |
||||||
|
* 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, unsigned int eccsize, unsigned int eccbytes, |
||||||
|
struct nand_ecclayout **ecclayout) |
||||||
|
{ |
||||||
|
unsigned int m, t, eccsteps, i; |
||||||
|
struct nand_ecclayout *layout; |
||||||
|
struct nand_bch_control *nbc = NULL; |
||||||
|
unsigned char *erased_page; |
||||||
|
|
||||||
|
if (!eccsize || !eccbytes) { |
||||||
|
printk(KERN_WARNING "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) { |
||||||
|
printk(KERN_WARNING "invalid eccbytes %u, should be %u\n", |
||||||
|
eccbytes, nbc->bch->ecc_bytes); |
||||||
|
goto fail; |
||||||
|
} |
||||||
|
|
||||||
|
eccsteps = mtd->writesize/eccsize; |
||||||
|
|
||||||
|
/* if no ecc placement scheme was provided, build one */ |
||||||
|
if (!*ecclayout) { |
||||||
|
|
||||||
|
/* handle large page devices only */ |
||||||
|
if (mtd->oobsize < 64) { |
||||||
|
printk(KERN_WARNING "must provide an oob scheme for " |
||||||
|
"oobsize %d\n", mtd->oobsize); |
||||||
|
goto fail; |
||||||
|
} |
||||||
|
|
||||||
|
layout = &nbc->ecclayout; |
||||||
|
layout->eccbytes = eccsteps*eccbytes; |
||||||
|
|
||||||
|
/* reserve 2 bytes for bad block marker */ |
||||||
|
if (layout->eccbytes+2 > mtd->oobsize) { |
||||||
|
printk(KERN_WARNING "no suitable oob scheme available " |
||||||
|
"for oobsize %d eccbytes %u\n", mtd->oobsize, |
||||||
|
eccbytes); |
||||||
|
goto fail; |
||||||
|
} |
||||||
|
/* put ecc bytes at oob tail */ |
||||||
|
for (i = 0; i < layout->eccbytes; i++) |
||||||
|
layout->eccpos[i] = mtd->oobsize-layout->eccbytes+i; |
||||||
|
|
||||||
|
layout->oobfree[0].offset = 2; |
||||||
|
layout->oobfree[0].length = mtd->oobsize-2-layout->eccbytes; |
||||||
|
|
||||||
|
*ecclayout = layout; |
||||||
|
} |
||||||
|
|
||||||
|
/* sanity checks */ |
||||||
|
if (8*(eccsize+eccbytes) >= (1 << m)) { |
||||||
|
printk(KERN_WARNING "eccsize %u is too large\n", eccsize); |
||||||
|
goto fail; |
||||||
|
} |
||||||
|
if ((*ecclayout)->eccbytes != (eccsteps*eccbytes)) { |
||||||
|
printk(KERN_WARNING "invalid ecc layout\n"); |
||||||
|
goto fail; |
||||||
|
} |
||||||
|
|
||||||
|
nbc->eccmask = kmalloc(eccbytes, GFP_KERNEL); |
||||||
|
nbc->errloc = kmalloc(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; |
||||||
|
|
||||||
|
return nbc; |
||||||
|
fail: |
||||||
|
nand_bch_free(nbc); |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* 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); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,79 @@ |
|||||||
|
/*
|
||||||
|
* Generic binary BCH encoding/decoding library |
||||||
|
* |
||||||
|
* This program is free software; you can redistribute it and/or modify it |
||||||
|
* under the terms of the GNU General Public License version 2 as published by |
||||||
|
* the Free Software Foundation. |
||||||
|
* |
||||||
|
* This program 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 program; if not, write to the Free Software Foundation, Inc., 51 |
||||||
|
* Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
||||||
|
* |
||||||
|
* Copyright © 2011 Parrot S.A. |
||||||
|
* |
||||||
|
* Author: Ivan Djelic <ivan.djelic@parrot.com> |
||||||
|
* |
||||||
|
* Description: |
||||||
|
* |
||||||
|
* This library provides runtime configurable encoding/decoding of binary |
||||||
|
* Bose-Chaudhuri-Hocquenghem (BCH) codes. |
||||||
|
*/ |
||||||
|
#ifndef _BCH_H |
||||||
|
#define _BCH_H |
||||||
|
|
||||||
|
#include <linux/types.h> |
||||||
|
|
||||||
|
/**
|
||||||
|
* struct bch_control - BCH control structure |
||||||
|
* @m: Galois field order |
||||||
|
* @n: maximum codeword size in bits (= 2^m-1) |
||||||
|
* @t: error correction capability in bits |
||||||
|
* @ecc_bits: ecc exact size in bits, i.e. generator polynomial degree (<=m*t) |
||||||
|
* @ecc_bytes: ecc max size (m*t bits) in bytes |
||||||
|
* @a_pow_tab: Galois field GF(2^m) exponentiation lookup table |
||||||
|
* @a_log_tab: Galois field GF(2^m) log lookup table |
||||||
|
* @mod8_tab: remainder generator polynomial lookup tables |
||||||
|
* @ecc_buf: ecc parity words buffer |
||||||
|
* @ecc_buf2: ecc parity words buffer |
||||||
|
* @xi_tab: GF(2^m) base for solving degree 2 polynomial roots |
||||||
|
* @syn: syndrome buffer |
||||||
|
* @cache: log-based polynomial representation buffer |
||||||
|
* @elp: error locator polynomial |
||||||
|
* @poly_2t: temporary polynomials of degree 2t |
||||||
|
*/ |
||||||
|
struct bch_control { |
||||||
|
unsigned int m; |
||||||
|
unsigned int n; |
||||||
|
unsigned int t; |
||||||
|
unsigned int ecc_bits; |
||||||
|
unsigned int ecc_bytes; |
||||||
|
/* private: */ |
||||||
|
uint16_t *a_pow_tab; |
||||||
|
uint16_t *a_log_tab; |
||||||
|
uint32_t *mod8_tab; |
||||||
|
uint32_t *ecc_buf; |
||||||
|
uint32_t *ecc_buf2; |
||||||
|
unsigned int *xi_tab; |
||||||
|
unsigned int *syn; |
||||||
|
int *cache; |
||||||
|
struct gf_poly *elp; |
||||||
|
struct gf_poly *poly_2t[4]; |
||||||
|
}; |
||||||
|
|
||||||
|
struct bch_control *init_bch(int m, int t, unsigned int prim_poly); |
||||||
|
|
||||||
|
void free_bch(struct bch_control *bch); |
||||||
|
|
||||||
|
void encode_bch(struct bch_control *bch, const uint8_t *data, |
||||||
|
unsigned int len, uint8_t *ecc); |
||||||
|
|
||||||
|
int decode_bch(struct bch_control *bch, const uint8_t *data, unsigned int len, |
||||||
|
const uint8_t *recv_ecc, const uint8_t *calc_ecc, |
||||||
|
const unsigned int *syn, unsigned int *errloc); |
||||||
|
|
||||||
|
#endif /* _BCH_H */ |
@ -0,0 +1,72 @@ |
|||||||
|
/*
|
||||||
|
* Copyright © 2011 Ivan Djelic <ivan.djelic@parrot.com> |
||||||
|
* |
||||||
|
* This program is free software; you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU General Public License version 2 as |
||||||
|
* published by the Free Software Foundation. |
||||||
|
* |
||||||
|
* This file is the header for the NAND BCH ECC implementation. |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef __MTD_NAND_BCH_H__ |
||||||
|
#define __MTD_NAND_BCH_H__ |
||||||
|
|
||||||
|
struct mtd_info; |
||||||
|
struct nand_bch_control; |
||||||
|
|
||||||
|
#if defined(CONFIG_NAND_ECC_BCH) |
||||||
|
|
||||||
|
static inline int mtd_nand_has_bch(void) { return 1; } |
||||||
|
|
||||||
|
/*
|
||||||
|
* Calculate BCH ecc code |
||||||
|
*/ |
||||||
|
int nand_bch_calculate_ecc(struct mtd_info *mtd, const u_char *dat, |
||||||
|
u_char *ecc_code); |
||||||
|
|
||||||
|
/*
|
||||||
|
* Detect and correct bit errors |
||||||
|
*/ |
||||||
|
int nand_bch_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, |
||||||
|
u_char *calc_ecc); |
||||||
|
/*
|
||||||
|
* Initialize BCH encoder/decoder |
||||||
|
*/ |
||||||
|
struct nand_bch_control * |
||||||
|
nand_bch_init(struct mtd_info *mtd, unsigned int eccsize, |
||||||
|
unsigned int eccbytes, struct nand_ecclayout **ecclayout); |
||||||
|
/*
|
||||||
|
* Release BCH encoder/decoder resources |
||||||
|
*/ |
||||||
|
void nand_bch_free(struct nand_bch_control *nbc); |
||||||
|
|
||||||
|
#else /* !CONFIG_NAND_ECC_BCH */ |
||||||
|
|
||||||
|
static inline int mtd_nand_has_bch(void) { return 0; } |
||||||
|
|
||||||
|
static inline int |
||||||
|
nand_bch_calculate_ecc(struct mtd_info *mtd, const u_char *dat, |
||||||
|
u_char *ecc_code) |
||||||
|
{ |
||||||
|
return -1; |
||||||
|
} |
||||||
|
|
||||||
|
static inline int |
||||||
|
nand_bch_correct_data(struct mtd_info *mtd, unsigned char *buf, |
||||||
|
unsigned char *read_ecc, unsigned char *calc_ecc) |
||||||
|
{ |
||||||
|
return -1; |
||||||
|
} |
||||||
|
|
||||||
|
static inline struct nand_bch_control * |
||||||
|
nand_bch_init(struct mtd_info *mtd, unsigned int eccsize, |
||||||
|
unsigned int eccbytes, struct nand_ecclayout **ecclayout) |
||||||
|
{ |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
static inline void nand_bch_free(struct nand_bch_control *nbc) {} |
||||||
|
|
||||||
|
#endif /* CONFIG_NAND_ECC_BCH */ |
||||||
|
|
||||||
|
#endif /* __MTD_NAND_BCH_H__ */ |
Loading…
Reference in new issue