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 *node; 33 const char *partname; 34 struct device_node *pp; 35 int nr_parts, i; 36 37 38 if (!data) 39 return 0; 40 41 node = data->of_node; 42 if (!node) 43 return 0; 44 45 /* First count the subnodes */ 46 nr_parts = 0; 47 for_each_child_of_node(node, pp) { 48 if (node_has_compatible(pp)) 49 continue; 50 51 nr_parts++; 52 } 53 54 if (nr_parts == 0) 55 return 0; 56 57 *pparts = kzalloc(nr_parts * sizeof(**pparts), GFP_KERNEL); 58 if (!*pparts) 59 return -ENOMEM; 60 61 i = 0; 62 for_each_child_of_node(node, pp) { 63 const __be32 *reg; 64 int len; 65 int a_cells, s_cells; 66 67 if (node_has_compatible(pp)) 68 continue; 69 70 reg = of_get_property(pp, "reg", &len); 71 if (!reg) { 72 nr_parts--; 73 continue; 74 } 75 76 a_cells = of_n_addr_cells(pp); 77 s_cells = of_n_size_cells(pp); 78 (*pparts)[i].offset = of_read_number(reg, a_cells); 79 (*pparts)[i].size = of_read_number(reg + a_cells, s_cells); 80 81 partname = of_get_property(pp, "label", &len); 82 if (!partname) 83 partname = of_get_property(pp, "name", &len); 84 (*pparts)[i].name = partname; 85 86 if (of_get_property(pp, "read-only", &len)) 87 (*pparts)[i].mask_flags |= MTD_WRITEABLE; 88 89 if (of_get_property(pp, "lock", &len)) 90 (*pparts)[i].mask_flags |= MTD_POWERUP_LOCK; 91 92 i++; 93 } 94 95 if (!i) { 96 of_node_put(pp); 97 pr_err("No valid partition found on %s\n", node->full_name); 98 kfree(*pparts); 99 *pparts = NULL; 100 return -EINVAL; 101 } 102 103 return nr_parts; 104} 105 106static struct mtd_part_parser ofpart_parser = { 107 .owner = THIS_MODULE, 108 .parse_fn = parse_ofpart_partitions, 109 .name = "ofpart", 110}; 111 112static int parse_ofoldpart_partitions(struct mtd_info *master, 113 struct mtd_partition **pparts, 114 struct mtd_part_parser_data *data) 115{ 116 struct device_node *dp; 117 int i, plen, nr_parts; 118 const struct { 119 __be32 offset, len; 120 } *part; 121 const char *names; 122 123 if (!data) 124 return 0; 125 126 dp = data->of_node; 127 if (!dp) 128 return 0; 129 130 part = of_get_property(dp, "partitions", &plen); 131 if (!part) 132 return 0; /* No partitions found */ 133 134 pr_warning("Device tree uses obsolete partition map binding: %s\n", 135 dp->full_name); 136 137 nr_parts = plen / sizeof(part[0]); 138 139 *pparts = kzalloc(nr_parts * sizeof(*(*pparts)), GFP_KERNEL); 140 if (!*pparts) 141 return -ENOMEM; 142 143 names = of_get_property(dp, "partition-names", &plen); 144 145 for (i = 0; i < nr_parts; i++) { 146 (*pparts)[i].offset = be32_to_cpu(part->offset); 147 (*pparts)[i].size = be32_to_cpu(part->len) & ~1; 148 /* bit 0 set signifies read only partition */ 149 if (be32_to_cpu(part->len) & 1) 150 (*pparts)[i].mask_flags = MTD_WRITEABLE; 151 152 if (names && (plen > 0)) { 153 int len = strlen(names) + 1; 154 155 (*pparts)[i].name = names; 156 plen -= len; 157 names += len; 158 } else { 159 (*pparts)[i].name = "unnamed"; 160 } 161 162 part++; 163 } 164 165 return nr_parts; 166} 167 168static struct mtd_part_parser ofoldpart_parser = { 169 .owner = THIS_MODULE, 170 .parse_fn = parse_ofoldpart_partitions, 171 .name = "ofoldpart", 172}; 173 174static int __init ofpart_parser_init(void) 175{ 176 register_mtd_parser(&ofpart_parser); 177 register_mtd_parser(&ofoldpart_parser); 178 return 0; 179} 180 181static void __exit ofpart_parser_exit(void) 182{ 183 deregister_mtd_parser(&ofpart_parser); 184 deregister_mtd_parser(&ofoldpart_parser); 185} 186 187module_init(ofpart_parser_init); 188module_exit(ofpart_parser_exit); 189 190MODULE_LICENSE("GPL"); 191MODULE_DESCRIPTION("Parser for MTD partitioning information in device tree"); 192MODULE_AUTHOR("Vitaly Wool, David Gibson"); 193/* 194 * When MTD core cannot find the requested parser, it tries to load the module 195 * with the same name. Since we provide the ofoldpart parser, we should have 196 * the corresponding alias. 197 */ 198MODULE_ALIAS("ofoldpart"); 199