Merge branch 'for-2638/i2c/nomadik' into for-linus/i2c-2638
[deliverable/linux.git] / drivers / mtd / maps / integrator-flash.c
CommitLineData
1da177e4
LT
1/*======================================================================
2
3 drivers/mtd/maps/integrator-flash.c: ARM Integrator flash map driver
69f34c98 4
1da177e4
LT
5 Copyright (C) 2000 ARM Limited
6 Copyright (C) 2003 Deep Blue Solutions Ltd.
69f34c98 7
1da177e4
LT
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
69f34c98 12
1da177e4
LT
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
69f34c98 17
1da177e4
LT
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
69f34c98
TG
21
22 This is access code for flashes using ARM's flash partitioning
1da177e4
LT
23 standards.
24
1da177e4
LT
25======================================================================*/
26
1da177e4
LT
27#include <linux/module.h>
28#include <linux/types.h>
29#include <linux/kernel.h>
30#include <linux/slab.h>
31#include <linux/ioport.h>
d052d1be 32#include <linux/platform_device.h>
1da177e4 33#include <linux/init.h>
99730225 34#include <linux/io.h>
1da177e4
LT
35
36#include <linux/mtd/mtd.h>
37#include <linux/mtd/map.h>
38#include <linux/mtd/partitions.h>
9fd1e8f9 39#include <linux/mtd/concat.h>
1da177e4
LT
40
41#include <asm/mach/flash.h>
a09e64fb 42#include <mach/hardware.h>
1da177e4
LT
43#include <asm/system.h>
44
9fd1e8f9 45struct armflash_subdev_info {
17659c60 46 char *name;
9fd1e8f9
CM
47 struct mtd_info *mtd;
48 struct map_info map;
1da177e4 49 struct flash_platform_data *plat;
9fd1e8f9
CM
50};
51
52struct armflash_info {
1da177e4
LT
53 struct resource *res;
54 struct mtd_partition *parts;
55 struct mtd_info *mtd;
9fd1e8f9
CM
56 int nr_subdev;
57 struct armflash_subdev_info subdev[0];
1da177e4
LT
58};
59
60static void armflash_set_vpp(struct map_info *map, int on)
61{
9fd1e8f9
CM
62 struct armflash_subdev_info *info =
63 container_of(map, struct armflash_subdev_info, map);
1da177e4
LT
64
65 if (info->plat && info->plat->set_vpp)
66 info->plat->set_vpp(on);
67}
68
69static const char *probes[] = { "cmdlinepart", "RedBoot", "afs", NULL };
70
9fd1e8f9
CM
71static int armflash_subdev_probe(struct armflash_subdev_info *subdev,
72 struct resource *res)
1da177e4 73{
9fd1e8f9
CM
74 struct flash_platform_data *plat = subdev->plat;
75 resource_size_t size = res->end - res->start + 1;
1da177e4 76 void __iomem *base;
9fd1e8f9 77 int err = 0;
1da177e4 78
9fd1e8f9 79 if (!request_mem_region(res->start, size, subdev->name)) {
1da177e4 80 err = -EBUSY;
9fd1e8f9 81 goto out;
1da177e4
LT
82 }
83
84 base = ioremap(res->start, size);
85 if (!base) {
86 err = -ENOMEM;
87 goto no_mem;
88 }
89
90 /*
91 * look for CFI based flash parts fitted to this board
92 */
9fd1e8f9
CM
93 subdev->map.size = size;
94 subdev->map.bankwidth = plat->width;
95 subdev->map.phys = res->start;
96 subdev->map.virt = base;
97 subdev->map.name = subdev->name;
98 subdev->map.set_vpp = armflash_set_vpp;
1da177e4 99
9fd1e8f9 100 simple_map_init(&subdev->map);
1da177e4
LT
101
102 /*
103 * Also, the CFI layer automatically works out what size
104 * of chips we have, and does the necessary identification
105 * for us automatically.
106 */
9fd1e8f9
CM
107 subdev->mtd = do_map_probe(plat->map_name, &subdev->map);
108 if (!subdev->mtd) {
1da177e4
LT
109 err = -ENXIO;
110 goto no_device;
111 }
112
9fd1e8f9
CM
113 subdev->mtd->owner = THIS_MODULE;
114
115 /* Successful? */
116 if (err == 0)
117 return err;
118
119 if (subdev->mtd)
120 map_destroy(subdev->mtd);
121 no_device:
122 iounmap(base);
123 no_mem:
124 release_mem_region(res->start, size);
125 out:
126 return err;
127}
128
129static void armflash_subdev_remove(struct armflash_subdev_info *subdev)
130{
131 if (subdev->mtd)
132 map_destroy(subdev->mtd);
133 if (subdev->map.virt)
134 iounmap(subdev->map.virt);
17659c60
DW
135 kfree(subdev->name);
136 subdev->name = NULL;
9fd1e8f9
CM
137 release_mem_region(subdev->map.phys, subdev->map.size);
138}
139
140static int armflash_probe(struct platform_device *dev)
141{
142 struct flash_platform_data *plat = dev->dev.platform_data;
143 unsigned int size;
144 struct armflash_info *info;
145 int i, nr, err;
146
147 /* Count the number of devices */
148 for (nr = 0; ; nr++)
149 if (!platform_get_resource(dev, IORESOURCE_MEM, nr))
150 break;
151 if (nr == 0) {
152 err = -ENODEV;
153 goto out;
154 }
155
156 size = sizeof(struct armflash_info) +
157 sizeof(struct armflash_subdev_info) * nr;
158 info = kzalloc(size, GFP_KERNEL);
159 if (!info) {
160 err = -ENOMEM;
161 goto out;
162 }
163
164 if (plat && plat->init) {
165 err = plat->init();
166 if (err)
167 goto no_resource;
168 }
169
170 for (i = 0; i < nr; i++) {
171 struct armflash_subdev_info *subdev = &info->subdev[i];
172 struct resource *res;
173
174 res = platform_get_resource(dev, IORESOURCE_MEM, i);
175 if (!res)
176 break;
177
178 if (nr == 1)
179 /* No MTD concatenation, just use the default name */
17659c60 180 subdev->name = kstrdup(dev_name(&dev->dev), GFP_KERNEL);
9fd1e8f9 181 else
17659c60
DW
182 subdev->name = kasprintf(GFP_KERNEL, "%s-%d",
183 dev_name(&dev->dev), i);
184 if (!subdev->name) {
185 err = -ENOMEM;
186 break;
187 }
9fd1e8f9
CM
188 subdev->plat = plat;
189
190 err = armflash_subdev_probe(subdev, res);
17659c60
DW
191 if (err) {
192 kfree(subdev->name);
193 subdev->name = NULL;
9fd1e8f9 194 break;
17659c60 195 }
9fd1e8f9
CM
196 }
197 info->nr_subdev = i;
198
199 if (err)
200 goto subdev_err;
201
202 if (info->nr_subdev == 1)
203 info->mtd = info->subdev[0].mtd;
204 else if (info->nr_subdev > 1) {
205#ifdef CONFIG_MTD_CONCAT
206 struct mtd_info *cdev[info->nr_subdev];
207
208 /*
209 * We detected multiple devices. Concatenate them together.
210 */
211 for (i = 0; i < info->nr_subdev; i++)
212 cdev[i] = info->subdev[i].mtd;
213
214 info->mtd = mtd_concat_create(cdev, info->nr_subdev,
215 dev_name(&dev->dev));
216 if (info->mtd == NULL)
217 err = -ENXIO;
218#else
219 printk(KERN_ERR "armflash: multiple devices found but "
220 "MTD concat support disabled.\n");
221 err = -ENXIO;
222#endif
223 }
224
225 if (err < 0)
226 goto cleanup;
1da177e4
LT
227
228 err = parse_mtd_partitions(info->mtd, probes, &info->parts, 0);
229 if (err > 0) {
230 err = add_mtd_partitions(info->mtd, info->parts, err);
231 if (err)
232 printk(KERN_ERR
233 "mtd partition registration failed: %d\n", err);
234 }
235
9fd1e8f9 236 if (err == 0) {
3ae5eaec 237 platform_set_drvdata(dev, info);
9fd1e8f9
CM
238 return err;
239 }
1da177e4
LT
240
241 /*
9fd1e8f9 242 * We got an error, free all resources.
1da177e4 243 */
9fd1e8f9
CM
244 cleanup:
245 if (info->mtd) {
246 del_mtd_partitions(info->mtd);
247#ifdef CONFIG_MTD_CONCAT
248 if (info->mtd != info->subdev[0].mtd)
249 mtd_concat_destroy(info->mtd);
250#endif
1da177e4 251 }
9fd1e8f9
CM
252 kfree(info->parts);
253 subdev_err:
254 for (i = info->nr_subdev - 1; i >= 0; i--)
255 armflash_subdev_remove(&info->subdev[i]);
256 no_resource:
257 if (plat && plat->exit)
258 plat->exit();
259 kfree(info);
1da177e4
LT
260 out:
261 return err;
262}
263
3ae5eaec 264static int armflash_remove(struct platform_device *dev)
1da177e4 265{
3ae5eaec 266 struct armflash_info *info = platform_get_drvdata(dev);
9fd1e8f9
CM
267 struct flash_platform_data *plat = dev->dev.platform_data;
268 int i;
1da177e4 269
3ae5eaec 270 platform_set_drvdata(dev, NULL);
1da177e4
LT
271
272 if (info) {
273 if (info->mtd) {
274 del_mtd_partitions(info->mtd);
9fd1e8f9
CM
275#ifdef CONFIG_MTD_CONCAT
276 if (info->mtd != info->subdev[0].mtd)
277 mtd_concat_destroy(info->mtd);
278#endif
1da177e4 279 }
fa671646 280 kfree(info->parts);
1da177e4 281
9fd1e8f9
CM
282 for (i = info->nr_subdev - 1; i >= 0; i--)
283 armflash_subdev_remove(&info->subdev[i]);
1da177e4 284
9fd1e8f9
CM
285 if (plat && plat->exit)
286 plat->exit();
1da177e4
LT
287
288 kfree(info);
289 }
290
291 return 0;
292}
293
3ae5eaec 294static struct platform_driver armflash_driver = {
1da177e4
LT
295 .probe = armflash_probe,
296 .remove = armflash_remove,
3ae5eaec
RK
297 .driver = {
298 .name = "armflash",
41d867c9 299 .owner = THIS_MODULE,
3ae5eaec 300 },
1da177e4
LT
301};
302
303static int __init armflash_init(void)
304{
3ae5eaec 305 return platform_driver_register(&armflash_driver);
1da177e4
LT
306}
307
308static void __exit armflash_exit(void)
309{
3ae5eaec 310 platform_driver_unregister(&armflash_driver);
1da177e4
LT
311}
312
313module_init(armflash_init);
314module_exit(armflash_exit);
315
316MODULE_AUTHOR("ARM Ltd");
317MODULE_DESCRIPTION("ARM Integrator CFI map driver");
318MODULE_LICENSE("GPL");
41d867c9 319MODULE_ALIAS("platform:armflash");
This page took 0.463151 seconds and 5 git commands to generate.