1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Flash partitions described by the OF (or flattened) device tree 4 * 5 * Copyright © 2006 MontaVista Software Inc. 6 * Author: Vitaly Wool <vwool@ru.mvista.com> 7 * 8 * Revised to handle newer style flash binding by: 9 * Copyright © 2007 David Gibson, IBM Corporation. 10 */ 11 12 #include <linux/module.h> 13 #include <linux/init.h> 14 #include <linux/of.h> 15 #include <linux/mtd/mtd.h> 16 #include <linux/slab.h> 17 #include <linux/mtd/partitions.h> 18 19 #include "ofpart_bcm4908.h" 20 #include "ofpart_linksys_ns.h" 21 22 struct fixed_partitions_quirks { 23 int (*post_parse)(struct mtd_info *mtd, struct mtd_partition *parts, int nr_parts); 24 }; 25 26 static struct fixed_partitions_quirks bcm4908_partitions_quirks = { 27 .post_parse = bcm4908_partitions_post_parse, 28 }; 29 30 static struct fixed_partitions_quirks linksys_ns_partitions_quirks = { 31 .post_parse = linksys_ns_partitions_post_parse, 32 }; 33 34 static const struct of_device_id parse_ofpart_match_table[]; 35 36 static bool node_has_compatible(struct device_node *pp) 37 { 38 return of_get_property(pp, "compatible", NULL); 39 } 40 41 static int parse_fixed_partitions(struct mtd_info *master, 42 const struct mtd_partition **pparts, 43 struct mtd_part_parser_data *data) 44 { 45 const struct fixed_partitions_quirks *quirks; 46 const struct of_device_id *of_id; 47 struct mtd_partition *parts; 48 struct device_node *mtd_node; 49 struct device_node *ofpart_node; 50 const char *partname; 51 struct device_node *pp; 52 int nr_parts, i, ret = 0; 53 bool dedicated = true; 54 55 /* Pull of_node from the master device node */ 56 mtd_node = mtd_get_of_node(master); 57 if (!mtd_node) 58 return 0; 59 60 if (!master->parent) { /* Master */ 61 ofpart_node = of_get_child_by_name(mtd_node, "partitions"); 62 if (!ofpart_node) { 63 /* 64 * We might get here even when ofpart isn't used at all (e.g., 65 * when using another parser), so don't be louder than 66 * KERN_DEBUG 67 */ 68 pr_debug("%s: 'partitions' subnode not found on %pOF. Trying to parse direct subnodes as partitions.\n", 69 master->name, mtd_node); 70 ofpart_node = mtd_node; 71 dedicated = false; 72 } 73 } else { /* Partition */ 74 ofpart_node = mtd_node; 75 } 76 77 of_id = of_match_node(parse_ofpart_match_table, ofpart_node); 78 if (dedicated && !of_id) { 79 /* The 'partitions' subnode might be used by another parser */ 80 return 0; 81 } 82 83 quirks = of_id ? of_id->data : NULL; 84 85 /* First count the subnodes */ 86 nr_parts = 0; 87 for_each_child_of_node(ofpart_node, pp) { 88 if (!dedicated && node_has_compatible(pp)) 89 continue; 90 91 nr_parts++; 92 } 93 94 if (nr_parts == 0) 95 return 0; 96 97 parts = kcalloc(nr_parts, sizeof(*parts), GFP_KERNEL); 98 if (!parts) 99 return -ENOMEM; 100 101 i = 0; 102 for_each_child_of_node(ofpart_node, pp) { 103 const __be32 *reg; 104 int len; 105 int a_cells, s_cells; 106 107 if (!dedicated && node_has_compatible(pp)) 108 continue; 109 110 reg = of_get_property(pp, "reg", &len); 111 if (!reg) { 112 if (dedicated) { 113 pr_debug("%s: ofpart partition %pOF (%pOF) missing reg property.\n", 114 master->name, pp, 115 mtd_node); 116 goto ofpart_fail; 117 } else { 118 nr_parts--; 119 continue; 120 } 121 } 122 123 a_cells = of_n_addr_cells(pp); 124 s_cells = of_n_size_cells(pp); 125 if (len / 4 != a_cells + s_cells) { 126 pr_debug("%s: ofpart partition %pOF (%pOF) error parsing reg property.\n", 127 master->name, pp, 128 mtd_node); 129 goto ofpart_fail; 130 } 131 132 parts[i].offset = of_read_number(reg, a_cells); 133 parts[i].size = of_read_number(reg + a_cells, s_cells); 134 parts[i].of_node = pp; 135 136 partname = of_get_property(pp, "label", &len); 137 if (!partname) 138 partname = of_get_property(pp, "name", &len); 139 parts[i].name = partname; 140 141 if (of_get_property(pp, "read-only", &len)) 142 parts[i].mask_flags |= MTD_WRITEABLE; 143 144 if (of_get_property(pp, "lock", &len)) 145 parts[i].mask_flags |= MTD_POWERUP_LOCK; 146 147 if (of_property_read_bool(pp, "slc-mode")) 148 parts[i].add_flags |= MTD_SLC_ON_MLC_EMULATION; 149 150 i++; 151 } 152 153 if (!nr_parts) 154 goto ofpart_none; 155 156 if (quirks && quirks->post_parse) 157 quirks->post_parse(master, parts, nr_parts); 158 159 *pparts = parts; 160 return nr_parts; 161 162 ofpart_fail: 163 pr_err("%s: error parsing ofpart partition %pOF (%pOF)\n", 164 master->name, pp, mtd_node); 165 ret = -EINVAL; 166 ofpart_none: 167 of_node_put(pp); 168 kfree(parts); 169 return ret; 170 } 171 172 static const struct of_device_id parse_ofpart_match_table[] = { 173 /* Generic */ 174 { .compatible = "fixed-partitions" }, 175 /* Customized */ 176 { .compatible = "brcm,bcm4908-partitions", .data = &bcm4908_partitions_quirks, }, 177 { .compatible = "linksys,ns-partitions", .data = &linksys_ns_partitions_quirks, }, 178 {}, 179 }; 180 MODULE_DEVICE_TABLE(of, parse_ofpart_match_table); 181 182 static struct mtd_part_parser ofpart_parser = { 183 .parse_fn = parse_fixed_partitions, 184 .name = "fixed-partitions", 185 .of_match_table = parse_ofpart_match_table, 186 }; 187 188 static int parse_ofoldpart_partitions(struct mtd_info *master, 189 const struct mtd_partition **pparts, 190 struct mtd_part_parser_data *data) 191 { 192 struct mtd_partition *parts; 193 struct device_node *dp; 194 int i, plen, nr_parts; 195 const struct { 196 __be32 offset, len; 197 } *part; 198 const char *names; 199 200 /* Pull of_node from the master device node */ 201 dp = mtd_get_of_node(master); 202 if (!dp) 203 return 0; 204 205 part = of_get_property(dp, "partitions", &plen); 206 if (!part) 207 return 0; /* No partitions found */ 208 209 pr_warn("Device tree uses obsolete partition map binding: %pOF\n", dp); 210 211 nr_parts = plen / sizeof(part[0]); 212 213 parts = kcalloc(nr_parts, sizeof(*parts), GFP_KERNEL); 214 if (!parts) 215 return -ENOMEM; 216 217 names = of_get_property(dp, "partition-names", &plen); 218 219 for (i = 0; i < nr_parts; i++) { 220 parts[i].offset = be32_to_cpu(part->offset); 221 parts[i].size = be32_to_cpu(part->len) & ~1; 222 /* bit 0 set signifies read only partition */ 223 if (be32_to_cpu(part->len) & 1) 224 parts[i].mask_flags = MTD_WRITEABLE; 225 226 if (names && (plen > 0)) { 227 int len = strlen(names) + 1; 228 229 parts[i].name = names; 230 plen -= len; 231 names += len; 232 } else { 233 parts[i].name = "unnamed"; 234 } 235 236 part++; 237 } 238 239 *pparts = parts; 240 return nr_parts; 241 } 242 243 static struct mtd_part_parser ofoldpart_parser = { 244 .parse_fn = parse_ofoldpart_partitions, 245 .name = "ofoldpart", 246 }; 247 248 static int __init ofpart_parser_init(void) 249 { 250 register_mtd_parser(&ofpart_parser); 251 register_mtd_parser(&ofoldpart_parser); 252 return 0; 253 } 254 255 static void __exit ofpart_parser_exit(void) 256 { 257 deregister_mtd_parser(&ofpart_parser); 258 deregister_mtd_parser(&ofoldpart_parser); 259 } 260 261 module_init(ofpart_parser_init); 262 module_exit(ofpart_parser_exit); 263 264 MODULE_LICENSE("GPL"); 265 MODULE_DESCRIPTION("Parser for MTD partitioning information in device tree"); 266 MODULE_AUTHOR("Vitaly Wool, David Gibson"); 267 /* 268 * When MTD core cannot find the requested parser, it tries to load the module 269 * with the same name. Since we provide the ofoldpart parser, we should have 270 * the corresponding alias. 271 */ 272 MODULE_ALIAS("fixed-partitions"); 273 MODULE_ALIAS("ofoldpart"); 274