@ -7,7 +7,7 @@
* Basic support for AG - AND chips is provided .
*
* Additional technical information is available on
* http : //www.linux-mtd.infradead.org/tech /nand.html
* http : //www.linux-mtd.infradead.org/doc /nand.html
*
* Copyright ( C ) 2000 Steven J . Hill ( sjhill @ realitydiluted . com )
* 2002 - 2006 Thomas Gleixner ( tglx @ linutronix . de )
@ -24,6 +24,7 @@
* if we have HW ecc support .
* The AG - AND chips have nice features for speed improvement ,
* which are not supported yet . Read / program 4 pages in one go .
* BBT table is not serialized , has to be fixed
*
* 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
@ -128,7 +129,7 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
static int nand_wait ( struct mtd_info * mtd , struct nand_chip * this ) ;
/*
* For devices which display every fart in the system on a sepe rate LED . Is
* For devices which display every fart in the system on a sepa rate LED . Is
* compiled away when LED support is disabled .
*/
/* XXX U-BOOT XXX */
@ -412,6 +413,7 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
/* We write two bytes, so we dont have to mess with 16 bit
* access
*/
nand_get_device ( chip , mtd , FL_WRITING ) ;
ofs + = mtd - > oobsize ;
chip - > ops . len = chip - > ops . ooblen = 2 ;
chip - > ops . datbuf = NULL ;
@ -419,9 +421,11 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
chip - > ops . ooboffs = chip - > badblockpos & ~ 0x01 ;
ret = nand_do_write_oob ( mtd , ofs , & chip - > ops ) ;
nand_release_device ( mtd ) ;
}
if ( ! ret )
mtd - > ecc_stats . badblocks + + ;
return ret ;
}
@ -911,7 +915,88 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
int stat ;
stat = chip - > ecc . correct ( mtd , p , & ecc_code [ i ] , & ecc_calc [ i ] ) ;
if ( stat = = - 1 )
if ( stat < 0 )
mtd - > ecc_stats . failed + + ;
else
mtd - > ecc_stats . corrected + = stat ;
}
return 0 ;
}
/**
* nand_read_subpage - [ REPLACABLE ] software ecc based sub - page read function
* @ mtd : mtd info structure
* @ chip : nand chip info structure
* @ dataofs offset of requested data within the page
* @ readlen data length
* @ buf : buffer to store read data
*/
static int nand_read_subpage ( struct mtd_info * mtd , struct nand_chip * chip , uint32_t data_offs , uint32_t readlen , uint8_t * bufpoi )
{
int start_step , end_step , num_steps ;
uint32_t * eccpos = chip - > ecc . layout - > eccpos ;
uint8_t * p ;
int data_col_addr , i , gaps = 0 ;
int datafrag_len , eccfrag_len , aligned_len , aligned_pos ;
int busw = ( chip - > options & NAND_BUSWIDTH_16 ) ? 2 : 1 ;
/* Column address wihin the page aligned to ECC size (256bytes). */
start_step = data_offs / chip - > ecc . size ;
end_step = ( data_offs + readlen - 1 ) / chip - > ecc . size ;
num_steps = end_step - start_step + 1 ;
/* Data size aligned to ECC ecc.size*/
datafrag_len = num_steps * chip - > ecc . size ;
eccfrag_len = num_steps * chip - > ecc . bytes ;
data_col_addr = start_step * chip - > ecc . size ;
/* If we read not a page aligned data */
if ( data_col_addr ! = 0 )
chip - > cmdfunc ( mtd , NAND_CMD_RNDOUT , data_col_addr , - 1 ) ;
p = bufpoi + data_col_addr ;
chip - > read_buf ( mtd , p , datafrag_len ) ;
/* Calculate ECC */
for ( i = 0 ; i < eccfrag_len ; i + = chip - > ecc . bytes , p + = chip - > ecc . size )
chip - > ecc . calculate ( mtd , p , & chip - > buffers - > ecccalc [ i ] ) ;
/* The performance is faster if to position offsets
according to ecc . pos . Let make sure here that
there are no gaps in ecc positions */
for ( i = 0 ; i < eccfrag_len - 1 ; i + + ) {
if ( eccpos [ i + start_step * chip - > ecc . bytes ] + 1 ! =
eccpos [ i + start_step * chip - > ecc . bytes + 1 ] ) {
gaps = 1 ;
break ;
}
}
if ( gaps ) {
chip - > cmdfunc ( mtd , NAND_CMD_RNDOUT , mtd - > writesize , - 1 ) ;
chip - > read_buf ( mtd , chip - > oob_poi , mtd - > oobsize ) ;
} else {
/* send the command to read the particular ecc bytes */
/* take care about buswidth alignment in read_buf */
aligned_pos = eccpos [ start_step * chip - > ecc . bytes ] & ~ ( busw - 1 ) ;
aligned_len = eccfrag_len ;
if ( eccpos [ start_step * chip - > ecc . bytes ] & ( busw - 1 ) )
aligned_len + + ;
if ( eccpos [ ( start_step + num_steps ) * chip - > ecc . bytes ] & ( busw - 1 ) )
aligned_len + + ;
chip - > cmdfunc ( mtd , NAND_CMD_RNDOUT , mtd - > writesize + aligned_pos , - 1 ) ;
chip - > read_buf ( mtd , & chip - > oob_poi [ aligned_pos ] , aligned_len ) ;
}
for ( i = 0 ; i < eccfrag_len ; i + + )
chip - > buffers - > ecccode [ i ] = chip - > oob_poi [ eccpos [ i + start_step * chip - > ecc . bytes ] ] ;
p = bufpoi + data_col_addr ;
for ( i = 0 ; i < eccfrag_len ; i + = chip - > ecc . bytes , p + = chip - > ecc . size ) {
int stat ;
stat = chip - > ecc . correct ( mtd , p , & chip - > buffers - > ecccode [ i ] , & chip - > buffers - > ecccalc [ i ] ) ;
if ( stat < 0 )
mtd - > ecc_stats . failed + + ;
else
mtd - > ecc_stats . corrected + = stat ;
@ -996,7 +1081,7 @@ static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
chip - > read_buf ( mtd , oob , eccbytes ) ;
stat = chip - > ecc . correct ( mtd , p , oob , NULL ) ;
if ( stat = = - 1 )
if ( stat < 0 )
mtd - > ecc_stats . failed + + ;
else
mtd - > ecc_stats . corrected + = stat ;
@ -1116,6 +1201,8 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
/* Now read the page into the buffer */
if ( unlikely ( ops - > mode = = MTD_OOB_RAW ) )
ret = chip - > ecc . read_page_raw ( mtd , chip , bufpoi ) ;
else if ( ! aligned & & NAND_SUBPAGE_READ ( chip ) & & ! oob )
ret = chip - > ecc . read_subpage ( mtd , chip , col , bytes , bufpoi ) ;
else
ret = chip - > ecc . read_page ( mtd , chip , bufpoi ) ;
if ( ret < 0 )
@ -1123,7 +1210,8 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
/* Transfer not aligned data */
if ( ! aligned ) {
chip - > pagebuf = realpage ;
if ( ! NAND_SUBPAGE_READ ( chip ) & & ! oob )
chip - > pagebuf = realpage ;
memcpy ( buf , chip - > buffers - > databuf + col , bytes ) ;
}
@ -2193,13 +2281,14 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
erase_exit :
ret = instr - > state = = MTD_ERASE_DONE ? 0 : - EIO ;
/* Do call back function */
if ( ! ret )
mtd_erase_callback ( instr ) ;
/* Deselect and wake up anyone waiting on the device */
nand_release_device ( mtd ) ;
/* Do call back function */
if ( ! ret )
mtd_erase_callback ( instr ) ;
/*
* If BBT requires refresh and erase was successful , rewrite any
* selected bad block tables
@ -2356,6 +2445,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
{
struct nand_flash_dev * type = NULL ;
int i , dev_id , maf_idx ;
int tmp_id , tmp_manf ;
/* Select the device */
chip - > select_chip ( mtd , 0 ) ;
@ -2367,6 +2457,26 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
* maf_id = chip - > read_byte ( mtd ) ;
dev_id = chip - > read_byte ( mtd ) ;
/* Try again to make sure, as some systems the bus-hold or other
* interface concerns can cause random data which looks like a
* possibly credible NAND flash to appear . If the two results do
* not match , ignore the device completely .
*/
chip - > cmdfunc ( mtd , NAND_CMD_READID , 0x00 , - 1 ) ;
/* Read manufacturer and device IDs */
tmp_manf = chip - > read_byte ( mtd ) ;
tmp_id = chip - > read_byte ( mtd ) ;
if ( tmp_manf ! = * maf_id | | tmp_id ! = dev_id ) {
printk ( KERN_INFO " %s: second ID read did not match "
" %02x,%02x against %02x,%02x \n " , __func__ ,
* maf_id , dev_id , tmp_manf , tmp_id ) ;
return ERR_PTR ( - ENODEV ) ;
}
/* Lookup the flash id */
for ( i = 0 ; nand_flash_ids [ i ] . name ! = NULL ; i + + ) {
if ( dev_id = = nand_flash_ids [ i ] . id ) {
@ -2630,6 +2740,7 @@ int nand_scan_tail(struct mtd_info *mtd)
chip - > ecc . calculate = nand_calculate_ecc ;
chip - > ecc . correct = nand_correct_data ;
chip - > ecc . read_page = nand_read_page_swecc ;
chip - > ecc . read_subpage = nand_read_subpage ;
chip - > ecc . write_page = nand_write_page_swecc ;
chip - > ecc . read_oob = nand_read_oob_std ;
chip - > ecc . write_oob = nand_write_oob_std ;