@ -31,6 +31,209 @@
# include <nand.h>
# ifdef CONFIG_ATMEL_NAND_HWECC
/* Register access macros */
# define ecc_readl(add, reg) \
readl ( AT91_BASE_SYS + add + ATMEL_ECC_ # # reg )
# define ecc_writel(add, reg, value) \
writel ( ( value ) , AT91_BASE_SYS + add + ATMEL_ECC_ # # reg )
# include "atmel_nand_ecc.h" /* Hardware ECC registers */
/* oob layout for large page size
* bad block info is on bytes 0 and 1
* the bytes have to be consecutives to avoid
* several NAND_CMD_RNDOUT during read
*/
static struct nand_ecclayout atmel_oobinfo_large = {
. eccbytes = 4 ,
. eccpos = { 60 , 61 , 62 , 63 } ,
. oobfree = {
{ 2 , 58 }
} ,
} ;
/* oob layout for small page size
* bad block info is on bytes 4 and 5
* the bytes have to be consecutives to avoid
* several NAND_CMD_RNDOUT during read
*/
static struct nand_ecclayout atmel_oobinfo_small = {
. eccbytes = 4 ,
. eccpos = { 0 , 1 , 2 , 3 } ,
. oobfree = {
{ 6 , 10 }
} ,
} ;
/*
* Calculate HW ECC
*
* function called after a write
*
* mtd : MTD block structure
* dat : raw data ( unused )
* ecc_code : buffer for ECC
*/
static int atmel_nand_calculate ( struct mtd_info * mtd ,
const u_char * dat , unsigned char * ecc_code )
{
struct nand_chip * nand_chip = mtd - > priv ;
unsigned int ecc_value ;
/* get the first 2 ECC bytes */
ecc_value = ecc_readl ( CONFIG_SYS_NAND_ECC_BASE , PR ) ;
ecc_code [ 0 ] = ecc_value & 0xFF ;
ecc_code [ 1 ] = ( ecc_value > > 8 ) & 0xFF ;
/* get the last 2 ECC bytes */
ecc_value = ecc_readl ( CONFIG_SYS_NAND_ECC_BASE , NPR ) & ATMEL_ECC_NPARITY ;
ecc_code [ 2 ] = ecc_value & 0xFF ;
ecc_code [ 3 ] = ( ecc_value > > 8 ) & 0xFF ;
return 0 ;
}
/*
* HW ECC read page function
*
* mtd : mtd info structure
* chip : nand chip info structure
* buf : buffer to store read data
*/
static int atmel_nand_read_page ( struct mtd_info * mtd ,
struct nand_chip * chip , uint8_t * buf , int page )
{
int eccsize = chip - > ecc . size ;
int eccbytes = chip - > ecc . bytes ;
uint32_t * eccpos = chip - > ecc . layout - > eccpos ;
uint8_t * p = buf ;
uint8_t * oob = chip - > oob_poi ;
uint8_t * ecc_pos ;
int stat ;
/* read the page */
chip - > read_buf ( mtd , p , eccsize ) ;
/* move to ECC position if needed */
if ( eccpos [ 0 ] ! = 0 ) {
/* This only works on large pages
* because the ECC controller waits for
* NAND_CMD_RNDOUTSTART after the
* NAND_CMD_RNDOUT .
* anyway , for small pages , the eccpos [ 0 ] = = 0
*/
chip - > cmdfunc ( mtd , NAND_CMD_RNDOUT ,
mtd - > writesize + eccpos [ 0 ] , - 1 ) ;
}
/* the ECC controller needs to read the ECC just after the data */
ecc_pos = oob + eccpos [ 0 ] ;
chip - > read_buf ( mtd , ecc_pos , eccbytes ) ;
/* check if there's an error */
stat = chip - > ecc . correct ( mtd , p , oob , NULL ) ;
if ( stat < 0 )
mtd - > ecc_stats . failed + + ;
else
mtd - > ecc_stats . corrected + = stat ;
/* get back to oob start (end of page) */
chip - > cmdfunc ( mtd , NAND_CMD_RNDOUT , mtd - > writesize , - 1 ) ;
/* read the oob */
chip - > read_buf ( mtd , oob , mtd - > oobsize ) ;
return 0 ;
}
/*
* HW ECC Correction
*
* function called after a read
*
* mtd : MTD block structure
* dat : raw data read from the chip
* read_ecc : ECC from the chip ( unused )
* isnull : unused
*
* Detect and correct a 1 bit error for a page
*/
static int atmel_nand_correct ( struct mtd_info * mtd , u_char * dat ,
u_char * read_ecc , u_char * isnull )
{
struct nand_chip * nand_chip = mtd - > priv ;
unsigned int ecc_status , ecc_parity , ecc_mode ;
unsigned int ecc_word , ecc_bit ;
/* get the status from the Status Register */
ecc_status = ecc_readl ( CONFIG_SYS_NAND_ECC_BASE , SR ) ;
/* if there's no error */
if ( likely ( ! ( ecc_status & ATMEL_ECC_RECERR ) ) )
return 0 ;
/* get error bit offset (4 bits) */
ecc_bit = ecc_readl ( CONFIG_SYS_NAND_ECC_BASE , PR ) & ATMEL_ECC_BITADDR ;
/* get word address (12 bits) */
ecc_word = ecc_readl ( CONFIG_SYS_NAND_ECC_BASE , PR ) & ATMEL_ECC_WORDADDR ;
ecc_word > > = 4 ;
/* if there are multiple errors */
if ( ecc_status & ATMEL_ECC_MULERR ) {
/* check if it is a freshly erased block
* ( filled with 0xff ) */
if ( ( ecc_bit = = ATMEL_ECC_BITADDR )
& & ( ecc_word = = ( ATMEL_ECC_WORDADDR > > 4 ) ) ) {
/* the block has just been erased, return OK */
return 0 ;
}
/* it doesn't seems to be a freshly
* erased block .
* We can ' t correct so many errors */
printk ( KERN_WARNING " atmel_nand : multiple errors detected. "
" Unable to correct. \n " ) ;
return - EIO ;
}
/* if there's a single bit error : we can correct it */
if ( ecc_status & ATMEL_ECC_ECCERR ) {
/* there's nothing much to do here.
* the bit error is on the ECC itself .
*/
printk ( KERN_WARNING " atmel_nand : one bit error on ECC code. "
" Nothing to correct \n " ) ;
return 0 ;
}
printk ( KERN_WARNING " atmel_nand : one bit error on data. "
" (word offset in the page : "
" 0x%x bit offset : 0x%x) \n " ,
ecc_word , ecc_bit ) ;
/* correct the error */
if ( nand_chip - > options & NAND_BUSWIDTH_16 ) {
/* 16 bits words */
( ( unsigned short * ) dat ) [ ecc_word ] ^ = ( 1 < < ecc_bit ) ;
} else {
/* 8 bits words */
dat [ ecc_word ] ^ = ( 1 < < ecc_bit ) ;
}
printk ( KERN_WARNING " atmel_nand : error corrected \n " ) ;
return 1 ;
}
/*
* Enable HW ECC : unused on most chips
*/
static void atmel_nand_hwctl ( struct mtd_info * mtd , int mode )
{
}
# endif
static void at91_nand_hwcontrol ( struct mtd_info * mtd ,
int cmd , unsigned int ctrl )
{
@ -64,6 +267,11 @@ static int at91_nand_ready(struct mtd_info *mtd)
int board_nand_init ( struct nand_chip * nand )
{
# ifdef CONFIG_ATMEL_NAND_HWECC
static int chip_nr = 0 ;
struct mtd_info * mtd ;
# endif
nand - > ecc . mode = NAND_ECC_SOFT ;
# ifdef CONFIG_SYS_NAND_DBW_16
nand - > options = NAND_BUSWIDTH_16 ;
@ -74,5 +282,62 @@ int board_nand_init(struct nand_chip *nand)
# endif
nand - > chip_delay = 20 ;
# ifdef CONFIG_ATMEL_NAND_HWECC
nand - > ecc . mode = NAND_ECC_HW ;
nand - > ecc . calculate = atmel_nand_calculate ;
nand - > ecc . correct = atmel_nand_correct ;
nand - > ecc . hwctl = atmel_nand_hwctl ;
nand - > ecc . read_page = atmel_nand_read_page ;
nand - > ecc . bytes = 4 ;
# endif
# ifdef CONFIG_ATMEL_NAND_HWECC
mtd = & nand_info [ chip_nr + + ] ;
mtd - > priv = nand ;
/* Detect NAND chips */
if ( nand_scan_ident ( mtd , 1 ) ) {
printk ( KERN_WARNING " NAND Flash not found ! \n " ) ;
return - ENXIO ;
}
if ( nand - > ecc . mode = = NAND_ECC_HW ) {
/* ECC is calculated for the whole page (1 step) */
nand - > ecc . size = mtd - > writesize ;
/* set ECC page size and oob layout */
switch ( mtd - > writesize ) {
case 512 :
nand - > ecc . layout = & atmel_oobinfo_small ;
ecc_writel ( CONFIG_SYS_NAND_ECC_BASE , MR , ATMEL_ECC_PAGESIZE_528 ) ;
break ;
case 1024 :
nand - > ecc . layout = & atmel_oobinfo_large ;
ecc_writel ( CONFIG_SYS_NAND_ECC_BASE , MR , ATMEL_ECC_PAGESIZE_1056 ) ;
break ;
case 2048 :
nand - > ecc . layout = & atmel_oobinfo_large ;
ecc_writel ( CONFIG_SYS_NAND_ECC_BASE , MR , ATMEL_ECC_PAGESIZE_2112 ) ;
break ;
case 4096 :
nand - > ecc . layout = & atmel_oobinfo_large ;
ecc_writel ( CONFIG_SYS_NAND_ECC_BASE , MR , ATMEL_ECC_PAGESIZE_4224 ) ;
break ;
default :
/* page size not handled by HW ECC */
/* switching back to soft ECC */
nand - > ecc . mode = NAND_ECC_SOFT ;
nand - > ecc . calculate = NULL ;
nand - > ecc . correct = NULL ;
nand - > ecc . hwctl = NULL ;
nand - > ecc . read_page = NULL ;
nand - > ecc . postpad = 0 ;
nand - > ecc . prepad = 0 ;
nand - > ecc . bytes = 0 ;
break ;
}
}
# endif
return 0 ;
}