diff options
author | David Gibson <david@gibson.dropbear.id.au> | 2007-09-06 23:23:53 -0400 |
---|---|---|
committer | Paul Mackerras <paulus@samba.org> | 2007-09-13 11:33:25 -0400 |
commit | 2099172d61abda1b793b499bb8edcaac4de2cdae (patch) | |
tree | 4b3bff327ad8e61fe5f71f9d399269c8db41220c /drivers/mtd | |
parent | 3c607ce2a3213f33b8b6b854b5f7db876021e466 (diff) |
[POWERPC] Document and implement an improved flash device binding for powerpc
This replaces the binding for flash chips in booting-without-of.txt
with an clarified and improved version. It also makes
drivers/mtd/maps/physmap_of.c recognize this new binding. Finally it
revises the Ebony device tree source to use the new binding as an
example.
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
Acked-by: Segher Boessenkool <segher@kernel.crashing.org>
Signed-off-by: Paul Mackerras <paulus@samba.org>
Diffstat (limited to 'drivers/mtd')
-rw-r--r-- | drivers/mtd/maps/physmap_of.c | 239 |
1 files changed, 174 insertions, 65 deletions
diff --git a/drivers/mtd/maps/physmap_of.c b/drivers/mtd/maps/physmap_of.c index bbb42c35b69b..3df001bfee36 100644 --- a/drivers/mtd/maps/physmap_of.c +++ b/drivers/mtd/maps/physmap_of.c | |||
@@ -4,6 +4,9 @@ | |||
4 | * Copyright (C) 2006 MontaVista Software Inc. | 4 | * Copyright (C) 2006 MontaVista Software Inc. |
5 | * Author: Vitaly Wool <vwool@ru.mvista.com> | 5 | * Author: Vitaly Wool <vwool@ru.mvista.com> |
6 | * | 6 | * |
7 | * Revised to handle newer style flash binding by: | ||
8 | * Copyright (C) 2007 David Gibson, IBM Corporation. | ||
9 | * | ||
7 | * This program is free software; you can redistribute it and/or modify it | 10 | * This program is free software; you can redistribute it and/or modify it |
8 | * under the terms of the GNU General Public License as published by the | 11 | * under the terms of the GNU General Public License as published by the |
9 | * Free Software Foundation; either version 2 of the License, or (at your | 12 | * Free Software Foundation; either version 2 of the License, or (at your |
@@ -30,56 +33,135 @@ struct physmap_flash_info { | |||
30 | struct map_info map; | 33 | struct map_info map; |
31 | struct resource *res; | 34 | struct resource *res; |
32 | #ifdef CONFIG_MTD_PARTITIONS | 35 | #ifdef CONFIG_MTD_PARTITIONS |
33 | int nr_parts; | ||
34 | struct mtd_partition *parts; | 36 | struct mtd_partition *parts; |
35 | #endif | 37 | #endif |
36 | }; | 38 | }; |
37 | 39 | ||
38 | static const char *rom_probe_types[] = { "cfi_probe", "jedec_probe", "map_rom", NULL }; | ||
39 | #ifdef CONFIG_MTD_PARTITIONS | ||
40 | static const char *part_probe_types[] = { "cmdlinepart", "RedBoot", NULL }; | ||
41 | #endif | ||
42 | |||
43 | #ifdef CONFIG_MTD_PARTITIONS | 40 | #ifdef CONFIG_MTD_PARTITIONS |
44 | static int parse_flash_partitions(struct device_node *node, | 41 | static int parse_obsolete_partitions(struct of_device *dev, |
45 | struct mtd_partition **parts) | 42 | struct physmap_flash_info *info, |
43 | struct device_node *dp) | ||
46 | { | 44 | { |
47 | int i, plen, retval = -ENOMEM; | 45 | int i, plen, nr_parts; |
48 | const u32 *part; | 46 | const struct { |
49 | const char *name; | 47 | u32 offset, len; |
48 | } *part; | ||
49 | const char *names; | ||
50 | 50 | ||
51 | part = of_get_property(node, "partitions", &plen); | 51 | part = of_get_property(dp, "partitions", &plen); |
52 | if (part == NULL) | 52 | if (!part) |
53 | goto err; | 53 | return -ENOENT; |
54 | 54 | ||
55 | retval = plen / (2 * sizeof(u32)); | 55 | dev_warn(&dev->dev, "Device tree uses obsolete partition map binding\n"); |
56 | *parts = kzalloc(retval * sizeof(struct mtd_partition), GFP_KERNEL); | 56 | |
57 | if (*parts == NULL) { | 57 | nr_parts = plen / sizeof(part[0]); |
58 | |||
59 | info->parts = kzalloc(nr_parts * sizeof(struct mtd_partition), GFP_KERNEL); | ||
60 | if (!info->parts) { | ||
58 | printk(KERN_ERR "Can't allocate the flash partition data!\n"); | 61 | printk(KERN_ERR "Can't allocate the flash partition data!\n"); |
59 | goto err; | 62 | return -ENOMEM; |
60 | } | 63 | } |
61 | 64 | ||
62 | name = of_get_property(node, "partition-names", &plen); | 65 | names = of_get_property(dp, "partition-names", &plen); |
63 | 66 | ||
64 | for (i = 0; i < retval; i++) { | 67 | for (i = 0; i < nr_parts; i++) { |
65 | (*parts)[i].offset = *part++; | 68 | info->parts[i].offset = part->offset; |
66 | (*parts)[i].size = *part & ~1; | 69 | info->parts[i].size = part->len & ~1; |
67 | if (*part++ & 1) /* bit 0 set signifies read only partition */ | 70 | if (part->len & 1) /* bit 0 set signifies read only partition */ |
68 | (*parts)[i].mask_flags = MTD_WRITEABLE; | 71 | info->parts[i].mask_flags = MTD_WRITEABLE; |
69 | 72 | ||
70 | if (name != NULL && plen > 0) { | 73 | if (names && (plen > 0)) { |
71 | int len = strlen(name) + 1; | 74 | int len = strlen(names) + 1; |
72 | 75 | ||
73 | (*parts)[i].name = (char *)name; | 76 | info->parts[i].name = (char *)names; |
74 | plen -= len; | 77 | plen -= len; |
75 | name += len; | 78 | names += len; |
76 | } else | 79 | } else { |
77 | (*parts)[i].name = "unnamed"; | 80 | info->parts[i].name = "unnamed"; |
81 | } | ||
82 | |||
83 | part++; | ||
78 | } | 84 | } |
79 | err: | 85 | |
80 | return retval; | 86 | return nr_parts; |
81 | } | 87 | } |
82 | #endif | 88 | |
89 | static int __devinit process_partitions(struct physmap_flash_info *info, | ||
90 | struct of_device *dev) | ||
91 | { | ||
92 | const char *partname; | ||
93 | static const char *part_probe_types[] | ||
94 | = { "cmdlinepart", "RedBoot", NULL }; | ||
95 | struct device_node *dp = dev->node, *pp; | ||
96 | int nr_parts, i; | ||
97 | |||
98 | /* First look for RedBoot table or partitions on the command | ||
99 | * line, these take precedence over device tree information */ | ||
100 | nr_parts = parse_mtd_partitions(info->mtd, part_probe_types, | ||
101 | &info->parts, 0); | ||
102 | if (nr_parts > 0) { | ||
103 | add_mtd_partitions(info->mtd, info->parts, nr_parts); | ||
104 | return 0; | ||
105 | } | ||
106 | |||
107 | /* First count the subnodes */ | ||
108 | nr_parts = 0; | ||
109 | for (pp = dp->child; pp; pp = pp->sibling) | ||
110 | nr_parts++; | ||
111 | |||
112 | if (nr_parts) { | ||
113 | info->parts = kzalloc(nr_parts * sizeof(struct mtd_partition), | ||
114 | GFP_KERNEL); | ||
115 | if (!info->parts) { | ||
116 | printk(KERN_ERR "Can't allocate the flash partition data!\n"); | ||
117 | return -ENOMEM; | ||
118 | } | ||
119 | |||
120 | for (pp = dp->child, i = 0 ; pp; pp = pp->sibling, i++) { | ||
121 | const u32 *reg; | ||
122 | int len; | ||
123 | |||
124 | reg = of_get_property(pp, "reg", &len); | ||
125 | if (!reg || (len != 2*sizeof(u32))) { | ||
126 | dev_err(&dev->dev, "Invalid 'reg' on %s\n", | ||
127 | dp->full_name); | ||
128 | kfree(info->parts); | ||
129 | info->parts = NULL; | ||
130 | return -EINVAL; | ||
131 | } | ||
132 | info->parts[i].offset = reg[0]; | ||
133 | info->parts[i].size = reg[1]; | ||
134 | |||
135 | partname = of_get_property(pp, "label", &len); | ||
136 | if (!partname) | ||
137 | partname = of_get_property(pp, "name", &len); | ||
138 | info->parts[i].name = (char *)partname; | ||
139 | |||
140 | if (of_get_property(pp, "read-only", &len)) | ||
141 | info->parts[i].mask_flags = MTD_WRITEABLE; | ||
142 | } | ||
143 | } else { | ||
144 | nr_parts = parse_obsolete_partitions(dev, info, dp); | ||
145 | } | ||
146 | |||
147 | if (nr_parts < 0) | ||
148 | return nr_parts; | ||
149 | |||
150 | if (nr_parts > 0) | ||
151 | add_mtd_partitions(info->mtd, info->parts, nr_parts); | ||
152 | else | ||
153 | add_mtd_device(info->mtd); | ||
154 | |||
155 | return 0; | ||
156 | } | ||
157 | #else /* MTD_PARTITIONS */ | ||
158 | static int __devinit process_partitions(struct physmap_flash_info *info, | ||
159 | struct device_node *dev) | ||
160 | { | ||
161 | add_mtd_device(info->mtd); | ||
162 | return 0; | ||
163 | } | ||
164 | #endif /* MTD_PARTITIONS */ | ||
83 | 165 | ||
84 | static int of_physmap_remove(struct of_device *dev) | 166 | static int of_physmap_remove(struct of_device *dev) |
85 | { | 167 | { |
@@ -92,7 +174,7 @@ static int of_physmap_remove(struct of_device *dev) | |||
92 | 174 | ||
93 | if (info->mtd != NULL) { | 175 | if (info->mtd != NULL) { |
94 | #ifdef CONFIG_MTD_PARTITIONS | 176 | #ifdef CONFIG_MTD_PARTITIONS |
95 | if (info->nr_parts) { | 177 | if (info->parts) { |
96 | del_mtd_partitions(info->mtd); | 178 | del_mtd_partitions(info->mtd); |
97 | kfree(info->parts); | 179 | kfree(info->parts); |
98 | } else { | 180 | } else { |
@@ -115,17 +197,51 @@ static int of_physmap_remove(struct of_device *dev) | |||
115 | return 0; | 197 | return 0; |
116 | } | 198 | } |
117 | 199 | ||
200 | /* Helper function to handle probing of the obsolete "direct-mapped" | ||
201 | * compatible binding, which has an extra "probe-type" property | ||
202 | * describing the type of flash probe necessary. */ | ||
203 | static struct mtd_info * __devinit obsolete_probe(struct of_device *dev, | ||
204 | struct map_info *map) | ||
205 | { | ||
206 | struct device_node *dp = dev->node; | ||
207 | const char *of_probe; | ||
208 | struct mtd_info *mtd; | ||
209 | static const char *rom_probe_types[] | ||
210 | = { "cfi_probe", "jedec_probe", "map_rom"}; | ||
211 | int i; | ||
212 | |||
213 | dev_warn(&dev->dev, "Device tree uses obsolete \"direct-mapped\" " | ||
214 | "flash binding\n"); | ||
215 | |||
216 | of_probe = of_get_property(dp, "probe-type", NULL); | ||
217 | if (!of_probe) { | ||
218 | for (i = 0; i < ARRAY_SIZE(rom_probe_types); i++) { | ||
219 | mtd = do_map_probe(rom_probe_types[i], map); | ||
220 | if (mtd) | ||
221 | return mtd; | ||
222 | } | ||
223 | return NULL; | ||
224 | } else if (strcmp(of_probe, "CFI") == 0) { | ||
225 | return do_map_probe("cfi_probe", map); | ||
226 | } else if (strcmp(of_probe, "JEDEC") == 0) { | ||
227 | return do_map_probe("jedec_probe", map); | ||
228 | } else { | ||
229 | if (strcmp(of_probe, "ROM") != 0) | ||
230 | dev_dbg(&dev->dev, "obsolete_probe: don't know probe type " | ||
231 | "'%s', mapping as rom\n", of_probe); | ||
232 | return do_map_probe("mtd_rom", map); | ||
233 | } | ||
234 | } | ||
235 | |||
118 | static int __devinit of_physmap_probe(struct of_device *dev, const struct of_device_id *match) | 236 | static int __devinit of_physmap_probe(struct of_device *dev, const struct of_device_id *match) |
119 | { | 237 | { |
120 | struct device_node *dp = dev->node; | 238 | struct device_node *dp = dev->node; |
121 | struct resource res; | 239 | struct resource res; |
122 | struct physmap_flash_info *info; | 240 | struct physmap_flash_info *info; |
123 | const char **probe_type; | 241 | const char *probe_type = (const char *)match->data; |
124 | const char *of_probe; | ||
125 | const u32 *width; | 242 | const u32 *width; |
126 | int err; | 243 | int err; |
127 | 244 | ||
128 | |||
129 | if (of_address_to_resource(dp, 0, &res)) { | 245 | if (of_address_to_resource(dp, 0, &res)) { |
130 | dev_err(&dev->dev, "Can't get the flash mapping!\n"); | 246 | dev_err(&dev->dev, "Can't get the flash mapping!\n"); |
131 | err = -EINVAL; | 247 | err = -EINVAL; |
@@ -174,21 +290,11 @@ static int __devinit of_physmap_probe(struct of_device *dev, const struct of_dev | |||
174 | 290 | ||
175 | simple_map_init(&info->map); | 291 | simple_map_init(&info->map); |
176 | 292 | ||
177 | of_probe = of_get_property(dp, "probe-type", NULL); | 293 | if (probe_type) |
178 | if (of_probe == NULL) { | 294 | info->mtd = do_map_probe(probe_type, &info->map); |
179 | probe_type = rom_probe_types; | 295 | else |
180 | for (; info->mtd == NULL && *probe_type != NULL; probe_type++) | 296 | info->mtd = obsolete_probe(dev, &info->map); |
181 | info->mtd = do_map_probe(*probe_type, &info->map); | 297 | |
182 | } else if (!strcmp(of_probe, "CFI")) | ||
183 | info->mtd = do_map_probe("cfi_probe", &info->map); | ||
184 | else if (!strcmp(of_probe, "JEDEC")) | ||
185 | info->mtd = do_map_probe("jedec_probe", &info->map); | ||
186 | else { | ||
187 | if (strcmp(of_probe, "ROM")) | ||
188 | dev_dbg(&dev->dev, "map_probe: don't know probe type " | ||
189 | "'%s', mapping as rom\n", of_probe); | ||
190 | info->mtd = do_map_probe("mtd_rom", &info->map); | ||
191 | } | ||
192 | if (info->mtd == NULL) { | 298 | if (info->mtd == NULL) { |
193 | dev_err(&dev->dev, "map_probe failed\n"); | 299 | dev_err(&dev->dev, "map_probe failed\n"); |
194 | err = -ENXIO; | 300 | err = -ENXIO; |
@@ -196,19 +302,7 @@ static int __devinit of_physmap_probe(struct of_device *dev, const struct of_dev | |||
196 | } | 302 | } |
197 | info->mtd->owner = THIS_MODULE; | 303 | info->mtd->owner = THIS_MODULE; |
198 | 304 | ||
199 | #ifdef CONFIG_MTD_PARTITIONS | 305 | return process_partitions(info, dev); |
200 | err = parse_mtd_partitions(info->mtd, part_probe_types, &info->parts, 0); | ||
201 | if (err > 0) { | ||
202 | add_mtd_partitions(info->mtd, info->parts, err); | ||
203 | } else if ((err = parse_flash_partitions(dp, &info->parts)) > 0) { | ||
204 | dev_info(&dev->dev, "Using OF partition information\n"); | ||
205 | add_mtd_partitions(info->mtd, info->parts, err); | ||
206 | info->nr_parts = err; | ||
207 | } else | ||
208 | #endif | ||
209 | |||
210 | add_mtd_device(info->mtd); | ||
211 | return 0; | ||
212 | 306 | ||
213 | err_out: | 307 | err_out: |
214 | of_physmap_remove(dev); | 308 | of_physmap_remove(dev); |
@@ -221,6 +315,21 @@ err_out: | |||
221 | 315 | ||
222 | static struct of_device_id of_physmap_match[] = { | 316 | static struct of_device_id of_physmap_match[] = { |
223 | { | 317 | { |
318 | .compatible = "cfi-flash", | ||
319 | .data = (void *)"cfi_probe", | ||
320 | }, | ||
321 | { | ||
322 | /* FIXME: JEDEC chips can't be safely and reliably | ||
323 | * probed, although the mtd code gets it right in | ||
324 | * practice most of the time. We should use the | ||
325 | * vendor and device ids specified by the binding to | ||
326 | * bypass the heuristic probe code, but the mtd layer | ||
327 | * provides, at present, no interface for doing so | ||
328 | * :(. */ | ||
329 | .compatible = "jedec-flash", | ||
330 | .data = (void *)"jedec_probe", | ||
331 | }, | ||
332 | { | ||
224 | .type = "rom", | 333 | .type = "rom", |
225 | .compatible = "direct-mapped" | 334 | .compatible = "direct-mapped" |
226 | }, | 335 | }, |