brcm63xx: enhance dt partitions support to match upstream more closely
[openwrt/openwrt.git] / target / linux / brcm63xx / patches-4.4 / 425-bcm63xxpart_parse_paritions_from_dt.patch
1 --- a/drivers/mtd/bcm63xxpart.c
2 +++ b/drivers/mtd/bcm63xxpart.c
3 @@ -32,6 +32,7 @@
4 #include <linux/vmalloc.h>
5 #include <linux/mtd/mtd.h>
6 #include <linux/mtd/partitions.h>
7 +#include <linux/of.h>
8
9 #include <asm/mach-bcm63xx/bcm63xx_nvram.h>
10 #include <asm/mach-bcm63xx/bcm963xx_tag.h>
11 @@ -43,46 +44,35 @@
12
13 #define BCM63XX_CFE_MAGIC_OFFSET 0x4e0
14
15 -static int bcm63xx_parse_cfe_partitions(struct mtd_info *master,
16 - struct mtd_partition **pparts,
17 - struct mtd_part_parser_data *data)
18 +static bool node_has_compatible(struct device_node *pp)
19 +{
20 + return of_get_property(pp, "compatible", NULL);
21 +}
22 +
23 +static int parse_bcmtag(struct mtd_info *master, struct mtd_partition *pparts,
24 + int next_part, size_t offset, size_t size)
25 {
26 - /* CFE, NVRAM and global Linux are always present */
27 - int nrparts = 3, curpart = 0;
28 struct bcm_tag *buf;
29 - struct mtd_partition *parts;
30 + u32 computed_crc;
31 int ret;
32 size_t retlen;
33 - unsigned int rootfsaddr, kerneladdr, spareaddr;
34 - unsigned int rootfslen, kernellen, sparelen, totallen;
35 - unsigned int cfelen, nvramlen;
36 - unsigned int cfe_erasesize;
37 - int i;
38 - u32 computed_crc;
39 + unsigned int rootfsaddr, kerneladdr;
40 + unsigned int rootfslen, kernellen, totallen;
41 bool rootfs_first = false;
42 -
43 - if (!bcm63xx_is_cfe_present())
44 - return -EINVAL;
45 -
46 - cfe_erasesize = max_t(uint32_t, master->erasesize,
47 - BCM63XX_CFE_BLOCK_SIZE);
48 -
49 - cfelen = cfe_erasesize;
50 - nvramlen = bcm63xx_nvram_get_psi_size() * SZ_1K;
51 - nvramlen = roundup(nvramlen, cfe_erasesize);
52 + int curr_part = next_part;
53
54 /* Allocate memory for buffer */
55 - buf = vmalloc(sizeof(struct bcm_tag));
56 + buf = vmalloc(sizeof(*buf));
57 if (!buf)
58 return -ENOMEM;
59
60 /* Get the tag */
61 - ret = mtd_read(master, cfelen, sizeof(struct bcm_tag), &retlen,
62 + ret = mtd_read(master, offset, sizeof(*buf), &retlen,
63 (void *)buf);
64
65 - if (retlen != sizeof(struct bcm_tag)) {
66 + if (retlen != sizeof(*buf)) {
67 vfree(buf);
68 - return -EIO;
69 + return 0;
70 }
71
72 computed_crc = crc32_le(IMAGETAG_CRC_START, (u8 *)buf,
73 @@ -101,7 +91,6 @@ static int bcm63xx_parse_cfe_partitions(
74
75 kerneladdr = kerneladdr - BCM63XX_EXTENDED_SIZE;
76 rootfsaddr = rootfsaddr - BCM63XX_EXTENDED_SIZE;
77 - spareaddr = roundup(totallen, master->erasesize) + cfelen;
78
79 if (rootfsaddr < kerneladdr) {
80 /* default Broadcom layout */
81 @@ -110,8 +99,8 @@ static int bcm63xx_parse_cfe_partitions(
82 } else {
83 /* OpenWrt layout */
84 rootfsaddr = kerneladdr + kernellen;
85 - rootfslen = buf->real_rootfs_length;
86 - spareaddr = rootfsaddr + rootfslen;
87 + rootfslen = size - kernellen -
88 + sizeof(*buf);
89 }
90 } else {
91 pr_warn("CFE boot tag CRC invalid (expected %08x, actual %08x)\n",
92 @@ -119,23 +108,145 @@ static int bcm63xx_parse_cfe_partitions(
93 kernellen = 0;
94 rootfslen = 0;
95 rootfsaddr = 0;
96 - spareaddr = cfelen;
97 }
98 - sparelen = master->size - spareaddr - nvramlen;
99
100 - /* Determine number of partitions */
101 - if (rootfslen > 0)
102 - nrparts++;
103 + if (kernellen > 0) {
104 + int kernelpart = curr_part;
105 +
106 + if (rootfslen > 0 && rootfs_first)
107 + kernelpart++;
108 + pparts[kernelpart].name = "kernel";
109 + pparts[kernelpart].offset = kerneladdr;
110 + pparts[kernelpart].size = kernellen;
111 + curr_part++;
112 + }
113 +
114 + if (rootfslen > 0) {
115 + int rootfspart = curr_part;
116 +
117 + if (kernellen > 0 && rootfs_first)
118 + rootfspart--;
119 + pparts[rootfspart].name = "rootfs";
120 + pparts[rootfspart].offset = rootfsaddr;
121 + pparts[rootfspart].size = rootfslen;
122 +
123 + curr_part++;
124 + }
125 +
126 + vfree(buf);
127 +
128 + return curr_part - next_part;
129 +}
130 +
131 +
132 +static int bcm63xx_parse_cfe_partitions_of(struct mtd_info *master,
133 + struct mtd_partition **pparts,
134 + struct mtd_part_parser_data *data)
135 +{
136 + struct device_node *dp, *mtd_node = mtd_get_of_node(master);
137 + struct device_node *pp;
138 + int i, nr_parts = 0;
139 + const char *partname;
140 + int len;
141 +
142 + dp = of_get_child_by_name(mtd_node, "partitions");
143 + if (!dp)
144 + dp = mtd_node;
145 +
146 + for_each_child_of_node(dp, pp) {
147 + if (node_has_compatible(pp) && dp == mtd_node)
148 + continue;
149 +
150 + if (!of_get_property(pp, "reg", &len))
151 + continue;
152 +
153 + partname = of_get_property(pp, "label", &len);
154 + if (!partname)
155 + partname = of_get_property(pp, "name", &len);
156 +
157 + if (!strcmp(partname, "linux") ||
158 + of_device_is_compatible(pp, "brcm,bcm963xx-imagetag"))
159 + nr_parts += 2;
160 +
161 + nr_parts++;
162 + }
163 +
164 + *pparts = kzalloc(nr_parts * sizeof(**pparts), GFP_KERNEL);
165 + if (!*pparts)
166 + return -ENOMEM;
167 +
168 + i = 0;
169 + for_each_child_of_node(dp, pp) {
170 + const __be32 *reg;
171 + int a_cells, s_cells;
172 + size_t size, offset;
173 +
174 + if (node_has_compatible(pp) && dp == mtd_node)
175 + continue;
176 +
177 + reg = of_get_property(pp, "reg", &len);
178 + if (!reg)
179 + continue;
180 +
181 + a_cells = of_n_addr_cells(pp);
182 + s_cells = of_n_size_cells(pp);
183 + offset = of_read_number(reg, a_cells);
184 + size = of_read_number(reg + a_cells, s_cells);
185 + partname = of_get_property(pp, "label", &len);
186 + if (!partname)
187 + partname = of_get_property(pp, "name", &len);
188 +
189 + if (!strcmp(partname, "linux") ||
190 + of_device_is_compatible(pp, "brcm,bcm963xx-imagetag"))
191 + i += parse_bcmtag(master, *pparts, i, offset, size);
192 +
193 + if (of_get_property(pp, "read-only", &len))
194 + (*pparts)[i].mask_flags |= MTD_WRITEABLE;
195 +
196 + if (of_get_property(pp, "lock", &len))
197 + (*pparts)[i].mask_flags |= MTD_POWERUP_LOCK;
198 +
199 + (*pparts)[i].offset = offset;
200 + (*pparts)[i].size = size;
201 + (*pparts)[i].name = partname;
202 +
203 + i++;
204 + }
205 +
206 + return i;
207 +}
208 +
209 +static int bcm63xx_parse_cfe_partitions(struct mtd_info *master,
210 + struct mtd_partition **pparts,
211 + struct mtd_part_parser_data *data)
212 +{
213 + /* CFE, NVRAM and global Linux are always present */
214 + int nrparts = 5, curpart = 0;
215 + struct mtd_partition *parts;
216 + unsigned int nvramaddr;
217 + unsigned int cfelen, nvramlen;
218 + unsigned int cfe_erasesize;
219 + unsigned int imageaddr, imagelen;
220 + int i;
221 +
222 + if (!bcm63xx_is_cfe_present())
223 + return -EINVAL;
224 +
225 + cfe_erasesize = max_t(uint32_t, master->erasesize,
226 + BCM63XX_CFE_BLOCK_SIZE);
227 +
228 + cfelen = cfe_erasesize;
229 + nvramlen = bcm63xx_nvram_get_psi_size() * SZ_1K;
230 + nvramlen = roundup(nvramlen, cfe_erasesize);
231 + nvramaddr = master->size - nvramlen;
232
233 - if (kernellen > 0)
234 - nrparts++;
235 + imageaddr = cfelen;
236 + imagelen = nvramaddr - imageaddr;
237
238 /* Ask kernel for more memory */
239 parts = kzalloc(sizeof(*parts) * nrparts + 10 * nrparts, GFP_KERNEL);
240 - if (!parts) {
241 - vfree(buf);
242 + if (!parts)
243 return -ENOMEM;
244 - }
245
246 /* Start building partition list */
247 parts[curpart].name = "CFE";
248 @@ -143,29 +254,7 @@ static int bcm63xx_parse_cfe_partitions(
249 parts[curpart].size = cfelen;
250 curpart++;
251
252 - if (kernellen > 0) {
253 - int kernelpart = curpart;
254 -
255 - if (rootfslen > 0 && rootfs_first)
256 - kernelpart++;
257 - parts[kernelpart].name = "kernel";
258 - parts[kernelpart].offset = kerneladdr;
259 - parts[kernelpart].size = kernellen;
260 - curpart++;
261 - }
262 -
263 - if (rootfslen > 0) {
264 - int rootfspart = curpart;
265 -
266 - if (kernellen > 0 && rootfs_first)
267 - rootfspart--;
268 - parts[rootfspart].name = "rootfs";
269 - parts[rootfspart].offset = rootfsaddr;
270 - parts[rootfspart].size = rootfslen;
271 - if (sparelen > 0 && !rootfs_first)
272 - parts[rootfspart].size += sparelen;
273 - curpart++;
274 - }
275 + curpart += parse_bcmtag(master, parts, curpart, imageaddr, imagelen);
276
277 parts[curpart].name = "nvram";
278 parts[curpart].offset = master->size - nvramlen;
279 @@ -174,25 +263,37 @@ static int bcm63xx_parse_cfe_partitions(
280
281 /* Global partition "linux" to make easy firmware upgrade */
282 parts[curpart].name = "linux";
283 - parts[curpart].offset = cfelen;
284 - parts[curpart].size = master->size - cfelen - nvramlen;
285 + parts[curpart].offset = imageaddr;
286 + parts[curpart].size = imagelen;
287 + curpart++;
288
289 - for (i = 0; i < nrparts; i++)
290 + for (i = 0; i < curpart; i++)
291 pr_info("Partition %d is %s offset %llx and length %llx\n", i,
292 parts[i].name, parts[i].offset, parts[i].size);
293
294 - pr_info("Spare partition is offset %x and length %x\n", spareaddr,
295 - sparelen);
296 -
297 *pparts = parts;
298 - vfree(buf);
299
300 return nrparts;
301 };
302
303 +
304 +static int bcm63xx_parse_partitions(struct mtd_info *master,
305 + struct mtd_partition **pparts,
306 + struct mtd_part_parser_data *data)
307 +{
308 + struct device_node *np, *mtd_node = mtd_get_of_node(master);
309 + np = of_get_child_by_name(mtd_node, "partitions");
310 +
311 + if ((np && of_device_is_compatible(np, "fixed-partitions")) ||
312 + (!np && of_get_child_count(mtd_node)))
313 + return bcm63xx_parse_cfe_partitions_of(master, pparts, data);
314 + else
315 + return bcm63xx_parse_cfe_partitions(master, pparts, data);
316 +}
317 +
318 static struct mtd_part_parser bcm63xx_cfe_parser = {
319 .owner = THIS_MODULE,
320 - .parse_fn = bcm63xx_parse_cfe_partitions,
321 + .parse_fn = bcm63xx_parse_partitions,
322 .name = "bcm63xxpart",
323 };
324