162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* sun_uflash.c - Driver for user-programmable flash on 362306a36Sopenharmony_ci * Sun Microsystems SME boardsets. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * This driver does NOT provide access to the OBP-flash for 662306a36Sopenharmony_ci * safety reasons-- use <linux>/drivers/sbus/char/flash.c instead. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Copyright (c) 2001 Eric Brower (ebrower@usa.net) 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/kernel.h> 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/fs.h> 1462306a36Sopenharmony_ci#include <linux/errno.h> 1562306a36Sopenharmony_ci#include <linux/ioport.h> 1662306a36Sopenharmony_ci#include <linux/of.h> 1762306a36Sopenharmony_ci#include <linux/platform_device.h> 1862306a36Sopenharmony_ci#include <linux/slab.h> 1962306a36Sopenharmony_ci#include <asm/prom.h> 2062306a36Sopenharmony_ci#include <linux/uaccess.h> 2162306a36Sopenharmony_ci#include <asm/io.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include <linux/mtd/mtd.h> 2462306a36Sopenharmony_ci#include <linux/mtd/map.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define UFLASH_OBPNAME "flashprom" 2762306a36Sopenharmony_ci#define DRIVER_NAME "sun_uflash" 2862306a36Sopenharmony_ci#define PFX DRIVER_NAME ": " 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#define UFLASH_WINDOW_SIZE 0x200000 3162306a36Sopenharmony_ci#define UFLASH_BUSWIDTH 1 /* EBus is 8-bit */ 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ciMODULE_AUTHOR("Eric Brower <ebrower@usa.net>"); 3462306a36Sopenharmony_ciMODULE_DESCRIPTION("User-programmable flash device on Sun Microsystems boardsets"); 3562306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 3662306a36Sopenharmony_ciMODULE_VERSION("2.1"); 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistruct uflash_dev { 3962306a36Sopenharmony_ci const char *name; /* device name */ 4062306a36Sopenharmony_ci struct map_info map; /* mtd map info */ 4162306a36Sopenharmony_ci struct mtd_info *mtd; /* mtd info */ 4262306a36Sopenharmony_ci}; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistruct map_info uflash_map_templ = { 4562306a36Sopenharmony_ci .name = "SUNW,???-????", 4662306a36Sopenharmony_ci .size = UFLASH_WINDOW_SIZE, 4762306a36Sopenharmony_ci .bankwidth = UFLASH_BUSWIDTH, 4862306a36Sopenharmony_ci}; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ciint uflash_devinit(struct platform_device *op, struct device_node *dp) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci struct uflash_dev *up; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci if (op->resource[1].flags) { 5562306a36Sopenharmony_ci /* Non-CFI userflash device-- once I find one we 5662306a36Sopenharmony_ci * can work on supporting it. 5762306a36Sopenharmony_ci */ 5862306a36Sopenharmony_ci printk(KERN_ERR PFX "Unsupported device at %pOF, 0x%llx\n", 5962306a36Sopenharmony_ci dp, (unsigned long long)op->resource[0].start); 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci return -ENODEV; 6262306a36Sopenharmony_ci } 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci up = kzalloc(sizeof(struct uflash_dev), GFP_KERNEL); 6562306a36Sopenharmony_ci if (!up) 6662306a36Sopenharmony_ci return -ENOMEM; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci /* copy defaults and tweak parameters */ 6962306a36Sopenharmony_ci memcpy(&up->map, &uflash_map_templ, sizeof(uflash_map_templ)); 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci up->map.size = resource_size(&op->resource[0]); 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci up->name = of_get_property(dp, "model", NULL); 7462306a36Sopenharmony_ci if (up->name && 0 < strlen(up->name)) 7562306a36Sopenharmony_ci up->map.name = up->name; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci up->map.phys = op->resource[0].start; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci up->map.virt = of_ioremap(&op->resource[0], 0, up->map.size, 8062306a36Sopenharmony_ci DRIVER_NAME); 8162306a36Sopenharmony_ci if (!up->map.virt) { 8262306a36Sopenharmony_ci printk(KERN_ERR PFX "Failed to map device.\n"); 8362306a36Sopenharmony_ci kfree(up); 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci return -EINVAL; 8662306a36Sopenharmony_ci } 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci simple_map_init(&up->map); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci /* MTD registration */ 9162306a36Sopenharmony_ci up->mtd = do_map_probe("cfi_probe", &up->map); 9262306a36Sopenharmony_ci if (!up->mtd) { 9362306a36Sopenharmony_ci of_iounmap(&op->resource[0], up->map.virt, up->map.size); 9462306a36Sopenharmony_ci kfree(up); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci return -ENXIO; 9762306a36Sopenharmony_ci } 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci up->mtd->owner = THIS_MODULE; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci mtd_device_register(up->mtd, NULL, 0); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci dev_set_drvdata(&op->dev, up); 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci return 0; 10662306a36Sopenharmony_ci} 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistatic int uflash_probe(struct platform_device *op) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci struct device_node *dp = op->dev.of_node; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci /* Flashprom must have the "user" property in order to 11362306a36Sopenharmony_ci * be used by this driver. 11462306a36Sopenharmony_ci */ 11562306a36Sopenharmony_ci if (!of_property_read_bool(dp, "user")) 11662306a36Sopenharmony_ci return -ENODEV; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci return uflash_devinit(op, dp); 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_cistatic int uflash_remove(struct platform_device *op) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci struct uflash_dev *up = dev_get_drvdata(&op->dev); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci if (up->mtd) { 12662306a36Sopenharmony_ci mtd_device_unregister(up->mtd); 12762306a36Sopenharmony_ci map_destroy(up->mtd); 12862306a36Sopenharmony_ci } 12962306a36Sopenharmony_ci if (up->map.virt) { 13062306a36Sopenharmony_ci of_iounmap(&op->resource[0], up->map.virt, up->map.size); 13162306a36Sopenharmony_ci up->map.virt = NULL; 13262306a36Sopenharmony_ci } 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci kfree(up); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci return 0; 13762306a36Sopenharmony_ci} 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_cistatic const struct of_device_id uflash_match[] = { 14062306a36Sopenharmony_ci { 14162306a36Sopenharmony_ci .name = UFLASH_OBPNAME, 14262306a36Sopenharmony_ci }, 14362306a36Sopenharmony_ci {}, 14462306a36Sopenharmony_ci}; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, uflash_match); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_cistatic struct platform_driver uflash_driver = { 14962306a36Sopenharmony_ci .driver = { 15062306a36Sopenharmony_ci .name = DRIVER_NAME, 15162306a36Sopenharmony_ci .of_match_table = uflash_match, 15262306a36Sopenharmony_ci }, 15362306a36Sopenharmony_ci .probe = uflash_probe, 15462306a36Sopenharmony_ci .remove = uflash_remove, 15562306a36Sopenharmony_ci}; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cimodule_platform_driver(uflash_driver); 158