2005-11-07 11:15:37 +00:00
/*
2005-04-16 15:20:36 -07:00
Common Flash Interface probe code .
( C ) 2000 Red Hat . GPL ' d .
*/
# include <linux/module.h>
# include <linux/types.h>
# include <linux/kernel.h>
# include <linux/init.h>
# include <asm/io.h>
# include <asm/byteorder.h>
# include <linux/errno.h>
# include <linux/slab.h>
# include <linux/interrupt.h>
# include <linux/mtd/xip.h>
# include <linux/mtd/map.h>
# include <linux/mtd/cfi.h>
# include <linux/mtd/gen_probe.h>
2005-11-07 11:15:37 +00:00
//#define DEBUG_CFI
2005-04-16 15:20:36 -07:00
# ifdef DEBUG_CFI
static void print_cfi_ident ( struct cfi_ident * ) ;
# endif
static int cfi_probe_chip ( struct map_info * map , __u32 base ,
unsigned long * chip_map , struct cfi_private * cfi ) ;
static int cfi_chip_setup ( struct map_info * map , struct cfi_private * cfi ) ;
struct mtd_info * cfi_probe ( struct map_info * map ) ;
# ifdef CONFIG_MTD_XIP
/* only needed for short periods, so this is rather simple */
# define xip_disable() local_irq_disable()
# define xip_allowed(base, map) \
do { \
( void ) map_read ( map , base ) ; \
2008-02-27 01:42:39 +02:00
xip_iprefetch ( ) ; \
2005-04-16 15:20:36 -07:00
local_irq_enable ( ) ; \
} while ( 0 )
# define xip_enable(base, map, cfi) \
do { \
2008-08-07 11:55:07 +01:00
cfi_qry_mode_off ( base , map , cfi ) ; \
2005-04-16 15:20:36 -07:00
xip_allowed ( base , map ) ; \
} while ( 0 )
# define xip_disable_qry(base, map, cfi) \
do { \
xip_disable ( ) ; \
2008-08-07 11:55:07 +01:00
cfi_qry_mode_on ( base , map , cfi ) ; \
2005-04-16 15:20:36 -07:00
} while ( 0 )
# else
# define xip_disable() do { } while (0)
# define xip_allowed(base, map) do { } while (0)
# define xip_enable(base, map, cfi) do { } while (0)
# define xip_disable_qry(base, map, cfi) do { } while (0)
# endif
2018-04-30 11:34:49 -05:00
/*
* This fixup occurs immediately after reading the CFI structure and can affect
* the number of chips detected , unlike cfi_fixup , which occurs after an
* mtd_info structure has been created for the chip .
*/
struct cfi_early_fixup {
uint16_t mfr ;
uint16_t id ;
void ( * fixup ) ( struct cfi_private * cfi ) ;
} ;
static void cfi_early_fixup ( struct cfi_private * cfi ,
const struct cfi_early_fixup * fixups )
{
const struct cfi_early_fixup * f ;
for ( f = fixups ; f - > fixup ; f + + ) {
if ( ( ( f - > mfr = = CFI_MFR_ANY ) | | ( f - > mfr = = cfi - > mfr ) ) & &
( ( f - > id = = CFI_ID_ANY ) | | ( f - > id = = cfi - > id ) ) ) {
f - > fixup ( cfi ) ;
}
}
}
2005-04-16 15:20:36 -07:00
/* check for QRY.
in : interleave , type , mode
ret : table index , < 0 for error
*/
static int __xipram cfi_probe_chip ( struct map_info * map , __u32 base ,
unsigned long * chip_map , struct cfi_private * cfi )
{
int i ;
2005-11-07 11:15:37 +00:00
2005-04-16 15:20:36 -07:00
if ( ( base + 0 ) > = map - > size ) {
printk ( KERN_NOTICE
" Probe at base[0x00](0x%08lx) past the end of the map(0x%08lx) \n " ,
( unsigned long ) base , map - > size - 1 ) ;
return 0 ;
}
if ( ( base + 0xff ) > = map - > size ) {
printk ( KERN_NOTICE
" Probe at base[0x55](0x%08lx) past the end of the map(0x%08lx) \n " ,
( unsigned long ) base + 0x55 , map - > size - 1 ) ;
return 0 ;
}
xip_disable ( ) ;
2008-08-07 11:55:07 +01:00
if ( ! cfi_qry_mode_on ( base , map , cfi ) ) {
2005-04-16 15:20:36 -07:00
xip_enable ( base , map , cfi ) ;
return 0 ;
}
if ( ! cfi - > numchips ) {
2005-11-07 11:15:37 +00:00
/* This is the first time we're called. Set up the CFI
2005-04-16 15:20:36 -07:00
stuff accordingly and return */
return cfi_chip_setup ( map , cfi ) ;
}
/* Check each previous chip to see if it's an alias */
for ( i = 0 ; i < ( base > > cfi - > chipshift ) ; i + + ) {
unsigned long start ;
if ( ! test_bit ( i , chip_map ) ) {
/* Skip location; no valid chip at this address */
2005-11-07 11:15:37 +00:00
continue ;
2005-04-16 15:20:36 -07:00
}
start = i < < cfi - > chipshift ;
/* This chip should be in read mode if it's one
we ' ve already touched . */
2008-08-07 11:55:07 +01:00
if ( cfi_qry_present ( map , start , cfi ) ) {
2005-11-07 11:15:37 +00:00
/* Eep. This chip also had the QRY marker.
2005-04-16 15:20:36 -07:00
* Is it an alias for the new one ? */
2008-08-07 11:55:07 +01:00
cfi_qry_mode_off ( start , map , cfi ) ;
2005-04-16 15:20:36 -07:00
/* If the QRY marker goes away, it's an alias */
2008-08-07 11:55:07 +01:00
if ( ! cfi_qry_present ( map , start , cfi ) ) {
2005-04-16 15:20:36 -07:00
xip_allowed ( base , map ) ;
printk ( KERN_DEBUG " %s: Found an alias at 0x%x for the chip at 0x%lx \n " ,
map - > name , base , start ) ;
return 0 ;
}
2005-11-07 11:15:37 +00:00
/* Yes, it's actually got QRY for data. Most
2005-04-16 15:20:36 -07:00
* unfortunate . Stick the new chip in read mode
* too and if it ' s the same , assume it ' s an alias . */
/* FIXME: Use other modes to do a proper check */
2008-08-07 11:55:07 +01:00
cfi_qry_mode_off ( base , map , cfi ) ;
2005-11-07 11:15:37 +00:00
2008-08-07 11:55:07 +01:00
if ( cfi_qry_present ( map , base , cfi ) ) {
2005-04-16 15:20:36 -07:00
xip_allowed ( base , map ) ;
printk ( KERN_DEBUG " %s: Found an alias at 0x%x for the chip at 0x%lx \n " ,
map - > name , base , start ) ;
return 0 ;
}
}
}
2005-11-07 11:15:37 +00:00
2005-04-16 15:20:36 -07:00
/* OK, if we got to here, then none of the previous chips appear to
be aliases for the current one . */
set_bit ( ( base > > cfi - > chipshift ) , chip_map ) ; /* Update chip map */
cfi - > numchips + + ;
2005-11-07 11:15:37 +00:00
2005-04-16 15:20:36 -07:00
/* Put it back into Read Mode */
2008-08-07 11:55:07 +01:00
cfi_qry_mode_off ( base , map , cfi ) ;
2005-04-16 15:20:36 -07:00
xip_allowed ( base , map ) ;
printk ( KERN_INFO " %s: Found %d x%d devices at 0x%x in %d-bit bank \n " ,
map - > name , cfi - > interleave , cfi - > device_type * 8 , base ,
map - > bankwidth * 8 ) ;
2005-11-07 11:15:37 +00:00
2005-04-16 15:20:36 -07:00
return 1 ;
}
2018-04-30 11:35:07 -05:00
static void fixup_s70gl02gs_chips ( struct cfi_private * cfi )
{
/*
* S70GL02GS flash reports a single 256 MiB chip , but is really made up
* of two 128 MiB chips with 1024 sectors each .
*/
cfi - > cfiq - > DevSize = 27 ;
cfi - > cfiq - > EraseRegionInfo [ 0 ] = 0x20003ff ;
pr_warn ( " Bad S70GL02GS CFI data; adjust to detect 2 chips \n " ) ;
}
2018-04-30 11:34:49 -05:00
static const struct cfi_early_fixup cfi_early_fixup_table [ ] = {
2018-04-30 11:35:07 -05:00
{ CFI_MFR_AMD , 0x4801 , fixup_s70gl02gs_chips } ,
2018-04-30 11:34:49 -05:00
{ } ,
} ;
2005-11-07 11:15:37 +00:00
static int __xipram cfi_chip_setup ( struct map_info * map ,
2005-04-16 15:20:36 -07:00
struct cfi_private * cfi )
{
int ofs_factor = cfi - > interleave * cfi - > device_type ;
__u32 base = 0 ;
int num_erase_regions = cfi_read_query ( map , base + ( 0x10 + 28 ) * ofs_factor ) ;
int i ;
2010-04-24 17:57:57 +02:00
int addr_unlock1 = 0x555 , addr_unlock2 = 0x2AA ;
2005-04-16 15:20:36 -07:00
xip_enable ( base , map , cfi ) ;
# ifdef DEBUG_CFI
printk ( " Number of erase regions: %d \n " , num_erase_regions ) ;
# endif
if ( ! num_erase_regions )
return 0 ;
cfi - > cfiq = kmalloc ( sizeof ( struct cfi_ident ) + num_erase_regions * 4 , GFP_KERNEL ) ;
2014-02-06 15:19:35 +09:00
if ( ! cfi - > cfiq )
2005-04-16 15:20:36 -07:00
return 0 ;
2005-11-07 11:15:37 +00:00
memset ( cfi - > cfiq , 0 , sizeof ( struct cfi_ident ) ) ;
2005-04-16 15:20:36 -07:00
cfi - > cfi_mode = CFI_MODE_CFI ;
2005-11-07 11:15:37 +00:00
2010-10-26 10:45:23 +01:00
cfi - > sector_erase_cmd = CMD ( 0x30 ) ;
2005-04-16 15:20:36 -07:00
/* Read the CFI info structure */
xip_disable_qry ( base , map , cfi ) ;
for ( i = 0 ; i < ( sizeof ( struct cfi_ident ) + num_erase_regions * 4 ) ; i + + )
( ( unsigned char * ) cfi - > cfiq ) [ i ] = cfi_read_query ( map , base + ( 0x10 + i ) * ofs_factor ) ;
/* Do any necessary byteswapping */
cfi - > cfiq - > P_ID = le16_to_cpu ( cfi - > cfiq - > P_ID ) ;
cfi - > cfiq - > P_ADR = le16_to_cpu ( cfi - > cfiq - > P_ADR ) ;
cfi - > cfiq - > A_ID = le16_to_cpu ( cfi - > cfiq - > A_ID ) ;
cfi - > cfiq - > A_ADR = le16_to_cpu ( cfi - > cfiq - > A_ADR ) ;
cfi - > cfiq - > InterfaceDesc = le16_to_cpu ( cfi - > cfiq - > InterfaceDesc ) ;
cfi - > cfiq - > MaxBufWriteSize = le16_to_cpu ( cfi - > cfiq - > MaxBufWriteSize ) ;
# ifdef DEBUG_CFI
/* Dump the information therein */
print_cfi_ident ( cfi - > cfiq ) ;
# endif
for ( i = 0 ; i < cfi - > cfiq - > NumEraseRegions ; i + + ) {
cfi - > cfiq - > EraseRegionInfo [ i ] = le32_to_cpu ( cfi - > cfiq - > EraseRegionInfo [ i ] ) ;
2005-11-07 11:15:37 +00:00
# ifdef DEBUG_CFI
2005-04-16 15:20:36 -07:00
printk ( " Erase Region #%d: BlockSize 0x%4.4X bytes, %d blocks \n " ,
2005-11-07 11:15:37 +00:00
i , ( cfi - > cfiq - > EraseRegionInfo [ i ] > > 8 ) & ~ 0xff ,
2005-04-16 15:20:36 -07:00
( cfi - > cfiq - > EraseRegionInfo [ i ] & 0xffff ) + 1 ) ;
# endif
}
2010-04-24 17:58:02 +02:00
if ( cfi - > cfiq - > P_ID = = P_ID_SST_OLD ) {
addr_unlock1 = 0x5555 ;
addr_unlock2 = 0x2AAA ;
}
2010-04-24 17:57:52 +02:00
/*
* Note we put the device back into Read Mode BEFORE going into Auto
* Select Mode , as some devices support nesting of modes , others
* don ' t . This way should always work .
* On cmdset 0001 the writes of 0xaa and 0x55 are not needed , and
* so should be treated as nops or illegal ( and so put the device
* back into Read Mode , which is a nop in this case ) .
*/
cfi_send_gen_cmd ( 0xf0 , 0 , base , map , cfi , cfi - > device_type , NULL ) ;
2010-04-24 17:57:57 +02:00
cfi_send_gen_cmd ( 0xaa , addr_unlock1 , base , map , cfi , cfi - > device_type , NULL ) ;
cfi_send_gen_cmd ( 0x55 , addr_unlock2 , base , map , cfi , cfi - > device_type , NULL ) ;
cfi_send_gen_cmd ( 0x90 , addr_unlock1 , base , map , cfi , cfi - > device_type , NULL ) ;
2010-04-24 17:57:52 +02:00
cfi - > mfr = cfi_read_query16 ( map , base ) ;
cfi - > id = cfi_read_query16 ( map , base + ofs_factor ) ;
/* Get AMD/Spansion extended JEDEC ID */
if ( cfi - > mfr = = CFI_MFR_AMD & & ( cfi - > id & 0xff ) = = 0x7e )
cfi - > id = cfi_read_query ( map , base + 0xe * ofs_factor ) < < 8 |
cfi_read_query ( map , base + 0xf * ofs_factor ) ;
/* Put it back into Read Mode */
cfi_qry_mode_off ( base , map , cfi ) ;
xip_allowed ( base , map ) ;
2018-04-30 11:34:49 -05:00
cfi_early_fixup ( cfi , cfi_early_fixup_table ) ;
2010-06-29 10:32:37 +02:00
printk ( KERN_INFO " %s: Found %d x%d devices at 0x%x in %d-bit bank. Manufacturer ID %#08x Chip ID %#08x \n " ,
2005-04-16 15:20:36 -07:00
map - > name , cfi - > interleave , cfi - > device_type * 8 , base ,
2010-06-29 10:32:37 +02:00
map - > bankwidth * 8 , cfi - > mfr , cfi - > id ) ;
2005-04-16 15:20:36 -07:00
return 1 ;
}
# ifdef DEBUG_CFI
2005-11-07 11:15:37 +00:00
static char * vendorname ( __u16 vendor )
2005-04-16 15:20:36 -07:00
{
switch ( vendor ) {
case P_ID_NONE :
return " None " ;
2005-11-07 11:15:37 +00:00
2005-04-16 15:20:36 -07:00
case P_ID_INTEL_EXT :
return " Intel/Sharp Extended " ;
2005-11-07 11:15:37 +00:00
2005-04-16 15:20:36 -07:00
case P_ID_AMD_STD :
return " AMD/Fujitsu Standard " ;
2005-11-07 11:15:37 +00:00
2005-04-16 15:20:36 -07:00
case P_ID_INTEL_STD :
return " Intel/Sharp Standard " ;
2005-11-07 11:15:37 +00:00
2005-04-16 15:20:36 -07:00
case P_ID_AMD_EXT :
return " AMD/Fujitsu Extended " ;
case P_ID_WINBOND :
return " Winbond Standard " ;
2005-11-07 11:15:37 +00:00
2005-04-16 15:20:36 -07:00
case P_ID_ST_ADV :
return " ST Advanced " ;
case P_ID_MITSUBISHI_STD :
return " Mitsubishi Standard " ;
2005-11-07 11:15:37 +00:00
2005-04-16 15:20:36 -07:00
case P_ID_MITSUBISHI_EXT :
return " Mitsubishi Extended " ;
case P_ID_SST_PAGE :
return " SST Page Write " ;
2010-04-24 17:58:02 +02:00
case P_ID_SST_OLD :
return " SST 39VF160x/39VF320x " ;
2005-04-16 15:20:36 -07:00
case P_ID_INTEL_PERFORMANCE :
return " Intel Performance Code " ;
2005-11-07 11:15:37 +00:00
2005-04-16 15:20:36 -07:00
case P_ID_INTEL_DATA :
return " Intel Data " ;
2005-11-07 11:15:37 +00:00
2005-04-16 15:20:36 -07:00
case P_ID_RESERVED :
return " Not Allowed / Reserved for Future Use " ;
2005-11-07 11:15:37 +00:00
2005-04-16 15:20:36 -07:00
default :
return " Unknown " ;
}
}
static void print_cfi_ident ( struct cfi_ident * cfip )
{
#if 0
if ( cfip - > qry [ 0 ] ! = ' Q ' | | cfip - > qry [ 1 ] ! = ' R ' | | cfip - > qry [ 2 ] ! = ' Y ' ) {
printk ( " Invalid CFI ident structure. \n " ) ;
return ;
2005-11-07 11:15:37 +00:00
}
# endif
2005-04-16 15:20:36 -07:00
printk ( " Primary Vendor Command Set: %4.4X (%s) \n " , cfip - > P_ID , vendorname ( cfip - > P_ID ) ) ;
if ( cfip - > P_ADR )
printk ( " Primary Algorithm Table at %4.4X \n " , cfip - > P_ADR ) ;
else
printk ( " No Primary Algorithm Table \n " ) ;
2005-11-07 11:15:37 +00:00
2005-04-16 15:20:36 -07:00
printk ( " Alternative Vendor Command Set: %4.4X (%s) \n " , cfip - > A_ID , vendorname ( cfip - > A_ID ) ) ;
if ( cfip - > A_ADR )
printk ( " Alternate Algorithm Table at %4.4X \n " , cfip - > A_ADR ) ;
else
printk ( " No Alternate Algorithm Table \n " ) ;
2005-11-07 11:15:37 +00:00
2005-04-16 15:20:36 -07:00
printk ( " Vcc Minimum: %2d.%d V \n " , cfip - > VccMin > > 4 , cfip - > VccMin & 0xf ) ;
printk ( " Vcc Maximum: %2d.%d V \n " , cfip - > VccMax > > 4 , cfip - > VccMax & 0xf ) ;
if ( cfip - > VppMin ) {
printk ( " Vpp Minimum: %2d.%d V \n " , cfip - > VppMin > > 4 , cfip - > VppMin & 0xf ) ;
printk ( " Vpp Maximum: %2d.%d V \n " , cfip - > VppMax > > 4 , cfip - > VppMax & 0xf ) ;
}
else
printk ( " No Vpp line \n " ) ;
2005-11-07 11:15:37 +00:00
2006-05-14 01:51:54 +01:00
printk ( " Typical byte/word write timeout: %d µs \n " , 1 < < cfip - > WordWriteTimeoutTyp ) ;
printk ( " Maximum byte/word write timeout: %d µs \n " , ( 1 < < cfip - > WordWriteTimeoutMax ) * ( 1 < < cfip - > WordWriteTimeoutTyp ) ) ;
2005-11-07 11:15:37 +00:00
2005-04-16 15:20:36 -07:00
if ( cfip - > BufWriteTimeoutTyp | | cfip - > BufWriteTimeoutMax ) {
2006-05-14 01:51:54 +01:00
printk ( " Typical full buffer write timeout: %d µs \n " , 1 < < cfip - > BufWriteTimeoutTyp ) ;
printk ( " Maximum full buffer write timeout: %d µs \n " , ( 1 < < cfip - > BufWriteTimeoutMax ) * ( 1 < < cfip - > BufWriteTimeoutTyp ) ) ;
2005-04-16 15:20:36 -07:00
}
else
printk ( " Full buffer write not supported \n " ) ;
2005-11-07 11:15:37 +00:00
2005-04-16 15:20:36 -07:00
printk ( " Typical block erase timeout: %d ms \n " , 1 < < cfip - > BlockEraseTimeoutTyp ) ;
printk ( " Maximum block erase timeout: %d ms \n " , ( 1 < < cfip - > BlockEraseTimeoutMax ) * ( 1 < < cfip - > BlockEraseTimeoutTyp ) ) ;
if ( cfip - > ChipEraseTimeoutTyp | | cfip - > ChipEraseTimeoutMax ) {
2005-11-07 11:15:37 +00:00
printk ( " Typical chip erase timeout: %d ms \n " , 1 < < cfip - > ChipEraseTimeoutTyp ) ;
2005-04-16 15:20:36 -07:00
printk ( " Maximum chip erase timeout: %d ms \n " , ( 1 < < cfip - > ChipEraseTimeoutMax ) * ( 1 < < cfip - > ChipEraseTimeoutTyp ) ) ;
}
else
printk ( " Chip erase not supported \n " ) ;
2005-11-07 11:15:37 +00:00
2005-04-16 15:20:36 -07:00
printk ( " Device size: 0x%X bytes (%d MiB) \n " , 1 < < cfip - > DevSize , 1 < < ( cfip - > DevSize - 20 ) ) ;
printk ( " Flash Device Interface description: 0x%4.4X \n " , cfip - > InterfaceDesc ) ;
switch ( cfip - > InterfaceDesc ) {
2007-11-26 18:55:18 +01:00
case CFI_INTERFACE_X8_ASYNC :
2005-04-16 15:20:36 -07:00
printk ( " - x8-only asynchronous interface \n " ) ;
break ;
2005-11-07 11:15:37 +00:00
2007-11-26 18:55:18 +01:00
case CFI_INTERFACE_X16_ASYNC :
2005-04-16 15:20:36 -07:00
printk ( " - x16-only asynchronous interface \n " ) ;
break ;
2005-11-07 11:15:37 +00:00
2007-11-26 18:55:18 +01:00
case CFI_INTERFACE_X8_BY_X16_ASYNC :
2005-04-16 15:20:36 -07:00
printk ( " - supports x8 and x16 via BYTE# with asynchronous interface \n " ) ;
break ;
2005-11-07 11:15:37 +00:00
2007-11-26 18:55:18 +01:00
case CFI_INTERFACE_X32_ASYNC :
2005-04-16 15:20:36 -07:00
printk ( " - x32-only asynchronous interface \n " ) ;
break ;
2005-11-07 11:15:37 +00:00
2007-11-26 18:55:18 +01:00
case CFI_INTERFACE_X16_BY_X32_ASYNC :
2005-04-16 15:20:36 -07:00
printk ( " - supports x16 and x32 via Word# with asynchronous interface \n " ) ;
break ;
2005-11-07 11:15:37 +00:00
2007-11-26 18:55:18 +01:00
case CFI_INTERFACE_NOT_ALLOWED :
2005-04-16 15:20:36 -07:00
printk ( " - Not Allowed / Reserved \n " ) ;
break ;
2005-11-07 11:15:37 +00:00
2005-04-16 15:20:36 -07:00
default :
printk ( " - Unknown \n " ) ;
break ;
}
2005-11-07 11:15:37 +00:00
2005-04-16 15:20:36 -07:00
printk ( " Max. bytes in buffer write: 0x%x \n " , 1 < < cfip - > MaxBufWriteSize ) ;
printk ( " Number of Erase Block Regions: %d \n " , cfip - > NumEraseRegions ) ;
2005-11-07 11:15:37 +00:00
2005-04-16 15:20:36 -07:00
}
# endif /* DEBUG_CFI */
static struct chip_probe cfi_chip_probe = {
. name = " CFI " ,
. probe_chip = cfi_probe_chip
} ;
struct mtd_info * cfi_probe ( struct map_info * map )
{
/*
* Just use the generic probe stuff to call our CFI - specific
* chip_probe routine in all the possible permutations , etc .
*/
return mtd_do_chip_probe ( map , & cfi_chip_probe ) ;
}
static struct mtd_chip_driver cfi_chipdrv = {
. probe = cfi_probe ,
. name = " cfi_probe " ,
. module = THIS_MODULE
} ;
2005-11-29 14:49:38 +00:00
static int __init cfi_probe_init ( void )
2005-04-16 15:20:36 -07:00
{
register_mtd_chip_driver ( & cfi_chipdrv ) ;
return 0 ;
}
static void __exit cfi_probe_exit ( void )
{
unregister_mtd_chip_driver ( & cfi_chipdrv ) ;
}
module_init ( cfi_probe_init ) ;
module_exit ( cfi_probe_exit ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " David Woodhouse <dwmw2@infradead.org> et al. " ) ;
MODULE_DESCRIPTION ( " Probe code for CFI-compliant flash chips " ) ;