]>
Commit | Line | Data |
---|---|---|
2874c5fd | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
9a310d21 SW |
2 | /* |
3 | * Flash partitions described by the OF (or flattened) device tree | |
4 | * | |
a1452a37 | 5 | * Copyright © 2006 MontaVista Software Inc. |
9a310d21 SW |
6 | * Author: Vitaly Wool <vwool@ru.mvista.com> |
7 | * | |
8 | * Revised to handle newer style flash binding by: | |
a1452a37 | 9 | * Copyright © 2007 David Gibson, IBM Corporation. |
9a310d21 SW |
10 | */ |
11 | ||
12 | #include <linux/module.h> | |
13 | #include <linux/init.h> | |
14 | #include <linux/of.h> | |
15 | #include <linux/mtd/mtd.h> | |
5a0e3ad6 | 16 | #include <linux/slab.h> |
9a310d21 SW |
17 | #include <linux/mtd/partitions.h> |
18 | ||
e79265ba JW |
19 | static bool node_has_compatible(struct device_node *pp) |
20 | { | |
21 | return of_get_property(pp, "compatible", NULL); | |
22 | } | |
23 | ||
c0faf434 RM |
24 | static int parse_fixed_partitions(struct mtd_info *master, |
25 | const struct mtd_partition **pparts, | |
26 | struct mtd_part_parser_data *data) | |
d26c87d6 | 27 | { |
c3168d26 | 28 | struct mtd_partition *parts; |
5cfdedb7 MS |
29 | struct device_node *mtd_node; |
30 | struct device_node *ofpart_node; | |
9a310d21 SW |
31 | const char *partname; |
32 | struct device_node *pp; | |
5cfdedb7 MS |
33 | int nr_parts, i, ret = 0; |
34 | bool dedicated = true; | |
9a310d21 | 35 | |
628376fb | 36 | |
e270bca5 BN |
37 | /* Pull of_node from the master device node */ |
38 | mtd_node = mtd_get_of_node(master); | |
5cfdedb7 | 39 | if (!mtd_node) |
628376fb DES |
40 | return 0; |
41 | ||
5cfdedb7 MS |
42 | ofpart_node = of_get_child_by_name(mtd_node, "partitions"); |
43 | if (!ofpart_node) { | |
8c62b4e1 BN |
44 | /* |
45 | * We might get here even when ofpart isn't used at all (e.g., | |
46 | * when using another parser), so don't be louder than | |
47 | * KERN_DEBUG | |
48 | */ | |
1d706077 RH |
49 | pr_debug("%s: 'partitions' subnode not found on %pOF. Trying to parse direct subnodes as partitions.\n", |
50 | master->name, mtd_node); | |
5cfdedb7 MS |
51 | ofpart_node = mtd_node; |
52 | dedicated = false; | |
e488ca9f BN |
53 | } else if (!of_device_is_compatible(ofpart_node, "fixed-partitions")) { |
54 | /* The 'partitions' subnode might be used by another parser */ | |
55 | return 0; | |
5cfdedb7 MS |
56 | } |
57 | ||
9a310d21 | 58 | /* First count the subnodes */ |
9a310d21 | 59 | nr_parts = 0; |
5cfdedb7 MS |
60 | for_each_child_of_node(ofpart_node, pp) { |
61 | if (!dedicated && node_has_compatible(pp)) | |
e79265ba JW |
62 | continue; |
63 | ||
9a310d21 | 64 | nr_parts++; |
e79265ba | 65 | } |
9a310d21 SW |
66 | |
67 | if (nr_parts == 0) | |
68 | return 0; | |
69 | ||
6396bb22 | 70 | parts = kcalloc(nr_parts, sizeof(*parts), GFP_KERNEL); |
c3168d26 | 71 | if (!parts) |
9a310d21 SW |
72 | return -ENOMEM; |
73 | ||
9a310d21 | 74 | i = 0; |
5cfdedb7 | 75 | for_each_child_of_node(ofpart_node, pp) { |
766f271a | 76 | const __be32 *reg; |
9a310d21 | 77 | int len; |
05ff8c25 | 78 | int a_cells, s_cells; |
9a310d21 | 79 | |
5cfdedb7 | 80 | if (!dedicated && node_has_compatible(pp)) |
e79265ba JW |
81 | continue; |
82 | ||
ebd5a74d BK |
83 | reg = of_get_property(pp, "reg", &len); |
84 | if (!reg) { | |
5cfdedb7 | 85 | if (dedicated) { |
1d706077 RH |
86 | pr_debug("%s: ofpart partition %pOF (%pOF) missing reg property.\n", |
87 | master->name, pp, | |
88 | mtd_node); | |
5cfdedb7 MS |
89 | goto ofpart_fail; |
90 | } else { | |
91 | nr_parts--; | |
92 | continue; | |
93 | } | |
4b08e149 BK |
94 | } |
95 | ||
05ff8c25 JS |
96 | a_cells = of_n_addr_cells(pp); |
97 | s_cells = of_n_size_cells(pp); | |
5cfdedb7 | 98 | if (len / 4 != a_cells + s_cells) { |
1d706077 RH |
99 | pr_debug("%s: ofpart partition %pOF (%pOF) error parsing reg property.\n", |
100 | master->name, pp, | |
101 | mtd_node); | |
5cfdedb7 MS |
102 | goto ofpart_fail; |
103 | } | |
104 | ||
c3168d26 BN |
105 | parts[i].offset = of_read_number(reg, a_cells); |
106 | parts[i].size = of_read_number(reg + a_cells, s_cells); | |
42e9401b | 107 | parts[i].of_node = pp; |
9a310d21 SW |
108 | |
109 | partname = of_get_property(pp, "label", &len); | |
110 | if (!partname) | |
111 | partname = of_get_property(pp, "name", &len); | |
c3168d26 | 112 | parts[i].name = partname; |
9a310d21 SW |
113 | |
114 | if (of_get_property(pp, "read-only", &len)) | |
c3168d26 | 115 | parts[i].mask_flags |= MTD_WRITEABLE; |
ab0b00bc JR |
116 | |
117 | if (of_get_property(pp, "lock", &len)) | |
c3168d26 | 118 | parts[i].mask_flags |= MTD_POWERUP_LOCK; |
9a310d21 SW |
119 | |
120 | i++; | |
121 | } | |
122 | ||
5cfdedb7 MS |
123 | if (!nr_parts) |
124 | goto ofpart_none; | |
ebd5a74d | 125 | |
c3168d26 | 126 | *pparts = parts; |
9a310d21 | 127 | return nr_parts; |
5cfdedb7 MS |
128 | |
129 | ofpart_fail: | |
1d706077 RH |
130 | pr_err("%s: error parsing ofpart partition %pOF (%pOF)\n", |
131 | master->name, pp, mtd_node); | |
5cfdedb7 MS |
132 | ret = -EINVAL; |
133 | ofpart_none: | |
134 | of_node_put(pp); | |
c3168d26 | 135 | kfree(parts); |
5cfdedb7 | 136 | return ret; |
9a310d21 | 137 | } |
950bcb25 | 138 | |
97b0c7c0 RM |
139 | static const struct of_device_id parse_ofpart_match_table[] = { |
140 | { .compatible = "fixed-partitions" }, | |
141 | {}, | |
142 | }; | |
143 | MODULE_DEVICE_TABLE(of, parse_ofpart_match_table); | |
144 | ||
d26c87d6 | 145 | static struct mtd_part_parser ofpart_parser = { |
c0faf434 RM |
146 | .parse_fn = parse_fixed_partitions, |
147 | .name = "fixed-partitions", | |
97b0c7c0 | 148 | .of_match_table = parse_ofpart_match_table, |
d26c87d6 DES |
149 | }; |
150 | ||
fbcf62a3 | 151 | static int parse_ofoldpart_partitions(struct mtd_info *master, |
b9adf469 | 152 | const struct mtd_partition **pparts, |
fbcf62a3 DES |
153 | struct mtd_part_parser_data *data) |
154 | { | |
c3168d26 | 155 | struct mtd_partition *parts; |
fbcf62a3 DES |
156 | struct device_node *dp; |
157 | int i, plen, nr_parts; | |
158 | const struct { | |
159 | __be32 offset, len; | |
160 | } *part; | |
161 | const char *names; | |
162 | ||
e270bca5 BN |
163 | /* Pull of_node from the master device node */ |
164 | dp = mtd_get_of_node(master); | |
fbcf62a3 DES |
165 | if (!dp) |
166 | return 0; | |
167 | ||
168 | part = of_get_property(dp, "partitions", &plen); | |
169 | if (!part) | |
170 | return 0; /* No partitions found */ | |
171 | ||
1d706077 | 172 | pr_warn("Device tree uses obsolete partition map binding: %pOF\n", dp); |
fbcf62a3 DES |
173 | |
174 | nr_parts = plen / sizeof(part[0]); | |
175 | ||
6396bb22 | 176 | parts = kcalloc(nr_parts, sizeof(*parts), GFP_KERNEL); |
c3168d26 | 177 | if (!parts) |
fbcf62a3 DES |
178 | return -ENOMEM; |
179 | ||
180 | names = of_get_property(dp, "partition-names", &plen); | |
181 | ||
182 | for (i = 0; i < nr_parts; i++) { | |
c3168d26 BN |
183 | parts[i].offset = be32_to_cpu(part->offset); |
184 | parts[i].size = be32_to_cpu(part->len) & ~1; | |
fbcf62a3 DES |
185 | /* bit 0 set signifies read only partition */ |
186 | if (be32_to_cpu(part->len) & 1) | |
c3168d26 | 187 | parts[i].mask_flags = MTD_WRITEABLE; |
fbcf62a3 DES |
188 | |
189 | if (names && (plen > 0)) { | |
190 | int len = strlen(names) + 1; | |
191 | ||
c3168d26 | 192 | parts[i].name = names; |
fbcf62a3 DES |
193 | plen -= len; |
194 | names += len; | |
195 | } else { | |
c3168d26 | 196 | parts[i].name = "unnamed"; |
fbcf62a3 DES |
197 | } |
198 | ||
199 | part++; | |
200 | } | |
201 | ||
c3168d26 | 202 | *pparts = parts; |
fbcf62a3 DES |
203 | return nr_parts; |
204 | } | |
205 | ||
206 | static struct mtd_part_parser ofoldpart_parser = { | |
fbcf62a3 DES |
207 | .parse_fn = parse_ofoldpart_partitions, |
208 | .name = "ofoldpart", | |
209 | }; | |
210 | ||
d26c87d6 DES |
211 | static int __init ofpart_parser_init(void) |
212 | { | |
6e14a61d AL |
213 | register_mtd_parser(&ofpart_parser); |
214 | register_mtd_parser(&ofoldpart_parser); | |
215 | return 0; | |
d26c87d6 DES |
216 | } |
217 | ||
422f3890 LR |
218 | static void __exit ofpart_parser_exit(void) |
219 | { | |
220 | deregister_mtd_parser(&ofpart_parser); | |
221 | deregister_mtd_parser(&ofoldpart_parser); | |
222 | } | |
223 | ||
d26c87d6 | 224 | module_init(ofpart_parser_init); |
422f3890 | 225 | module_exit(ofpart_parser_exit); |
d26c87d6 | 226 | |
950bcb25 | 227 | MODULE_LICENSE("GPL"); |
d6137bad DES |
228 | MODULE_DESCRIPTION("Parser for MTD partitioning information in device tree"); |
229 | MODULE_AUTHOR("Vitaly Wool, David Gibson"); | |
9786f6e6 DES |
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 | */ | |
c0faf434 | 235 | MODULE_ALIAS("fixed-partitions"); |
9786f6e6 | 236 | MODULE_ALIAS("ofoldpart"); |