aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mtd/maps/integrator-flash.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mtd/maps/integrator-flash.c')
-rw-r--r--drivers/mtd/maps/integrator-flash.c232
1 files changed, 170 insertions, 62 deletions
diff --git a/drivers/mtd/maps/integrator-flash.c b/drivers/mtd/maps/integrator-flash.c
index c9681a339a59..2aac41bde8b3 100644
--- a/drivers/mtd/maps/integrator-flash.c
+++ b/drivers/mtd/maps/integrator-flash.c
@@ -36,27 +36,31 @@
36#include <linux/mtd/mtd.h> 36#include <linux/mtd/mtd.h>
37#include <linux/mtd/map.h> 37#include <linux/mtd/map.h>
38#include <linux/mtd/partitions.h> 38#include <linux/mtd/partitions.h>
39#include <linux/mtd/concat.h>
39 40
40#include <asm/mach/flash.h> 41#include <asm/mach/flash.h>
41#include <mach/hardware.h> 42#include <mach/hardware.h>
42#include <asm/system.h> 43#include <asm/system.h>
43 44
44#ifdef CONFIG_ARCH_P720T 45struct armflash_subdev_info {
45#define FLASH_BASE (0x04000000) 46 char *name;
46#define FLASH_SIZE (64*1024*1024) 47 struct mtd_info *mtd;
47#endif 48 struct map_info map;
49 struct flash_platform_data *plat;
50};
48 51
49struct armflash_info { 52struct armflash_info {
50 struct flash_platform_data *plat;
51 struct resource *res; 53 struct resource *res;
52 struct mtd_partition *parts; 54 struct mtd_partition *parts;
53 struct mtd_info *mtd; 55 struct mtd_info *mtd;
54 struct map_info map; 56 int nr_subdev;
57 struct armflash_subdev_info subdev[0];
55}; 58};
56 59
57static void armflash_set_vpp(struct map_info *map, int on) 60static void armflash_set_vpp(struct map_info *map, int on)
58{ 61{
59 struct armflash_info *info = container_of(map, struct armflash_info, map); 62 struct armflash_subdev_info *info =
63 container_of(map, struct armflash_subdev_info, map);
60 64
61 if (info->plat && info->plat->set_vpp) 65 if (info->plat && info->plat->set_vpp)
62 info->plat->set_vpp(on); 66 info->plat->set_vpp(on);
@@ -64,32 +68,17 @@ static void armflash_set_vpp(struct map_info *map, int on)
64 68
65static const char *probes[] = { "cmdlinepart", "RedBoot", "afs", NULL }; 69static const char *probes[] = { "cmdlinepart", "RedBoot", "afs", NULL };
66 70
67static int armflash_probe(struct platform_device *dev) 71static int armflash_subdev_probe(struct armflash_subdev_info *subdev,
72 struct resource *res)
68{ 73{
69 struct flash_platform_data *plat = dev->dev.platform_data; 74 struct flash_platform_data *plat = subdev->plat;
70 struct resource *res = dev->resource; 75 resource_size_t size = res->end - res->start + 1;
71 unsigned int size = res->end - res->start + 1;
72 struct armflash_info *info;
73 int err;
74 void __iomem *base; 76 void __iomem *base;
77 int err = 0;
75 78
76 info = kzalloc(sizeof(struct armflash_info), GFP_KERNEL); 79 if (!request_mem_region(res->start, size, subdev->name)) {
77 if (!info) {
78 err = -ENOMEM;
79 goto out;
80 }
81
82 info->plat = plat;
83 if (plat && plat->init) {
84 err = plat->init();
85 if (err)
86 goto no_resource;
87 }
88
89 info->res = request_mem_region(res->start, size, "armflash");
90 if (!info->res) {
91 err = -EBUSY; 80 err = -EBUSY;
92 goto no_resource; 81 goto out;
93 } 82 }
94 83
95 base = ioremap(res->start, size); 84 base = ioremap(res->start, size);
@@ -101,27 +90,140 @@ static int armflash_probe(struct platform_device *dev)
101 /* 90 /*
102 * look for CFI based flash parts fitted to this board 91 * look for CFI based flash parts fitted to this board
103 */ 92 */
104 info->map.size = size; 93 subdev->map.size = size;
105 info->map.bankwidth = plat->width; 94 subdev->map.bankwidth = plat->width;
106 info->map.phys = res->start; 95 subdev->map.phys = res->start;
107 info->map.virt = base; 96 subdev->map.virt = base;
108 info->map.name = dev_name(&dev->dev); 97 subdev->map.name = subdev->name;
109 info->map.set_vpp = armflash_set_vpp; 98 subdev->map.set_vpp = armflash_set_vpp;
110 99
111 simple_map_init(&info->map); 100 simple_map_init(&subdev->map);
112 101
113 /* 102 /*
114 * Also, the CFI layer automatically works out what size 103 * Also, the CFI layer automatically works out what size
115 * of chips we have, and does the necessary identification 104 * of chips we have, and does the necessary identification
116 * for us automatically. 105 * for us automatically.
117 */ 106 */
118 info->mtd = do_map_probe(plat->map_name, &info->map); 107 subdev->mtd = do_map_probe(plat->map_name, &subdev->map);
119 if (!info->mtd) { 108 if (!subdev->mtd) {
120 err = -ENXIO; 109 err = -ENXIO;
121 goto no_device; 110 goto no_device;
122 } 111 }
123 112
124 info->mtd->owner = THIS_MODULE; 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);
135 kfree(subdev->name);
136 subdev->name = NULL;
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 */
180 subdev->name = kstrdup(dev_name(&dev->dev), GFP_KERNEL);
181 else
182 subdev->name = kasprintf(GFP_KERNEL, "%s-%d",
183 dev_name(&dev->dev), i);
184 if (!subdev->name) {
185 err = -ENOMEM;
186 break;
187 }
188 subdev->plat = plat;
189
190 err = armflash_subdev_probe(subdev, res);
191 if (err) {
192 kfree(subdev->name);
193 subdev->name = NULL;
194 break;
195 }
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;
125 227
126 err = parse_mtd_partitions(info->mtd, probes, &info->parts, 0); 228 err = parse_mtd_partitions(info->mtd, probes, &info->parts, 0);
127 if (err > 0) { 229 if (err > 0) {
@@ -131,28 +233,30 @@ static int armflash_probe(struct platform_device *dev)
131 "mtd partition registration failed: %d\n", err); 233 "mtd partition registration failed: %d\n", err);
132 } 234 }
133 235
134 if (err == 0) 236 if (err == 0) {
135 platform_set_drvdata(dev, info); 237 platform_set_drvdata(dev, info);
238 return err;
239 }
136 240
137 /* 241 /*
138 * If we got an error, free all resources. 242 * We got an error, free all resources.
139 */ 243 */
140 if (err < 0) { 244 cleanup:
141 if (info->mtd) { 245 if (info->mtd) {
142 del_mtd_partitions(info->mtd); 246 del_mtd_partitions(info->mtd);
143 map_destroy(info->mtd); 247#ifdef CONFIG_MTD_CONCAT
144 } 248 if (info->mtd != info->subdev[0].mtd)
145 kfree(info->parts); 249 mtd_concat_destroy(info->mtd);
146 250#endif
147 no_device:
148 iounmap(base);
149 no_mem:
150 release_mem_region(res->start, size);
151 no_resource:
152 if (plat && plat->exit)
153 plat->exit();
154 kfree(info);
155 } 251 }
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);
156 out: 260 out:
157 return err; 261 return err;
158} 262}
@@ -160,22 +264,26 @@ static int armflash_probe(struct platform_device *dev)
160static int armflash_remove(struct platform_device *dev) 264static int armflash_remove(struct platform_device *dev)
161{ 265{
162 struct armflash_info *info = platform_get_drvdata(dev); 266 struct armflash_info *info = platform_get_drvdata(dev);
267 struct flash_platform_data *plat = dev->dev.platform_data;
268 int i;
163 269
164 platform_set_drvdata(dev, NULL); 270 platform_set_drvdata(dev, NULL);
165 271
166 if (info) { 272 if (info) {
167 if (info->mtd) { 273 if (info->mtd) {
168 del_mtd_partitions(info->mtd); 274 del_mtd_partitions(info->mtd);
169 map_destroy(info->mtd); 275#ifdef CONFIG_MTD_CONCAT
276 if (info->mtd != info->subdev[0].mtd)
277 mtd_concat_destroy(info->mtd);
278#endif
170 } 279 }
171 kfree(info->parts); 280 kfree(info->parts);
172 281
173 iounmap(info->map.virt); 282 for (i = info->nr_subdev - 1; i >= 0; i--)
174 release_resource(info->res); 283 armflash_subdev_remove(&info->subdev[i]);
175 kfree(info->res);
176 284
177 if (info->plat && info->plat->exit) 285 if (plat && plat->exit)
178 info->plat->exit(); 286 plat->exit();
179 287
180 kfree(info); 288 kfree(info);
181 } 289 }