2006-12-06 13:17:49 +03:00
/*
* Normal mappings of chips in physical memory for OF devices
*
* Copyright ( C ) 2006 MontaVista Software Inc .
* Author : Vitaly Wool < vwool @ ru . mvista . com >
*
* 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/kernel.h>
# include <linux/init.h>
# include <linux/slab.h>
# include <linux/device.h>
# include <linux/mtd/mtd.h>
# include <linux/mtd/map.h>
# include <linux/mtd/partitions.h>
# include <linux/mtd/physmap.h>
# include <asm/io.h>
# include <asm/prom.h>
# include <asm/of_device.h>
# include <asm/of_platform.h>
struct physmap_flash_info {
struct mtd_info * mtd ;
struct map_info map ;
struct resource * res ;
# ifdef CONFIG_MTD_PARTITIONS
int nr_parts ;
struct mtd_partition * parts ;
# endif
} ;
static const char * rom_probe_types [ ] = { " cfi_probe " , " jedec_probe " , " map_rom " , NULL } ;
# ifdef CONFIG_MTD_PARTITIONS
static const char * part_probe_types [ ] = { " cmdlinepart " , " RedBoot " , NULL } ;
# endif
# ifdef CONFIG_MTD_PARTITIONS
static int parse_flash_partitions ( struct device_node * node ,
struct mtd_partition * * parts )
{
int i , plen , retval = - ENOMEM ;
const u32 * part ;
const char * name ;
2007-05-01 07:54:02 +04:00
part = of_get_property ( node , " partitions " , & plen ) ;
2006-12-06 13:17:49 +03:00
if ( part = = NULL )
goto err ;
retval = plen / ( 2 * sizeof ( u32 ) ) ;
* parts = kzalloc ( retval * sizeof ( struct mtd_partition ) , GFP_KERNEL ) ;
if ( * parts = = NULL ) {
printk ( KERN_ERR " Can't allocate the flash partition data! \n " ) ;
goto err ;
}
2007-05-01 07:54:02 +04:00
name = of_get_property ( node , " partition-names " , & plen ) ;
2006-12-06 13:17:49 +03:00
for ( i = 0 ; i < retval ; i + + ) {
( * parts ) [ i ] . offset = * part + + ;
( * parts ) [ i ] . size = * part & ~ 1 ;
if ( * part + + & 1 ) /* bit 0 set signifies read only partition */
( * parts ) [ i ] . mask_flags = MTD_WRITEABLE ;
if ( name ! = NULL & & plen > 0 ) {
int len = strlen ( name ) + 1 ;
( * parts ) [ i ] . name = ( char * ) name ;
plen - = len ;
name + = len ;
} else
( * parts ) [ i ] . name = " unnamed " ;
}
err :
return retval ;
}
# endif
static int of_physmap_remove ( struct of_device * dev )
{
struct physmap_flash_info * info ;
info = dev_get_drvdata ( & dev - > dev ) ;
if ( info = = NULL )
return 0 ;
dev_set_drvdata ( & dev - > dev , NULL ) ;
if ( info - > mtd ! = NULL ) {
# ifdef CONFIG_MTD_PARTITIONS
if ( info - > nr_parts ) {
del_mtd_partitions ( info - > mtd ) ;
kfree ( info - > parts ) ;
} else {
del_mtd_device ( info - > mtd ) ;
}
# else
del_mtd_device ( info - > mtd ) ;
# endif
map_destroy ( info - > mtd ) ;
}
if ( info - > map . virt ! = NULL )
iounmap ( info - > map . virt ) ;
if ( info - > res ! = NULL ) {
release_resource ( info - > res ) ;
kfree ( info - > res ) ;
}
return 0 ;
}
static int __devinit of_physmap_probe ( struct of_device * dev , const struct of_device_id * match )
{
struct device_node * dp = dev - > node ;
struct resource res ;
struct physmap_flash_info * info ;
const char * * probe_type ;
const char * of_probe ;
const u32 * width ;
int err ;
if ( of_address_to_resource ( dp , 0 , & res ) ) {
dev_err ( & dev - > dev , " Can't get the flash mapping! \n " ) ;
err = - EINVAL ;
goto err_out ;
}
dev_dbg ( & dev - > dev , " physmap flash device: %.8llx at %.8llx \n " ,
( unsigned long long ) res . end - res . start + 1 ,
( unsigned long long ) res . start ) ;
info = kzalloc ( sizeof ( struct physmap_flash_info ) , GFP_KERNEL ) ;
if ( info = = NULL ) {
err = - ENOMEM ;
goto err_out ;
}
memset ( info , 0 , sizeof ( * info ) ) ;
dev_set_drvdata ( & dev - > dev , info ) ;
info - > res = request_mem_region ( res . start , res . end - res . start + 1 ,
dev - > dev . bus_id ) ;
if ( info - > res = = NULL ) {
dev_err ( & dev - > dev , " Could not reserve memory region \n " ) ;
err = - ENOMEM ;
goto err_out ;
}
2007-05-01 07:54:02 +04:00
width = of_get_property ( dp , " bank-width " , NULL ) ;
2006-12-06 13:17:49 +03:00
if ( width = = NULL ) {
dev_err ( & dev - > dev , " Can't get the flash bank width! \n " ) ;
err = - EINVAL ;
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 ;
info - > map . virt = ioremap ( info - > map . phys , info - > map . size ) ;
if ( info - > map . virt = = NULL ) {
dev_err ( & dev - > dev , " Failed to ioremap flash region \n " ) ;
err = EIO ;
goto err_out ;
}
simple_map_init ( & info - > map ) ;
2007-05-01 07:54:02 +04:00
of_probe = of_get_property ( dp , " probe-type " , NULL ) ;
2006-12-06 13:17:49 +03:00
if ( of_probe = = NULL ) {
probe_type = rom_probe_types ;
for ( ; info - > mtd = = NULL & & * probe_type ! = NULL ; probe_type + + )
info - > mtd = do_map_probe ( * probe_type , & info - > map ) ;
} else if ( ! strcmp ( of_probe , " CFI " ) )
info - > mtd = do_map_probe ( " cfi_probe " , & info - > map ) ;
else if ( ! strcmp ( of_probe , " JEDEC " ) )
info - > mtd = do_map_probe ( " jedec_probe " , & info - > map ) ;
else {
if ( strcmp ( of_probe , " ROM " ) )
dev_dbg ( & dev - > dev , " map_probe: don't know probe type "
2007-05-09 05:55:51 +04:00
" '%s', mapping as rom \n " , of_probe ) ;
2006-12-06 13:17:49 +03:00
info - > mtd = do_map_probe ( " mtd_rom " , & info - > map ) ;
}
if ( info - > mtd = = NULL ) {
dev_err ( & dev - > dev , " map_probe failed \n " ) ;
err = - ENXIO ;
goto err_out ;
}
info - > mtd - > owner = THIS_MODULE ;
# ifdef CONFIG_MTD_PARTITIONS
err = parse_mtd_partitions ( info - > mtd , part_probe_types , & info - > parts , 0 ) ;
if ( err > 0 ) {
add_mtd_partitions ( info - > mtd , info - > parts , err ) ;
} else if ( ( err = parse_flash_partitions ( dp , & info - > parts ) ) > 0 ) {
dev_info ( & dev - > dev , " Using OF partition information \n " ) ;
add_mtd_partitions ( info - > mtd , info - > parts , err ) ;
info - > nr_parts = err ;
} else
# endif
add_mtd_device ( info - > mtd ) ;
return 0 ;
err_out :
of_physmap_remove ( dev ) ;
return err ;
return 0 ;
}
static struct of_device_id of_physmap_match [ ] = {
{
. type = " rom " ,
. compatible = " direct-mapped "
} ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , of_physmap_match ) ;
static struct of_platform_driver of_physmap_flash_driver = {
. name = " physmap-flash " ,
. match_table = of_physmap_match ,
. probe = of_physmap_probe ,
. remove = of_physmap_remove ,
} ;
static int __init of_physmap_init ( void )
{
return of_register_platform_driver ( & of_physmap_flash_driver ) ;
}
static void __exit of_physmap_exit ( void )
{
of_unregister_platform_driver ( & of_physmap_flash_driver ) ;
}
module_init ( of_physmap_init ) ;
module_exit ( of_physmap_exit ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Vitaly Wool <vwool@ru.mvista.com> " ) ;
MODULE_DESCRIPTION ( " Configurable MTD map driver for OF " ) ;