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 #ifdef CONFIG_MTD_OF_PARTS_BCM4908 27 static struct fixed_partitions_quirks bcm4908_partitions_quirks = { 28 .post_parse = bcm4908_partitions_post_parse, 29 }; 30 #endif 31 32 #ifdef CONFIG_MTD_OF_PARTS_LINKSYS_NS 33 static struct fixed_partitions_quirks linksys_ns_partitions_quirks = { 34 .post_parse = linksys_ns_partitions_post_parse, 35 }; 36 #endif 37 38 static const struct of_device_id parse_ofpart_match_table[]; 39 40 static bool node_has_compatible(struct device_node *pp) 41 { 42 return of_get_property(pp, "compatible", NULL); 43 } 44 45 static int parse_fixed_partitions(struct mtd_info *master, 46 const struct mtd_partition **pparts, 47 struct mtd_part_parser_data *data) 48 { 49 const struct fixed_partitions_quirks *quirks; 50 const struct of_device_id *of_id; 51 struct mtd_partition *parts; 52 struct device_node *mtd_node; 53 struct device_node *ofpart_node; 54 const char *partname; 55 struct device_node *pp; 56 int nr_parts, i, ret = 0; 57 bool dedicated = true; 58 59 /* Pull of_node from the master device node */ 60 mtd_node = mtd_get_of_node(master); 61 if (!mtd_node) 62 return 0; 63 64 if (!master->parent) { /* Master */ 65 ofpart_node = of_get_child_by_name(mtd_node, "partitions"); 66 if (!ofpart_node) { 67 /* 68 * We might get here even when ofpart isn't used at all (e.g., 69 * when using another parser), so don't be louder than 70 * KERN_DEBUG 71 */ 72 pr_debug("%s: 'partitions' subnode not found on %pOF. Trying to parse direct subnodes as partitions.\n", 73 master->name, mtd_node); 74 ofpart_node = mtd_node; 75 dedicated = false; 76 } 77 } else { /* Partition */ 78 ofpart_node = mtd_node; 79 } 80 81 of_id = of_match_node(parse_ofpart_match_table, ofpart_node); 82 if (dedicated && !of_id) { 83 /* The 'partitions' subnode might be used by another parser */ 84 of_node_put(ofpart_node); 85 return 0; 86 } 87 88 quirks = of_id ? of_id->data : NULL; 89 90 /* First count the subnodes */ 91 nr_parts = 0; 92 for_each_child_of_node(ofpart_node, pp) { 93 if (!dedicated && node_has_compatible(pp)) 94 continue; 95 96 nr_parts++; 97 } 98 99 if (nr_parts == 0) { 100 if (dedicated) 101 of_node_put(ofpart_node); 102 return 0; 103 } 104 105 parts = kzalloc_objs(*parts, nr_parts); 106 if (!parts) { 107 if (dedicated) 108 of_node_put(ofpart_node); 109 return -ENOMEM; 110 } 111 112 i = 0; 113 for_each_child_of_node(ofpart_node, pp) { 114 const __be32 *reg; 115 int len; 116 int a_cells, s_cells; 117 118 if (!dedicated && node_has_compatible(pp)) 119 continue; 120 121 reg = of_get_property(pp, "reg", &len); 122 if (!reg) { 123 if (dedicated) { 124 pr_debug("%s: ofpart partition %pOF (%pOF) missing reg property.\n", 125 master->name, pp, 126 mtd_node); 127 goto ofpart_fail; 128 } else { 129 nr_parts--; 130 continue; 131 } 132 } 133 134 a_cells = of_n_addr_cells(pp); 135 s_cells = of_n_size_cells(pp); 136 if (!dedicated && s_cells == 0) { 137 /* 138 * This is a ugly workaround to not create 139 * regression on devices that are still creating 140 * partitions as direct children of the nand controller. 141 * This can happen in case the nand controller node has 142 * #size-cells equal to 0 and the firmware (e.g. 143 * U-Boot) just add the partitions there assuming 144 * 32-bit addressing. 145 * 146 * If you get this warning your firmware and/or DTS 147 * should be really fixed. 148 * 149 * This is working only for devices smaller than 4GiB. 150 */ 151 pr_warn("%s: ofpart partition %pOF (%pOF) #size-cells is wrongly set to <0>, assuming <1> for parsing partitions.\n", 152 master->name, pp, mtd_node); 153 s_cells = 1; 154 } 155 if (len / 4 != a_cells + s_cells) { 156 pr_debug("%s: ofpart partition %pOF (%pOF) error parsing reg property.\n", 157 master->name, pp, 158 mtd_node); 159 goto ofpart_fail; 160 } 161 162 parts[i].offset = of_read_number(reg, a_cells); 163 parts[i].size = of_read_number(reg + a_cells, s_cells); 164 parts[i].of_node = pp; 165 166 partname = of_get_property(pp, "label", &len); 167 if (!partname) 168 partname = of_get_property(pp, "name", &len); 169 parts[i].name = partname; 170 171 if (of_property_read_bool(pp, "read-only")) 172 parts[i].mask_flags |= MTD_WRITEABLE; 173 174 if (of_property_read_bool(pp, "lock")) 175 parts[i].mask_flags |= MTD_POWERUP_LOCK; 176 177 if (of_property_read_bool(pp, "slc-mode")) 178 parts[i].add_flags |= MTD_SLC_ON_MLC_EMULATION; 179 180 i++; 181 } 182 183 if (!nr_parts) 184 goto ofpart_none; 185 186 if (quirks && quirks->post_parse) 187 quirks->post_parse(master, parts, nr_parts); 188 189 if (dedicated) 190 of_node_put(ofpart_node); 191 192 *pparts = parts; 193 return nr_parts; 194 195 ofpart_fail: 196 pr_err("%s: error parsing ofpart partition %pOF (%pOF)\n", 197 master->name, pp, mtd_node); 198 ret = -EINVAL; 199 ofpart_none: 200 if (dedicated) 201 of_node_put(ofpart_node); 202 of_node_put(pp); 203 kfree(parts); 204 return ret; 205 } 206 207 static const struct of_device_id parse_ofpart_match_table[] = { 208 /* Generic */ 209 { .compatible = "fixed-partitions" }, 210 /* Customized */ 211 #ifdef CONFIG_MTD_OF_PARTS_BCM4908 212 { .compatible = "brcm,bcm4908-partitions", .data = &bcm4908_partitions_quirks, }, 213 #endif 214 #ifdef CONFIG_MTD_OF_PARTS_LINKSYS_NS 215 { .compatible = "linksys,ns-partitions", .data = &linksys_ns_partitions_quirks, }, 216 #endif 217 {}, 218 }; 219 MODULE_DEVICE_TABLE(of, parse_ofpart_match_table); 220 221 static struct mtd_part_parser ofpart_parser = { 222 .parse_fn = parse_fixed_partitions, 223 .name = "fixed-partitions", 224 .of_match_table = parse_ofpart_match_table, 225 }; 226 227 static int parse_ofoldpart_partitions(struct mtd_info *master, 228 const struct mtd_partition **pparts, 229 struct mtd_part_parser_data *data) 230 { 231 struct mtd_partition *parts; 232 struct device_node *dp; 233 int i, plen, nr_parts; 234 const struct { 235 __be32 offset, len; 236 } *part; 237 const char *names; 238 239 /* Pull of_node from the master device node */ 240 dp = mtd_get_of_node(master); 241 if (!dp) 242 return 0; 243 244 part = of_get_property(dp, "partitions", &plen); 245 if (!part) 246 return 0; /* No partitions found */ 247 248 pr_warn("Device tree uses obsolete partition map binding: %pOF\n", dp); 249 250 nr_parts = plen / sizeof(part[0]); 251 252 parts = kzalloc_objs(*parts, nr_parts); 253 if (!parts) 254 return -ENOMEM; 255 256 names = of_get_property(dp, "partition-names", &plen); 257 258 for (i = 0; i < nr_parts; i++) { 259 parts[i].offset = be32_to_cpu(part->offset); 260 parts[i].size = be32_to_cpu(part->len) & ~1; 261 /* bit 0 set signifies read only partition */ 262 if (be32_to_cpu(part->len) & 1) 263 parts[i].mask_flags = MTD_WRITEABLE; 264 265 if (names && (plen > 0)) { 266 int len = strlen(names) + 1; 267 268 parts[i].name = names; 269 plen -= len; 270 names += len; 271 } else { 272 parts[i].name = "unnamed"; 273 } 274 275 part++; 276 } 277 278 *pparts = parts; 279 return nr_parts; 280 } 281 282 static struct mtd_part_parser ofoldpart_parser = { 283 .parse_fn = parse_ofoldpart_partitions, 284 .name = "ofoldpart", 285 }; 286 287 static int __init ofpart_parser_init(void) 288 { 289 register_mtd_parser(&ofpart_parser); 290 register_mtd_parser(&ofoldpart_parser); 291 return 0; 292 } 293 294 static void __exit ofpart_parser_exit(void) 295 { 296 deregister_mtd_parser(&ofpart_parser); 297 deregister_mtd_parser(&ofoldpart_parser); 298 } 299 300 module_init(ofpart_parser_init); 301 module_exit(ofpart_parser_exit); 302 303 MODULE_LICENSE("GPL"); 304 MODULE_DESCRIPTION("Parser for MTD partitioning information in device tree"); 305 MODULE_AUTHOR("Vitaly Wool, David Gibson"); 306 /* 307 * When MTD core cannot find the requested parser, it tries to load the module 308 * with the same name. Since we provide the ofoldpart parser, we should have 309 * the corresponding alias. 310 */ 311 MODULE_ALIAS("fixed-partitions"); 312 MODULE_ALIAS("ofoldpart"); 313