2005-04-17 02:20:36 +04:00
/*
* Flash memory access on SA11x0 based devices
2005-11-07 14:15:40 +03:00
*
2009-09-14 11:25:28 +04:00
* ( C ) 2000 Nicolas Pitre < nico @ fluxnic . net >
2005-04-17 02:20:36 +04:00
*/
# 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>
2009-03-25 13:21:35 +03:00
# include <linux/io.h>
2005-04-17 02:20:36 +04:00
# 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/sizes.h>
# include <asm/mach/flash.h>
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_info * mtd ;
int num_subdev ;
struct sa_subdev_info subdev [ 0 ] ;
} ;
2012-03-07 18:14:31 +04:00
static DEFINE_SPINLOCK ( sa1100_vpp_lock ) ;
static int sa1100_vpp_refcnt ;
2005-04-17 02:20:36 +04:00
static void sa1100_set_vpp ( struct map_info * map , int on )
{
struct sa_subdev_info * subdev = container_of ( map , struct sa_subdev_info , map ) ;
2012-03-07 18:14:31 +04:00
unsigned long flags ;
spin_lock_irqsave ( & sa1100_vpp_lock , flags ) ;
if ( on ) {
if ( + + sa1100_vpp_refcnt = = 1 ) /* first nested 'on' */
subdev - > plat - > set_vpp ( 1 ) ;
} else {
if ( - - sa1100_vpp_refcnt = = 0 ) /* last nested 'off' */
subdev - > plat - > set_vpp ( 0 ) ;
}
spin_unlock_irqrestore ( & sa1100_vpp_lock , flags ) ;
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 ;
2009-09-28 02:51:04 +04:00
printk ( KERN_INFO " SA1100 flash: CFI device at 0x%08lx, %uMiB, %d-bit \n " ,
phys , ( unsigned ) ( subdev - > mtd - > size > > 20 ) ,
2005-04-17 02:20:36 +04:00
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 ) {
2011-05-23 13:23:09 +04:00
mtd_device_unregister ( info - > mtd ) ;
2005-04-17 02:20:36 +04:00
if ( info - > mtd ! = info - > subdev [ 0 ] . mtd )
mtd_concat_destroy ( info - > mtd ) ;
}
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
}
2012-11-22 14:16:28 +04:00
static struct sa_info * 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 ) {
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 ;
}
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 ) ;
}
2013-03-12 12:46:37 +04:00
static const char * const part_probes [ ] = { " cmdlinepart " , " RedBoot " , NULL } ;
2005-04-17 02:20:36 +04:00
2012-11-19 22:23:07 +04:00
static int sa1100_mtd_probe ( struct platform_device * pdev )
2005-04-17 02:20:36 +04:00
{
2013-07-30 12:18:06 +04:00
struct flash_platform_data * plat = dev_get_platdata ( & pdev - > dev ) ;
2005-04-17 02:20:36 +04:00
struct sa_info * info ;
2011-06-02 18:00:06 +04:00
int err ;
2005-04-17 02:20:36 +04:00
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 .
*/
mtd: do not use plain 0 as NULL
The first 3 arguments of 'mtd_device_parse_register()' are pointers,
but many callers pass '0' instead of 'NULL'. Fix this globally. Thanks
to coccinelle for making it easy to do with the following semantic patch:
@@
expression mtd, types, parser_data, parts, nr_parts;
@@
(
-mtd_device_parse_register(mtd, 0, parser_data, parts, nr_parts)
+mtd_device_parse_register(mtd, NULL, parser_data, parts, nr_parts)
|
-mtd_device_parse_register(mtd, types, 0, parts, nr_parts)
+mtd_device_parse_register(mtd, types, NULL, parts, nr_parts)
|
-mtd_device_parse_register(mtd, types, parser_data, 0, nr_parts)
+mtd_device_parse_register(mtd, types, parser_data, NULL, nr_parts)
)
Signed-off-by: Artem Bityutskiy <artem.bityutskiy@linux.intel.com>
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
2012-03-09 21:24:26 +04:00
mtd_device_parse_register ( info - > mtd , part_probes , NULL , plat - > parts ,
plat - > nr_parts ) ;
2005-10-29 19:03:24 +04:00
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 ) ;
2013-07-30 12:18:06 +04:00
struct flash_platform_data * plat = dev_get_platdata ( & pdev - > dev ) ;
2005-10-29 18:57:20 +04:00
sa1100_destroy ( info , plat ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
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 ) ,
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
} ;
2011-11-27 16:45:03 +04:00
module_platform_driver ( sa1100_mtd_driver ) ;
2005-04-17 02:20:36 +04:00
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 " ) ;