aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mtd/maps
diff options
context:
space:
mode:
authorStefan Roese <sr@denx.de>2009-04-16 08:10:45 -0400
committerDavid Woodhouse <David.Woodhouse@intel.com>2009-06-06 03:27:42 -0400
commit143070e74630b9557e1bb64d899ff2cc5a1dcb48 (patch)
tree90f22516649e24837cf435b7ca9f86da0374d257 /drivers/mtd/maps
parent43950a605dc76677f0c74dcd818a57d4df040e12 (diff)
mtd: physmap_of: Add multiple regions and concatenation support
This patch adds support to handle multiple non-identical chips in one flash device tree node. It also adds concat support to physmap_of. This makes it possible to support e.g. the Intel P30 48F4400 chips which internally consists of 2 non-identical NOR chips on one die. Additionally partitions now can span over multiple chips. To describe such a chip's, multiple "reg" tuples are now supported in one flash device tree node. Here an dts example: flash@f0000000,0 { #address-cells = <1>; #size-cells = <1>; compatible = "cfi-flash"; reg = <0 0x00000000 0x02000000 0 0x02000000 0x02000000>; bank-width = <2>; partition@0 { label = "test-part1"; reg = <0 0x04000000>; }; }; Signed-off-by: Stefan Roese <sr@denx.de> Reviewed-by: Grant Likely <grant.likely@secretlab.ca> Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
Diffstat (limited to 'drivers/mtd/maps')
-rw-r--r--drivers/mtd/maps/physmap_of.c199
1 files changed, 143 insertions, 56 deletions
diff --git a/drivers/mtd/maps/physmap_of.c b/drivers/mtd/maps/physmap_of.c
index c83a60fada53..39d357b2eb47 100644
--- a/drivers/mtd/maps/physmap_of.c
+++ b/drivers/mtd/maps/physmap_of.c
@@ -20,16 +20,23 @@
20#include <linux/mtd/mtd.h> 20#include <linux/mtd/mtd.h>
21#include <linux/mtd/map.h> 21#include <linux/mtd/map.h>
22#include <linux/mtd/partitions.h> 22#include <linux/mtd/partitions.h>
23#include <linux/mtd/concat.h>
23#include <linux/of.h> 24#include <linux/of.h>
24#include <linux/of_platform.h> 25#include <linux/of_platform.h>
25 26
27struct of_flash_list {
28 struct mtd_info *mtd;
29 struct map_info map;
30 struct resource *res;
31};
32
26struct of_flash { 33struct of_flash {
27 struct mtd_info *mtd; 34 struct mtd_info *cmtd;
28 struct map_info map;
29 struct resource *res;
30#ifdef CONFIG_MTD_PARTITIONS 35#ifdef CONFIG_MTD_PARTITIONS
31 struct mtd_partition *parts; 36 struct mtd_partition *parts;
32#endif 37#endif
38 int list_size; /* number of elements in of_flash_list */
39 struct of_flash_list list[0];
33}; 40};
34 41
35#ifdef CONFIG_MTD_PARTITIONS 42#ifdef CONFIG_MTD_PARTITIONS
@@ -88,30 +95,44 @@ static int parse_obsolete_partitions(struct of_device *dev,
88static int of_flash_remove(struct of_device *dev) 95static int of_flash_remove(struct of_device *dev)
89{ 96{
90 struct of_flash *info; 97 struct of_flash *info;
98 int i;
91 99
92 info = dev_get_drvdata(&dev->dev); 100 info = dev_get_drvdata(&dev->dev);
93 if (!info) 101 if (!info)
94 return 0; 102 return 0;
95 dev_set_drvdata(&dev->dev, NULL); 103 dev_set_drvdata(&dev->dev, NULL);
96 104
97 if (info->mtd) { 105#ifdef CONFIG_MTD_CONCAT
106 if (info->cmtd != info->list[0].mtd) {
107 del_mtd_device(info->cmtd);
108 mtd_concat_destroy(info->cmtd);
109 }
110#endif
111
112 if (info->cmtd) {
98 if (OF_FLASH_PARTS(info)) { 113 if (OF_FLASH_PARTS(info)) {
99 del_mtd_partitions(info->mtd); 114 del_mtd_partitions(info->cmtd);
100 kfree(OF_FLASH_PARTS(info)); 115 kfree(OF_FLASH_PARTS(info));
101 } else { 116 } else {
102 del_mtd_device(info->mtd); 117 del_mtd_device(info->cmtd);
103 } 118 }
104 map_destroy(info->mtd);
105 } 119 }
106 120
107 if (info->map.virt) 121 for (i = 0; i < info->list_size; i++) {
108 iounmap(info->map.virt); 122 if (info->list[i].mtd)
123 map_destroy(info->list[i].mtd);
109 124
110 if (info->res) { 125 if (info->list[i].map.virt)
111 release_resource(info->res); 126 iounmap(info->list[i].map.virt);
112 kfree(info->res); 127
128 if (info->list[i].res) {
129 release_resource(info->list[i].res);
130 kfree(info->list[i].res);
131 }
113 } 132 }
114 133
134 kfree(info);
135
115 return 0; 136 return 0;
116} 137}
117 138
@@ -164,68 +185,130 @@ static int __devinit of_flash_probe(struct of_device *dev,
164 const char *probe_type = match->data; 185 const char *probe_type = match->data;
165 const u32 *width; 186 const u32 *width;
166 int err; 187 int err;
167 188 int i;
168 err = -ENXIO; 189 int count;
169 if (of_address_to_resource(dp, 0, &res)) { 190 const u32 *p;
170 dev_err(&dev->dev, "Can't get IO address from device tree\n"); 191 int reg_tuple_size;
192 struct mtd_info **mtd_list = NULL;
193
194 reg_tuple_size = (of_n_addr_cells(dp) + of_n_size_cells(dp)) * sizeof(u32);
195
196 /*
197 * Get number of "reg" tuples. Scan for MTD devices on area's
198 * described by each "reg" region. This makes it possible (including
199 * the concat support) to support the Intel P30 48F4400 chips which
200 * consists internally of 2 non-identical NOR chips on one die.
201 */
202 p = of_get_property(dp, "reg", &count);
203 if (count % reg_tuple_size != 0) {
204 dev_err(&dev->dev, "Malformed reg property on %s\n",
205 dev->node->full_name);
206 err = -EINVAL;
171 goto err_out; 207 goto err_out;
172 } 208 }
173 209 count /= reg_tuple_size;
174 dev_dbg(&dev->dev, "of_flash device: %.8llx-%.8llx\n",
175 (unsigned long long)res.start, (unsigned long long)res.end);
176 210
177 err = -ENOMEM; 211 err = -ENOMEM;
178 info = kzalloc(sizeof(*info), GFP_KERNEL); 212 info = kzalloc(sizeof(struct of_flash) +
213 sizeof(struct of_flash_list) * count, GFP_KERNEL);
214 if (!info)
215 goto err_out;
216
217 mtd_list = kzalloc(sizeof(struct mtd_info) * count, GFP_KERNEL);
179 if (!info) 218 if (!info)
180 goto err_out; 219 goto err_out;
181 220
182 dev_set_drvdata(&dev->dev, info); 221 dev_set_drvdata(&dev->dev, info);
183 222
184 err = -EBUSY; 223 for (i = 0; i < count; i++) {
185 info->res = request_mem_region(res.start, res.end - res.start + 1, 224 err = -ENXIO;
186 dev_name(&dev->dev)); 225 if (of_address_to_resource(dp, i, &res)) {
187 if (!info->res) 226 dev_err(&dev->dev, "Can't get IO address from device"
188 goto err_out; 227 " tree\n");
228 goto err_out;
229 }
189 230
190 err = -ENXIO; 231 dev_dbg(&dev->dev, "of_flash device: %.8llx-%.8llx\n",
191 width = of_get_property(dp, "bank-width", NULL); 232 (unsigned long long)res.start,
192 if (!width) { 233 (unsigned long long)res.end);
193 dev_err(&dev->dev, "Can't get bank width from device tree\n"); 234
194 goto err_out; 235 err = -EBUSY;
195 } 236 info->list[i].res = request_mem_region(res.start, res.end -
237 res.start + 1,
238 dev_name(&dev->dev));
239 if (!info->list[i].res)
240 goto err_out;
241
242 err = -ENXIO;
243 width = of_get_property(dp, "bank-width", NULL);
244 if (!width) {
245 dev_err(&dev->dev, "Can't get bank width from device"
246 " tree\n");
247 goto err_out;
248 }
196 249
197 info->map.name = dev_name(&dev->dev); 250 info->list[i].map.name = dev_name(&dev->dev);
198 info->map.phys = res.start; 251 info->list[i].map.phys = res.start;
199 info->map.size = res.end - res.start + 1; 252 info->list[i].map.size = res.end - res.start + 1;
200 info->map.bankwidth = *width; 253 info->list[i].map.bankwidth = *width;
254
255 err = -ENOMEM;
256 info->list[i].map.virt = ioremap(info->list[i].map.phys,
257 info->list[i].map.size);
258 if (!info->list[i].map.virt) {
259 dev_err(&dev->dev, "Failed to ioremap() flash"
260 " region\n");
261 goto err_out;
262 }
201 263
202 err = -ENOMEM; 264 simple_map_init(&info->list[i].map);
203 info->map.virt = ioremap(info->map.phys, info->map.size);
204 if (!info->map.virt) {
205 dev_err(&dev->dev, "Failed to ioremap() flash region\n");
206 goto err_out;
207 }
208 265
209 simple_map_init(&info->map); 266 if (probe_type) {
267 info->list[i].mtd = do_map_probe(probe_type,
268 &info->list[i].map);
269 } else {
270 info->list[i].mtd = obsolete_probe(dev,
271 &info->list[i].map);
272 }
273 mtd_list[i] = info->list[i].mtd;
210 274
211 if (probe_type) 275 err = -ENXIO;
212 info->mtd = do_map_probe(probe_type, &info->map); 276 if (!info->list[i].mtd) {
213 else 277 dev_err(&dev->dev, "do_map_probe() failed\n");
214 info->mtd = obsolete_probe(dev, &info->map); 278 goto err_out;
279 } else {
280 info->list_size++;
281 }
282 info->list[i].mtd->owner = THIS_MODULE;
283 info->list[i].mtd->dev.parent = &dev->dev;
284 }
215 285
216 err = -ENXIO; 286 err = 0;
217 if (!info->mtd) { 287 if (info->list_size == 1) {
218 dev_err(&dev->dev, "do_map_probe() failed\n"); 288 info->cmtd = info->list[0].mtd;
219 goto err_out; 289 } else if (info->list_size > 1) {
290 /*
291 * We detected multiple devices. Concatenate them together.
292 */
293#ifdef CONFIG_MTD_CONCAT
294 info->cmtd = mtd_concat_create(mtd_list, info->list_size,
295 dev_name(&dev->dev));
296 if (info->cmtd == NULL)
297 err = -ENXIO;
298#else
299 printk(KERN_ERR "physmap_of: multiple devices "
300 "found but MTD concat support disabled.\n");
301 err = -ENXIO;
302#endif
220 } 303 }
221 info->mtd->owner = THIS_MODULE; 304 if (err)
222 info->mtd->dev.parent = &dev->dev; 305 goto err_out;
223 306
224#ifdef CONFIG_MTD_PARTITIONS 307#ifdef CONFIG_MTD_PARTITIONS
225 /* First look for RedBoot table or partitions on the command 308 /* First look for RedBoot table or partitions on the command
226 * line, these take precedence over device tree information */ 309 * line, these take precedence over device tree information */
227 err = parse_mtd_partitions(info->mtd, part_probe_types, 310 err = parse_mtd_partitions(info->cmtd, part_probe_types,
228 &info->parts, 0); 311 &info->parts, 0);
229 if (err < 0) 312 if (err < 0)
230 return err; 313 return err;
231 314
@@ -244,15 +327,19 @@ static int __devinit of_flash_probe(struct of_device *dev,
244 } 327 }
245 328
246 if (err > 0) 329 if (err > 0)
247 add_mtd_partitions(info->mtd, info->parts, err); 330 add_mtd_partitions(info->cmtd, info->parts, err);
248 else 331 else
249#endif 332#endif
250 add_mtd_device(info->mtd); 333 add_mtd_device(info->cmtd);
334
335 kfree(mtd_list);
251 336
252 return 0; 337 return 0;
253 338
254err_out: 339err_out:
340 kfree(mtd_list);
255 of_flash_remove(dev); 341 of_flash_remove(dev);
342
256 return err; 343 return err;
257} 344}
258 345