0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012 #include <linux/module.h>
0013 #include <linux/init.h>
0014 #include <linux/of.h>
0015 #include <linux/mtd/mtd.h>
0016 #include <linux/slab.h>
0017 #include <linux/mtd/partitions.h>
0018
0019 #include "ofpart_bcm4908.h"
0020 #include "ofpart_linksys_ns.h"
0021
0022 struct fixed_partitions_quirks {
0023 int (*post_parse)(struct mtd_info *mtd, struct mtd_partition *parts, int nr_parts);
0024 };
0025
0026 static struct fixed_partitions_quirks bcm4908_partitions_quirks = {
0027 .post_parse = bcm4908_partitions_post_parse,
0028 };
0029
0030 static struct fixed_partitions_quirks linksys_ns_partitions_quirks = {
0031 .post_parse = linksys_ns_partitions_post_parse,
0032 };
0033
0034 static const struct of_device_id parse_ofpart_match_table[];
0035
0036 static bool node_has_compatible(struct device_node *pp)
0037 {
0038 return of_get_property(pp, "compatible", NULL);
0039 }
0040
0041 static int parse_fixed_partitions(struct mtd_info *master,
0042 const struct mtd_partition **pparts,
0043 struct mtd_part_parser_data *data)
0044 {
0045 const struct fixed_partitions_quirks *quirks;
0046 const struct of_device_id *of_id;
0047 struct mtd_partition *parts;
0048 struct device_node *mtd_node;
0049 struct device_node *ofpart_node;
0050 const char *partname;
0051 struct device_node *pp;
0052 int nr_parts, i, ret = 0;
0053 bool dedicated = true;
0054
0055
0056 mtd_node = mtd_get_of_node(master);
0057 if (!mtd_node)
0058 return 0;
0059
0060 if (!master->parent) {
0061 ofpart_node = of_get_child_by_name(mtd_node, "partitions");
0062 if (!ofpart_node) {
0063
0064
0065
0066
0067
0068 pr_debug("%s: 'partitions' subnode not found on %pOF. Trying to parse direct subnodes as partitions.\n",
0069 master->name, mtd_node);
0070 ofpart_node = mtd_node;
0071 dedicated = false;
0072 }
0073 } else {
0074 ofpart_node = mtd_node;
0075 }
0076
0077 of_id = of_match_node(parse_ofpart_match_table, ofpart_node);
0078 if (dedicated && !of_id) {
0079
0080 return 0;
0081 }
0082
0083 quirks = of_id ? of_id->data : NULL;
0084
0085
0086 nr_parts = 0;
0087 for_each_child_of_node(ofpart_node, pp) {
0088 if (!dedicated && node_has_compatible(pp))
0089 continue;
0090
0091 nr_parts++;
0092 }
0093
0094 if (nr_parts == 0)
0095 return 0;
0096
0097 parts = kcalloc(nr_parts, sizeof(*parts), GFP_KERNEL);
0098 if (!parts)
0099 return -ENOMEM;
0100
0101 i = 0;
0102 for_each_child_of_node(ofpart_node, pp) {
0103 const __be32 *reg;
0104 int len;
0105 int a_cells, s_cells;
0106
0107 if (!dedicated && node_has_compatible(pp))
0108 continue;
0109
0110 reg = of_get_property(pp, "reg", &len);
0111 if (!reg) {
0112 if (dedicated) {
0113 pr_debug("%s: ofpart partition %pOF (%pOF) missing reg property.\n",
0114 master->name, pp,
0115 mtd_node);
0116 goto ofpart_fail;
0117 } else {
0118 nr_parts--;
0119 continue;
0120 }
0121 }
0122
0123 a_cells = of_n_addr_cells(pp);
0124 s_cells = of_n_size_cells(pp);
0125 if (len / 4 != a_cells + s_cells) {
0126 pr_debug("%s: ofpart partition %pOF (%pOF) error parsing reg property.\n",
0127 master->name, pp,
0128 mtd_node);
0129 goto ofpart_fail;
0130 }
0131
0132 parts[i].offset = of_read_number(reg, a_cells);
0133 parts[i].size = of_read_number(reg + a_cells, s_cells);
0134 parts[i].of_node = pp;
0135
0136 partname = of_get_property(pp, "label", &len);
0137 if (!partname)
0138 partname = of_get_property(pp, "name", &len);
0139 parts[i].name = partname;
0140
0141 if (of_get_property(pp, "read-only", &len))
0142 parts[i].mask_flags |= MTD_WRITEABLE;
0143
0144 if (of_get_property(pp, "lock", &len))
0145 parts[i].mask_flags |= MTD_POWERUP_LOCK;
0146
0147 if (of_property_read_bool(pp, "slc-mode"))
0148 parts[i].add_flags |= MTD_SLC_ON_MLC_EMULATION;
0149
0150 i++;
0151 }
0152
0153 if (!nr_parts)
0154 goto ofpart_none;
0155
0156 if (quirks && quirks->post_parse)
0157 quirks->post_parse(master, parts, nr_parts);
0158
0159 *pparts = parts;
0160 return nr_parts;
0161
0162 ofpart_fail:
0163 pr_err("%s: error parsing ofpart partition %pOF (%pOF)\n",
0164 master->name, pp, mtd_node);
0165 ret = -EINVAL;
0166 ofpart_none:
0167 of_node_put(pp);
0168 kfree(parts);
0169 return ret;
0170 }
0171
0172 static const struct of_device_id parse_ofpart_match_table[] = {
0173
0174 { .compatible = "fixed-partitions" },
0175
0176 { .compatible = "brcm,bcm4908-partitions", .data = &bcm4908_partitions_quirks, },
0177 { .compatible = "linksys,ns-partitions", .data = &linksys_ns_partitions_quirks, },
0178 {},
0179 };
0180 MODULE_DEVICE_TABLE(of, parse_ofpart_match_table);
0181
0182 static struct mtd_part_parser ofpart_parser = {
0183 .parse_fn = parse_fixed_partitions,
0184 .name = "fixed-partitions",
0185 .of_match_table = parse_ofpart_match_table,
0186 };
0187
0188 static int parse_ofoldpart_partitions(struct mtd_info *master,
0189 const struct mtd_partition **pparts,
0190 struct mtd_part_parser_data *data)
0191 {
0192 struct mtd_partition *parts;
0193 struct device_node *dp;
0194 int i, plen, nr_parts;
0195 const struct {
0196 __be32 offset, len;
0197 } *part;
0198 const char *names;
0199
0200
0201 dp = mtd_get_of_node(master);
0202 if (!dp)
0203 return 0;
0204
0205 part = of_get_property(dp, "partitions", &plen);
0206 if (!part)
0207 return 0;
0208
0209 pr_warn("Device tree uses obsolete partition map binding: %pOF\n", dp);
0210
0211 nr_parts = plen / sizeof(part[0]);
0212
0213 parts = kcalloc(nr_parts, sizeof(*parts), GFP_KERNEL);
0214 if (!parts)
0215 return -ENOMEM;
0216
0217 names = of_get_property(dp, "partition-names", &plen);
0218
0219 for (i = 0; i < nr_parts; i++) {
0220 parts[i].offset = be32_to_cpu(part->offset);
0221 parts[i].size = be32_to_cpu(part->len) & ~1;
0222
0223 if (be32_to_cpu(part->len) & 1)
0224 parts[i].mask_flags = MTD_WRITEABLE;
0225
0226 if (names && (plen > 0)) {
0227 int len = strlen(names) + 1;
0228
0229 parts[i].name = names;
0230 plen -= len;
0231 names += len;
0232 } else {
0233 parts[i].name = "unnamed";
0234 }
0235
0236 part++;
0237 }
0238
0239 *pparts = parts;
0240 return nr_parts;
0241 }
0242
0243 static struct mtd_part_parser ofoldpart_parser = {
0244 .parse_fn = parse_ofoldpart_partitions,
0245 .name = "ofoldpart",
0246 };
0247
0248 static int __init ofpart_parser_init(void)
0249 {
0250 register_mtd_parser(&ofpart_parser);
0251 register_mtd_parser(&ofoldpart_parser);
0252 return 0;
0253 }
0254
0255 static void __exit ofpart_parser_exit(void)
0256 {
0257 deregister_mtd_parser(&ofpart_parser);
0258 deregister_mtd_parser(&ofoldpart_parser);
0259 }
0260
0261 module_init(ofpart_parser_init);
0262 module_exit(ofpart_parser_exit);
0263
0264 MODULE_LICENSE("GPL");
0265 MODULE_DESCRIPTION("Parser for MTD partitioning information in device tree");
0266 MODULE_AUTHOR("Vitaly Wool, David Gibson");
0267
0268
0269
0270
0271
0272 MODULE_ALIAS("fixed-partitions");
0273 MODULE_ALIAS("ofoldpart");