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