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