2006-12-06 13:17:49 +03:00
/*
2007-09-20 11:22:25 +10:00
* Flash mappings described by the OF ( or flattened ) device tree
2006-12-06 13:17:49 +03:00
*
* Copyright ( C ) 2006 MontaVista Software Inc .
* Author : Vitaly Wool < vwool @ ru . mvista . com >
*
2007-09-07 13:23:53 +10:00
* Revised to handle newer style flash binding by :
* Copyright ( C ) 2007 David Gibson , IBM Corporation .
*
2006-12-06 13:17:49 +03: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 .
*/
# include <linux/module.h>
# include <linux/types.h>
# include <linux/init.h>
# include <linux/device.h>
# include <linux/mtd/mtd.h>
# include <linux/mtd/map.h>
# include <linux/mtd/partitions.h>
2007-09-20 11:22:25 +10:00
# include <linux/of.h>
# include <linux/of_platform.h>
2006-12-06 13:17:49 +03:00
2007-09-20 11:22:25 +10:00
struct of_flash {
2006-12-06 13:17:49 +03:00
struct mtd_info * mtd ;
struct map_info map ;
struct resource * res ;
# ifdef CONFIG_MTD_PARTITIONS
struct mtd_partition * parts ;
# endif
} ;
# ifdef CONFIG_MTD_PARTITIONS
2007-09-20 11:22:25 +10:00
# define OF_FLASH_PARTS(info) ((info)->parts)
2006-12-06 13:17:49 +03:00
2007-09-07 13:23:53 +10:00
static int parse_obsolete_partitions ( struct of_device * dev ,
2007-09-20 11:22:25 +10:00
struct of_flash * info ,
2007-09-07 13:23:53 +10:00
struct device_node * dp )
2006-12-06 13:17:49 +03:00
{
2007-09-07 13:23:53 +10:00
int i , plen , nr_parts ;
const struct {
u32 offset , len ;
} * part ;
const char * names ;
2006-12-06 13:17:49 +03:00
2007-09-07 13:23:53 +10:00
part = of_get_property ( dp , " partitions " , & plen ) ;
if ( ! part )
2007-09-20 11:22:25 +10:00
return 0 ; /* No partitions found */
2006-12-06 13:17:49 +03:00
2007-09-07 13:23:53 +10:00
dev_warn ( & dev - > dev , " Device tree uses obsolete partition map binding \n " ) ;
nr_parts = plen / sizeof ( part [ 0 ] ) ;
2007-09-20 11:22:25 +10:00
info - > parts = kzalloc ( nr_parts * sizeof ( * info - > parts ) , GFP_KERNEL ) ;
if ( ! info - > parts )
2007-09-07 13:23:53 +10:00
return - ENOMEM ;
2006-12-06 13:17:49 +03:00
2007-09-07 13:23:53 +10:00
names = of_get_property ( dp , " partition-names " , & plen ) ;
2006-12-06 13:17:49 +03:00
2007-09-07 13:23:53 +10:00
for ( i = 0 ; i < nr_parts ; i + + ) {
info - > parts [ i ] . offset = part - > offset ;
info - > parts [ i ] . size = part - > len & ~ 1 ;
if ( part - > len & 1 ) /* bit 0 set signifies read only partition */
info - > parts [ i ] . mask_flags = MTD_WRITEABLE ;
2006-12-06 13:17:49 +03:00
2007-09-07 13:23:53 +10:00
if ( names & & ( plen > 0 ) ) {
int len = strlen ( names ) + 1 ;
2006-12-06 13:17:49 +03:00
2007-09-07 13:23:53 +10:00
info - > parts [ i ] . name = ( char * ) names ;
2006-12-06 13:17:49 +03:00
plen - = len ;
2007-09-07 13:23:53 +10:00
names + = len ;
} else {
info - > parts [ i ] . name = " unnamed " ;
}
part + + ;
2006-12-06 13:17:49 +03:00
}
2007-09-07 13:23:53 +10:00
return nr_parts ;
2006-12-06 13:17:49 +03:00
}
2007-09-07 13:23:53 +10:00
# else /* MTD_PARTITIONS */
2007-09-20 11:22:25 +10:00
# define OF_FLASH_PARTS(info) (0)
# define parse_partitions(info, dev) (0)
2007-09-07 13:23:53 +10:00
# endif /* MTD_PARTITIONS */
2006-12-06 13:17:49 +03:00
2007-09-20 11:22:25 +10:00
static int of_flash_remove ( struct of_device * dev )
2006-12-06 13:17:49 +03:00
{
2007-09-20 11:22:25 +10:00
struct of_flash * info ;
2006-12-06 13:17:49 +03:00
info = dev_get_drvdata ( & dev - > dev ) ;
2007-09-20 11:22:25 +10:00
if ( ! info )
2006-12-06 13:17:49 +03:00
return 0 ;
dev_set_drvdata ( & dev - > dev , NULL ) ;
2007-09-20 11:22:25 +10:00
if ( info - > mtd ) {
if ( OF_FLASH_PARTS ( info ) ) {
2006-12-06 13:17:49 +03:00
del_mtd_partitions ( info - > mtd ) ;
2007-09-20 11:22:25 +10:00
kfree ( OF_FLASH_PARTS ( info ) ) ;
2006-12-06 13:17:49 +03:00
} else {
del_mtd_device ( info - > mtd ) ;
}
map_destroy ( info - > mtd ) ;
}
2007-09-20 11:22:25 +10:00
if ( info - > map . virt )
2006-12-06 13:17:49 +03:00
iounmap ( info - > map . virt ) ;
2007-09-20 11:22:25 +10:00
if ( info - > res ) {
2006-12-06 13:17:49 +03:00
release_resource ( info - > res ) ;
kfree ( info - > res ) ;
}
return 0 ;
}
2007-09-07 13:23:53 +10:00
/* Helper function to handle probing of the obsolete "direct-mapped"
* compatible binding , which has an extra " probe-type " property
* describing the type of flash probe necessary . */
static struct mtd_info * __devinit obsolete_probe ( struct of_device * dev ,
struct map_info * map )
2006-12-06 13:17:49 +03:00
{
struct device_node * dp = dev - > node ;
const char * of_probe ;
2007-09-07 13:23:53 +10:00
struct mtd_info * mtd ;
static const char * rom_probe_types [ ]
= { " cfi_probe " , " jedec_probe " , " map_rom " } ;
int i ;
dev_warn ( & dev - > dev , " Device tree uses obsolete \" direct-mapped \" "
" flash binding \n " ) ;
of_probe = of_get_property ( dp , " probe-type " , NULL ) ;
if ( ! of_probe ) {
for ( i = 0 ; i < ARRAY_SIZE ( rom_probe_types ) ; i + + ) {
mtd = do_map_probe ( rom_probe_types [ i ] , map ) ;
if ( mtd )
return mtd ;
}
return NULL ;
} else if ( strcmp ( of_probe , " CFI " ) = = 0 ) {
return do_map_probe ( " cfi_probe " , map ) ;
} else if ( strcmp ( of_probe , " JEDEC " ) = = 0 ) {
return do_map_probe ( " jedec_probe " , map ) ;
} else {
if ( strcmp ( of_probe , " ROM " ) ! = 0 )
2007-09-20 11:22:25 +10:00
dev_warn ( & dev - > dev , " obsolete_probe: don't know probe "
" type '%s', mapping as rom \n " , of_probe ) ;
2007-09-07 13:23:53 +10:00
return do_map_probe ( " mtd_rom " , map ) ;
}
}
2007-09-20 11:22:25 +10:00
static int __devinit of_flash_probe ( struct of_device * dev ,
const struct of_device_id * match )
2006-12-06 13:17:49 +03:00
{
2008-01-15 17:54:43 -06:00
# ifdef CONFIG_MTD_PARTITIONS
static const char * part_probe_types [ ]
= { " cmdlinepart " , " RedBoot " , NULL } ;
# endif
2006-12-06 13:17:49 +03:00
struct device_node * dp = dev - > node ;
struct resource res ;
2007-09-20 11:22:25 +10:00
struct of_flash * info ;
const char * probe_type = match - > data ;
2006-12-06 13:17:49 +03:00
const u32 * width ;
int err ;
2007-09-20 11:22:25 +10:00
err = - ENXIO ;
2006-12-06 13:17:49 +03:00
if ( of_address_to_resource ( dp , 0 , & res ) ) {
2007-09-20 11:22:25 +10:00
dev_err ( & dev - > dev , " Can't get IO address from device tree \n " ) ;
2006-12-06 13:17:49 +03:00
goto err_out ;
}
2007-09-20 11:22:25 +10:00
dev_dbg ( & dev - > dev , " of_flash device: %.8llx-%.8llx \n " ,
( unsigned long long ) res . start , ( unsigned long long ) res . end ) ;
2006-12-06 13:17:49 +03:00
2007-09-20 11:22:25 +10:00
err = - ENOMEM ;
info = kzalloc ( sizeof ( * info ) , GFP_KERNEL ) ;
if ( ! info )
2006-12-06 13:17:49 +03:00
goto err_out ;
dev_set_drvdata ( & dev - > dev , info ) ;
2007-09-20 11:22:25 +10:00
err = - EBUSY ;
2006-12-06 13:17:49 +03:00
info - > res = request_mem_region ( res . start , res . end - res . start + 1 ,
2007-09-20 11:22:25 +10:00
dev - > dev . bus_id ) ;
if ( ! info - > res )
2006-12-06 13:17:49 +03:00
goto err_out ;
2007-09-20 11:22:25 +10:00
err = - ENXIO ;
2007-05-01 13:54:02 +10:00
width = of_get_property ( dp , " bank-width " , NULL ) ;
2007-09-20 11:22:25 +10:00
if ( ! width ) {
dev_err ( & dev - > dev , " Can't get bank width from device tree \n " ) ;
2006-12-06 13:17:49 +03:00
goto err_out ;
}
info - > map . name = dev - > dev . bus_id ;
info - > map . phys = res . start ;
info - > map . size = res . end - res . start + 1 ;
info - > map . bankwidth = * width ;
2007-09-20 11:22:25 +10:00
err = - ENOMEM ;
2006-12-06 13:17:49 +03:00
info - > map . virt = ioremap ( info - > map . phys , info - > map . size ) ;
2007-09-20 11:22:25 +10:00
if ( ! info - > map . virt ) {
dev_err ( & dev - > dev , " Failed to ioremap() flash region \n " ) ;
2006-12-06 13:17:49 +03:00
goto err_out ;
}
simple_map_init ( & info - > map ) ;
2007-09-07 13:23:53 +10:00
if ( probe_type )
info - > mtd = do_map_probe ( probe_type , & info - > map ) ;
else
info - > mtd = obsolete_probe ( dev , & info - > map ) ;
2007-09-20 11:22:25 +10:00
err = - ENXIO ;
if ( ! info - > mtd ) {
dev_err ( & dev - > dev , " do_map_probe() failed \n " ) ;
2006-12-06 13:17:49 +03:00
goto err_out ;
}
info - > mtd - > owner = THIS_MODULE ;
2008-01-15 17:54:43 -06:00
# ifdef CONFIG_MTD_PARTITIONS
/* First look for RedBoot table or partitions on the command
* line , these take precedence over device tree information */
err = parse_mtd_partitions ( info - > mtd , part_probe_types ,
& info - > parts , 0 ) ;
2007-09-20 11:22:25 +10:00
if ( err < 0 )
2008-01-15 17:54:43 -06:00
return err ;
# ifdef CONFIG_MTD_OF_PARTS
if ( err = = 0 ) {
err = of_mtd_parse_partitions ( & dev - > dev , info - > mtd ,
dp , & info - > parts ) ;
if ( err < 0 )
return err ;
}
# endif
if ( err = = 0 ) {
err = parse_obsolete_partitions ( dev , info , dp ) ;
if ( err < 0 )
return err ;
}
2006-12-06 13:17:49 +03:00
2007-09-20 11:22:25 +10:00
if ( err > 0 )
2008-01-15 17:54:43 -06:00
add_mtd_partitions ( info - > mtd , info - > parts , err ) ;
2007-09-20 11:22:25 +10:00
else
2008-01-15 17:54:43 -06:00
# endif
2007-09-20 11:22:25 +10:00
add_mtd_device ( info - > mtd ) ;
2006-12-06 13:17:49 +03:00
return 0 ;
err_out :
2007-09-20 11:22:25 +10:00
of_flash_remove ( dev ) ;
2006-12-06 13:17:49 +03:00
return err ;
}
2007-09-20 11:22:25 +10:00
static struct of_device_id of_flash_match [ ] = {
2007-09-07 13:23:53 +10:00
{
. compatible = " cfi-flash " ,
. data = ( void * ) " cfi_probe " ,
} ,
{
/* FIXME: JEDEC chips can't be safely and reliably
* probed , although the mtd code gets it right in
* practice most of the time . We should use the
* vendor and device ids specified by the binding to
* bypass the heuristic probe code , but the mtd layer
* provides , at present , no interface for doing so
* : ( . */
. compatible = " jedec-flash " ,
. data = ( void * ) " jedec_probe " ,
} ,
2006-12-06 13:17:49 +03:00
{
. type = " rom " ,
. compatible = " direct-mapped "
} ,
{ } ,
} ;
2007-09-20 11:22:25 +10:00
MODULE_DEVICE_TABLE ( of , of_flash_match ) ;
2006-12-06 13:17:49 +03:00
2007-09-20 11:22:25 +10:00
static struct of_platform_driver of_flash_driver = {
. name = " of-flash " ,
. match_table = of_flash_match ,
. probe = of_flash_probe ,
. remove = of_flash_remove ,
2006-12-06 13:17:49 +03:00
} ;
2007-09-20 11:22:25 +10:00
static int __init of_flash_init ( void )
2006-12-06 13:17:49 +03:00
{
2007-09-20 11:22:25 +10:00
return of_register_platform_driver ( & of_flash_driver ) ;
2006-12-06 13:17:49 +03:00
}
2007-09-20 11:22:25 +10:00
static void __exit of_flash_exit ( void )
2006-12-06 13:17:49 +03:00
{
2007-09-20 11:22:25 +10:00
of_unregister_platform_driver ( & of_flash_driver ) ;
2006-12-06 13:17:49 +03:00
}
2007-09-20 11:22:25 +10:00
module_init ( of_flash_init ) ;
module_exit ( of_flash_exit ) ;
2006-12-06 13:17:49 +03:00
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Vitaly Wool <vwool@ru.mvista.com> " ) ;
2007-09-20 11:22:25 +10:00
MODULE_DESCRIPTION ( " Device tree based MTD map driver " ) ;