2005-11-07 14:15:49 +03:00
/*
2005-04-17 02:20:36 +04:00
* drivers / mtd / nand / diskonchip . c
*
* ( C ) 2003 Red Hat , Inc .
* ( C ) 2004 Dan Brown < dan_brown @ ieee . org >
* ( C ) 2004 Kalev Lember < kalev @ smartlink . ee >
*
* Author : David Woodhouse < dwmw2 @ infradead . org >
* Additional Diskonchip 2000 and Millennium support by Dan Brown < dan_brown @ ieee . org >
* Diskonchip Millennium Plus support by Kalev Lember < kalev @ smartlink . ee >
2005-11-07 14:15:49 +03:00
*
2005-04-17 02:20:36 +04:00
* Error correction code lifted from the old docecc code
2005-11-07 14:15:49 +03:00
* Author : Fabrice Bellard ( fabrice . bellard @ netgem . com )
2005-04-17 02:20:36 +04:00
* Copyright ( C ) 2000 Netgem S . A .
* converted to the generic Reed - Solomon library by Thomas Gleixner < tglx @ linutronix . de >
2005-11-07 14:15:49 +03:00
*
2005-04-17 02:20:36 +04:00
* Interface to generic NAND code for M - Systems DiskOnChip devices
*
2005-11-07 14:15:49 +03:00
* $ Id : diskonchip . c , v 1.55 2005 / 11 / 07 11 : 14 : 30 gleixner Exp $
2005-04-17 02:20:36 +04:00
*/
# include <linux/kernel.h>
# include <linux/init.h>
# include <linux/sched.h>
# include <linux/delay.h>
# include <linux/rslib.h>
# include <linux/moduleparam.h>
# include <asm/io.h>
# include <linux/mtd/mtd.h>
# include <linux/mtd/nand.h>
# include <linux/mtd/doc2000.h>
# include <linux/mtd/compatmac.h>
# include <linux/mtd/partitions.h>
# include <linux/mtd/inftl.h>
/* Where to look for the devices? */
2005-01-31 23:36:46 +03:00
# ifndef CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDRESS
# define CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDRESS 0
2005-04-17 02:20:36 +04:00
# endif
static unsigned long __initdata doc_locations [ ] = {
# if defined (__alpha__) || defined(__i386__) || defined(__x86_64__)
2005-01-31 23:36:46 +03:00
# ifdef CONFIG_MTD_NAND_DISKONCHIP_PROBE_HIGH
2005-11-07 14:15:49 +03:00
0xfffc8000 , 0xfffca000 , 0xfffcc000 , 0xfffce000 ,
2005-04-17 02:20:36 +04:00
0xfffd0000 , 0xfffd2000 , 0xfffd4000 , 0xfffd6000 ,
2005-11-07 14:15:49 +03:00
0xfffd8000 , 0xfffda000 , 0xfffdc000 , 0xfffde000 ,
0xfffe0000 , 0xfffe2000 , 0xfffe4000 , 0xfffe6000 ,
2005-04-17 02:20:36 +04:00
0xfffe8000 , 0xfffea000 , 0xfffec000 , 0xfffee000 ,
# else /* CONFIG_MTD_DOCPROBE_HIGH */
2005-11-07 14:15:49 +03:00
0xc8000 , 0xca000 , 0xcc000 , 0xce000 ,
2005-04-17 02:20:36 +04:00
0xd0000 , 0xd2000 , 0xd4000 , 0xd6000 ,
2005-11-07 14:15:49 +03:00
0xd8000 , 0xda000 , 0xdc000 , 0xde000 ,
0xe0000 , 0xe2000 , 0xe4000 , 0xe6000 ,
2005-04-17 02:20:36 +04:00
0xe8000 , 0xea000 , 0xec000 , 0xee000 ,
# endif /* CONFIG_MTD_DOCPROBE_HIGH */
# elif defined(__PPC__)
0xe4000000 ,
# elif defined(CONFIG_MOMENCO_OCELOT)
0x2f000000 ,
2006-05-13 21:07:53 +04:00
0xff000000 ,
2005-04-17 02:20:36 +04:00
# elif defined(CONFIG_MOMENCO_OCELOT_G) || defined (CONFIG_MOMENCO_OCELOT_C)
2006-05-13 21:07:53 +04:00
0xff000000 ,
# else
2005-04-17 02:20:36 +04:00
# warning Unknown architecture for DiskOnChip. No default probe locations defined
# endif
0xffffffff } ;
static struct mtd_info * doclist = NULL ;
struct doc_priv {
void __iomem * virtadr ;
unsigned long physadr ;
u_char ChipID ;
u_char CDSNControl ;
2006-05-13 21:07:53 +04:00
int chips_per_floor ; /* The number of chips detected on each floor */
2005-04-17 02:20:36 +04:00
int curfloor ;
int curchip ;
int mh0_page ;
int mh1_page ;
struct mtd_info * nextdoc ;
} ;
/* This is the syndrome computed by the HW ecc generator upon reading an empty
page , one with all 0xff for data and stored ecc code . */
static u_char empty_read_syndrome [ 6 ] = { 0x26 , 0xff , 0x6d , 0x47 , 0x73 , 0x7a } ;
2006-05-13 21:07:53 +04:00
2005-04-17 02:20:36 +04:00
/* This is the ecc value computed by the HW ecc generator upon writing an empty
page , one with all 0xff for data . */
static u_char empty_write_ecc [ 6 ] = { 0x4b , 0x00 , 0xe2 , 0x0e , 0x93 , 0xf7 } ;
# define INFTL_BBT_RESERVED_BLOCKS 4
# define DoC_is_MillenniumPlus(doc) ((doc)->ChipID == DOC_ChipID_DocMilPlus16 || (doc)->ChipID == DOC_ChipID_DocMilPlus32)
# define DoC_is_Millennium(doc) ((doc)->ChipID == DOC_ChipID_DocMil)
# define DoC_is_2000(doc) ((doc)->ChipID == DOC_ChipID_Doc2k)
2006-05-24 01:25:53 +04:00
static void doc200x_hwcontrol ( struct mtd_info * mtd , int cmd ,
unsigned int bitmask ) ;
2005-04-17 02:20:36 +04:00
static void doc200x_select_chip ( struct mtd_info * mtd , int chip ) ;
2006-05-13 21:07:53 +04:00
static int debug = 0 ;
2005-04-17 02:20:36 +04:00
module_param ( debug , int , 0 ) ;
2006-05-13 21:07:53 +04:00
static int try_dword = 1 ;
2005-04-17 02:20:36 +04:00
module_param ( try_dword , int , 0 ) ;
2006-05-13 21:07:53 +04:00
static int no_ecc_failures = 0 ;
2005-04-17 02:20:36 +04:00
module_param ( no_ecc_failures , int , 0 ) ;
2006-05-13 21:07:53 +04:00
static int no_autopart = 0 ;
2005-04-17 02:20:36 +04:00
module_param ( no_autopart , int , 0 ) ;
2005-03-30 00:57:48 +04:00
2006-05-13 21:07:53 +04:00
static int show_firmware_partition = 0 ;
2005-03-30 00:57:48 +04:00
module_param ( show_firmware_partition , int , 0 ) ;
2005-04-17 02:20:36 +04:00
# ifdef MTD_NAND_DISKONCHIP_BBTWRITE
2006-05-13 21:07:53 +04:00
static int inftl_bbt_write = 1 ;
2005-04-17 02:20:36 +04:00
# else
2006-05-13 21:07:53 +04:00
static int inftl_bbt_write = 0 ;
2005-04-17 02:20:36 +04:00
# endif
module_param ( inftl_bbt_write , int , 0 ) ;
2005-01-31 23:36:46 +03:00
static unsigned long doc_config_location = CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDRESS ;
2005-04-17 02:20:36 +04:00
module_param ( doc_config_location , ulong , 0 ) ;
MODULE_PARM_DESC ( doc_config_location , " Physical memory address at which to probe for DiskOnChip " ) ;
/* Sector size for HW ECC */
# define SECTOR_SIZE 512
/* The sector bytes are packed into NB_DATA 10 bit words */
# define NB_DATA (((SECTOR_SIZE + 1) * 8 + 6) / 10)
/* Number of roots */
# define NROOTS 4
/* First consective root */
# define FCR 510
/* Number of symbols */
# define NN 1023
/* the Reed Solomon control structure */
static struct rs_control * rs_decoder ;
2005-11-07 14:15:49 +03:00
/*
2005-04-17 02:20:36 +04:00
* The HW decoder in the DoC ASIC ' s provides us a error syndrome ,
* which we must convert to a standard syndrom usable by the generic
* Reed - Solomon library code .
*
* Fabrice Bellard figured this out in the old docecc code . I added
* some comments , improved a minor bit and converted it to make use
* of the generic Reed - Solomon libary . tglx
*/
2006-05-13 21:07:53 +04:00
static int doc_ecc_decode ( struct rs_control * rs , uint8_t * data , uint8_t * ecc )
2005-04-17 02:20:36 +04:00
{
int i , j , nerr , errpos [ 8 ] ;
uint8_t parity ;
uint16_t ds [ 4 ] , s [ 5 ] , tmp , errval [ 8 ] , syn [ 4 ] ;
/* Convert the ecc bytes into words */
ds [ 0 ] = ( ( ecc [ 4 ] & 0xff ) > > 0 ) | ( ( ecc [ 5 ] & 0x03 ) < < 8 ) ;
ds [ 1 ] = ( ( ecc [ 5 ] & 0xfc ) > > 2 ) | ( ( ecc [ 2 ] & 0x0f ) < < 6 ) ;
ds [ 2 ] = ( ( ecc [ 2 ] & 0xf0 ) > > 4 ) | ( ( ecc [ 3 ] & 0x3f ) < < 4 ) ;
ds [ 3 ] = ( ( ecc [ 3 ] & 0xc0 ) > > 6 ) | ( ( ecc [ 0 ] & 0xff ) < < 2 ) ;
parity = ecc [ 1 ] ;
/* Initialize the syndrom buffer */
for ( i = 0 ; i < NROOTS ; i + + )
s [ i ] = ds [ 0 ] ;
2005-11-07 14:15:49 +03:00
/*
* Evaluate
2005-04-17 02:20:36 +04:00
* s [ i ] = ds [ 3 ] x ^ 3 + ds [ 2 ] x ^ 2 + ds [ 1 ] x ^ 1 + ds [ 0 ]
* where x = alpha ^ ( FCR + i )
*/
2006-05-13 21:07:53 +04:00
for ( j = 1 ; j < NROOTS ; j + + ) {
if ( ds [ j ] = = 0 )
2005-04-17 02:20:36 +04:00
continue ;
tmp = rs - > index_of [ ds [ j ] ] ;
2006-05-13 21:07:53 +04:00
for ( i = 0 ; i < NROOTS ; i + + )
2005-04-17 02:20:36 +04:00
s [ i ] ^ = rs - > alpha_to [ rs_modnn ( rs , tmp + ( FCR + i ) * j ) ] ;
}
/* Calc s[i] = s[i] / alpha^(v + i) */
for ( i = 0 ; i < NROOTS ; i + + ) {
if ( syn [ i ] )
2006-05-13 21:07:53 +04:00
syn [ i ] = rs_modnn ( rs , rs - > index_of [ s [ i ] ] + ( NN - FCR - i ) ) ;
2005-04-17 02:20:36 +04:00
}
/* Call the decoder library */
nerr = decode_rs16 ( rs , NULL , NULL , 1019 , syn , 0 , errpos , 0 , errval ) ;
/* Incorrectable errors ? */
if ( nerr < 0 )
return nerr ;
2005-11-07 14:15:49 +03:00
/*
2005-04-17 02:20:36 +04:00
* Correct the errors . The bitpositions are a bit of magic ,
* but they are given by the design of the de / encoder circuit
* in the DoC ASIC ' s .
*/
2006-05-13 21:07:53 +04:00
for ( i = 0 ; i < nerr ; i + + ) {
2005-04-17 02:20:36 +04:00
int index , bitpos , pos = 1015 - errpos [ i ] ;
uint8_t val ;
if ( pos > = NB_DATA & & pos < 1019 )
continue ;
if ( pos < NB_DATA ) {
/* extract bit position (MSB first) */
pos = 10 * ( NB_DATA - 1 - pos ) - 6 ;
/* now correct the following 10 bits. At most two bytes
can be modified since pos is even */
index = ( pos > > 3 ) ^ 1 ;
bitpos = pos & 7 ;
2006-05-13 21:07:53 +04:00
if ( ( index > = 0 & & index < SECTOR_SIZE ) | | index = = ( SECTOR_SIZE + 1 ) ) {
2005-04-17 02:20:36 +04:00
val = ( uint8_t ) ( errval [ i ] > > ( 2 + bitpos ) ) ;
parity ^ = val ;
if ( index < SECTOR_SIZE )
data [ index ] ^ = val ;
}
index = ( ( pos > > 3 ) + 1 ) ^ 1 ;
bitpos = ( bitpos + 10 ) & 7 ;
if ( bitpos = = 0 )
bitpos = 8 ;
2006-05-13 21:07:53 +04:00
if ( ( index > = 0 & & index < SECTOR_SIZE ) | | index = = ( SECTOR_SIZE + 1 ) ) {
val = ( uint8_t ) ( errval [ i ] < < ( 8 - bitpos ) ) ;
2005-04-17 02:20:36 +04:00
parity ^ = val ;
if ( index < SECTOR_SIZE )
data [ index ] ^ = val ;
}
}
}
/* If the parity is wrong, no rescue possible */
return parity ? - 1 : nerr ;
}
static void DoC_Delay ( struct doc_priv * doc , unsigned short cycles )
{
volatile char dummy ;
int i ;
2005-11-07 14:15:49 +03:00
2005-04-17 02:20:36 +04:00
for ( i = 0 ; i < cycles ; i + + ) {
if ( DoC_is_Millennium ( doc ) )
dummy = ReadDOC ( doc - > virtadr , NOP ) ;
else if ( DoC_is_MillenniumPlus ( doc ) )
dummy = ReadDOC ( doc - > virtadr , Mplus_NOP ) ;
else
dummy = ReadDOC ( doc - > virtadr , DOCStatus ) ;
}
2005-11-07 14:15:49 +03:00
2005-04-17 02:20:36 +04:00
}
# define CDSN_CTRL_FR_B_MASK (CDSN_CTRL_FR_B0 | CDSN_CTRL_FR_B1)
/* DOC_WaitReady: Wait for RDY line to be asserted by the flash chip */
static int _DoC_WaitReady ( struct doc_priv * doc )
{
2006-05-13 21:07:53 +04:00
void __iomem * docptr = doc - > virtadr ;
2005-04-17 02:20:36 +04:00
unsigned long timeo = jiffies + ( HZ * 10 ) ;
2006-05-13 21:07:53 +04:00
if ( debug )
printk ( " _DoC_WaitReady... \n " ) ;
2005-04-17 02:20:36 +04:00
/* Out-of-line routine to wait for chip response */
if ( DoC_is_MillenniumPlus ( doc ) ) {
while ( ( ReadDOC ( docptr , Mplus_FlashControl ) & CDSN_CTRL_FR_B_MASK ) ! = CDSN_CTRL_FR_B_MASK ) {
if ( time_after ( jiffies , timeo ) ) {
printk ( " _DoC_WaitReady timed out. \n " ) ;
return - EIO ;
}
udelay ( 1 ) ;
cond_resched ( ) ;
}
} else {
while ( ! ( ReadDOC ( docptr , CDSNControl ) & CDSN_CTRL_FR_B ) ) {
if ( time_after ( jiffies , timeo ) ) {
printk ( " _DoC_WaitReady timed out. \n " ) ;
return - EIO ;
}
udelay ( 1 ) ;
cond_resched ( ) ;
}
}
return 0 ;
}
static inline int DoC_WaitReady ( struct doc_priv * doc )
{
2006-05-13 21:07:53 +04:00
void __iomem * docptr = doc - > virtadr ;
2005-04-17 02:20:36 +04:00
int ret = 0 ;
if ( DoC_is_MillenniumPlus ( doc ) ) {
DoC_Delay ( doc , 4 ) ;
if ( ( ReadDOC ( docptr , Mplus_FlashControl ) & CDSN_CTRL_FR_B_MASK ) ! = CDSN_CTRL_FR_B_MASK )
/* Call the out-of-line routine to wait */
ret = _DoC_WaitReady ( doc ) ;
} else {
DoC_Delay ( doc , 4 ) ;
if ( ! ( ReadDOC ( docptr , CDSNControl ) & CDSN_CTRL_FR_B ) )
/* Call the out-of-line routine to wait */
ret = _DoC_WaitReady ( doc ) ;
DoC_Delay ( doc , 2 ) ;
}
2006-05-13 21:07:53 +04:00
if ( debug )
printk ( " DoC_WaitReady OK \n " ) ;
2005-04-17 02:20:36 +04:00
return ret ;
}
static void doc2000_write_byte ( struct mtd_info * mtd , u_char datum )
{
struct nand_chip * this = mtd - > priv ;
struct doc_priv * doc = this - > priv ;
2006-05-13 21:07:53 +04:00
void __iomem * docptr = doc - > virtadr ;
2005-04-17 02:20:36 +04:00
2006-05-13 21:07:53 +04:00
if ( debug )
printk ( " write_byte %02x \n " , datum ) ;
2005-04-17 02:20:36 +04:00
WriteDOC ( datum , docptr , CDSNSlowIO ) ;
WriteDOC ( datum , docptr , 2 k_CDSN_IO ) ;
}
static u_char doc2000_read_byte ( struct mtd_info * mtd )
{
struct nand_chip * this = mtd - > priv ;
struct doc_priv * doc = this - > priv ;
2006-05-13 21:07:53 +04:00
void __iomem * docptr = doc - > virtadr ;
2005-04-17 02:20:36 +04:00
u_char ret ;
ReadDOC ( docptr , CDSNSlowIO ) ;
DoC_Delay ( doc , 2 ) ;
ret = ReadDOC ( docptr , 2 k_CDSN_IO ) ;
2006-05-13 21:07:53 +04:00
if ( debug )
printk ( " read_byte returns %02x \n " , ret ) ;
2005-04-17 02:20:36 +04:00
return ret ;
}
2006-05-13 21:07:53 +04:00
static void doc2000_writebuf ( struct mtd_info * mtd , const u_char * buf , int len )
2005-04-17 02:20:36 +04:00
{
struct nand_chip * this = mtd - > priv ;
struct doc_priv * doc = this - > priv ;
2006-05-13 21:07:53 +04:00
void __iomem * docptr = doc - > virtadr ;
2005-04-17 02:20:36 +04:00
int i ;
2006-05-13 21:07:53 +04:00
if ( debug )
printk ( " writebuf of %d bytes: " , len ) ;
for ( i = 0 ; i < len ; i + + ) {
2005-04-17 02:20:36 +04:00
WriteDOC_ ( buf [ i ] , docptr , DoC_2k_CDSN_IO + i ) ;
if ( debug & & i < 16 )
printk ( " %02x " , buf [ i ] ) ;
}
2006-05-13 21:07:53 +04:00
if ( debug )
printk ( " \n " ) ;
2005-04-17 02:20:36 +04:00
}
2006-05-13 21:07:53 +04:00
static void doc2000_readbuf ( struct mtd_info * mtd , u_char * buf , int len )
2005-04-17 02:20:36 +04:00
{
struct nand_chip * this = mtd - > priv ;
struct doc_priv * doc = this - > priv ;
2006-05-13 21:07:53 +04:00
void __iomem * docptr = doc - > virtadr ;
int i ;
2005-04-17 02:20:36 +04:00
2006-05-13 21:07:53 +04:00
if ( debug )
printk ( " readbuf of %d bytes: " , len ) ;
2005-04-17 02:20:36 +04:00
2006-05-13 21:07:53 +04:00
for ( i = 0 ; i < len ; i + + ) {
2005-04-17 02:20:36 +04:00
buf [ i ] = ReadDOC ( docptr , 2 k_CDSN_IO + i ) ;
}
}
2006-05-13 21:07:53 +04:00
static void doc2000_readbuf_dword ( struct mtd_info * mtd , u_char * buf , int len )
2005-04-17 02:20:36 +04:00
{
struct nand_chip * this = mtd - > priv ;
struct doc_priv * doc = this - > priv ;
2006-05-13 21:07:53 +04:00
void __iomem * docptr = doc - > virtadr ;
int i ;
2005-04-17 02:20:36 +04:00
2006-05-13 21:07:53 +04:00
if ( debug )
printk ( " readbuf_dword of %d bytes: " , len ) ;
2005-04-17 02:20:36 +04:00
2006-05-13 21:07:53 +04:00
if ( unlikely ( ( ( ( unsigned long ) buf ) | len ) & 3 ) ) {
for ( i = 0 ; i < len ; i + + ) {
* ( uint8_t * ) ( & buf [ i ] ) = ReadDOC ( docptr , 2 k_CDSN_IO + i ) ;
2005-04-17 02:20:36 +04:00
}
} else {
2006-05-13 21:07:53 +04:00
for ( i = 0 ; i < len ; i + = 4 ) {
* ( uint32_t * ) ( & buf [ i ] ) = readl ( docptr + DoC_2k_CDSN_IO + i ) ;
2005-04-17 02:20:36 +04:00
}
}
}
2006-05-13 21:07:53 +04:00
static int doc2000_verifybuf ( struct mtd_info * mtd , const u_char * buf , int len )
2005-04-17 02:20:36 +04:00
{
struct nand_chip * this = mtd - > priv ;
struct doc_priv * doc = this - > priv ;
2006-05-13 21:07:53 +04:00
void __iomem * docptr = doc - > virtadr ;
2005-04-17 02:20:36 +04:00
int i ;
2006-05-13 21:07:53 +04:00
for ( i = 0 ; i < len ; i + + )
2005-04-17 02:20:36 +04:00
if ( buf [ i ] ! = ReadDOC ( docptr , 2 k_CDSN_IO ) )
return - EFAULT ;
return 0 ;
}
static uint16_t __init doc200x_ident_chip ( struct mtd_info * mtd , int nr )
{
struct nand_chip * this = mtd - > priv ;
struct doc_priv * doc = this - > priv ;
uint16_t ret ;
doc200x_select_chip ( mtd , nr ) ;
2006-05-24 01:25:53 +04:00
doc200x_hwcontrol ( mtd , NAND_CMD_READID ,
NAND_CTRL_CLE | NAND_CTRL_CHANGE ) ;
doc200x_hwcontrol ( mtd , 0 , NAND_CTRL_ALE | NAND_CTRL_CHANGE ) ;
doc200x_hwcontrol ( mtd , NAND_CMD_NONE , NAND_NCE | NAND_CTRL_CHANGE ) ;
2005-11-07 14:15:49 +03:00
2005-02-23 00:48:25 +03:00
/* We cant' use dev_ready here, but at least we wait for the
2005-11-07 14:15:49 +03:00
* command to complete
2005-02-23 00:48:25 +03:00
*/
udelay ( 50 ) ;
2005-11-07 14:15:49 +03:00
2005-04-17 02:20:36 +04:00
ret = this - > read_byte ( mtd ) < < 8 ;
ret | = this - > read_byte ( mtd ) ;
if ( doc - > ChipID = = DOC_ChipID_Doc2k & & try_dword & & ! nr ) {
/* First chip probe. See if we get same results by 32-bit access */
union {
uint32_t dword ;
uint8_t byte [ 4 ] ;
} ident ;
void __iomem * docptr = doc - > virtadr ;
2006-05-24 01:25:53 +04:00
doc200x_hwcontrol ( mtd , NAND_CMD_READID ,
NAND_CTRL_CLE | NAND_CTRL_CHANGE ) ;
doc200x_hwcontrol ( mtd , 0 , NAND_CTRL_ALE | NAND_CTRL_CHANGE ) ;
doc200x_hwcontrol ( mtd , NAND_CMD_NONE ,
NAND_NCE | NAND_CTRL_CHANGE ) ;
2005-04-17 02:20:36 +04:00
2005-02-23 00:48:25 +03:00
udelay ( 50 ) ;
2005-04-17 02:20:36 +04:00
ident . dword = readl ( docptr + DoC_2k_CDSN_IO ) ;
if ( ( ( ident . byte [ 0 ] < < 8 ) | ident . byte [ 1 ] ) = = ret ) {
printk ( KERN_INFO " DiskOnChip 2000 responds to DWORD access \n " ) ;
this - > read_buf = & doc2000_readbuf_dword ;
}
}
2005-11-07 14:15:49 +03:00
2005-04-17 02:20:36 +04:00
return ret ;
}
static void __init doc2000_count_chips ( struct mtd_info * mtd )
{
struct nand_chip * this = mtd - > priv ;
struct doc_priv * doc = this - > priv ;
uint16_t mfrid ;
int i ;
/* Max 4 chips per floor on DiskOnChip 2000 */
doc - > chips_per_floor = 4 ;
/* Find out what the first chip is */
mfrid = doc200x_ident_chip ( mtd , 0 ) ;
/* Find how many chips in each floor. */
for ( i = 1 ; i < 4 ; i + + ) {
if ( doc200x_ident_chip ( mtd , i ) ! = mfrid )
break ;
}
doc - > chips_per_floor = i ;
printk ( KERN_DEBUG " Detected %d chips per floor. \n " , i ) ;
}
static int doc200x_wait ( struct mtd_info * mtd , struct nand_chip * this , int state )
{
struct doc_priv * doc = this - > priv ;
int status ;
2005-11-07 14:15:49 +03:00
2005-04-17 02:20:36 +04:00
DoC_WaitReady ( doc ) ;
this - > cmdfunc ( mtd , NAND_CMD_STATUS , - 1 , - 1 ) ;
DoC_WaitReady ( doc ) ;
status = ( int ) this - > read_byte ( mtd ) ;
return status ;
}
static void doc2001_write_byte ( struct mtd_info * mtd , u_char datum )
{
struct nand_chip * this = mtd - > priv ;
struct doc_priv * doc = this - > priv ;
2006-05-13 21:07:53 +04:00
void __iomem * docptr = doc - > virtadr ;
2005-04-17 02:20:36 +04:00
WriteDOC ( datum , docptr , CDSNSlowIO ) ;
WriteDOC ( datum , docptr , Mil_CDSN_IO ) ;
WriteDOC ( datum , docptr , WritePipeTerm ) ;
}
static u_char doc2001_read_byte ( struct mtd_info * mtd )
{
struct nand_chip * this = mtd - > priv ;
struct doc_priv * doc = this - > priv ;
2006-05-13 21:07:53 +04:00
void __iomem * docptr = doc - > virtadr ;
2005-04-17 02:20:36 +04:00
//ReadDOC(docptr, CDSNSlowIO);
/* 11.4.5 -- delay twice to allow extended length cycle */
DoC_Delay ( doc , 2 ) ;
ReadDOC ( docptr , ReadPipeInit ) ;
//return ReadDOC(docptr, Mil_CDSN_IO);
return ReadDOC ( docptr , LastDataRead ) ;
}
2006-05-13 21:07:53 +04:00
static void doc2001_writebuf ( struct mtd_info * mtd , const u_char * buf , int len )
2005-04-17 02:20:36 +04:00
{
struct nand_chip * this = mtd - > priv ;
struct doc_priv * doc = this - > priv ;
2006-05-13 21:07:53 +04:00
void __iomem * docptr = doc - > virtadr ;
2005-04-17 02:20:36 +04:00
int i ;
2006-05-13 21:07:53 +04:00
for ( i = 0 ; i < len ; i + + )
2005-04-17 02:20:36 +04:00
WriteDOC_ ( buf [ i ] , docptr , DoC_Mil_CDSN_IO + i ) ;
/* Terminate write pipeline */
WriteDOC ( 0x00 , docptr , WritePipeTerm ) ;
}
2006-05-13 21:07:53 +04:00
static void doc2001_readbuf ( struct mtd_info * mtd , u_char * buf , int len )
2005-04-17 02:20:36 +04:00
{
struct nand_chip * this = mtd - > priv ;
struct doc_priv * doc = this - > priv ;
2006-05-13 21:07:53 +04:00
void __iomem * docptr = doc - > virtadr ;
2005-04-17 02:20:36 +04:00
int i ;
/* Start read pipeline */
ReadDOC ( docptr , ReadPipeInit ) ;
2006-05-13 21:07:53 +04:00
for ( i = 0 ; i < len - 1 ; i + + )
2005-04-17 02:20:36 +04:00
buf [ i ] = ReadDOC ( docptr , Mil_CDSN_IO + ( i & 0xff ) ) ;
/* Terminate read pipeline */
buf [ i ] = ReadDOC ( docptr , LastDataRead ) ;
}
2006-05-13 21:07:53 +04:00
static int doc2001_verifybuf ( struct mtd_info * mtd , const u_char * buf , int len )
2005-04-17 02:20:36 +04:00
{
struct nand_chip * this = mtd - > priv ;
struct doc_priv * doc = this - > priv ;
2006-05-13 21:07:53 +04:00
void __iomem * docptr = doc - > virtadr ;
2005-04-17 02:20:36 +04:00
int i ;
/* Start read pipeline */
ReadDOC ( docptr , ReadPipeInit ) ;
2006-05-13 21:07:53 +04:00
for ( i = 0 ; i < len - 1 ; i + + )
2005-04-17 02:20:36 +04:00
if ( buf [ i ] ! = ReadDOC ( docptr , Mil_CDSN_IO ) ) {
ReadDOC ( docptr , LastDataRead ) ;
return i ;
}
if ( buf [ i ] ! = ReadDOC ( docptr , LastDataRead ) )
return i ;
return 0 ;
}
static u_char doc2001plus_read_byte ( struct mtd_info * mtd )
{
struct nand_chip * this = mtd - > priv ;
struct doc_priv * doc = this - > priv ;
2006-05-13 21:07:53 +04:00
void __iomem * docptr = doc - > virtadr ;
2005-04-17 02:20:36 +04:00
u_char ret ;
2006-05-13 21:07:53 +04:00
ReadDOC ( docptr , Mplus_ReadPipeInit ) ;
ReadDOC ( docptr , Mplus_ReadPipeInit ) ;
ret = ReadDOC ( docptr , Mplus_LastDataRead ) ;
if ( debug )
printk ( " read_byte returns %02x \n " , ret ) ;
2005-04-17 02:20:36 +04:00
return ret ;
}
2006-05-13 21:07:53 +04:00
static void doc2001plus_writebuf ( struct mtd_info * mtd , const u_char * buf , int len )
2005-04-17 02:20:36 +04:00
{
struct nand_chip * this = mtd - > priv ;
struct doc_priv * doc = this - > priv ;
2006-05-13 21:07:53 +04:00
void __iomem * docptr = doc - > virtadr ;
2005-04-17 02:20:36 +04:00
int i ;
2006-05-13 21:07:53 +04:00
if ( debug )
printk ( " writebuf of %d bytes: " , len ) ;
for ( i = 0 ; i < len ; i + + ) {
2005-04-17 02:20:36 +04:00
WriteDOC_ ( buf [ i ] , docptr , DoC_Mil_CDSN_IO + i ) ;
if ( debug & & i < 16 )
printk ( " %02x " , buf [ i ] ) ;
}
2006-05-13 21:07:53 +04:00
if ( debug )
printk ( " \n " ) ;
2005-04-17 02:20:36 +04:00
}
2006-05-13 21:07:53 +04:00
static void doc2001plus_readbuf ( struct mtd_info * mtd , u_char * buf , int len )
2005-04-17 02:20:36 +04:00
{
struct nand_chip * this = mtd - > priv ;
struct doc_priv * doc = this - > priv ;
2006-05-13 21:07:53 +04:00
void __iomem * docptr = doc - > virtadr ;
2005-04-17 02:20:36 +04:00
int i ;
2006-05-13 21:07:53 +04:00
if ( debug )
printk ( " readbuf of %d bytes: " , len ) ;
2005-04-17 02:20:36 +04:00
/* Start read pipeline */
ReadDOC ( docptr , Mplus_ReadPipeInit ) ;
ReadDOC ( docptr , Mplus_ReadPipeInit ) ;
2006-05-13 21:07:53 +04:00
for ( i = 0 ; i < len - 2 ; i + + ) {
2005-04-17 02:20:36 +04:00
buf [ i ] = ReadDOC ( docptr , Mil_CDSN_IO ) ;
if ( debug & & i < 16 )
printk ( " %02x " , buf [ i ] ) ;
}
/* Terminate read pipeline */
2006-05-13 21:07:53 +04:00
buf [ len - 2 ] = ReadDOC ( docptr , Mplus_LastDataRead ) ;
2005-04-17 02:20:36 +04:00
if ( debug & & i < 16 )
2006-05-13 21:07:53 +04:00
printk ( " %02x " , buf [ len - 2 ] ) ;
buf [ len - 1 ] = ReadDOC ( docptr , Mplus_LastDataRead ) ;
2005-04-17 02:20:36 +04:00
if ( debug & & i < 16 )
2006-05-13 21:07:53 +04:00
printk ( " %02x " , buf [ len - 1 ] ) ;
if ( debug )
printk ( " \n " ) ;
2005-04-17 02:20:36 +04:00
}
2006-05-13 21:07:53 +04:00
static int doc2001plus_verifybuf ( struct mtd_info * mtd , const u_char * buf , int len )
2005-04-17 02:20:36 +04:00
{
struct nand_chip * this = mtd - > priv ;
struct doc_priv * doc = this - > priv ;
2006-05-13 21:07:53 +04:00
void __iomem * docptr = doc - > virtadr ;
2005-04-17 02:20:36 +04:00
int i ;
2006-05-13 21:07:53 +04:00
if ( debug )
printk ( " verifybuf of %d bytes: " , len ) ;
2005-04-17 02:20:36 +04:00
/* Start read pipeline */
ReadDOC ( docptr , Mplus_ReadPipeInit ) ;
ReadDOC ( docptr , Mplus_ReadPipeInit ) ;
2006-05-13 21:07:53 +04:00
for ( i = 0 ; i < len - 2 ; i + + )
2005-04-17 02:20:36 +04:00
if ( buf [ i ] ! = ReadDOC ( docptr , Mil_CDSN_IO ) ) {
ReadDOC ( docptr , Mplus_LastDataRead ) ;
ReadDOC ( docptr , Mplus_LastDataRead ) ;
return i ;
}
2006-05-13 21:07:53 +04:00
if ( buf [ len - 2 ] ! = ReadDOC ( docptr , Mplus_LastDataRead ) )
return len - 2 ;
if ( buf [ len - 1 ] ! = ReadDOC ( docptr , Mplus_LastDataRead ) )
return len - 1 ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
static void doc2001plus_select_chip ( struct mtd_info * mtd , int chip )
{
struct nand_chip * this = mtd - > priv ;
struct doc_priv * doc = this - > priv ;
2006-05-13 21:07:53 +04:00
void __iomem * docptr = doc - > virtadr ;
2005-04-17 02:20:36 +04:00
int floor = 0 ;
2006-05-13 21:07:53 +04:00
if ( debug )
printk ( " select chip (%d) \n " , chip ) ;
2005-04-17 02:20:36 +04:00
if ( chip = = - 1 ) {
/* Disable flash internally */
WriteDOC ( 0 , docptr , Mplus_FlashSelect ) ;
return ;
}
floor = chip / doc - > chips_per_floor ;
2006-05-13 21:07:53 +04:00
chip - = ( floor * doc - > chips_per_floor ) ;
2005-04-17 02:20:36 +04:00
/* Assert ChipEnable and deassert WriteProtect */
WriteDOC ( ( DOC_FLASH_CE ) , docptr , Mplus_FlashSelect ) ;
this - > cmdfunc ( mtd , NAND_CMD_RESET , - 1 , - 1 ) ;
doc - > curchip = chip ;
doc - > curfloor = floor ;
}
static void doc200x_select_chip ( struct mtd_info * mtd , int chip )
{
struct nand_chip * this = mtd - > priv ;
struct doc_priv * doc = this - > priv ;
2006-05-13 21:07:53 +04:00
void __iomem * docptr = doc - > virtadr ;
2005-04-17 02:20:36 +04:00
int floor = 0 ;
2006-05-13 21:07:53 +04:00
if ( debug )
printk ( " select chip (%d) \n " , chip ) ;
2005-04-17 02:20:36 +04:00
if ( chip = = - 1 )
return ;
floor = chip / doc - > chips_per_floor ;
2006-05-13 21:07:53 +04:00
chip - = ( floor * doc - > chips_per_floor ) ;
2005-04-17 02:20:36 +04:00
/* 11.4.4 -- deassert CE before changing chip */
2006-05-24 01:25:53 +04:00
doc200x_hwcontrol ( mtd , NAND_CMD_NONE , 0 | NAND_CTRL_CHANGE ) ;
2005-04-17 02:20:36 +04:00
WriteDOC ( floor , docptr , FloorSelect ) ;
WriteDOC ( chip , docptr , CDSNDeviceSelect ) ;
2006-05-24 01:25:53 +04:00
doc200x_hwcontrol ( mtd , NAND_CMD_NONE , NAND_NCE | NAND_CTRL_CHANGE ) ;
2005-04-17 02:20:36 +04:00
doc - > curchip = chip ;
doc - > curfloor = floor ;
}
2006-05-24 01:25:53 +04:00
# define CDSN_CTRL_MSK (CDSN_CTRL_CE | CDSN_CTRL_CLE | CDSN_CTRL_ALE)
static void doc200x_hwcontrol ( struct mtd_info * mtd , int cmd ,
unsigned int ctrl )
2005-04-17 02:20:36 +04:00
{
struct nand_chip * this = mtd - > priv ;
struct doc_priv * doc = this - > priv ;
2006-05-13 21:07:53 +04:00
void __iomem * docptr = doc - > virtadr ;
2005-04-17 02:20:36 +04:00
2006-05-24 01:25:53 +04:00
if ( ctrl & NAND_CTRL_CHANGE ) {
doc - > CDSNControl & = ~ CDSN_CTRL_MSK ;
doc - > CDSNControl | = ctrl & CDSN_CTRL_MSK ;
if ( debug )
printk ( " hwcontrol(%d): %02x \n " , cmd , doc - > CDSNControl ) ;
WriteDOC ( doc - > CDSNControl , docptr , CDSNControl ) ;
/* 11.4.3 -- 4 NOPs after CSDNControl write */
DoC_Delay ( doc , 4 ) ;
2005-04-17 02:20:36 +04:00
}
2006-05-24 01:28:48 +04:00
if ( cmd ! = NAND_CMD_NONE ) {
if ( DoC_is_2000 ( doc ) )
doc2000_write_byte ( mtd , cmd ) ;
else
doc2001_write_byte ( mtd , cmd ) ;
}
2005-04-17 02:20:36 +04:00
}
2006-05-13 21:07:53 +04:00
static void doc2001plus_command ( struct mtd_info * mtd , unsigned command , int column , int page_addr )
2005-04-17 02:20:36 +04:00
{
struct nand_chip * this = mtd - > priv ;
struct doc_priv * doc = this - > priv ;
2006-05-13 21:07:53 +04:00
void __iomem * docptr = doc - > virtadr ;
2005-04-17 02:20:36 +04:00
/*
* Must terminate write pipeline before sending any commands
* to the device .
*/
if ( command = = NAND_CMD_PAGEPROG ) {
WriteDOC ( 0x00 , docptr , Mplus_WritePipeTerm ) ;
WriteDOC ( 0x00 , docptr , Mplus_WritePipeTerm ) ;
}
/*
* Write out the command to the device .
*/
if ( command = = NAND_CMD_SEQIN ) {
int readcmd ;
2006-05-23 01:18:05 +04:00
if ( column > = mtd - > writesize ) {
2005-04-17 02:20:36 +04:00
/* OOB area */
2006-05-23 01:18:05 +04:00
column - = mtd - > writesize ;
2005-04-17 02:20:36 +04:00
readcmd = NAND_CMD_READOOB ;
} else if ( column < 256 ) {
/* First 256 bytes --> READ0 */
readcmd = NAND_CMD_READ0 ;
} else {
column - = 256 ;
readcmd = NAND_CMD_READ1 ;
}
WriteDOC ( readcmd , docptr , Mplus_FlashCmd ) ;
}
WriteDOC ( command , docptr , Mplus_FlashCmd ) ;
WriteDOC ( 0 , docptr , Mplus_WritePipeTerm ) ;
WriteDOC ( 0 , docptr , Mplus_WritePipeTerm ) ;
if ( column ! = - 1 | | page_addr ! = - 1 ) {
/* Serially input address */
if ( column ! = - 1 ) {
/* Adjust columns for 16 bit buswidth */
if ( this - > options & NAND_BUSWIDTH_16 )
column > > = 1 ;
WriteDOC ( column , docptr , Mplus_FlashAddress ) ;
}
if ( page_addr ! = - 1 ) {
2006-05-13 21:07:53 +04:00
WriteDOC ( ( unsigned char ) ( page_addr & 0xff ) , docptr , Mplus_FlashAddress ) ;
WriteDOC ( ( unsigned char ) ( ( page_addr > > 8 ) & 0xff ) , docptr , Mplus_FlashAddress ) ;
2005-04-17 02:20:36 +04:00
/* One more address cycle for higher density devices */
if ( this - > chipsize & 0x0c000000 ) {
2006-05-13 21:07:53 +04:00
WriteDOC ( ( unsigned char ) ( ( page_addr > > 16 ) & 0x0f ) , docptr , Mplus_FlashAddress ) ;
2005-04-17 02:20:36 +04:00
printk ( " high density \n " ) ;
}
}
WriteDOC ( 0 , docptr , Mplus_WritePipeTerm ) ;
WriteDOC ( 0 , docptr , Mplus_WritePipeTerm ) ;
/* deassert ALE */
2006-05-13 21:07:53 +04:00
if ( command = = NAND_CMD_READ0 | | command = = NAND_CMD_READ1 | |
command = = NAND_CMD_READOOB | | command = = NAND_CMD_READID )
2005-04-17 02:20:36 +04:00
WriteDOC ( 0 , docptr , Mplus_FlashControl ) ;
}
2005-11-07 14:15:49 +03:00
/*
2005-04-17 02:20:36 +04:00
* program and erase have their own busy handlers
* status and sequential in needs no delay
2006-05-13 21:07:53 +04:00
*/
2005-04-17 02:20:36 +04:00
switch ( command ) {
case NAND_CMD_PAGEPROG :
case NAND_CMD_ERASE1 :
case NAND_CMD_ERASE2 :
case NAND_CMD_SEQIN :
case NAND_CMD_STATUS :
return ;
case NAND_CMD_RESET :
if ( this - > dev_ready )
break ;
udelay ( this - > chip_delay ) ;
WriteDOC ( NAND_CMD_STATUS , docptr , Mplus_FlashCmd ) ;
WriteDOC ( 0 , docptr , Mplus_WritePipeTerm ) ;
WriteDOC ( 0 , docptr , Mplus_WritePipeTerm ) ;
2006-05-13 21:07:53 +04:00
while ( ! ( this - > read_byte ( mtd ) & 0x40 ) ) ;
2005-04-17 02:20:36 +04:00
return ;
2006-05-13 21:07:53 +04:00
/* This applies to read commands */
2005-04-17 02:20:36 +04:00
default :
2005-11-07 14:15:49 +03:00
/*
2005-04-17 02:20:36 +04:00
* If we don ' t have access to the busy pin , we apply the given
* command delay
2006-05-13 21:07:53 +04:00
*/
2005-04-17 02:20:36 +04:00
if ( ! this - > dev_ready ) {
2006-05-13 21:07:53 +04:00
udelay ( this - > chip_delay ) ;
2005-04-17 02:20:36 +04:00
return ;
}
}
/* Apply this short delay always to ensure that we do wait tWB in
* any case on any machine . */
2006-05-13 21:07:53 +04:00
ndelay ( 100 ) ;
2005-04-17 02:20:36 +04:00
/* wait until command is processed */
2006-05-13 21:07:53 +04:00
while ( ! this - > dev_ready ( mtd ) ) ;
2005-04-17 02:20:36 +04:00
}
static int doc200x_dev_ready ( struct mtd_info * mtd )
{
struct nand_chip * this = mtd - > priv ;
struct doc_priv * doc = this - > priv ;
2006-05-13 21:07:53 +04:00
void __iomem * docptr = doc - > virtadr ;
2005-04-17 02:20:36 +04:00
if ( DoC_is_MillenniumPlus ( doc ) ) {
/* 11.4.2 -- must NOP four times before checking FR/B# */
DoC_Delay ( doc , 4 ) ;
if ( ( ReadDOC ( docptr , Mplus_FlashControl ) & CDSN_CTRL_FR_B_MASK ) ! = CDSN_CTRL_FR_B_MASK ) {
2006-05-13 21:07:53 +04:00
if ( debug )
2005-04-17 02:20:36 +04:00
printk ( " not ready \n " ) ;
return 0 ;
}
2006-05-13 21:07:53 +04:00
if ( debug )
printk ( " was ready \n " ) ;
2005-04-17 02:20:36 +04:00
return 1 ;
} else {
/* 11.4.2 -- must NOP four times before checking FR/B# */
DoC_Delay ( doc , 4 ) ;
if ( ! ( ReadDOC ( docptr , CDSNControl ) & CDSN_CTRL_FR_B ) ) {
2006-05-13 21:07:53 +04:00
if ( debug )
2005-04-17 02:20:36 +04:00
printk ( " not ready \n " ) ;
return 0 ;
}
/* 11.4.2 -- Must NOP twice if it's ready */
DoC_Delay ( doc , 2 ) ;
2006-05-13 21:07:53 +04:00
if ( debug )
printk ( " was ready \n " ) ;
2005-04-17 02:20:36 +04:00
return 1 ;
}
}
static int doc200x_block_bad ( struct mtd_info * mtd , loff_t ofs , int getchip )
{
/* This is our last resort if we couldn't find or create a BBT. Just
pretend all blocks are good . */
return 0 ;
}
static void doc200x_enable_hwecc ( struct mtd_info * mtd , int mode )
{
struct nand_chip * this = mtd - > priv ;
struct doc_priv * doc = this - > priv ;
2006-05-13 21:07:53 +04:00
void __iomem * docptr = doc - > virtadr ;
2005-04-17 02:20:36 +04:00
/* Prime the ECC engine */
2006-05-13 21:07:53 +04:00
switch ( mode ) {
2005-04-17 02:20:36 +04:00
case NAND_ECC_READ :
WriteDOC ( DOC_ECC_RESET , docptr , ECCConf ) ;
WriteDOC ( DOC_ECC_EN , docptr , ECCConf ) ;
break ;
case NAND_ECC_WRITE :
WriteDOC ( DOC_ECC_RESET , docptr , ECCConf ) ;
WriteDOC ( DOC_ECC_EN | DOC_ECC_RW , docptr , ECCConf ) ;
break ;
}
}
static void doc2001plus_enable_hwecc ( struct mtd_info * mtd , int mode )
{
struct nand_chip * this = mtd - > priv ;
struct doc_priv * doc = this - > priv ;
2006-05-13 21:07:53 +04:00
void __iomem * docptr = doc - > virtadr ;
2005-04-17 02:20:36 +04:00
/* Prime the ECC engine */
2006-05-13 21:07:53 +04:00
switch ( mode ) {
2005-04-17 02:20:36 +04:00
case NAND_ECC_READ :
WriteDOC ( DOC_ECC_RESET , docptr , Mplus_ECCConf ) ;
WriteDOC ( DOC_ECC_EN , docptr , Mplus_ECCConf ) ;
break ;
case NAND_ECC_WRITE :
WriteDOC ( DOC_ECC_RESET , docptr , Mplus_ECCConf ) ;
WriteDOC ( DOC_ECC_EN | DOC_ECC_RW , docptr , Mplus_ECCConf ) ;
break ;
}
}
/* This code is only called on write */
2006-05-13 21:07:53 +04:00
static int doc200x_calculate_ecc ( struct mtd_info * mtd , const u_char * dat , unsigned char * ecc_code )
2005-04-17 02:20:36 +04:00
{
struct nand_chip * this = mtd - > priv ;
struct doc_priv * doc = this - > priv ;
2006-05-13 21:07:53 +04:00
void __iomem * docptr = doc - > virtadr ;
2005-04-17 02:20:36 +04:00
int i ;
int emptymatch = 1 ;
/* flush the pipeline */
if ( DoC_is_2000 ( doc ) ) {
WriteDOC ( doc - > CDSNControl & ~ CDSN_CTRL_FLASH_IO , docptr , CDSNControl ) ;
WriteDOC ( 0 , docptr , 2 k_CDSN_IO ) ;
WriteDOC ( 0 , docptr , 2 k_CDSN_IO ) ;
WriteDOC ( 0 , docptr , 2 k_CDSN_IO ) ;
WriteDOC ( doc - > CDSNControl , docptr , CDSNControl ) ;
} else if ( DoC_is_MillenniumPlus ( doc ) ) {
WriteDOC ( 0 , docptr , Mplus_NOP ) ;
WriteDOC ( 0 , docptr , Mplus_NOP ) ;
WriteDOC ( 0 , docptr , Mplus_NOP ) ;
} else {
WriteDOC ( 0 , docptr , NOP ) ;
WriteDOC ( 0 , docptr , NOP ) ;
WriteDOC ( 0 , docptr , NOP ) ;
}
for ( i = 0 ; i < 6 ; i + + ) {
if ( DoC_is_MillenniumPlus ( doc ) )
ecc_code [ i ] = ReadDOC_ ( docptr , DoC_Mplus_ECCSyndrome0 + i ) ;
2005-11-07 14:15:49 +03:00
else
2005-04-17 02:20:36 +04:00
ecc_code [ i ] = ReadDOC_ ( docptr , DoC_ECCSyndrome0 + i ) ;
if ( ecc_code [ i ] ! = empty_write_ecc [ i ] )
emptymatch = 0 ;
}
if ( DoC_is_MillenniumPlus ( doc ) )
WriteDOC ( DOC_ECC_DIS , docptr , Mplus_ECCConf ) ;
else
WriteDOC ( DOC_ECC_DIS , docptr , ECCConf ) ;
#if 0
/* If emptymatch=1, we might have an all-0xff data buffer. Check. */
if ( emptymatch ) {
/* Note: this somewhat expensive test should not be triggered
often . It could be optimized away by examining the data in
the writebuf routine , and remembering the result . */
for ( i = 0 ; i < 512 ; i + + ) {
2006-05-13 21:07:53 +04:00
if ( dat [ i ] = = 0xff )
continue ;
2005-04-17 02:20:36 +04:00
emptymatch = 0 ;
break ;
}
}
/* If emptymatch still =1, we do have an all-0xff data buffer.
Return all - 0xff ecc value instead of the computed one , so
it ' ll look just like a freshly - erased page . */
2006-05-13 21:07:53 +04:00
if ( emptymatch )
memset ( ecc_code , 0xff , 6 ) ;
2005-04-17 02:20:36 +04:00
# endif
return 0 ;
}
2006-05-25 12:07:16 +04:00
static int doc200x_correct_data ( struct mtd_info * mtd , u_char * dat ,
u_char * read_ecc , u_char * isnull )
2005-04-17 02:20:36 +04:00
{
int i , ret = 0 ;
struct nand_chip * this = mtd - > priv ;
struct doc_priv * doc = this - > priv ;
2006-05-13 21:07:53 +04:00
void __iomem * docptr = doc - > virtadr ;
2006-05-25 12:07:16 +04:00
uint8_t calc_ecc [ 6 ] ;
2005-04-17 02:20:36 +04:00
volatile u_char dummy ;
int emptymatch = 1 ;
2005-11-07 14:15:49 +03:00
2005-04-17 02:20:36 +04:00
/* flush the pipeline */
if ( DoC_is_2000 ( doc ) ) {
dummy = ReadDOC ( docptr , 2 k_ECCStatus ) ;
dummy = ReadDOC ( docptr , 2 k_ECCStatus ) ;
dummy = ReadDOC ( docptr , 2 k_ECCStatus ) ;
} else if ( DoC_is_MillenniumPlus ( doc ) ) {
dummy = ReadDOC ( docptr , Mplus_ECCConf ) ;
dummy = ReadDOC ( docptr , Mplus_ECCConf ) ;
dummy = ReadDOC ( docptr , Mplus_ECCConf ) ;
} else {
dummy = ReadDOC ( docptr , ECCConf ) ;
dummy = ReadDOC ( docptr , ECCConf ) ;
dummy = ReadDOC ( docptr , ECCConf ) ;
}
2005-11-07 14:15:49 +03:00
2005-04-17 02:20:36 +04:00
/* Error occured ? */
if ( dummy & 0x80 ) {
for ( i = 0 ; i < 6 ; i + + ) {
if ( DoC_is_MillenniumPlus ( doc ) )
calc_ecc [ i ] = ReadDOC_ ( docptr , DoC_Mplus_ECCSyndrome0 + i ) ;
else
calc_ecc [ i ] = ReadDOC_ ( docptr , DoC_ECCSyndrome0 + i ) ;
if ( calc_ecc [ i ] ! = empty_read_syndrome [ i ] )
emptymatch = 0 ;
}
/* If emptymatch=1, the read syndrome is consistent with an
all - 0xff data and stored ecc block . Check the stored ecc . */
if ( emptymatch ) {
for ( i = 0 ; i < 6 ; i + + ) {
2006-05-13 21:07:53 +04:00
if ( read_ecc [ i ] = = 0xff )
continue ;
2005-04-17 02:20:36 +04:00
emptymatch = 0 ;
break ;
}
}
/* If emptymatch still =1, check the data block. */
if ( emptymatch ) {
2006-05-13 21:07:53 +04:00
/* Note: this somewhat expensive test should not be triggered
often . It could be optimized away by examining the data in
the readbuf routine , and remembering the result . */
2005-04-17 02:20:36 +04:00
for ( i = 0 ; i < 512 ; i + + ) {
2006-05-13 21:07:53 +04:00
if ( dat [ i ] = = 0xff )
continue ;
2005-04-17 02:20:36 +04:00
emptymatch = 0 ;
break ;
}
}
/* If emptymatch still =1, this is almost certainly a freshly-
erased block , in which case the ECC will not come out right .
We ' ll suppress the error and tell the caller everything ' s
OK . Because it is . */
2006-05-13 21:07:53 +04:00
if ( ! emptymatch )
ret = doc_ecc_decode ( rs_decoder , dat , calc_ecc ) ;
2005-04-17 02:20:36 +04:00
if ( ret > 0 )
printk ( KERN_ERR " doc200x_correct_data corrected %d errors \n " , ret ) ;
2005-11-07 14:15:49 +03:00
}
2005-04-17 02:20:36 +04:00
if ( DoC_is_MillenniumPlus ( doc ) )
WriteDOC ( DOC_ECC_DIS , docptr , Mplus_ECCConf ) ;
else
WriteDOC ( DOC_ECC_DIS , docptr , ECCConf ) ;
if ( no_ecc_failures & & ( ret = = - 1 ) ) {
printk ( KERN_ERR " suppressing ECC failure \n " ) ;
ret = 0 ;
}
return ret ;
}
2005-11-07 14:15:49 +03:00
2005-04-17 02:20:36 +04:00
//u_char mydatabuf[528];
2005-04-07 18:22:58 +04:00
/* The strange out-of-order .oobfree list below is a (possibly unneeded)
* attempt to retain compatibility . It used to read :
* . oobfree = { { 8 , 8 } }
* Since that leaves two bytes unusable , it was changed . But the following
* scheme might affect existing jffs2 installs by moving the cleanmarker :
* . oobfree = { { 6 , 10 } }
* jffs2 seems to handle the above gracefully , but the current scheme seems
* safer . The only problem with it is that any code that parses oobfree must
* be able to handle out - of - order segments .
*/
2006-05-28 00:16:10 +04:00
static struct nand_ecclayout doc200x_oobinfo = {
2006-05-13 21:07:53 +04:00
. eccbytes = 6 ,
. eccpos = { 0 , 1 , 2 , 3 , 4 , 5 } ,
. oobfree = { { 8 , 8 } , { 6 , 2 } }
2005-04-17 02:20:36 +04:00
} ;
2005-11-07 14:15:49 +03:00
2005-04-17 02:20:36 +04:00
/* Find the (I)NFTL Media Header, and optionally also the mirror media header.
On sucessful return , buf will contain a copy of the media header for
further processing . id is the string to scan for , and will presumably be
either " ANAND " or " BNAND " . If findmirror = 1 , also look for the mirror media
header . The page # s of the found media headers are placed in mh0_page and
mh1_page in the DOC private structure . */
2006-05-13 21:07:53 +04:00
static int __init find_media_headers ( struct mtd_info * mtd , u_char * buf , const char * id , int findmirror )
2005-04-17 02:20:36 +04:00
{
struct nand_chip * this = mtd - > priv ;
struct doc_priv * doc = this - > priv ;
2005-03-30 00:57:48 +04:00
unsigned offs ;
2005-04-17 02:20:36 +04:00
int ret ;
size_t retlen ;
2005-03-30 00:57:48 +04:00
for ( offs = 0 ; offs < mtd - > size ; offs + = mtd - > erasesize ) {
2006-05-23 01:18:05 +04:00
ret = mtd - > read ( mtd , offs , mtd - > writesize , & retlen , buf ) ;
if ( retlen ! = mtd - > writesize )
2006-05-13 21:07:53 +04:00
continue ;
2005-04-17 02:20:36 +04:00
if ( ret ) {
2006-05-13 21:07:53 +04:00
printk ( KERN_WARNING " ECC error scanning DOC at 0x%x \n " , offs ) ;
2005-04-17 02:20:36 +04:00
}
2006-05-13 21:07:53 +04:00
if ( memcmp ( buf , id , 6 ) )
continue ;
2005-04-17 02:20:36 +04:00
printk ( KERN_INFO " Found DiskOnChip %s Media Header at 0x%x \n " , id , offs ) ;
if ( doc - > mh0_page = = - 1 ) {
doc - > mh0_page = offs > > this - > page_shift ;
2006-05-13 21:07:53 +04:00
if ( ! findmirror )
return 1 ;
2005-04-17 02:20:36 +04:00
continue ;
}
doc - > mh1_page = offs > > this - > page_shift ;
return 2 ;
}
if ( doc - > mh0_page = = - 1 ) {
printk ( KERN_WARNING " DiskOnChip %s Media Header not found. \n " , id ) ;
return 0 ;
}
/* Only one mediaheader was found. We want buf to contain a
mediaheader on return , so we ' ll have to re - read the one we found . */
offs = doc - > mh0_page < < this - > page_shift ;
2006-05-23 01:18:05 +04:00
ret = mtd - > read ( mtd , offs , mtd - > writesize , & retlen , buf ) ;
if ( retlen ! = mtd - > writesize ) {
2005-04-17 02:20:36 +04:00
/* Insanity. Give up. */
printk ( KERN_ERR " Read DiskOnChip Media Header once, but can't reread it??? \n " ) ;
return 0 ;
}
return 1 ;
}
2006-05-13 21:07:53 +04:00
static inline int __init nftl_partscan ( struct mtd_info * mtd , struct mtd_partition * parts )
2005-04-17 02:20:36 +04:00
{
struct nand_chip * this = mtd - > priv ;
struct doc_priv * doc = this - > priv ;
int ret = 0 ;
u_char * buf ;
struct NFTLMediaHeader * mh ;
const unsigned psize = 1 < < this - > page_shift ;
2005-03-30 00:57:48 +04:00
int numparts = 0 ;
2005-04-17 02:20:36 +04:00
unsigned blocks , maxblocks ;
int offs , numheaders ;
2006-05-23 01:18:05 +04:00
buf = kmalloc ( mtd - > writesize , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! buf ) {
printk ( KERN_ERR " DiskOnChip mediaheader kmalloc failed! \n " ) ;
return 0 ;
}
2006-05-13 21:07:53 +04:00
if ( ! ( numheaders = find_media_headers ( mtd , buf , " ANAND " , 1 ) ) )
goto out ;
mh = ( struct NFTLMediaHeader * ) buf ;
2005-04-17 02:20:36 +04:00
2005-02-01 01:22:24 +03:00
mh - > NumEraseUnits = le16_to_cpu ( mh - > NumEraseUnits ) ;
mh - > FirstPhysicalEUN = le16_to_cpu ( mh - > FirstPhysicalEUN ) ;
mh - > FormattedSize = le32_to_cpu ( mh - > FormattedSize ) ;
2005-04-17 02:20:36 +04:00
printk ( KERN_INFO " DataOrgID = %s \n "
" NumEraseUnits = %d \n "
" FirstPhysicalEUN = %d \n "
" FormattedSize = %d \n "
" UnitSizeFactor = %d \n " ,
mh - > DataOrgID , mh - > NumEraseUnits ,
mh - > FirstPhysicalEUN , mh - > FormattedSize ,
mh - > UnitSizeFactor ) ;
blocks = mtd - > size > > this - > phys_erase_shift ;
maxblocks = min ( 32768U , mtd - > erasesize - psize ) ;
if ( mh - > UnitSizeFactor = = 0x00 ) {
/* Auto-determine UnitSizeFactor. The constraints are:
- There can be at most 32768 virtual blocks .
- There can be at most ( virtual block size - page size )
2006-05-13 21:07:53 +04:00
virtual blocks ( because MediaHeader + BBT must fit in 1 ) .
*/
2005-04-17 02:20:36 +04:00
mh - > UnitSizeFactor = 0xff ;
while ( blocks > maxblocks ) {
blocks > > = 1 ;
maxblocks = min ( 32768U , ( maxblocks < < 1 ) + psize ) ;
mh - > UnitSizeFactor - - ;
}
printk ( KERN_WARNING " UnitSizeFactor=0x00 detected. Correct value is assumed to be 0x%02x. \n " , mh - > UnitSizeFactor ) ;
}
/* NOTE: The lines below modify internal variables of the NAND and MTD
layers ; variables with have already been configured by nand_scan .
Unfortunately , we didn ' t know before this point what these values
should be . Thus , this code is somewhat dependant on the exact
implementation of the NAND layer . */
if ( mh - > UnitSizeFactor ! = 0xff ) {
this - > bbt_erase_shift + = ( 0xff - mh - > UnitSizeFactor ) ;
mtd - > erasesize < < = ( 0xff - mh - > UnitSizeFactor ) ;
printk ( KERN_INFO " Setting virtual erase size to %d \n " , mtd - > erasesize ) ;
blocks = mtd - > size > > this - > bbt_erase_shift ;
maxblocks = min ( 32768U , mtd - > erasesize - psize ) ;
}
if ( blocks > maxblocks ) {
printk ( KERN_ERR " UnitSizeFactor of 0x%02x is inconsistent with device size. Aborting. \n " , mh - > UnitSizeFactor ) ;
goto out ;
}
/* Skip past the media headers. */
offs = max ( doc - > mh0_page , doc - > mh1_page ) ;
offs < < = this - > page_shift ;
offs + = mtd - > erasesize ;
2005-03-30 00:57:48 +04:00
if ( show_firmware_partition = = 1 ) {
parts [ 0 ] . name = " DiskOnChip Firmware / Media Header partition " ;
parts [ 0 ] . offset = 0 ;
parts [ 0 ] . size = offs ;
numparts = 1 ;
}
parts [ numparts ] . name = " DiskOnChip BDTL partition " ;
parts [ numparts ] . offset = offs ;
parts [ numparts ] . size = ( mh - > NumEraseUnits - numheaders ) < < this - > bbt_erase_shift ;
offs + = parts [ numparts ] . size ;
numparts + + ;
2005-04-17 02:20:36 +04:00
if ( offs < mtd - > size ) {
2005-03-30 00:57:48 +04:00
parts [ numparts ] . name = " DiskOnChip Remainder partition " ;
parts [ numparts ] . offset = offs ;
parts [ numparts ] . size = mtd - > size - offs ;
numparts + + ;
2005-04-17 02:20:36 +04:00
}
2005-03-30 00:57:48 +04:00
ret = numparts ;
2006-05-13 21:07:53 +04:00
out :
2005-04-17 02:20:36 +04:00
kfree ( buf ) ;
return ret ;
}
/* This is a stripped-down copy of the code in inftlmount.c */
2006-05-13 21:07:53 +04:00
static inline int __init inftl_partscan ( struct mtd_info * mtd , struct mtd_partition * parts )
2005-04-17 02:20:36 +04:00
{
struct nand_chip * this = mtd - > priv ;
struct doc_priv * doc = this - > priv ;
int ret = 0 ;
u_char * buf ;
struct INFTLMediaHeader * mh ;
struct INFTLPartition * ip ;
int numparts = 0 ;
int blocks ;
int vshift , lastvunit = 0 ;
int i ;
int end = mtd - > size ;
if ( inftl_bbt_write )
end - = ( INFTL_BBT_RESERVED_BLOCKS < < this - > phys_erase_shift ) ;
2006-05-23 01:18:05 +04:00
buf = kmalloc ( mtd - > writesize , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! buf ) {
printk ( KERN_ERR " DiskOnChip mediaheader kmalloc failed! \n " ) ;
return 0 ;
}
2006-05-13 21:07:53 +04:00
if ( ! find_media_headers ( mtd , buf , " BNAND " , 0 ) )
goto out ;
2005-04-17 02:20:36 +04:00
doc - > mh1_page = doc - > mh0_page + ( 4096 > > this - > page_shift ) ;
2006-05-13 21:07:53 +04:00
mh = ( struct INFTLMediaHeader * ) buf ;
2005-04-17 02:20:36 +04:00
mh - > NoOfBootImageBlocks = le32_to_cpu ( mh - > NoOfBootImageBlocks ) ;
mh - > NoOfBinaryPartitions = le32_to_cpu ( mh - > NoOfBinaryPartitions ) ;
mh - > NoOfBDTLPartitions = le32_to_cpu ( mh - > NoOfBDTLPartitions ) ;
mh - > BlockMultiplierBits = le32_to_cpu ( mh - > BlockMultiplierBits ) ;
mh - > FormatFlags = le32_to_cpu ( mh - > FormatFlags ) ;
mh - > PercentUsed = le32_to_cpu ( mh - > PercentUsed ) ;
2005-11-07 14:15:49 +03:00
2005-04-17 02:20:36 +04:00
printk ( KERN_INFO " bootRecordID = %s \n "
" NoOfBootImageBlocks = %d \n "
" NoOfBinaryPartitions = %d \n "
" NoOfBDTLPartitions = %d \n "
" BlockMultiplerBits = %d \n "
" FormatFlgs = %d \n "
" OsakVersion = %d.%d.%d.%d \n "
" PercentUsed = %d \n " ,
mh - > bootRecordID , mh - > NoOfBootImageBlocks ,
mh - > NoOfBinaryPartitions ,
mh - > NoOfBDTLPartitions ,
mh - > BlockMultiplierBits , mh - > FormatFlags ,
( ( unsigned char * ) & mh - > OsakVersion ) [ 0 ] & 0xf ,
( ( unsigned char * ) & mh - > OsakVersion ) [ 1 ] & 0xf ,
( ( unsigned char * ) & mh - > OsakVersion ) [ 2 ] & 0xf ,
( ( unsigned char * ) & mh - > OsakVersion ) [ 3 ] & 0xf ,
mh - > PercentUsed ) ;
vshift = this - > phys_erase_shift + mh - > BlockMultiplierBits ;
blocks = mtd - > size > > vshift ;
if ( blocks > 32768 ) {
printk ( KERN_ERR " BlockMultiplierBits=%d is inconsistent with device size. Aborting. \n " , mh - > BlockMultiplierBits ) ;
goto out ;
}
blocks = doc - > chips_per_floor < < ( this - > chip_shift - this - > phys_erase_shift ) ;
if ( inftl_bbt_write & & ( blocks > mtd - > erasesize ) ) {
printk ( KERN_ERR " Writeable BBTs spanning more than one erase block are not yet supported. FIX ME! \n " ) ;
goto out ;
}
/* Scan the partitions */
for ( i = 0 ; ( i < 4 ) ; i + + ) {
ip = & ( mh - > Partitions [ i ] ) ;
ip - > virtualUnits = le32_to_cpu ( ip - > virtualUnits ) ;
ip - > firstUnit = le32_to_cpu ( ip - > firstUnit ) ;
ip - > lastUnit = le32_to_cpu ( ip - > lastUnit ) ;
ip - > flags = le32_to_cpu ( ip - > flags ) ;
ip - > spareUnits = le32_to_cpu ( ip - > spareUnits ) ;
ip - > Reserved0 = le32_to_cpu ( ip - > Reserved0 ) ;
printk ( KERN_INFO " PARTITION[%d] -> \n "
" virtualUnits = %d \n "
" firstUnit = %d \n "
" lastUnit = %d \n "
" flags = 0x%x \n "
" spareUnits = %d \n " ,
i , ip - > virtualUnits , ip - > firstUnit ,
ip - > lastUnit , ip - > flags ,
ip - > spareUnits ) ;
2005-03-30 00:57:48 +04:00
if ( ( show_firmware_partition = = 1 ) & &
( i = = 0 ) & & ( ip - > firstUnit > 0 ) ) {
2005-04-17 02:20:36 +04:00
parts [ 0 ] . name = " DiskOnChip IPL / Media Header partition " ;
parts [ 0 ] . offset = 0 ;
parts [ 0 ] . size = mtd - > erasesize * ip - > firstUnit ;
numparts = 1 ;
}
if ( ip - > flags & INFTL_BINARY )
parts [ numparts ] . name = " DiskOnChip BDK partition " ;
else
parts [ numparts ] . name = " DiskOnChip BDTL partition " ;
parts [ numparts ] . offset = ip - > firstUnit < < vshift ;
parts [ numparts ] . size = ( 1 + ip - > lastUnit - ip - > firstUnit ) < < vshift ;
numparts + + ;
2006-05-13 21:07:53 +04:00
if ( ip - > lastUnit > lastvunit )
lastvunit = ip - > lastUnit ;
if ( ip - > flags & INFTL_LAST )
break ;
2005-04-17 02:20:36 +04:00
}
lastvunit + + ;
if ( ( lastvunit < < vshift ) < end ) {
parts [ numparts ] . name = " DiskOnChip Remainder partition " ;
parts [ numparts ] . offset = lastvunit < < vshift ;
parts [ numparts ] . size = end - parts [ numparts ] . offset ;
numparts + + ;
}
ret = numparts ;
2006-05-13 21:07:53 +04:00
out :
2005-04-17 02:20:36 +04:00
kfree ( buf ) ;
return ret ;
}
static int __init nftl_scan_bbt ( struct mtd_info * mtd )
{
int ret , numparts ;
struct nand_chip * this = mtd - > priv ;
struct doc_priv * doc = this - > priv ;
struct mtd_partition parts [ 2 ] ;
2006-05-13 21:07:53 +04:00
memset ( ( char * ) parts , 0 , sizeof ( parts ) ) ;
2005-04-17 02:20:36 +04:00
/* On NFTL, we have to find the media headers before we can read the
BBTs , since they ' re stored in the media header eraseblocks . */
numparts = nftl_partscan ( mtd , parts ) ;
2006-05-13 21:07:53 +04:00
if ( ! numparts )
return - EIO ;
2005-04-17 02:20:36 +04:00
this - > bbt_td - > options = NAND_BBT_ABSPAGE | NAND_BBT_8BIT |
NAND_BBT_SAVECONTENT | NAND_BBT_WRITE |
NAND_BBT_VERSION ;
this - > bbt_td - > veroffs = 7 ;
this - > bbt_td - > pages [ 0 ] = doc - > mh0_page + 1 ;
if ( doc - > mh1_page ! = - 1 ) {
this - > bbt_md - > options = NAND_BBT_ABSPAGE | NAND_BBT_8BIT |
NAND_BBT_SAVECONTENT | NAND_BBT_WRITE |
NAND_BBT_VERSION ;
this - > bbt_md - > veroffs = 7 ;
this - > bbt_md - > pages [ 0 ] = doc - > mh1_page + 1 ;
} else {
this - > bbt_md = NULL ;
}
/* It's safe to set bd=NULL below because NAND_BBT_CREATE is not set.
At least as nand_bbt . c is currently written . */
if ( ( ret = nand_scan_bbt ( mtd , NULL ) ) )
return ret ;
add_mtd_device ( mtd ) ;
# ifdef CONFIG_MTD_PARTITIONS
if ( ! no_autopart )
add_mtd_partitions ( mtd , parts , numparts ) ;
# endif
return 0 ;
}
static int __init inftl_scan_bbt ( struct mtd_info * mtd )
{
int ret , numparts ;
struct nand_chip * this = mtd - > priv ;
struct doc_priv * doc = this - > priv ;
struct mtd_partition parts [ 5 ] ;
if ( this - > numchips > doc - > chips_per_floor ) {
printk ( KERN_ERR " Multi-floor INFTL devices not yet supported. \n " ) ;
return - EIO ;
}
if ( DoC_is_MillenniumPlus ( doc ) ) {
this - > bbt_td - > options = NAND_BBT_2BIT | NAND_BBT_ABSPAGE ;
if ( inftl_bbt_write )
this - > bbt_td - > options | = NAND_BBT_WRITE ;
this - > bbt_td - > pages [ 0 ] = 2 ;
this - > bbt_md = NULL ;
} else {
2006-05-13 21:07:53 +04:00
this - > bbt_td - > options = NAND_BBT_LASTBLOCK | NAND_BBT_8BIT | NAND_BBT_VERSION ;
2005-04-17 02:20:36 +04:00
if ( inftl_bbt_write )
this - > bbt_td - > options | = NAND_BBT_WRITE ;
this - > bbt_td - > offs = 8 ;
this - > bbt_td - > len = 8 ;
this - > bbt_td - > veroffs = 7 ;
this - > bbt_td - > maxblocks = INFTL_BBT_RESERVED_BLOCKS ;
this - > bbt_td - > reserved_block_code = 0x01 ;
this - > bbt_td - > pattern = " MSYS_BBT " ;
2006-05-13 21:07:53 +04:00
this - > bbt_md - > options = NAND_BBT_LASTBLOCK | NAND_BBT_8BIT | NAND_BBT_VERSION ;
2005-04-17 02:20:36 +04:00
if ( inftl_bbt_write )
this - > bbt_md - > options | = NAND_BBT_WRITE ;
this - > bbt_md - > offs = 8 ;
this - > bbt_md - > len = 8 ;
this - > bbt_md - > veroffs = 7 ;
this - > bbt_md - > maxblocks = INFTL_BBT_RESERVED_BLOCKS ;
this - > bbt_md - > reserved_block_code = 0x01 ;
this - > bbt_md - > pattern = " TBB_SYSM " ;
}
/* It's safe to set bd=NULL below because NAND_BBT_CREATE is not set.
At least as nand_bbt . c is currently written . */
if ( ( ret = nand_scan_bbt ( mtd , NULL ) ) )
return ret ;
2006-05-13 21:07:53 +04:00
memset ( ( char * ) parts , 0 , sizeof ( parts ) ) ;
2005-04-17 02:20:36 +04:00
numparts = inftl_partscan ( mtd , parts ) ;
/* At least for now, require the INFTL Media Header. We could probably
do without it for non - INFTL use , since all it gives us is
autopartitioning , but I want to give it more thought . */
2006-05-13 21:07:53 +04:00
if ( ! numparts )
return - EIO ;
2005-04-17 02:20:36 +04:00
add_mtd_device ( mtd ) ;
# ifdef CONFIG_MTD_PARTITIONS
if ( ! no_autopart )
add_mtd_partitions ( mtd , parts , numparts ) ;
# endif
return 0 ;
}
static inline int __init doc2000_init ( struct mtd_info * mtd )
{
struct nand_chip * this = mtd - > priv ;
struct doc_priv * doc = this - > priv ;
this - > read_byte = doc2000_read_byte ;
this - > write_buf = doc2000_writebuf ;
this - > read_buf = doc2000_readbuf ;
this - > verify_buf = doc2000_verifybuf ;
this - > scan_bbt = nftl_scan_bbt ;
doc - > CDSNControl = CDSN_CTRL_FLASH_IO | CDSN_CTRL_ECC_IO ;
doc2000_count_chips ( mtd ) ;
mtd - > name = " DiskOnChip 2000 (NFTL Model) " ;
return ( 4 * doc - > chips_per_floor ) ;
}
static inline int __init doc2001_init ( struct mtd_info * mtd )
{
struct nand_chip * this = mtd - > priv ;
struct doc_priv * doc = this - > priv ;
this - > read_byte = doc2001_read_byte ;
this - > write_buf = doc2001_writebuf ;
this - > read_buf = doc2001_readbuf ;
this - > verify_buf = doc2001_verifybuf ;
ReadDOC ( doc - > virtadr , ChipID ) ;
ReadDOC ( doc - > virtadr , ChipID ) ;
ReadDOC ( doc - > virtadr , ChipID ) ;
if ( ReadDOC ( doc - > virtadr , ChipID ) ! = DOC_ChipID_DocMil ) {
/* It's not a Millennium; it's one of the newer
2005-11-07 14:15:49 +03:00
DiskOnChip 2000 units with a similar ASIC .
2005-04-17 02:20:36 +04:00
Treat it like a Millennium , except that it
can have multiple chips . */
doc2000_count_chips ( mtd ) ;
mtd - > name = " DiskOnChip 2000 (INFTL Model) " ;
this - > scan_bbt = inftl_scan_bbt ;
return ( 4 * doc - > chips_per_floor ) ;
} else {
/* Bog-standard Millennium */
doc - > chips_per_floor = 1 ;
mtd - > name = " DiskOnChip Millennium " ;
this - > scan_bbt = nftl_scan_bbt ;
return 1 ;
}
}
static inline int __init doc2001plus_init ( struct mtd_info * mtd )
{
struct nand_chip * this = mtd - > priv ;
struct doc_priv * doc = this - > priv ;
this - > read_byte = doc2001plus_read_byte ;
this - > write_buf = doc2001plus_writebuf ;
this - > read_buf = doc2001plus_readbuf ;
this - > verify_buf = doc2001plus_verifybuf ;
this - > scan_bbt = inftl_scan_bbt ;
2006-05-24 01:25:53 +04:00
this - > cmd_ctrl = NULL ;
2005-04-17 02:20:36 +04:00
this - > select_chip = doc2001plus_select_chip ;
this - > cmdfunc = doc2001plus_command ;
2006-05-23 17:59:58 +04:00
this - > ecc . hwctl = doc2001plus_enable_hwecc ;
2005-04-17 02:20:36 +04:00
doc - > chips_per_floor = 1 ;
mtd - > name = " DiskOnChip Millennium Plus " ;
return 1 ;
}
2006-01-15 00:20:43 +03:00
static int __init doc_probe ( unsigned long physadr )
2005-04-17 02:20:36 +04:00
{
unsigned char ChipID ;
struct mtd_info * mtd ;
struct nand_chip * nand ;
struct doc_priv * doc ;
void __iomem * virtadr ;
unsigned char save_control ;
unsigned char tmp , tmpb , tmpc ;
int reg , len , numchips ;
int ret = 0 ;
virtadr = ioremap ( physadr , DOC_IOREMAP_LEN ) ;
if ( ! virtadr ) {
printk ( KERN_ERR " Diskonchip ioremap failed: 0x%x bytes at 0x%lx \n " , DOC_IOREMAP_LEN , physadr ) ;
return - EIO ;
}
/* It's not possible to cleanly detect the DiskOnChip - the
* bootup procedure will put the device into reset mode , and
* it ' s not possible to talk to it without actually writing
* to the DOCControl register . So we store the current contents
* of the DOCControl register ' s location , in case we later decide
* that it ' s not a DiskOnChip , and want to put it back how we
2005-11-07 14:15:49 +03:00
* found it .
2005-04-17 02:20:36 +04:00
*/
save_control = ReadDOC ( virtadr , DOCControl ) ;
/* Reset the DiskOnChip ASIC */
2006-05-13 21:07:53 +04:00
WriteDOC ( DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET , virtadr , DOCControl ) ;
WriteDOC ( DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET , virtadr , DOCControl ) ;
2005-04-17 02:20:36 +04:00
/* Enable the DiskOnChip ASIC */
2006-05-13 21:07:53 +04:00
WriteDOC ( DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL , virtadr , DOCControl ) ;
WriteDOC ( DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL , virtadr , DOCControl ) ;
2005-04-17 02:20:36 +04:00
ChipID = ReadDOC ( virtadr , ChipID ) ;
2006-05-13 21:07:53 +04:00
switch ( ChipID ) {
2005-04-17 02:20:36 +04:00
case DOC_ChipID_Doc2k :
reg = DoC_2k_ECCStatus ;
break ;
case DOC_ChipID_DocMil :
reg = DoC_ECCConf ;
break ;
case DOC_ChipID_DocMilPlus16 :
case DOC_ChipID_DocMilPlus32 :
case 0 :
/* Possible Millennium Plus, need to do more checks */
/* Possibly release from power down mode */
for ( tmp = 0 ; ( tmp < 4 ) ; tmp + + )
ReadDOC ( virtadr , Mplus_Power ) ;
/* Reset the Millennium Plus ASIC */
2006-05-13 21:07:53 +04:00
tmp = DOC_MODE_RESET | DOC_MODE_MDWREN | DOC_MODE_RST_LAT | DOC_MODE_BDECT ;
2005-04-17 02:20:36 +04:00
WriteDOC ( tmp , virtadr , Mplus_DOCControl ) ;
WriteDOC ( ~ tmp , virtadr , Mplus_CtrlConfirm ) ;
mdelay ( 1 ) ;
/* Enable the Millennium Plus ASIC */
2006-05-13 21:07:53 +04:00
tmp = DOC_MODE_NORMAL | DOC_MODE_MDWREN | DOC_MODE_RST_LAT | DOC_MODE_BDECT ;
2005-04-17 02:20:36 +04:00
WriteDOC ( tmp , virtadr , Mplus_DOCControl ) ;
WriteDOC ( ~ tmp , virtadr , Mplus_CtrlConfirm ) ;
mdelay ( 1 ) ;
ChipID = ReadDOC ( virtadr , ChipID ) ;
switch ( ChipID ) {
case DOC_ChipID_DocMilPlus16 :
reg = DoC_Mplus_Toggle ;
break ;
case DOC_ChipID_DocMilPlus32 :
printk ( KERN_ERR " DiskOnChip Millennium Plus 32MB is not supported, ignoring. \n " ) ;
default :
ret = - ENODEV ;
goto notfound ;
}
break ;
default :
ret = - ENODEV ;
goto notfound ;
}
/* Check the TOGGLE bit in the ECC register */
2006-05-13 21:07:53 +04:00
tmp = ReadDOC_ ( virtadr , reg ) & DOC_TOGGLE_BIT ;
2005-04-17 02:20:36 +04:00
tmpb = ReadDOC_ ( virtadr , reg ) & DOC_TOGGLE_BIT ;
tmpc = ReadDOC_ ( virtadr , reg ) & DOC_TOGGLE_BIT ;
if ( ( tmp = = tmpb ) | | ( tmp ! = tmpc ) ) {
printk ( KERN_WARNING " Possible DiskOnChip at 0x%lx failed TOGGLE test, dropping. \n " , physadr ) ;
ret = - ENODEV ;
goto notfound ;
}
for ( mtd = doclist ; mtd ; mtd = doc - > nextdoc ) {
unsigned char oldval ;
unsigned char newval ;
nand = mtd - > priv ;
doc = nand - > priv ;
/* Use the alias resolution register to determine if this is
in fact the same DOC aliased to a new address . If writes
to one chip ' s alias resolution register change the value on
the other chip , they ' re the same chip . */
if ( ChipID = = DOC_ChipID_DocMilPlus16 ) {
oldval = ReadDOC ( doc - > virtadr , Mplus_AliasResolution ) ;
newval = ReadDOC ( virtadr , Mplus_AliasResolution ) ;
} else {
oldval = ReadDOC ( doc - > virtadr , AliasResolution ) ;
newval = ReadDOC ( virtadr , AliasResolution ) ;
}
if ( oldval ! = newval )
continue ;
if ( ChipID = = DOC_ChipID_DocMilPlus16 ) {
WriteDOC ( ~ newval , virtadr , Mplus_AliasResolution ) ;
oldval = ReadDOC ( doc - > virtadr , Mplus_AliasResolution ) ;
2006-05-13 21:07:53 +04:00
WriteDOC ( newval , virtadr , Mplus_AliasResolution ) ; // restore it
2005-04-17 02:20:36 +04:00
} else {
WriteDOC ( ~ newval , virtadr , AliasResolution ) ;
oldval = ReadDOC ( doc - > virtadr , AliasResolution ) ;
2006-05-13 21:07:53 +04:00
WriteDOC ( newval , virtadr , AliasResolution ) ; // restore it
2005-04-17 02:20:36 +04:00
}
newval = ~ newval ;
if ( oldval = = newval ) {
printk ( KERN_DEBUG " Found alias of DOC at 0x%lx to 0x%lx \n " , doc - > physadr , physadr ) ;
goto notfound ;
}
}
printk ( KERN_NOTICE " DiskOnChip found at 0x%lx \n " , physadr ) ;
len = sizeof ( struct mtd_info ) +
2006-05-13 21:07:53 +04:00
sizeof ( struct nand_chip ) + sizeof ( struct doc_priv ) + ( 2 * sizeof ( struct nand_bbt_descr ) ) ;
mtd = kmalloc ( len , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! mtd ) {
printk ( KERN_ERR " DiskOnChip kmalloc (%d bytes) failed! \n " , len ) ;
ret = - ENOMEM ;
goto fail ;
}
memset ( mtd , 0 , len ) ;
nand = ( struct nand_chip * ) ( mtd + 1 ) ;
doc = ( struct doc_priv * ) ( nand + 1 ) ;
nand - > bbt_td = ( struct nand_bbt_descr * ) ( doc + 1 ) ;
nand - > bbt_md = nand - > bbt_td + 1 ;
mtd - > priv = nand ;
mtd - > owner = THIS_MODULE ;
nand - > priv = doc ;
nand - > select_chip = doc200x_select_chip ;
2006-05-24 01:25:53 +04:00
nand - > cmd_ctrl = doc200x_hwcontrol ;
2005-04-17 02:20:36 +04:00
nand - > dev_ready = doc200x_dev_ready ;
nand - > waitfunc = doc200x_wait ;
nand - > block_bad = doc200x_block_bad ;
2006-05-23 14:00:46 +04:00
nand - > ecc . hwctl = doc200x_enable_hwecc ;
nand - > ecc . calculate = doc200x_calculate_ecc ;
nand - > ecc . correct = doc200x_correct_data ;
2005-04-17 02:20:36 +04:00
2006-05-28 00:16:10 +04:00
nand - > ecc . layout = & doc200x_oobinfo ;
2006-05-23 14:00:46 +04:00
nand - > ecc . mode = NAND_ECC_HW_SYNDROME ;
nand - > ecc . size = 512 ;
nand - > ecc . bytes = 6 ;
2006-05-26 20:52:08 +04:00
nand - > options = NAND_USE_FLASH_BBT ;
2005-04-17 02:20:36 +04:00
doc - > physadr = physadr ;
doc - > virtadr = virtadr ;
doc - > ChipID = ChipID ;
doc - > curfloor = - 1 ;
doc - > curchip = - 1 ;
doc - > mh0_page = - 1 ;
doc - > mh1_page = - 1 ;
doc - > nextdoc = doclist ;
if ( ChipID = = DOC_ChipID_Doc2k )
numchips = doc2000_init ( mtd ) ;
else if ( ChipID = = DOC_ChipID_DocMilPlus16 )
numchips = doc2001plus_init ( mtd ) ;
else
numchips = doc2001_init ( mtd ) ;
if ( ( ret = nand_scan ( mtd , numchips ) ) ) {
/* DBB note: i believe nand_release is necessary here, as
buffers may have been allocated in nand_base . Check with
Thomas . FIX ME ! */
/* nand_release will call del_mtd_device, but we haven't yet
added it . This is handled without incident by
del_mtd_device , as far as I can tell . */
nand_release ( mtd ) ;
kfree ( mtd ) ;
goto fail ;
}
/* Success! */
doclist = mtd ;
return 0 ;
2006-05-13 21:07:53 +04:00
notfound :
2005-04-17 02:20:36 +04:00
/* Put back the contents of the DOCControl register, in case it's not
actually a DiskOnChip . */
WriteDOC ( save_control , virtadr , DOCControl ) ;
2006-05-13 21:07:53 +04:00
fail :
2005-04-17 02:20:36 +04:00
iounmap ( virtadr ) ;
return ret ;
}
static void release_nanddoc ( void )
{
2006-05-13 21:07:53 +04:00
struct mtd_info * mtd , * nextmtd ;
2005-04-17 02:20:36 +04:00
struct nand_chip * nand ;
struct doc_priv * doc ;
for ( mtd = doclist ; mtd ; mtd = nextmtd ) {
nand = mtd - > priv ;
doc = nand - > priv ;
nextmtd = doc - > nextdoc ;
nand_release ( mtd ) ;
iounmap ( doc - > virtadr ) ;
kfree ( mtd ) ;
}
}
static int __init init_nanddoc ( void )
{
int i , ret = 0 ;
/* We could create the decoder on demand, if memory is a concern.
2005-11-07 14:15:49 +03:00
* This way we have it handy , if an error happens
2005-04-17 02:20:36 +04:00
*
* Symbolsize is 10 ( bits )
* Primitve polynomial is x ^ 10 + x ^ 3 + 1
* first consecutive root is 510
* primitve element to generate roots = 1
* generator polinomial degree = 4
*/
rs_decoder = init_rs ( 10 , 0x409 , FCR , 1 , NROOTS ) ;
2006-05-13 21:07:53 +04:00
if ( ! rs_decoder ) {
printk ( KERN_ERR " DiskOnChip: Could not create a RS decoder \n " ) ;
2005-04-17 02:20:36 +04:00
return - ENOMEM ;
}
if ( doc_config_location ) {
printk ( KERN_INFO " Using configured DiskOnChip probe address 0x%lx \n " , doc_config_location ) ;
ret = doc_probe ( doc_config_location ) ;
if ( ret < 0 )
goto outerr ;
} else {
2006-05-13 21:07:53 +04:00
for ( i = 0 ; ( doc_locations [ i ] ! = 0xffffffff ) ; i + + ) {
2005-04-17 02:20:36 +04:00
doc_probe ( doc_locations [ i ] ) ;
}
}
/* No banner message any more. Print a message if no DiskOnChip
found , so the user knows we at least tried . */
if ( ! doclist ) {
printk ( KERN_INFO " No valid DiskOnChip devices found \n " ) ;
ret = - ENODEV ;
goto outerr ;
}
return 0 ;
2006-05-13 21:07:53 +04:00
outerr :
2005-04-17 02:20:36 +04:00
free_rs ( rs_decoder ) ;
return ret ;
}
static void __exit cleanup_nanddoc ( void )
{
/* Cleanup the nand/DoC resources */
release_nanddoc ( ) ;
/* Free the reed solomon resources */
if ( rs_decoder ) {
free_rs ( rs_decoder ) ;
}
}
module_init ( init_nanddoc ) ;
module_exit ( cleanup_nanddoc ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " David Woodhouse <dwmw2@infradead.org> " ) ;
MODULE_DESCRIPTION ( " M-Systems DiskOnChip 2000, Millennium and Millennium Plus device driver \n " ) ;