diff options
author | Stefan Roese <sr@denx.de> | 2009-04-16 08:10:45 -0400 |
---|---|---|
committer | David Woodhouse <David.Woodhouse@intel.com> | 2009-06-06 03:27:42 -0400 |
commit | 143070e74630b9557e1bb64d899ff2cc5a1dcb48 (patch) | |
tree | 90f22516649e24837cf435b7ca9f86da0374d257 /drivers/mtd | |
parent | 43950a605dc76677f0c74dcd818a57d4df040e12 (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')
-rw-r--r-- | drivers/mtd/maps/physmap_of.c | 199 |
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 | ||
27 | struct of_flash_list { | ||
28 | struct mtd_info *mtd; | ||
29 | struct map_info map; | ||
30 | struct resource *res; | ||
31 | }; | ||
32 | |||
26 | struct of_flash { | 33 | struct 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, | |||
88 | static int of_flash_remove(struct of_device *dev) | 95 | static 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 | ||
254 | err_out: | 339 | err_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 | ||