2005-04-17 02:20:36 +04:00
/*
* Common Flash Interface support :
* ST Advanced Architecture Command Set ( ID 0x0020 )
*
* ( C ) 2000 Red Hat . GPL ' d
*
2009-09-14 11:25:28 +04:00
* 10 / 10 / 2000 Nicolas Pitre < nico @ fluxnic . net >
2005-04-17 02:20:36 +04:00
* - completely revamped method functions so they are aware and
* independent of the flash geometry ( buswidth , interleave , etc . )
* - scalability vs code size is completely set at compile - time
* ( see include / linux / mtd / cfi . h for selection )
* - optimized write buffer method
* 06 / 21 / 2002 Joern Engel < joern @ wh . fh - wedel . de > and others
* - modified Intel Command Set 0x0001 to support ST Advanced Architecture
* ( command set 0x0020 )
* - added a writev function
2005-07-13 19:45:43 +04:00
* 07 / 13 / 2005 Joern Engel < joern @ wh . fh - wedel . de >
* - Plugged memory leak in cfi_staa_writev ( ) .
2005-04-17 02:20:36 +04:00
*/
# include <linux/module.h>
# include <linux/types.h>
# include <linux/kernel.h>
# include <linux/sched.h>
# include <linux/init.h>
# include <asm/io.h>
# include <asm/byteorder.h>
# include <linux/errno.h>
# include <linux/slab.h>
# include <linux/delay.h>
# include <linux/interrupt.h>
# include <linux/mtd/map.h>
# include <linux/mtd/cfi.h>
# include <linux/mtd/mtd.h>
static int cfi_staa_read ( struct mtd_info * , loff_t , size_t , size_t * , u_char * ) ;
static int cfi_staa_write_buffers ( struct mtd_info * , loff_t , size_t , size_t * , const u_char * ) ;
static int cfi_staa_writev ( struct mtd_info * mtd , const struct kvec * vecs ,
unsigned long count , loff_t to , size_t * retlen ) ;
static int cfi_staa_erase_varsize ( struct mtd_info * , struct erase_info * ) ;
static void cfi_staa_sync ( struct mtd_info * ) ;
2008-12-10 16:37:21 +03:00
static int cfi_staa_lock ( struct mtd_info * mtd , loff_t ofs , uint64_t len ) ;
static int cfi_staa_unlock ( struct mtd_info * mtd , loff_t ofs , uint64_t len ) ;
2005-04-17 02:20:36 +04:00
static int cfi_staa_suspend ( struct mtd_info * ) ;
static void cfi_staa_resume ( struct mtd_info * ) ;
static void cfi_staa_destroy ( struct mtd_info * ) ;
struct mtd_info * cfi_cmdset_0020 ( struct map_info * , int ) ;
static struct mtd_info * cfi_staa_setup ( struct map_info * ) ;
static struct mtd_chip_driver cfi_staa_chipdrv = {
. probe = NULL , /* Not usable directly */
. destroy = cfi_staa_destroy ,
. name = " cfi_cmdset_0020 " ,
. module = THIS_MODULE
} ;
/* #define DEBUG_LOCK_BITS */
//#define DEBUG_CFI_FEATURES
# ifdef DEBUG_CFI_FEATURES
static void cfi_tell_features ( struct cfi_pri_intelext * extp )
{
int i ;
printk ( " Feature/Command Support: %4.4X \n " , extp - > FeatureSupport ) ;
printk ( " - Chip Erase: %s \n " , extp - > FeatureSupport & 1 ? " supported " : " unsupported " ) ;
printk ( " - Suspend Erase: %s \n " , extp - > FeatureSupport & 2 ? " supported " : " unsupported " ) ;
printk ( " - Suspend Program: %s \n " , extp - > FeatureSupport & 4 ? " supported " : " unsupported " ) ;
printk ( " - Legacy Lock/Unlock: %s \n " , extp - > FeatureSupport & 8 ? " supported " : " unsupported " ) ;
printk ( " - Queued Erase: %s \n " , extp - > FeatureSupport & 16 ? " supported " : " unsupported " ) ;
printk ( " - Instant block lock: %s \n " , extp - > FeatureSupport & 32 ? " supported " : " unsupported " ) ;
printk ( " - Protection Bits: %s \n " , extp - > FeatureSupport & 64 ? " supported " : " unsupported " ) ;
printk ( " - Page-mode read: %s \n " , extp - > FeatureSupport & 128 ? " supported " : " unsupported " ) ;
printk ( " - Synchronous read: %s \n " , extp - > FeatureSupport & 256 ? " supported " : " unsupported " ) ;
for ( i = 9 ; i < 32 ; i + + ) {
2005-11-07 14:15:37 +03:00
if ( extp - > FeatureSupport & ( 1 < < i ) )
2005-04-17 02:20:36 +04:00
printk ( " - Unknown Bit %X: supported \n " , i ) ;
}
2005-11-07 14:15:37 +03:00
2005-04-17 02:20:36 +04:00
printk ( " Supported functions after Suspend: %2.2X \n " , extp - > SuspendCmdSupport ) ;
printk ( " - Program after Erase Suspend: %s \n " , extp - > SuspendCmdSupport & 1 ? " supported " : " unsupported " ) ;
for ( i = 1 ; i < 8 ; i + + ) {
if ( extp - > SuspendCmdSupport & ( 1 < < i ) )
printk ( " - Unknown Bit %X: supported \n " , i ) ;
}
2005-11-07 14:15:37 +03:00
2005-04-17 02:20:36 +04:00
printk ( " Block Status Register Mask: %4.4X \n " , extp - > BlkStatusRegMask ) ;
printk ( " - Lock Bit Active: %s \n " , extp - > BlkStatusRegMask & 1 ? " yes " : " no " ) ;
printk ( " - Valid Bit Active: %s \n " , extp - > BlkStatusRegMask & 2 ? " yes " : " no " ) ;
for ( i = 2 ; i < 16 ; i + + ) {
if ( extp - > BlkStatusRegMask & ( 1 < < i ) )
printk ( " - Unknown Bit %X Active: yes \n " , i ) ;
}
2005-11-07 14:15:37 +03:00
printk ( " Vcc Logic Supply Optimum Program/Erase Voltage: %d.%d V \n " ,
2005-04-17 02:20:36 +04:00
extp - > VccOptimal > > 8 , extp - > VccOptimal & 0xf ) ;
if ( extp - > VppOptimal )
2005-11-07 14:15:37 +03:00
printk ( " Vpp Programming Supply Optimum Program/Erase Voltage: %d.%d V \n " ,
2005-04-17 02:20:36 +04:00
extp - > VppOptimal > > 8 , extp - > VppOptimal & 0xf ) ;
}
# endif
/* This routine is made available to other mtd code via
* inter_module_register . It must only be accessed through
* inter_module_get which will bump the use count of this module . The
* addresses passed back in cfi are valid as long as the use count of
* this module is non - zero , i . e . between inter_module_get and
* inter_module_put . Keith Owens < kaos @ ocs . com . au > 29 Oct 2000.
*/
struct mtd_info * cfi_cmdset_0020 ( struct map_info * map , int primary )
{
struct cfi_private * cfi = map - > fldrv_priv ;
int i ;
if ( cfi - > cfi_mode ) {
2005-11-07 14:15:37 +03:00
/*
2005-04-17 02:20:36 +04:00
* It ' s a real CFI chip , not one for which the probe
* routine faked a CFI structure . So we read the feature
* table from it .
*/
__u16 adr = primary ? cfi - > cfiq - > P_ADR : cfi - > cfiq - > A_ADR ;
struct cfi_pri_intelext * extp ;
extp = ( struct cfi_pri_intelext * ) cfi_read_pri ( map , adr , sizeof ( * extp ) , " ST Microelectronics " ) ;
if ( ! extp )
return NULL ;
2005-07-21 01:01:17 +04:00
if ( extp - > MajorVersion ! = ' 1 ' | |
( extp - > MinorVersion < ' 0 ' | | extp - > MinorVersion > ' 3 ' ) ) {
printk ( KERN_ERR " Unknown ST Microelectronics "
" Extended Query version %c.%c. \n " ,
extp - > MajorVersion , extp - > MinorVersion ) ;
kfree ( extp ) ;
return NULL ;
}
2005-04-17 02:20:36 +04:00
/* Do some byteswapping if necessary */
extp - > FeatureSupport = cfi32_to_cpu ( extp - > FeatureSupport ) ;
extp - > BlkStatusRegMask = cfi32_to_cpu ( extp - > BlkStatusRegMask ) ;
2005-11-07 14:15:37 +03:00
2005-04-17 02:20:36 +04:00
# ifdef DEBUG_CFI_FEATURES
/* Tell the user about it in lots of lovely detail */
cfi_tell_features ( extp ) ;
2005-11-07 14:15:37 +03:00
# endif
2005-04-17 02:20:36 +04:00
/* Install our own private info structure */
cfi - > cmdset_priv = extp ;
2005-11-07 14:15:37 +03:00
}
2005-04-17 02:20:36 +04:00
for ( i = 0 ; i < cfi - > numchips ; i + + ) {
cfi - > chips [ i ] . word_write_time = 128 ;
cfi - > chips [ i ] . buffer_write_time = 128 ;
cfi - > chips [ i ] . erase_time = 1024 ;
2007-03-06 13:39:44 +03:00
cfi - > chips [ i ] . ref_point_counter = 0 ;
init_waitqueue_head ( & ( cfi - > chips [ i ] . wq ) ) ;
2005-11-07 14:15:37 +03:00
}
2005-04-17 02:20:36 +04:00
return cfi_staa_setup ( map ) ;
}
2006-05-09 01:58:25 +04:00
EXPORT_SYMBOL_GPL ( cfi_cmdset_0020 ) ;
2005-04-17 02:20:36 +04:00
static struct mtd_info * cfi_staa_setup ( struct map_info * map )
{
struct cfi_private * cfi = map - > fldrv_priv ;
struct mtd_info * mtd ;
unsigned long offset = 0 ;
int i , j ;
unsigned long devsize = ( 1 < < cfi - > cfiq - > DevSize ) * cfi - > interleave ;
2006-11-15 22:10:29 +03:00
mtd = kzalloc ( sizeof ( * mtd ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
//printk(KERN_DEBUG "number of CFI chips: %d\n", cfi->numchips);
if ( ! mtd ) {
printk ( KERN_ERR " Failed to allocate memory for MTD device \n " ) ;
kfree ( cfi - > cmdset_priv ) ;
return NULL ;
}
mtd - > priv = map ;
mtd - > type = MTD_NORFLASH ;
mtd - > size = devsize * cfi - > numchips ;
mtd - > numeraseregions = cfi - > cfiq - > NumEraseRegions * cfi - > numchips ;
2005-11-07 14:15:37 +03:00
mtd - > eraseregions = kmalloc ( sizeof ( struct mtd_erase_region_info )
2005-04-17 02:20:36 +04:00
* mtd - > numeraseregions , GFP_KERNEL ) ;
2005-11-07 14:15:37 +03:00
if ( ! mtd - > eraseregions ) {
2005-04-17 02:20:36 +04:00
printk ( KERN_ERR " Failed to allocate memory for MTD erase region info \n " ) ;
kfree ( cfi - > cmdset_priv ) ;
kfree ( mtd ) ;
return NULL ;
}
2005-11-07 14:15:37 +03:00
2005-04-17 02:20:36 +04:00
for ( i = 0 ; i < cfi - > cfiq - > NumEraseRegions ; i + + ) {
unsigned long ernum , ersize ;
ersize = ( ( cfi - > cfiq - > EraseRegionInfo [ i ] > > 8 ) & ~ 0xff ) * cfi - > interleave ;
ernum = ( cfi - > cfiq - > EraseRegionInfo [ i ] & 0xffff ) + 1 ;
if ( mtd - > erasesize < ersize ) {
mtd - > erasesize = ersize ;
}
for ( j = 0 ; j < cfi - > numchips ; j + + ) {
mtd - > eraseregions [ ( j * cfi - > cfiq - > NumEraseRegions ) + i ] . offset = ( j * devsize ) + offset ;
mtd - > eraseregions [ ( j * cfi - > cfiq - > NumEraseRegions ) + i ] . erasesize = ersize ;
mtd - > eraseregions [ ( j * cfi - > cfiq - > NumEraseRegions ) + i ] . numblocks = ernum ;
}
offset + = ( ersize * ernum ) ;
}
if ( offset ! = devsize ) {
/* Argh */
printk ( KERN_WARNING " Sum of regions (%lx) != total size of set of interleaved chips (%lx) \n " , offset , devsize ) ;
kfree ( mtd - > eraseregions ) ;
kfree ( cfi - > cmdset_priv ) ;
kfree ( mtd ) ;
return NULL ;
}
for ( i = 0 ; i < mtd - > numeraseregions ; i + + ) {
2008-12-10 16:37:21 +03:00
printk ( KERN_DEBUG " %d: offset=0x%llx,size=0x%x,blocks=%d \n " ,
i , ( unsigned long long ) mtd - > eraseregions [ i ] . offset ,
2005-04-17 02:20:36 +04:00
mtd - > eraseregions [ i ] . erasesize ,
mtd - > eraseregions [ i ] . numblocks ) ;
}
2005-11-07 14:15:37 +03:00
/* Also select the correct geometry setup too */
2005-04-17 02:20:36 +04:00
mtd - > erase = cfi_staa_erase_varsize ;
mtd - > read = cfi_staa_read ;
mtd - > write = cfi_staa_write_buffers ;
mtd - > writev = cfi_staa_writev ;
mtd - > sync = cfi_staa_sync ;
mtd - > lock = cfi_staa_lock ;
mtd - > unlock = cfi_staa_unlock ;
mtd - > suspend = cfi_staa_suspend ;
mtd - > resume = cfi_staa_resume ;
2006-05-23 01:18:29 +04:00
mtd - > flags = MTD_CAP_NORFLASH & ~ MTD_BIT_WRITEABLE ;
2006-05-23 01:18:12 +04:00
mtd - > writesize = 8 ; /* FIXME: Should be 0 for STMicro flashes w/out ECC */
2011-02-10 18:01:46 +03:00
mtd - > writebufsize = cfi_interleave ( cfi ) < < cfi - > cfiq - > MaxBufWriteSize ;
2005-04-17 02:20:36 +04:00
map - > fldrv = & cfi_staa_chipdrv ;
__module_get ( THIS_MODULE ) ;
mtd - > name = map - > name ;
return mtd ;
}
static inline int do_read_onechip ( struct map_info * map , struct flchip * chip , loff_t adr , size_t len , u_char * buf )
{
map_word status , status_OK ;
unsigned long timeo ;
DECLARE_WAITQUEUE ( wait , current ) ;
int suspended = 0 ;
unsigned long cmd_addr ;
struct cfi_private * cfi = map - > fldrv_priv ;
adr + = chip - > start ;
2005-11-07 14:15:37 +03:00
/* Ensure cmd read/writes are aligned. */
cmd_addr = adr & ~ ( map_bankwidth ( map ) - 1 ) ;
2005-04-17 02:20:36 +04:00
/* Let's determine this according to the interleave only once */
status_OK = CMD ( 0x80 ) ;
timeo = jiffies + HZ ;
retry :
2010-04-19 00:46:44 +04:00
mutex_lock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
/* Check that the chip's ready to talk to us.
* If it ' s in FL_ERASING state , suspend it and make it talk now .
*/
switch ( chip - > state ) {
case FL_ERASING :
if ( ! ( ( ( struct cfi_pri_intelext * ) cfi - > cmdset_priv ) - > FeatureSupport & 2 ) )
goto sleep ; /* We don't support erase suspend */
2005-11-07 14:15:37 +03:00
2005-04-17 02:20:36 +04:00
map_write ( map , CMD ( 0xb0 ) , cmd_addr ) ;
/* If the flash has finished erasing, then 'erase suspend'
* appears to make some ( 28F 320 ) flash devices switch to
* ' read ' mode . Make sure that we switch to ' read status '
* mode so we get the right data . - - rmk
*/
map_write ( map , CMD ( 0x70 ) , cmd_addr ) ;
chip - > oldstate = FL_ERASING ;
chip - > state = FL_ERASE_SUSPENDING ;
// printk("Erase suspending at 0x%lx\n", cmd_addr);
for ( ; ; ) {
status = map_read ( map , cmd_addr ) ;
if ( map_word_andequal ( map , status , status_OK , status_OK ) )
break ;
2005-11-07 14:15:37 +03:00
2005-04-17 02:20:36 +04:00
if ( time_after ( jiffies , timeo ) ) {
/* Urgh */
map_write ( map , CMD ( 0xd0 ) , cmd_addr ) ;
/* make sure we're in 'read status' mode */
map_write ( map , CMD ( 0x70 ) , cmd_addr ) ;
chip - > state = FL_ERASING ;
2010-04-19 00:46:44 +04:00
mutex_unlock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
printk ( KERN_ERR " Chip not ready after erase "
" suspended: status = 0x%lx \n " , status . x [ 0 ] ) ;
return - EIO ;
}
2005-11-07 14:15:37 +03:00
2010-04-19 00:46:44 +04:00
mutex_unlock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
cfi_udelay ( 1 ) ;
2010-04-19 00:46:44 +04:00
mutex_lock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
}
2005-11-07 14:15:37 +03:00
2005-04-17 02:20:36 +04:00
suspended = 1 ;
map_write ( map , CMD ( 0xff ) , cmd_addr ) ;
chip - > state = FL_READY ;
break ;
2005-11-07 14:15:37 +03:00
2005-04-17 02:20:36 +04:00
#if 0
case FL_WRITING :
/* Not quite yet */
# endif
case FL_READY :
break ;
case FL_CFI_QUERY :
case FL_JEDEC_QUERY :
map_write ( map , CMD ( 0x70 ) , cmd_addr ) ;
chip - > state = FL_STATUS ;
case FL_STATUS :
status = map_read ( map , cmd_addr ) ;
if ( map_word_andequal ( map , status , status_OK , status_OK ) ) {
map_write ( map , CMD ( 0xff ) , cmd_addr ) ;
chip - > state = FL_READY ;
break ;
}
2005-11-07 14:15:37 +03:00
2005-04-17 02:20:36 +04:00
/* Urgh. Chip not yet ready to talk to us. */
if ( time_after ( jiffies , timeo ) ) {
2010-04-19 00:46:44 +04:00
mutex_unlock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
printk ( KERN_ERR " waiting for chip to be ready timed out in read. WSM status = %lx \n " , status . x [ 0 ] ) ;
return - EIO ;
}
/* Latency issues. Drop the lock, wait a while and retry */
2010-04-19 00:46:44 +04:00
mutex_unlock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
cfi_udelay ( 1 ) ;
goto retry ;
default :
sleep :
/* Stick ourselves on a wait queue to be woken when
someone changes the status */
set_current_state ( TASK_UNINTERRUPTIBLE ) ;
add_wait_queue ( & chip - > wq , & wait ) ;
2010-04-19 00:46:44 +04:00
mutex_unlock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
schedule ( ) ;
remove_wait_queue ( & chip - > wq , & wait ) ;
timeo = jiffies + HZ ;
goto retry ;
}
map_copy_from ( map , buf , adr , len ) ;
if ( suspended ) {
chip - > state = chip - > oldstate ;
2005-11-07 14:15:37 +03:00
/* What if one interleaved chip has finished and the
2005-04-17 02:20:36 +04:00
other hasn ' t ? The old code would leave the finished
2005-11-07 14:15:37 +03:00
one in READY mode . That ' s bad , and caused - EROFS
2005-04-17 02:20:36 +04:00
errors to be returned from do_erase_oneblock because
that ' s the only bit it checked for at the time .
2005-11-07 14:15:37 +03:00
As the state machine appears to explicitly allow
2005-04-17 02:20:36 +04:00
sending the 0x70 ( Read Status ) command to an erasing
2005-11-07 14:15:37 +03:00
chip and expecting it to be ignored , that ' s what we
2005-04-17 02:20:36 +04:00
do . */
map_write ( map , CMD ( 0xd0 ) , cmd_addr ) ;
2005-11-07 14:15:37 +03:00
map_write ( map , CMD ( 0x70 ) , cmd_addr ) ;
2005-04-17 02:20:36 +04:00
}
wake_up ( & chip - > wq ) ;
2010-04-19 00:46:44 +04:00
mutex_unlock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
static int cfi_staa_read ( struct mtd_info * mtd , loff_t from , size_t len , size_t * retlen , u_char * buf )
{
struct map_info * map = mtd - > priv ;
struct cfi_private * cfi = map - > fldrv_priv ;
unsigned long ofs ;
int chipnum ;
int ret = 0 ;
/* ofs: offset within the first chip that the first read should start */
chipnum = ( from > > cfi - > chipshift ) ;
ofs = from - ( chipnum < < cfi - > chipshift ) ;
* retlen = 0 ;
while ( len ) {
unsigned long thislen ;
if ( chipnum > = cfi - > numchips )
break ;
if ( ( len + ofs - 1 ) > > cfi - > chipshift )
thislen = ( 1 < < cfi - > chipshift ) - ofs ;
else
thislen = len ;
ret = do_read_onechip ( map , & cfi - > chips [ chipnum ] , ofs , thislen , buf ) ;
if ( ret )
break ;
* retlen + = thislen ;
len - = thislen ;
buf + = thislen ;
2005-11-07 14:15:37 +03:00
2005-04-17 02:20:36 +04:00
ofs = 0 ;
chipnum + + ;
}
return ret ;
}
2005-11-07 14:15:37 +03:00
static inline int do_write_buffer ( struct map_info * map , struct flchip * chip ,
2005-04-17 02:20:36 +04:00
unsigned long adr , const u_char * buf , int len )
{
struct cfi_private * cfi = map - > fldrv_priv ;
map_word status , status_OK ;
unsigned long cmd_adr , timeo ;
DECLARE_WAITQUEUE ( wait , current ) ;
int wbufsize , z ;
2005-11-07 14:15:37 +03:00
2005-04-17 02:20:36 +04:00
/* M58LW064A requires bus alignment for buffer wriets -- saw */
if ( adr & ( map_bankwidth ( map ) - 1 ) )
return - EINVAL ;
wbufsize = cfi_interleave ( cfi ) < < cfi - > cfiq - > MaxBufWriteSize ;
adr + = chip - > start ;
cmd_adr = adr & ~ ( wbufsize - 1 ) ;
2005-11-07 14:15:37 +03:00
2005-04-17 02:20:36 +04:00
/* Let's determine this according to the interleave only once */
status_OK = CMD ( 0x80 ) ;
2005-11-07 14:15:37 +03:00
2005-04-17 02:20:36 +04:00
timeo = jiffies + HZ ;
retry :
# ifdef DEBUG_CFI_FEATURES
2008-04-19 00:44:19 +04:00
printk ( " %s: chip->state[%d] \n " , __func__ , chip - > state ) ;
2005-04-17 02:20:36 +04:00
# endif
2010-04-19 00:46:44 +04:00
mutex_lock ( & chip - > mutex ) ;
2005-11-07 14:15:37 +03:00
2005-04-17 02:20:36 +04:00
/* Check that the chip's ready to talk to us.
* Later , we can actually think about interrupting it
* if it ' s in FL_ERASING state .
* Not just yet , though .
*/
switch ( chip - > state ) {
case FL_READY :
break ;
2005-11-07 14:15:37 +03:00
2005-04-17 02:20:36 +04:00
case FL_CFI_QUERY :
case FL_JEDEC_QUERY :
map_write ( map , CMD ( 0x70 ) , cmd_adr ) ;
chip - > state = FL_STATUS ;
# ifdef DEBUG_CFI_FEATURES
2008-04-19 00:44:19 +04:00
printk ( " %s: 1 status[%x] \n " , __func__ , map_read ( map , cmd_adr ) ) ;
2005-04-17 02:20:36 +04:00
# endif
case FL_STATUS :
status = map_read ( map , cmd_adr ) ;
if ( map_word_andequal ( map , status , status_OK , status_OK ) )
break ;
/* Urgh. Chip not yet ready to talk to us. */
if ( time_after ( jiffies , timeo ) ) {
2010-04-19 00:46:44 +04:00
mutex_unlock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
printk ( KERN_ERR " waiting for chip to be ready timed out in buffer write Xstatus = %lx, status = %lx \n " ,
status . x [ 0 ] , map_read ( map , cmd_adr ) . x [ 0 ] ) ;
return - EIO ;
}
/* Latency issues. Drop the lock, wait a while and retry */
2010-04-19 00:46:44 +04:00
mutex_unlock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
cfi_udelay ( 1 ) ;
goto retry ;
default :
/* Stick ourselves on a wait queue to be woken when
someone changes the status */
set_current_state ( TASK_UNINTERRUPTIBLE ) ;
add_wait_queue ( & chip - > wq , & wait ) ;
2010-04-19 00:46:44 +04:00
mutex_unlock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
schedule ( ) ;
remove_wait_queue ( & chip - > wq , & wait ) ;
timeo = jiffies + HZ ;
goto retry ;
}
ENABLE_VPP ( map ) ;
map_write ( map , CMD ( 0xe8 ) , cmd_adr ) ;
chip - > state = FL_WRITING_TO_BUFFER ;
z = 0 ;
for ( ; ; ) {
status = map_read ( map , cmd_adr ) ;
if ( map_word_andequal ( map , status , status_OK , status_OK ) )
break ;
2010-04-19 00:46:44 +04:00
mutex_unlock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
cfi_udelay ( 1 ) ;
2010-04-19 00:46:44 +04:00
mutex_lock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
if ( + + z > 100 ) {
/* Argh. Not ready for write to buffer */
DISABLE_VPP ( map ) ;
map_write ( map , CMD ( 0x70 ) , cmd_adr ) ;
chip - > state = FL_STATUS ;
2010-04-19 00:46:44 +04:00
mutex_unlock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
printk ( KERN_ERR " Chip not ready for buffer write. Xstatus = %lx \n " , status . x [ 0 ] ) ;
return - EIO ;
}
}
/* Write length of data to come */
map_write ( map , CMD ( len / map_bankwidth ( map ) - 1 ) , cmd_adr ) ;
2005-11-07 14:15:37 +03:00
2005-04-17 02:20:36 +04:00
/* Write data */
for ( z = 0 ; z < len ;
z + = map_bankwidth ( map ) , buf + = map_bankwidth ( map ) ) {
map_word d ;
d = map_word_load ( map , buf ) ;
map_write ( map , d , adr + z ) ;
}
/* GO GO GO */
map_write ( map , CMD ( 0xd0 ) , cmd_adr ) ;
chip - > state = FL_WRITING ;
2010-04-19 00:46:44 +04:00
mutex_unlock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
cfi_udelay ( chip - > buffer_write_time ) ;
2010-04-19 00:46:44 +04:00
mutex_lock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
timeo = jiffies + ( HZ / 2 ) ;
z = 0 ;
for ( ; ; ) {
if ( chip - > state ! = FL_WRITING ) {
/* Someone's suspended the write. Sleep */
set_current_state ( TASK_UNINTERRUPTIBLE ) ;
add_wait_queue ( & chip - > wq , & wait ) ;
2010-04-19 00:46:44 +04:00
mutex_unlock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
schedule ( ) ;
remove_wait_queue ( & chip - > wq , & wait ) ;
timeo = jiffies + ( HZ / 2 ) ; /* FIXME */
2010-04-19 00:46:44 +04:00
mutex_lock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
continue ;
}
status = map_read ( map , cmd_adr ) ;
if ( map_word_andequal ( map , status , status_OK , status_OK ) )
break ;
/* OK Still waiting */
if ( time_after ( jiffies , timeo ) ) {
/* clear status */
map_write ( map , CMD ( 0x50 ) , cmd_adr ) ;
/* put back into read status register mode */
map_write ( map , CMD ( 0x70 ) , adr ) ;
chip - > state = FL_STATUS ;
DISABLE_VPP ( map ) ;
2010-04-19 00:46:44 +04:00
mutex_unlock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
printk ( KERN_ERR " waiting for chip to be ready timed out in bufwrite \n " ) ;
return - EIO ;
}
2005-11-07 14:15:37 +03:00
2005-04-17 02:20:36 +04:00
/* Latency issues. Drop the lock, wait a while and retry */
2010-04-19 00:46:44 +04:00
mutex_unlock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
cfi_udelay ( 1 ) ;
z + + ;
2010-04-19 00:46:44 +04:00
mutex_lock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
}
if ( ! z ) {
chip - > buffer_write_time - - ;
if ( ! chip - > buffer_write_time )
chip - > buffer_write_time + + ;
}
2005-11-07 14:15:37 +03:00
if ( z > 1 )
2005-04-17 02:20:36 +04:00
chip - > buffer_write_time + + ;
2005-11-07 14:15:37 +03:00
2005-04-17 02:20:36 +04:00
/* Done and happy. */
DISABLE_VPP ( map ) ;
chip - > state = FL_STATUS ;
/* check for errors: 'lock bit', 'VPP', 'dead cell'/'unerased cell' or 'incorrect cmd' -- saw */
if ( map_word_bitsset ( map , status , CMD ( 0x3a ) ) ) {
# ifdef DEBUG_CFI_FEATURES
2008-04-19 00:44:19 +04:00
printk ( " %s: 2 status[%lx] \n " , __func__ , status . x [ 0 ] ) ;
2005-04-17 02:20:36 +04:00
# endif
/* clear status */
map_write ( map , CMD ( 0x50 ) , cmd_adr ) ;
/* put back into read status register mode */
map_write ( map , CMD ( 0x70 ) , adr ) ;
wake_up ( & chip - > wq ) ;
2010-04-19 00:46:44 +04:00
mutex_unlock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
return map_word_bitsset ( map , status , CMD ( 0x02 ) ) ? - EROFS : - EIO ;
}
wake_up ( & chip - > wq ) ;
2010-04-19 00:46:44 +04:00
mutex_unlock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2005-11-07 14:15:37 +03:00
static int cfi_staa_write_buffers ( struct mtd_info * mtd , loff_t to ,
2005-04-17 02:20:36 +04:00
size_t len , size_t * retlen , const u_char * buf )
{
struct map_info * map = mtd - > priv ;
struct cfi_private * cfi = map - > fldrv_priv ;
int wbufsize = cfi_interleave ( cfi ) < < cfi - > cfiq - > MaxBufWriteSize ;
int ret = 0 ;
int chipnum ;
unsigned long ofs ;
* retlen = 0 ;
if ( ! len )
return 0 ;
chipnum = to > > cfi - > chipshift ;
ofs = to - ( chipnum < < cfi - > chipshift ) ;
# ifdef DEBUG_CFI_FEATURES
2008-04-19 00:44:19 +04:00
printk ( " %s: map_bankwidth(map)[%x] \n " , __func__ , map_bankwidth ( map ) ) ;
printk ( " %s: chipnum[%x] wbufsize[%x] \n " , __func__ , chipnum , wbufsize ) ;
printk ( " %s: ofs[%x] len[%x] \n " , __func__ , ofs , len ) ;
2005-04-17 02:20:36 +04:00
# endif
2005-11-07 14:15:37 +03:00
2005-04-17 02:20:36 +04:00
/* Write buffer is worth it only if more than one word to write... */
while ( len > 0 ) {
/* We must not cross write block boundaries */
int size = wbufsize - ( ofs & ( wbufsize - 1 ) ) ;
if ( size > len )
size = len ;
2005-11-07 14:15:37 +03:00
ret = do_write_buffer ( map , & cfi - > chips [ chipnum ] ,
2005-04-17 02:20:36 +04:00
ofs , buf , size ) ;
if ( ret )
return ret ;
ofs + = size ;
buf + = size ;
( * retlen ) + = size ;
len - = size ;
if ( ofs > > cfi - > chipshift ) {
2005-11-07 14:15:37 +03:00
chipnum + + ;
2005-04-17 02:20:36 +04:00
ofs = 0 ;
if ( chipnum = = cfi - > numchips )
return 0 ;
}
}
2005-11-07 14:15:37 +03:00
2005-04-17 02:20:36 +04:00
return 0 ;
}
/*
* Writev for ECC - Flashes is a little more complicated . We need to maintain
* a small buffer for this .
* XXX : If the buffer size is not a multiple of 2 , this will break
*/
2007-01-29 13:05:03 +03:00
# define ECCBUF_SIZE (mtd->writesize)
2005-04-17 02:20:36 +04:00
# define ECCBUF_DIV(x) ((x) & ~(ECCBUF_SIZE - 1))
# define ECCBUF_MOD(x) ((x) & (ECCBUF_SIZE - 1))
static int
cfi_staa_writev ( struct mtd_info * mtd , const struct kvec * vecs ,
unsigned long count , loff_t to , size_t * retlen )
{
unsigned long i ;
size_t totlen = 0 , thislen ;
int ret = 0 ;
size_t buflen = 0 ;
static char * buffer ;
if ( ! ECCBUF_SIZE ) {
/* We should fall back to a general writev implementation.
* Until that is written , just break .
*/
return - EIO ;
}
buffer = kmalloc ( ECCBUF_SIZE , GFP_KERNEL ) ;
if ( ! buffer )
return - ENOMEM ;
for ( i = 0 ; i < count ; i + + ) {
size_t elem_len = vecs [ i ] . iov_len ;
void * elem_base = vecs [ i ] . iov_base ;
if ( ! elem_len ) /* FIXME: Might be unnecessary. Check that */
continue ;
if ( buflen ) { /* cut off head */
if ( buflen + elem_len < ECCBUF_SIZE ) { /* just accumulate */
memcpy ( buffer + buflen , elem_base , elem_len ) ;
buflen + = elem_len ;
continue ;
}
memcpy ( buffer + buflen , elem_base , ECCBUF_SIZE - buflen ) ;
ret = mtd - > write ( mtd , to , ECCBUF_SIZE , & thislen , buffer ) ;
totlen + = thislen ;
if ( ret | | thislen ! = ECCBUF_SIZE )
goto write_error ;
elem_len - = thislen - buflen ;
elem_base + = thislen - buflen ;
to + = ECCBUF_SIZE ;
}
if ( ECCBUF_DIV ( elem_len ) ) { /* write clean aligned data */
ret = mtd - > write ( mtd , to , ECCBUF_DIV ( elem_len ) , & thislen , elem_base ) ;
totlen + = thislen ;
if ( ret | | thislen ! = ECCBUF_DIV ( elem_len ) )
goto write_error ;
to + = thislen ;
}
buflen = ECCBUF_MOD ( elem_len ) ; /* cut off tail */
if ( buflen ) {
memset ( buffer , 0xff , ECCBUF_SIZE ) ;
memcpy ( buffer , elem_base + thislen , buflen ) ;
}
}
if ( buflen ) { /* flush last page, even if not full */
/* This is sometimes intended behaviour, really */
ret = mtd - > write ( mtd , to , buflen , & thislen , buffer ) ;
totlen + = thislen ;
if ( ret | | thislen ! = ECCBUF_SIZE )
goto write_error ;
}
write_error :
if ( retlen )
* retlen = totlen ;
2005-07-13 19:45:43 +04:00
kfree ( buffer ) ;
2005-04-17 02:20:36 +04:00
return ret ;
}
static inline int do_erase_oneblock ( struct map_info * map , struct flchip * chip , unsigned long adr )
{
struct cfi_private * cfi = map - > fldrv_priv ;
map_word status , status_OK ;
unsigned long timeo ;
int retries = 3 ;
DECLARE_WAITQUEUE ( wait , current ) ;
int ret = 0 ;
adr + = chip - > start ;
/* Let's determine this according to the interleave only once */
status_OK = CMD ( 0x80 ) ;
timeo = jiffies + HZ ;
retry :
2010-04-19 00:46:44 +04:00
mutex_lock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
/* Check that the chip's ready to talk to us. */
switch ( chip - > state ) {
case FL_CFI_QUERY :
case FL_JEDEC_QUERY :
case FL_READY :
map_write ( map , CMD ( 0x70 ) , adr ) ;
chip - > state = FL_STATUS ;
case FL_STATUS :
status = map_read ( map , adr ) ;
if ( map_word_andequal ( map , status , status_OK , status_OK ) )
break ;
2005-11-07 14:15:37 +03:00
2005-04-17 02:20:36 +04:00
/* Urgh. Chip not yet ready to talk to us. */
if ( time_after ( jiffies , timeo ) ) {
2010-04-19 00:46:44 +04:00
mutex_unlock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
printk ( KERN_ERR " waiting for chip to be ready timed out in erase \n " ) ;
return - EIO ;
}
/* Latency issues. Drop the lock, wait a while and retry */
2010-04-19 00:46:44 +04:00
mutex_unlock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
cfi_udelay ( 1 ) ;
goto retry ;
default :
/* Stick ourselves on a wait queue to be woken when
someone changes the status */
set_current_state ( TASK_UNINTERRUPTIBLE ) ;
add_wait_queue ( & chip - > wq , & wait ) ;
2010-04-19 00:46:44 +04:00
mutex_unlock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
schedule ( ) ;
remove_wait_queue ( & chip - > wq , & wait ) ;
timeo = jiffies + HZ ;
goto retry ;
}
ENABLE_VPP ( map ) ;
/* Clear the status register first */
map_write ( map , CMD ( 0x50 ) , adr ) ;
/* Now erase */
map_write ( map , CMD ( 0x20 ) , adr ) ;
map_write ( map , CMD ( 0xD0 ) , adr ) ;
chip - > state = FL_ERASING ;
2005-11-07 14:15:37 +03:00
2010-04-19 00:46:44 +04:00
mutex_unlock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
msleep ( 1000 ) ;
2010-04-19 00:46:44 +04:00
mutex_lock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
/* FIXME. Use a timer to check this, and return immediately. */
/* Once the state machine's known to be working I'll do that */
timeo = jiffies + ( HZ * 20 ) ;
for ( ; ; ) {
if ( chip - > state ! = FL_ERASING ) {
/* Someone's suspended the erase. Sleep */
set_current_state ( TASK_UNINTERRUPTIBLE ) ;
add_wait_queue ( & chip - > wq , & wait ) ;
2010-04-19 00:46:44 +04:00
mutex_unlock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
schedule ( ) ;
remove_wait_queue ( & chip - > wq , & wait ) ;
timeo = jiffies + ( HZ * 20 ) ; /* FIXME */
2010-04-19 00:46:44 +04:00
mutex_lock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
continue ;
}
status = map_read ( map , adr ) ;
if ( map_word_andequal ( map , status , status_OK , status_OK ) )
break ;
2005-11-07 14:15:37 +03:00
2005-04-17 02:20:36 +04:00
/* OK Still waiting */
if ( time_after ( jiffies , timeo ) ) {
map_write ( map , CMD ( 0x70 ) , adr ) ;
chip - > state = FL_STATUS ;
printk ( KERN_ERR " waiting for erase to complete timed out. Xstatus = %lx, status = %lx. \n " , status . x [ 0 ] , map_read ( map , adr ) . x [ 0 ] ) ;
DISABLE_VPP ( map ) ;
2010-04-19 00:46:44 +04:00
mutex_unlock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
return - EIO ;
}
2005-11-07 14:15:37 +03:00
2005-04-17 02:20:36 +04:00
/* Latency issues. Drop the lock, wait a while and retry */
2010-04-19 00:46:44 +04:00
mutex_unlock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
cfi_udelay ( 1 ) ;
2010-04-19 00:46:44 +04:00
mutex_lock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
}
2005-11-07 14:15:37 +03:00
2005-04-17 02:20:36 +04:00
DISABLE_VPP ( map ) ;
ret = 0 ;
/* We've broken this before. It doesn't hurt to be safe */
map_write ( map , CMD ( 0x70 ) , adr ) ;
chip - > state = FL_STATUS ;
status = map_read ( map , adr ) ;
/* check for lock bit */
if ( map_word_bitsset ( map , status , CMD ( 0x3a ) ) ) {
unsigned char chipstatus = status . x [ 0 ] ;
if ( ! map_word_equal ( map , status , CMD ( chipstatus ) ) ) {
int i , w ;
for ( w = 0 ; w < map_words ( map ) ; w + + ) {
for ( i = 0 ; i < cfi_interleave ( cfi ) ; i + + ) {
chipstatus | = status . x [ w ] > > ( cfi - > device_type * 8 ) ;
}
}
printk ( KERN_WARNING " Status is not identical for all chips: 0x%lx. Merging to give 0x%02x \n " ,
status . x [ 0 ] , chipstatus ) ;
}
/* Reset the error bits */
map_write ( map , CMD ( 0x50 ) , adr ) ;
map_write ( map , CMD ( 0x70 ) , adr ) ;
2005-11-07 14:15:37 +03:00
2005-04-17 02:20:36 +04:00
if ( ( chipstatus & 0x30 ) = = 0x30 ) {
printk ( KERN_NOTICE " Chip reports improper command sequence: status 0x%x \n " , chipstatus ) ;
ret = - EIO ;
} else if ( chipstatus & 0x02 ) {
/* Protection bit set */
ret = - EROFS ;
} else if ( chipstatus & 0x8 ) {
/* Voltage */
printk ( KERN_WARNING " Chip reports voltage low on erase: status 0x%x \n " , chipstatus ) ;
ret = - EIO ;
} else if ( chipstatus & 0x20 ) {
if ( retries - - ) {
printk ( KERN_DEBUG " Chip erase failed at 0x%08lx: status 0x%x. Retrying... \n " , adr , chipstatus ) ;
timeo = jiffies + HZ ;
chip - > state = FL_STATUS ;
2010-04-19 00:46:44 +04:00
mutex_unlock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
goto retry ;
}
printk ( KERN_DEBUG " Chip erase failed at 0x%08lx: status 0x%x \n " , adr , chipstatus ) ;
ret = - EIO ;
}
}
wake_up ( & chip - > wq ) ;
2010-04-19 00:46:44 +04:00
mutex_unlock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
return ret ;
}
2008-04-14 18:20:30 +04:00
static int cfi_staa_erase_varsize ( struct mtd_info * mtd ,
struct erase_info * instr )
2005-04-17 02:20:36 +04:00
{ struct map_info * map = mtd - > priv ;
struct cfi_private * cfi = map - > fldrv_priv ;
unsigned long adr , len ;
int chipnum , ret = 0 ;
int i , first ;
struct mtd_erase_region_info * regions = mtd - > eraseregions ;
if ( instr - > addr > mtd - > size )
return - EINVAL ;
if ( ( instr - > len + instr - > addr ) > mtd - > size )
return - EINVAL ;
/* Check that both start and end of the requested erase are
* aligned with the erasesize at the appropriate addresses .
*/
i = 0 ;
2005-11-07 14:15:37 +03:00
/* Skip all erase regions which are ended before the start of
2005-04-17 02:20:36 +04:00
the requested erase . Actually , to save on the calculations ,
we skip to the first erase region which starts after the
start of the requested erase , and then go back one .
*/
2005-11-07 14:15:37 +03:00
2005-04-17 02:20:36 +04:00
while ( i < mtd - > numeraseregions & & instr - > addr > = regions [ i ] . offset )
i + + ;
i - - ;
2005-11-07 14:15:37 +03:00
/* OK, now i is pointing at the erase region in which this
2005-04-17 02:20:36 +04:00
erase request starts . Check the start of the requested
erase range is aligned with the erase size which is in
effect here .
*/
if ( instr - > addr & ( regions [ i ] . erasesize - 1 ) )
return - EINVAL ;
/* Remember the erase region we start on */
first = i ;
/* Next, check that the end of the requested erase is aligned
* with the erase region at that address .
*/
while ( i < mtd - > numeraseregions & & ( instr - > addr + instr - > len ) > = regions [ i ] . offset )
i + + ;
/* As before, drop back one to point at the region in which
the address actually falls
*/
i - - ;
2005-11-07 14:15:37 +03:00
2005-04-17 02:20:36 +04:00
if ( ( instr - > addr + instr - > len ) & ( regions [ i ] . erasesize - 1 ) )
return - EINVAL ;
chipnum = instr - > addr > > cfi - > chipshift ;
adr = instr - > addr - ( chipnum < < cfi - > chipshift ) ;
len = instr - > len ;
i = first ;
while ( len ) {
ret = do_erase_oneblock ( map , & cfi - > chips [ chipnum ] , adr ) ;
2005-11-07 14:15:37 +03:00
2005-04-17 02:20:36 +04:00
if ( ret )
return ret ;
adr + = regions [ i ] . erasesize ;
len - = regions [ i ] . erasesize ;
2008-12-10 16:37:21 +03:00
if ( adr % ( 1 < < cfi - > chipshift ) = = ( ( ( unsigned long ) regions [ i ] . offset + ( regions [ i ] . erasesize * regions [ i ] . numblocks ) ) % ( 1 < < cfi - > chipshift ) ) )
2005-04-17 02:20:36 +04:00
i + + ;
if ( adr > > cfi - > chipshift ) {
adr = 0 ;
chipnum + + ;
2005-11-07 14:15:37 +03:00
2005-04-17 02:20:36 +04:00
if ( chipnum > = cfi - > numchips )
break ;
}
}
2005-11-07 14:15:37 +03:00
2005-04-17 02:20:36 +04:00
instr - > state = MTD_ERASE_DONE ;
mtd_erase_callback ( instr ) ;
2005-11-07 14:15:37 +03:00
2005-04-17 02:20:36 +04:00
return 0 ;
}
static void cfi_staa_sync ( struct mtd_info * mtd )
{
struct map_info * map = mtd - > priv ;
struct cfi_private * cfi = map - > fldrv_priv ;
int i ;
struct flchip * chip ;
int ret = 0 ;
DECLARE_WAITQUEUE ( wait , current ) ;
for ( i = 0 ; ! ret & & i < cfi - > numchips ; i + + ) {
chip = & cfi - > chips [ i ] ;
retry :
2010-04-19 00:46:44 +04:00
mutex_lock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
switch ( chip - > state ) {
case FL_READY :
case FL_STATUS :
case FL_CFI_QUERY :
case FL_JEDEC_QUERY :
chip - > oldstate = chip - > state ;
chip - > state = FL_SYNCING ;
2005-11-07 14:15:37 +03:00
/* No need to wake_up() on this state change -
2005-04-17 02:20:36 +04:00
* as the whole point is that nobody can do anything
* with the chip now anyway .
*/
case FL_SYNCING :
2010-04-19 00:46:44 +04:00
mutex_unlock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
break ;
default :
/* Not an idle state */
2008-04-09 04:41:59 +04:00
set_current_state ( TASK_UNINTERRUPTIBLE ) ;
2005-04-17 02:20:36 +04:00
add_wait_queue ( & chip - > wq , & wait ) ;
2005-11-07 14:15:37 +03:00
2010-04-19 00:46:44 +04:00
mutex_unlock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
schedule ( ) ;
remove_wait_queue ( & chip - > wq , & wait ) ;
2005-11-07 14:15:37 +03:00
2005-04-17 02:20:36 +04:00
goto retry ;
}
}
/* Unlock the chips again */
for ( i - - ; i > = 0 ; i - - ) {
chip = & cfi - > chips [ i ] ;
2010-04-19 00:46:44 +04:00
mutex_lock ( & chip - > mutex ) ;
2005-11-07 14:15:37 +03:00
2005-04-17 02:20:36 +04:00
if ( chip - > state = = FL_SYNCING ) {
chip - > state = chip - > oldstate ;
wake_up ( & chip - > wq ) ;
}
2010-04-19 00:46:44 +04:00
mutex_unlock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
}
}
static inline int do_lock_oneblock ( struct map_info * map , struct flchip * chip , unsigned long adr )
{
struct cfi_private * cfi = map - > fldrv_priv ;
map_word status , status_OK ;
unsigned long timeo = jiffies + HZ ;
DECLARE_WAITQUEUE ( wait , current ) ;
adr + = chip - > start ;
/* Let's determine this according to the interleave only once */
status_OK = CMD ( 0x80 ) ;
timeo = jiffies + HZ ;
retry :
2010-04-19 00:46:44 +04:00
mutex_lock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
/* Check that the chip's ready to talk to us. */
switch ( chip - > state ) {
case FL_CFI_QUERY :
case FL_JEDEC_QUERY :
case FL_READY :
map_write ( map , CMD ( 0x70 ) , adr ) ;
chip - > state = FL_STATUS ;
case FL_STATUS :
status = map_read ( map , adr ) ;
2005-11-07 14:15:37 +03:00
if ( map_word_andequal ( map , status , status_OK , status_OK ) )
2005-04-17 02:20:36 +04:00
break ;
2005-11-07 14:15:37 +03:00
2005-04-17 02:20:36 +04:00
/* Urgh. Chip not yet ready to talk to us. */
if ( time_after ( jiffies , timeo ) ) {
2010-04-19 00:46:44 +04:00
mutex_unlock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
printk ( KERN_ERR " waiting for chip to be ready timed out in lock \n " ) ;
return - EIO ;
}
/* Latency issues. Drop the lock, wait a while and retry */
2010-04-19 00:46:44 +04:00
mutex_unlock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
cfi_udelay ( 1 ) ;
goto retry ;
default :
/* Stick ourselves on a wait queue to be woken when
someone changes the status */
set_current_state ( TASK_UNINTERRUPTIBLE ) ;
add_wait_queue ( & chip - > wq , & wait ) ;
2010-04-19 00:46:44 +04:00
mutex_unlock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
schedule ( ) ;
remove_wait_queue ( & chip - > wq , & wait ) ;
timeo = jiffies + HZ ;
goto retry ;
}
ENABLE_VPP ( map ) ;
map_write ( map , CMD ( 0x60 ) , adr ) ;
map_write ( map , CMD ( 0x01 ) , adr ) ;
chip - > state = FL_LOCKING ;
2005-11-07 14:15:37 +03:00
2010-04-19 00:46:44 +04:00
mutex_unlock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
msleep ( 1000 ) ;
2010-04-19 00:46:44 +04:00
mutex_lock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
/* FIXME. Use a timer to check this, and return immediately. */
/* Once the state machine's known to be working I'll do that */
timeo = jiffies + ( HZ * 2 ) ;
for ( ; ; ) {
status = map_read ( map , adr ) ;
if ( map_word_andequal ( map , status , status_OK , status_OK ) )
break ;
2005-11-07 14:15:37 +03:00
2005-04-17 02:20:36 +04:00
/* OK Still waiting */
if ( time_after ( jiffies , timeo ) ) {
map_write ( map , CMD ( 0x70 ) , adr ) ;
chip - > state = FL_STATUS ;
printk ( KERN_ERR " waiting for lock to complete timed out. Xstatus = %lx, status = %lx. \n " , status . x [ 0 ] , map_read ( map , adr ) . x [ 0 ] ) ;
DISABLE_VPP ( map ) ;
2010-04-19 00:46:44 +04:00
mutex_unlock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
return - EIO ;
}
2005-11-07 14:15:37 +03:00
2005-04-17 02:20:36 +04:00
/* Latency issues. Drop the lock, wait a while and retry */
2010-04-19 00:46:44 +04:00
mutex_unlock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
cfi_udelay ( 1 ) ;
2010-04-19 00:46:44 +04:00
mutex_lock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
}
2005-11-07 14:15:37 +03:00
2005-04-17 02:20:36 +04:00
/* Done and happy. */
chip - > state = FL_STATUS ;
DISABLE_VPP ( map ) ;
wake_up ( & chip - > wq ) ;
2010-04-19 00:46:44 +04:00
mutex_unlock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2008-12-10 16:37:21 +03:00
static int cfi_staa_lock ( struct mtd_info * mtd , loff_t ofs , uint64_t len )
2005-04-17 02:20:36 +04:00
{
struct map_info * map = mtd - > priv ;
struct cfi_private * cfi = map - > fldrv_priv ;
unsigned long adr ;
int chipnum , ret = 0 ;
# ifdef DEBUG_LOCK_BITS
int ofs_factor = cfi - > interleave * cfi - > device_type ;
# endif
if ( ofs & ( mtd - > erasesize - 1 ) )
return - EINVAL ;
if ( len & ( mtd - > erasesize - 1 ) )
return - EINVAL ;
if ( ( len + ofs ) > mtd - > size )
return - EINVAL ;
chipnum = ofs > > cfi - > chipshift ;
adr = ofs - ( chipnum < < cfi - > chipshift ) ;
while ( len ) {
# ifdef DEBUG_LOCK_BITS
cfi_send_gen_cmd ( 0x90 , 0x55 , 0 , map , cfi , cfi - > device_type , NULL ) ;
printk ( " before lock: block status register is %x \n " , cfi_read_query ( map , adr + ( 2 * ofs_factor ) ) ) ;
cfi_send_gen_cmd ( 0xff , 0x55 , 0 , map , cfi , cfi - > device_type , NULL ) ;
# endif
ret = do_lock_oneblock ( map , & cfi - > chips [ chipnum ] , adr ) ;
# ifdef DEBUG_LOCK_BITS
cfi_send_gen_cmd ( 0x90 , 0x55 , 0 , map , cfi , cfi - > device_type , NULL ) ;
printk ( " after lock: block status register is %x \n " , cfi_read_query ( map , adr + ( 2 * ofs_factor ) ) ) ;
cfi_send_gen_cmd ( 0xff , 0x55 , 0 , map , cfi , cfi - > device_type , NULL ) ;
2005-11-07 14:15:37 +03:00
# endif
2005-04-17 02:20:36 +04:00
if ( ret )
return ret ;
adr + = mtd - > erasesize ;
len - = mtd - > erasesize ;
if ( adr > > cfi - > chipshift ) {
adr = 0 ;
chipnum + + ;
2005-11-07 14:15:37 +03:00
2005-04-17 02:20:36 +04:00
if ( chipnum > = cfi - > numchips )
break ;
}
}
return 0 ;
}
static inline int do_unlock_oneblock ( struct map_info * map , struct flchip * chip , unsigned long adr )
{
struct cfi_private * cfi = map - > fldrv_priv ;
map_word status , status_OK ;
unsigned long timeo = jiffies + HZ ;
DECLARE_WAITQUEUE ( wait , current ) ;
adr + = chip - > start ;
/* Let's determine this according to the interleave only once */
status_OK = CMD ( 0x80 ) ;
timeo = jiffies + HZ ;
retry :
2010-04-19 00:46:44 +04:00
mutex_lock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
/* Check that the chip's ready to talk to us. */
switch ( chip - > state ) {
case FL_CFI_QUERY :
case FL_JEDEC_QUERY :
case FL_READY :
map_write ( map , CMD ( 0x70 ) , adr ) ;
chip - > state = FL_STATUS ;
case FL_STATUS :
status = map_read ( map , adr ) ;
if ( map_word_andequal ( map , status , status_OK , status_OK ) )
break ;
2005-11-07 14:15:37 +03:00
2005-04-17 02:20:36 +04:00
/* Urgh. Chip not yet ready to talk to us. */
if ( time_after ( jiffies , timeo ) ) {
2010-04-19 00:46:44 +04:00
mutex_unlock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
printk ( KERN_ERR " waiting for chip to be ready timed out in unlock \n " ) ;
return - EIO ;
}
/* Latency issues. Drop the lock, wait a while and retry */
2010-04-19 00:46:44 +04:00
mutex_unlock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
cfi_udelay ( 1 ) ;
goto retry ;
default :
/* Stick ourselves on a wait queue to be woken when
someone changes the status */
set_current_state ( TASK_UNINTERRUPTIBLE ) ;
add_wait_queue ( & chip - > wq , & wait ) ;
2010-04-19 00:46:44 +04:00
mutex_unlock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
schedule ( ) ;
remove_wait_queue ( & chip - > wq , & wait ) ;
timeo = jiffies + HZ ;
goto retry ;
}
ENABLE_VPP ( map ) ;
map_write ( map , CMD ( 0x60 ) , adr ) ;
map_write ( map , CMD ( 0xD0 ) , adr ) ;
chip - > state = FL_UNLOCKING ;
2005-11-07 14:15:37 +03:00
2010-04-19 00:46:44 +04:00
mutex_unlock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
msleep ( 1000 ) ;
2010-04-19 00:46:44 +04:00
mutex_lock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
/* FIXME. Use a timer to check this, and return immediately. */
/* Once the state machine's known to be working I'll do that */
timeo = jiffies + ( HZ * 2 ) ;
for ( ; ; ) {
status = map_read ( map , adr ) ;
if ( map_word_andequal ( map , status , status_OK , status_OK ) )
break ;
2005-11-07 14:15:37 +03:00
2005-04-17 02:20:36 +04:00
/* OK Still waiting */
if ( time_after ( jiffies , timeo ) ) {
map_write ( map , CMD ( 0x70 ) , adr ) ;
chip - > state = FL_STATUS ;
printk ( KERN_ERR " waiting for unlock to complete timed out. Xstatus = %lx, status = %lx. \n " , status . x [ 0 ] , map_read ( map , adr ) . x [ 0 ] ) ;
DISABLE_VPP ( map ) ;
2010-04-19 00:46:44 +04:00
mutex_unlock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
return - EIO ;
}
2005-11-07 14:15:37 +03:00
2005-04-17 02:20:36 +04:00
/* Latency issues. Drop the unlock, wait a while and retry */
2010-04-19 00:46:44 +04:00
mutex_unlock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
cfi_udelay ( 1 ) ;
2010-04-19 00:46:44 +04:00
mutex_lock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
}
2005-11-07 14:15:37 +03:00
2005-04-17 02:20:36 +04:00
/* Done and happy. */
chip - > state = FL_STATUS ;
DISABLE_VPP ( map ) ;
wake_up ( & chip - > wq ) ;
2010-04-19 00:46:44 +04:00
mutex_unlock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2008-12-10 16:37:21 +03:00
static int cfi_staa_unlock ( struct mtd_info * mtd , loff_t ofs , uint64_t len )
2005-04-17 02:20:36 +04:00
{
struct map_info * map = mtd - > priv ;
struct cfi_private * cfi = map - > fldrv_priv ;
unsigned long adr ;
int chipnum , ret = 0 ;
# ifdef DEBUG_LOCK_BITS
int ofs_factor = cfi - > interleave * cfi - > device_type ;
# endif
chipnum = ofs > > cfi - > chipshift ;
adr = ofs - ( chipnum < < cfi - > chipshift ) ;
# ifdef DEBUG_LOCK_BITS
{
unsigned long temp_adr = adr ;
unsigned long temp_len = len ;
2005-11-07 14:15:37 +03:00
2005-04-17 02:20:36 +04:00
cfi_send_gen_cmd ( 0x90 , 0x55 , 0 , map , cfi , cfi - > device_type , NULL ) ;
while ( temp_len ) {
printk ( " before unlock %x: block status register is %x \n " , temp_adr , cfi_read_query ( map , temp_adr + ( 2 * ofs_factor ) ) ) ;
temp_adr + = mtd - > erasesize ;
temp_len - = mtd - > erasesize ;
}
cfi_send_gen_cmd ( 0xff , 0x55 , 0 , map , cfi , cfi - > device_type , NULL ) ;
}
# endif
ret = do_unlock_oneblock ( map , & cfi - > chips [ chipnum ] , adr ) ;
# ifdef DEBUG_LOCK_BITS
cfi_send_gen_cmd ( 0x90 , 0x55 , 0 , map , cfi , cfi - > device_type , NULL ) ;
printk ( " after unlock: block status register is %x \n " , cfi_read_query ( map , adr + ( 2 * ofs_factor ) ) ) ;
cfi_send_gen_cmd ( 0xff , 0x55 , 0 , map , cfi , cfi - > device_type , NULL ) ;
# endif
2005-11-07 14:15:37 +03:00
2005-04-17 02:20:36 +04:00
return ret ;
}
static int cfi_staa_suspend ( struct mtd_info * mtd )
{
struct map_info * map = mtd - > priv ;
struct cfi_private * cfi = map - > fldrv_priv ;
int i ;
struct flchip * chip ;
int ret = 0 ;
for ( i = 0 ; ! ret & & i < cfi - > numchips ; i + + ) {
chip = & cfi - > chips [ i ] ;
2010-04-19 00:46:44 +04:00
mutex_lock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
switch ( chip - > state ) {
case FL_READY :
case FL_STATUS :
case FL_CFI_QUERY :
case FL_JEDEC_QUERY :
chip - > oldstate = chip - > state ;
chip - > state = FL_PM_SUSPENDED ;
2005-11-07 14:15:37 +03:00
/* No need to wake_up() on this state change -
2005-04-17 02:20:36 +04:00
* as the whole point is that nobody can do anything
* with the chip now anyway .
*/
case FL_PM_SUSPENDED :
break ;
default :
ret = - EAGAIN ;
break ;
}
2010-04-19 00:46:44 +04:00
mutex_unlock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
}
/* Unlock the chips again */
if ( ret ) {
for ( i - - ; i > = 0 ; i - - ) {
chip = & cfi - > chips [ i ] ;
2005-11-07 14:15:37 +03:00
2010-04-19 00:46:44 +04:00
mutex_lock ( & chip - > mutex ) ;
2005-11-07 14:15:37 +03:00
2005-04-17 02:20:36 +04:00
if ( chip - > state = = FL_PM_SUSPENDED ) {
/* No need to force it into a known state here,
because we ' re returning failure , and it didn ' t
get power cycled */
chip - > state = chip - > oldstate ;
wake_up ( & chip - > wq ) ;
}
2010-04-19 00:46:44 +04:00
mutex_unlock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
}
2005-11-07 14:15:37 +03:00
}
2005-04-17 02:20:36 +04:00
return ret ;
}
static void cfi_staa_resume ( struct mtd_info * mtd )
{
struct map_info * map = mtd - > priv ;
struct cfi_private * cfi = map - > fldrv_priv ;
int i ;
struct flchip * chip ;
for ( i = 0 ; i < cfi - > numchips ; i + + ) {
2005-11-07 14:15:37 +03:00
2005-04-17 02:20:36 +04:00
chip = & cfi - > chips [ i ] ;
2010-04-19 00:46:44 +04:00
mutex_lock ( & chip - > mutex ) ;
2005-11-07 14:15:37 +03:00
2005-04-17 02:20:36 +04:00
/* Go to known state. Chip may have been power cycled */
if ( chip - > state = = FL_PM_SUSPENDED ) {
map_write ( map , CMD ( 0xFF ) , 0 ) ;
chip - > state = FL_READY ;
wake_up ( & chip - > wq ) ;
}
2010-04-19 00:46:44 +04:00
mutex_unlock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
}
}
static void cfi_staa_destroy ( struct mtd_info * mtd )
{
struct map_info * map = mtd - > priv ;
struct cfi_private * cfi = map - > fldrv_priv ;
kfree ( cfi - > cmdset_priv ) ;
kfree ( cfi ) ;
}
MODULE_LICENSE ( " GPL " ) ;