@ -103,6 +103,7 @@ struct nfc_config {
int addr_cycles ;
int nseeds ;
bool randomize ;
bool valid ;
} ;
/* minimal "boot0" style NAND support for Allwinner A20 */
@ -219,6 +220,26 @@ static int nand_load_page(const struct nfc_config *conf, u32 offs)
return 0 ;
}
static int nand_reset_column ( void )
{
writel ( ( NFC_CMD_RNDOUTSTART < < NFC_RANDOM_READ_CMD1_OFFSET ) |
( NFC_CMD_RNDOUT < < NFC_RANDOM_READ_CMD0_OFFSET ) |
( NFC_CMD_RNDOUTSTART < < NFC_READ_CMD_OFFSET ) ,
SUNXI_NFC_BASE + NFC_RCMD_SET ) ;
writel ( 0 , SUNXI_NFC_BASE + NFC_ADDR_LOW ) ;
writel ( NFC_SEND_CMD1 | NFC_SEND_CMD2 | NFC_RAW_CMD |
( 1 < < NFC_ADDR_NUM_OFFSET ) | NFC_SEND_ADR | NFC_CMD_RNDOUT ,
SUNXI_NFC_BASE + NFC_CMD ) ;
if ( ! check_value ( SUNXI_NFC_BASE + NFC_ST , NFC_ST_CMD_INT_FLAG ,
DEFAULT_TIMEOUT_US ) ) {
printf ( " Error while initializing dma interrupt \n " ) ;
return - 1 ;
}
return 0 ;
}
static int nand_read_page ( const struct nfc_config * conf , u32 offs ,
void * dest , int len )
{
@ -303,88 +324,213 @@ static int nand_read_page(const struct nfc_config *conf, u32 offs,
return ( val & 0x10000 ) ? 1 : 0 ;
}
static int nand_read_ecc ( int page_size , int ecc_strength , int ecc_page_size ,
int addr_cycles , uint32_t offs , uint32_t size , void * dest )
static int nand_max_ecc_strength ( struct nfc_config * conf )
{
void * end = dest + size ;
static const u8 strengths [ ] = { 16 , 24 , 28 , 32 , 40 , 48 , 56 , 60 , 64 } ;
struct nfc_config conf = {
. page_size = page_size ,
. ecc_size = ecc_page_size ,
. addr_cycles = addr_cycles ,
. nseeds = ARRAY_SIZE ( random_seed ) ,
. randomize = true ,
} ;
static const int ecc_bytes [ ] = { 32 , 46 , 54 , 60 , 74 , 88 , 102 , 110 , 116 } ;
int max_oobsize , max_ecc_bytes ;
int nsectors = conf - > page_size / conf - > ecc_size ;
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( strengths ) ; i + + ) {
if ( ecc_strength = = strengths [ i ] ) {
conf . ecc_strength = i ;
/*
* ECC strength is limited by the size of the OOB area which is
* correlated with the page size .
*/
switch ( conf - > page_size ) {
case 2048 :
max_oobsize = 64 ;
break ;
case 4096 :
max_oobsize = 256 ;
break ;
case 8192 :
max_oobsize = 640 ;
break ;
case 16384 :
max_oobsize = 1664 ;
break ;
default :
return - EINVAL ;
}
max_ecc_bytes = max_oobsize / nsectors ;
for ( i = 0 ; i < ARRAY_SIZE ( ecc_bytes ) ; i + + ) {
if ( ecc_bytes [ i ] > max_ecc_bytes )
break ;
}
}
if ( ! i )
return - EINVAL ;
nand_apply_config ( & conf ) ;
return i - 1 ;
}
for ( ; dest < end ; dest + = ecc_page_size , offs + = page_size ) {
if ( nand_load_page ( & conf , offs ) )
return - 1 ;
static int nand_detect_ecc_config ( struct nfc_config * conf , u32 offs ,
void * dest )
{
/* NAND with pages > 4k will likely require 1k sector size. */
int min_ecc_size = conf - > page_size > 4096 ? 1024 : 512 ;
int page = offs / conf - > page_size ;
int ret ;
if ( nand_read_page ( & conf , offs , dest , page_size ) )
return - 1 ;
/*
* In most cases , 1 k sectors are preferred over 512 b ones , start
* testing this config first .
*/
for ( conf - > ecc_size = 1024 ; conf - > ecc_size > = min_ecc_size ;
conf - > ecc_size > > = 1 ) {
int max_ecc_strength = nand_max_ecc_strength ( conf ) ;
nand_apply_config ( conf ) ;
/*
* We are starting from the maximum ECC strength because
* most of the time NAND vendors provide an OOB area that
* barely meets the ECC requirements .
*/
for ( conf - > ecc_strength = max_ecc_strength ;
conf - > ecc_strength > = 0 ;
conf - > ecc_strength - - ) {
conf - > randomize = false ;
if ( nand_reset_column ( ) )
return - EIO ;
/*
* Only read the first sector to speedup detection .
*/
ret = nand_read_page ( conf , offs , dest , conf - > ecc_size ) ;
if ( ! ret ) {
return 0 ;
} else if ( ret > 0 ) {
/*
* If page is empty we can ' t deduce anything
* about the ECC config = > stop the detection .
*/
return - EINVAL ;
}
conf - > randomize = true ;
conf - > nseeds = ARRAY_SIZE ( random_seed ) ;
do {
if ( nand_reset_column ( ) )
return - EIO ;
if ( ! nand_read_page ( conf , offs , dest ,
conf - > ecc_size ) )
return 0 ;
/*
* Find the next - > nseeds value that would
* change the randomizer seed for the page
* we ' re trying to read .
*/
while ( conf - > nseeds > = 16 ) {
int seed = page % conf - > nseeds ;
conf - > nseeds > > = 1 ;
if ( seed ! = page % conf - > nseeds )
break ;
}
} while ( conf - > nseeds > = 16 ) ;
}
}
return 0 ;
return - EINVAL ;
}
static int nand_read_buffer ( uint32_t offs , unsigned int size , void * dest )
static int nand_detect_config ( struct nfc_config * conf , u32 offs , void * dest )
{
const struct {
int page_size ;
int ecc_strength ;
int ecc_page_size ;
int addr_cycles ;
} nand_configs [ ] = {
{ 8192 , 40 , 1024 , 5 } ,
{ 16384 , 56 , 1024 , 5 } ,
{ 8192 , 24 , 1024 , 5 } ,
{ 4096 , 24 , 1024 , 5 } ,
} ;
static int nand_config = - 1 ;
int i ;
if ( conf - > valid )
return 0 ;
if ( nand_config = = - 1 ) {
for ( i = 0 ; i < ARRAY_SIZE ( nand_configs ) ; i + + ) {
debug ( " nand: trying page %d ecc %d / %d addr %d: " ,
nand_configs [ i ] . page_size ,
nand_configs [ i ] . ecc_strength ,
nand_configs [ i ] . ecc_page_size ,
nand_configs [ i ] . addr_cycles ) ;
if ( nand_read_ecc ( nand_configs [ i ] . page_size ,
nand_configs [ i ] . ecc_strength ,
nand_configs [ i ] . ecc_page_size ,
nand_configs [ i ] . addr_cycles ,
offs , size , dest ) = = 0 ) {
debug ( " success \n " ) ;
nand_config = i ;
/*
* Modern NANDs are more likely than legacy ones , so we start testing
* with 5 address cycles .
*/
for ( conf - > addr_cycles = 5 ;
conf - > addr_cycles > = 4 ;
conf - > addr_cycles - - ) {
int max_page_size = conf - > addr_cycles = = 4 ? 2048 : 16384 ;
/*
* Ignoring 1 k pages cause I ' m not even sure this case exist
* in the real world .
*/
for ( conf - > page_size = 2048 ; conf - > page_size < = max_page_size ;
conf - > page_size < < = 1 ) {
if ( nand_load_page ( conf , offs ) )
return - 1 ;
if ( ! nand_detect_ecc_config ( conf , offs , dest ) ) {
conf - > valid = true ;
return 0 ;
}
debug ( " failed \n " ) ;
}
return - 1 ;
}
return nand_read_ecc ( nand_configs [ nand_config ] . page_size ,
nand_configs [ nand_config ] . ecc_strength ,
nand_configs [ nand_config ] . ecc_page_size ,
nand_configs [ nand_config ] . addr_cycles ,
offs , size , dest ) ;
return - EINVAL ;
}
static int nand_read_buffer ( struct nfc_config * conf , uint32_t offs ,
unsigned int size , void * dest )
{
int first_seed , page , ret ;
size = ALIGN ( size , conf - > page_size ) ;
page = offs / conf - > page_size ;
first_seed = page % conf - > nseeds ;
for ( ; size ; size - = conf - > page_size ) {
if ( nand_load_page ( conf , offs ) )
return - 1 ;
ret = nand_read_page ( conf , offs , dest , conf - > page_size ) ;
/*
* The - > nseeds value should be equal to the number of pages
* in an eraseblock . Since we don ' t know this information in
* advance we might have picked a wrong value .
*/
if ( ret < 0 & & conf - > randomize ) {
int cur_seed = page % conf - > nseeds ;
/*
* We already tried all the seed values = > we are
* facing a real corruption .
*/
if ( cur_seed < first_seed )
return - EIO ;
/* Try to adjust ->nseeds and read the page again... */
conf - > nseeds = cur_seed ;
if ( nand_reset_column ( ) )
return - EIO ;
/* ... it still fails => it's a real corruption. */
if ( nand_read_page ( conf , offs , dest , conf - > page_size ) )
return - EIO ;
} else if ( ret & & conf - > randomize ) {
memset ( dest , 0xff , conf - > page_size ) ;
}
page + + ;
offs + = conf - > page_size ;
dest + = conf - > page_size ;
}
return 0 ;
}
int nand_spl_load_image ( uint32_t offs , unsigned int size , void * dest )
{
return nand_read_buffer ( offs , size , dest ) ;
static struct nfc_config conf = { } ;
int ret ;
ret = nand_detect_config ( & conf , offs , dest ) ;
if ( ret )
return ret ;
return nand_read_buffer ( & conf , offs , size , dest ) ;
}
void nand_deselect ( void )