xref: /openbmc/linux/drivers/mtd/maps/sun_uflash.c (revision ccf0dec6fcadb4e1c877b9bafb031a6bdb7112b9)
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