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