2005-04-17 02:20:36 +04:00
/*
* Routines common to all CFI - type probes .
* ( C ) 2001 - 2003 Red Hat , Inc .
* GPL ' d
*/
# include <linux/kernel.h>
# include <linux/slab.h>
# include <linux/module.h>
# include <linux/mtd/mtd.h>
# include <linux/mtd/map.h>
# include <linux/mtd/cfi.h>
# include <linux/mtd/gen_probe.h>
static struct mtd_info * check_cmd_set ( struct map_info * , int ) ;
static struct cfi_private * genprobe_ident_chips ( struct map_info * map ,
struct chip_probe * cp ) ;
static int genprobe_new_chip ( struct map_info * map , struct chip_probe * cp ,
struct cfi_private * cfi ) ;
struct mtd_info * mtd_do_chip_probe ( struct map_info * map , struct chip_probe * cp )
{
struct mtd_info * mtd = NULL ;
struct cfi_private * cfi ;
/* First probe the map to see if we have CFI stuff there. */
cfi = genprobe_ident_chips ( map , cp ) ;
2005-11-07 14:15:37 +03:00
2005-04-17 02:20:36 +04:00
if ( ! cfi )
return NULL ;
map - > fldrv_priv = cfi ;
/* OK we liked it. Now find a driver for the command set it talks */
mtd = check_cmd_set ( map , 1 ) ; /* First the primary cmdset */
if ( ! mtd )
mtd = check_cmd_set ( map , 0 ) ; /* Then the secondary */
2005-11-07 14:15:37 +03:00
2006-05-14 04:40:50 +04:00
if ( mtd ) {
if ( mtd - > size > map - > size ) {
2006-05-18 01:03:10 +04:00
printk ( KERN_WARNING " Reducing visibility of %ldKiB chip to %ldKiB \n " ,
2006-11-30 11:17:38 +03:00
( unsigned long ) mtd - > size > > 10 ,
2006-05-14 04:40:50 +04:00
( unsigned long ) map - > size > > 10 ) ;
mtd - > size = map - > size ;
}
2005-04-17 02:20:36 +04:00
return mtd ;
2006-05-14 04:40:50 +04:00
}
2005-04-17 02:20:36 +04:00
printk ( KERN_WARNING " gen_probe: No supported Vendor Command Set found \n " ) ;
2005-11-07 14:15:37 +03:00
2005-04-17 02:20:36 +04:00
kfree ( cfi - > cfiq ) ;
kfree ( cfi ) ;
map - > fldrv_priv = NULL ;
return NULL ;
}
EXPORT_SYMBOL ( mtd_do_chip_probe ) ;
static struct cfi_private * genprobe_ident_chips ( struct map_info * map , struct chip_probe * cp )
{
struct cfi_private cfi ;
struct cfi_private * retcfi ;
unsigned long * chip_map ;
int i , j , mapsize ;
int max_chips ;
memset ( & cfi , 0 , sizeof ( cfi ) ) ;
2005-11-07 14:15:37 +03:00
/* Call the probetype-specific code with all permutations of
2005-04-17 02:20:36 +04:00
interleave and device type , etc . */
if ( ! genprobe_new_chip ( map , cp , & cfi ) ) {
/* The probe didn't like it */
2008-07-10 15:37:08 +04:00
pr_debug ( " %s: Found no %s device at location zero \n " ,
cp - > name , map - > name ) ;
2005-04-17 02:20:36 +04:00
return NULL ;
2005-11-07 14:15:37 +03:00
}
2005-04-17 02:20:36 +04:00
#if 0 /* Let the CFI probe routine do this sanity check. The Intel and AMD
probe routines won ' t ever return a broken CFI structure anyway ,
because they make them up themselves .
*/
if ( cfi . cfiq - > NumEraseRegions = = 0 ) {
printk ( KERN_WARNING " Number of erase regions is zero \n " ) ;
kfree ( cfi . cfiq ) ;
return NULL ;
}
# endif
cfi . chipshift = cfi . cfiq - > DevSize ;
if ( cfi_interleave_is_1 ( & cfi ) ) {
;
} else if ( cfi_interleave_is_2 ( & cfi ) ) {
cfi . chipshift + + ;
} else if ( cfi_interleave_is_4 ( ( & cfi ) ) ) {
cfi . chipshift + = 2 ;
} else if ( cfi_interleave_is_8 ( & cfi ) ) {
cfi . chipshift + = 3 ;
} else {
BUG ( ) ;
}
2005-11-07 14:15:37 +03:00
2005-04-17 02:20:36 +04:00
cfi . numchips = 1 ;
2005-11-07 14:15:37 +03:00
/*
* Allocate memory for bitmap of valid chips .
* Align bitmap storage size to full byte .
*/
2005-04-17 02:20:36 +04:00
max_chips = map - > size > > cfi . chipshift ;
2006-05-14 04:40:50 +04:00
if ( ! max_chips ) {
printk ( KERN_WARNING " NOR chip too large to fit in mapping. Attempting to cope... \n " ) ;
max_chips = 1 ;
}
2008-08-02 19:14:21 +04:00
mapsize = sizeof ( long ) * DIV_ROUND_UP ( max_chips , BITS_PER_LONG ) ;
2006-11-15 22:10:29 +03:00
chip_map = kzalloc ( mapsize , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! chip_map ) {
kfree ( cfi . cfiq ) ;
return NULL ;
}
set_bit ( 0 , chip_map ) ; /* Mark first chip valid */
/*
* Now probe for other chips , checking sensibly for aliases while
* we ' re at it . The new_chip probe above should have let the first
* chip in read mode .
*/
for ( i = 1 ; i < max_chips ; i + + ) {
cp - > probe_chip ( map , i < < cfi . chipshift , chip_map , & cfi ) ;
}
/*
2005-11-07 14:15:37 +03:00
* Now allocate the space for the structures we need to return to
2005-04-17 02:20:36 +04:00
* our caller , and copy the appropriate data into them .
*/
retcfi = kmalloc ( sizeof ( struct cfi_private ) + cfi . numchips * sizeof ( struct flchip ) , GFP_KERNEL ) ;
if ( ! retcfi ) {
kfree ( cfi . cfiq ) ;
kfree ( chip_map ) ;
return NULL ;
}
memcpy ( retcfi , & cfi , sizeof ( cfi ) ) ;
memset ( & retcfi - > chips [ 0 ] , 0 , sizeof ( struct flchip ) * cfi . numchips ) ;
for ( i = 0 , j = 0 ; ( j < cfi . numchips ) & & ( i < max_chips ) ; i + + ) {
if ( test_bit ( i , chip_map ) ) {
struct flchip * pchip = & retcfi - > chips [ j + + ] ;
pchip - > start = ( i < < cfi . chipshift ) ;
pchip - > state = FL_READY ;
init_waitqueue_head ( & pchip - > wq ) ;
2010-04-19 00:46:44 +04:00
mutex_init ( & pchip - > mutex ) ;
2005-04-17 02:20:36 +04:00
}
}
kfree ( chip_map ) ;
return retcfi ;
}
2005-11-07 14:15:37 +03:00
2005-04-17 02:20:36 +04:00
static int genprobe_new_chip ( struct map_info * map , struct chip_probe * cp ,
struct cfi_private * cfi )
{
int min_chips = ( map_bankwidth ( map ) / 4 ? : 1 ) ; /* At most 4-bytes wide. */
int max_chips = map_bankwidth ( map ) ; /* And minimum 1 */
int nr_chips , type ;
2005-01-25 02:49:54 +03:00
for ( nr_chips = max_chips ; nr_chips > = min_chips ; nr_chips > > = 1 ) {
2005-04-17 02:20:36 +04:00
if ( ! cfi_interleave_supported ( nr_chips ) )
continue ;
cfi - > interleave = nr_chips ;
/* Minimum device size. Don't look for one 8-bit device
in a 16 - bit bus , etc . */
type = map_bankwidth ( map ) / nr_chips ;
for ( ; type < = CFI_DEVICETYPE_X32 ; type < < = 1 ) {
cfi - > device_type = type ;
if ( cp - > probe_chip ( map , 0 , NULL , cfi ) )
return 1 ;
}
}
return 0 ;
}
typedef struct mtd_info * cfi_cmdset_fn_t ( struct map_info * , int ) ;
extern cfi_cmdset_fn_t cfi_cmdset_0001 ;
extern cfi_cmdset_fn_t cfi_cmdset_0002 ;
extern cfi_cmdset_fn_t cfi_cmdset_0020 ;
2005-11-07 14:15:37 +03:00
static inline struct mtd_info * cfi_cmdset_unknown ( struct map_info * map ,
2005-04-17 02:20:36 +04:00
int primary )
{
struct cfi_private * cfi = map - > fldrv_priv ;
__u16 type = primary ? cfi - > cfiq - > P_ID : cfi - > cfiq - > A_ID ;
2006-05-09 01:35:05 +04:00
# ifdef CONFIG_MODULES
2013-03-15 08:34:17 +04:00
char probename [ sizeof ( VMLINUX_SYMBOL_STR ( cfi_cmdset_ % 4.4 X ) ) ] ;
2005-04-17 02:20:36 +04:00
cfi_cmdset_fn_t * probe_function ;
2013-03-15 08:34:17 +04:00
sprintf ( probename , VMLINUX_SYMBOL_STR ( cfi_cmdset_ % 4.4 X ) , type ) ;
2005-11-07 14:15:37 +03:00
2006-05-20 05:41:34 +04:00
probe_function = __symbol_get ( probename ) ;
2006-05-09 01:35:05 +04:00
if ( ! probe_function ) {
2013-07-08 17:05:12 +04:00
request_module ( " cfi_cmdset_%4.4X " , type ) ;
2006-05-20 05:41:34 +04:00
probe_function = __symbol_get ( probename ) ;
2006-05-09 01:35:05 +04:00
}
2005-04-17 02:20:36 +04:00
if ( probe_function ) {
struct mtd_info * mtd ;
mtd = ( * probe_function ) ( map , primary ) ;
/* If it was happy, it'll have increased its own use count */
2006-05-09 01:35:05 +04:00
symbol_put_addr ( probe_function ) ;
2005-04-17 02:20:36 +04:00
return mtd ;
}
# endif
2006-05-09 01:35:05 +04:00
printk ( KERN_NOTICE " Support for command set %04X not present \n " , type ) ;
2005-04-17 02:20:36 +04:00
return NULL ;
}
static struct mtd_info * check_cmd_set ( struct map_info * map , int primary )
{
struct cfi_private * cfi = map - > fldrv_priv ;
__u16 type = primary ? cfi - > cfiq - > P_ID : cfi - > cfiq - > A_ID ;
2005-11-07 14:15:37 +03:00
2005-04-17 02:20:36 +04:00
if ( type = = P_ID_NONE | | type = = P_ID_RESERVED )
return NULL ;
switch ( type ) {
2006-05-09 01:35:05 +04:00
/* We need these for the !CONFIG_MODULES case,
because symbol_get ( ) doesn ' t work there */
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_MTD_CFI_INTELEXT
2010-04-24 19:58:07 +04:00
case P_ID_INTEL_EXT :
case P_ID_INTEL_STD :
case P_ID_INTEL_PERFORMANCE :
2005-04-17 02:20:36 +04:00
return cfi_cmdset_0001 ( map , primary ) ;
# endif
# ifdef CONFIG_MTD_CFI_AMDSTD
2010-04-24 19:58:07 +04:00
case P_ID_AMD_STD :
2010-04-24 19:58:22 +04:00
case P_ID_SST_OLD :
2010-05-20 18:54:10 +04:00
case P_ID_WINBOND :
2005-04-17 02:20:36 +04:00
return cfi_cmdset_0002 ( map , primary ) ;
# endif
# ifdef CONFIG_MTD_CFI_STAA
2010-04-24 19:58:07 +04:00
case P_ID_ST_ADV :
2005-04-17 02:20:36 +04:00
return cfi_cmdset_0020 ( map , primary ) ;
# endif
2006-05-09 01:35:05 +04:00
default :
return cfi_cmdset_unknown ( map , primary ) ;
2005-04-17 02:20:36 +04:00
}
}
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " David Woodhouse <dwmw2@infradead.org> " ) ;
MODULE_DESCRIPTION ( " Helper routines for flash chip probe code " ) ;