18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Flash partitions described by the OF (or flattened) device tree 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright © 2006 MontaVista Software Inc. 68c2ecf20Sopenharmony_ci * Author: Vitaly Wool <vwool@ru.mvista.com> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Revised to handle newer style flash binding by: 98c2ecf20Sopenharmony_ci * Copyright © 2007 David Gibson, IBM Corporation. 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/init.h> 148c2ecf20Sopenharmony_ci#include <linux/of.h> 158c2ecf20Sopenharmony_ci#include <linux/mtd/mtd.h> 168c2ecf20Sopenharmony_ci#include <linux/slab.h> 178c2ecf20Sopenharmony_ci#include <linux/mtd/partitions.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_cistatic bool node_has_compatible(struct device_node *pp) 208c2ecf20Sopenharmony_ci{ 218c2ecf20Sopenharmony_ci return of_get_property(pp, "compatible", NULL); 228c2ecf20Sopenharmony_ci} 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistatic int parse_fixed_partitions(struct mtd_info *master, 258c2ecf20Sopenharmony_ci const struct mtd_partition **pparts, 268c2ecf20Sopenharmony_ci struct mtd_part_parser_data *data) 278c2ecf20Sopenharmony_ci{ 288c2ecf20Sopenharmony_ci struct mtd_partition *parts; 298c2ecf20Sopenharmony_ci struct device_node *mtd_node; 308c2ecf20Sopenharmony_ci struct device_node *ofpart_node; 318c2ecf20Sopenharmony_ci const char *partname; 328c2ecf20Sopenharmony_ci struct device_node *pp; 338c2ecf20Sopenharmony_ci int nr_parts, i, ret = 0; 348c2ecf20Sopenharmony_ci bool dedicated = true; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci /* Pull of_node from the master device node */ 388c2ecf20Sopenharmony_ci mtd_node = mtd_get_of_node(master); 398c2ecf20Sopenharmony_ci if (!mtd_node) 408c2ecf20Sopenharmony_ci return 0; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci ofpart_node = of_get_child_by_name(mtd_node, "partitions"); 438c2ecf20Sopenharmony_ci if (!ofpart_node) { 448c2ecf20Sopenharmony_ci /* 458c2ecf20Sopenharmony_ci * We might get here even when ofpart isn't used at all (e.g., 468c2ecf20Sopenharmony_ci * when using another parser), so don't be louder than 478c2ecf20Sopenharmony_ci * KERN_DEBUG 488c2ecf20Sopenharmony_ci */ 498c2ecf20Sopenharmony_ci pr_debug("%s: 'partitions' subnode not found on %pOF. Trying to parse direct subnodes as partitions.\n", 508c2ecf20Sopenharmony_ci master->name, mtd_node); 518c2ecf20Sopenharmony_ci ofpart_node = mtd_node; 528c2ecf20Sopenharmony_ci dedicated = false; 538c2ecf20Sopenharmony_ci } else if (!of_device_is_compatible(ofpart_node, "fixed-partitions")) { 548c2ecf20Sopenharmony_ci /* The 'partitions' subnode might be used by another parser */ 558c2ecf20Sopenharmony_ci return 0; 568c2ecf20Sopenharmony_ci } 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci /* First count the subnodes */ 598c2ecf20Sopenharmony_ci nr_parts = 0; 608c2ecf20Sopenharmony_ci for_each_child_of_node(ofpart_node, pp) { 618c2ecf20Sopenharmony_ci if (!dedicated && node_has_compatible(pp)) 628c2ecf20Sopenharmony_ci continue; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci nr_parts++; 658c2ecf20Sopenharmony_ci } 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci if (nr_parts == 0) 688c2ecf20Sopenharmony_ci return 0; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci parts = kcalloc(nr_parts, sizeof(*parts), GFP_KERNEL); 718c2ecf20Sopenharmony_ci if (!parts) 728c2ecf20Sopenharmony_ci return -ENOMEM; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci i = 0; 758c2ecf20Sopenharmony_ci for_each_child_of_node(ofpart_node, pp) { 768c2ecf20Sopenharmony_ci const __be32 *reg; 778c2ecf20Sopenharmony_ci int len; 788c2ecf20Sopenharmony_ci int a_cells, s_cells; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci if (!dedicated && node_has_compatible(pp)) 818c2ecf20Sopenharmony_ci continue; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci reg = of_get_property(pp, "reg", &len); 848c2ecf20Sopenharmony_ci if (!reg) { 858c2ecf20Sopenharmony_ci if (dedicated) { 868c2ecf20Sopenharmony_ci pr_debug("%s: ofpart partition %pOF (%pOF) missing reg property.\n", 878c2ecf20Sopenharmony_ci master->name, pp, 888c2ecf20Sopenharmony_ci mtd_node); 898c2ecf20Sopenharmony_ci goto ofpart_fail; 908c2ecf20Sopenharmony_ci } else { 918c2ecf20Sopenharmony_ci nr_parts--; 928c2ecf20Sopenharmony_ci continue; 938c2ecf20Sopenharmony_ci } 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci a_cells = of_n_addr_cells(pp); 978c2ecf20Sopenharmony_ci s_cells = of_n_size_cells(pp); 988c2ecf20Sopenharmony_ci if (len / 4 != a_cells + s_cells) { 998c2ecf20Sopenharmony_ci pr_debug("%s: ofpart partition %pOF (%pOF) error parsing reg property.\n", 1008c2ecf20Sopenharmony_ci master->name, pp, 1018c2ecf20Sopenharmony_ci mtd_node); 1028c2ecf20Sopenharmony_ci goto ofpart_fail; 1038c2ecf20Sopenharmony_ci } 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci parts[i].offset = of_read_number(reg, a_cells); 1068c2ecf20Sopenharmony_ci parts[i].size = of_read_number(reg + a_cells, s_cells); 1078c2ecf20Sopenharmony_ci parts[i].of_node = pp; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci partname = of_get_property(pp, "label", &len); 1108c2ecf20Sopenharmony_ci if (!partname) 1118c2ecf20Sopenharmony_ci partname = of_get_property(pp, "name", &len); 1128c2ecf20Sopenharmony_ci parts[i].name = partname; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci if (of_get_property(pp, "read-only", &len)) 1158c2ecf20Sopenharmony_ci parts[i].mask_flags |= MTD_WRITEABLE; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci if (of_get_property(pp, "lock", &len)) 1188c2ecf20Sopenharmony_ci parts[i].mask_flags |= MTD_POWERUP_LOCK; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci if (of_property_read_bool(pp, "slc-mode")) 1218c2ecf20Sopenharmony_ci parts[i].add_flags |= MTD_SLC_ON_MLC_EMULATION; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci i++; 1248c2ecf20Sopenharmony_ci } 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci if (!nr_parts) 1278c2ecf20Sopenharmony_ci goto ofpart_none; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci *pparts = parts; 1308c2ecf20Sopenharmony_ci return nr_parts; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ciofpart_fail: 1338c2ecf20Sopenharmony_ci pr_err("%s: error parsing ofpart partition %pOF (%pOF)\n", 1348c2ecf20Sopenharmony_ci master->name, pp, mtd_node); 1358c2ecf20Sopenharmony_ci ret = -EINVAL; 1368c2ecf20Sopenharmony_ciofpart_none: 1378c2ecf20Sopenharmony_ci of_node_put(pp); 1388c2ecf20Sopenharmony_ci kfree(parts); 1398c2ecf20Sopenharmony_ci return ret; 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic const struct of_device_id parse_ofpart_match_table[] = { 1438c2ecf20Sopenharmony_ci { .compatible = "fixed-partitions" }, 1448c2ecf20Sopenharmony_ci {}, 1458c2ecf20Sopenharmony_ci}; 1468c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, parse_ofpart_match_table); 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cistatic struct mtd_part_parser ofpart_parser = { 1498c2ecf20Sopenharmony_ci .parse_fn = parse_fixed_partitions, 1508c2ecf20Sopenharmony_ci .name = "fixed-partitions", 1518c2ecf20Sopenharmony_ci .of_match_table = parse_ofpart_match_table, 1528c2ecf20Sopenharmony_ci}; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_cistatic int parse_ofoldpart_partitions(struct mtd_info *master, 1558c2ecf20Sopenharmony_ci const struct mtd_partition **pparts, 1568c2ecf20Sopenharmony_ci struct mtd_part_parser_data *data) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci struct mtd_partition *parts; 1598c2ecf20Sopenharmony_ci struct device_node *dp; 1608c2ecf20Sopenharmony_ci int i, plen, nr_parts; 1618c2ecf20Sopenharmony_ci const struct { 1628c2ecf20Sopenharmony_ci __be32 offset, len; 1638c2ecf20Sopenharmony_ci } *part; 1648c2ecf20Sopenharmony_ci const char *names; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci /* Pull of_node from the master device node */ 1678c2ecf20Sopenharmony_ci dp = mtd_get_of_node(master); 1688c2ecf20Sopenharmony_ci if (!dp) 1698c2ecf20Sopenharmony_ci return 0; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci part = of_get_property(dp, "partitions", &plen); 1728c2ecf20Sopenharmony_ci if (!part) 1738c2ecf20Sopenharmony_ci return 0; /* No partitions found */ 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci pr_warn("Device tree uses obsolete partition map binding: %pOF\n", dp); 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci nr_parts = plen / sizeof(part[0]); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci parts = kcalloc(nr_parts, sizeof(*parts), GFP_KERNEL); 1808c2ecf20Sopenharmony_ci if (!parts) 1818c2ecf20Sopenharmony_ci return -ENOMEM; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci names = of_get_property(dp, "partition-names", &plen); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci for (i = 0; i < nr_parts; i++) { 1868c2ecf20Sopenharmony_ci parts[i].offset = be32_to_cpu(part->offset); 1878c2ecf20Sopenharmony_ci parts[i].size = be32_to_cpu(part->len) & ~1; 1888c2ecf20Sopenharmony_ci /* bit 0 set signifies read only partition */ 1898c2ecf20Sopenharmony_ci if (be32_to_cpu(part->len) & 1) 1908c2ecf20Sopenharmony_ci parts[i].mask_flags = MTD_WRITEABLE; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci if (names && (plen > 0)) { 1938c2ecf20Sopenharmony_ci int len = strlen(names) + 1; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci parts[i].name = names; 1968c2ecf20Sopenharmony_ci plen -= len; 1978c2ecf20Sopenharmony_ci names += len; 1988c2ecf20Sopenharmony_ci } else { 1998c2ecf20Sopenharmony_ci parts[i].name = "unnamed"; 2008c2ecf20Sopenharmony_ci } 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci part++; 2038c2ecf20Sopenharmony_ci } 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci *pparts = parts; 2068c2ecf20Sopenharmony_ci return nr_parts; 2078c2ecf20Sopenharmony_ci} 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_cistatic struct mtd_part_parser ofoldpart_parser = { 2108c2ecf20Sopenharmony_ci .parse_fn = parse_ofoldpart_partitions, 2118c2ecf20Sopenharmony_ci .name = "ofoldpart", 2128c2ecf20Sopenharmony_ci}; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_cistatic int __init ofpart_parser_init(void) 2158c2ecf20Sopenharmony_ci{ 2168c2ecf20Sopenharmony_ci register_mtd_parser(&ofpart_parser); 2178c2ecf20Sopenharmony_ci register_mtd_parser(&ofoldpart_parser); 2188c2ecf20Sopenharmony_ci return 0; 2198c2ecf20Sopenharmony_ci} 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_cistatic void __exit ofpart_parser_exit(void) 2228c2ecf20Sopenharmony_ci{ 2238c2ecf20Sopenharmony_ci deregister_mtd_parser(&ofpart_parser); 2248c2ecf20Sopenharmony_ci deregister_mtd_parser(&ofoldpart_parser); 2258c2ecf20Sopenharmony_ci} 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_cimodule_init(ofpart_parser_init); 2288c2ecf20Sopenharmony_cimodule_exit(ofpart_parser_exit); 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 2318c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Parser for MTD partitioning information in device tree"); 2328c2ecf20Sopenharmony_ciMODULE_AUTHOR("Vitaly Wool, David Gibson"); 2338c2ecf20Sopenharmony_ci/* 2348c2ecf20Sopenharmony_ci * When MTD core cannot find the requested parser, it tries to load the module 2358c2ecf20Sopenharmony_ci * with the same name. Since we provide the ofoldpart parser, we should have 2368c2ecf20Sopenharmony_ci * the corresponding alias. 2378c2ecf20Sopenharmony_ci */ 2388c2ecf20Sopenharmony_ciMODULE_ALIAS("fixed-partitions"); 2398c2ecf20Sopenharmony_ciMODULE_ALIAS("ofoldpart"); 240