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