162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*======================================================================
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci    drivers/mtd/afs.c: ARM Flash Layout/Partitioning
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci    Copyright © 2000 ARM Limited
762306a36Sopenharmony_ci    Copyright (C) 2019 Linus Walleij
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci   This is access code for flashes using ARM's flash partitioning
1162306a36Sopenharmony_ci   standards.
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci======================================================================*/
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include <linux/module.h>
1662306a36Sopenharmony_ci#include <linux/types.h>
1762306a36Sopenharmony_ci#include <linux/kernel.h>
1862306a36Sopenharmony_ci#include <linux/slab.h>
1962306a36Sopenharmony_ci#include <linux/string.h>
2062306a36Sopenharmony_ci#include <linux/init.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#include <linux/mtd/mtd.h>
2362306a36Sopenharmony_ci#include <linux/mtd/map.h>
2462306a36Sopenharmony_ci#include <linux/mtd/partitions.h>
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#define AFSV1_FOOTER_MAGIC 0xA0FFFF9F
2762306a36Sopenharmony_ci#define AFSV2_FOOTER_MAGIC1 0x464C5348 /* "FLSH" */
2862306a36Sopenharmony_ci#define AFSV2_FOOTER_MAGIC2 0x464F4F54 /* "FOOT" */
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistruct footer_v1 {
3162306a36Sopenharmony_ci	u32 image_info_base;	/* Address of first word of ImageFooter  */
3262306a36Sopenharmony_ci	u32 image_start;	/* Start of area reserved by this footer */
3362306a36Sopenharmony_ci	u32 signature;		/* 'Magic' number proves it's a footer   */
3462306a36Sopenharmony_ci	u32 type;		/* Area type: ARM Image, SIB, customer   */
3562306a36Sopenharmony_ci	u32 checksum;		/* Just this structure                   */
3662306a36Sopenharmony_ci};
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistruct image_info_v1 {
3962306a36Sopenharmony_ci	u32 bootFlags;		/* Boot flags, compression etc.          */
4062306a36Sopenharmony_ci	u32 imageNumber;	/* Unique number, selects for boot etc.  */
4162306a36Sopenharmony_ci	u32 loadAddress;	/* Address program should be loaded to   */
4262306a36Sopenharmony_ci	u32 length;		/* Actual size of image                  */
4362306a36Sopenharmony_ci	u32 address;		/* Image is executed from here           */
4462306a36Sopenharmony_ci	char name[16];		/* Null terminated                       */
4562306a36Sopenharmony_ci	u32 headerBase;		/* Flash Address of any stripped header  */
4662306a36Sopenharmony_ci	u32 header_length;	/* Length of header in memory            */
4762306a36Sopenharmony_ci	u32 headerType;		/* AIF, RLF, s-record etc.               */
4862306a36Sopenharmony_ci	u32 checksum;		/* Image checksum (inc. this struct)     */
4962306a36Sopenharmony_ci};
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistatic u32 word_sum(void *words, int num)
5262306a36Sopenharmony_ci{
5362306a36Sopenharmony_ci	u32 *p = words;
5462306a36Sopenharmony_ci	u32 sum = 0;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	while (num--)
5762306a36Sopenharmony_ci		sum += *p++;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	return sum;
6062306a36Sopenharmony_ci}
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_cistatic u32 word_sum_v2(u32 *p, u32 num)
6362306a36Sopenharmony_ci{
6462306a36Sopenharmony_ci	u32 sum = 0;
6562306a36Sopenharmony_ci	int i;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	for (i = 0; i < num; i++) {
6862306a36Sopenharmony_ci		u32 val;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci		val = p[i];
7162306a36Sopenharmony_ci		if (val > ~sum)
7262306a36Sopenharmony_ci			sum++;
7362306a36Sopenharmony_ci		sum += val;
7462306a36Sopenharmony_ci	}
7562306a36Sopenharmony_ci	return ~sum;
7662306a36Sopenharmony_ci}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_cistatic bool afs_is_v1(struct mtd_info *mtd, u_int off)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	/* The magic is 12 bytes from the end of the erase block */
8162306a36Sopenharmony_ci	u_int ptr = off + mtd->erasesize - 12;
8262306a36Sopenharmony_ci	u32 magic;
8362306a36Sopenharmony_ci	size_t sz;
8462306a36Sopenharmony_ci	int ret;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	ret = mtd_read(mtd, ptr, 4, &sz, (u_char *)&magic);
8762306a36Sopenharmony_ci	if (ret < 0) {
8862306a36Sopenharmony_ci		printk(KERN_ERR "AFS: mtd read failed at 0x%x: %d\n",
8962306a36Sopenharmony_ci		       ptr, ret);
9062306a36Sopenharmony_ci		return false;
9162306a36Sopenharmony_ci	}
9262306a36Sopenharmony_ci	if (ret >= 0 && sz != 4)
9362306a36Sopenharmony_ci		return false;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	return (magic == AFSV1_FOOTER_MAGIC);
9662306a36Sopenharmony_ci}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cistatic bool afs_is_v2(struct mtd_info *mtd, u_int off)
9962306a36Sopenharmony_ci{
10062306a36Sopenharmony_ci	/* The magic is the 8 last bytes of the erase block */
10162306a36Sopenharmony_ci	u_int ptr = off + mtd->erasesize - 8;
10262306a36Sopenharmony_ci	u32 foot[2];
10362306a36Sopenharmony_ci	size_t sz;
10462306a36Sopenharmony_ci	int ret;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	ret = mtd_read(mtd, ptr, 8, &sz, (u_char *)foot);
10762306a36Sopenharmony_ci	if (ret < 0) {
10862306a36Sopenharmony_ci		printk(KERN_ERR "AFS: mtd read failed at 0x%x: %d\n",
10962306a36Sopenharmony_ci		       ptr, ret);
11062306a36Sopenharmony_ci		return false;
11162306a36Sopenharmony_ci	}
11262306a36Sopenharmony_ci	if (ret >= 0 && sz != 8)
11362306a36Sopenharmony_ci		return false;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	return (foot[0] == AFSV2_FOOTER_MAGIC1 &&
11662306a36Sopenharmony_ci		foot[1] == AFSV2_FOOTER_MAGIC2);
11762306a36Sopenharmony_ci}
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_cistatic int afs_parse_v1_partition(struct mtd_info *mtd,
12062306a36Sopenharmony_ci				  u_int off, struct mtd_partition *part)
12162306a36Sopenharmony_ci{
12262306a36Sopenharmony_ci	struct footer_v1 fs;
12362306a36Sopenharmony_ci	struct image_info_v1 iis;
12462306a36Sopenharmony_ci	u_int mask;
12562306a36Sopenharmony_ci	/*
12662306a36Sopenharmony_ci	 * Static checks cannot see that we bail out if we have an error
12762306a36Sopenharmony_ci	 * reading the footer.
12862306a36Sopenharmony_ci	 */
12962306a36Sopenharmony_ci	u_int iis_ptr;
13062306a36Sopenharmony_ci	u_int img_ptr;
13162306a36Sopenharmony_ci	u_int ptr;
13262306a36Sopenharmony_ci	size_t sz;
13362306a36Sopenharmony_ci	int ret;
13462306a36Sopenharmony_ci	int i;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	/*
13762306a36Sopenharmony_ci	 * This is the address mask; we use this to mask off out of
13862306a36Sopenharmony_ci	 * range address bits.
13962306a36Sopenharmony_ci	 */
14062306a36Sopenharmony_ci	mask = mtd->size - 1;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	ptr = off + mtd->erasesize - sizeof(fs);
14362306a36Sopenharmony_ci	ret = mtd_read(mtd, ptr, sizeof(fs), &sz, (u_char *)&fs);
14462306a36Sopenharmony_ci	if (ret >= 0 && sz != sizeof(fs))
14562306a36Sopenharmony_ci		ret = -EINVAL;
14662306a36Sopenharmony_ci	if (ret < 0) {
14762306a36Sopenharmony_ci		printk(KERN_ERR "AFS: mtd read failed at 0x%x: %d\n",
14862306a36Sopenharmony_ci		       ptr, ret);
14962306a36Sopenharmony_ci		return ret;
15062306a36Sopenharmony_ci	}
15162306a36Sopenharmony_ci	/*
15262306a36Sopenharmony_ci	 * Check the checksum.
15362306a36Sopenharmony_ci	 */
15462306a36Sopenharmony_ci	if (word_sum(&fs, sizeof(fs) / sizeof(u32)) != 0xffffffff)
15562306a36Sopenharmony_ci		return -EINVAL;
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	/*
15862306a36Sopenharmony_ci	 * Hide the SIB (System Information Block)
15962306a36Sopenharmony_ci	 */
16062306a36Sopenharmony_ci	if (fs.type == 2)
16162306a36Sopenharmony_ci		return 0;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	iis_ptr = fs.image_info_base & mask;
16462306a36Sopenharmony_ci	img_ptr = fs.image_start & mask;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	/*
16762306a36Sopenharmony_ci	 * Check the image info base.  This can not
16862306a36Sopenharmony_ci	 * be located after the footer structure.
16962306a36Sopenharmony_ci	 */
17062306a36Sopenharmony_ci	if (iis_ptr >= ptr)
17162306a36Sopenharmony_ci		return 0;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	/*
17462306a36Sopenharmony_ci	 * Check the start of this image.  The image
17562306a36Sopenharmony_ci	 * data can not be located after this block.
17662306a36Sopenharmony_ci	 */
17762306a36Sopenharmony_ci	if (img_ptr > off)
17862306a36Sopenharmony_ci		return 0;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	/* Read the image info block */
18162306a36Sopenharmony_ci	memset(&iis, 0, sizeof(iis));
18262306a36Sopenharmony_ci	ret = mtd_read(mtd, iis_ptr, sizeof(iis), &sz, (u_char *)&iis);
18362306a36Sopenharmony_ci	if (ret < 0) {
18462306a36Sopenharmony_ci		printk(KERN_ERR "AFS: mtd read failed at 0x%x: %d\n",
18562306a36Sopenharmony_ci		       iis_ptr, ret);
18662306a36Sopenharmony_ci		return -EINVAL;
18762306a36Sopenharmony_ci	}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	if (sz != sizeof(iis))
19062306a36Sopenharmony_ci		return -EINVAL;
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	/*
19362306a36Sopenharmony_ci	 * Validate the name - it must be NUL terminated.
19462306a36Sopenharmony_ci	 */
19562306a36Sopenharmony_ci	for (i = 0; i < sizeof(iis.name); i++)
19662306a36Sopenharmony_ci		if (iis.name[i] == '\0')
19762306a36Sopenharmony_ci			break;
19862306a36Sopenharmony_ci	if (i > sizeof(iis.name))
19962306a36Sopenharmony_ci		return -EINVAL;
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	part->name = kstrdup(iis.name, GFP_KERNEL);
20262306a36Sopenharmony_ci	if (!part->name)
20362306a36Sopenharmony_ci		return -ENOMEM;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	part->size = (iis.length + mtd->erasesize - 1) & ~(mtd->erasesize - 1);
20662306a36Sopenharmony_ci	part->offset = img_ptr;
20762306a36Sopenharmony_ci	part->mask_flags = 0;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	printk("  mtd: at 0x%08x, %5lluKiB, %8u, %s\n",
21062306a36Sopenharmony_ci	       img_ptr, part->size / 1024,
21162306a36Sopenharmony_ci	       iis.imageNumber, part->name);
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	return 0;
21462306a36Sopenharmony_ci}
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_cistatic int afs_parse_v2_partition(struct mtd_info *mtd,
21762306a36Sopenharmony_ci				  u_int off, struct mtd_partition *part)
21862306a36Sopenharmony_ci{
21962306a36Sopenharmony_ci	u_int ptr;
22062306a36Sopenharmony_ci	u32 footer[12];
22162306a36Sopenharmony_ci	u32 imginfo[36];
22262306a36Sopenharmony_ci	char *name;
22362306a36Sopenharmony_ci	u32 version;
22462306a36Sopenharmony_ci	u32 entrypoint;
22562306a36Sopenharmony_ci	u32 attributes;
22662306a36Sopenharmony_ci	u32 region_count;
22762306a36Sopenharmony_ci	u32 block_start;
22862306a36Sopenharmony_ci	u32 block_end;
22962306a36Sopenharmony_ci	u32 crc;
23062306a36Sopenharmony_ci	size_t sz;
23162306a36Sopenharmony_ci	int ret;
23262306a36Sopenharmony_ci	int i;
23362306a36Sopenharmony_ci	int pad = 0;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	pr_debug("Parsing v2 partition @%08x-%08x\n",
23662306a36Sopenharmony_ci		 off, off + mtd->erasesize);
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	/* First read the footer */
23962306a36Sopenharmony_ci	ptr = off + mtd->erasesize - sizeof(footer);
24062306a36Sopenharmony_ci	ret = mtd_read(mtd, ptr, sizeof(footer), &sz, (u_char *)footer);
24162306a36Sopenharmony_ci	if ((ret < 0) || (ret >= 0 && sz != sizeof(footer))) {
24262306a36Sopenharmony_ci		pr_err("AFS: mtd read failed at 0x%x: %d\n",
24362306a36Sopenharmony_ci		       ptr, ret);
24462306a36Sopenharmony_ci		return -EIO;
24562306a36Sopenharmony_ci	}
24662306a36Sopenharmony_ci	name = (char *) &footer[0];
24762306a36Sopenharmony_ci	version = footer[9];
24862306a36Sopenharmony_ci	ptr = off + mtd->erasesize - sizeof(footer) - footer[8];
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	pr_debug("found image \"%s\", version %08x, info @%08x\n",
25162306a36Sopenharmony_ci		 name, version, ptr);
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	/* Then read the image information */
25462306a36Sopenharmony_ci	ret = mtd_read(mtd, ptr, sizeof(imginfo), &sz, (u_char *)imginfo);
25562306a36Sopenharmony_ci	if ((ret < 0) || (ret >= 0 && sz != sizeof(imginfo))) {
25662306a36Sopenharmony_ci		pr_err("AFS: mtd read failed at 0x%x: %d\n",
25762306a36Sopenharmony_ci		       ptr, ret);
25862306a36Sopenharmony_ci		return -EIO;
25962306a36Sopenharmony_ci	}
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	/* 32bit platforms have 4 bytes padding */
26262306a36Sopenharmony_ci	crc = word_sum_v2(&imginfo[1], 34);
26362306a36Sopenharmony_ci	if (!crc) {
26462306a36Sopenharmony_ci		pr_debug("Padding 1 word (4 bytes)\n");
26562306a36Sopenharmony_ci		pad = 1;
26662306a36Sopenharmony_ci	} else {
26762306a36Sopenharmony_ci		/* 64bit platforms have 8 bytes padding */
26862306a36Sopenharmony_ci		crc = word_sum_v2(&imginfo[2], 34);
26962306a36Sopenharmony_ci		if (!crc) {
27062306a36Sopenharmony_ci			pr_debug("Padding 2 words (8 bytes)\n");
27162306a36Sopenharmony_ci			pad = 2;
27262306a36Sopenharmony_ci		}
27362306a36Sopenharmony_ci	}
27462306a36Sopenharmony_ci	if (crc) {
27562306a36Sopenharmony_ci		pr_err("AFS: bad checksum on v2 image info: %08x\n", crc);
27662306a36Sopenharmony_ci		return -EINVAL;
27762306a36Sopenharmony_ci	}
27862306a36Sopenharmony_ci	entrypoint = imginfo[pad];
27962306a36Sopenharmony_ci	attributes = imginfo[pad+1];
28062306a36Sopenharmony_ci	region_count = imginfo[pad+2];
28162306a36Sopenharmony_ci	block_start = imginfo[20];
28262306a36Sopenharmony_ci	block_end = imginfo[21];
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	pr_debug("image entry=%08x, attr=%08x, regions=%08x, "
28562306a36Sopenharmony_ci		 "bs=%08x, be=%08x\n",
28662306a36Sopenharmony_ci		 entrypoint, attributes, region_count,
28762306a36Sopenharmony_ci		 block_start, block_end);
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	for (i = 0; i < region_count; i++) {
29062306a36Sopenharmony_ci		u32 region_load_addr = imginfo[pad + 3 + i*4];
29162306a36Sopenharmony_ci		u32 region_size = imginfo[pad + 4 + i*4];
29262306a36Sopenharmony_ci		u32 region_offset = imginfo[pad + 5 + i*4];
29362306a36Sopenharmony_ci		u32 region_start;
29462306a36Sopenharmony_ci		u32 region_end;
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci		pr_debug("  region %d: address: %08x, size: %08x, "
29762306a36Sopenharmony_ci			 "offset: %08x\n",
29862306a36Sopenharmony_ci			 i,
29962306a36Sopenharmony_ci			 region_load_addr,
30062306a36Sopenharmony_ci			 region_size,
30162306a36Sopenharmony_ci			 region_offset);
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci		region_start = off + region_offset;
30462306a36Sopenharmony_ci		region_end = region_start + region_size;
30562306a36Sopenharmony_ci		/* Align partition to end of erase block */
30662306a36Sopenharmony_ci		region_end += (mtd->erasesize - 1);
30762306a36Sopenharmony_ci		region_end &= ~(mtd->erasesize -1);
30862306a36Sopenharmony_ci		pr_debug("   partition start = %08x, partition end = %08x\n",
30962306a36Sopenharmony_ci			 region_start, region_end);
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci		/* Create one partition per region */
31262306a36Sopenharmony_ci		part->name = kstrdup(name, GFP_KERNEL);
31362306a36Sopenharmony_ci		if (!part->name)
31462306a36Sopenharmony_ci			return -ENOMEM;
31562306a36Sopenharmony_ci		part->offset = region_start;
31662306a36Sopenharmony_ci		part->size = region_end - region_start;
31762306a36Sopenharmony_ci		part->mask_flags = 0;
31862306a36Sopenharmony_ci	}
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	return 0;
32162306a36Sopenharmony_ci}
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_cistatic int parse_afs_partitions(struct mtd_info *mtd,
32462306a36Sopenharmony_ci				const struct mtd_partition **pparts,
32562306a36Sopenharmony_ci				struct mtd_part_parser_data *data)
32662306a36Sopenharmony_ci{
32762306a36Sopenharmony_ci	struct mtd_partition *parts;
32862306a36Sopenharmony_ci	u_int off, sz;
32962306a36Sopenharmony_ci	int ret = 0;
33062306a36Sopenharmony_ci	int i;
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	/* Count the partitions by looping over all erase blocks */
33362306a36Sopenharmony_ci	for (i = off = sz = 0; off < mtd->size; off += mtd->erasesize) {
33462306a36Sopenharmony_ci		if (afs_is_v1(mtd, off)) {
33562306a36Sopenharmony_ci			sz += sizeof(struct mtd_partition);
33662306a36Sopenharmony_ci			i += 1;
33762306a36Sopenharmony_ci		}
33862306a36Sopenharmony_ci		if (afs_is_v2(mtd, off)) {
33962306a36Sopenharmony_ci			sz += sizeof(struct mtd_partition);
34062306a36Sopenharmony_ci			i += 1;
34162306a36Sopenharmony_ci		}
34262306a36Sopenharmony_ci	}
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	if (!i)
34562306a36Sopenharmony_ci		return 0;
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	parts = kzalloc(sz, GFP_KERNEL);
34862306a36Sopenharmony_ci	if (!parts)
34962306a36Sopenharmony_ci		return -ENOMEM;
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	/*
35262306a36Sopenharmony_ci	 * Identify the partitions
35362306a36Sopenharmony_ci	 */
35462306a36Sopenharmony_ci	for (i = off = 0; off < mtd->size; off += mtd->erasesize) {
35562306a36Sopenharmony_ci		if (afs_is_v1(mtd, off)) {
35662306a36Sopenharmony_ci			ret = afs_parse_v1_partition(mtd, off, &parts[i]);
35762306a36Sopenharmony_ci			if (ret)
35862306a36Sopenharmony_ci				goto out_free_parts;
35962306a36Sopenharmony_ci			i++;
36062306a36Sopenharmony_ci		}
36162306a36Sopenharmony_ci		if (afs_is_v2(mtd, off)) {
36262306a36Sopenharmony_ci			ret = afs_parse_v2_partition(mtd, off, &parts[i]);
36362306a36Sopenharmony_ci			if (ret)
36462306a36Sopenharmony_ci				goto out_free_parts;
36562306a36Sopenharmony_ci			i++;
36662306a36Sopenharmony_ci		}
36762306a36Sopenharmony_ci	}
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	*pparts = parts;
37062306a36Sopenharmony_ci	return i;
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ciout_free_parts:
37362306a36Sopenharmony_ci	while (--i >= 0)
37462306a36Sopenharmony_ci		kfree(parts[i].name);
37562306a36Sopenharmony_ci	kfree(parts);
37662306a36Sopenharmony_ci	*pparts = NULL;
37762306a36Sopenharmony_ci	return ret;
37862306a36Sopenharmony_ci}
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_cistatic const struct of_device_id mtd_parser_afs_of_match_table[] = {
38162306a36Sopenharmony_ci	{ .compatible = "arm,arm-firmware-suite" },
38262306a36Sopenharmony_ci	{},
38362306a36Sopenharmony_ci};
38462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, mtd_parser_afs_of_match_table);
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_cistatic struct mtd_part_parser afs_parser = {
38762306a36Sopenharmony_ci	.parse_fn = parse_afs_partitions,
38862306a36Sopenharmony_ci	.name = "afs",
38962306a36Sopenharmony_ci	.of_match_table = mtd_parser_afs_of_match_table,
39062306a36Sopenharmony_ci};
39162306a36Sopenharmony_cimodule_mtd_part_parser(afs_parser);
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ciMODULE_AUTHOR("ARM Ltd");
39462306a36Sopenharmony_ciMODULE_DESCRIPTION("ARM Firmware Suite partition parser");
39562306a36Sopenharmony_ciMODULE_LICENSE("GPL");
396