@ -28,6 +28,7 @@
# include <asm/arch/cpu.h>
# include <asm/omap_gpmc.h>
# include <linux/mtd/nand_ecc.h>
# include <linux/bch.h>
# include <linux/compiler.h>
# include <nand.h>
# ifdef CONFIG_AM33XX
@ -37,6 +38,8 @@
static uint8_t cs ;
static __maybe_unused struct nand_ecclayout hw_nand_oob =
GPMC_NAND_HW_ECC_LAYOUT ;
static __maybe_unused struct nand_ecclayout hw_bch8_nand_oob =
GPMC_NAND_HW_BCH8_ECC_LAYOUT ;
/*
* omap_nand_hwcontrol - Set the address pointers corretly for the
@ -239,13 +242,13 @@ static void __maybe_unused omap_enable_hwecc(struct mtd_info *mtd, int32_t mode)
}
/*
* BCH8 support ( needs ELM and thus AM33xx - only )
* Generic BCH interface
*/
# ifdef CONFIG_AM33XX
struct nand_bch_priv {
uint8_t mode ;
uint8_t type ;
uint8_t nibbles ;
struct bch_control * control ;
} ;
/* bch types */
@ -253,21 +256,146 @@ struct nand_bch_priv {
# define ECC_BCH8 1
# define ECC_BCH16 2
/* GPMC ecc engine settings */
# define BCH_WRAPMODE_1 1 /* BCH wrap mode 1 */
# define BCH_WRAPMODE_6 6 /* BCH wrap mode 6 */
/* BCH nibbles for diff bch levels */
# define NAND_ECC_HW_BCH ((uint8_t)(NAND_ECC_HW_OOB_FIRST) + 1)
# define ECC_BCH4_NIBBLES 13
# define ECC_BCH8_NIBBLES 26
# define ECC_BCH16_NIBBLES 52
static struct nand_ecclayout hw_bch8_nand_oob = GPMC_NAND_HW_BCH8_ECC_LAYOUT ;
static struct nand_bch_priv bch_priv = {
/*
* This can be a single instance cause all current users have only one NAND
* with nearly the same setup ( BCH8 , some with ELM and others with sw BCH
* library ) .
* When some users with other BCH strength will exists this have to change !
*/
static __maybe_unused struct nand_bch_priv bch_priv = {
. mode = NAND_ECC_HW_BCH ,
. type = ECC_BCH8 ,
. nibbles = ECC_BCH8_NIBBLES
. nibbles = ECC_BCH8_NIBBLES ,
. control = NULL
} ;
/*
* omap_hwecc_init_bch - Initialize the BCH Hardware ECC for NAND flash in
* GPMC controller
* @ mtd : MTD device structure
* @ mode : Read / Write mode
*/
__maybe_unused
static void omap_hwecc_init_bch ( struct nand_chip * chip , int32_t mode )
{
uint32_t val ;
uint32_t dev_width = ( chip - > options & NAND_BUSWIDTH_16 ) > > 1 ;
# ifdef CONFIG_AM33XX
uint32_t unused_length = 0 ;
# endif
uint32_t wr_mode = BCH_WRAPMODE_6 ;
struct nand_bch_priv * bch = chip - > priv ;
/* Clear the ecc result registers, select ecc reg as 1 */
writel ( ECCCLEAR | ECCRESULTREG1 , & gpmc_cfg - > ecc_control ) ;
# ifdef CONFIG_AM33XX
wr_mode = BCH_WRAPMODE_1 ;
switch ( bch - > nibbles ) {
case ECC_BCH4_NIBBLES :
unused_length = 3 ;
break ;
case ECC_BCH8_NIBBLES :
unused_length = 2 ;
break ;
case ECC_BCH16_NIBBLES :
unused_length = 0 ;
break ;
}
/*
* This is ecc_size_config for ELM mode .
* Here we are using different settings for read and write access and
* also depending on BCH strength .
*/
switch ( mode ) {
case NAND_ECC_WRITE :
/* write access only setup eccsize1 config */
val = ( ( unused_length + bch - > nibbles ) < < 22 ) ;
break ;
case NAND_ECC_READ :
default :
/*
* by default eccsize0 selected for ecc1resultsize
* eccsize0 config .
*/
val = ( bch - > nibbles < < 12 ) ;
/* eccsize1 config */
val | = ( unused_length < < 22 ) ;
break ;
}
# else
/*
* This ecc_size_config setting is for BCH sw library .
*
* Note : we only support BCH8 currently with BCH sw library !
* Should be really easy to adobt to BCH4 , however some omap3 have
* flaws with BCH4 .
*
* Here we are using wrapping mode 6 both for reading and writing , with :
* size0 = 0 ( no additional protected byte in spare area )
* size1 = 32 ( skip 32 nibbles = 16 bytes per sector in spare area )
*/
val = ( 32 < < 22 ) | ( 0 < < 12 ) ;
# endif
/* ecc size configuration */
writel ( val , & gpmc_cfg - > ecc_size_config ) ;
/*
* Configure the ecc engine in gpmc
* We assume 512 Byte sector pages for access to NAND .
*/
val = ( 1 < < 16 ) ; /* enable BCH mode */
val | = ( bch - > type < < 12 ) ; /* setup BCH type */
val | = ( wr_mode < < 8 ) ; /* setup wrapping mode */
val | = ( dev_width < < 7 ) ; /* setup device width (16 or 8 bit) */
val | = ( cs < < 1 ) ; /* setup chip select to work on */
debug ( " set ECC_CONFIG=0x%08x \n " , val ) ;
writel ( val , & gpmc_cfg - > ecc_config ) ;
}
/*
* omap_enable_ecc_bch - This function enables the bch h / w ecc functionality
* @ mtd : MTD device structure
* @ mode : Read / Write mode
*/
__maybe_unused
static void omap_enable_ecc_bch ( struct mtd_info * mtd , int32_t mode )
{
struct nand_chip * chip = mtd - > priv ;
omap_hwecc_init_bch ( chip , mode ) ;
/* enable ecc */
writel ( ( readl ( & gpmc_cfg - > ecc_config ) | 0x1 ) , & gpmc_cfg - > ecc_config ) ;
}
/*
* omap_ecc_disable - Disable H / W ECC calculation
*
* @ mtd : MTD device structure
*/
static void __maybe_unused omap_ecc_disable ( struct mtd_info * mtd )
{
writel ( ( readl ( & gpmc_cfg - > ecc_config ) & ~ 0x1 ) , & gpmc_cfg - > ecc_config ) ;
}
/*
* BCH8 support ( needs ELM and thus AM33xx - only )
*/
# ifdef CONFIG_AM33XX
/*
* omap_read_bch8_result - Read BCH result for BCH8 level
*
* @ mtd : MTD device structure
@ -306,18 +434,6 @@ static void omap_read_bch8_result(struct mtd_info *mtd, uint8_t big_endian,
}
/*
* omap_ecc_disable - Disable H / W ECC calculation
*
* @ mtd : MTD device structure
*
*/
static void omap_ecc_disable ( struct mtd_info * mtd )
{
writel ( ( readl ( & gpmc_cfg - > ecc_config ) & ~ 0x1 ) ,
& gpmc_cfg - > ecc_config ) ;
}
/*
* omap_rotate_ecc_bch - Rotate the syndrome bytes
*
* @ mtd : MTD device structure
@ -468,76 +584,6 @@ static int omap_correct_data_bch(struct mtd_info *mtd, uint8_t *dat,
return 0 ;
}
/*
* omap_hwecc_init_bch - Initialize the BCH Hardware ECC for NAND flash in
* GPMC controller
* @ mtd : MTD device structure
* @ mode : Read / Write mode
*/
static void omap_hwecc_init_bch ( struct nand_chip * chip , int32_t mode )
{
uint32_t val , dev_width = ( chip - > options & NAND_BUSWIDTH_16 ) > > 1 ;
uint32_t unused_length = 0 ;
struct nand_bch_priv * bch = chip - > priv ;
switch ( bch - > nibbles ) {
case ECC_BCH4_NIBBLES :
unused_length = 3 ;
break ;
case ECC_BCH8_NIBBLES :
unused_length = 2 ;
break ;
case ECC_BCH16_NIBBLES :
unused_length = 0 ;
break ;
}
/* Clear the ecc result registers, select ecc reg as 1 */
writel ( ECCCLEAR | ECCRESULTREG1 , & gpmc_cfg - > ecc_control ) ;
switch ( mode ) {
case NAND_ECC_WRITE :
/* eccsize1 config */
val = ( ( unused_length + bch - > nibbles ) < < 22 ) ;
break ;
case NAND_ECC_READ :
default :
/* by default eccsize0 selected for ecc1resultsize */
/* eccsize0 config */
val = ( bch - > nibbles < < 12 ) ;
/* eccsize1 config */
val | = ( unused_length < < 22 ) ;
break ;
}
/* ecc size configuration */
writel ( val , & gpmc_cfg - > ecc_size_config ) ;
/* by default 512bytes sector page is selected */
/* set bch mode */
val = ( 1 < < 16 ) ;
/* bch4 / bch8 / bch16 */
val | = ( bch - > type < < 12 ) ;
/* set wrap mode to 1 */
val | = ( 1 < < 8 ) ;
val | = ( dev_width < < 7 ) ;
val | = ( cs < < 1 ) ;
writel ( val , & gpmc_cfg - > ecc_config ) ;
}
/*
* omap_enable_ecc_bch - This function enables the bch h / w ecc functionality
* @ mtd : MTD device structure
* @ mode : Read / Write mode
*
*/
static void omap_enable_ecc_bch ( struct mtd_info * mtd , int32_t mode )
{
struct nand_chip * chip = mtd - > priv ;
omap_hwecc_init_bch ( chip , mode ) ;
/* enable ecc */
writel ( ( readl ( & gpmc_cfg - > ecc_config ) | 0x1 ) , & gpmc_cfg - > ecc_config ) ;
}
/**
* omap_read_page_bch - hardware ecc based page read function
@ -602,6 +648,127 @@ static int omap_read_page_bch(struct mtd_info *mtd, struct nand_chip *chip,
}
# endif /* CONFIG_AM33XX */
/*
* OMAP3 BCH8 support ( with BCH library )
*/
# ifdef CONFIG_NAND_OMAP_BCH8
/*
* omap_calculate_ecc_bch - Read BCH ECC result
*
* @ mtd : MTD device structure
* @ dat : The pointer to data on which ecc is computed ( unused here )
* @ ecc : The ECC output buffer
*/
static int omap_calculate_ecc_bch ( struct mtd_info * mtd , const uint8_t * dat ,
uint8_t * ecc )
{
int ret = 0 ;
size_t i ;
unsigned long nsectors , val1 , val2 , val3 , val4 ;
nsectors = ( ( readl ( & gpmc_cfg - > ecc_config ) > > 4 ) & 0x7 ) + 1 ;
for ( i = 0 ; i < nsectors ; i + + ) {
/* Read hw-computed remainder */
val1 = readl ( & gpmc_cfg - > bch_result_0_3 [ i ] . bch_result_x [ 0 ] ) ;
val2 = readl ( & gpmc_cfg - > bch_result_0_3 [ i ] . bch_result_x [ 1 ] ) ;
val3 = readl ( & gpmc_cfg - > bch_result_0_3 [ i ] . bch_result_x [ 2 ] ) ;
val4 = readl ( & gpmc_cfg - > bch_result_0_3 [ i ] . bch_result_x [ 3 ] ) ;
/*
* Add constant polynomial to remainder , in order to get an ecc
* sequence of 0xFF s for a buffer filled with 0xFF s .
*/
* ecc + + = 0xef ^ ( val4 & 0xFF ) ;
* ecc + + = 0x51 ^ ( ( val3 > > 24 ) & 0xFF ) ;
* ecc + + = 0x2e ^ ( ( val3 > > 16 ) & 0xFF ) ;
* ecc + + = 0x09 ^ ( ( val3 > > 8 ) & 0xFF ) ;
* ecc + + = 0xed ^ ( val3 & 0xFF ) ;
* ecc + + = 0x93 ^ ( ( val2 > > 24 ) & 0xFF ) ;
* ecc + + = 0x9a ^ ( ( val2 > > 16 ) & 0xFF ) ;
* ecc + + = 0xc2 ^ ( ( val2 > > 8 ) & 0xFF ) ;
* ecc + + = 0x97 ^ ( val2 & 0xFF ) ;
* ecc + + = 0x79 ^ ( ( val1 > > 24 ) & 0xFF ) ;
* ecc + + = 0xe5 ^ ( ( val1 > > 16 ) & 0xFF ) ;
* ecc + + = 0x24 ^ ( ( val1 > > 8 ) & 0xFF ) ;
* ecc + + = 0xb5 ^ ( val1 & 0xFF ) ;
}
/*
* Stop reading anymore ECC vals and clear old results
* enable will be called if more reads are required
*/
omap_ecc_disable ( mtd ) ;
return ret ;
}
/**
* omap_correct_data_bch - Decode received data and correct errors
* @ mtd : MTD device structure
* @ data : page data
* @ read_ecc : ecc read from nand flash
* @ calc_ecc : ecc read from HW ECC registers
*/
static int omap_correct_data_bch ( struct mtd_info * mtd , u_char * data ,
u_char * read_ecc , u_char * calc_ecc )
{
int i , count ;
/* cannot correct more than 8 errors */
unsigned int errloc [ 8 ] ;
struct nand_chip * chip = mtd - > priv ;
struct nand_bch_priv * chip_priv = chip - > priv ;
struct bch_control * bch = chip_priv - > control ;
count = decode_bch ( bch , NULL , 512 , read_ecc , calc_ecc , NULL , errloc ) ;
if ( count > 0 ) {
/* correct errors */
for ( i = 0 ; i < count ; i + + ) {
/* correct data only, not ecc bytes */
if ( errloc [ i ] < 8 * 512 )
data [ errloc [ i ] / 8 ] ^ = 1 < < ( errloc [ i ] & 7 ) ;
printf ( " corrected bitflip %u \n " , errloc [ i ] ) ;
# ifdef DEBUG
puts ( " read_ecc: " ) ;
/*
* BCH8 have 13 bytes of ECC ; BCH4 needs adoption
* here !
*/
for ( i = 0 ; i < 13 ; i + + )
printf ( " %02x " , read_ecc [ i ] ) ;
puts ( " \n " ) ;
puts ( " calc_ecc: " ) ;
for ( i = 0 ; i < 13 ; i + + )
printf ( " %02x " , calc_ecc [ i ] ) ;
puts ( " \n " ) ;
# endif
}
} else if ( count < 0 ) {
puts ( " ecc unrecoverable error \n " ) ;
}
return count ;
}
/**
* omap_free_bch - Release BCH ecc resources
* @ mtd : MTD device structure
*/
static void __maybe_unused omap_free_bch ( struct mtd_info * mtd )
{
struct nand_chip * chip = mtd - > priv ;
struct nand_bch_priv * chip_priv = chip - > priv ;
struct bch_control * bch = NULL ;
if ( chip_priv )
bch = chip_priv - > control ;
if ( bch ) {
free_bch ( bch ) ;
chip_priv - > control = NULL ;
}
}
# endif /* CONFIG_NAND_OMAP_BCH8 */
# ifndef CONFIG_SPL_BUILD
/*
* omap_nand_switch_ecc - switch the ECC operation between different engines
@ -651,13 +818,17 @@ void omap_nand_switch_ecc(uint32_t hardware, uint32_t eccstrength)
omap_hwecc_init ( nand ) ;
printf ( " 1-bit hamming HW ECC selected \n " ) ;
}
# ifdef CONFIG_AM33XX
# if defined(CONFIG_AM33XX) || defined(CONFIG_NAND_OMAP_BCH8)
else if ( eccstrength = = 8 ) {
nand - > ecc . mode = NAND_ECC_HW ;
nand - > ecc . layout = & hw_bch8_nand_oob ;
nand - > ecc . size = 512 ;
# ifdef CONFIG_AM33XX
nand - > ecc . bytes = 14 ;
nand - > ecc . read_page = omap_read_page_bch ;
# else
nand - > ecc . bytes = 13 ;
# endif
nand - > ecc . hwctl = omap_enable_ecc_bch ;
nand - > ecc . correct = omap_correct_data_bch ;
nand - > ecc . calculate = omap_calculate_ecc_bch ;
@ -737,16 +908,28 @@ int board_nand_init(struct nand_chip *nand)
nand - > chip_delay = 100 ;
# if defined(CONFIG_AM33XX) || defined(CONFIG_NAND_OMAP_BCH8)
# ifdef CONFIG_AM33XX
/* AM33xx uses the ELM */
/* required in case of BCH */
elm_init ( ) ;
# else
/*
* Whereas other OMAP based SoC do not have the ELM , they use the BCH
* SW library .
*/
bch_priv . control = init_bch ( 13 , 8 , 0x201b /* hw polynominal */ ) ;
if ( ! bch_priv . control ) {
puts ( " Could not init_bch() \n " ) ;
return - ENODEV ;
}
# endif
/* BCH info that will be correct for SPL or overridden otherwise. */
nand - > priv = & bch_priv ;
# endif
/* Default ECC mode */
# ifdef CONFIG_AM33XX
# if defined(CONFIG_AM33XX) || defined(CONFIG_NAND_OMAP_BCH8)
nand - > ecc . mode = NAND_ECC_HW ;
nand - > ecc . layout = & hw_bch8_nand_oob ;
nand - > ecc . size = CONFIG_SYS_NAND_ECCSIZE ;
@ -754,7 +937,9 @@ int board_nand_init(struct nand_chip *nand)
nand - > ecc . hwctl = omap_enable_ecc_bch ;
nand - > ecc . correct = omap_correct_data_bch ;
nand - > ecc . calculate = omap_calculate_ecc_bch ;
# ifdef CONFIG_AM33XX
nand - > ecc . read_page = omap_read_page_bch ;
# endif
omap_hwecc_init_bch ( nand , NAND_ECC_READ ) ;
# else
# if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_NAND_SOFTECC)