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