2019-05-20 19:08:14 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
2011-03-11 11:05:33 +01:00
/*
* 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>
2020-09-30 01:01:11 +02:00
# include <linux/mtd/nand.h>
2020-09-30 01:01:06 +02:00
# include <linux/mtd/nand-ecc-sw-bch.h>
2011-03-11 11:05:33 +01:00
/**
2020-09-30 01:01:11 +02:00
* nand_ecc_sw_bch_calculate - Calculate the ECC corresponding to a data block
* @ nand : NAND device
2020-09-30 01:01:07 +02:00
* @ buf : Input buffer with raw data
* @ code : Output buffer with ECC
2011-03-11 11:05:33 +01:00
*/
2020-09-30 01:01:11 +02:00
int nand_ecc_sw_bch_calculate ( struct nand_device * nand ,
const unsigned char * buf , unsigned char * code )
2011-03-11 11:05:33 +01:00
{
2020-09-30 01:01:12 +02:00
struct nand_ecc_sw_bch_conf * engine_conf = nand - > ecc . ctx . priv ;
2011-03-11 11:05:33 +01:00
unsigned int i ;
2020-09-30 01:01:12 +02:00
memset ( code , 0 , engine_conf - > code_size ) ;
bch_encode ( engine_conf - > bch , buf , nand - > ecc . ctx . conf . step_size , code ) ;
2011-03-11 11:05:33 +01:00
/* apply mask so that an erased page is a valid codeword */
2020-09-30 01:01:12 +02:00
for ( i = 0 ; i < engine_conf - > code_size ; i + + )
code [ i ] ^ = engine_conf - > eccmask [ i ] ;
2011-03-11 11:05:33 +01:00
return 0 ;
}
2020-09-30 01:01:11 +02:00
EXPORT_SYMBOL ( nand_ecc_sw_bch_calculate ) ;
2011-03-11 11:05:33 +01:00
/**
2020-09-30 01:01:11 +02:00
* nand_ecc_sw_bch_correct - Detect , correct and report bit error ( s )
* @ nand : NAND device
2020-09-30 01:01:07 +02:00
* @ buf : Raw data read from the chip
* @ read_ecc : ECC bytes from the chip
* @ calc_ecc : ECC calculated from the raw data
2011-03-11 11:05:33 +01:00
*
2020-09-30 01:01:07 +02:00
* Detect and correct bit errors for a data block .
2011-03-11 11:05:33 +01:00
*/
2020-09-30 01:01:11 +02:00
int nand_ecc_sw_bch_correct ( struct nand_device * nand , unsigned char * buf ,
unsigned char * read_ecc , unsigned char * calc_ecc )
2011-03-11 11:05:33 +01:00
{
2020-09-30 01:01:12 +02:00
struct nand_ecc_sw_bch_conf * engine_conf = nand - > ecc . ctx . priv ;
unsigned int step_size = nand - > ecc . ctx . conf . step_size ;
unsigned int * errloc = engine_conf - > errloc ;
2011-03-11 11:05:33 +01:00
int i , count ;
2020-09-30 01:01:12 +02:00
count = bch_decode ( engine_conf - > bch , NULL , step_size , read_ecc ,
calc_ecc , NULL , errloc ) ;
2011-03-11 11:05:33 +01:00
if ( count > 0 ) {
for ( i = 0 ; i < count ; i + + ) {
2020-09-30 01:01:12 +02:00
if ( errloc [ i ] < ( step_size * 8 ) )
2020-09-30 01:01:07 +02:00
/* The error is in the data area: correct it */
2011-03-11 11:05:33 +01:00
buf [ errloc [ i ] > > 3 ] ^ = ( 1 < < ( errloc [ i ] & 7 ) ) ;
2020-09-30 01:01:07 +02:00
/* Otherwise the error is in the ECC area: nothing to do */
2011-07-19 10:06:10 -07:00
pr_debug ( " %s: corrected bitflip %u \n " , __func__ ,
2020-09-30 01:01:07 +02:00
errloc [ i ] ) ;
2011-03-11 11:05:33 +01:00
}
} else if ( count < 0 ) {
2020-09-30 01:01:07 +02:00
pr_err ( " ECC unrecoverable error \n " ) ;
2015-12-30 20:32:03 +01:00
count = - EBADMSG ;
2011-03-11 11:05:33 +01:00
}
2020-09-30 01:01:07 +02:00
2011-03-11 11:05:33 +01:00
return count ;
}
2020-09-30 01:01:11 +02:00
EXPORT_SYMBOL ( nand_ecc_sw_bch_correct ) ;
2011-03-11 11:05:33 +01:00
2020-09-30 01:01:13 +02:00
/**
* nand_ecc_sw_bch_cleanup - Cleanup software BCH ECC resources
* @ nand : NAND device
*/
static void nand_ecc_sw_bch_cleanup ( struct nand_device * nand )
{
struct nand_ecc_sw_bch_conf * engine_conf = nand - > ecc . ctx . priv ;
bch_free ( engine_conf - > bch ) ;
kfree ( engine_conf - > errloc ) ;
kfree ( engine_conf - > eccmask ) ;
}
2011-03-11 11:05:33 +01:00
/**
2020-09-30 01:01:11 +02:00
* nand_ecc_sw_bch_init - Initialize software BCH ECC engine
* @ nand : NAND device
2011-03-11 11:05:33 +01:00
*
2020-09-30 01:01:07 +02:00
* Returns : a pointer to a new NAND BCH control structure , or NULL upon failure
2011-03-11 11:05:33 +01:00
*
2020-09-30 01:01:12 +02:00
* Initialize NAND BCH error correction . @ nand . ecc parameters ' step_size ' and
* ' bytes ' are used to compute the following BCH parameters :
2020-09-30 01:01:07 +02:00
* m , the Galois field order
* t , the error correction capability
2020-09-30 01:01:12 +02:00
* ' bytes ' should be equal to the number of bytes required to store m * t
2020-09-30 01:01:07 +02:00
* bits , where m is such that 2 ^ m - 1 > step_size * 8.
2011-03-11 11:05:33 +01:00
*
* Example : to configure 4 bit correction per 512 bytes , you should pass
2020-09-30 01:01:12 +02:00
* step_size = 512 ( thus , m = 13 is the smallest integer such that 2 ^ m - 1 > 512 * 8 )
* bytes = 7 ( 7 bytes are required to store m * t = 13 * 4 = 52 bits )
2011-03-11 11:05:33 +01:00
*/
2020-09-30 01:01:13 +02:00
static int nand_ecc_sw_bch_init ( struct nand_device * nand )
2011-03-11 11:05:33 +01:00
{
2020-09-30 01:01:12 +02:00
struct nand_ecc_sw_bch_conf * engine_conf = nand - > ecc . ctx . priv ;
unsigned int eccsize = nand - > ecc . ctx . conf . step_size ;
unsigned int eccbytes = engine_conf - > code_size ;
2020-09-30 01:01:13 +02:00
unsigned int m , t , i ;
unsigned char * erased_page ;
int ret ;
2011-03-11 11:05:33 +01:00
2020-09-30 01:01:13 +02:00
m = fls ( 1 + ( 8 * eccsize ) ) ;
t = ( eccbytes * 8 ) / m ;
2011-03-11 11:05:33 +01:00
2020-09-30 01:01:12 +02:00
engine_conf - > bch = bch_init ( m , t , 0 , false ) ;
if ( ! engine_conf - > bch )
return - EINVAL ;
2011-03-11 11:05:33 +01:00
2020-09-30 01:01:13 +02:00
engine_conf - > eccmask = kzalloc ( eccbytes , GFP_KERNEL ) ;
engine_conf - > errloc = kmalloc_array ( t , sizeof ( * engine_conf - > errloc ) ,
GFP_KERNEL ) ;
if ( ! engine_conf - > eccmask | | ! engine_conf - > errloc ) {
ret = - ENOMEM ;
goto cleanup ;
}
/* Compute and store the inverted ECC of an erased step */
erased_page = kmalloc ( eccsize , GFP_KERNEL ) ;
if ( ! erased_page ) {
ret = - ENOMEM ;
goto cleanup ;
2011-03-11 11:05:33 +01:00
}
2020-09-30 01:01:13 +02:00
memset ( erased_page , 0xff , eccsize ) ;
bch_encode ( engine_conf - > bch , erased_page , eccsize ,
engine_conf - > eccmask ) ;
kfree ( erased_page ) ;
for ( i = 0 ; i < eccbytes ; i + + )
engine_conf - > eccmask [ i ] ^ = 0xff ;
2011-03-11 11:05:33 +01:00
2020-09-30 01:01:13 +02:00
/* Verify that the number of code bytes has the expected value */
if ( engine_conf - > bch - > ecc_bytes ! = eccbytes ) {
pr_err ( " Invalid number of ECC bytes: %u, expected: %u \n " ,
eccbytes , engine_conf - > bch - > ecc_bytes ) ;
ret = - EINVAL ;
goto cleanup ;
2011-03-11 11:05:33 +01:00
}
2020-09-30 01:01:13 +02:00
/* Sanity checks */
if ( 8 * ( eccsize + eccbytes ) > = ( 1 < < m ) ) {
pr_err ( " ECC step size is too large (%u) \n " , eccsize ) ;
ret = - EINVAL ;
goto cleanup ;
2011-03-11 11:05:33 +01:00
}
2016-02-03 20:11:00 +01:00
2020-09-30 01:01:13 +02:00
return 0 ;
cleanup :
nand_ecc_sw_bch_cleanup ( nand ) ;
return ret ;
}
int nand_ecc_sw_bch_init_ctx ( struct nand_device * nand )
{
struct nand_ecc_props * conf = & nand - > ecc . ctx . conf ;
struct mtd_info * mtd = nanddev_to_mtd ( nand ) ;
struct nand_ecc_sw_bch_conf * engine_conf ;
unsigned int code_size = 0 , nsteps ;
int ret ;
/* Only large page NAND chips may use BCH */
if ( mtd - > oobsize < 64 ) {
pr_err ( " BCH cannot be used with small page NAND chips \n " ) ;
return - EINVAL ;
2011-03-11 11:05:33 +01:00
}
2020-09-30 01:01:13 +02:00
if ( ! mtd - > ooblayout )
mtd_set_ooblayout ( mtd , nand_get_large_page_ooblayout ( ) ) ;
conf - > engine_type = NAND_ECC_ENGINE_TYPE_SOFT ;
conf - > algo = NAND_ECC_ALGO_BCH ;
conf - > step_size = nand - > ecc . user_conf . step_size ;
conf - > strength = nand - > ecc . user_conf . strength ;
2020-09-30 01:01:07 +02:00
2011-03-11 11:05:33 +01:00
/*
2020-09-30 01:01:13 +02:00
* Board driver should supply ECC size and ECC strength
* values to select how many bits are correctable .
* Otherwise , default to 512 bytes for large page devices and 256 for
* small page devices .
2011-03-11 11:05:33 +01:00
*/
2020-09-30 01:01:13 +02:00
if ( ! conf - > step_size ) {
if ( mtd - > oobsize > = 64 )
conf - > step_size = 512 ;
else
conf - > step_size = 256 ;
2011-03-11 11:05:33 +01:00
2020-09-30 01:01:13 +02:00
conf - > strength = 4 ;
}
2011-03-11 11:05:33 +01:00
2020-09-30 01:01:13 +02:00
nsteps = mtd - > writesize / conf - > step_size ;
/* Maximize */
if ( nand - > ecc . user_conf . flags & NAND_ECC_MAXIMIZE_STRENGTH ) {
conf - > step_size = 1024 ;
nsteps = mtd - > writesize / conf - > step_size ;
/* Reserve 2 bytes for the BBM */
code_size = ( mtd - > oobsize - 2 ) / nsteps ;
conf - > strength = code_size * 8 / fls ( 8 * conf - > step_size ) ;
}
if ( ! code_size )
code_size = DIV_ROUND_UP ( conf - > strength *
fls ( 8 * conf - > step_size ) , 8 ) ;
if ( ! conf - > strength )
conf - > strength = ( code_size * 8 ) / fls ( 8 * conf - > step_size ) ;
if ( ! code_size & & ! conf - > strength ) {
pr_err ( " Missing ECC parameters \n " ) ;
return - EINVAL ;
}
engine_conf = kzalloc ( sizeof ( * engine_conf ) , GFP_KERNEL ) ;
if ( ! engine_conf )
return - ENOMEM ;
ret = nand_ecc_init_req_tweaking ( & engine_conf - > req_ctx , nand ) ;
if ( ret )
goto free_engine_conf ;
engine_conf - > code_size = code_size ;
engine_conf - > calc_buf = kzalloc ( mtd - > oobsize , GFP_KERNEL ) ;
engine_conf - > code_buf = kzalloc ( mtd - > oobsize , GFP_KERNEL ) ;
if ( ! engine_conf - > calc_buf | | ! engine_conf - > code_buf ) {
ret = - ENOMEM ;
goto free_bufs ;
}
2011-03-11 11:05:33 +01:00
2020-09-30 01:01:13 +02:00
nand - > ecc . ctx . priv = engine_conf ;
2021-01-27 21:30:13 +01:00
nand - > ecc . ctx . nsteps = nsteps ;
2020-09-30 01:01:13 +02:00
nand - > ecc . ctx . total = nsteps * code_size ;
ret = nand_ecc_sw_bch_init ( nand ) ;
if ( ret )
goto free_bufs ;
/* Verify the layout validity */
if ( mtd_ooblayout_count_eccbytes ( mtd ) ! =
2021-01-27 21:30:19 +01:00
nand - > ecc . ctx . nsteps * engine_conf - > code_size ) {
2020-09-30 01:01:13 +02:00
pr_err ( " Invalid ECC layout \n " ) ;
ret = - EINVAL ;
goto cleanup_bch_ctx ;
}
2016-03-07 10:46:54 +01:00
2020-09-30 01:01:08 +02:00
return 0 ;
2020-09-30 01:01:11 +02:00
2020-09-30 01:01:13 +02:00
cleanup_bch_ctx :
2020-09-30 01:01:11 +02:00
nand_ecc_sw_bch_cleanup ( nand ) ;
2020-09-30 01:01:13 +02:00
free_bufs :
nand_ecc_cleanup_req_tweaking ( & engine_conf - > req_ctx ) ;
kfree ( engine_conf - > calc_buf ) ;
kfree ( engine_conf - > code_buf ) ;
free_engine_conf :
kfree ( engine_conf ) ;
2020-09-30 01:01:11 +02:00
2020-09-30 01:01:13 +02:00
return ret ;
2011-03-11 11:05:33 +01:00
}
2020-09-30 01:01:13 +02:00
EXPORT_SYMBOL ( nand_ecc_sw_bch_init_ctx ) ;
2011-03-11 11:05:33 +01:00
2020-09-30 01:01:13 +02:00
void nand_ecc_sw_bch_cleanup_ctx ( struct nand_device * nand )
2011-03-11 11:05:33 +01:00
{
2020-09-30 01:01:12 +02:00
struct nand_ecc_sw_bch_conf * engine_conf = nand - > ecc . ctx . priv ;
if ( engine_conf ) {
2020-09-30 01:01:13 +02:00
nand_ecc_sw_bch_cleanup ( nand ) ;
nand_ecc_cleanup_req_tweaking ( & engine_conf - > req_ctx ) ;
kfree ( engine_conf - > calc_buf ) ;
kfree ( engine_conf - > code_buf ) ;
kfree ( engine_conf ) ;
}
}
EXPORT_SYMBOL ( nand_ecc_sw_bch_cleanup_ctx ) ;
static int nand_ecc_sw_bch_prepare_io_req ( struct nand_device * nand ,
struct nand_page_io_req * req )
{
struct nand_ecc_sw_bch_conf * engine_conf = nand - > ecc . ctx . priv ;
struct mtd_info * mtd = nanddev_to_mtd ( nand ) ;
int eccsize = nand - > ecc . ctx . conf . step_size ;
int eccbytes = engine_conf - > code_size ;
2021-01-27 21:30:19 +01:00
int eccsteps = nand - > ecc . ctx . nsteps ;
2020-09-30 01:01:13 +02:00
int total = nand - > ecc . ctx . total ;
u8 * ecccalc = engine_conf - > calc_buf ;
const u8 * data ;
int i ;
/* Nothing to do for a raw operation */
if ( req - > mode = = MTD_OPS_RAW )
return 0 ;
/* This engine does not provide BBM/free OOB bytes protection */
if ( ! req - > datalen )
return 0 ;
nand_ecc_tweak_req ( & engine_conf - > req_ctx , req ) ;
/* No more preparation for page read */
if ( req - > type = = NAND_PAGE_READ )
return 0 ;
/* Preparation for page write: derive the ECC bytes and place them */
for ( i = 0 , data = req - > databuf . out ;
eccsteps ;
eccsteps - - , i + = eccbytes , data + = eccsize )
nand_ecc_sw_bch_calculate ( nand , data , & ecccalc [ i ] ) ;
return mtd_ooblayout_set_eccbytes ( mtd , ecccalc , ( void * ) req - > oobbuf . out ,
0 , total ) ;
}
static int nand_ecc_sw_bch_finish_io_req ( struct nand_device * nand ,
struct nand_page_io_req * req )
{
struct nand_ecc_sw_bch_conf * engine_conf = nand - > ecc . ctx . priv ;
struct mtd_info * mtd = nanddev_to_mtd ( nand ) ;
int eccsize = nand - > ecc . ctx . conf . step_size ;
int total = nand - > ecc . ctx . total ;
int eccbytes = engine_conf - > code_size ;
2021-01-27 21:30:19 +01:00
int eccsteps = nand - > ecc . ctx . nsteps ;
2020-09-30 01:01:13 +02:00
u8 * ecccalc = engine_conf - > calc_buf ;
u8 * ecccode = engine_conf - > code_buf ;
unsigned int max_bitflips = 0 ;
u8 * data = req - > databuf . in ;
int i , ret ;
/* Nothing to do for a raw operation */
if ( req - > mode = = MTD_OPS_RAW )
return 0 ;
/* This engine does not provide BBM/free OOB bytes protection */
if ( ! req - > datalen )
return 0 ;
/* No more preparation for page write */
if ( req - > type = = NAND_PAGE_WRITE ) {
nand_ecc_restore_req ( & engine_conf - > req_ctx , req ) ;
return 0 ;
2011-03-11 11:05:33 +01:00
}
2020-09-30 01:01:13 +02:00
/* Finish a page read: retrieve the (raw) ECC bytes*/
ret = mtd_ooblayout_get_eccbytes ( mtd , ecccode , req - > oobbuf . in , 0 ,
total ) ;
if ( ret )
return ret ;
/* Calculate the ECC bytes */
for ( i = 0 ; eccsteps ; eccsteps - - , i + = eccbytes , data + = eccsize )
nand_ecc_sw_bch_calculate ( nand , data , & ecccalc [ i ] ) ;
/* Finish a page read: compare and correct */
2021-01-27 21:30:19 +01:00
for ( eccsteps = nand - > ecc . ctx . nsteps , i = 0 , data = req - > databuf . in ;
2020-09-30 01:01:13 +02:00
eccsteps ;
eccsteps - - , i + = eccbytes , data + = eccsize ) {
int stat = nand_ecc_sw_bch_correct ( nand , data ,
& ecccode [ i ] ,
& ecccalc [ i ] ) ;
if ( stat < 0 ) {
mtd - > ecc_stats . failed + + ;
} else {
mtd - > ecc_stats . corrected + = stat ;
max_bitflips = max_t ( unsigned int , max_bitflips , stat ) ;
}
}
nand_ecc_restore_req ( & engine_conf - > req_ctx , req ) ;
return max_bitflips ;
}
static struct nand_ecc_engine_ops nand_ecc_sw_bch_engine_ops = {
. init_ctx = nand_ecc_sw_bch_init_ctx ,
. cleanup_ctx = nand_ecc_sw_bch_cleanup_ctx ,
. prepare_io_req = nand_ecc_sw_bch_prepare_io_req ,
. finish_io_req = nand_ecc_sw_bch_finish_io_req ,
} ;
static struct nand_ecc_engine nand_ecc_sw_bch_engine = {
. ops = & nand_ecc_sw_bch_engine_ops ,
} ;
struct nand_ecc_engine * nand_ecc_sw_bch_get_engine ( void )
{
return & nand_ecc_sw_bch_engine ;
2011-03-11 11:05:33 +01:00
}
2020-09-30 01:01:13 +02:00
EXPORT_SYMBOL ( nand_ecc_sw_bch_get_engine ) ;
2011-03-11 11:05:33 +01:00
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Ivan Djelic <ivan.djelic@parrot.com> " ) ;
MODULE_DESCRIPTION ( " NAND software BCH ECC support " ) ;