169f34c98SThomas Gleixner /* $Id: sun_uflash.c,v 1.13 2005/11/07 11:14:28 gleixner Exp $ 21da177e4SLinus Torvalds * 31da177e4SLinus Torvalds * sun_uflash - Driver implementation for user-programmable flash 41da177e4SLinus Torvalds * present on many Sun Microsystems SME boardsets. 51da177e4SLinus Torvalds * 61da177e4SLinus Torvalds * This driver does NOT provide access to the OBP-flash for 71da177e4SLinus Torvalds * safety reasons-- use <linux>/drivers/sbus/char/flash.c instead. 81da177e4SLinus Torvalds * 91da177e4SLinus Torvalds * Copyright (c) 2001 Eric Brower (ebrower@usa.net) 101da177e4SLinus Torvalds * 111da177e4SLinus Torvalds */ 121da177e4SLinus Torvalds 131da177e4SLinus Torvalds #include <linux/kernel.h> 141da177e4SLinus Torvalds #include <linux/module.h> 151da177e4SLinus Torvalds #include <linux/fs.h> 161da177e4SLinus Torvalds #include <linux/errno.h> 171da177e4SLinus Torvalds #include <linux/init.h> 181da177e4SLinus Torvalds #include <linux/ioport.h> 191da177e4SLinus Torvalds #include <asm/ebus.h> 201da177e4SLinus Torvalds #include <asm/oplib.h> 2129f7ac7eSDavid S. Miller #include <asm/prom.h> 221da177e4SLinus Torvalds #include <asm/uaccess.h> 231da177e4SLinus Torvalds #include <asm/io.h> 241da177e4SLinus Torvalds 251da177e4SLinus Torvalds #include <linux/mtd/mtd.h> 261da177e4SLinus Torvalds #include <linux/mtd/map.h> 271da177e4SLinus Torvalds 281da177e4SLinus Torvalds #define UFLASH_OBPNAME "flashprom" 291da177e4SLinus Torvalds #define UFLASH_DEVNAME "userflash" 301da177e4SLinus Torvalds 311da177e4SLinus Torvalds #define UFLASH_WINDOW_SIZE 0x200000 321da177e4SLinus Torvalds #define UFLASH_BUSWIDTH 1 /* EBus is 8-bit */ 331da177e4SLinus Torvalds 3429f7ac7eSDavid S. Miller MODULE_AUTHOR("Eric Brower <ebrower@usa.net>"); 3529f7ac7eSDavid S. Miller MODULE_DESCRIPTION("User-programmable flash device on Sun Microsystems boardsets"); 3629f7ac7eSDavid S. Miller MODULE_SUPPORTED_DEVICE("userflash"); 3729f7ac7eSDavid S. Miller MODULE_LICENSE("GPL"); 3829f7ac7eSDavid S. Miller MODULE_VERSION("2.0"); 391da177e4SLinus Torvalds 401da177e4SLinus Torvalds static LIST_HEAD(device_list); 411da177e4SLinus Torvalds struct uflash_dev { 42*ccf0dec6SStephen Rothwell const char *name; /* device name */ 431da177e4SLinus Torvalds struct map_info map; /* mtd map info */ 441da177e4SLinus Torvalds struct mtd_info *mtd; /* mtd info */ 451da177e4SLinus Torvalds }; 461da177e4SLinus Torvalds 471da177e4SLinus Torvalds 481da177e4SLinus Torvalds struct map_info uflash_map_templ = { 491da177e4SLinus Torvalds .name = "SUNW,???-????", 501da177e4SLinus Torvalds .size = UFLASH_WINDOW_SIZE, 511da177e4SLinus Torvalds .bankwidth = UFLASH_BUSWIDTH, 521da177e4SLinus Torvalds }; 531da177e4SLinus Torvalds 5429f7ac7eSDavid S. Miller int uflash_devinit(struct linux_ebus_device *edev, struct device_node *dp) 551da177e4SLinus Torvalds { 5629f7ac7eSDavid S. Miller struct uflash_dev *up; 5729f7ac7eSDavid S. Miller struct resource *res; 581da177e4SLinus Torvalds 5929f7ac7eSDavid S. Miller res = &edev->resource[0]; 601da177e4SLinus Torvalds 6129f7ac7eSDavid S. Miller if (edev->num_addrs != 1) { 621da177e4SLinus Torvalds /* Non-CFI userflash device-- once I find one we 631da177e4SLinus Torvalds * can work on supporting it. 641da177e4SLinus Torvalds */ 65176dfc63SGreg Kroah-Hartman printk("%s: unsupported device at 0x%llx (%d regs): " \ 661da177e4SLinus Torvalds "email ebrower@usa.net\n", 67176dfc63SGreg Kroah-Hartman dp->full_name, (unsigned long long)res->start, 68176dfc63SGreg Kroah-Hartman edev->num_addrs); 6929f7ac7eSDavid S. Miller 701da177e4SLinus Torvalds return -ENODEV; 711da177e4SLinus Torvalds } 721da177e4SLinus Torvalds 7329f7ac7eSDavid S. Miller up = kzalloc(sizeof(struct uflash_dev), GFP_KERNEL); 7429f7ac7eSDavid S. Miller if (!up) 7529f7ac7eSDavid S. Miller return -ENOMEM; 761da177e4SLinus Torvalds 771da177e4SLinus Torvalds /* copy defaults and tweak parameters */ 7829f7ac7eSDavid S. Miller memcpy(&up->map, &uflash_map_templ, sizeof(uflash_map_templ)); 7929f7ac7eSDavid S. Miller up->map.size = (res->end - res->start) + 1UL; 801da177e4SLinus Torvalds 8129f7ac7eSDavid S. Miller up->name = of_get_property(dp, "model", NULL); 8229f7ac7eSDavid S. Miller if (up->name && 0 < strlen(up->name)) 83*ccf0dec6SStephen Rothwell up->map.name = (char *)up->name; 8429f7ac7eSDavid S. Miller 8529f7ac7eSDavid S. Miller up->map.phys = res->start; 8629f7ac7eSDavid S. Miller 8729f7ac7eSDavid S. Miller up->map.virt = ioremap_nocache(res->start, up->map.size); 8829f7ac7eSDavid S. Miller if (!up->map.virt) { 8929f7ac7eSDavid S. Miller printk("%s: Failed to map device.\n", dp->full_name); 9029f7ac7eSDavid S. Miller kfree(up); 9129f7ac7eSDavid S. Miller 9229f7ac7eSDavid S. Miller return -EINVAL; 931da177e4SLinus Torvalds } 941da177e4SLinus Torvalds 9529f7ac7eSDavid S. Miller simple_map_init(&up->map); 961da177e4SLinus Torvalds 971da177e4SLinus Torvalds /* MTD registration */ 9829f7ac7eSDavid S. Miller up->mtd = do_map_probe("cfi_probe", &up->map); 9929f7ac7eSDavid S. Miller if (!up->mtd) { 10029f7ac7eSDavid S. Miller iounmap(up->map.virt); 10129f7ac7eSDavid S. Miller kfree(up); 10229f7ac7eSDavid S. Miller 10329f7ac7eSDavid S. Miller return -ENXIO; 1041da177e4SLinus Torvalds } 1051da177e4SLinus Torvalds 10629f7ac7eSDavid S. Miller up->mtd->owner = THIS_MODULE; 1071da177e4SLinus Torvalds 10829f7ac7eSDavid S. Miller add_mtd_device(up->mtd); 1091da177e4SLinus Torvalds 11029f7ac7eSDavid S. Miller dev_set_drvdata(&edev->ofdev.dev, up); 11129f7ac7eSDavid S. Miller 11229f7ac7eSDavid S. Miller return 0; 1131da177e4SLinus Torvalds } 1141da177e4SLinus Torvalds 11529f7ac7eSDavid S. Miller static int __devinit uflash_probe(struct of_device *dev, const struct of_device_id *match) 11629f7ac7eSDavid S. Miller { 11729f7ac7eSDavid S. Miller struct linux_ebus_device *edev = to_ebus_device(&dev->dev); 11829f7ac7eSDavid S. Miller struct device_node *dp = dev->node; 11929f7ac7eSDavid S. Miller 12029f7ac7eSDavid S. Miller if (of_find_property(dp, "user", NULL)) 12129f7ac7eSDavid S. Miller return -ENODEV; 12229f7ac7eSDavid S. Miller 12329f7ac7eSDavid S. Miller return uflash_devinit(edev, dp); 12429f7ac7eSDavid S. Miller } 12529f7ac7eSDavid S. Miller 12629f7ac7eSDavid S. Miller static int __devexit uflash_remove(struct of_device *dev) 12729f7ac7eSDavid S. Miller { 12829f7ac7eSDavid S. Miller struct uflash_dev *up = dev_get_drvdata(&dev->dev); 12929f7ac7eSDavid S. Miller 13029f7ac7eSDavid S. Miller if (up->mtd) { 13129f7ac7eSDavid S. Miller del_mtd_device(up->mtd); 13229f7ac7eSDavid S. Miller map_destroy(up->mtd); 13329f7ac7eSDavid S. Miller } 13429f7ac7eSDavid S. Miller if (up->map.virt) { 13529f7ac7eSDavid S. Miller iounmap(up->map.virt); 13629f7ac7eSDavid S. Miller up->map.virt = NULL; 13729f7ac7eSDavid S. Miller } 13829f7ac7eSDavid S. Miller 13929f7ac7eSDavid S. Miller kfree(up); 14029f7ac7eSDavid S. Miller 14129f7ac7eSDavid S. Miller return 0; 14229f7ac7eSDavid S. Miller } 14329f7ac7eSDavid S. Miller 14429f7ac7eSDavid S. Miller static struct of_device_id uflash_match[] = { 14529f7ac7eSDavid S. Miller { 14629f7ac7eSDavid S. Miller .name = UFLASH_OBPNAME, 14729f7ac7eSDavid S. Miller }, 14829f7ac7eSDavid S. Miller {}, 14929f7ac7eSDavid S. Miller }; 15029f7ac7eSDavid S. Miller 15129f7ac7eSDavid S. Miller MODULE_DEVICE_TABLE(of, uflash_match); 15229f7ac7eSDavid S. Miller 15329f7ac7eSDavid S. Miller static struct of_platform_driver uflash_driver = { 15429f7ac7eSDavid S. Miller .name = UFLASH_DEVNAME, 15529f7ac7eSDavid S. Miller .match_table = uflash_match, 15629f7ac7eSDavid S. Miller .probe = uflash_probe, 15729f7ac7eSDavid S. Miller .remove = __devexit_p(uflash_remove), 15829f7ac7eSDavid S. Miller }; 15929f7ac7eSDavid S. Miller 1601da177e4SLinus Torvalds static int __init uflash_init(void) 1611da177e4SLinus Torvalds { 16229f7ac7eSDavid S. Miller return of_register_driver(&uflash_driver, &ebus_bus_type); 1631da177e4SLinus Torvalds } 1641da177e4SLinus Torvalds 16529f7ac7eSDavid S. Miller static void __exit uflash_exit(void) 1661da177e4SLinus Torvalds { 16729f7ac7eSDavid S. Miller of_unregister_driver(&uflash_driver); 1681da177e4SLinus Torvalds } 1691da177e4SLinus Torvalds 1701da177e4SLinus Torvalds module_init(uflash_init); 17129f7ac7eSDavid S. Miller module_exit(uflash_exit); 172