xref: /openbmc/linux/drivers/mtd/parsers/ofpart_core.c (revision 9a87ffc99ec8eb8d35eed7c4f816d75f5cc9662e)
1bb17230cSRafał Miłecki // SPDX-License-Identifier: GPL-2.0-or-later
2bb17230cSRafał Miłecki /*
3bb17230cSRafał Miłecki  * Flash partitions described by the OF (or flattened) device tree
4bb17230cSRafał Miłecki  *
5bb17230cSRafał Miłecki  * Copyright © 2006 MontaVista Software Inc.
6bb17230cSRafał Miłecki  * Author: Vitaly Wool <vwool@ru.mvista.com>
7bb17230cSRafał Miłecki  *
8bb17230cSRafał Miłecki  * Revised to handle newer style flash binding by:
9bb17230cSRafał Miłecki  *   Copyright © 2007 David Gibson, IBM Corporation.
10bb17230cSRafał Miłecki  */
11bb17230cSRafał Miłecki 
12bb17230cSRafał Miłecki #include <linux/module.h>
13bb17230cSRafał Miłecki #include <linux/init.h>
14bb17230cSRafał Miłecki #include <linux/of.h>
15bb17230cSRafał Miłecki #include <linux/mtd/mtd.h>
16bb17230cSRafał Miłecki #include <linux/slab.h>
17bb17230cSRafał Miłecki #include <linux/mtd/partitions.h>
18bb17230cSRafał Miłecki 
19bb17230cSRafał Miłecki #include "ofpart_bcm4908.h"
207134a2d0SRafał Miłecki #include "ofpart_linksys_ns.h"
21bb17230cSRafał Miłecki 
22bb17230cSRafał Miłecki struct fixed_partitions_quirks {
23bb17230cSRafał Miłecki 	int (*post_parse)(struct mtd_info *mtd, struct mtd_partition *parts, int nr_parts);
24bb17230cSRafał Miłecki };
25bb17230cSRafał Miłecki 
26b87b6d2dSWei Yongjun static struct fixed_partitions_quirks bcm4908_partitions_quirks = {
27bb17230cSRafał Miłecki 	.post_parse = bcm4908_partitions_post_parse,
28bb17230cSRafał Miłecki };
29bb17230cSRafał Miłecki 
307134a2d0SRafał Miłecki static struct fixed_partitions_quirks linksys_ns_partitions_quirks = {
317134a2d0SRafał Miłecki 	.post_parse = linksys_ns_partitions_post_parse,
327134a2d0SRafał Miłecki };
337134a2d0SRafał Miłecki 
34bb17230cSRafał Miłecki static const struct of_device_id parse_ofpart_match_table[];
35bb17230cSRafał Miłecki 
node_has_compatible(struct device_node * pp)36bb17230cSRafał Miłecki static bool node_has_compatible(struct device_node *pp)
37bb17230cSRafał Miłecki {
38bb17230cSRafał Miłecki 	return of_get_property(pp, "compatible", NULL);
39bb17230cSRafał Miłecki }
40bb17230cSRafał Miłecki 
parse_fixed_partitions(struct mtd_info * master,const struct mtd_partition ** pparts,struct mtd_part_parser_data * data)41bb17230cSRafał Miłecki static int parse_fixed_partitions(struct mtd_info *master,
42bb17230cSRafał Miłecki 				  const struct mtd_partition **pparts,
43bb17230cSRafał Miłecki 				  struct mtd_part_parser_data *data)
44bb17230cSRafał Miłecki {
45bb17230cSRafał Miłecki 	const struct fixed_partitions_quirks *quirks;
46bb17230cSRafał Miłecki 	const struct of_device_id *of_id;
47bb17230cSRafał Miłecki 	struct mtd_partition *parts;
48bb17230cSRafał Miłecki 	struct device_node *mtd_node;
49bb17230cSRafał Miłecki 	struct device_node *ofpart_node;
50bb17230cSRafał Miłecki 	const char *partname;
51bb17230cSRafał Miłecki 	struct device_node *pp;
52bb17230cSRafał Miłecki 	int nr_parts, i, ret = 0;
53bb17230cSRafał Miłecki 	bool dedicated = true;
54bb17230cSRafał Miłecki 
55bb17230cSRafał Miłecki 	/* Pull of_node from the master device node */
56bb17230cSRafał Miłecki 	mtd_node = mtd_get_of_node(master);
57bb17230cSRafał Miłecki 	if (!mtd_node)
58bb17230cSRafał Miłecki 		return 0;
59bb17230cSRafał Miłecki 
60562b4e91SRafał Miłecki 	if (!master->parent) { /* Master */
61bb17230cSRafał Miłecki 		ofpart_node = of_get_child_by_name(mtd_node, "partitions");
62562b4e91SRafał Miłecki 		if (!ofpart_node) {
63bb17230cSRafał Miłecki 			/*
64bb17230cSRafał Miłecki 			 * We might get here even when ofpart isn't used at all (e.g.,
65bb17230cSRafał Miłecki 			 * when using another parser), so don't be louder than
66bb17230cSRafał Miłecki 			 * KERN_DEBUG
67bb17230cSRafał Miłecki 			 */
68bb17230cSRafał Miłecki 			pr_debug("%s: 'partitions' subnode not found on %pOF. Trying to parse direct subnodes as partitions.\n",
69bb17230cSRafał Miłecki 				master->name, mtd_node);
70bb17230cSRafał Miłecki 			ofpart_node = mtd_node;
71bb17230cSRafał Miłecki 			dedicated = false;
72bb17230cSRafał Miłecki 		}
73562b4e91SRafał Miłecki 	} else { /* Partition */
74562b4e91SRafał Miłecki 		ofpart_node = mtd_node;
75562b4e91SRafał Miłecki 	}
76bb17230cSRafał Miłecki 
77bb17230cSRafał Miłecki 	of_id = of_match_node(parse_ofpart_match_table, ofpart_node);
78bb17230cSRafał Miłecki 	if (dedicated && !of_id) {
79bb17230cSRafał Miłecki 		/* The 'partitions' subnode might be used by another parser */
80bb17230cSRafał Miłecki 		return 0;
81bb17230cSRafał Miłecki 	}
82bb17230cSRafał Miłecki 
83bb17230cSRafał Miłecki 	quirks = of_id ? of_id->data : NULL;
84bb17230cSRafał Miłecki 
85bb17230cSRafał Miłecki 	/* First count the subnodes */
86bb17230cSRafał Miłecki 	nr_parts = 0;
87bb17230cSRafał Miłecki 	for_each_child_of_node(ofpart_node,  pp) {
88bb17230cSRafał Miłecki 		if (!dedicated && node_has_compatible(pp))
89bb17230cSRafał Miłecki 			continue;
90bb17230cSRafał Miłecki 
91bb17230cSRafał Miłecki 		nr_parts++;
92bb17230cSRafał Miłecki 	}
93bb17230cSRafał Miłecki 
94bb17230cSRafał Miłecki 	if (nr_parts == 0)
95bb17230cSRafał Miłecki 		return 0;
96bb17230cSRafał Miłecki 
97bb17230cSRafał Miłecki 	parts = kcalloc(nr_parts, sizeof(*parts), GFP_KERNEL);
98bb17230cSRafał Miłecki 	if (!parts)
99bb17230cSRafał Miłecki 		return -ENOMEM;
100bb17230cSRafał Miłecki 
101bb17230cSRafał Miłecki 	i = 0;
102bb17230cSRafał Miłecki 	for_each_child_of_node(ofpart_node,  pp) {
103bb17230cSRafał Miłecki 		const __be32 *reg;
104bb17230cSRafał Miłecki 		int len;
105bb17230cSRafał Miłecki 		int a_cells, s_cells;
106bb17230cSRafał Miłecki 
107bb17230cSRafał Miłecki 		if (!dedicated && node_has_compatible(pp))
108bb17230cSRafał Miłecki 			continue;
109bb17230cSRafał Miłecki 
110bb17230cSRafał Miłecki 		reg = of_get_property(pp, "reg", &len);
111bb17230cSRafał Miłecki 		if (!reg) {
112bb17230cSRafał Miłecki 			if (dedicated) {
113bb17230cSRafał Miłecki 				pr_debug("%s: ofpart partition %pOF (%pOF) missing reg property.\n",
114bb17230cSRafał Miłecki 					 master->name, pp,
115bb17230cSRafał Miłecki 					 mtd_node);
116bb17230cSRafał Miłecki 				goto ofpart_fail;
117bb17230cSRafał Miłecki 			} else {
118bb17230cSRafał Miłecki 				nr_parts--;
119bb17230cSRafał Miłecki 				continue;
120bb17230cSRafał Miłecki 			}
121bb17230cSRafał Miłecki 		}
122bb17230cSRafał Miłecki 
123bb17230cSRafał Miłecki 		a_cells = of_n_addr_cells(pp);
124bb17230cSRafał Miłecki 		s_cells = of_n_size_cells(pp);
125*84549c81SFrancesco Dolcini 		if (!dedicated && s_cells == 0) {
126*84549c81SFrancesco Dolcini 			/*
127*84549c81SFrancesco Dolcini 			 * This is a ugly workaround to not create
128*84549c81SFrancesco Dolcini 			 * regression on devices that are still creating
129*84549c81SFrancesco Dolcini 			 * partitions as direct children of the nand controller.
130*84549c81SFrancesco Dolcini 			 * This can happen in case the nand controller node has
131*84549c81SFrancesco Dolcini 			 * #size-cells equal to 0 and the firmware (e.g.
132*84549c81SFrancesco Dolcini 			 * U-Boot) just add the partitions there assuming
133*84549c81SFrancesco Dolcini 			 * 32-bit addressing.
134*84549c81SFrancesco Dolcini 			 *
135*84549c81SFrancesco Dolcini 			 * If you get this warning your firmware and/or DTS
136*84549c81SFrancesco Dolcini 			 * should be really fixed.
137*84549c81SFrancesco Dolcini 			 *
138*84549c81SFrancesco Dolcini 			 * This is working only for devices smaller than 4GiB.
139*84549c81SFrancesco Dolcini 			 */
140*84549c81SFrancesco Dolcini 			pr_warn("%s: ofpart partition %pOF (%pOF) #size-cells is wrongly set to <0>, assuming <1> for parsing partitions.\n",
141*84549c81SFrancesco Dolcini 				master->name, pp, mtd_node);
142*84549c81SFrancesco Dolcini 			s_cells = 1;
143*84549c81SFrancesco Dolcini 		}
144bb17230cSRafał Miłecki 		if (len / 4 != a_cells + s_cells) {
145bb17230cSRafał Miłecki 			pr_debug("%s: ofpart partition %pOF (%pOF) error parsing reg property.\n",
146bb17230cSRafał Miłecki 				 master->name, pp,
147bb17230cSRafał Miłecki 				 mtd_node);
148bb17230cSRafał Miłecki 			goto ofpart_fail;
149bb17230cSRafał Miłecki 		}
150bb17230cSRafał Miłecki 
151bb17230cSRafał Miłecki 		parts[i].offset = of_read_number(reg, a_cells);
152bb17230cSRafał Miłecki 		parts[i].size = of_read_number(reg + a_cells, s_cells);
153bb17230cSRafał Miłecki 		parts[i].of_node = pp;
154bb17230cSRafał Miłecki 
155bb17230cSRafał Miłecki 		partname = of_get_property(pp, "label", &len);
156bb17230cSRafał Miłecki 		if (!partname)
157bb17230cSRafał Miłecki 			partname = of_get_property(pp, "name", &len);
158bb17230cSRafał Miłecki 		parts[i].name = partname;
159bb17230cSRafał Miłecki 
160bb17230cSRafał Miłecki 		if (of_get_property(pp, "read-only", &len))
161bb17230cSRafał Miłecki 			parts[i].mask_flags |= MTD_WRITEABLE;
162bb17230cSRafał Miłecki 
163bb17230cSRafał Miłecki 		if (of_get_property(pp, "lock", &len))
164bb17230cSRafał Miłecki 			parts[i].mask_flags |= MTD_POWERUP_LOCK;
165bb17230cSRafał Miłecki 
166bb17230cSRafał Miłecki 		if (of_property_read_bool(pp, "slc-mode"))
167bb17230cSRafał Miłecki 			parts[i].add_flags |= MTD_SLC_ON_MLC_EMULATION;
168bb17230cSRafał Miłecki 
169bb17230cSRafał Miłecki 		i++;
170bb17230cSRafał Miłecki 	}
171bb17230cSRafał Miłecki 
172bb17230cSRafał Miłecki 	if (!nr_parts)
173bb17230cSRafał Miłecki 		goto ofpart_none;
174bb17230cSRafał Miłecki 
175bb17230cSRafał Miłecki 	if (quirks && quirks->post_parse)
176bb17230cSRafał Miłecki 		quirks->post_parse(master, parts, nr_parts);
177bb17230cSRafał Miłecki 
178bb17230cSRafał Miłecki 	*pparts = parts;
179bb17230cSRafał Miłecki 	return nr_parts;
180bb17230cSRafał Miłecki 
181bb17230cSRafał Miłecki ofpart_fail:
182bb17230cSRafał Miłecki 	pr_err("%s: error parsing ofpart partition %pOF (%pOF)\n",
183bb17230cSRafał Miłecki 	       master->name, pp, mtd_node);
184bb17230cSRafał Miłecki 	ret = -EINVAL;
185bb17230cSRafał Miłecki ofpart_none:
186bb17230cSRafał Miłecki 	of_node_put(pp);
187bb17230cSRafał Miłecki 	kfree(parts);
188bb17230cSRafał Miłecki 	return ret;
189bb17230cSRafał Miłecki }
190bb17230cSRafał Miłecki 
191bb17230cSRafał Miłecki static const struct of_device_id parse_ofpart_match_table[] = {
192bb17230cSRafał Miłecki 	/* Generic */
193bb17230cSRafał Miłecki 	{ .compatible = "fixed-partitions" },
194bb17230cSRafał Miłecki 	/* Customized */
195bb17230cSRafał Miłecki 	{ .compatible = "brcm,bcm4908-partitions", .data = &bcm4908_partitions_quirks, },
1967134a2d0SRafał Miłecki 	{ .compatible = "linksys,ns-partitions", .data = &linksys_ns_partitions_quirks, },
197bb17230cSRafał Miłecki 	{},
198bb17230cSRafał Miłecki };
199bb17230cSRafał Miłecki MODULE_DEVICE_TABLE(of, parse_ofpart_match_table);
200bb17230cSRafał Miłecki 
201bb17230cSRafał Miłecki static struct mtd_part_parser ofpart_parser = {
202bb17230cSRafał Miłecki 	.parse_fn = parse_fixed_partitions,
203bb17230cSRafał Miłecki 	.name = "fixed-partitions",
204bb17230cSRafał Miłecki 	.of_match_table = parse_ofpart_match_table,
205bb17230cSRafał Miłecki };
206bb17230cSRafał Miłecki 
parse_ofoldpart_partitions(struct mtd_info * master,const struct mtd_partition ** pparts,struct mtd_part_parser_data * data)207bb17230cSRafał Miłecki static int parse_ofoldpart_partitions(struct mtd_info *master,
208bb17230cSRafał Miłecki 				      const struct mtd_partition **pparts,
209bb17230cSRafał Miłecki 				      struct mtd_part_parser_data *data)
210bb17230cSRafał Miłecki {
211bb17230cSRafał Miłecki 	struct mtd_partition *parts;
212bb17230cSRafał Miłecki 	struct device_node *dp;
213bb17230cSRafał Miłecki 	int i, plen, nr_parts;
214bb17230cSRafał Miłecki 	const struct {
215bb17230cSRafał Miłecki 		__be32 offset, len;
216bb17230cSRafał Miłecki 	} *part;
217bb17230cSRafał Miłecki 	const char *names;
218bb17230cSRafał Miłecki 
219bb17230cSRafał Miłecki 	/* Pull of_node from the master device node */
220bb17230cSRafał Miłecki 	dp = mtd_get_of_node(master);
221bb17230cSRafał Miłecki 	if (!dp)
222bb17230cSRafał Miłecki 		return 0;
223bb17230cSRafał Miłecki 
224bb17230cSRafał Miłecki 	part = of_get_property(dp, "partitions", &plen);
225bb17230cSRafał Miłecki 	if (!part)
226bb17230cSRafał Miłecki 		return 0; /* No partitions found */
227bb17230cSRafał Miłecki 
228bb17230cSRafał Miłecki 	pr_warn("Device tree uses obsolete partition map binding: %pOF\n", dp);
229bb17230cSRafał Miłecki 
230bb17230cSRafał Miłecki 	nr_parts = plen / sizeof(part[0]);
231bb17230cSRafał Miłecki 
232bb17230cSRafał Miłecki 	parts = kcalloc(nr_parts, sizeof(*parts), GFP_KERNEL);
233bb17230cSRafał Miłecki 	if (!parts)
234bb17230cSRafał Miłecki 		return -ENOMEM;
235bb17230cSRafał Miłecki 
236bb17230cSRafał Miłecki 	names = of_get_property(dp, "partition-names", &plen);
237bb17230cSRafał Miłecki 
238bb17230cSRafał Miłecki 	for (i = 0; i < nr_parts; i++) {
239bb17230cSRafał Miłecki 		parts[i].offset = be32_to_cpu(part->offset);
240bb17230cSRafał Miłecki 		parts[i].size   = be32_to_cpu(part->len) & ~1;
241bb17230cSRafał Miłecki 		/* bit 0 set signifies read only partition */
242bb17230cSRafał Miłecki 		if (be32_to_cpu(part->len) & 1)
243bb17230cSRafał Miłecki 			parts[i].mask_flags = MTD_WRITEABLE;
244bb17230cSRafał Miłecki 
245bb17230cSRafał Miłecki 		if (names && (plen > 0)) {
246bb17230cSRafał Miłecki 			int len = strlen(names) + 1;
247bb17230cSRafał Miłecki 
248bb17230cSRafał Miłecki 			parts[i].name = names;
249bb17230cSRafał Miłecki 			plen -= len;
250bb17230cSRafał Miłecki 			names += len;
251bb17230cSRafał Miłecki 		} else {
252bb17230cSRafał Miłecki 			parts[i].name = "unnamed";
253bb17230cSRafał Miłecki 		}
254bb17230cSRafał Miłecki 
255bb17230cSRafał Miłecki 		part++;
256bb17230cSRafał Miłecki 	}
257bb17230cSRafał Miłecki 
258bb17230cSRafał Miłecki 	*pparts = parts;
259bb17230cSRafał Miłecki 	return nr_parts;
260bb17230cSRafał Miłecki }
261bb17230cSRafał Miłecki 
262bb17230cSRafał Miłecki static struct mtd_part_parser ofoldpart_parser = {
263bb17230cSRafał Miłecki 	.parse_fn = parse_ofoldpart_partitions,
264bb17230cSRafał Miłecki 	.name = "ofoldpart",
265bb17230cSRafał Miłecki };
266bb17230cSRafał Miłecki 
ofpart_parser_init(void)267bb17230cSRafał Miłecki static int __init ofpart_parser_init(void)
268bb17230cSRafał Miłecki {
269bb17230cSRafał Miłecki 	register_mtd_parser(&ofpart_parser);
270bb17230cSRafał Miłecki 	register_mtd_parser(&ofoldpart_parser);
271bb17230cSRafał Miłecki 	return 0;
272bb17230cSRafał Miłecki }
273bb17230cSRafał Miłecki 
ofpart_parser_exit(void)274bb17230cSRafał Miłecki static void __exit ofpart_parser_exit(void)
275bb17230cSRafał Miłecki {
276bb17230cSRafał Miłecki 	deregister_mtd_parser(&ofpart_parser);
277bb17230cSRafał Miłecki 	deregister_mtd_parser(&ofoldpart_parser);
278bb17230cSRafał Miłecki }
279bb17230cSRafał Miłecki 
280bb17230cSRafał Miłecki module_init(ofpart_parser_init);
281bb17230cSRafał Miłecki module_exit(ofpart_parser_exit);
282bb17230cSRafał Miłecki 
283bb17230cSRafał Miłecki MODULE_LICENSE("GPL");
284bb17230cSRafał Miłecki MODULE_DESCRIPTION("Parser for MTD partitioning information in device tree");
285bb17230cSRafał Miłecki MODULE_AUTHOR("Vitaly Wool, David Gibson");
286bb17230cSRafał Miłecki /*
287bb17230cSRafał Miłecki  * When MTD core cannot find the requested parser, it tries to load the module
288bb17230cSRafał Miłecki  * with the same name. Since we provide the ofoldpart parser, we should have
289bb17230cSRafał Miłecki  * the corresponding alias.
290bb17230cSRafał Miłecki  */
291bb17230cSRafał Miłecki MODULE_ALIAS("fixed-partitions");
292bb17230cSRafał Miłecki MODULE_ALIAS("ofoldpart");
293