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.c226
1 files changed, 164 insertions, 62 deletions
diff --git a/drivers/mtd/maps/integrator-flash.c b/drivers/mtd/maps/integrator-flash.c
index c9681a339a59..b08a798ee254 100644
--- a/drivers/mtd/maps/integrator-flash.c
+++ b/drivers/mtd/maps/integrator-flash.c
@@ -36,27 +36,33 @@
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 45#define SUBDEV_NAME_SIZE (BUS_ID_SIZE + 2)
45#define FLASH_BASE (0x04000000)
46#define FLASH_SIZE (64*1024*1024)
47#endif
48 46
49struct armflash_info { 47struct armflash_subdev_info {
48 char name[SUBDEV_NAME_SIZE];
49 struct mtd_info *mtd;
50 struct map_info map;
50 struct flash_platform_data *plat; 51 struct flash_platform_data *plat;
52};
53
54struct armflash_info {
51 struct resource *res; 55 struct resource *res;
52 struct mtd_partition *parts; 56 struct mtd_partition *parts;
53 struct mtd_info *mtd; 57 struct mtd_info *mtd;
54 struct map_info map; 58 int nr_subdev;
59 struct armflash_subdev_info subdev[0];
55}; 60};
56 61
57static void armflash_set_vpp(struct map_info *map, int on) 62static void armflash_set_vpp(struct map_info *map, int on)
58{ 63{
59 struct armflash_info *info = container_of(map, struct armflash_info, map); 64 struct armflash_subdev_info *info =
65 container_of(map, struct armflash_subdev_info, map);
60 66
61 if (info->plat && info->plat->set_vpp) 67 if (info->plat && info->plat->set_vpp)
62 info->plat->set_vpp(on); 68 info->plat->set_vpp(on);
@@ -64,32 +70,17 @@ static void armflash_set_vpp(struct map_info *map, int on)
64 70
65static const char *probes[] = { "cmdlinepart", "RedBoot", "afs", NULL }; 71static const char *probes[] = { "cmdlinepart", "RedBoot", "afs", NULL };
66 72
67static int armflash_probe(struct platform_device *dev) 73static int armflash_subdev_probe(struct armflash_subdev_info *subdev,
74 struct resource *res)
68{ 75{
69 struct flash_platform_data *plat = dev->dev.platform_data; 76 struct flash_platform_data *plat = subdev->plat;
70 struct resource *res = dev->resource; 77 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; 78 void __iomem *base;
79 int err = 0;
75 80
76 info = kzalloc(sizeof(struct armflash_info), GFP_KERNEL); 81 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; 82 err = -EBUSY;
92 goto no_resource; 83 goto out;
93 } 84 }
94 85
95 base = ioremap(res->start, size); 86 base = ioremap(res->start, size);
@@ -101,27 +92,132 @@ static int armflash_probe(struct platform_device *dev)
101 /* 92 /*
102 * look for CFI based flash parts fitted to this board 93 * look for CFI based flash parts fitted to this board
103 */ 94 */
104 info->map.size = size; 95 subdev->map.size = size;
105 info->map.bankwidth = plat->width; 96 subdev->map.bankwidth = plat->width;
106 info->map.phys = res->start; 97 subdev->map.phys = res->start;
107 info->map.virt = base; 98 subdev->map.virt = base;
108 info->map.name = dev_name(&dev->dev); 99 subdev->map.name = subdev->name;
109 info->map.set_vpp = armflash_set_vpp; 100 subdev->map.set_vpp = armflash_set_vpp;
110 101
111 simple_map_init(&info->map); 102 simple_map_init(&subdev->map);
112 103
113 /* 104 /*
114 * Also, the CFI layer automatically works out what size 105 * Also, the CFI layer automatically works out what size
115 * of chips we have, and does the necessary identification 106 * of chips we have, and does the necessary identification
116 * for us automatically. 107 * for us automatically.
117 */ 108 */
118 info->mtd = do_map_probe(plat->map_name, &info->map); 109 subdev->mtd = do_map_probe(plat->map_name, &subdev->map);
119 if (!info->mtd) { 110 if (!subdev->mtd) {
120 err = -ENXIO; 111 err = -ENXIO;
121 goto no_device; 112 goto no_device;
122 } 113 }
123 114
124 info->mtd->owner = THIS_MODULE; 115 subdev->mtd->owner = THIS_MODULE;
116
117 /* Successful? */
118 if (err == 0)
119 return err;
120
121 if (subdev->mtd)
122 map_destroy(subdev->mtd);
123 no_device:
124 iounmap(base);
125 no_mem:
126 release_mem_region(res->start, size);
127 out:
128 return err;
129}
130
131static void armflash_subdev_remove(struct armflash_subdev_info *subdev)
132{
133 if (subdev->mtd)
134 map_destroy(subdev->mtd);
135 if (subdev->map.virt)
136 iounmap(subdev->map.virt);
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 snprintf(subdev->name, SUBDEV_NAME_SIZE, "%s",
181 dev_name(&dev->dev));
182 else
183 snprintf(subdev->name, SUBDEV_NAME_SIZE, "%s-%d",
184 dev_name(&dev->dev), i);
185 subdev->plat = plat;
186
187 err = armflash_subdev_probe(subdev, res);
188 if (err)
189 break;
190 }
191 info->nr_subdev = i;
192
193 if (err)
194 goto subdev_err;
195
196 if (info->nr_subdev == 1)
197 info->mtd = info->subdev[0].mtd;
198 else if (info->nr_subdev > 1) {
199#ifdef CONFIG_MTD_CONCAT
200 struct mtd_info *cdev[info->nr_subdev];
201
202 /*
203 * We detected multiple devices. Concatenate them together.
204 */
205 for (i = 0; i < info->nr_subdev; i++)
206 cdev[i] = info->subdev[i].mtd;
207
208 info->mtd = mtd_concat_create(cdev, info->nr_subdev,
209 dev_name(&dev->dev));
210 if (info->mtd == NULL)
211 err = -ENXIO;
212#else
213 printk(KERN_ERR "armflash: multiple devices found but "
214 "MTD concat support disabled.\n");
215 err = -ENXIO;
216#endif
217 }
218
219 if (err < 0)
220 goto cleanup;
125 221
126 err = parse_mtd_partitions(info->mtd, probes, &info->parts, 0); 222 err = parse_mtd_partitions(info->mtd, probes, &info->parts, 0);
127 if (err > 0) { 223 if (err > 0) {
@@ -131,28 +227,30 @@ static int armflash_probe(struct platform_device *dev)
131 "mtd partition registration failed: %d\n", err); 227 "mtd partition registration failed: %d\n", err);
132 } 228 }
133 229
134 if (err == 0) 230 if (err == 0) {
135 platform_set_drvdata(dev, info); 231 platform_set_drvdata(dev, info);
232 return err;
233 }
136 234
137 /* 235 /*
138 * If we got an error, free all resources. 236 * We got an error, free all resources.
139 */ 237 */
140 if (err < 0) { 238 cleanup:
141 if (info->mtd) { 239 if (info->mtd) {
142 del_mtd_partitions(info->mtd); 240 del_mtd_partitions(info->mtd);
143 map_destroy(info->mtd); 241#ifdef CONFIG_MTD_CONCAT
144 } 242 if (info->mtd != info->subdev[0].mtd)
145 kfree(info->parts); 243 mtd_concat_destroy(info->mtd);
146 244#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 } 245 }
246 kfree(info->parts);
247 subdev_err:
248 for (i = info->nr_subdev - 1; i >= 0; i--)
249 armflash_subdev_remove(&info->subdev[i]);
250 no_resource:
251 if (plat && plat->exit)
252 plat->exit();
253 kfree(info);
156 out: 254 out:
157 return err; 255 return err;
158} 256}
@@ -160,22 +258,26 @@ static int armflash_probe(struct platform_device *dev)
160static int armflash_remove(struct platform_device *dev) 258static int armflash_remove(struct platform_device *dev)
161{ 259{
162 struct armflash_info *info = platform_get_drvdata(dev); 260 struct armflash_info *info = platform_get_drvdata(dev);
261 struct flash_platform_data *plat = dev->dev.platform_data;
262 int i;
163 263
164 platform_set_drvdata(dev, NULL); 264 platform_set_drvdata(dev, NULL);
165 265
166 if (info) { 266 if (info) {
167 if (info->mtd) { 267 if (info->mtd) {
168 del_mtd_partitions(info->mtd); 268 del_mtd_partitions(info->mtd);
169 map_destroy(info->mtd); 269#ifdef CONFIG_MTD_CONCAT
270 if (info->mtd != info->subdev[0].mtd)
271 mtd_concat_destroy(info->mtd);
272#endif
170 } 273 }
171 kfree(info->parts); 274 kfree(info->parts);
172 275
173 iounmap(info->map.virt); 276 for (i = info->nr_subdev - 1; i >= 0; i--)
174 release_resource(info->res); 277 armflash_subdev_remove(&info->subdev[i]);
175 kfree(info->res);
176 278
177 if (info->plat && info->plat->exit) 279 if (plat && plat->exit)
178 info->plat->exit(); 280 plat->exit();
179 281
180 kfree(info); 282 kfree(info);
181 } 283 }