2005-04-16 15:20:36 -07:00
/*======================================================================
drivers / mtd / maps / integrator - flash . c : ARM Integrator flash map driver
2005-11-07 11:15:40 +00:00
2005-04-16 15:20:36 -07:00
Copyright ( C ) 2000 ARM Limited
Copyright ( C ) 2003 Deep Blue Solutions Ltd .
2005-11-07 11:15:40 +00:00
2005-04-16 15:20:36 -07:00
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation ; either version 2 of the License , or
( at your option ) any later version .
2005-11-07 11:15:40 +00:00
2005-04-16 15:20:36 -07:00
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
2005-11-07 11:15:40 +00:00
2005-04-16 15:20:36 -07:00
You should have received a copy of the GNU General Public License
along with this program ; if not , write to the Free Software
Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
2005-11-07 11:15:40 +00:00
This is access code for flashes using ARM ' s flash partitioning
2005-04-16 15:20:36 -07:00
standards .
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
# include <linux/module.h>
# include <linux/types.h>
# include <linux/kernel.h>
# include <linux/slab.h>
# include <linux/ioport.h>
2005-10-29 19:07:23 +01:00
# include <linux/platform_device.h>
2005-04-16 15:20:36 -07:00
# include <linux/init.h>
2009-03-25 10:21:35 +00:00
# include <linux/io.h>
2005-04-16 15:20:36 -07:00
# include <linux/mtd/mtd.h>
# include <linux/mtd/map.h>
# include <linux/mtd/partitions.h>
2009-04-23 17:41:10 +01:00
# include <linux/mtd/concat.h>
2005-04-16 15:20:36 -07:00
# include <asm/mach/flash.h>
2008-08-05 16:14:15 +01:00
# include <mach/hardware.h>
2005-04-16 15:20:36 -07:00
# include <asm/system.h>
2009-04-23 17:41:10 +01:00
struct armflash_subdev_info {
2009-06-24 15:35:15 +01:00
char * name ;
2009-04-23 17:41:10 +01:00
struct mtd_info * mtd ;
struct map_info map ;
2005-04-16 15:20:36 -07:00
struct flash_platform_data * plat ;
2009-04-23 17:41:10 +01:00
} ;
struct armflash_info {
2005-04-16 15:20:36 -07:00
struct resource * res ;
struct mtd_partition * parts ;
struct mtd_info * mtd ;
2009-04-23 17:41:10 +01:00
int nr_subdev ;
struct armflash_subdev_info subdev [ 0 ] ;
2005-04-16 15:20:36 -07:00
} ;
static void armflash_set_vpp ( struct map_info * map , int on )
{
2009-04-23 17:41:10 +01:00
struct armflash_subdev_info * info =
container_of ( map , struct armflash_subdev_info , map ) ;
2005-04-16 15:20:36 -07:00
if ( info - > plat & & info - > plat - > set_vpp )
info - > plat - > set_vpp ( on ) ;
}
static const char * probes [ ] = { " cmdlinepart " , " RedBoot " , " afs " , NULL } ;
2009-04-23 17:41:10 +01:00
static int armflash_subdev_probe ( struct armflash_subdev_info * subdev ,
struct resource * res )
2005-04-16 15:20:36 -07:00
{
2009-04-23 17:41:10 +01:00
struct flash_platform_data * plat = subdev - > plat ;
resource_size_t size = res - > end - res - > start + 1 ;
2005-04-16 15:20:36 -07:00
void __iomem * base ;
2009-04-23 17:41:10 +01:00
int err = 0 ;
2005-04-16 15:20:36 -07:00
2009-04-23 17:41:10 +01:00
if ( ! request_mem_region ( res - > start , size , subdev - > name ) ) {
2005-04-16 15:20:36 -07:00
err = - EBUSY ;
2009-04-23 17:41:10 +01:00
goto out ;
2005-04-16 15:20:36 -07:00
}
base = ioremap ( res - > start , size ) ;
if ( ! base ) {
err = - ENOMEM ;
goto no_mem ;
}
/*
* look for CFI based flash parts fitted to this board
*/
2009-04-23 17:41:10 +01:00
subdev - > map . size = size ;
subdev - > map . bankwidth = plat - > width ;
subdev - > map . phys = res - > start ;
subdev - > map . virt = base ;
subdev - > map . name = subdev - > name ;
subdev - > map . set_vpp = armflash_set_vpp ;
2005-04-16 15:20:36 -07:00
2009-04-23 17:41:10 +01:00
simple_map_init ( & subdev - > map ) ;
2005-04-16 15:20:36 -07:00
/*
* Also , the CFI layer automatically works out what size
* of chips we have , and does the necessary identification
* for us automatically .
*/
2009-04-23 17:41:10 +01:00
subdev - > mtd = do_map_probe ( plat - > map_name , & subdev - > map ) ;
if ( ! subdev - > mtd ) {
2005-04-16 15:20:36 -07:00
err = - ENXIO ;
goto no_device ;
}
2009-04-23 17:41:10 +01:00
subdev - > mtd - > owner = THIS_MODULE ;
/* Successful? */
if ( err = = 0 )
return err ;
if ( subdev - > mtd )
map_destroy ( subdev - > mtd ) ;
no_device :
iounmap ( base ) ;
no_mem :
release_mem_region ( res - > start , size ) ;
out :
return err ;
}
static void armflash_subdev_remove ( struct armflash_subdev_info * subdev )
{
if ( subdev - > mtd )
map_destroy ( subdev - > mtd ) ;
if ( subdev - > map . virt )
iounmap ( subdev - > map . virt ) ;
2009-06-24 15:35:15 +01:00
kfree ( subdev - > name ) ;
subdev - > name = NULL ;
2009-04-23 17:41:10 +01:00
release_mem_region ( subdev - > map . phys , subdev - > map . size ) ;
}
static int armflash_probe ( struct platform_device * dev )
{
struct flash_platform_data * plat = dev - > dev . platform_data ;
unsigned int size ;
struct armflash_info * info ;
int i , nr , err ;
/* Count the number of devices */
for ( nr = 0 ; ; nr + + )
if ( ! platform_get_resource ( dev , IORESOURCE_MEM , nr ) )
break ;
if ( nr = = 0 ) {
err = - ENODEV ;
goto out ;
}
size = sizeof ( struct armflash_info ) +
sizeof ( struct armflash_subdev_info ) * nr ;
info = kzalloc ( size , GFP_KERNEL ) ;
if ( ! info ) {
err = - ENOMEM ;
goto out ;
}
if ( plat & & plat - > init ) {
err = plat - > init ( ) ;
if ( err )
goto no_resource ;
}
for ( i = 0 ; i < nr ; i + + ) {
struct armflash_subdev_info * subdev = & info - > subdev [ i ] ;
struct resource * res ;
res = platform_get_resource ( dev , IORESOURCE_MEM , i ) ;
if ( ! res )
break ;
if ( nr = = 1 )
/* No MTD concatenation, just use the default name */
2009-06-24 15:35:15 +01:00
subdev - > name = kstrdup ( dev_name ( & dev - > dev ) , GFP_KERNEL ) ;
2009-04-23 17:41:10 +01:00
else
2009-06-24 15:35:15 +01:00
subdev - > name = kasprintf ( GFP_KERNEL , " %s-%d " ,
dev_name ( & dev - > dev ) , i ) ;
if ( ! subdev - > name ) {
err = - ENOMEM ;
break ;
}
2009-04-23 17:41:10 +01:00
subdev - > plat = plat ;
err = armflash_subdev_probe ( subdev , res ) ;
2009-06-24 15:35:15 +01:00
if ( err ) {
kfree ( subdev - > name ) ;
subdev - > name = NULL ;
2009-04-23 17:41:10 +01:00
break ;
2009-06-24 15:35:15 +01:00
}
2009-04-23 17:41:10 +01:00
}
info - > nr_subdev = i ;
if ( err )
goto subdev_err ;
if ( info - > nr_subdev = = 1 )
info - > mtd = info - > subdev [ 0 ] . mtd ;
else if ( info - > nr_subdev > 1 ) {
# ifdef CONFIG_MTD_CONCAT
struct mtd_info * cdev [ info - > nr_subdev ] ;
/*
* We detected multiple devices . Concatenate them together .
*/
for ( i = 0 ; i < info - > nr_subdev ; i + + )
cdev [ i ] = info - > subdev [ i ] . mtd ;
info - > mtd = mtd_concat_create ( cdev , info - > nr_subdev ,
dev_name ( & dev - > dev ) ) ;
if ( info - > mtd = = NULL )
err = - ENXIO ;
# else
printk ( KERN_ERR " armflash: multiple devices found but "
" MTD concat support disabled. \n " ) ;
err = - ENXIO ;
# endif
}
if ( err < 0 )
goto cleanup ;
2005-04-16 15:20:36 -07:00
err = parse_mtd_partitions ( info - > mtd , probes , & info - > parts , 0 ) ;
if ( err > 0 ) {
err = add_mtd_partitions ( info - > mtd , info - > parts , err ) ;
if ( err )
printk ( KERN_ERR
" mtd partition registration failed: %d \n " , err ) ;
}
2009-04-23 17:41:10 +01:00
if ( err = = 0 ) {
2005-11-09 22:32:44 +00:00
platform_set_drvdata ( dev , info ) ;
2009-04-23 17:41:10 +01:00
return err ;
}
2005-04-16 15:20:36 -07:00
/*
2009-04-23 17:41:10 +01:00
* We got an error , free all resources .
2005-04-16 15:20:36 -07:00
*/
2009-04-23 17:41:10 +01:00
cleanup :
if ( info - > mtd ) {
del_mtd_partitions ( info - > mtd ) ;
# ifdef CONFIG_MTD_CONCAT
if ( info - > mtd ! = info - > subdev [ 0 ] . mtd )
mtd_concat_destroy ( info - > mtd ) ;
# endif
2005-04-16 15:20:36 -07:00
}
2009-04-23 17:41:10 +01:00
kfree ( info - > parts ) ;
subdev_err :
for ( i = info - > nr_subdev - 1 ; i > = 0 ; i - - )
armflash_subdev_remove ( & info - > subdev [ i ] ) ;
no_resource :
if ( plat & & plat - > exit )
plat - > exit ( ) ;
kfree ( info ) ;
2005-04-16 15:20:36 -07:00
out :
return err ;
}
2005-11-09 22:32:44 +00:00
static int armflash_remove ( struct platform_device * dev )
2005-04-16 15:20:36 -07:00
{
2005-11-09 22:32:44 +00:00
struct armflash_info * info = platform_get_drvdata ( dev ) ;
2009-04-23 17:41:10 +01:00
struct flash_platform_data * plat = dev - > dev . platform_data ;
int i ;
2005-04-16 15:20:36 -07:00
2005-11-09 22:32:44 +00:00
platform_set_drvdata ( dev , NULL ) ;
2005-04-16 15:20:36 -07:00
if ( info ) {
if ( info - > mtd ) {
del_mtd_partitions ( info - > mtd ) ;
2009-04-23 17:41:10 +01:00
# ifdef CONFIG_MTD_CONCAT
if ( info - > mtd ! = info - > subdev [ 0 ] . mtd )
mtd_concat_destroy ( info - > mtd ) ;
# endif
2005-04-16 15:20:36 -07:00
}
2005-11-07 01:01:27 -08:00
kfree ( info - > parts ) ;
2005-04-16 15:20:36 -07:00
2009-04-23 17:41:10 +01:00
for ( i = info - > nr_subdev - 1 ; i > = 0 ; i - - )
armflash_subdev_remove ( & info - > subdev [ i ] ) ;
2005-04-16 15:20:36 -07:00
2009-04-23 17:41:10 +01:00
if ( plat & & plat - > exit )
plat - > exit ( ) ;
2005-04-16 15:20:36 -07:00
kfree ( info ) ;
}
return 0 ;
}
2005-11-09 22:32:44 +00:00
static struct platform_driver armflash_driver = {
2005-04-16 15:20:36 -07:00
. probe = armflash_probe ,
. remove = armflash_remove ,
2005-11-09 22:32:44 +00:00
. driver = {
. name = " armflash " ,
2008-04-18 13:44:26 -07:00
. owner = THIS_MODULE ,
2005-11-09 22:32:44 +00:00
} ,
2005-04-16 15:20:36 -07:00
} ;
static int __init armflash_init ( void )
{
2005-11-09 22:32:44 +00:00
return platform_driver_register ( & armflash_driver ) ;
2005-04-16 15:20:36 -07:00
}
static void __exit armflash_exit ( void )
{
2005-11-09 22:32:44 +00:00
platform_driver_unregister ( & armflash_driver ) ;
2005-04-16 15:20:36 -07:00
}
module_init ( armflash_init ) ;
module_exit ( armflash_exit ) ;
MODULE_AUTHOR ( " ARM Ltd " ) ;
MODULE_DESCRIPTION ( " ARM Integrator CFI map driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
2008-04-18 13:44:26 -07:00
MODULE_ALIAS ( " platform:armflash " ) ;