2008-05-20 01:03:52 +03:00
/*
2005-04-16 15:20:36 -07:00
*
* sun_uflash - Driver implementation for user - programmable flash
* present on many Sun Microsystems SME boardsets .
*
* This driver does NOT provide access to the OBP - flash for
* safety reasons - - use < linux > / drivers / sbus / char / flash . c instead .
*
* Copyright ( c ) 2001 Eric Brower ( ebrower @ usa . net )
*
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/fs.h>
# include <linux/errno.h>
# include <linux/init.h>
# include <linux/ioport.h>
# include <asm/ebus.h>
# include <asm/oplib.h>
2006-06-24 23:27:00 -07:00
# include <asm/prom.h>
2005-04-16 15:20:36 -07:00
# include <asm/uaccess.h>
# include <asm/io.h>
# include <linux/mtd/mtd.h>
# include <linux/mtd/map.h>
# define UFLASH_OBPNAME "flashprom"
# define UFLASH_DEVNAME "userflash"
# define UFLASH_WINDOW_SIZE 0x200000
# define UFLASH_BUSWIDTH 1 /* EBus is 8-bit */
2006-06-24 23:27:00 -07:00
MODULE_AUTHOR ( " Eric Brower <ebrower@usa.net> " ) ;
MODULE_DESCRIPTION ( " User-programmable flash device on Sun Microsystems boardsets " ) ;
MODULE_SUPPORTED_DEVICE ( " userflash " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_VERSION ( " 2.0 " ) ;
2005-04-16 15:20:36 -07:00
static LIST_HEAD ( device_list ) ;
struct uflash_dev {
2007-03-29 00:49:54 -07:00
const char * name ; /* device name */
2005-04-16 15:20:36 -07:00
struct map_info map ; /* mtd map info */
2006-06-24 23:27:00 -07:00
struct mtd_info * mtd ; /* mtd info */
2005-04-16 15:20:36 -07:00
} ;
struct map_info uflash_map_templ = {
2006-06-24 23:27:00 -07:00
. name = " SUNW,???-???? " ,
. size = UFLASH_WINDOW_SIZE ,
. bankwidth = UFLASH_BUSWIDTH ,
2005-04-16 15:20:36 -07:00
} ;
2006-06-24 23:27:00 -07:00
int uflash_devinit ( struct linux_ebus_device * edev , struct device_node * dp )
2005-04-16 15:20:36 -07:00
{
2006-06-24 23:27:00 -07:00
struct uflash_dev * up ;
struct resource * res ;
2005-04-16 15:20:36 -07:00
2006-06-24 23:27:00 -07:00
res = & edev - > resource [ 0 ] ;
2005-04-16 15:20:36 -07:00
2006-06-24 23:27:00 -07:00
if ( edev - > num_addrs ! = 1 ) {
2005-04-16 15:20:36 -07:00
/* Non-CFI userflash device-- once I find one we
* can work on supporting it .
*/
2006-06-12 15:15:17 -07:00
printk ( " %s: unsupported device at 0x%llx (%d regs): " \
2005-11-07 11:15:40 +00:00
" email ebrower@usa.net \n " ,
2006-06-12 15:15:17 -07:00
dp - > full_name , ( unsigned long long ) res - > start ,
edev - > num_addrs ) ;
2006-06-24 23:27:00 -07:00
2005-04-16 15:20:36 -07:00
return - ENODEV ;
}
2006-06-24 23:27:00 -07:00
up = kzalloc ( sizeof ( struct uflash_dev ) , GFP_KERNEL ) ;
if ( ! up )
return - ENOMEM ;
2005-11-07 11:15:40 +00:00
2005-04-16 15:20:36 -07:00
/* copy defaults and tweak parameters */
2006-06-24 23:27:00 -07:00
memcpy ( & up - > map , & uflash_map_templ , sizeof ( uflash_map_templ ) ) ;
up - > map . size = ( res - > end - res - > start ) + 1UL ;
up - > name = of_get_property ( dp , " model " , NULL ) ;
if ( up - > name & & 0 < strlen ( up - > name ) )
2007-03-29 00:49:54 -07:00
up - > map . name = ( char * ) up - > name ;
2006-06-24 23:27:00 -07:00
up - > map . phys = res - > start ;
up - > map . virt = ioremap_nocache ( res - > start , up - > map . size ) ;
if ( ! up - > map . virt ) {
printk ( " %s: Failed to map device. \n " , dp - > full_name ) ;
kfree ( up ) ;
return - EINVAL ;
2005-04-16 15:20:36 -07:00
}
2006-06-24 23:27:00 -07:00
simple_map_init ( & up - > map ) ;
2005-04-16 15:20:36 -07:00
/* MTD registration */
2006-06-24 23:27:00 -07:00
up - > mtd = do_map_probe ( " cfi_probe " , & up - > map ) ;
if ( ! up - > mtd ) {
iounmap ( up - > map . virt ) ;
kfree ( up ) ;
return - ENXIO ;
2005-04-16 15:20:36 -07:00
}
2006-06-24 23:27:00 -07:00
up - > mtd - > owner = THIS_MODULE ;
2005-04-16 15:20:36 -07:00
2006-06-24 23:27:00 -07:00
add_mtd_device ( up - > mtd ) ;
2005-04-16 15:20:36 -07:00
2006-06-24 23:27:00 -07:00
dev_set_drvdata ( & edev - > ofdev . dev , up ) ;
return 0 ;
2005-04-16 15:20:36 -07:00
}
2006-06-24 23:27:00 -07:00
static int __devinit uflash_probe ( struct of_device * dev , const struct of_device_id * match )
2005-04-16 15:20:36 -07:00
{
2006-06-24 23:27:00 -07:00
struct linux_ebus_device * edev = to_ebus_device ( & dev - > dev ) ;
struct device_node * dp = dev - > node ;
2005-04-16 15:20:36 -07:00
2006-06-24 23:27:00 -07:00
if ( of_find_property ( dp , " user " , NULL ) )
2005-04-16 15:20:36 -07:00
return - ENODEV ;
2006-06-24 23:27:00 -07:00
return uflash_devinit ( edev , dp ) ;
2005-04-16 15:20:36 -07:00
}
2006-06-24 23:27:00 -07:00
static int __devexit uflash_remove ( struct of_device * dev )
2005-04-16 15:20:36 -07:00
{
2006-06-24 23:27:00 -07:00
struct uflash_dev * up = dev_get_drvdata ( & dev - > dev ) ;
if ( up - > mtd ) {
del_mtd_device ( up - > mtd ) ;
map_destroy ( up - > mtd ) ;
2005-11-07 11:15:40 +00:00
}
2006-06-24 23:27:00 -07:00
if ( up - > map . virt ) {
iounmap ( up - > map . virt ) ;
up - > map . virt = NULL ;
}
kfree ( up ) ;
return 0 ;
}
static struct of_device_id uflash_match [ ] = {
{
. name = UFLASH_OBPNAME ,
} ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , uflash_match ) ;
static struct of_platform_driver uflash_driver = {
. name = UFLASH_DEVNAME ,
. match_table = uflash_match ,
. probe = uflash_probe ,
. remove = __devexit_p ( uflash_remove ) ,
} ;
static int __init uflash_init ( void )
{
return of_register_driver ( & uflash_driver , & ebus_bus_type ) ;
}
static void __exit uflash_exit ( void )
{
of_unregister_driver ( & uflash_driver ) ;
2005-04-16 15:20:36 -07:00
}
module_init ( uflash_init ) ;
2006-06-24 23:27:00 -07:00
module_exit ( uflash_exit ) ;