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> 21*29f7ac7eSDavid 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 34*29f7ac7eSDavid S. Miller MODULE_AUTHOR("Eric Brower <ebrower@usa.net>"); 35*29f7ac7eSDavid S. Miller MODULE_DESCRIPTION("User-programmable flash device on Sun Microsystems boardsets"); 36*29f7ac7eSDavid S. Miller MODULE_SUPPORTED_DEVICE("userflash"); 37*29f7ac7eSDavid S. Miller MODULE_LICENSE("GPL"); 38*29f7ac7eSDavid S. Miller MODULE_VERSION("2.0"); 391da177e4SLinus Torvalds 401da177e4SLinus Torvalds static LIST_HEAD(device_list); 411da177e4SLinus Torvalds struct uflash_dev { 421da177e4SLinus Torvalds 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 54*29f7ac7eSDavid S. Miller int uflash_devinit(struct linux_ebus_device *edev, struct device_node *dp) 551da177e4SLinus Torvalds { 56*29f7ac7eSDavid S. Miller struct uflash_dev *up; 57*29f7ac7eSDavid S. Miller struct resource *res; 581da177e4SLinus Torvalds 59*29f7ac7eSDavid S. Miller res = &edev->resource[0]; 601da177e4SLinus Torvalds 61*29f7ac7eSDavid 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 */ 651da177e4SLinus Torvalds printk("%s: unsupported device at 0x%lx (%d regs): " \ 661da177e4SLinus Torvalds "email ebrower@usa.net\n", 67*29f7ac7eSDavid S. Miller dp->full_name, res->start, edev->num_addrs); 68*29f7ac7eSDavid S. Miller 691da177e4SLinus Torvalds return -ENODEV; 701da177e4SLinus Torvalds } 711da177e4SLinus Torvalds 72*29f7ac7eSDavid S. Miller up = kzalloc(sizeof(struct uflash_dev), GFP_KERNEL); 73*29f7ac7eSDavid S. Miller if (!up) 74*29f7ac7eSDavid S. Miller return -ENOMEM; 751da177e4SLinus Torvalds 761da177e4SLinus Torvalds /* copy defaults and tweak parameters */ 77*29f7ac7eSDavid S. Miller memcpy(&up->map, &uflash_map_templ, sizeof(uflash_map_templ)); 78*29f7ac7eSDavid S. Miller up->map.size = (res->end - res->start) + 1UL; 791da177e4SLinus Torvalds 80*29f7ac7eSDavid S. Miller up->name = of_get_property(dp, "model", NULL); 81*29f7ac7eSDavid S. Miller if (up->name && 0 < strlen(up->name)) 82*29f7ac7eSDavid S. Miller up->map.name = up->name; 83*29f7ac7eSDavid S. Miller 84*29f7ac7eSDavid S. Miller up->map.phys = res->start; 85*29f7ac7eSDavid S. Miller 86*29f7ac7eSDavid S. Miller up->map.virt = ioremap_nocache(res->start, up->map.size); 87*29f7ac7eSDavid S. Miller if (!up->map.virt) { 88*29f7ac7eSDavid S. Miller printk("%s: Failed to map device.\n", dp->full_name); 89*29f7ac7eSDavid S. Miller kfree(up); 90*29f7ac7eSDavid S. Miller 91*29f7ac7eSDavid S. Miller return -EINVAL; 921da177e4SLinus Torvalds } 931da177e4SLinus Torvalds 94*29f7ac7eSDavid S. Miller simple_map_init(&up->map); 951da177e4SLinus Torvalds 961da177e4SLinus Torvalds /* MTD registration */ 97*29f7ac7eSDavid S. Miller up->mtd = do_map_probe("cfi_probe", &up->map); 98*29f7ac7eSDavid S. Miller if (!up->mtd) { 99*29f7ac7eSDavid S. Miller iounmap(up->map.virt); 100*29f7ac7eSDavid S. Miller kfree(up); 101*29f7ac7eSDavid S. Miller 102*29f7ac7eSDavid S. Miller return -ENXIO; 1031da177e4SLinus Torvalds } 1041da177e4SLinus Torvalds 105*29f7ac7eSDavid S. Miller up->mtd->owner = THIS_MODULE; 1061da177e4SLinus Torvalds 107*29f7ac7eSDavid S. Miller add_mtd_device(up->mtd); 1081da177e4SLinus Torvalds 109*29f7ac7eSDavid S. Miller dev_set_drvdata(&edev->ofdev.dev, up); 110*29f7ac7eSDavid S. Miller 111*29f7ac7eSDavid S. Miller return 0; 1121da177e4SLinus Torvalds } 1131da177e4SLinus Torvalds 114*29f7ac7eSDavid S. Miller static int __devinit uflash_probe(struct of_device *dev, const struct of_device_id *match) 115*29f7ac7eSDavid S. Miller { 116*29f7ac7eSDavid S. Miller struct linux_ebus_device *edev = to_ebus_device(&dev->dev); 117*29f7ac7eSDavid S. Miller struct device_node *dp = dev->node; 118*29f7ac7eSDavid S. Miller 119*29f7ac7eSDavid S. Miller if (of_find_property(dp, "user", NULL)) 120*29f7ac7eSDavid S. Miller return -ENODEV; 121*29f7ac7eSDavid S. Miller 122*29f7ac7eSDavid S. Miller return uflash_devinit(edev, dp); 123*29f7ac7eSDavid S. Miller } 124*29f7ac7eSDavid S. Miller 125*29f7ac7eSDavid S. Miller static int __devexit uflash_remove(struct of_device *dev) 126*29f7ac7eSDavid S. Miller { 127*29f7ac7eSDavid S. Miller struct uflash_dev *up = dev_get_drvdata(&dev->dev); 128*29f7ac7eSDavid S. Miller 129*29f7ac7eSDavid S. Miller if (up->mtd) { 130*29f7ac7eSDavid S. Miller del_mtd_device(up->mtd); 131*29f7ac7eSDavid S. Miller map_destroy(up->mtd); 132*29f7ac7eSDavid S. Miller } 133*29f7ac7eSDavid S. Miller if (up->map.virt) { 134*29f7ac7eSDavid S. Miller iounmap(up->map.virt); 135*29f7ac7eSDavid S. Miller up->map.virt = NULL; 136*29f7ac7eSDavid S. Miller } 137*29f7ac7eSDavid S. Miller 138*29f7ac7eSDavid S. Miller kfree(up); 139*29f7ac7eSDavid S. Miller 140*29f7ac7eSDavid S. Miller return 0; 141*29f7ac7eSDavid S. Miller } 142*29f7ac7eSDavid S. Miller 143*29f7ac7eSDavid S. Miller static struct of_device_id uflash_match[] = { 144*29f7ac7eSDavid S. Miller { 145*29f7ac7eSDavid S. Miller .name = UFLASH_OBPNAME, 146*29f7ac7eSDavid S. Miller }, 147*29f7ac7eSDavid S. Miller {}, 148*29f7ac7eSDavid S. Miller }; 149*29f7ac7eSDavid S. Miller 150*29f7ac7eSDavid S. Miller MODULE_DEVICE_TABLE(of, uflash_match); 151*29f7ac7eSDavid S. Miller 152*29f7ac7eSDavid S. Miller static struct of_platform_driver uflash_driver = { 153*29f7ac7eSDavid S. Miller .name = UFLASH_DEVNAME, 154*29f7ac7eSDavid S. Miller .match_table = uflash_match, 155*29f7ac7eSDavid S. Miller .probe = uflash_probe, 156*29f7ac7eSDavid S. Miller .remove = __devexit_p(uflash_remove), 157*29f7ac7eSDavid S. Miller }; 158*29f7ac7eSDavid S. Miller 1591da177e4SLinus Torvalds static int __init uflash_init(void) 1601da177e4SLinus Torvalds { 161*29f7ac7eSDavid S. Miller return of_register_driver(&uflash_driver, &ebus_bus_type); 1621da177e4SLinus Torvalds } 1631da177e4SLinus Torvalds 164*29f7ac7eSDavid S. Miller static void __exit uflash_exit(void) 1651da177e4SLinus Torvalds { 166*29f7ac7eSDavid S. Miller of_unregister_driver(&uflash_driver); 1671da177e4SLinus Torvalds } 1681da177e4SLinus Torvalds 1691da177e4SLinus Torvalds module_init(uflash_init); 170*29f7ac7eSDavid S. Miller module_exit(uflash_exit); 171