10e52fe8cSDavid S. Miller /* sun_uflash.c - Driver for user-programmable flash on 20e52fe8cSDavid 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/ioport.h> 150e52fe8cSDavid S. Miller #include <linux/of.h> 160e52fe8cSDavid S. Miller #include <linux/of_device.h> 175a0e3ad6STejun Heo #include <linux/slab.h> 1829f7ac7eSDavid S. Miller #include <asm/prom.h> 197c0f6ba6SLinus Torvalds #include <linux/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" 260e52fe8cSDavid S. Miller #define DRIVER_NAME "sun_uflash" 270e52fe8cSDavid 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"); 340e52fe8cSDavid S. Miller MODULE_SUPPORTED_DEVICE(DRIVER_NAME); 3529f7ac7eSDavid S. Miller MODULE_LICENSE("GPL"); 360e52fe8cSDavid 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 502dc11581SGrant Likely int uflash_devinit(struct platform_device *op, struct device_node *dp) 511da177e4SLinus Torvalds { 5229f7ac7eSDavid S. Miller struct uflash_dev *up; 531da177e4SLinus Torvalds 540e52fe8cSDavid 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*1d706077SRob Herring printk(KERN_ERR PFX "Unsupported device at %pOF, 0x%llx\n", 59*1d706077SRob Herring dp, (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); 650e52fe8cSDavid S. Miller if (!up) { 660e52fe8cSDavid S. Miller printk(KERN_ERR PFX "Cannot allocate struct uflash_dev\n"); 6729f7ac7eSDavid S. Miller return -ENOMEM; 680e52fe8cSDavid S. Miller } 691da177e4SLinus Torvalds 701da177e4SLinus Torvalds /* copy defaults and tweak parameters */ 7129f7ac7eSDavid S. Miller memcpy(&up->map, &uflash_map_templ, sizeof(uflash_map_templ)); 720e52fe8cSDavid S. Miller 730e52fe8cSDavid 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)) 777c4bb4f8SGeert Uytterhoeven up->map.name = up->name; 7829f7ac7eSDavid S. Miller 790e52fe8cSDavid S. Miller up->map.phys = op->resource[0].start; 8029f7ac7eSDavid S. Miller 810e52fe8cSDavid S. Miller up->map.virt = of_ioremap(&op->resource[0], 0, up->map.size, 820e52fe8cSDavid S. Miller DRIVER_NAME); 8329f7ac7eSDavid S. Miller if (!up->map.virt) { 840e52fe8cSDavid 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) { 950e52fe8cSDavid 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 103ee0e87b1SJamie Iles mtd_device_register(up->mtd, NULL, 0); 1041da177e4SLinus Torvalds 1050e52fe8cSDavid S. Miller dev_set_drvdata(&op->dev, up); 10629f7ac7eSDavid S. Miller 10729f7ac7eSDavid S. Miller return 0; 1081da177e4SLinus Torvalds } 1091da177e4SLinus Torvalds 11006f25510SBill Pemberton static int uflash_probe(struct platform_device *op) 11129f7ac7eSDavid S. Miller { 11261c7a080SGrant Likely struct device_node *dp = op->dev.of_node; 11329f7ac7eSDavid S. Miller 1140e52fe8cSDavid S. Miller /* Flashprom must have the "user" property in order to 1150e52fe8cSDavid S. Miller * be used by this driver. 1160e52fe8cSDavid S. Miller */ 1170e52fe8cSDavid S. Miller if (!of_find_property(dp, "user", NULL)) 11829f7ac7eSDavid S. Miller return -ENODEV; 11929f7ac7eSDavid S. Miller 1200e52fe8cSDavid S. Miller return uflash_devinit(op, dp); 12129f7ac7eSDavid S. Miller } 12229f7ac7eSDavid S. Miller 123810b7e06SBill Pemberton static int uflash_remove(struct platform_device *op) 12429f7ac7eSDavid S. Miller { 1250e52fe8cSDavid S. Miller struct uflash_dev *up = dev_get_drvdata(&op->dev); 12629f7ac7eSDavid S. Miller 12729f7ac7eSDavid S. Miller if (up->mtd) { 128ee0e87b1SJamie Iles mtd_device_unregister(up->mtd); 12929f7ac7eSDavid S. Miller map_destroy(up->mtd); 13029f7ac7eSDavid S. Miller } 13129f7ac7eSDavid S. Miller if (up->map.virt) { 1320e52fe8cSDavid 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 141fd098316SDavid S. Miller static const 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 1501c48a5c9SGrant Likely static struct platform_driver uflash_driver = { 1514018294bSGrant Likely .driver = { 1520e52fe8cSDavid S. Miller .name = DRIVER_NAME, 1534018294bSGrant Likely .of_match_table = uflash_match, 1544018294bSGrant Likely }, 15529f7ac7eSDavid S. Miller .probe = uflash_probe, 1565153b88cSBill Pemberton .remove = uflash_remove, 15729f7ac7eSDavid S. Miller }; 15829f7ac7eSDavid S. Miller 159f99640deSAxel Lin module_platform_driver(uflash_driver); 160