2005-04-17 02:20:36 +04:00
/*
* Common Flash Interface support :
* AMD & Fujitsu Standard Vendor Command Set ( ID 0x0002 )
*
* Copyright ( C ) 2000 Crossnet Co . < info @ crossnet . co . jp >
* Copyright ( C ) 2004 Arcom Control Systems Ltd < linux @ arcom . com >
2005-06-07 03:04:39 +04:00
* Copyright ( C ) 2005 MontaVista Software Inc . < source @ mvista . com >
2005-04-17 02:20:36 +04:00
*
* 2 _by_8 routines added by Simon Munton
*
* 4 _by_16 work by Carolyn J . Smith
*
2005-11-07 14:15:37 +03:00
* XIP support hooks by Vitaly Wool ( based on code for Intel flash
2005-06-07 03:04:39 +04:00
* by Nicolas Pitre )
2005-11-07 14:15:37 +03:00
*
2008-10-17 07:32:22 +04:00
* 25 / 09 / 2008 Christopher Moore : TopBottom fixup for many Macronix with CFI V1 .0
*
2005-04-17 02:20:36 +04:00
* Occasionally maintained by Thayne Harbaugh tharbaugh at lnxi dot com
*
* This code is GPL
*/
# 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>
2010-04-29 21:26:56 +04:00
# include <linux/reboot.h>
2005-04-17 02:20:36 +04:00
# include <linux/mtd/map.h>
# include <linux/mtd/mtd.h>
# include <linux/mtd/cfi.h>
2005-06-07 03:04:39 +04:00
# include <linux/mtd/xip.h>
2005-04-17 02:20:36 +04:00
# define AMD_BOOTLOC_BUG
# define FORCE_WORD_WRITE 0
# define MAX_WORD_RETRIES 3
# define SST49LF004B 0x0060
2006-10-21 01:41:03 +04:00
# define SST49LF040B 0x0050
2005-05-20 07:28:26 +04:00
# define SST49LF008A 0x005a
2006-08-09 13:06:07 +04:00
# define AT49BV6416 0x00d6
2005-04-17 02:20:36 +04:00
static int cfi_amdstd_read ( struct mtd_info * , loff_t , size_t , size_t * , u_char * ) ;
static int cfi_amdstd_write_words ( struct mtd_info * , loff_t , size_t , size_t * , const u_char * ) ;
static int cfi_amdstd_write_buffers ( struct mtd_info * , loff_t , size_t , size_t * , const u_char * ) ;
static int cfi_amdstd_erase_chip ( struct mtd_info * , struct erase_info * ) ;
static int cfi_amdstd_erase_varsize ( struct mtd_info * , struct erase_info * ) ;
static void cfi_amdstd_sync ( struct mtd_info * ) ;
static int cfi_amdstd_suspend ( struct mtd_info * ) ;
static void cfi_amdstd_resume ( struct mtd_info * ) ;
2010-04-29 21:26:56 +04:00
static int cfi_amdstd_reboot ( struct notifier_block * , unsigned long , void * ) ;
2005-04-17 02:20:36 +04:00
static int cfi_amdstd_secsi_read ( struct mtd_info * , loff_t , size_t , size_t * , u_char * ) ;
2012-01-06 23:29:19 +04:00
static int cfi_amdstd_panic_write ( struct mtd_info * mtd , loff_t to , size_t len ,
size_t * retlen , const u_char * buf ) ;
2005-04-17 02:20:36 +04:00
static void cfi_amdstd_destroy ( struct mtd_info * ) ;
struct mtd_info * cfi_cmdset_0002 ( struct map_info * , int ) ;
static struct mtd_info * cfi_amdstd_setup ( struct mtd_info * ) ;
static int get_chip ( struct map_info * map , struct flchip * chip , unsigned long adr , int mode ) ;
static void put_chip ( struct map_info * map , struct flchip * chip , unsigned long adr ) ;
# include "fwh_lock.h"
2008-12-10 16:37:21 +03:00
static int cfi_atmel_lock ( struct mtd_info * mtd , loff_t ofs , uint64_t len ) ;
static int cfi_atmel_unlock ( struct mtd_info * mtd , loff_t ofs , uint64_t len ) ;
2006-08-09 13:06:07 +04:00
2005-04-17 02:20:36 +04:00
static struct mtd_chip_driver cfi_amdstd_chipdrv = {
. probe = NULL , /* Not usable directly */
. destroy = cfi_amdstd_destroy ,
. name = " cfi_cmdset_0002 " ,
. module = THIS_MODULE
} ;
/* #define DEBUG_CFI_FEATURES */
# ifdef DEBUG_CFI_FEATURES
static void cfi_tell_features ( struct cfi_pri_amdstd * extp )
{
const char * erase_suspend [ 3 ] = {
" Not supported " , " Read only " , " Read/write "
} ;
const char * top_bottom [ 6 ] = {
" No WP " , " 8x8KiB sectors at top & bottom, no WP " ,
" Bottom boot " , " Top boot " ,
" Uniform, Bottom WP " , " Uniform, Top WP "
} ;
printk ( " Silicon revision: %d \n " , extp - > SiliconRevision > > 1 ) ;
2005-11-07 14:15:37 +03:00
printk ( " Address sensitive unlock: %s \n " ,
2005-04-17 02:20:36 +04:00
( extp - > SiliconRevision & 1 ) ? " Not required " : " Required " ) ;
if ( extp - > EraseSuspend < ARRAY_SIZE ( erase_suspend ) )
printk ( " Erase Suspend: %s \n " , erase_suspend [ extp - > EraseSuspend ] ) ;
else
printk ( " Erase Suspend: Unknown value %d \n " , extp - > EraseSuspend ) ;
if ( extp - > BlkProt = = 0 )
printk ( " Block protection: Not supported \n " ) ;
else
printk ( " Block protection: %d sectors per group \n " , extp - > BlkProt ) ;
printk ( " Temporary block unprotect: %s \n " ,
extp - > TmpBlkUnprotect ? " Supported " : " Not supported " ) ;
printk ( " Block protect/unprotect scheme: %d \n " , extp - > BlkProtUnprot ) ;
printk ( " Number of simultaneous operations: %d \n " , extp - > SimultaneousOps ) ;
printk ( " Burst mode: %s \n " ,
extp - > BurstMode ? " Supported " : " Not supported " ) ;
if ( extp - > PageMode = = 0 )
printk ( " Page mode: Not supported \n " ) ;
else
printk ( " Page mode: %d word page \n " , extp - > PageMode < < 2 ) ;
2005-11-07 14:15:37 +03:00
printk ( " Vpp Supply Minimum Program/Erase Voltage: %d.%d V \n " ,
2005-04-17 02:20:36 +04:00
extp - > VppMin > > 4 , extp - > VppMin & 0xf ) ;
2005-11-07 14:15:37 +03:00
printk ( " Vpp Supply Maximum Program/Erase Voltage: %d.%d V \n " ,
2005-04-17 02:20:36 +04:00
extp - > VppMax > > 4 , extp - > VppMax & 0xf ) ;
if ( extp - > TopBottom < ARRAY_SIZE ( top_bottom ) )
printk ( " Top/Bottom Boot Block: %s \n " , top_bottom [ extp - > TopBottom ] ) ;
else
printk ( " Top/Bottom Boot Block: Unknown value %d \n " , extp - > TopBottom ) ;
}
# endif
# ifdef AMD_BOOTLOC_BUG
/* Wheee. Bring me the head of someone at AMD. */
2010-11-17 14:35:50 +03:00
static void fixup_amd_bootblock ( struct mtd_info * mtd )
2005-04-17 02:20:36 +04:00
{
struct map_info * map = mtd - > priv ;
struct cfi_private * cfi = map - > fldrv_priv ;
struct cfi_pri_amdstd * extp = cfi - > cmdset_priv ;
__u8 major = extp - > MajorVersion ;
__u8 minor = extp - > MinorVersion ;
if ( ( ( major < < 8 ) | minor ) < 0x3131 ) {
/* CFI version 1.0 => don't trust bootloc */
2008-10-17 07:32:22 +04:00
2011-07-19 21:06:09 +04:00
pr_debug ( " %s: JEDEC Vendor ID is 0x%02X Device ID is 0x%02X \n " ,
2008-10-17 07:32:22 +04:00
map - > name , cfi - > mfr , cfi - > id ) ;
/* AFAICS all 29LV400 with a bottom boot block have a device ID
* of 0x22BA in 16 - bit mode and 0xBA in 8 - bit mode .
* These were badly detected as they have the 0x80 bit set
* so treat them as a special case .
*/
if ( ( ( cfi - > id = = 0xBA ) | | ( cfi - > id = = 0x22BA ) ) & &
/* Macronix added CFI to their 2nd generation
* MX29LV400C B / T but AFAICS no other 29L V400 ( AMD ,
* Fujitsu , Spansion , EON , ESI and older Macronix )
* has CFI .
*
* Therefore also check the manufacturer .
* This reduces the risk of false detection due to
* the 8 - bit device ID .
*/
2009-12-16 01:01:06 +03:00
( cfi - > mfr = = CFI_MFR_MACRONIX ) ) {
2011-07-19 21:06:09 +04:00
pr_debug ( " %s: Macronix MX29LV400C with bottom boot block "
2008-10-17 07:32:22 +04:00
" detected \n " , map - > name ) ;
extp - > TopBottom = 2 ; /* bottom boot */
} else
2005-04-17 02:20:36 +04:00
if ( cfi - > id & 0x80 ) {
printk ( KERN_WARNING " %s: JEDEC Device ID is 0x%02X. Assuming broken CFI table. \n " , map - > name , cfi - > id ) ;
extp - > TopBottom = 3 ; /* top boot */
} else {
extp - > TopBottom = 2 ; /* bottom boot */
}
2008-10-17 07:32:22 +04:00
2011-07-19 21:06:09 +04:00
pr_debug ( " %s: AMD CFI PRI V%c.%c has no boot block field; "
2008-10-17 07:32:22 +04:00
" deduced %s from Device ID \n " , map - > name , major , minor ,
extp - > TopBottom = = 2 ? " bottom " : " top " ) ;
2005-04-17 02:20:36 +04:00
}
}
# endif
2010-11-17 14:35:50 +03:00
static void fixup_use_write_buffers ( struct mtd_info * mtd )
2005-04-17 02:20:36 +04:00
{
struct map_info * map = mtd - > priv ;
struct cfi_private * cfi = map - > fldrv_priv ;
if ( cfi - > cfiq - > BufWriteTimeoutTyp ) {
2011-07-19 21:06:09 +04:00
pr_debug ( " Using buffer write method \n " ) ;
2012-01-30 16:58:32 +04:00
mtd - > _write = cfi_amdstd_write_buffers ;
2005-04-17 02:20:36 +04:00
}
}
2006-08-09 12:54:44 +04:00
/* Atmel chips don't use the same PRI format as AMD chips */
2010-11-17 14:35:50 +03:00
static void fixup_convert_atmel_pri ( struct mtd_info * mtd )
2006-08-09 12:54:44 +04:00
{
struct map_info * map = mtd - > priv ;
struct cfi_private * cfi = map - > fldrv_priv ;
struct cfi_pri_amdstd * extp = cfi - > cmdset_priv ;
struct cfi_pri_atmel atmel_pri ;
memcpy ( & atmel_pri , extp , sizeof ( atmel_pri ) ) ;
2006-09-15 19:19:31 +04:00
memset ( ( char * ) extp + 5 , 0 , sizeof ( * extp ) - 5 ) ;
2006-08-09 12:54:44 +04:00
if ( atmel_pri . Features & 0x02 )
extp - > EraseSuspend = 2 ;
2008-09-30 15:55:33 +04:00
/* Some chips got it backwards... */
if ( cfi - > id = = AT49BV6416 ) {
if ( atmel_pri . BottomBoot )
extp - > TopBottom = 3 ;
else
extp - > TopBottom = 2 ;
} else {
if ( atmel_pri . BottomBoot )
extp - > TopBottom = 2 ;
else
extp - > TopBottom = 3 ;
}
2007-10-30 18:33:07 +03:00
/* burst write mode not supported */
cfi - > cfiq - > BufWriteTimeoutTyp = 0 ;
cfi - > cfiq - > BufWriteTimeoutMax = 0 ;
2006-08-09 12:54:44 +04:00
}
2010-11-17 14:35:50 +03:00
static void fixup_use_secsi ( struct mtd_info * mtd )
2005-04-17 02:20:36 +04:00
{
/* Setup for chips with a secsi area */
2012-01-30 16:58:32 +04:00
mtd - > _read_user_prot_reg = cfi_amdstd_secsi_read ;
mtd - > _read_fact_prot_reg = cfi_amdstd_secsi_read ;
2005-04-17 02:20:36 +04:00
}
2010-11-17 14:35:50 +03:00
static void fixup_use_erase_chip ( struct mtd_info * mtd )
2005-04-17 02:20:36 +04:00
{
struct map_info * map = mtd - > priv ;
struct cfi_private * cfi = map - > fldrv_priv ;
if ( ( cfi - > cfiq - > NumEraseRegions = = 1 ) & &
( ( cfi - > cfiq - > EraseRegionInfo [ 0 ] & 0xffff ) = = 0 ) ) {
2012-01-30 16:58:32 +04:00
mtd - > _erase = cfi_amdstd_erase_chip ;
2005-04-17 02:20:36 +04:00
}
2005-11-07 14:15:37 +03:00
2005-04-17 02:20:36 +04:00
}
2006-08-09 13:06:07 +04:00
/*
* Some Atmel chips ( e . g . the AT49BV6416 ) power - up with all sectors
* locked by default .
*/
2010-11-17 14:35:50 +03:00
static void fixup_use_atmel_lock ( struct mtd_info * mtd )
2006-08-09 13:06:07 +04:00
{
2012-01-30 16:58:32 +04:00
mtd - > _lock = cfi_atmel_lock ;
mtd - > _unlock = cfi_atmel_unlock ;
2008-01-30 21:25:49 +03:00
mtd - > flags | = MTD_POWERUP_LOCK ;
2006-08-09 13:06:07 +04:00
}
2010-04-24 19:58:22 +04:00
static void fixup_old_sst_eraseregion ( struct mtd_info * mtd )
{
struct map_info * map = mtd - > priv ;
struct cfi_private * cfi = map - > fldrv_priv ;
/*
2011-03-31 05:57:33 +04:00
* These flashes report two separate eraseblock regions based on the
2010-04-24 19:58:22 +04:00
* sector_erase - size and block_erase - size , although they both operate on the
* same memory . This is not allowed according to CFI , so we just pick the
* sector_erase - size .
*/
cfi - > cfiq - > NumEraseRegions = 1 ;
}
2010-11-17 14:35:50 +03:00
static void fixup_sst39vf ( struct mtd_info * mtd )
2010-04-24 19:58:22 +04:00
{
struct map_info * map = mtd - > priv ;
struct cfi_private * cfi = map - > fldrv_priv ;
fixup_old_sst_eraseregion ( mtd ) ;
cfi - > addr_unlock1 = 0x5555 ;
cfi - > addr_unlock2 = 0x2AAA ;
}
2010-11-17 14:35:50 +03:00
static void fixup_sst39vf_rev_b ( struct mtd_info * mtd )
2010-04-24 19:58:27 +04:00
{
struct map_info * map = mtd - > priv ;
struct cfi_private * cfi = map - > fldrv_priv ;
fixup_old_sst_eraseregion ( mtd ) ;
cfi - > addr_unlock1 = 0x555 ;
cfi - > addr_unlock2 = 0x2AA ;
2010-10-26 13:45:23 +04:00
cfi - > sector_erase_cmd = CMD ( 0x50 ) ;
2010-04-24 19:58:27 +04:00
}
2010-11-17 14:35:50 +03:00
static void fixup_sst38vf640x_sectorsize ( struct mtd_info * mtd )
2010-10-26 14:31:55 +04:00
{
struct map_info * map = mtd - > priv ;
struct cfi_private * cfi = map - > fldrv_priv ;
2010-11-17 14:35:50 +03:00
fixup_sst39vf_rev_b ( mtd ) ;
2010-10-26 14:31:55 +04:00
/*
* CFI reports 1024 sectors ( 0x03ff + 1 ) of 64 KBytes ( 0x0100 * 256 ) where
* it should report a size of 8 KBytes ( 0x0020 * 256 ) .
*/
cfi - > cfiq - > EraseRegionInfo [ 0 ] = 0x002003ff ;
pr_warning ( " %s: Bad 38VF640x CFI data; adjusting sector size from 64 to 8KiB \n " , mtd - > name ) ;
}
2010-11-17 14:35:50 +03:00
static void fixup_s29gl064n_sectors ( struct mtd_info * mtd )
2008-03-31 08:19:30 +04:00
{
struct map_info * map = mtd - > priv ;
struct cfi_private * cfi = map - > fldrv_priv ;
if ( ( cfi - > cfiq - > EraseRegionInfo [ 0 ] & 0xffff ) = = 0x003f ) {
cfi - > cfiq - > EraseRegionInfo [ 0 ] | = 0x0040 ;
2012-05-14 08:34:24 +04:00
pr_warning ( " %s: Bad S29GL064N CFI data; adjust from 64 to 128 sectors \n " , mtd - > name ) ;
2008-03-31 08:19:30 +04:00
}
}
2010-11-17 14:35:50 +03:00
static void fixup_s29gl032n_sectors ( struct mtd_info * mtd )
2008-03-31 08:19:30 +04:00
{
struct map_info * map = mtd - > priv ;
struct cfi_private * cfi = map - > fldrv_priv ;
if ( ( cfi - > cfiq - > EraseRegionInfo [ 1 ] & 0xffff ) = = 0x007e ) {
cfi - > cfiq - > EraseRegionInfo [ 1 ] & = ~ 0x0040 ;
2012-05-14 08:34:24 +04:00
pr_warning ( " %s: Bad S29GL032N CFI data; adjust from 127 to 63 sectors \n " , mtd - > name ) ;
2008-03-31 08:19:30 +04:00
}
}
2012-05-11 14:15:41 +04:00
static void fixup_s29ns512p_sectors ( struct mtd_info * mtd )
{
struct map_info * map = mtd - > priv ;
struct cfi_private * cfi = map - > fldrv_priv ;
/*
* S29NS512P flash uses more than 8 bits to report number of sectors ,
* which is not permitted by CFI .
*/
cfi - > cfiq - > EraseRegionInfo [ 0 ] = 0x020001ff ;
2012-05-14 08:34:24 +04:00
pr_warning ( " %s: Bad S29NS512P CFI data; adjust to 512 sectors \n " , mtd - > name ) ;
2012-05-11 14:15:41 +04:00
}
2010-04-24 19:58:22 +04:00
/* Used to fix CFI-Tables of chips without Extended Query Tables */
static struct cfi_fixup cfi_nopri_fixup_table [ ] = {
2010-11-17 14:35:50 +03:00
{ CFI_MFR_SST , 0x234a , fixup_sst39vf } , /* SST39VF1602 */
{ CFI_MFR_SST , 0x234b , fixup_sst39vf } , /* SST39VF1601 */
{ CFI_MFR_SST , 0x235a , fixup_sst39vf } , /* SST39VF3202 */
{ CFI_MFR_SST , 0x235b , fixup_sst39vf } , /* SST39VF3201 */
{ CFI_MFR_SST , 0x235c , fixup_sst39vf_rev_b } , /* SST39VF3202B */
{ CFI_MFR_SST , 0x235d , fixup_sst39vf_rev_b } , /* SST39VF3201B */
{ CFI_MFR_SST , 0x236c , fixup_sst39vf_rev_b } , /* SST39VF6402B */
{ CFI_MFR_SST , 0x236d , fixup_sst39vf_rev_b } , /* SST39VF6401B */
{ 0 , 0 , NULL }
2010-04-24 19:58:22 +04:00
} ;
2005-04-17 02:20:36 +04:00
static struct cfi_fixup cfi_fixup_table [ ] = {
2010-11-17 14:35:50 +03:00
{ CFI_MFR_ATMEL , CFI_ID_ANY , fixup_convert_atmel_pri } ,
2005-04-17 02:20:36 +04:00
# ifdef AMD_BOOTLOC_BUG
2010-11-17 14:35:50 +03:00
{ CFI_MFR_AMD , CFI_ID_ANY , fixup_amd_bootblock } ,
2011-03-10 11:05:12 +03:00
{ CFI_MFR_AMIC , CFI_ID_ANY , fixup_amd_bootblock } ,
2010-11-17 14:35:50 +03:00
{ CFI_MFR_MACRONIX , CFI_ID_ANY , fixup_amd_bootblock } ,
2005-04-17 02:20:36 +04:00
# endif
2010-11-17 14:35:50 +03:00
{ CFI_MFR_AMD , 0x0050 , fixup_use_secsi } ,
{ CFI_MFR_AMD , 0x0053 , fixup_use_secsi } ,
{ CFI_MFR_AMD , 0x0055 , fixup_use_secsi } ,
{ CFI_MFR_AMD , 0x0056 , fixup_use_secsi } ,
{ CFI_MFR_AMD , 0x005C , fixup_use_secsi } ,
{ CFI_MFR_AMD , 0x005F , fixup_use_secsi } ,
{ CFI_MFR_AMD , 0x0c01 , fixup_s29gl064n_sectors } ,
{ CFI_MFR_AMD , 0x1301 , fixup_s29gl064n_sectors } ,
{ CFI_MFR_AMD , 0x1a00 , fixup_s29gl032n_sectors } ,
{ CFI_MFR_AMD , 0x1a01 , fixup_s29gl032n_sectors } ,
2012-05-11 14:15:41 +04:00
{ CFI_MFR_AMD , 0x3f00 , fixup_s29ns512p_sectors } ,
2010-11-17 14:35:50 +03:00
{ CFI_MFR_SST , 0x536a , fixup_sst38vf640x_sectorsize } , /* SST38VF6402 */
{ CFI_MFR_SST , 0x536b , fixup_sst38vf640x_sectorsize } , /* SST38VF6401 */
{ CFI_MFR_SST , 0x536c , fixup_sst38vf640x_sectorsize } , /* SST38VF6404 */
{ CFI_MFR_SST , 0x536d , fixup_sst38vf640x_sectorsize } , /* SST38VF6403 */
2005-04-17 02:20:36 +04:00
# if !FORCE_WORD_WRITE
2010-11-17 14:35:50 +03:00
{ CFI_MFR_ANY , CFI_ID_ANY , fixup_use_write_buffers } ,
2005-04-17 02:20:36 +04:00
# endif
2010-11-17 14:35:50 +03:00
{ 0 , 0 , NULL }
2005-04-17 02:20:36 +04:00
} ;
static struct cfi_fixup jedec_fixup_table [ ] = {
2010-11-17 14:35:50 +03:00
{ CFI_MFR_SST , SST49LF004B , fixup_use_fwh_lock } ,
{ CFI_MFR_SST , SST49LF040B , fixup_use_fwh_lock } ,
{ CFI_MFR_SST , SST49LF008A , fixup_use_fwh_lock } ,
{ 0 , 0 , NULL }
2005-04-17 02:20:36 +04:00
} ;
static struct cfi_fixup fixup_table [ ] = {
/* The CFI vendor ids and the JEDEC vendor IDs appear
* to be common . It is like the devices id ' s are as
* well . This table is to pick all cases where
* we know that is the case .
*/
2010-11-17 14:35:50 +03:00
{ CFI_MFR_ANY , CFI_ID_ANY , fixup_use_erase_chip } ,
{ CFI_MFR_ATMEL , AT49BV6416 , fixup_use_atmel_lock } ,
{ 0 , 0 , NULL }
2005-04-17 02:20:36 +04:00
} ;
2009-01-08 21:21:27 +03:00
static void cfi_fixup_major_minor ( struct cfi_private * cfi ,
struct cfi_pri_amdstd * extp )
{
2010-12-17 12:59:41 +03:00
if ( cfi - > mfr = = CFI_MFR_SAMSUNG ) {
2010-12-17 12:59:47 +03:00
if ( ( extp - > MajorVersion = = ' 0 ' & & extp - > MinorVersion = = ' 0 ' ) | |
( extp - > MajorVersion = = ' 3 ' & & extp - > MinorVersion = = ' 3 ' ) ) {
2010-12-17 12:59:41 +03:00
/*
* Samsung K8P2815UQB and K8D6x16UxM chips
* report major = 0 / minor = 0.
2010-12-17 12:59:47 +03:00
* K8D3x16UxC chips report major = 3 / minor = 3.
2010-12-17 12:59:41 +03:00
*/
printk ( KERN_NOTICE " Fixing Samsung's Amd/Fujitsu "
" Extended Query version to 1.%c \n " ,
extp - > MinorVersion ) ;
extp - > MajorVersion = ' 1 ' ;
}
}
2010-10-26 14:31:55 +04:00
/*
* SST 38 VF640x chips report major = 0xFF / minor = 0xFF .
*/
if ( cfi - > mfr = = CFI_MFR_SST & & ( cfi - > id > > 4 ) = = 0x0536 ) {
extp - > MajorVersion = ' 1 ' ;
extp - > MinorVersion = ' 0 ' ;
}
2009-01-08 21:21:27 +03:00
}
2005-04-17 02:20:36 +04:00
struct mtd_info * cfi_cmdset_0002 ( struct map_info * map , int primary )
{
struct cfi_private * cfi = map - > fldrv_priv ;
struct mtd_info * mtd ;
int i ;
2006-11-15 22:10:29 +03:00
mtd = kzalloc ( sizeof ( * mtd ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! mtd ) {
printk ( KERN_WARNING " Failed to allocate memory for MTD device \n " ) ;
return NULL ;
}
mtd - > priv = map ;
mtd - > type = MTD_NORFLASH ;
/* Fill in the default mtd operations */
2012-01-30 16:58:32 +04:00
mtd - > _erase = cfi_amdstd_erase_varsize ;
mtd - > _write = cfi_amdstd_write_words ;
mtd - > _read = cfi_amdstd_read ;
mtd - > _sync = cfi_amdstd_sync ;
mtd - > _suspend = cfi_amdstd_suspend ;
mtd - > _resume = cfi_amdstd_resume ;
2005-04-17 02:20:36 +04:00
mtd - > flags = MTD_CAP_NORFLASH ;
mtd - > name = map - > name ;
2006-06-14 19:53:44 +04:00
mtd - > writesize = 1 ;
2011-02-10 18:01:46 +03:00
mtd - > writebufsize = cfi_interleave ( cfi ) < < cfi - > cfiq - > MaxBufWriteSize ;
2010-12-17 01:42:15 +03:00
2011-07-19 21:06:10 +04:00
pr_debug ( " MTD %s(): write buffer size %d \n " , __func__ ,
mtd - > writebufsize ) ;
2005-04-17 02:20:36 +04:00
2012-01-30 16:58:32 +04:00
mtd - > _panic_write = cfi_amdstd_panic_write ;
2010-04-29 21:26:56 +04:00
mtd - > reboot_notifier . notifier_call = cfi_amdstd_reboot ;
2005-04-17 02:20:36 +04:00
if ( cfi - > cfi_mode = = CFI_MODE_CFI ) {
unsigned char bootloc ;
__u16 adr = primary ? cfi - > cfiq - > P_ADR : cfi - > cfiq - > A_ADR ;
struct cfi_pri_amdstd * extp ;
extp = ( struct cfi_pri_amdstd * ) cfi_read_pri ( map , adr , sizeof ( * extp ) , " Amd/Fujitsu " ) ;
2010-04-24 19:58:17 +04:00
if ( extp ) {
/*
* It ' s a real CFI chip , not one for which the probe
* routine faked a CFI structure .
*/
cfi_fixup_major_minor ( cfi , extp ) ;
2010-07-02 16:39:10 +04:00
/*
2011-04-11 17:53:35 +04:00
* Valid primary extension versions are : 1.0 , 1.1 , 1.2 , 1.3 , 1.4 , 1.5
2010-10-18 13:03:14 +04:00
* see : http : //cs.ozerki.net/zap/pub/axim-x5/docs/cfi_r20.pdf, page 19
* http : //www.spansion.com/Support/AppNotes/cfi_100_20011201.pdf
2010-08-05 15:55:24 +04:00
* http : //www.spansion.com/Support/Datasheets/s29ws-p_00_a12_e.pdf
2011-04-11 17:53:35 +04:00
* http : //www.spansion.com/Support/Datasheets/S29GL_128S_01GS_00_02_e.pdf
2010-07-02 16:39:10 +04:00
*/
2010-04-24 19:58:17 +04:00
if ( extp - > MajorVersion ! = ' 1 ' | |
2011-04-11 17:53:35 +04:00
( extp - > MajorVersion = = ' 1 ' & & ( extp - > MinorVersion < ' 0 ' | | extp - > MinorVersion > ' 5 ' ) ) ) {
2010-04-24 19:58:17 +04:00
printk ( KERN_ERR " Unknown Amd/Fujitsu Extended Query "
2010-07-02 16:39:10 +04:00
" version %c.%c (%#02x/%#02x). \n " ,
extp - > MajorVersion , extp - > MinorVersion ,
extp - > MajorVersion , extp - > MinorVersion ) ;
2010-04-24 19:58:17 +04:00
kfree ( extp ) ;
kfree ( mtd ) ;
return NULL ;
}
2005-07-21 01:01:17 +04:00
2010-07-02 16:39:10 +04:00
printk ( KERN_INFO " Amd/Fujitsu Extended Query version %c.%c. \n " ,
extp - > MajorVersion , extp - > MinorVersion ) ;
2010-04-24 19:58:17 +04:00
/* Install our own private info structure */
cfi - > cmdset_priv = extp ;
2005-04-17 02:20:36 +04:00
2010-04-24 19:58:17 +04:00
/* Apply cfi device specific fixups */
cfi_fixup ( mtd , cfi_fixup_table ) ;
2005-04-17 02:20:36 +04:00
# ifdef DEBUG_CFI_FEATURES
2010-04-24 19:58:17 +04:00
/* 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
2010-04-24 19:58:17 +04:00
bootloc = extp - > TopBottom ;
2010-05-14 04:35:54 +04:00
if ( ( bootloc < 2 ) | | ( bootloc > 5 ) ) {
printk ( KERN_WARNING " %s: CFI contains unrecognised boot "
" bank location (%d). Assuming bottom. \n " ,
2010-05-14 12:14:24 +04:00
map - > name , bootloc ) ;
2010-04-24 19:58:17 +04:00
bootloc = 2 ;
}
2005-04-17 02:20:36 +04:00
2010-04-24 19:58:17 +04:00
if ( bootloc = = 3 & & cfi - > cfiq - > NumEraseRegions > 1 ) {
2010-05-14 04:35:54 +04:00
printk ( KERN_WARNING " %s: Swapping erase regions for top-boot CFI table. \n " , map - > name ) ;
2005-11-07 14:15:37 +03:00
2010-04-24 19:58:17 +04:00
for ( i = 0 ; i < cfi - > cfiq - > NumEraseRegions / 2 ; i + + ) {
int j = ( cfi - > cfiq - > NumEraseRegions - 1 ) - i ;
__u32 swap ;
2005-11-07 14:15:37 +03:00
2010-04-24 19:58:17 +04:00
swap = cfi - > cfiq - > EraseRegionInfo [ i ] ;
cfi - > cfiq - > EraseRegionInfo [ i ] = cfi - > cfiq - > EraseRegionInfo [ j ] ;
cfi - > cfiq - > EraseRegionInfo [ j ] = swap ;
}
2005-04-17 02:20:36 +04:00
}
2010-04-24 19:58:17 +04:00
/* Set the default CFI lock/unlock addresses */
cfi - > addr_unlock1 = 0x555 ;
cfi - > addr_unlock2 = 0x2aa ;
}
2010-04-24 19:58:22 +04:00
cfi_fixup ( mtd , cfi_nopri_fixup_table ) ;
2010-04-24 19:58:17 +04:00
if ( ! cfi - > addr_unlock1 | | ! cfi - > addr_unlock2 ) {
kfree ( mtd ) ;
return NULL ;
2005-04-17 02:20:36 +04:00
}
} /* CFI mode */
else if ( cfi - > cfi_mode = = CFI_MODE_JEDEC ) {
/* Apply jedec specific fixups */
cfi_fixup ( mtd , jedec_fixup_table ) ;
}
/* Apply generic fixups */
cfi_fixup ( mtd , fixup_table ) ;
for ( i = 0 ; i < cfi - > numchips ; i + + ) {
cfi - > chips [ i ] . word_write_time = 1 < < cfi - > cfiq - > WordWriteTimeoutTyp ;
cfi - > chips [ i ] . buffer_write_time = 1 < < cfi - > cfiq - > BufWriteTimeoutTyp ;
cfi - > chips [ i ] . erase_time = 1 < < cfi - > cfiq - > BlockEraseTimeoutTyp ;
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
map - > fldrv = & cfi_amdstd_chipdrv ;
2005-11-07 14:15:37 +03:00
2005-04-17 02:20:36 +04:00
return cfi_amdstd_setup ( mtd ) ;
}
2010-05-20 18:54:10 +04:00
struct mtd_info * cfi_cmdset_0006 ( struct map_info * map , int primary ) __attribute__ ( ( alias ( " cfi_cmdset_0002 " ) ) ) ;
2010-05-20 18:54:05 +04:00
struct mtd_info * cfi_cmdset_0701 ( struct map_info * map , int primary ) __attribute__ ( ( alias ( " cfi_cmdset_0002 " ) ) ) ;
2006-05-09 01:58:25 +04:00
EXPORT_SYMBOL_GPL ( cfi_cmdset_0002 ) ;
2010-05-20 18:54:10 +04:00
EXPORT_SYMBOL_GPL ( cfi_cmdset_0006 ) ;
2010-05-20 18:54:05 +04:00
EXPORT_SYMBOL_GPL ( cfi_cmdset_0701 ) ;
2005-04-17 02:20:36 +04:00
static struct mtd_info * cfi_amdstd_setup ( struct mtd_info * mtd )
{
struct map_info * map = mtd - > priv ;
struct cfi_private * cfi = map - > fldrv_priv ;
unsigned long devsize = ( 1 < < cfi - > cfiq - > DevSize ) * cfi - > interleave ;
unsigned long offset = 0 ;
int i , j ;
2005-11-07 14:15:37 +03:00
printk ( KERN_NOTICE " number of %s chips: %d \n " ,
2005-04-17 02:20:36 +04:00
( cfi - > cfi_mode = = CFI_MODE_CFI ) ? " CFI " : " JEDEC " , cfi - > numchips ) ;
2005-11-07 14:15:37 +03:00
/* Select the correct geometry setup */
2005-04-17 02:20:36 +04:00
mtd - > size = devsize * cfi - > numchips ;
mtd - > numeraseregions = cfi - > cfiq - > NumEraseRegions * cfi - > numchips ;
mtd - > eraseregions = kmalloc ( sizeof ( struct mtd_erase_region_info )
* 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_WARNING " Failed to allocate memory for MTD erase region info \n " ) ;
goto setup_err ;
}
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 ;
2005-11-07 14:15:37 +03:00
2005-04-17 02:20:36 +04:00
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 ) ;
goto setup_err ;
}
__module_get ( THIS_MODULE ) ;
2010-04-29 21:26:56 +04:00
register_reboot_notifier ( & mtd - > reboot_notifier ) ;
2005-04-17 02:20:36 +04:00
return mtd ;
setup_err :
2010-01-10 12:01:19 +03:00
kfree ( mtd - > eraseregions ) ;
kfree ( mtd ) ;
2005-04-17 02:20:36 +04:00
kfree ( cfi - > cmdset_priv ) ;
kfree ( cfi - > cfiq ) ;
return NULL ;
}
/*
* Return true if the chip is ready .
*
* Ready is one of : read mode , query mode , erase - suspend - read mode ( in any
* non - suspended sector ) and is indicated by no toggle bits toggling .
*
* Note that anything more complicated than checking if no bits are toggling
* ( including checking DQ5 for an error status ) is tricky to get working
2011-03-31 05:57:33 +04:00
* correctly and is therefore not done ( particularly with interleaved chips
* as each chip must be checked independently of the others ) .
2005-04-17 02:20:36 +04:00
*/
2005-06-07 03:04:39 +04:00
static int __xipram chip_ready ( struct map_info * map , unsigned long addr )
2005-04-17 02:20:36 +04:00
{
map_word d , t ;
d = map_read ( map , addr ) ;
t = map_read ( map , addr ) ;
return map_word_equal ( map , d , t ) ;
}
2005-05-20 07:28:26 +04:00
/*
* Return true if the chip is ready and has the correct value .
*
* Ready is one of : read mode , query mode , erase - suspend - read mode ( in any
* non - suspended sector ) and it is indicated by no bits toggling .
*
* Error are indicated by toggling bits or bits held with the wrong value ,
* or with bits toggling .
*
* Note that anything more complicated than checking if no bits are toggling
* ( including checking DQ5 for an error status ) is tricky to get working
2011-03-31 05:57:33 +04:00
* correctly and is therefore not done ( particularly with interleaved chips
* as each chip must be checked independently of the others ) .
2005-05-20 07:28:26 +04:00
*
*/
2005-06-07 03:04:39 +04:00
static int __xipram chip_good ( struct map_info * map , unsigned long addr , map_word expected )
2005-05-20 07:28:26 +04:00
{
map_word oldd , curd ;
oldd = map_read ( map , addr ) ;
curd = map_read ( map , addr ) ;
2005-11-07 14:15:37 +03:00
return map_word_equal ( map , oldd , curd ) & &
2005-05-20 07:28:26 +04:00
map_word_equal ( map , curd , expected ) ;
}
2005-04-17 02:20:36 +04:00
static int get_chip ( struct map_info * map , struct flchip * chip , unsigned long adr , int mode )
{
DECLARE_WAITQUEUE ( wait , current ) ;
struct cfi_private * cfi = map - > fldrv_priv ;
unsigned long timeo ;
struct cfi_pri_amdstd * cfip = ( struct cfi_pri_amdstd * ) cfi - > cmdset_priv ;
resettime :
timeo = jiffies + HZ ;
retry :
switch ( chip - > state ) {
case FL_STATUS :
for ( ; ; ) {
if ( chip_ready ( map , adr ) )
break ;
if ( time_after ( jiffies , timeo ) ) {
printk ( KERN_ERR " Waiting for chip to be ready timed out. \n " ) ;
return - EIO ;
}
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
/* Someone else might have been playing with it. */
goto retry ;
}
2005-11-07 14:15:37 +03:00
2005-04-17 02:20:36 +04:00
case FL_READY :
case FL_CFI_QUERY :
case FL_JEDEC_QUERY :
return 0 ;
case FL_ERASING :
2009-11-19 14:01:58 +03:00
if ( ! cfip | | ! ( cfip - > EraseSuspend & ( 0x1 | 0x2 ) ) | |
! ( mode = = FL_READY | | mode = = FL_POINT | |
( mode = = FL_WRITING & & ( cfip - > EraseSuspend & 0x2 ) ) ) )
2005-04-17 02:20:36 +04:00
goto sleep ;
/* We could check to see if we're trying to access the sector
* that is currently being erased . However , no user will try
* anything like that so we just wait for the timeout . */
/* Erase suspend */
/* It's harmless to issue the Erase-Suspend and Erase-Resume
* commands when the erase algorithm isn ' t in progress . */
map_write ( map , CMD ( 0xB0 ) , chip - > in_progress_block_addr ) ;
chip - > oldstate = FL_ERASING ;
chip - > state = FL_ERASE_SUSPENDING ;
chip - > erase_suspended = 1 ;
for ( ; ; ) {
if ( chip_ready ( map , adr ) )
break ;
if ( time_after ( jiffies , timeo ) ) {
/* Should have suspended the erase by now.
* Send an Erase - Resume command as either
* there was an error ( so leave the erase
* routine to recover from it ) or we trying to
* use the erase - in - progress sector . */
2011-05-19 10:58:15 +04:00
put_chip ( map , chip , adr ) ;
2005-04-17 02:20:36 +04:00
printk ( KERN_ERR " MTD %s(): chip not ready after erase suspend \n " , __func__ ) ;
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
/* Nobody will touch it while it's in state FL_ERASE_SUSPENDING.
So we can just loop here . */
}
chip - > state = FL_READY ;
return 0 ;
2005-06-07 03:04:39 +04:00
case FL_XIP_WHILE_ERASING :
if ( mode ! = FL_READY & & mode ! = FL_POINT & &
( ! cfip | | ! ( cfip - > EraseSuspend & 2 ) ) )
goto sleep ;
chip - > oldstate = chip - > state ;
chip - > state = FL_READY ;
return 0 ;
2010-04-29 21:26:56 +04:00
case FL_SHUTDOWN :
/* The machine is rebooting */
return - EIO ;
2005-04-17 02:20:36 +04:00
case FL_POINT :
/* Only if there's no operation suspended... */
if ( mode = = FL_READY & & chip - > oldstate = = FL_READY )
return 0 ;
default :
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 ) ;
2010-04-19 00:46:44 +04:00
mutex_lock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
goto resettime ;
}
}
static void put_chip ( struct map_info * map , struct flchip * chip , unsigned long adr )
{
struct cfi_private * cfi = map - > fldrv_priv ;
switch ( chip - > oldstate ) {
case FL_ERASING :
2010-10-26 13:45:23 +04:00
map_write ( map , cfi - > sector_erase_cmd , chip - > in_progress_block_addr ) ;
2005-04-17 02:20:36 +04:00
chip - > oldstate = FL_READY ;
chip - > state = FL_ERASING ;
break ;
2005-06-07 03:04:39 +04:00
case FL_XIP_WHILE_ERASING :
chip - > state = chip - > oldstate ;
chip - > oldstate = FL_READY ;
break ;
2005-04-17 02:20:36 +04:00
case FL_READY :
case FL_STATUS :
break ;
default :
printk ( KERN_ERR " MTD: put_chip() called with oldstate %d!! \n " , chip - > oldstate ) ;
}
wake_up ( & chip - > wq ) ;
}
2005-06-07 03:04:39 +04:00
# ifdef CONFIG_MTD_XIP
/*
* No interrupt what so ever can be serviced while the flash isn ' t in array
* mode . This is ensured by the xip_disable ( ) and xip_enable ( ) functions
* enclosing any code path where the flash is known not to be in array mode .
* And within a XIP disabled code path , only functions marked with __xipram
* may be called and nothing else ( it ' s a good thing to inspect generated
* assembly to make sure inline functions were actually inlined and that gcc
* didn ' t emit calls to its own support functions ) . Also configuring MTD CFI
* support to a single buswidth and a single interleave is also recommended .
*/
2005-07-05 03:03:06 +04:00
2005-06-07 03:04:39 +04:00
static void xip_disable ( struct map_info * map , struct flchip * chip ,
unsigned long adr )
{
/* TODO: chips with no XIP use should ignore and return */
( void ) map_read ( map , adr ) ; /* ensure mmu mapping is up to date */
local_irq_disable ( ) ;
}
static void __xipram xip_enable ( struct map_info * map , struct flchip * chip ,
unsigned long adr )
{
struct cfi_private * cfi = map - > fldrv_priv ;
if ( chip - > state ! = FL_POINT & & chip - > state ! = FL_READY ) {
map_write ( map , CMD ( 0xf0 ) , adr ) ;
chip - > state = FL_READY ;
}
( void ) map_read ( map , adr ) ;
2005-07-07 18:50:16 +04:00
xip_iprefetch ( ) ;
2005-06-07 03:04:39 +04:00
local_irq_enable ( ) ;
}
/*
* When a delay is required for the flash operation to complete , the
* xip_udelay ( ) function is polling for both the given timeout and pending
* ( but still masked ) hardware interrupts . Whenever there is an interrupt
2005-11-07 14:15:37 +03:00
* pending then the flash erase operation is suspended , array mode restored
2005-06-07 03:04:39 +04:00
* and interrupts unmasked . Task scheduling might also happen at that
* point . The CPU eventually returns from the interrupt or the call to
* schedule ( ) and the suspended flash operation is resumed for the remaining
* of the delay period .
*
* Warning : this function _will_ fool interrupt latency tracing tools .
*/
static void __xipram xip_udelay ( struct map_info * map , struct flchip * chip ,
unsigned long adr , int usec )
{
struct cfi_private * cfi = map - > fldrv_priv ;
struct cfi_pri_amdstd * extp = cfi - > cmdset_priv ;
map_word status , OK = CMD ( 0x80 ) ;
unsigned long suspended , start = xip_currtime ( ) ;
flstate_t oldstate ;
do {
cpu_relax ( ) ;
if ( xip_irqpending ( ) & & extp & &
( ( chip - > state = = FL_ERASING & & ( extp - > EraseSuspend & 2 ) ) ) & &
( cfi_interleave_is_1 ( cfi ) | | chip - > oldstate = = FL_READY ) ) {
/*
2005-11-07 14:15:37 +03:00
* Let ' s suspend the erase operation when supported .
* Note that we currently don ' t try to suspend
* interleaved chips if there is already another
2005-06-07 03:04:39 +04:00
* operation suspended ( imagine what happens
* when one chip was already done with the current
* operation while another chip suspended it , then
* we resume the whole thing at once ) . Yes , it
* can happen !
*/
map_write ( map , CMD ( 0xb0 ) , adr ) ;
usec - = xip_elapsed_since ( start ) ;
suspended = xip_currtime ( ) ;
do {
if ( xip_elapsed_since ( suspended ) > 100000 ) {
/*
* The chip doesn ' t want to suspend
* after waiting for 100 msecs .
* This is a critical error but there
* is not much we can do here .
*/
return ;
}
status = map_read ( map , adr ) ;
} while ( ! map_word_andequal ( map , status , OK , OK ) ) ;
/* Suspend succeeded */
oldstate = chip - > state ;
if ( ! map_word_bitsset ( map , status , CMD ( 0x40 ) ) )
break ;
chip - > state = FL_XIP_WHILE_ERASING ;
chip - > erase_suspended = 1 ;
map_write ( map , CMD ( 0xf0 ) , adr ) ;
( void ) map_read ( map , adr ) ;
2008-02-27 02:42:39 +03:00
xip_iprefetch ( ) ;
2005-06-07 03:04:39 +04:00
local_irq_enable ( ) ;
2010-04-19 00:46:44 +04:00
mutex_unlock ( & chip - > mutex ) ;
2008-02-27 02:42:39 +03:00
xip_iprefetch ( ) ;
2005-06-07 03:04:39 +04:00
cond_resched ( ) ;
/*
* We ' re back . However someone else might have
* decided to go write to the chip if we are in
* a suspended erase state . If so let ' s wait
* until it ' s done .
*/
2010-04-19 00:46:44 +04:00
mutex_lock ( & chip - > mutex ) ;
2005-06-07 03:04:39 +04:00
while ( chip - > state ! = FL_XIP_WHILE_ERASING ) {
DECLARE_WAITQUEUE ( wait , current ) ;
set_current_state ( TASK_UNINTERRUPTIBLE ) ;
add_wait_queue ( & chip - > wq , & wait ) ;
2010-04-19 00:46:44 +04:00
mutex_unlock ( & chip - > mutex ) ;
2005-06-07 03:04:39 +04:00
schedule ( ) ;
remove_wait_queue ( & chip - > wq , & wait ) ;
2010-04-19 00:46:44 +04:00
mutex_lock ( & chip - > mutex ) ;
2005-06-07 03:04:39 +04:00
}
/* Disallow XIP again */
local_irq_disable ( ) ;
/* Resume the write or erase operation */
2010-10-26 13:45:23 +04:00
map_write ( map , cfi - > sector_erase_cmd , adr ) ;
2005-06-07 03:04:39 +04:00
chip - > state = oldstate ;
start = xip_currtime ( ) ;
} else if ( usec > = 1000000 / HZ ) {
/*
* Try to save on CPU power when waiting delay
* is at least a system timer tick period .
* No need to be extremely accurate here .
*/
xip_cpu_idle ( ) ;
}
status = map_read ( map , adr ) ;
} while ( ! map_word_andequal ( map , status , OK , OK )
& & xip_elapsed_since ( start ) < usec ) ;
}
# define UDELAY(map, chip, adr, usec) xip_udelay(map, chip, adr, usec)
/*
* The INVALIDATE_CACHED_RANGE ( ) macro is normally used in parallel while
* the flash is actively programming or erasing since we have to poll for
* the operation to complete anyway . We can ' t do that in a generic way with
* a XIP setup so do it before the actual flash operation in this case
* and stub it out from INVALIDATE_CACHE_UDELAY .
*/
# define XIP_INVAL_CACHED_RANGE(map, from, size) \
INVALIDATE_CACHED_RANGE ( map , from , size )
# define INVALIDATE_CACHE_UDELAY(map, chip, adr, len, usec) \
UDELAY ( map , chip , adr , usec )
/*
* Extra notes :
*
* Activating this XIP support changes the way the code works a bit . For
* example the code to suspend the current process when concurrent access
* happens is never executed because xip_udelay ( ) will always return with the
* same chip state as it was entered with . This is why there is no care for
* the presence of add_wait_queue ( ) or schedule ( ) calls from within a couple
* xip_disable ( ) ' d areas of code , like in do_erase_oneblock for example .
* The queueing and scheduling are always happening within xip_udelay ( ) .
*
* Similarly , get_chip ( ) and put_chip ( ) just happen to always be executed
* with chip - > state set to FL_READY ( or FL_XIP_WHILE_ * ) where flash state
* is in array mode , therefore never executing many cases therein and not
* causing any problem with XIP .
*/
# else
# define xip_disable(map, chip, adr)
# define xip_enable(map, chip, adr)
# define XIP_INVAL_CACHED_RANGE(x...)
# define UDELAY(map, chip, adr, usec) \
do { \
2010-04-19 00:46:44 +04:00
mutex_unlock ( & chip - > mutex ) ; \
2005-06-07 03:04:39 +04:00
cfi_udelay ( usec ) ; \
2010-04-19 00:46:44 +04:00
mutex_lock ( & chip - > mutex ) ; \
2005-06-07 03:04:39 +04:00
} while ( 0 )
# define INVALIDATE_CACHE_UDELAY(map, chip, adr, len, usec) \
do { \
2010-04-19 00:46:44 +04:00
mutex_unlock ( & chip - > mutex ) ; \
2005-06-07 03:04:39 +04:00
INVALIDATE_CACHED_RANGE ( map , adr , len ) ; \
cfi_udelay ( usec ) ; \
2010-04-19 00:46:44 +04:00
mutex_lock ( & chip - > mutex ) ; \
2005-06-07 03:04:39 +04:00
} while ( 0 )
# endif
2005-04-17 02:20:36 +04:00
static inline int do_read_onechip ( struct map_info * map , struct flchip * chip , loff_t adr , size_t len , u_char * buf )
{
unsigned long cmd_addr ;
struct cfi_private * cfi = map - > fldrv_priv ;
int ret ;
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
2010-04-19 00:46:44 +04:00
mutex_lock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
ret = get_chip ( map , chip , cmd_addr , FL_READY ) ;
if ( ret ) {
2010-04-19 00:46:44 +04:00
mutex_unlock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
return ret ;
}
if ( chip - > state ! = FL_POINT & & chip - > state ! = FL_READY ) {
map_write ( map , CMD ( 0xf0 ) , cmd_addr ) ;
chip - > state = FL_READY ;
}
map_copy_from ( map , buf , adr , len ) ;
put_chip ( map , chip , cmd_addr ) ;
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_amdstd_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 ) ;
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 ;
ofs = 0 ;
chipnum + + ;
}
return ret ;
}
static inline int do_read_secsi_onechip ( struct map_info * map , struct flchip * chip , loff_t adr , size_t len , u_char * buf )
{
DECLARE_WAITQUEUE ( wait , current ) ;
unsigned long timeo = jiffies + HZ ;
struct cfi_private * cfi = map - > fldrv_priv ;
retry :
2010-04-19 00:46:44 +04:00
mutex_lock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
if ( chip - > state ! = FL_READY ) {
set_current_state ( TASK_UNINTERRUPTIBLE ) ;
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 ) ;
timeo = jiffies + HZ ;
goto retry ;
2005-11-07 14:15:37 +03:00
}
2005-04-17 02:20:36 +04:00
adr + = chip - > start ;
chip - > state = FL_READY ;
cfi_send_gen_cmd ( 0xAA , cfi - > addr_unlock1 , chip - > start , map , cfi , cfi - > device_type , NULL ) ;
cfi_send_gen_cmd ( 0x55 , cfi - > addr_unlock2 , chip - > start , map , cfi , cfi - > device_type , NULL ) ;
cfi_send_gen_cmd ( 0x88 , cfi - > addr_unlock1 , chip - > start , map , cfi , cfi - > device_type , NULL ) ;
2005-11-07 14:15:37 +03:00
2005-04-17 02:20:36 +04:00
map_copy_from ( map , buf , adr , len ) ;
cfi_send_gen_cmd ( 0xAA , cfi - > addr_unlock1 , chip - > start , map , cfi , cfi - > device_type , NULL ) ;
cfi_send_gen_cmd ( 0x55 , cfi - > addr_unlock2 , chip - > start , map , cfi , cfi - > device_type , NULL ) ;
cfi_send_gen_cmd ( 0x90 , cfi - > addr_unlock1 , chip - > start , map , cfi , cfi - > device_type , NULL ) ;
cfi_send_gen_cmd ( 0x00 , cfi - > addr_unlock1 , chip - > start , map , cfi , cfi - > device_type , NULL ) ;
2005-11-07 14:15:37 +03:00
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_amdstd_secsi_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 */
/* 8 secsi bytes per chip */
chipnum = from > > 3 ;
ofs = from & 7 ;
while ( len ) {
unsigned long thislen ;
if ( chipnum > = cfi - > numchips )
break ;
if ( ( len + ofs - 1 ) > > 3 )
thislen = ( 1 < < 3 ) - ofs ;
else
thislen = len ;
ret = do_read_secsi_onechip ( map , & cfi - > chips [ chipnum ] , ofs , thislen , buf ) ;
if ( ret )
break ;
* retlen + = thislen ;
len - = thislen ;
buf + = thislen ;
ofs = 0 ;
chipnum + + ;
}
return ret ;
}
2005-06-07 03:04:39 +04:00
static int __xipram do_write_oneword ( struct map_info * map , struct flchip * chip , unsigned long adr , map_word datum )
2005-04-17 02:20:36 +04:00
{
struct cfi_private * cfi = map - > fldrv_priv ;
unsigned long timeo = jiffies + HZ ;
/*
* We use a 1 ms + 1 jiffies generic timeout for writes ( most devices
* have a max write time of a few hundreds usec ) . However , we should
* use the maximum timeout value given by the chip at probe time
* instead . Unfortunately , struct flchip does have a field for
* maximum timeout , only for typical which can be far too short
* depending of the conditions . The ' + 1 ' is to avoid having a
* timeout of 0 jiffies if HZ is smaller than 1000.
*/
unsigned long uWriteTimeout = ( HZ / 1000 ) + 1 ;
int ret = 0 ;
map_word oldd ;
int retry_cnt = 0 ;
adr + = chip - > start ;
2010-04-19 00:46:44 +04:00
mutex_lock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
ret = get_chip ( map , chip , adr , FL_WRITING ) ;
if ( ret ) {
2010-04-19 00:46:44 +04:00
mutex_unlock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
return ret ;
}
2011-07-19 21:06:09 +04:00
pr_debug ( " MTD %s(): WRITE 0x%.8lx(0x%.8lx) \n " ,
2005-04-17 02:20:36 +04:00
__func__ , adr , datum . x [ 0 ] ) ;
/*
* Check for a NOP for the case when the datum to write is already
* present - it saves time and works around buggy chips that corrupt
* data at other locations when 0xff is written to a location that
* already contains 0xff .
*/
oldd = map_read ( map , adr ) ;
if ( map_word_equal ( map , oldd , datum ) ) {
2011-07-19 21:06:09 +04:00
pr_debug ( " MTD %s(): NOP \n " ,
2005-04-17 02:20:36 +04:00
__func__ ) ;
goto op_done ;
}
2005-06-07 03:04:39 +04:00
XIP_INVAL_CACHED_RANGE ( map , adr , map_bankwidth ( map ) ) ;
2005-04-17 02:20:36 +04:00
ENABLE_VPP ( map ) ;
2005-06-07 03:04:39 +04:00
xip_disable ( map , chip , adr ) ;
2005-04-17 02:20:36 +04:00
retry :
cfi_send_gen_cmd ( 0xAA , cfi - > addr_unlock1 , chip - > start , map , cfi , cfi - > device_type , NULL ) ;
cfi_send_gen_cmd ( 0x55 , cfi - > addr_unlock2 , chip - > start , map , cfi , cfi - > device_type , NULL ) ;
cfi_send_gen_cmd ( 0xA0 , cfi - > addr_unlock1 , chip - > start , map , cfi , cfi - > device_type , NULL ) ;
map_write ( map , datum , adr ) ;
chip - > state = FL_WRITING ;
2005-06-07 03:04:39 +04:00
INVALIDATE_CACHE_UDELAY ( map , chip ,
adr , map_bankwidth ( map ) ,
chip - > word_write_time ) ;
2005-04-17 02:20:36 +04:00
/* See comment above for timeout value. */
2005-11-07 14:15:37 +03:00
timeo = jiffies + uWriteTimeout ;
2005-04-17 02:20:36 +04:00
for ( ; ; ) {
if ( chip - > state ! = FL_WRITING ) {
/* Someone's suspended the write. Sleep */
DECLARE_WAITQUEUE ( wait , current ) ;
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 ;
}
2005-11-07 12:00:05 +03:00
if ( time_after ( jiffies , timeo ) & & ! chip_ready ( map , adr ) ) {
2005-06-07 03:04:39 +04:00
xip_enable ( map , chip , adr ) ;
2005-05-20 07:28:26 +04:00
printk ( KERN_WARNING " MTD %s(): software timeout \n " , __func__ ) ;
2005-06-07 03:04:39 +04:00
xip_disable ( map , chip , adr ) ;
2005-11-07 12:00:05 +03:00
break ;
2005-05-20 07:28:26 +04:00
}
2005-04-17 02:20:36 +04:00
2005-11-07 12:00:05 +03:00
if ( chip_ready ( map , adr ) )
break ;
2005-04-17 02:20:36 +04:00
/* Latency issues. Drop the lock, wait a while and retry */
2005-06-07 03:04:39 +04:00
UDELAY ( map , chip , adr , 1 ) ;
2005-04-17 02:20:36 +04:00
}
2005-05-20 07:28:26 +04:00
/* Did we succeed? */
if ( ! chip_good ( map , adr , datum ) ) {
/* reset on all failures. */
map_write ( map , CMD ( 0xF0 ) , chip - > start ) ;
/* FIXME - should have reset delay before continuing */
2005-04-17 02:20:36 +04:00
2005-11-07 14:15:37 +03:00
if ( + + retry_cnt < = MAX_WORD_RETRIES )
2005-05-20 07:28:26 +04:00
goto retry ;
2005-04-17 02:20:36 +04:00
2005-05-20 07:28:26 +04:00
ret = - EIO ;
}
2005-06-07 03:04:39 +04:00
xip_enable ( map , chip , adr ) ;
2005-04-17 02:20:36 +04:00
op_done :
chip - > state = FL_READY ;
2012-03-07 18:11:16 +04:00
DISABLE_VPP ( map ) ;
2005-04-17 02:20:36 +04:00
put_chip ( map , chip , adr ) ;
2010-04-19 00:46:44 +04:00
mutex_unlock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
return ret ;
}
static int cfi_amdstd_write_words ( struct mtd_info * mtd , loff_t to , size_t len ,
size_t * retlen , const u_char * buf )
{
struct map_info * map = mtd - > priv ;
struct cfi_private * cfi = map - > fldrv_priv ;
int ret = 0 ;
int chipnum ;
unsigned long ofs , chipstart ;
DECLARE_WAITQUEUE ( wait , current ) ;
chipnum = to > > cfi - > chipshift ;
ofs = to - ( chipnum < < cfi - > chipshift ) ;
chipstart = cfi - > chips [ chipnum ] . start ;
/* If it's not bus-aligned, do the first byte write */
if ( ofs & ( map_bankwidth ( map ) - 1 ) ) {
unsigned long bus_ofs = ofs & ~ ( map_bankwidth ( map ) - 1 ) ;
int i = ofs - bus_ofs ;
int n = 0 ;
map_word tmp_buf ;
retry :
2010-04-19 00:46:44 +04:00
mutex_lock ( & cfi - > chips [ chipnum ] . mutex ) ;
2005-04-17 02:20:36 +04:00
if ( cfi - > chips [ chipnum ] . state ! = FL_READY ) {
set_current_state ( TASK_UNINTERRUPTIBLE ) ;
add_wait_queue ( & cfi - > chips [ chipnum ] . wq , & wait ) ;
2010-04-19 00:46:44 +04:00
mutex_unlock ( & cfi - > chips [ chipnum ] . mutex ) ;
2005-04-17 02:20:36 +04:00
schedule ( ) ;
remove_wait_queue ( & cfi - > chips [ chipnum ] . wq , & wait ) ;
goto retry ;
}
/* Load 'tmp_buf' with old contents of flash */
tmp_buf = map_read ( map , bus_ofs + chipstart ) ;
2010-04-19 00:46:44 +04:00
mutex_unlock ( & cfi - > chips [ chipnum ] . mutex ) ;
2005-04-17 02:20:36 +04:00
/* Number of bytes to copy from buffer */
n = min_t ( int , len , map_bankwidth ( map ) - i ) ;
2005-11-07 14:15:37 +03:00
2005-04-17 02:20:36 +04:00
tmp_buf = map_word_load_partial ( map , tmp_buf , buf , i , n ) ;
2005-11-07 14:15:37 +03:00
ret = do_write_oneword ( map , & cfi - > chips [ chipnum ] ,
2005-04-17 02:20:36 +04:00
bus_ofs , tmp_buf ) ;
2005-11-07 14:15:37 +03:00
if ( ret )
2005-04-17 02:20:36 +04:00
return ret ;
2005-11-07 14:15:37 +03:00
2005-04-17 02:20:36 +04:00
ofs + = n ;
buf + = n ;
( * retlen ) + = n ;
len - = n ;
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
/* We are now aligned, write as much as possible */
while ( len > = map_bankwidth ( map ) ) {
map_word datum ;
datum = map_word_load ( map , buf ) ;
ret = do_write_oneword ( map , & cfi - > chips [ chipnum ] ,
ofs , datum ) ;
if ( ret )
return ret ;
ofs + = map_bankwidth ( map ) ;
buf + = map_bankwidth ( map ) ;
( * retlen ) + = map_bankwidth ( map ) ;
len - = map_bankwidth ( map ) ;
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 ;
chipstart = cfi - > chips [ chipnum ] . start ;
}
}
/* Write the trailing bytes if any */
if ( len & ( map_bankwidth ( map ) - 1 ) ) {
map_word tmp_buf ;
retry1 :
2010-04-19 00:46:44 +04:00
mutex_lock ( & cfi - > chips [ chipnum ] . mutex ) ;
2005-04-17 02:20:36 +04:00
if ( cfi - > chips [ chipnum ] . state ! = FL_READY ) {
set_current_state ( TASK_UNINTERRUPTIBLE ) ;
add_wait_queue ( & cfi - > chips [ chipnum ] . wq , & wait ) ;
2010-04-19 00:46:44 +04:00
mutex_unlock ( & cfi - > chips [ chipnum ] . mutex ) ;
2005-04-17 02:20:36 +04:00
schedule ( ) ;
remove_wait_queue ( & cfi - > chips [ chipnum ] . wq , & wait ) ;
goto retry1 ;
}
tmp_buf = map_read ( map , ofs + chipstart ) ;
2010-04-19 00:46:44 +04:00
mutex_unlock ( & cfi - > chips [ chipnum ] . mutex ) ;
2005-04-17 02:20:36 +04:00
tmp_buf = map_word_load_partial ( map , tmp_buf , buf , 0 , len ) ;
2005-11-07 14:15:37 +03:00
ret = do_write_oneword ( map , & cfi - > chips [ chipnum ] ,
2005-04-17 02:20:36 +04:00
ofs , tmp_buf ) ;
2005-11-07 14:15:37 +03:00
if ( ret )
2005-04-17 02:20:36 +04:00
return ret ;
2005-11-07 14:15:37 +03:00
2005-04-17 02:20:36 +04:00
( * retlen ) + = len ;
}
return 0 ;
}
/*
* FIXME : interleaved mode not tested , and probably not supported !
*/
2005-06-07 03:04:39 +04:00
static int __xipram do_write_buffer ( struct map_info * map , struct flchip * chip ,
2005-11-07 14:15:37 +03:00
unsigned long adr , const u_char * buf ,
2005-06-07 03:04:39 +04:00
int len )
2005-04-17 02:20:36 +04:00
{
struct cfi_private * cfi = map - > fldrv_priv ;
unsigned long timeo = jiffies + HZ ;
/* see comments in do_write_oneword() regarding uWriteTimeo. */
unsigned long uWriteTimeout = ( HZ / 1000 ) + 1 ;
int ret = - EIO ;
unsigned long cmd_adr ;
int z , words ;
map_word datum ;
adr + = chip - > start ;
cmd_adr = adr ;
2010-04-19 00:46:44 +04:00
mutex_lock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
ret = get_chip ( map , chip , adr , FL_WRITING ) ;
if ( ret ) {
2010-04-19 00:46:44 +04:00
mutex_unlock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
return ret ;
}
datum = map_word_load ( map , buf ) ;
2011-07-19 21:06:09 +04:00
pr_debug ( " MTD %s(): WRITE 0x%.8lx(0x%.8lx) \n " ,
2005-04-17 02:20:36 +04:00
__func__ , adr , datum . x [ 0 ] ) ;
2005-06-07 03:04:39 +04:00
XIP_INVAL_CACHED_RANGE ( map , adr , len ) ;
2005-04-17 02:20:36 +04:00
ENABLE_VPP ( map ) ;
2005-06-07 03:04:39 +04:00
xip_disable ( map , chip , cmd_adr ) ;
2005-11-07 14:15:37 +03:00
2005-04-17 02:20:36 +04:00
cfi_send_gen_cmd ( 0xAA , cfi - > addr_unlock1 , chip - > start , map , cfi , cfi - > device_type , NULL ) ;
cfi_send_gen_cmd ( 0x55 , cfi - > addr_unlock2 , chip - > start , map , cfi , cfi - > device_type , NULL ) ;
/* Write Buffer Load */
map_write ( map , CMD ( 0x25 ) , cmd_adr ) ;
chip - > state = FL_WRITING_TO_BUFFER ;
/* Write length of data to come */
words = len / map_bankwidth ( map ) ;
map_write ( map , CMD ( words - 1 ) , cmd_adr ) ;
/* Write data */
z = 0 ;
while ( z < words * map_bankwidth ( map ) ) {
datum = map_word_load ( map , buf ) ;
map_write ( map , datum , adr + z ) ;
z + = map_bankwidth ( map ) ;
buf + = map_bankwidth ( map ) ;
}
z - = map_bankwidth ( map ) ;
adr + = z ;
/* Write Buffer Program Confirm: GO GO GO */
map_write ( map , CMD ( 0x29 ) , cmd_adr ) ;
chip - > state = FL_WRITING ;
2005-06-07 03:04:39 +04:00
INVALIDATE_CACHE_UDELAY ( map , chip ,
adr , map_bankwidth ( map ) ,
chip - > word_write_time ) ;
2005-04-17 02:20:36 +04:00
2005-11-07 14:15:37 +03:00
timeo = jiffies + uWriteTimeout ;
2005-04-17 02:20:36 +04:00
for ( ; ; ) {
if ( chip - > state ! = FL_WRITING ) {
/* Someone's suspended the write. Sleep */
DECLARE_WAITQUEUE ( wait , current ) ;
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 ;
}
2005-11-07 12:00:05 +03:00
if ( time_after ( jiffies , timeo ) & & ! chip_ready ( map , adr ) )
break ;
2005-06-07 03:04:39 +04:00
if ( chip_ready ( map , adr ) ) {
xip_enable ( map , chip , adr ) ;
2005-04-17 02:20:36 +04:00
goto op_done ;
2005-06-07 03:04:39 +04:00
}
2005-04-17 02:20:36 +04:00
/* Latency issues. Drop the lock, wait a while and retry */
2005-06-07 03:04:39 +04:00
UDELAY ( map , chip , adr , 1 ) ;
2005-04-17 02:20:36 +04:00
}
/* reset on all failures. */
map_write ( map , CMD ( 0xF0 ) , chip - > start ) ;
2005-06-07 03:04:39 +04:00
xip_enable ( map , chip , adr ) ;
2005-04-17 02:20:36 +04:00
/* FIXME - should have reset delay before continuing */
2005-06-07 03:04:39 +04:00
printk ( KERN_WARNING " MTD %s(): software timeout \n " ,
__func__ ) ;
2005-04-17 02:20:36 +04:00
ret = - EIO ;
op_done :
chip - > state = FL_READY ;
2012-03-07 18:11:16 +04:00
DISABLE_VPP ( map ) ;
2005-04-17 02:20:36 +04:00
put_chip ( map , chip , adr ) ;
2010-04-19 00:46:44 +04:00
mutex_unlock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
return ret ;
}
static int cfi_amdstd_write_buffers ( struct mtd_info * mtd , loff_t to , 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 ;
chipnum = to > > cfi - > chipshift ;
ofs = to - ( chipnum < < cfi - > chipshift ) ;
/* If it's not bus-aligned, do the first word write */
if ( ofs & ( map_bankwidth ( map ) - 1 ) ) {
size_t local_len = ( - ofs ) & ( map_bankwidth ( map ) - 1 ) ;
if ( local_len > len )
local_len = len ;
ret = cfi_amdstd_write_words ( mtd , ofs + ( chipnum < < cfi - > chipshift ) ,
local_len , retlen , buf ) ;
if ( ret )
return ret ;
ofs + = local_len ;
buf + = local_len ;
len - = local_len ;
if ( ofs > > cfi - > chipshift ) {
chipnum + + ;
ofs = 0 ;
if ( chipnum = = cfi - > numchips )
return 0 ;
}
}
/* Write buffer is worth it only if more than one word to write... */
while ( len > = map_bankwidth ( map ) * 2 ) {
/* We must not cross write block boundaries */
int size = wbufsize - ( ofs & ( wbufsize - 1 ) ) ;
if ( size > len )
size = len ;
if ( size % map_bankwidth ( map ) )
size - = size % map_bankwidth ( map ) ;
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 ;
}
}
if ( len ) {
size_t retlen_dregs = 0 ;
ret = cfi_amdstd_write_words ( mtd , ofs + ( chipnum < < cfi - > chipshift ) ,
len , & retlen_dregs , buf ) ;
* retlen + = retlen_dregs ;
return ret ;
}
return 0 ;
}
2012-01-06 23:29:19 +04:00
/*
* Wait for the flash chip to become ready to write data
*
* This is only called during the panic_write ( ) path . When panic_write ( )
* is called , the kernel is in the process of a panic , and will soon be
* dead . Therefore we don ' t take any locks , and attempt to get access
* to the chip as soon as possible .
*/
static int cfi_amdstd_panic_wait ( struct map_info * map , struct flchip * chip ,
unsigned long adr )
{
struct cfi_private * cfi = map - > fldrv_priv ;
int retries = 10 ;
int i ;
/*
* If the driver thinks the chip is idle , and no toggle bits
* are changing , then the chip is actually idle for sure .
*/
if ( chip - > state = = FL_READY & & chip_ready ( map , adr ) )
return 0 ;
/*
* Try several times to reset the chip and then wait for it
* to become idle . The upper limit of a few milliseconds of
* delay isn ' t a big problem : the kernel is dying anyway . It
* is more important to save the messages .
*/
while ( retries > 0 ) {
const unsigned long timeo = ( HZ / 1000 ) + 1 ;
/* send the reset command */
map_write ( map , CMD ( 0xF0 ) , chip - > start ) ;
/* wait for the chip to become ready */
for ( i = 0 ; i < jiffies_to_usecs ( timeo ) ; i + + ) {
if ( chip_ready ( map , adr ) )
return 0 ;
udelay ( 1 ) ;
}
}
/* the chip never became ready */
return - EBUSY ;
}
/*
* Write out one word of data to a single flash chip during a kernel panic
*
* This is only called during the panic_write ( ) path . When panic_write ( )
* is called , the kernel is in the process of a panic , and will soon be
* dead . Therefore we don ' t take any locks , and attempt to get access
* to the chip as soon as possible .
*
* The implementation of this routine is intentionally similar to
* do_write_oneword ( ) , in order to ease code maintenance .
*/
static int do_panic_write_oneword ( struct map_info * map , struct flchip * chip ,
unsigned long adr , map_word datum )
{
const unsigned long uWriteTimeout = ( HZ / 1000 ) + 1 ;
struct cfi_private * cfi = map - > fldrv_priv ;
int retry_cnt = 0 ;
map_word oldd ;
int ret = 0 ;
int i ;
adr + = chip - > start ;
ret = cfi_amdstd_panic_wait ( map , chip , adr ) ;
if ( ret )
return ret ;
pr_debug ( " MTD %s(): PANIC WRITE 0x%.8lx(0x%.8lx) \n " ,
__func__ , adr , datum . x [ 0 ] ) ;
/*
* Check for a NOP for the case when the datum to write is already
* present - it saves time and works around buggy chips that corrupt
* data at other locations when 0xff is written to a location that
* already contains 0xff .
*/
oldd = map_read ( map , adr ) ;
if ( map_word_equal ( map , oldd , datum ) ) {
pr_debug ( " MTD %s(): NOP \n " , __func__ ) ;
goto op_done ;
}
ENABLE_VPP ( map ) ;
retry :
cfi_send_gen_cmd ( 0xAA , cfi - > addr_unlock1 , chip - > start , map , cfi , cfi - > device_type , NULL ) ;
cfi_send_gen_cmd ( 0x55 , cfi - > addr_unlock2 , chip - > start , map , cfi , cfi - > device_type , NULL ) ;
cfi_send_gen_cmd ( 0xA0 , cfi - > addr_unlock1 , chip - > start , map , cfi , cfi - > device_type , NULL ) ;
map_write ( map , datum , adr ) ;
for ( i = 0 ; i < jiffies_to_usecs ( uWriteTimeout ) ; i + + ) {
if ( chip_ready ( map , adr ) )
break ;
udelay ( 1 ) ;
}
if ( ! chip_good ( map , adr , datum ) ) {
/* reset on all failures. */
map_write ( map , CMD ( 0xF0 ) , chip - > start ) ;
/* FIXME - should have reset delay before continuing */
if ( + + retry_cnt < = MAX_WORD_RETRIES )
goto retry ;
ret = - EIO ;
}
op_done :
DISABLE_VPP ( map ) ;
return ret ;
}
/*
* Write out some data during a kernel panic
*
* This is used by the mtdoops driver to save the dying messages from a
* kernel which has panic ' d .
*
* This routine ignores all of the locking used throughout the rest of the
* driver , in order to ensure that the data gets written out no matter what
* state this driver ( and the flash chip itself ) was in when the kernel crashed .
*
* The implementation of this routine is intentionally similar to
* cfi_amdstd_write_words ( ) , in order to ease code maintenance .
*/
static int cfi_amdstd_panic_write ( struct mtd_info * mtd , loff_t to , size_t len ,
size_t * retlen , const u_char * buf )
{
struct map_info * map = mtd - > priv ;
struct cfi_private * cfi = map - > fldrv_priv ;
unsigned long ofs , chipstart ;
int ret = 0 ;
int chipnum ;
chipnum = to > > cfi - > chipshift ;
ofs = to - ( chipnum < < cfi - > chipshift ) ;
chipstart = cfi - > chips [ chipnum ] . start ;
/* If it's not bus aligned, do the first byte write */
if ( ofs & ( map_bankwidth ( map ) - 1 ) ) {
unsigned long bus_ofs = ofs & ~ ( map_bankwidth ( map ) - 1 ) ;
int i = ofs - bus_ofs ;
int n = 0 ;
map_word tmp_buf ;
ret = cfi_amdstd_panic_wait ( map , & cfi - > chips [ chipnum ] , bus_ofs ) ;
if ( ret )
return ret ;
/* Load 'tmp_buf' with old contents of flash */
tmp_buf = map_read ( map , bus_ofs + chipstart ) ;
/* Number of bytes to copy from buffer */
n = min_t ( int , len , map_bankwidth ( map ) - i ) ;
tmp_buf = map_word_load_partial ( map , tmp_buf , buf , i , n ) ;
ret = do_panic_write_oneword ( map , & cfi - > chips [ chipnum ] ,
bus_ofs , tmp_buf ) ;
if ( ret )
return ret ;
ofs + = n ;
buf + = n ;
( * retlen ) + = n ;
len - = n ;
if ( ofs > > cfi - > chipshift ) {
chipnum + + ;
ofs = 0 ;
if ( chipnum = = cfi - > numchips )
return 0 ;
}
}
/* We are now aligned, write as much as possible */
while ( len > = map_bankwidth ( map ) ) {
map_word datum ;
datum = map_word_load ( map , buf ) ;
ret = do_panic_write_oneword ( map , & cfi - > chips [ chipnum ] ,
ofs , datum ) ;
if ( ret )
return ret ;
ofs + = map_bankwidth ( map ) ;
buf + = map_bankwidth ( map ) ;
( * retlen ) + = map_bankwidth ( map ) ;
len - = map_bankwidth ( map ) ;
if ( ofs > > cfi - > chipshift ) {
chipnum + + ;
ofs = 0 ;
if ( chipnum = = cfi - > numchips )
return 0 ;
chipstart = cfi - > chips [ chipnum ] . start ;
}
}
/* Write the trailing bytes if any */
if ( len & ( map_bankwidth ( map ) - 1 ) ) {
map_word tmp_buf ;
ret = cfi_amdstd_panic_wait ( map , & cfi - > chips [ chipnum ] , ofs ) ;
if ( ret )
return ret ;
tmp_buf = map_read ( map , ofs + chipstart ) ;
tmp_buf = map_word_load_partial ( map , tmp_buf , buf , 0 , len ) ;
ret = do_panic_write_oneword ( map , & cfi - > chips [ chipnum ] ,
ofs , tmp_buf ) ;
if ( ret )
return ret ;
( * retlen ) + = len ;
}
return 0 ;
}
2005-04-17 02:20:36 +04:00
/*
* Handle devices with one erase region , that only implement
* the chip erase command .
*/
2005-06-07 03:04:39 +04:00
static int __xipram do_erase_chip ( struct map_info * map , struct flchip * chip )
2005-04-17 02:20:36 +04:00
{
struct cfi_private * cfi = map - > fldrv_priv ;
unsigned long timeo = jiffies + HZ ;
unsigned long int adr ;
DECLARE_WAITQUEUE ( wait , current ) ;
int ret = 0 ;
adr = cfi - > addr_unlock1 ;
2010-04-19 00:46:44 +04:00
mutex_lock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
ret = get_chip ( map , chip , adr , FL_WRITING ) ;
if ( ret ) {
2010-04-19 00:46:44 +04:00
mutex_unlock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
return ret ;
}
2011-07-19 21:06:09 +04:00
pr_debug ( " MTD %s(): ERASE 0x%.8lx \n " ,
2005-04-17 02:20:36 +04:00
__func__ , chip - > start ) ;
2005-06-07 03:04:39 +04:00
XIP_INVAL_CACHED_RANGE ( map , adr , map - > size ) ;
2005-04-17 02:20:36 +04:00
ENABLE_VPP ( map ) ;
2005-06-07 03:04:39 +04:00
xip_disable ( map , chip , adr ) ;
2005-04-17 02:20:36 +04:00
cfi_send_gen_cmd ( 0xAA , cfi - > addr_unlock1 , chip - > start , map , cfi , cfi - > device_type , NULL ) ;
cfi_send_gen_cmd ( 0x55 , cfi - > addr_unlock2 , chip - > start , map , cfi , cfi - > device_type , NULL ) ;
cfi_send_gen_cmd ( 0x80 , cfi - > addr_unlock1 , chip - > start , map , cfi , cfi - > device_type , NULL ) ;
cfi_send_gen_cmd ( 0xAA , cfi - > addr_unlock1 , chip - > start , map , cfi , cfi - > device_type , NULL ) ;
cfi_send_gen_cmd ( 0x55 , cfi - > addr_unlock2 , chip - > start , map , cfi , cfi - > device_type , NULL ) ;
cfi_send_gen_cmd ( 0x10 , cfi - > addr_unlock1 , chip - > start , map , cfi , cfi - > device_type , NULL ) ;
chip - > state = FL_ERASING ;
chip - > erase_suspended = 0 ;
chip - > in_progress_block_addr = adr ;
2005-06-07 03:04:39 +04:00
INVALIDATE_CACHE_UDELAY ( map , chip ,
adr , map - > size ,
chip - > erase_time * 500 ) ;
2005-04-17 02:20:36 +04:00
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 ) ;
2010-04-19 00:46:44 +04:00
mutex_lock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
continue ;
}
if ( chip - > erase_suspended ) {
/* This erase was suspended and resumed.
Adjust the timeout */
timeo = jiffies + ( HZ * 20 ) ; /* FIXME */
chip - > erase_suspended = 0 ;
}
if ( chip_ready ( map , adr ) )
2005-05-20 07:28:26 +04:00
break ;
2005-04-17 02:20:36 +04:00
2005-05-20 07:28:26 +04:00
if ( time_after ( jiffies , timeo ) ) {
printk ( KERN_WARNING " MTD %s(): software timeout \n " ,
__func__ ) ;
2005-04-17 02:20:36 +04:00
break ;
2005-05-20 07:28:26 +04:00
}
2005-04-17 02:20:36 +04:00
/* Latency issues. Drop the lock, wait a while and retry */
2005-06-07 03:04:39 +04:00
UDELAY ( map , chip , adr , 1000000 / HZ ) ;
2005-04-17 02:20:36 +04:00
}
2005-05-20 07:28:26 +04:00
/* Did we succeed? */
if ( ! chip_good ( map , adr , map_word_ff ( map ) ) ) {
/* reset on all failures. */
map_write ( map , CMD ( 0xF0 ) , chip - > start ) ;
/* FIXME - should have reset delay before continuing */
2005-04-17 02:20:36 +04:00
2005-05-20 07:28:26 +04:00
ret = - EIO ;
}
2005-04-17 02:20:36 +04:00
chip - > state = FL_READY ;
2005-06-07 03:04:39 +04:00
xip_enable ( map , chip , adr ) ;
2012-03-07 18:11:16 +04:00
DISABLE_VPP ( map ) ;
2005-04-17 02:20:36 +04:00
put_chip ( map , chip , adr ) ;
2010-04-19 00:46:44 +04:00
mutex_unlock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
return ret ;
}
2005-06-07 03:04:39 +04:00
static int __xipram do_erase_oneblock ( struct map_info * map , struct flchip * chip , unsigned long adr , int len , void * thunk )
2005-04-17 02:20:36 +04:00
{
struct cfi_private * cfi = map - > fldrv_priv ;
unsigned long timeo = jiffies + HZ ;
DECLARE_WAITQUEUE ( wait , current ) ;
int ret = 0 ;
adr + = chip - > start ;
2010-04-19 00:46:44 +04:00
mutex_lock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
ret = get_chip ( map , chip , adr , FL_ERASING ) ;
if ( ret ) {
2010-04-19 00:46:44 +04:00
mutex_unlock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
return ret ;
}
2011-07-19 21:06:09 +04:00
pr_debug ( " MTD %s(): ERASE 0x%.8lx \n " ,
2005-04-17 02:20:36 +04:00
__func__ , adr ) ;
2005-06-07 03:04:39 +04:00
XIP_INVAL_CACHED_RANGE ( map , adr , len ) ;
2005-04-17 02:20:36 +04:00
ENABLE_VPP ( map ) ;
2005-06-07 03:04:39 +04:00
xip_disable ( map , chip , adr ) ;
2005-04-17 02:20:36 +04:00
cfi_send_gen_cmd ( 0xAA , cfi - > addr_unlock1 , chip - > start , map , cfi , cfi - > device_type , NULL ) ;
cfi_send_gen_cmd ( 0x55 , cfi - > addr_unlock2 , chip - > start , map , cfi , cfi - > device_type , NULL ) ;
cfi_send_gen_cmd ( 0x80 , cfi - > addr_unlock1 , chip - > start , map , cfi , cfi - > device_type , NULL ) ;
cfi_send_gen_cmd ( 0xAA , cfi - > addr_unlock1 , chip - > start , map , cfi , cfi - > device_type , NULL ) ;
cfi_send_gen_cmd ( 0x55 , cfi - > addr_unlock2 , chip - > start , map , cfi , cfi - > device_type , NULL ) ;
2010-10-26 13:45:23 +04:00
map_write ( map , cfi - > sector_erase_cmd , adr ) ;
2005-04-17 02:20:36 +04:00
chip - > state = FL_ERASING ;
chip - > erase_suspended = 0 ;
chip - > in_progress_block_addr = adr ;
2005-06-07 03:04:39 +04:00
INVALIDATE_CACHE_UDELAY ( map , chip ,
adr , len ,
chip - > erase_time * 500 ) ;
2005-04-17 02:20:36 +04:00
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 ) ;
2010-04-19 00:46:44 +04:00
mutex_lock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
continue ;
}
if ( chip - > erase_suspended ) {
/* This erase was suspended and resumed.
Adjust the timeout */
timeo = jiffies + ( HZ * 20 ) ; /* FIXME */
chip - > erase_suspended = 0 ;
}
2005-06-07 03:04:39 +04:00
if ( chip_ready ( map , adr ) ) {
xip_enable ( map , chip , adr ) ;
2005-05-20 07:28:26 +04:00
break ;
2005-06-07 03:04:39 +04:00
}
2005-04-17 02:20:36 +04:00
2005-05-20 07:28:26 +04:00
if ( time_after ( jiffies , timeo ) ) {
2005-06-07 03:04:39 +04:00
xip_enable ( map , chip , adr ) ;
2005-05-20 07:28:26 +04:00
printk ( KERN_WARNING " MTD %s(): software timeout \n " ,
__func__ ) ;
2005-04-17 02:20:36 +04:00
break ;
2005-05-20 07:28:26 +04:00
}
2005-04-17 02:20:36 +04:00
/* Latency issues. Drop the lock, wait a while and retry */
2005-06-07 03:04:39 +04:00
UDELAY ( map , chip , adr , 1000000 / HZ ) ;
2005-04-17 02:20:36 +04:00
}
2005-05-20 07:28:26 +04:00
/* Did we succeed? */
2005-05-24 17:33:49 +04:00
if ( ! chip_good ( map , adr , map_word_ff ( map ) ) ) {
2005-05-20 07:28:26 +04:00
/* reset on all failures. */
map_write ( map , CMD ( 0xF0 ) , chip - > start ) ;
/* FIXME - should have reset delay before continuing */
ret = - EIO ;
}
2005-04-17 02:20:36 +04:00
chip - > state = FL_READY ;
2012-03-07 18:11:16 +04:00
DISABLE_VPP ( map ) ;
2005-04-17 02:20:36 +04:00
put_chip ( map , chip , adr ) ;
2010-04-19 00:46:44 +04:00
mutex_unlock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
return ret ;
}
2007-05-28 22:59:00 +04:00
static int cfi_amdstd_erase_varsize ( struct mtd_info * mtd , struct erase_info * instr )
2005-04-17 02:20:36 +04:00
{
unsigned long ofs , len ;
int ret ;
ofs = instr - > addr ;
len = instr - > len ;
ret = cfi_varsize_frob ( mtd , do_erase_oneblock , ofs , len , NULL ) ;
if ( ret )
return ret ;
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 int cfi_amdstd_erase_chip ( struct mtd_info * mtd , struct erase_info * instr )
{
struct map_info * map = mtd - > priv ;
struct cfi_private * cfi = map - > fldrv_priv ;
int ret = 0 ;
if ( instr - > addr ! = 0 )
return - EINVAL ;
if ( instr - > len ! = mtd - > size )
return - EINVAL ;
ret = do_erase_chip ( map , & cfi - > chips [ 0 ] ) ;
if ( ret )
return ret ;
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 ;
}
2006-08-09 13:06:07 +04:00
static int do_atmel_lock ( struct map_info * map , struct flchip * chip ,
unsigned long adr , int len , void * thunk )
{
struct cfi_private * cfi = map - > fldrv_priv ;
int ret ;
2010-04-19 00:46:44 +04:00
mutex_lock ( & chip - > mutex ) ;
2006-08-09 13:06:07 +04:00
ret = get_chip ( map , chip , adr + chip - > start , FL_LOCKING ) ;
if ( ret )
goto out_unlock ;
chip - > state = FL_LOCKING ;
2011-07-19 21:06:10 +04:00
pr_debug ( " MTD %s(): LOCK 0x%08lx len %d \n " , __func__ , adr , len ) ;
2006-08-09 13:06:07 +04:00
cfi_send_gen_cmd ( 0xAA , cfi - > addr_unlock1 , chip - > start , map , cfi ,
cfi - > device_type , NULL ) ;
cfi_send_gen_cmd ( 0x55 , cfi - > addr_unlock2 , chip - > start , map , cfi ,
cfi - > device_type , NULL ) ;
cfi_send_gen_cmd ( 0x80 , cfi - > addr_unlock1 , chip - > start , map , cfi ,
cfi - > device_type , NULL ) ;
cfi_send_gen_cmd ( 0xAA , cfi - > addr_unlock1 , chip - > start , map , cfi ,
cfi - > device_type , NULL ) ;
cfi_send_gen_cmd ( 0x55 , cfi - > addr_unlock2 , chip - > start , map , cfi ,
cfi - > device_type , NULL ) ;
map_write ( map , CMD ( 0x40 ) , chip - > start + adr ) ;
chip - > state = FL_READY ;
put_chip ( map , chip , adr + chip - > start ) ;
ret = 0 ;
out_unlock :
2010-04-19 00:46:44 +04:00
mutex_unlock ( & chip - > mutex ) ;
2006-08-09 13:06:07 +04:00
return ret ;
}
static int do_atmel_unlock ( struct map_info * map , struct flchip * chip ,
unsigned long adr , int len , void * thunk )
{
struct cfi_private * cfi = map - > fldrv_priv ;
int ret ;
2010-04-19 00:46:44 +04:00
mutex_lock ( & chip - > mutex ) ;
2006-08-09 13:06:07 +04:00
ret = get_chip ( map , chip , adr + chip - > start , FL_UNLOCKING ) ;
if ( ret )
goto out_unlock ;
chip - > state = FL_UNLOCKING ;
2011-07-19 21:06:10 +04:00
pr_debug ( " MTD %s(): LOCK 0x%08lx len %d \n " , __func__ , adr , len ) ;
2006-08-09 13:06:07 +04:00
cfi_send_gen_cmd ( 0xAA , cfi - > addr_unlock1 , chip - > start , map , cfi ,
cfi - > device_type , NULL ) ;
map_write ( map , CMD ( 0x70 ) , adr ) ;
chip - > state = FL_READY ;
put_chip ( map , chip , adr + chip - > start ) ;
ret = 0 ;
out_unlock :
2010-04-19 00:46:44 +04:00
mutex_unlock ( & chip - > mutex ) ;
2006-08-09 13:06:07 +04:00
return ret ;
}
2008-12-10 16:37:21 +03:00
static int cfi_atmel_lock ( struct mtd_info * mtd , loff_t ofs , uint64_t len )
2006-08-09 13:06:07 +04:00
{
return cfi_varsize_frob ( mtd , do_atmel_lock , ofs , len , NULL ) ;
}
2008-12-10 16:37:21 +03:00
static int cfi_atmel_unlock ( struct mtd_info * mtd , loff_t ofs , uint64_t len )
2006-08-09 13:06:07 +04:00
{
return cfi_varsize_frob ( mtd , do_atmel_unlock , ofs , len , NULL ) ;
}
2005-04-17 02:20:36 +04:00
static void cfi_amdstd_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 int cfi_amdstd_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 ] ;
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 ) {
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_amdstd_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
if ( chip - > state = = FL_PM_SUSPENDED ) {
chip - > state = FL_READY ;
map_write ( map , CMD ( 0xF0 ) , chip - > start ) ;
wake_up ( & chip - > wq ) ;
}
else
printk ( KERN_ERR " Argh. Chip not in PM_SUSPENDED state upon resume() \n " ) ;
2010-04-19 00:46:44 +04:00
mutex_unlock ( & chip - > mutex ) ;
2005-04-17 02:20:36 +04:00
}
}
2010-04-29 21:26:56 +04:00
/*
* Ensure that the flash device is put back into read array mode before
* unloading the driver or rebooting . On some systems , rebooting while
* the flash is in query / program / erase mode will prevent the CPU from
* fetching the bootloader code , requiring a hard reset or power cycle .
*/
static int cfi_amdstd_reset ( struct mtd_info * mtd )
{
struct map_info * map = mtd - > priv ;
struct cfi_private * cfi = map - > fldrv_priv ;
int i , ret ;
struct flchip * chip ;
for ( i = 0 ; i < cfi - > numchips ; i + + ) {
chip = & cfi - > chips [ i ] ;
mutex_lock ( & chip - > mutex ) ;
ret = get_chip ( map , chip , chip - > start , FL_SHUTDOWN ) ;
if ( ! ret ) {
map_write ( map , CMD ( 0xF0 ) , chip - > start ) ;
chip - > state = FL_SHUTDOWN ;
put_chip ( map , chip , chip - > start ) ;
}
mutex_unlock ( & chip - > mutex ) ;
}
return 0 ;
}
static int cfi_amdstd_reboot ( struct notifier_block * nb , unsigned long val ,
void * v )
{
struct mtd_info * mtd ;
mtd = container_of ( nb , struct mtd_info , reboot_notifier ) ;
cfi_amdstd_reset ( mtd ) ;
return NOTIFY_DONE ;
}
2005-04-17 02:20:36 +04:00
static void cfi_amdstd_destroy ( struct mtd_info * mtd )
{
struct map_info * map = mtd - > priv ;
struct cfi_private * cfi = map - > fldrv_priv ;
2005-11-07 12:01:27 +03:00
2010-04-29 21:26:56 +04:00
cfi_amdstd_reset ( mtd ) ;
unregister_reboot_notifier ( & mtd - > reboot_notifier ) ;
2005-04-17 02:20:36 +04:00
kfree ( cfi - > cmdset_priv ) ;
kfree ( cfi - > cfiq ) ;
kfree ( cfi ) ;
kfree ( mtd - > eraseregions ) ;
}
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Crossnet Co. <info@crossnet.co.jp> et al. " ) ;
MODULE_DESCRIPTION ( " MTD chip driver for AMD/Fujitsu flash chips " ) ;
2010-05-20 18:54:10 +04:00
MODULE_ALIAS ( " cfi_cmdset_0006 " ) ;
2010-05-20 18:54:05 +04:00
MODULE_ALIAS ( " cfi_cmdset_0701 " ) ;