1*0e52fe8cSDavid S. Miller /* sun_uflash.c - Driver for user-programmable flash on 2*0e52fe8cSDavid S. Miller * Sun Microsystems SME boardsets. 31da177e4SLinus Torvalds * 41da177e4SLinus Torvalds * This driver does NOT provide access to the OBP-flash for 51da177e4SLinus Torvalds * safety reasons-- use <linux>/drivers/sbus/char/flash.c instead. 61da177e4SLinus Torvalds * 71da177e4SLinus Torvalds * Copyright (c) 2001 Eric Brower (ebrower@usa.net) 81da177e4SLinus Torvalds */ 91da177e4SLinus Torvalds 101da177e4SLinus Torvalds #include <linux/kernel.h> 111da177e4SLinus Torvalds #include <linux/module.h> 121da177e4SLinus Torvalds #include <linux/fs.h> 131da177e4SLinus Torvalds #include <linux/errno.h> 141da177e4SLinus Torvalds #include <linux/init.h> 151da177e4SLinus Torvalds #include <linux/ioport.h> 16*0e52fe8cSDavid S. Miller #include <linux/of.h> 17*0e52fe8cSDavid S. Miller #include <linux/of_device.h> 1829f7ac7eSDavid S. Miller #include <asm/prom.h> 191da177e4SLinus Torvalds #include <asm/uaccess.h> 201da177e4SLinus Torvalds #include <asm/io.h> 211da177e4SLinus Torvalds 221da177e4SLinus Torvalds #include <linux/mtd/mtd.h> 231da177e4SLinus Torvalds #include <linux/mtd/map.h> 241da177e4SLinus Torvalds 251da177e4SLinus Torvalds #define UFLASH_OBPNAME "flashprom" 26*0e52fe8cSDavid S. Miller #define DRIVER_NAME "sun_uflash" 27*0e52fe8cSDavid S. Miller #define PFX DRIVER_NAME ": " 281da177e4SLinus Torvalds 291da177e4SLinus Torvalds #define UFLASH_WINDOW_SIZE 0x200000 301da177e4SLinus Torvalds #define UFLASH_BUSWIDTH 1 /* EBus is 8-bit */ 311da177e4SLinus Torvalds 3229f7ac7eSDavid S. Miller MODULE_AUTHOR("Eric Brower <ebrower@usa.net>"); 3329f7ac7eSDavid S. Miller MODULE_DESCRIPTION("User-programmable flash device on Sun Microsystems boardsets"); 34*0e52fe8cSDavid S. Miller MODULE_SUPPORTED_DEVICE(DRIVER_NAME); 3529f7ac7eSDavid S. Miller MODULE_LICENSE("GPL"); 36*0e52fe8cSDavid S. Miller MODULE_VERSION("2.1"); 371da177e4SLinus Torvalds 381da177e4SLinus Torvalds struct uflash_dev { 39ccf0dec6SStephen Rothwell const char *name; /* device name */ 401da177e4SLinus Torvalds struct map_info map; /* mtd map info */ 411da177e4SLinus Torvalds struct mtd_info *mtd; /* mtd info */ 421da177e4SLinus Torvalds }; 431da177e4SLinus Torvalds 441da177e4SLinus Torvalds struct map_info uflash_map_templ = { 451da177e4SLinus Torvalds .name = "SUNW,???-????", 461da177e4SLinus Torvalds .size = UFLASH_WINDOW_SIZE, 471da177e4SLinus Torvalds .bankwidth = UFLASH_BUSWIDTH, 481da177e4SLinus Torvalds }; 491da177e4SLinus Torvalds 50*0e52fe8cSDavid S. Miller int uflash_devinit(struct of_device *op, struct device_node *dp) 511da177e4SLinus Torvalds { 5229f7ac7eSDavid S. Miller struct uflash_dev *up; 531da177e4SLinus Torvalds 54*0e52fe8cSDavid S. Miller if (op->resource[1].flags) { 551da177e4SLinus Torvalds /* Non-CFI userflash device-- once I find one we 561da177e4SLinus Torvalds * can work on supporting it. 571da177e4SLinus Torvalds */ 58*0e52fe8cSDavid S. Miller printk(KERN_ERR PFX "Unsupported device at %s, 0x%llx\n", 59*0e52fe8cSDavid S. Miller dp->full_name, (unsigned long long)op->resource[0].start); 6029f7ac7eSDavid S. Miller 611da177e4SLinus Torvalds return -ENODEV; 621da177e4SLinus Torvalds } 631da177e4SLinus Torvalds 6429f7ac7eSDavid S. Miller up = kzalloc(sizeof(struct uflash_dev), GFP_KERNEL); 65*0e52fe8cSDavid S. Miller if (!up) { 66*0e52fe8cSDavid S. Miller printk(KERN_ERR PFX "Cannot allocate struct uflash_dev\n"); 6729f7ac7eSDavid S. Miller return -ENOMEM; 68*0e52fe8cSDavid S. Miller } 691da177e4SLinus Torvalds 701da177e4SLinus Torvalds /* copy defaults and tweak parameters */ 7129f7ac7eSDavid S. Miller memcpy(&up->map, &uflash_map_templ, sizeof(uflash_map_templ)); 72*0e52fe8cSDavid S. Miller 73*0e52fe8cSDavid S. Miller up->map.size = resource_size(&op->resource[0]); 741da177e4SLinus Torvalds 7529f7ac7eSDavid S. Miller up->name = of_get_property(dp, "model", NULL); 7629f7ac7eSDavid S. Miller if (up->name && 0 < strlen(up->name)) 77ccf0dec6SStephen Rothwell up->map.name = (char *)up->name; 7829f7ac7eSDavid S. Miller 79*0e52fe8cSDavid S. Miller up->map.phys = op->resource[0].start; 8029f7ac7eSDavid S. Miller 81*0e52fe8cSDavid S. Miller up->map.virt = of_ioremap(&op->resource[0], 0, up->map.size, 82*0e52fe8cSDavid S. Miller DRIVER_NAME); 8329f7ac7eSDavid S. Miller if (!up->map.virt) { 84*0e52fe8cSDavid S. Miller printk(KERN_ERR PFX "Failed to map device.\n"); 8529f7ac7eSDavid S. Miller kfree(up); 8629f7ac7eSDavid S. Miller 8729f7ac7eSDavid S. Miller return -EINVAL; 881da177e4SLinus Torvalds } 891da177e4SLinus Torvalds 9029f7ac7eSDavid S. Miller simple_map_init(&up->map); 911da177e4SLinus Torvalds 921da177e4SLinus Torvalds /* MTD registration */ 9329f7ac7eSDavid S. Miller up->mtd = do_map_probe("cfi_probe", &up->map); 9429f7ac7eSDavid S. Miller if (!up->mtd) { 95*0e52fe8cSDavid S. Miller of_iounmap(&op->resource[0], up->map.virt, up->map.size); 9629f7ac7eSDavid S. Miller kfree(up); 9729f7ac7eSDavid S. Miller 9829f7ac7eSDavid S. Miller return -ENXIO; 991da177e4SLinus Torvalds } 1001da177e4SLinus Torvalds 10129f7ac7eSDavid S. Miller up->mtd->owner = THIS_MODULE; 1021da177e4SLinus Torvalds 10329f7ac7eSDavid S. Miller add_mtd_device(up->mtd); 1041da177e4SLinus Torvalds 105*0e52fe8cSDavid S. Miller dev_set_drvdata(&op->dev, up); 10629f7ac7eSDavid S. Miller 10729f7ac7eSDavid S. Miller return 0; 1081da177e4SLinus Torvalds } 1091da177e4SLinus Torvalds 110*0e52fe8cSDavid S. Miller static int __devinit uflash_probe(struct of_device *op, const struct of_device_id *match) 11129f7ac7eSDavid S. Miller { 112*0e52fe8cSDavid S. Miller struct device_node *dp = op->node; 11329f7ac7eSDavid S. Miller 114*0e52fe8cSDavid S. Miller /* Flashprom must have the "user" property in order to 115*0e52fe8cSDavid S. Miller * be used by this driver. 116*0e52fe8cSDavid S. Miller */ 117*0e52fe8cSDavid S. Miller if (!of_find_property(dp, "user", NULL)) 11829f7ac7eSDavid S. Miller return -ENODEV; 11929f7ac7eSDavid S. Miller 120*0e52fe8cSDavid S. Miller return uflash_devinit(op, dp); 12129f7ac7eSDavid S. Miller } 12229f7ac7eSDavid S. Miller 123*0e52fe8cSDavid S. Miller static int __devexit uflash_remove(struct of_device *op) 12429f7ac7eSDavid S. Miller { 125*0e52fe8cSDavid S. Miller struct uflash_dev *up = dev_get_drvdata(&op->dev); 12629f7ac7eSDavid S. Miller 12729f7ac7eSDavid S. Miller if (up->mtd) { 12829f7ac7eSDavid S. Miller del_mtd_device(up->mtd); 12929f7ac7eSDavid S. Miller map_destroy(up->mtd); 13029f7ac7eSDavid S. Miller } 13129f7ac7eSDavid S. Miller if (up->map.virt) { 132*0e52fe8cSDavid S. Miller of_iounmap(&op->resource[0], up->map.virt, up->map.size); 13329f7ac7eSDavid S. Miller up->map.virt = NULL; 13429f7ac7eSDavid S. Miller } 13529f7ac7eSDavid S. Miller 13629f7ac7eSDavid S. Miller kfree(up); 13729f7ac7eSDavid S. Miller 13829f7ac7eSDavid S. Miller return 0; 13929f7ac7eSDavid S. Miller } 14029f7ac7eSDavid S. Miller 14129f7ac7eSDavid S. Miller static struct of_device_id uflash_match[] = { 14229f7ac7eSDavid S. Miller { 14329f7ac7eSDavid S. Miller .name = UFLASH_OBPNAME, 14429f7ac7eSDavid S. Miller }, 14529f7ac7eSDavid S. Miller {}, 14629f7ac7eSDavid S. Miller }; 14729f7ac7eSDavid S. Miller 14829f7ac7eSDavid S. Miller MODULE_DEVICE_TABLE(of, uflash_match); 14929f7ac7eSDavid S. Miller 15029f7ac7eSDavid S. Miller static struct of_platform_driver uflash_driver = { 151*0e52fe8cSDavid S. Miller .name = DRIVER_NAME, 15229f7ac7eSDavid S. Miller .match_table = uflash_match, 15329f7ac7eSDavid S. Miller .probe = uflash_probe, 15429f7ac7eSDavid S. Miller .remove = __devexit_p(uflash_remove), 15529f7ac7eSDavid S. Miller }; 15629f7ac7eSDavid S. Miller 1571da177e4SLinus Torvalds static int __init uflash_init(void) 1581da177e4SLinus Torvalds { 159*0e52fe8cSDavid S. Miller return of_register_driver(&uflash_driver, &of_bus_type); 1601da177e4SLinus Torvalds } 1611da177e4SLinus Torvalds 16229f7ac7eSDavid S. Miller static void __exit uflash_exit(void) 1631da177e4SLinus Torvalds { 16429f7ac7eSDavid S. Miller of_unregister_driver(&uflash_driver); 1651da177e4SLinus Torvalds } 1661da177e4SLinus Torvalds 1671da177e4SLinus Torvalds module_init(uflash_init); 16829f7ac7eSDavid S. Miller module_exit(uflash_exit); 169