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>
2009-04-16 14:10:45 +02:00
# include <linux/mtd/concat.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
2009-04-16 14:10:45 +02:00
struct of_flash_list {
struct mtd_info * mtd ;
struct map_info map ;
struct resource * res ;
} ;
2007-09-20 11:22:25 +10:00
struct of_flash {
2009-04-16 14:10:45 +02:00
struct mtd_info * cmtd ;
2006-12-06 13:17:49 +03:00
# ifdef CONFIG_MTD_PARTITIONS
struct mtd_partition * parts ;
# endif
2009-04-16 14:10:45 +02:00
int list_size ; /* number of elements in of_flash_list */
struct of_flash_list list [ 0 ] ;
2006-12-06 13:17:49 +03:00
} ;
# 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 ;
2009-04-16 14:10:45 +02:00
int i ;
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 ) ;
2009-04-16 14:10:45 +02:00
# ifdef CONFIG_MTD_CONCAT
if ( info - > cmtd ! = info - > list [ 0 ] . mtd ) {
del_mtd_device ( info - > cmtd ) ;
mtd_concat_destroy ( info - > cmtd ) ;
}
# endif
if ( info - > cmtd ) {
2007-09-20 11:22:25 +10:00
if ( OF_FLASH_PARTS ( info ) ) {
2009-04-16 14:10:45 +02:00
del_mtd_partitions ( info - > cmtd ) ;
2007-09-20 11:22:25 +10:00
kfree ( OF_FLASH_PARTS ( info ) ) ;
2006-12-06 13:17:49 +03:00
} else {
2009-04-16 14:10:45 +02:00
del_mtd_device ( info - > cmtd ) ;
2006-12-06 13:17:49 +03:00
}
}
2009-04-16 14:10:45 +02:00
for ( i = 0 ; i < info - > list_size ; i + + ) {
if ( info - > list [ i ] . mtd )
map_destroy ( info - > list [ i ] . mtd ) ;
2006-12-06 13:17:49 +03:00
2009-04-16 14:10:45 +02:00
if ( info - > list [ i ] . map . virt )
iounmap ( info - > list [ i ] . map . virt ) ;
if ( info - > list [ i ] . res ) {
release_resource ( info - > list [ i ] . res ) ;
kfree ( info - > list [ i ] . res ) ;
}
2006-12-06 13:17:49 +03:00
}
2009-04-16 14:10:45 +02:00
kfree ( info ) ;
2006-12-06 13:17:49 +03:00
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 ;
2009-04-16 14:10:45 +02:00
int i ;
int count ;
const u32 * p ;
int reg_tuple_size ;
struct mtd_info * * mtd_list = NULL ;
reg_tuple_size = ( of_n_addr_cells ( dp ) + of_n_size_cells ( dp ) ) * sizeof ( u32 ) ;
/*
* Get number of " reg " tuples . Scan for MTD devices on area ' s
* described by each " reg " region . This makes it possible ( including
* the concat support ) to support the Intel P30 48F 4400 chips which
* consists internally of 2 non - identical NOR chips on one die .
*/
p = of_get_property ( dp , " reg " , & count ) ;
if ( count % reg_tuple_size ! = 0 ) {
dev_err ( & dev - > dev , " Malformed reg property on %s \n " ,
dev - > node - > full_name ) ;
err = - EINVAL ;
2006-12-06 13:17:49 +03:00
goto err_out ;
}
2009-04-16 14:10:45 +02:00
count / = reg_tuple_size ;
2006-12-06 13:17:49 +03:00
2007-09-20 11:22:25 +10:00
err = - ENOMEM ;
2009-04-16 14:10:45 +02:00
info = kzalloc ( sizeof ( struct of_flash ) +
sizeof ( struct of_flash_list ) * count , GFP_KERNEL ) ;
if ( ! info )
goto err_out ;
mtd_list = kzalloc ( sizeof ( struct mtd_info ) * count , GFP_KERNEL ) ;
2007-09-20 11:22:25 +10:00
if ( ! info )
2006-12-06 13:17:49 +03:00
goto err_out ;
dev_set_drvdata ( & dev - > dev , info ) ;
2009-04-16 14:10:45 +02:00
for ( i = 0 ; i < count ; i + + ) {
err = - ENXIO ;
if ( of_address_to_resource ( dp , i , & res ) ) {
dev_err ( & dev - > dev , " Can't get IO address from device "
" tree \n " ) ;
goto err_out ;
}
2006-12-06 13:17:49 +03:00
2009-04-16 14:10:45 +02:00
dev_dbg ( & dev - > dev , " of_flash device: %.8llx-%.8llx \n " ,
( unsigned long long ) res . start ,
( unsigned long long ) res . end ) ;
err = - EBUSY ;
info - > list [ i ] . res = request_mem_region ( res . start , res . end -
res . start + 1 ,
dev_name ( & dev - > dev ) ) ;
if ( ! info - > list [ i ] . res )
goto err_out ;
err = - ENXIO ;
width = of_get_property ( dp , " bank-width " , NULL ) ;
if ( ! width ) {
dev_err ( & dev - > dev , " Can't get bank width from device "
" tree \n " ) ;
goto err_out ;
}
2006-12-06 13:17:49 +03:00
2009-04-16 14:10:45 +02:00
info - > list [ i ] . map . name = dev_name ( & dev - > dev ) ;
info - > list [ i ] . map . phys = res . start ;
info - > list [ i ] . map . size = res . end - res . start + 1 ;
info - > list [ i ] . map . bankwidth = * width ;
err = - ENOMEM ;
info - > list [ i ] . map . virt = ioremap ( info - > list [ i ] . map . phys ,
info - > list [ i ] . map . size ) ;
if ( ! info - > list [ i ] . map . virt ) {
dev_err ( & dev - > dev , " Failed to ioremap() flash "
" region \n " ) ;
goto err_out ;
}
2006-12-06 13:17:49 +03:00
2009-04-16 14:10:45 +02:00
simple_map_init ( & info - > list [ i ] . map ) ;
2006-12-06 13:17:49 +03:00
2009-04-16 14:10:45 +02:00
if ( probe_type ) {
info - > list [ i ] . mtd = do_map_probe ( probe_type ,
& info - > list [ i ] . map ) ;
} else {
info - > list [ i ] . mtd = obsolete_probe ( dev ,
& info - > list [ i ] . map ) ;
}
mtd_list [ i ] = info - > list [ i ] . mtd ;
2006-12-06 13:17:49 +03:00
2009-04-16 14:10:45 +02:00
err = - ENXIO ;
if ( ! info - > list [ i ] . mtd ) {
dev_err ( & dev - > dev , " do_map_probe() failed \n " ) ;
goto err_out ;
} else {
info - > list_size + + ;
}
info - > list [ i ] . mtd - > owner = THIS_MODULE ;
info - > list [ i ] . mtd - > dev . parent = & dev - > dev ;
}
2007-09-07 13:23:53 +10:00
2009-04-16 14:10:45 +02:00
err = 0 ;
if ( info - > list_size = = 1 ) {
info - > cmtd = info - > list [ 0 ] . mtd ;
} else if ( info - > list_size > 1 ) {
/*
* We detected multiple devices . Concatenate them together .
*/
# ifdef CONFIG_MTD_CONCAT
info - > cmtd = mtd_concat_create ( mtd_list , info - > list_size ,
dev_name ( & dev - > dev ) ) ;
if ( info - > cmtd = = NULL )
err = - ENXIO ;
# else
printk ( KERN_ERR " physmap_of: multiple devices "
" found but MTD concat support disabled. \n " ) ;
err = - ENXIO ;
# endif
2006-12-06 13:17:49 +03:00
}
2009-04-16 14:10:45 +02:00
if ( err )
goto err_out ;
2006-12-06 13:17:49 +03:00
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 */
2009-04-16 14:10:45 +02:00
err = parse_mtd_partitions ( info - > cmtd , 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 ) {
2008-10-12 16:18:36 +02:00
err = of_mtd_parse_partitions ( & dev - > dev , dp , & info - > parts ) ;
2008-01-15 17:54:43 -06:00
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 )
2009-04-16 14:10:45 +02:00
add_mtd_partitions ( info - > cmtd , info - > parts , err ) ;
2007-09-20 11:22:25 +10:00
else
2008-01-15 17:54:43 -06:00
# endif
2009-04-16 14:10:45 +02:00
add_mtd_device ( info - > cmtd ) ;
kfree ( mtd_list ) ;
2006-12-06 13:17:49 +03:00
return 0 ;
err_out :
2009-04-16 14:10:45 +02:00
kfree ( mtd_list ) ;
2007-09-20 11:22:25 +10:00
of_flash_remove ( dev ) ;
2009-04-16 14:10:45 +02:00
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 " ) ;