11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
22aa3b8e1SLinus Walleij /*======================================================================
32aa3b8e1SLinus Walleij
42aa3b8e1SLinus Walleij drivers/mtd/afs.c: ARM Flash Layout/Partitioning
52aa3b8e1SLinus Walleij
62aa3b8e1SLinus Walleij Copyright © 2000 ARM Limited
7b7cf5e28SLinus Walleij Copyright (C) 2019 Linus Walleij
82aa3b8e1SLinus Walleij
92aa3b8e1SLinus Walleij
102aa3b8e1SLinus Walleij This is access code for flashes using ARM's flash partitioning
112aa3b8e1SLinus Walleij standards.
122aa3b8e1SLinus Walleij
132aa3b8e1SLinus Walleij ======================================================================*/
142aa3b8e1SLinus Walleij
152aa3b8e1SLinus Walleij #include <linux/module.h>
162aa3b8e1SLinus Walleij #include <linux/types.h>
172aa3b8e1SLinus Walleij #include <linux/kernel.h>
182aa3b8e1SLinus Walleij #include <linux/slab.h>
192aa3b8e1SLinus Walleij #include <linux/string.h>
202aa3b8e1SLinus Walleij #include <linux/init.h>
212aa3b8e1SLinus Walleij
222aa3b8e1SLinus Walleij #include <linux/mtd/mtd.h>
232aa3b8e1SLinus Walleij #include <linux/mtd/map.h>
242aa3b8e1SLinus Walleij #include <linux/mtd/partitions.h>
252aa3b8e1SLinus Walleij
262aa3b8e1SLinus Walleij #define AFSV1_FOOTER_MAGIC 0xA0FFFF9F
27b7cf5e28SLinus Walleij #define AFSV2_FOOTER_MAGIC1 0x464C5348 /* "FLSH" */
28b7cf5e28SLinus Walleij #define AFSV2_FOOTER_MAGIC2 0x464F4F54 /* "FOOT" */
292aa3b8e1SLinus Walleij
302aa3b8e1SLinus Walleij struct footer_v1 {
312aa3b8e1SLinus Walleij u32 image_info_base; /* Address of first word of ImageFooter */
322aa3b8e1SLinus Walleij u32 image_start; /* Start of area reserved by this footer */
332aa3b8e1SLinus Walleij u32 signature; /* 'Magic' number proves it's a footer */
342aa3b8e1SLinus Walleij u32 type; /* Area type: ARM Image, SIB, customer */
352aa3b8e1SLinus Walleij u32 checksum; /* Just this structure */
362aa3b8e1SLinus Walleij };
372aa3b8e1SLinus Walleij
382aa3b8e1SLinus Walleij struct image_info_v1 {
392aa3b8e1SLinus Walleij u32 bootFlags; /* Boot flags, compression etc. */
402aa3b8e1SLinus Walleij u32 imageNumber; /* Unique number, selects for boot etc. */
412aa3b8e1SLinus Walleij u32 loadAddress; /* Address program should be loaded to */
422aa3b8e1SLinus Walleij u32 length; /* Actual size of image */
432aa3b8e1SLinus Walleij u32 address; /* Image is executed from here */
442aa3b8e1SLinus Walleij char name[16]; /* Null terminated */
452aa3b8e1SLinus Walleij u32 headerBase; /* Flash Address of any stripped header */
462aa3b8e1SLinus Walleij u32 header_length; /* Length of header in memory */
472aa3b8e1SLinus Walleij u32 headerType; /* AIF, RLF, s-record etc. */
482aa3b8e1SLinus Walleij u32 checksum; /* Image checksum (inc. this struct) */
492aa3b8e1SLinus Walleij };
502aa3b8e1SLinus Walleij
word_sum(void * words,int num)512aa3b8e1SLinus Walleij static u32 word_sum(void *words, int num)
522aa3b8e1SLinus Walleij {
532aa3b8e1SLinus Walleij u32 *p = words;
542aa3b8e1SLinus Walleij u32 sum = 0;
552aa3b8e1SLinus Walleij
562aa3b8e1SLinus Walleij while (num--)
572aa3b8e1SLinus Walleij sum += *p++;
582aa3b8e1SLinus Walleij
592aa3b8e1SLinus Walleij return sum;
602aa3b8e1SLinus Walleij }
612aa3b8e1SLinus Walleij
word_sum_v2(u32 * p,u32 num)62b7cf5e28SLinus Walleij static u32 word_sum_v2(u32 *p, u32 num)
63b7cf5e28SLinus Walleij {
64b7cf5e28SLinus Walleij u32 sum = 0;
65b7cf5e28SLinus Walleij int i;
66b7cf5e28SLinus Walleij
67b7cf5e28SLinus Walleij for (i = 0; i < num; i++) {
68b7cf5e28SLinus Walleij u32 val;
69b7cf5e28SLinus Walleij
70b7cf5e28SLinus Walleij val = p[i];
71b7cf5e28SLinus Walleij if (val > ~sum)
72b7cf5e28SLinus Walleij sum++;
73b7cf5e28SLinus Walleij sum += val;
74b7cf5e28SLinus Walleij }
75b7cf5e28SLinus Walleij return ~sum;
76b7cf5e28SLinus Walleij }
77b7cf5e28SLinus Walleij
afs_is_v1(struct mtd_info * mtd,u_int off)7820700171SLinus Walleij static bool afs_is_v1(struct mtd_info *mtd, u_int off)
7920700171SLinus Walleij {
8020700171SLinus Walleij /* The magic is 12 bytes from the end of the erase block */
8120700171SLinus Walleij u_int ptr = off + mtd->erasesize - 12;
8220700171SLinus Walleij u32 magic;
8320700171SLinus Walleij size_t sz;
8420700171SLinus Walleij int ret;
8520700171SLinus Walleij
8620700171SLinus Walleij ret = mtd_read(mtd, ptr, 4, &sz, (u_char *)&magic);
8720700171SLinus Walleij if (ret < 0) {
8820700171SLinus Walleij printk(KERN_ERR "AFS: mtd read failed at 0x%x: %d\n",
8920700171SLinus Walleij ptr, ret);
9020700171SLinus Walleij return false;
9120700171SLinus Walleij }
9220700171SLinus Walleij if (ret >= 0 && sz != 4)
9320700171SLinus Walleij return false;
9420700171SLinus Walleij
9520700171SLinus Walleij return (magic == AFSV1_FOOTER_MAGIC);
9620700171SLinus Walleij }
9720700171SLinus Walleij
afs_is_v2(struct mtd_info * mtd,u_int off)98b7cf5e28SLinus Walleij static bool afs_is_v2(struct mtd_info *mtd, u_int off)
99b7cf5e28SLinus Walleij {
100b7cf5e28SLinus Walleij /* The magic is the 8 last bytes of the erase block */
101b7cf5e28SLinus Walleij u_int ptr = off + mtd->erasesize - 8;
102b7cf5e28SLinus Walleij u32 foot[2];
103b7cf5e28SLinus Walleij size_t sz;
104b7cf5e28SLinus Walleij int ret;
105b7cf5e28SLinus Walleij
106b7cf5e28SLinus Walleij ret = mtd_read(mtd, ptr, 8, &sz, (u_char *)foot);
107b7cf5e28SLinus Walleij if (ret < 0) {
108b7cf5e28SLinus Walleij printk(KERN_ERR "AFS: mtd read failed at 0x%x: %d\n",
109b7cf5e28SLinus Walleij ptr, ret);
110b7cf5e28SLinus Walleij return false;
111b7cf5e28SLinus Walleij }
112b7cf5e28SLinus Walleij if (ret >= 0 && sz != 8)
113b7cf5e28SLinus Walleij return false;
114b7cf5e28SLinus Walleij
115b7cf5e28SLinus Walleij return (foot[0] == AFSV2_FOOTER_MAGIC1 &&
116b7cf5e28SLinus Walleij foot[1] == AFSV2_FOOTER_MAGIC2);
117b7cf5e28SLinus Walleij }
118b7cf5e28SLinus Walleij
afs_parse_v1_partition(struct mtd_info * mtd,u_int off,struct mtd_partition * part)1194aeb1594SLinus Walleij static int afs_parse_v1_partition(struct mtd_info *mtd,
1204aeb1594SLinus Walleij u_int off, struct mtd_partition *part)
1212aa3b8e1SLinus Walleij {
122ff827b4eSLinus Walleij struct footer_v1 fs;
1234aeb1594SLinus Walleij struct image_info_v1 iis;
1244aeb1594SLinus Walleij u_int mask;
1254aeb1594SLinus Walleij /*
1264aeb1594SLinus Walleij * Static checks cannot see that we bail out if we have an error
1274aeb1594SLinus Walleij * reading the footer.
1284aeb1594SLinus Walleij */
1293f649ab7SKees Cook u_int iis_ptr;
1303f649ab7SKees Cook u_int img_ptr;
131ff827b4eSLinus Walleij u_int ptr;
132ff827b4eSLinus Walleij size_t sz;
1334aeb1594SLinus Walleij int ret;
13432e68beaSLinus Walleij int i;
1352aa3b8e1SLinus Walleij
1362aa3b8e1SLinus Walleij /*
1372aa3b8e1SLinus Walleij * This is the address mask; we use this to mask off out of
1382aa3b8e1SLinus Walleij * range address bits.
1392aa3b8e1SLinus Walleij */
1402aa3b8e1SLinus Walleij mask = mtd->size - 1;
1412aa3b8e1SLinus Walleij
142ff827b4eSLinus Walleij ptr = off + mtd->erasesize - sizeof(fs);
143ff827b4eSLinus Walleij ret = mtd_read(mtd, ptr, sizeof(fs), &sz, (u_char *)&fs);
144ff827b4eSLinus Walleij if (ret >= 0 && sz != sizeof(fs))
145ff827b4eSLinus Walleij ret = -EINVAL;
146ff827b4eSLinus Walleij if (ret < 0) {
147ff827b4eSLinus Walleij printk(KERN_ERR "AFS: mtd read failed at 0x%x: %d\n",
148ff827b4eSLinus Walleij ptr, ret);
1494aeb1594SLinus Walleij return ret;
150ff827b4eSLinus Walleij }
151ff827b4eSLinus Walleij /*
152ff827b4eSLinus Walleij * Check the checksum.
153ff827b4eSLinus Walleij */
154ff827b4eSLinus Walleij if (word_sum(&fs, sizeof(fs) / sizeof(u32)) != 0xffffffff)
155ff827b4eSLinus Walleij return -EINVAL;
156ff827b4eSLinus Walleij
157ff827b4eSLinus Walleij /*
158ff827b4eSLinus Walleij * Hide the SIB (System Information Block)
159ff827b4eSLinus Walleij */
160ff827b4eSLinus Walleij if (fs.type == 2)
161ff827b4eSLinus Walleij return 0;
162ff827b4eSLinus Walleij
163ff827b4eSLinus Walleij iis_ptr = fs.image_info_base & mask;
164ff827b4eSLinus Walleij img_ptr = fs.image_start & mask;
165ff827b4eSLinus Walleij
166ff827b4eSLinus Walleij /*
167ff827b4eSLinus Walleij * Check the image info base. This can not
168ff827b4eSLinus Walleij * be located after the footer structure.
169ff827b4eSLinus Walleij */
170ff827b4eSLinus Walleij if (iis_ptr >= ptr)
171ff827b4eSLinus Walleij return 0;
172ff827b4eSLinus Walleij
173ff827b4eSLinus Walleij /*
174ff827b4eSLinus Walleij * Check the start of this image. The image
175ff827b4eSLinus Walleij * data can not be located after this block.
176ff827b4eSLinus Walleij */
177ff827b4eSLinus Walleij if (img_ptr > off)
178ff827b4eSLinus Walleij return 0;
1794aeb1594SLinus Walleij
1804aeb1594SLinus Walleij /* Read the image info block */
18132e68beaSLinus Walleij memset(&iis, 0, sizeof(iis));
18232e68beaSLinus Walleij ret = mtd_read(mtd, iis_ptr, sizeof(iis), &sz, (u_char *)&iis);
18332e68beaSLinus Walleij if (ret < 0) {
18432e68beaSLinus Walleij printk(KERN_ERR "AFS: mtd read failed at 0x%x: %d\n",
18532e68beaSLinus Walleij iis_ptr, ret);
18632e68beaSLinus Walleij return -EINVAL;
18732e68beaSLinus Walleij }
18832e68beaSLinus Walleij
18932e68beaSLinus Walleij if (sz != sizeof(iis))
19032e68beaSLinus Walleij return -EINVAL;
19132e68beaSLinus Walleij
19232e68beaSLinus Walleij /*
19332e68beaSLinus Walleij * Validate the name - it must be NUL terminated.
19432e68beaSLinus Walleij */
19532e68beaSLinus Walleij for (i = 0; i < sizeof(iis.name); i++)
19632e68beaSLinus Walleij if (iis.name[i] == '\0')
19732e68beaSLinus Walleij break;
19832e68beaSLinus Walleij if (i > sizeof(iis.name))
19932e68beaSLinus Walleij return -EINVAL;
2004aeb1594SLinus Walleij
2014aeb1594SLinus Walleij part->name = kstrdup(iis.name, GFP_KERNEL);
2024aeb1594SLinus Walleij if (!part->name)
2034aeb1594SLinus Walleij return -ENOMEM;
2044aeb1594SLinus Walleij
2054aeb1594SLinus Walleij part->size = (iis.length + mtd->erasesize - 1) & ~(mtd->erasesize - 1);
2064aeb1594SLinus Walleij part->offset = img_ptr;
2074aeb1594SLinus Walleij part->mask_flags = 0;
2084aeb1594SLinus Walleij
2094aeb1594SLinus Walleij printk(" mtd: at 0x%08x, %5lluKiB, %8u, %s\n",
2104aeb1594SLinus Walleij img_ptr, part->size / 1024,
2114aeb1594SLinus Walleij iis.imageNumber, part->name);
2124aeb1594SLinus Walleij
2134aeb1594SLinus Walleij return 0;
2144aeb1594SLinus Walleij }
2154aeb1594SLinus Walleij
afs_parse_v2_partition(struct mtd_info * mtd,u_int off,struct mtd_partition * part)216b7cf5e28SLinus Walleij static int afs_parse_v2_partition(struct mtd_info *mtd,
217b7cf5e28SLinus Walleij u_int off, struct mtd_partition *part)
218b7cf5e28SLinus Walleij {
219b7cf5e28SLinus Walleij u_int ptr;
220b7cf5e28SLinus Walleij u32 footer[12];
221b7cf5e28SLinus Walleij u32 imginfo[36];
222b7cf5e28SLinus Walleij char *name;
223b7cf5e28SLinus Walleij u32 version;
224b7cf5e28SLinus Walleij u32 entrypoint;
225b7cf5e28SLinus Walleij u32 attributes;
226b7cf5e28SLinus Walleij u32 region_count;
227b7cf5e28SLinus Walleij u32 block_start;
228b7cf5e28SLinus Walleij u32 block_end;
229b7cf5e28SLinus Walleij u32 crc;
230b7cf5e28SLinus Walleij size_t sz;
231b7cf5e28SLinus Walleij int ret;
232b7cf5e28SLinus Walleij int i;
233b7cf5e28SLinus Walleij int pad = 0;
234b7cf5e28SLinus Walleij
235b7cf5e28SLinus Walleij pr_debug("Parsing v2 partition @%08x-%08x\n",
236b7cf5e28SLinus Walleij off, off + mtd->erasesize);
237b7cf5e28SLinus Walleij
238b7cf5e28SLinus Walleij /* First read the footer */
239b7cf5e28SLinus Walleij ptr = off + mtd->erasesize - sizeof(footer);
240b7cf5e28SLinus Walleij ret = mtd_read(mtd, ptr, sizeof(footer), &sz, (u_char *)footer);
241b7cf5e28SLinus Walleij if ((ret < 0) || (ret >= 0 && sz != sizeof(footer))) {
242b7cf5e28SLinus Walleij pr_err("AFS: mtd read failed at 0x%x: %d\n",
243b7cf5e28SLinus Walleij ptr, ret);
244b7cf5e28SLinus Walleij return -EIO;
245b7cf5e28SLinus Walleij }
246b7cf5e28SLinus Walleij name = (char *) &footer[0];
247b7cf5e28SLinus Walleij version = footer[9];
248b7cf5e28SLinus Walleij ptr = off + mtd->erasesize - sizeof(footer) - footer[8];
249b7cf5e28SLinus Walleij
250b7cf5e28SLinus Walleij pr_debug("found image \"%s\", version %08x, info @%08x\n",
251b7cf5e28SLinus Walleij name, version, ptr);
252b7cf5e28SLinus Walleij
253b7cf5e28SLinus Walleij /* Then read the image information */
254b7cf5e28SLinus Walleij ret = mtd_read(mtd, ptr, sizeof(imginfo), &sz, (u_char *)imginfo);
255b7cf5e28SLinus Walleij if ((ret < 0) || (ret >= 0 && sz != sizeof(imginfo))) {
256b7cf5e28SLinus Walleij pr_err("AFS: mtd read failed at 0x%x: %d\n",
257b7cf5e28SLinus Walleij ptr, ret);
258b7cf5e28SLinus Walleij return -EIO;
259b7cf5e28SLinus Walleij }
260b7cf5e28SLinus Walleij
261b7cf5e28SLinus Walleij /* 32bit platforms have 4 bytes padding */
262b7cf5e28SLinus Walleij crc = word_sum_v2(&imginfo[1], 34);
263b7cf5e28SLinus Walleij if (!crc) {
264b7cf5e28SLinus Walleij pr_debug("Padding 1 word (4 bytes)\n");
265b7cf5e28SLinus Walleij pad = 1;
266b7cf5e28SLinus Walleij } else {
267b7cf5e28SLinus Walleij /* 64bit platforms have 8 bytes padding */
268b7cf5e28SLinus Walleij crc = word_sum_v2(&imginfo[2], 34);
269b7cf5e28SLinus Walleij if (!crc) {
270b7cf5e28SLinus Walleij pr_debug("Padding 2 words (8 bytes)\n");
271b7cf5e28SLinus Walleij pad = 2;
272b7cf5e28SLinus Walleij }
273b7cf5e28SLinus Walleij }
274b7cf5e28SLinus Walleij if (crc) {
275b7cf5e28SLinus Walleij pr_err("AFS: bad checksum on v2 image info: %08x\n", crc);
276b7cf5e28SLinus Walleij return -EINVAL;
277b7cf5e28SLinus Walleij }
278b7cf5e28SLinus Walleij entrypoint = imginfo[pad];
279b7cf5e28SLinus Walleij attributes = imginfo[pad+1];
280b7cf5e28SLinus Walleij region_count = imginfo[pad+2];
281b7cf5e28SLinus Walleij block_start = imginfo[20];
282b7cf5e28SLinus Walleij block_end = imginfo[21];
283b7cf5e28SLinus Walleij
284b7cf5e28SLinus Walleij pr_debug("image entry=%08x, attr=%08x, regions=%08x, "
285b7cf5e28SLinus Walleij "bs=%08x, be=%08x\n",
286b7cf5e28SLinus Walleij entrypoint, attributes, region_count,
287b7cf5e28SLinus Walleij block_start, block_end);
288b7cf5e28SLinus Walleij
289b7cf5e28SLinus Walleij for (i = 0; i < region_count; i++) {
290b7cf5e28SLinus Walleij u32 region_load_addr = imginfo[pad + 3 + i*4];
291b7cf5e28SLinus Walleij u32 region_size = imginfo[pad + 4 + i*4];
292b7cf5e28SLinus Walleij u32 region_offset = imginfo[pad + 5 + i*4];
293b7cf5e28SLinus Walleij u32 region_start;
294b7cf5e28SLinus Walleij u32 region_end;
295b7cf5e28SLinus Walleij
296b7cf5e28SLinus Walleij pr_debug(" region %d: address: %08x, size: %08x, "
297b7cf5e28SLinus Walleij "offset: %08x\n",
298b7cf5e28SLinus Walleij i,
299b7cf5e28SLinus Walleij region_load_addr,
300b7cf5e28SLinus Walleij region_size,
301b7cf5e28SLinus Walleij region_offset);
302b7cf5e28SLinus Walleij
303b7cf5e28SLinus Walleij region_start = off + region_offset;
304b7cf5e28SLinus Walleij region_end = region_start + region_size;
305b7cf5e28SLinus Walleij /* Align partition to end of erase block */
306b7cf5e28SLinus Walleij region_end += (mtd->erasesize - 1);
307b7cf5e28SLinus Walleij region_end &= ~(mtd->erasesize -1);
308b7cf5e28SLinus Walleij pr_debug(" partition start = %08x, partition end = %08x\n",
309b7cf5e28SLinus Walleij region_start, region_end);
310b7cf5e28SLinus Walleij
311b7cf5e28SLinus Walleij /* Create one partition per region */
312b7cf5e28SLinus Walleij part->name = kstrdup(name, GFP_KERNEL);
313b7cf5e28SLinus Walleij if (!part->name)
314b7cf5e28SLinus Walleij return -ENOMEM;
315b7cf5e28SLinus Walleij part->offset = region_start;
316b7cf5e28SLinus Walleij part->size = region_end - region_start;
317b7cf5e28SLinus Walleij part->mask_flags = 0;
318b7cf5e28SLinus Walleij }
319b7cf5e28SLinus Walleij
320b7cf5e28SLinus Walleij return 0;
321b7cf5e28SLinus Walleij }
322b7cf5e28SLinus Walleij
parse_afs_partitions(struct mtd_info * mtd,const struct mtd_partition ** pparts,struct mtd_part_parser_data * data)3234aeb1594SLinus Walleij static int parse_afs_partitions(struct mtd_info *mtd,
3244aeb1594SLinus Walleij const struct mtd_partition **pparts,
3254aeb1594SLinus Walleij struct mtd_part_parser_data *data)
3264aeb1594SLinus Walleij {
3274aeb1594SLinus Walleij struct mtd_partition *parts;
3284aeb1594SLinus Walleij u_int off, sz;
3294aeb1594SLinus Walleij int ret = 0;
3304aeb1594SLinus Walleij int i;
3314aeb1594SLinus Walleij
33220700171SLinus Walleij /* Count the partitions by looping over all erase blocks */
3331fca1f6aSLinus Walleij for (i = off = sz = 0; off < mtd->size; off += mtd->erasesize) {
33420700171SLinus Walleij if (afs_is_v1(mtd, off)) {
3351fca1f6aSLinus Walleij sz += sizeof(struct mtd_partition);
3361fca1f6aSLinus Walleij i += 1;
3371fca1f6aSLinus Walleij }
338b7cf5e28SLinus Walleij if (afs_is_v2(mtd, off)) {
339b7cf5e28SLinus Walleij sz += sizeof(struct mtd_partition);
340b7cf5e28SLinus Walleij i += 1;
341b7cf5e28SLinus Walleij }
3421fca1f6aSLinus Walleij }
3431fca1f6aSLinus Walleij
3441fca1f6aSLinus Walleij if (!i)
3451fca1f6aSLinus Walleij return 0;
3462aa3b8e1SLinus Walleij
3472aa3b8e1SLinus Walleij parts = kzalloc(sz, GFP_KERNEL);
3482aa3b8e1SLinus Walleij if (!parts)
3492aa3b8e1SLinus Walleij return -ENOMEM;
3502aa3b8e1SLinus Walleij
3512aa3b8e1SLinus Walleij /*
3522aa3b8e1SLinus Walleij * Identify the partitions
3532aa3b8e1SLinus Walleij */
3541fca1f6aSLinus Walleij for (i = off = 0; off < mtd->size; off += mtd->erasesize) {
3554aeb1594SLinus Walleij if (afs_is_v1(mtd, off)) {
3564aeb1594SLinus Walleij ret = afs_parse_v1_partition(mtd, off, &parts[i]);
3574aeb1594SLinus Walleij if (ret)
3581fca1f6aSLinus Walleij goto out_free_parts;
3594aeb1594SLinus Walleij i++;
3602aa3b8e1SLinus Walleij }
361b7cf5e28SLinus Walleij if (afs_is_v2(mtd, off)) {
362b7cf5e28SLinus Walleij ret = afs_parse_v2_partition(mtd, off, &parts[i]);
363b7cf5e28SLinus Walleij if (ret)
364b7cf5e28SLinus Walleij goto out_free_parts;
365b7cf5e28SLinus Walleij i++;
366b7cf5e28SLinus Walleij }
3672aa3b8e1SLinus Walleij }
3682aa3b8e1SLinus Walleij
3692aa3b8e1SLinus Walleij *pparts = parts;
3701fca1f6aSLinus Walleij return i;
3711fca1f6aSLinus Walleij
3721fca1f6aSLinus Walleij out_free_parts:
373*7b844cf4SManivannan Sadhasivam while (--i >= 0)
3741fca1f6aSLinus Walleij kfree(parts[i].name);
3751fca1f6aSLinus Walleij kfree(parts);
3761fca1f6aSLinus Walleij *pparts = NULL;
3771fca1f6aSLinus Walleij return ret;
3782aa3b8e1SLinus Walleij }
3792aa3b8e1SLinus Walleij
38022749bf5SLinus Walleij static const struct of_device_id mtd_parser_afs_of_match_table[] = {
38122749bf5SLinus Walleij { .compatible = "arm,arm-firmware-suite" },
38222749bf5SLinus Walleij {},
38322749bf5SLinus Walleij };
38422749bf5SLinus Walleij MODULE_DEVICE_TABLE(of, mtd_parser_afs_of_match_table);
38522749bf5SLinus Walleij
3862aa3b8e1SLinus Walleij static struct mtd_part_parser afs_parser = {
3872aa3b8e1SLinus Walleij .parse_fn = parse_afs_partitions,
3882aa3b8e1SLinus Walleij .name = "afs",
38922749bf5SLinus Walleij .of_match_table = mtd_parser_afs_of_match_table,
3902aa3b8e1SLinus Walleij };
3912aa3b8e1SLinus Walleij module_mtd_part_parser(afs_parser);
3922aa3b8e1SLinus Walleij
3932aa3b8e1SLinus Walleij MODULE_AUTHOR("ARM Ltd");
3942aa3b8e1SLinus Walleij MODULE_DESCRIPTION("ARM Firmware Suite partition parser");
3952aa3b8e1SLinus Walleij MODULE_LICENSE("GPL");
396