2005-04-17 02:20:36 +04:00
/*
* Flash memory access on SA11x0 based devices
2005-11-07 14:15:40 +03:00
*
2005-04-17 02:20:36 +04:00
* ( C ) 2000 Nicolas Pitre < nico @ cam . org >
*/
# include <linux/module.h>
# include <linux/types.h>
# include <linux/ioport.h>
# include <linux/kernel.h>
# include <linux/init.h>
# include <linux/errno.h>
# include <linux/slab.h>
2005-10-29 22:07:23 +04:00
# include <linux/platform_device.h>
2005-04-17 02:20:36 +04:00
# include <linux/err.h>
# include <linux/mtd/mtd.h>
# include <linux/mtd/map.h>
# include <linux/mtd/partitions.h>
# include <linux/mtd/concat.h>
2008-08-05 19:14:15 +04:00
# include <mach/hardware.h>
2005-04-17 02:20:36 +04:00
# include <asm/io.h>
# include <asm/sizes.h>
# include <asm/mach/flash.h>
#if 0
/*
* This is here for documentation purposes only - until these people
* submit their machine types . It will be gone January 2005.
*/
static struct mtd_partition consus_partitions [ ] = {
{
. name = " Consus boot firmware " ,
. offset = 0 ,
. size = 0x00040000 ,
. mask_flags = MTD_WRITABLE , /* force read-only */
} , {
. name = " Consus kernel " ,
. offset = 0x00040000 ,
. size = 0x00100000 ,
. mask_flags = 0 ,
} , {
. name = " Consus disk " ,
. offset = 0x00140000 ,
/* The rest (up to 16M) for jffs. We could put 0 and
make it find the size automatically , but right now
i have 32 megs . jffs will use all 32 megs if given
the chance , and this leads to horrible problems
when you try to re - flash the image because blob
won ' t erase the whole partition . */
. size = 0x01000000 - 0x00140000 ,
. mask_flags = 0 ,
} , {
/* this disk is a secondary disk, which can be used as
needed , for simplicity , make it the size of the other
consus partition , although realistically it could be
the remainder of the disk ( depending on the file
system used ) */
. name = " Consus disk2 " ,
. offset = 0x01000000 ,
. size = 0x01000000 - 0x00140000 ,
. mask_flags = 0 ,
}
} ;
/* Frodo has 2 x 16M 28F128J3A flash chips in bank 0: */
static struct mtd_partition frodo_partitions [ ] =
{
{
. name = " bootloader " ,
. size = 0x00040000 ,
. offset = 0x00000000 ,
. mask_flags = MTD_WRITEABLE
} , {
. name = " bootloader params " ,
. size = 0x00040000 ,
. offset = MTDPART_OFS_APPEND ,
. mask_flags = MTD_WRITEABLE
} , {
. name = " kernel " ,
. size = 0x00100000 ,
. offset = MTDPART_OFS_APPEND ,
. mask_flags = MTD_WRITEABLE
} , {
. name = " ramdisk " ,
. size = 0x00400000 ,
. offset = MTDPART_OFS_APPEND ,
. mask_flags = MTD_WRITEABLE
} , {
. name = " file system " ,
. size = MTDPART_SIZ_FULL ,
. offset = MTDPART_OFS_APPEND
}
} ;
static struct mtd_partition jornada56x_partitions [ ] = {
{
. name = " bootldr " ,
. size = 0x00040000 ,
. offset = 0 ,
. mask_flags = MTD_WRITEABLE ,
} , {
. name = " rootfs " ,
. size = MTDPART_SIZ_FULL ,
. offset = MTDPART_OFS_APPEND ,
}
} ;
static void jornada56x_set_vpp ( int vpp )
{
if ( vpp )
GPSR = GPIO_GPIO26 ;
else
GPCR = GPIO_GPIO26 ;
GPDR | = GPIO_GPIO26 ;
}
/*
* Machine Phys Size set_vpp
* Consus : SA1100_CS0_PHYS SZ_32M
* Frodo : SA1100_CS0_PHYS SZ_32M
* Jornada56x : SA1100_CS0_PHYS SZ_32M jornada56x_set_vpp
*/
# endif
struct sa_subdev_info {
char name [ 16 ] ;
struct map_info map ;
struct mtd_info * mtd ;
2005-10-29 18:51:14 +04:00
struct flash_platform_data * plat ;
2005-04-17 02:20:36 +04:00
} ;
struct sa_info {
struct mtd_partition * parts ;
struct mtd_info * mtd ;
int num_subdev ;
2005-10-29 19:03:24 +04:00
unsigned int nr_parts ;
2005-04-17 02:20:36 +04:00
struct sa_subdev_info subdev [ 0 ] ;
} ;
static void sa1100_set_vpp ( struct map_info * map , int on )
{
struct sa_subdev_info * subdev = container_of ( map , struct sa_subdev_info , map ) ;
2005-10-29 18:51:14 +04:00
subdev - > plat - > set_vpp ( on ) ;
2005-04-17 02:20:36 +04:00
}
static void sa1100_destroy_subdev ( struct sa_subdev_info * subdev )
{
if ( subdev - > mtd )
map_destroy ( subdev - > mtd ) ;
if ( subdev - > map . virt )
iounmap ( subdev - > map . virt ) ;
release_mem_region ( subdev - > map . phys , subdev - > map . size ) ;
}
static int sa1100_probe_subdev ( struct sa_subdev_info * subdev , struct resource * res )
{
unsigned long phys ;
unsigned int size ;
int ret ;
phys = res - > start ;
size = res - > end - phys + 1 ;
/*
* Retrieve the bankwidth from the MSC registers .
* We currently only implement CS0 and CS1 here .
*/
switch ( phys ) {
default :
printk ( KERN_WARNING " SA1100 flash: unknown base address "
" 0x%08lx, assuming CS0 \n " , phys ) ;
case SA1100_CS0_PHYS :
subdev - > map . bankwidth = ( MSC0 & MSC_RBW ) ? 2 : 4 ;
break ;
case SA1100_CS1_PHYS :
subdev - > map . bankwidth = ( ( MSC0 > > 16 ) & MSC_RBW ) ? 2 : 4 ;
break ;
}
if ( ! request_mem_region ( phys , size , subdev - > name ) ) {
ret = - EBUSY ;
goto out ;
}
2005-10-29 18:51:14 +04:00
if ( subdev - > plat - > set_vpp )
2005-04-17 02:20:36 +04:00
subdev - > map . set_vpp = sa1100_set_vpp ;
subdev - > map . phys = phys ;
subdev - > map . size = size ;
subdev - > map . virt = ioremap ( phys , size ) ;
if ( ! subdev - > map . virt ) {
ret = - ENOMEM ;
goto err ;
}
simple_map_init ( & subdev - > map ) ;
/*
* Now let ' s probe for the actual flash . Do it here since
* specific machine settings might have been set above .
*/
2005-10-29 18:51:14 +04:00
subdev - > mtd = do_map_probe ( subdev - > plat - > map_name , & subdev - > map ) ;
2005-04-17 02:20:36 +04:00
if ( subdev - > mtd = = NULL ) {
ret = - ENXIO ;
goto err ;
}
subdev - > mtd - > owner = THIS_MODULE ;
printk ( KERN_INFO " SA1100 flash: CFI device at 0x%08lx, %dMiB, "
" %d-bit \n " , phys , subdev - > mtd - > size > > 20 ,
subdev - > map . bankwidth * 8 ) ;
return 0 ;
err :
sa1100_destroy_subdev ( subdev ) ;
out :
return ret ;
}
2005-10-29 18:57:20 +04:00
static void sa1100_destroy ( struct sa_info * info , struct flash_platform_data * plat )
2005-04-17 02:20:36 +04:00
{
int i ;
if ( info - > mtd ) {
2005-10-29 19:03:24 +04:00
if ( info - > nr_parts = = 0 )
del_mtd_device ( info - > mtd ) ;
# ifdef CONFIG_MTD_PARTITIONS
else
del_mtd_partitions ( info - > mtd ) ;
# endif
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_MTD_CONCAT
if ( info - > mtd ! = info - > subdev [ 0 ] . mtd )
mtd_concat_destroy ( info - > mtd ) ;
# endif
}
2005-11-07 12:01:27 +03:00
kfree ( info - > parts ) ;
2005-04-17 02:20:36 +04:00
for ( i = info - > num_subdev - 1 ; i > = 0 ; i - - )
sa1100_destroy_subdev ( & info - > subdev [ i ] ) ;
kfree ( info ) ;
2005-10-29 18:57:20 +04:00
if ( plat - > exit )
plat - > exit ( ) ;
2005-04-17 02:20:36 +04:00
}
static struct sa_info * __init
2005-10-29 18:51:14 +04:00
sa1100_setup_mtd ( struct platform_device * pdev , struct flash_platform_data * plat )
2005-04-17 02:20:36 +04:00
{
struct sa_info * info ;
int nr , size , i , ret = 0 ;
/*
* Count number of devices .
*/
for ( nr = 0 ; ; nr + + )
if ( ! platform_get_resource ( pdev , IORESOURCE_MEM , nr ) )
break ;
if ( nr = = 0 ) {
ret = - ENODEV ;
goto out ;
}
size = sizeof ( struct sa_info ) + sizeof ( struct sa_subdev_info ) * nr ;
/*
* Allocate the map_info structs in one go .
*/
2006-11-15 22:10:29 +03:00
info = kzalloc ( size , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! info ) {
ret = - ENOMEM ;
goto out ;
}
2005-10-29 18:57:20 +04:00
if ( plat - > init ) {
ret = plat - > init ( ) ;
if ( ret )
goto err ;
}
2005-04-17 02:20:36 +04:00
/*
* Claim and then map the memory regions .
*/
for ( i = 0 ; i < nr ; i + + ) {
struct sa_subdev_info * subdev = & info - > subdev [ i ] ;
struct resource * res ;
res = platform_get_resource ( pdev , IORESOURCE_MEM , i ) ;
if ( ! res )
break ;
subdev - > map . name = subdev - > name ;
2005-10-29 19:08:31 +04:00
sprintf ( subdev - > name , " %s-%d " , plat - > name , i ) ;
2005-10-29 18:51:14 +04:00
subdev - > plat = plat ;
2005-04-17 02:20:36 +04:00
ret = sa1100_probe_subdev ( subdev , res ) ;
if ( ret )
break ;
}
info - > num_subdev = i ;
/*
* ENXIO is special . It means we didn ' t find a chip when we probed .
*/
if ( ret ! = 0 & & ! ( ret = = - ENXIO & & info - > num_subdev > 0 ) )
goto err ;
/*
* If we found one device , don ' t bother with concat support . If
* we found multiple devices , use concat if we have it available ,
* otherwise fail . Either way , it ' ll be called " sa1100 " .
*/
if ( info - > num_subdev = = 1 ) {
2005-10-29 19:08:31 +04:00
strcpy ( info - > subdev [ 0 ] . name , plat - > name ) ;
2005-04-17 02:20:36 +04:00
info - > mtd = info - > subdev [ 0 ] . mtd ;
ret = 0 ;
} else if ( info - > num_subdev > 1 ) {
# ifdef CONFIG_MTD_CONCAT
struct mtd_info * cdev [ nr ] ;
/*
* We detected multiple devices . Concatenate them together .
*/
for ( i = 0 ; i < info - > num_subdev ; i + + )
cdev [ i ] = info - > subdev [ i ] . mtd ;
info - > mtd = mtd_concat_create ( cdev , info - > num_subdev ,
2005-10-29 19:08:31 +04:00
plat - > name ) ;
2005-04-17 02:20:36 +04:00
if ( info - > mtd = = NULL )
ret = - ENXIO ;
# else
printk ( KERN_ERR " SA1100 flash: multiple devices "
" found but MTD concat support disabled. \n " ) ;
ret = - ENXIO ;
# endif
}
if ( ret = = 0 )
return info ;
err :
2005-10-29 18:57:20 +04:00
sa1100_destroy ( info , plat ) ;
2005-04-17 02:20:36 +04:00
out :
return ERR_PTR ( ret ) ;
}
static const char * part_probes [ ] = { " cmdlinepart " , " RedBoot " , NULL } ;
2005-11-10 01:32:44 +03:00
static int __init sa1100_mtd_probe ( struct platform_device * pdev )
2005-04-17 02:20:36 +04:00
{
2005-10-29 18:51:14 +04:00
struct flash_platform_data * plat = pdev - > dev . platform_data ;
2005-04-17 02:20:36 +04:00
struct mtd_partition * parts ;
const char * part_type = NULL ;
struct sa_info * info ;
int err , nr_parts = 0 ;
2005-10-29 18:51:14 +04:00
if ( ! plat )
2005-04-17 02:20:36 +04:00
return - ENODEV ;
2005-10-29 18:51:14 +04:00
info = sa1100_setup_mtd ( pdev , plat ) ;
2005-04-17 02:20:36 +04:00
if ( IS_ERR ( info ) ) {
err = PTR_ERR ( info ) ;
goto out ;
}
/*
* Partition selection stuff .
*/
# ifdef CONFIG_MTD_PARTITIONS
nr_parts = parse_mtd_partitions ( info - > mtd , part_probes , & parts , 0 ) ;
if ( nr_parts > 0 ) {
info - > parts = parts ;
part_type = " dynamic " ;
} else
# endif
{
2005-10-29 18:51:14 +04:00
parts = plat - > parts ;
nr_parts = plat - > nr_parts ;
2005-04-17 02:20:36 +04:00
part_type = " static " ;
}
if ( nr_parts = = 0 ) {
printk ( KERN_NOTICE " SA1100 flash: no partition info "
" available, registering whole flash \n " ) ;
add_mtd_device ( info - > mtd ) ;
} else {
printk ( KERN_NOTICE " SA1100 flash: using %s partition "
" definition \n " , part_type ) ;
add_mtd_partitions ( info - > mtd , parts , nr_parts ) ;
}
2005-10-29 19:03:24 +04:00
info - > nr_parts = nr_parts ;
2005-11-10 01:32:44 +03:00
platform_set_drvdata ( pdev , info ) ;
2005-04-17 02:20:36 +04:00
err = 0 ;
out :
return err ;
}
2005-11-10 01:32:44 +03:00
static int __exit sa1100_mtd_remove ( struct platform_device * pdev )
2005-04-17 02:20:36 +04:00
{
2005-11-10 01:32:44 +03:00
struct sa_info * info = platform_get_drvdata ( pdev ) ;
struct flash_platform_data * plat = pdev - > dev . platform_data ;
2005-10-29 18:57:20 +04:00
2005-11-10 01:32:44 +03:00
platform_set_drvdata ( pdev , NULL ) ;
2005-10-29 18:57:20 +04:00
sa1100_destroy ( info , plat ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
# ifdef CONFIG_PM
2005-11-10 01:32:44 +03:00
static int sa1100_mtd_suspend ( struct platform_device * dev , pm_message_t state )
2005-04-17 02:20:36 +04:00
{
2005-11-10 01:32:44 +03:00
struct sa_info * info = platform_get_drvdata ( dev ) ;
2005-04-17 02:20:36 +04:00
int ret = 0 ;
2005-10-28 20:52:56 +04:00
if ( info )
2005-04-17 02:20:36 +04:00
ret = info - > mtd - > suspend ( info - > mtd ) ;
return ret ;
}
2005-11-10 01:32:44 +03:00
static int sa1100_mtd_resume ( struct platform_device * dev )
2005-04-17 02:20:36 +04:00
{
2005-11-10 01:32:44 +03:00
struct sa_info * info = platform_get_drvdata ( dev ) ;
2005-10-28 20:52:56 +04:00
if ( info )
2005-04-17 02:20:36 +04:00
info - > mtd - > resume ( info - > mtd ) ;
return 0 ;
}
2005-10-29 19:14:08 +04:00
2005-11-10 01:32:44 +03:00
static void sa1100_mtd_shutdown ( struct platform_device * dev )
2005-10-29 19:14:08 +04:00
{
2005-11-10 01:32:44 +03:00
struct sa_info * info = platform_get_drvdata ( dev ) ;
2005-10-29 19:14:08 +04:00
if ( info & & info - > mtd - > suspend ( info - > mtd ) = = 0 )
info - > mtd - > resume ( info - > mtd ) ;
}
2005-04-17 02:20:36 +04:00
# else
# define sa1100_mtd_suspend NULL
# define sa1100_mtd_resume NULL
2005-10-29 19:14:08 +04:00
# define sa1100_mtd_shutdown NULL
2005-04-17 02:20:36 +04:00
# endif
2005-11-10 01:32:44 +03:00
static struct platform_driver sa1100_mtd_driver = {
2005-04-17 02:20:36 +04:00
. probe = sa1100_mtd_probe ,
. remove = __exit_p ( sa1100_mtd_remove ) ,
. suspend = sa1100_mtd_suspend ,
. resume = sa1100_mtd_resume ,
2005-10-29 19:14:08 +04:00
. shutdown = sa1100_mtd_shutdown ,
2005-11-10 01:32:44 +03:00
. driver = {
2009-01-31 03:21:58 +03:00
. name = " sa1100-mtd " ,
2008-04-19 00:44:26 +04:00
. owner = THIS_MODULE ,
2005-11-10 01:32:44 +03:00
} ,
2005-04-17 02:20:36 +04:00
} ;
static int __init sa1100_mtd_init ( void )
{
2005-11-10 01:32:44 +03:00
return platform_driver_register ( & sa1100_mtd_driver ) ;
2005-04-17 02:20:36 +04:00
}
static void __exit sa1100_mtd_exit ( void )
{
2005-11-10 01:32:44 +03:00
platform_driver_unregister ( & sa1100_mtd_driver ) ;
2005-04-17 02:20:36 +04:00
}
module_init ( sa1100_mtd_init ) ;
module_exit ( sa1100_mtd_exit ) ;
MODULE_AUTHOR ( " Nicolas Pitre " ) ;
MODULE_DESCRIPTION ( " SA1100 CFI map driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
2009-01-31 03:21:58 +03:00
MODULE_ALIAS ( " platform:sa1100-mtd " ) ;