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 (!dedicated && s_cells == 0) { 126 /* 127 * This is a ugly workaround to not create 128 * regression on devices that are still creating 129 * partitions as direct children of the nand controller. 130 * This can happen in case the nand controller node has 131 * #size-cells equal to 0 and the firmware (e.g. 132 * U-Boot) just add the partitions there assuming 133 * 32-bit addressing. 134 * 135 * If you get this warning your firmware and/or DTS 136 * should be really fixed. 137 * 138 * This is working only for devices smaller than 4GiB. 139 */ 140 pr_warn("%s: ofpart partition %pOF (%pOF) #size-cells is wrongly set to <0>, assuming <1> for parsing partitions.\n", 141 master->name, pp, mtd_node); 142 s_cells = 1; 143 } 144 if (len / 4 != a_cells + s_cells) { 145 pr_debug("%s: ofpart partition %pOF (%pOF) error parsing reg property.\n", 146 master->name, pp, 147 mtd_node); 148 goto ofpart_fail; 149 } 150 151 parts[i].offset = of_read_number(reg, a_cells); 152 parts[i].size = of_read_number(reg + a_cells, s_cells); 153 parts[i].of_node = pp; 154 155 partname = of_get_property(pp, "label", &len); 156 if (!partname) 157 partname = of_get_property(pp, "name", &len); 158 parts[i].name = partname; 159 160 if (of_get_property(pp, "read-only", &len)) 161 parts[i].mask_flags |= MTD_WRITEABLE; 162 163 if (of_get_property(pp, "lock", &len)) 164 parts[i].mask_flags |= MTD_POWERUP_LOCK; 165 166 if (of_property_read_bool(pp, "slc-mode")) 167 parts[i].add_flags |= MTD_SLC_ON_MLC_EMULATION; 168 169 i++; 170 } 171 172 if (!nr_parts) 173 goto ofpart_none; 174 175 if (quirks && quirks->post_parse) 176 quirks->post_parse(master, parts, nr_parts); 177 178 *pparts = parts; 179 return nr_parts; 180 181 ofpart_fail: 182 pr_err("%s: error parsing ofpart partition %pOF (%pOF)\n", 183 master->name, pp, mtd_node); 184 ret = -EINVAL; 185 ofpart_none: 186 of_node_put(pp); 187 kfree(parts); 188 return ret; 189 } 190 191 static const struct of_device_id parse_ofpart_match_table[] = { 192 /* Generic */ 193 { .compatible = "fixed-partitions" }, 194 /* Customized */ 195 { .compatible = "brcm,bcm4908-partitions", .data = &bcm4908_partitions_quirks, }, 196 { .compatible = "linksys,ns-partitions", .data = &linksys_ns_partitions_quirks, }, 197 {}, 198 }; 199 MODULE_DEVICE_TABLE(of, parse_ofpart_match_table); 200 201 static struct mtd_part_parser ofpart_parser = { 202 .parse_fn = parse_fixed_partitions, 203 .name = "fixed-partitions", 204 .of_match_table = parse_ofpart_match_table, 205 }; 206 207 static int parse_ofoldpart_partitions(struct mtd_info *master, 208 const struct mtd_partition **pparts, 209 struct mtd_part_parser_data *data) 210 { 211 struct mtd_partition *parts; 212 struct device_node *dp; 213 int i, plen, nr_parts; 214 const struct { 215 __be32 offset, len; 216 } *part; 217 const char *names; 218 219 /* Pull of_node from the master device node */ 220 dp = mtd_get_of_node(master); 221 if (!dp) 222 return 0; 223 224 part = of_get_property(dp, "partitions", &plen); 225 if (!part) 226 return 0; /* No partitions found */ 227 228 pr_warn("Device tree uses obsolete partition map binding: %pOF\n", dp); 229 230 nr_parts = plen / sizeof(part[0]); 231 232 parts = kcalloc(nr_parts, sizeof(*parts), GFP_KERNEL); 233 if (!parts) 234 return -ENOMEM; 235 236 names = of_get_property(dp, "partition-names", &plen); 237 238 for (i = 0; i < nr_parts; i++) { 239 parts[i].offset = be32_to_cpu(part->offset); 240 parts[i].size = be32_to_cpu(part->len) & ~1; 241 /* bit 0 set signifies read only partition */ 242 if (be32_to_cpu(part->len) & 1) 243 parts[i].mask_flags = MTD_WRITEABLE; 244 245 if (names && (plen > 0)) { 246 int len = strlen(names) + 1; 247 248 parts[i].name = names; 249 plen -= len; 250 names += len; 251 } else { 252 parts[i].name = "unnamed"; 253 } 254 255 part++; 256 } 257 258 *pparts = parts; 259 return nr_parts; 260 } 261 262 static struct mtd_part_parser ofoldpart_parser = { 263 .parse_fn = parse_ofoldpart_partitions, 264 .name = "ofoldpart", 265 }; 266 267 static int __init ofpart_parser_init(void) 268 { 269 register_mtd_parser(&ofpart_parser); 270 register_mtd_parser(&ofoldpart_parser); 271 return 0; 272 } 273 274 static void __exit ofpart_parser_exit(void) 275 { 276 deregister_mtd_parser(&ofpart_parser); 277 deregister_mtd_parser(&ofoldpart_parser); 278 } 279 280 module_init(ofpart_parser_init); 281 module_exit(ofpart_parser_exit); 282 283 MODULE_LICENSE("GPL"); 284 MODULE_DESCRIPTION("Parser for MTD partitioning information in device tree"); 285 MODULE_AUTHOR("Vitaly Wool, David Gibson"); 286 /* 287 * When MTD core cannot find the requested parser, it tries to load the module 288 * with the same name. Since we provide the ofoldpart parser, we should have 289 * the corresponding alias. 290 */ 291 MODULE_ALIAS("fixed-partitions"); 292 MODULE_ALIAS("ofoldpart"); 293