Commit | Line | Data |
---|---|---|
69f34c98 | 1 | /* $Id: sun_uflash.c,v 1.13 2005/11/07 11:14:28 gleixner Exp $ |
1da177e4 LT |
2 | * |
3 | * sun_uflash - Driver implementation for user-programmable flash | |
4 | * present on many Sun Microsystems SME boardsets. | |
5 | * | |
6 | * This driver does NOT provide access to the OBP-flash for | |
7 | * safety reasons-- use <linux>/drivers/sbus/char/flash.c instead. | |
8 | * | |
9 | * Copyright (c) 2001 Eric Brower (ebrower@usa.net) | |
10 | * | |
11 | */ | |
12 | ||
13 | #include <linux/kernel.h> | |
14 | #include <linux/module.h> | |
15 | #include <linux/fs.h> | |
16 | #include <linux/errno.h> | |
17 | #include <linux/init.h> | |
18 | #include <linux/ioport.h> | |
19 | #include <asm/ebus.h> | |
20 | #include <asm/oplib.h> | |
29f7ac7e | 21 | #include <asm/prom.h> |
1da177e4 LT |
22 | #include <asm/uaccess.h> |
23 | #include <asm/io.h> | |
24 | ||
25 | #include <linux/mtd/mtd.h> | |
26 | #include <linux/mtd/map.h> | |
27 | ||
28 | #define UFLASH_OBPNAME "flashprom" | |
29 | #define UFLASH_DEVNAME "userflash" | |
30 | ||
31 | #define UFLASH_WINDOW_SIZE 0x200000 | |
32 | #define UFLASH_BUSWIDTH 1 /* EBus is 8-bit */ | |
33 | ||
29f7ac7e DM |
34 | MODULE_AUTHOR("Eric Brower <ebrower@usa.net>"); |
35 | MODULE_DESCRIPTION("User-programmable flash device on Sun Microsystems boardsets"); | |
36 | MODULE_SUPPORTED_DEVICE("userflash"); | |
37 | MODULE_LICENSE("GPL"); | |
38 | MODULE_VERSION("2.0"); | |
1da177e4 LT |
39 | |
40 | static LIST_HEAD(device_list); | |
41 | struct uflash_dev { | |
ccf0dec6 | 42 | const char *name; /* device name */ |
1da177e4 | 43 | struct map_info map; /* mtd map info */ |
29f7ac7e | 44 | struct mtd_info *mtd; /* mtd info */ |
1da177e4 LT |
45 | }; |
46 | ||
47 | ||
48 | struct map_info uflash_map_templ = { | |
29f7ac7e DM |
49 | .name = "SUNW,???-????", |
50 | .size = UFLASH_WINDOW_SIZE, | |
51 | .bankwidth = UFLASH_BUSWIDTH, | |
1da177e4 LT |
52 | }; |
53 | ||
29f7ac7e | 54 | int uflash_devinit(struct linux_ebus_device *edev, struct device_node *dp) |
1da177e4 | 55 | { |
29f7ac7e DM |
56 | struct uflash_dev *up; |
57 | struct resource *res; | |
1da177e4 | 58 | |
29f7ac7e | 59 | res = &edev->resource[0]; |
1da177e4 | 60 | |
29f7ac7e | 61 | if (edev->num_addrs != 1) { |
1da177e4 LT |
62 | /* Non-CFI userflash device-- once I find one we |
63 | * can work on supporting it. | |
64 | */ | |
176dfc63 | 65 | printk("%s: unsupported device at 0x%llx (%d regs): " \ |
69f34c98 | 66 | "email ebrower@usa.net\n", |
176dfc63 GKH |
67 | dp->full_name, (unsigned long long)res->start, |
68 | edev->num_addrs); | |
29f7ac7e | 69 | |
1da177e4 LT |
70 | return -ENODEV; |
71 | } | |
72 | ||
29f7ac7e DM |
73 | up = kzalloc(sizeof(struct uflash_dev), GFP_KERNEL); |
74 | if (!up) | |
75 | return -ENOMEM; | |
69f34c98 | 76 | |
1da177e4 | 77 | /* copy defaults and tweak parameters */ |
29f7ac7e DM |
78 | memcpy(&up->map, &uflash_map_templ, sizeof(uflash_map_templ)); |
79 | up->map.size = (res->end - res->start) + 1UL; | |
80 | ||
81 | up->name = of_get_property(dp, "model", NULL); | |
82 | if (up->name && 0 < strlen(up->name)) | |
ccf0dec6 | 83 | up->map.name = (char *)up->name; |
29f7ac7e DM |
84 | |
85 | up->map.phys = res->start; | |
86 | ||
87 | up->map.virt = ioremap_nocache(res->start, up->map.size); | |
88 | if (!up->map.virt) { | |
89 | printk("%s: Failed to map device.\n", dp->full_name); | |
90 | kfree(up); | |
91 | ||
92 | return -EINVAL; | |
1da177e4 LT |
93 | } |
94 | ||
29f7ac7e | 95 | simple_map_init(&up->map); |
1da177e4 LT |
96 | |
97 | /* MTD registration */ | |
29f7ac7e DM |
98 | up->mtd = do_map_probe("cfi_probe", &up->map); |
99 | if (!up->mtd) { | |
100 | iounmap(up->map.virt); | |
101 | kfree(up); | |
102 | ||
103 | return -ENXIO; | |
1da177e4 LT |
104 | } |
105 | ||
29f7ac7e | 106 | up->mtd->owner = THIS_MODULE; |
1da177e4 | 107 | |
29f7ac7e | 108 | add_mtd_device(up->mtd); |
1da177e4 | 109 | |
29f7ac7e DM |
110 | dev_set_drvdata(&edev->ofdev.dev, up); |
111 | ||
112 | return 0; | |
1da177e4 LT |
113 | } |
114 | ||
29f7ac7e | 115 | static int __devinit uflash_probe(struct of_device *dev, const struct of_device_id *match) |
1da177e4 | 116 | { |
29f7ac7e DM |
117 | struct linux_ebus_device *edev = to_ebus_device(&dev->dev); |
118 | struct device_node *dp = dev->node; | |
1da177e4 | 119 | |
29f7ac7e | 120 | if (of_find_property(dp, "user", NULL)) |
1da177e4 | 121 | return -ENODEV; |
29f7ac7e DM |
122 | |
123 | return uflash_devinit(edev, dp); | |
1da177e4 LT |
124 | } |
125 | ||
29f7ac7e | 126 | static int __devexit uflash_remove(struct of_device *dev) |
1da177e4 | 127 | { |
29f7ac7e DM |
128 | struct uflash_dev *up = dev_get_drvdata(&dev->dev); |
129 | ||
130 | if (up->mtd) { | |
131 | del_mtd_device(up->mtd); | |
132 | map_destroy(up->mtd); | |
69f34c98 | 133 | } |
29f7ac7e DM |
134 | if (up->map.virt) { |
135 | iounmap(up->map.virt); | |
136 | up->map.virt = NULL; | |
137 | } | |
138 | ||
139 | kfree(up); | |
140 | ||
141 | return 0; | |
142 | } | |
143 | ||
144 | static struct of_device_id uflash_match[] = { | |
145 | { | |
146 | .name = UFLASH_OBPNAME, | |
147 | }, | |
148 | {}, | |
149 | }; | |
150 | ||
151 | MODULE_DEVICE_TABLE(of, uflash_match); | |
152 | ||
153 | static struct of_platform_driver uflash_driver = { | |
154 | .name = UFLASH_DEVNAME, | |
155 | .match_table = uflash_match, | |
156 | .probe = uflash_probe, | |
157 | .remove = __devexit_p(uflash_remove), | |
158 | }; | |
159 | ||
160 | static int __init uflash_init(void) | |
161 | { | |
162 | return of_register_driver(&uflash_driver, &ebus_bus_type); | |
163 | } | |
164 | ||
165 | static void __exit uflash_exit(void) | |
166 | { | |
167 | of_unregister_driver(&uflash_driver); | |
1da177e4 LT |
168 | } |
169 | ||
170 | module_init(uflash_init); | |
29f7ac7e | 171 | module_exit(uflash_exit); |