kernel/5.4: backport mtd parser for Sercomm parts

Fixes: #10143
This commit is contained in:
AmadeusGhost 2022-09-24 23:36:18 +08:00
parent b5bb491bc3
commit 3929a9889f
7 changed files with 314 additions and 273 deletions

View File

@ -0,0 +1,301 @@
From 9b78ef0c7997052e9eaa0f7a4513d546fa17358c Mon Sep 17 00:00:00 2001
From: Mikhail Zhilkin <csharper2005@gmail.com>
Date: Sun, 29 May 2022 11:07:14 +0000
Subject: [PATCH] mtd: parsers: add support for Sercomm partitions
This adds an MTD partition parser for the Sercomm partition table that
is used in some Beeline, Netgear and Sercomm routers.
The Sercomm partition map table contains real partition offsets, which
may differ from device to device depending on the number and location of
bad blocks on NAND.
Original patch (proposed by NOGUCHI Hiroshi):
Link: https://github.com/openwrt/openwrt/pull/1318#issuecomment-420607394
Signed-off-by: NOGUCHI Hiroshi <drvlabo@gmail.com>
Signed-off-by: Mikhail Zhilkin <csharper2005@gmail.com>
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
Link: https://lore.kernel.org/linux-mtd/20220529110714.189732-1-csharper2005@gmail.com
---
drivers/mtd/parsers/Kconfig | 9 ++
drivers/mtd/parsers/Makefile | 1 +
drivers/mtd/parsers/scpart.c | 248 +++++++++++++++++++++++++++++++++++
3 files changed, 258 insertions(+)
create mode 100644 drivers/mtd/parsers/scpart.c
--- a/drivers/mtd/parsers/Kconfig
+++ b/drivers/mtd/parsers/Kconfig
@@ -179,3 +179,12 @@ config MTD_REDBOOT_PARTS_READONLY
'FIS directory' images, enable this option.
endif # MTD_REDBOOT_PARTS
+
+config MTD_SERCOMM_PARTS
+ tristate "Sercomm partition table parser"
+ depends on MTD && RALINK
+ help
+ This provides partitions table parser for devices with Sercomm
+ partition map. This partition table contains real partition
+ offsets, which may differ from device to device depending on the
+ number and location of bad blocks on NAND.
--- a/drivers/mtd/parsers/Makefile
+++ b/drivers/mtd/parsers/Makefile
@@ -10,5 +10,6 @@ ofpart-$(CONFIG_MTD_OF_PARTS_LINKSYS_NS)
obj-$(CONFIG_MTD_PARSER_IMAGETAG) += parser_imagetag.o
obj-$(CONFIG_MTD_AFS_PARTS) += afs.o
obj-$(CONFIG_MTD_PARSER_TRX) += parser_trx.o
+obj-$(CONFIG_MTD_SERCOMM_PARTS) += scpart.o
obj-$(CONFIG_MTD_SHARPSL_PARTS) += sharpslpart.o
obj-$(CONFIG_MTD_REDBOOT_PARTS) += redboot.o
--- /dev/null
+++ b/drivers/mtd/parsers/scpart.c
@@ -0,0 +1,248 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * drivers/mtd/scpart.c: Sercomm Partition Parser
+ *
+ * Copyright (C) 2018 NOGUCHI Hiroshi
+ * Copyright (C) 2022 Mikhail Zhilkin
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/module.h>
+
+#define MOD_NAME "scpart"
+
+#ifdef pr_fmt
+#undef pr_fmt
+#endif
+
+#define pr_fmt(fmt) MOD_NAME ": " fmt
+
+#define ID_ALREADY_FOUND 0xffffffffUL
+
+#define MAP_OFFS_IN_BLK 0x800
+#define MAP_MIRROR_NUM 2
+
+static const char sc_part_magic[] = {
+ 'S', 'C', 'F', 'L', 'M', 'A', 'P', 'O', 'K', '\0',
+};
+#define PART_MAGIC_LEN sizeof(sc_part_magic)
+
+/* assumes that all fields are set by CPU native endian */
+struct sc_part_desc {
+ uint32_t part_id;
+ uint32_t part_offs;
+ uint32_t part_bytes;
+};
+
+static uint32_t scpart_desc_is_valid(struct sc_part_desc *pdesc)
+{
+ return ((pdesc->part_id != 0xffffffffUL) &&
+ (pdesc->part_offs != 0xffffffffUL) &&
+ (pdesc->part_bytes != 0xffffffffUL));
+}
+
+static int scpart_scan_partmap(struct mtd_info *master, loff_t partmap_offs,
+ struct sc_part_desc **ppdesc)
+{
+ int cnt = 0;
+ int res = 0;
+ int res2;
+ loff_t offs;
+ size_t retlen;
+ struct sc_part_desc *pdesc = NULL;
+ struct sc_part_desc *tmpdesc;
+ uint8_t *buf;
+
+ buf = kzalloc(master->erasesize, GFP_KERNEL);
+ if (!buf) {
+ res = -ENOMEM;
+ goto out;
+ }
+
+ res2 = mtd_read(master, partmap_offs, master->erasesize, &retlen, buf);
+ if (res2 || retlen != master->erasesize) {
+ res = -EIO;
+ goto free;
+ }
+
+ for (offs = MAP_OFFS_IN_BLK;
+ offs < master->erasesize - sizeof(*tmpdesc);
+ offs += sizeof(*tmpdesc)) {
+ tmpdesc = (struct sc_part_desc *)&buf[offs];
+ if (!scpart_desc_is_valid(tmpdesc))
+ break;
+ cnt++;
+ }
+
+ if (cnt > 0) {
+ int bytes = cnt * sizeof(*pdesc);
+
+ pdesc = kcalloc(cnt, sizeof(*pdesc), GFP_KERNEL);
+ if (!pdesc) {
+ res = -ENOMEM;
+ goto free;
+ }
+ memcpy(pdesc, &(buf[MAP_OFFS_IN_BLK]), bytes);
+
+ *ppdesc = pdesc;
+ res = cnt;
+ }
+
+free:
+ kfree(buf);
+
+out:
+ return res;
+}
+
+static int scpart_find_partmap(struct mtd_info *master,
+ struct sc_part_desc **ppdesc)
+{
+ int magic_found = 0;
+ int res = 0;
+ int res2;
+ loff_t offs = 0;
+ size_t retlen;
+ uint8_t rdbuf[PART_MAGIC_LEN];
+
+ while ((magic_found < MAP_MIRROR_NUM) &&
+ (offs < master->size) &&
+ !mtd_block_isbad(master, offs)) {
+ res2 = mtd_read(master, offs, PART_MAGIC_LEN, &retlen, rdbuf);
+ if (res2 || retlen != PART_MAGIC_LEN) {
+ res = -EIO;
+ goto out;
+ }
+ if (!memcmp(rdbuf, sc_part_magic, PART_MAGIC_LEN)) {
+ pr_debug("Signature found at 0x%llx\n", offs);
+ magic_found++;
+ res = scpart_scan_partmap(master, offs, ppdesc);
+ if (res > 0)
+ goto out;
+ }
+ offs += master->erasesize;
+ }
+
+out:
+ if (res > 0)
+ pr_info("Valid 'SC PART MAP' (%d partitions) found at 0x%llx\n", res, offs);
+ else
+ pr_info("No valid 'SC PART MAP' was found\n");
+
+ return res;
+}
+
+static int scpart_parse(struct mtd_info *master,
+ const struct mtd_partition **pparts,
+ struct mtd_part_parser_data *data)
+{
+ const char *partname;
+ int n;
+ int nr_scparts;
+ int nr_parts = 0;
+ int res = 0;
+ struct sc_part_desc *scpart_map = NULL;
+ struct mtd_partition *parts = NULL;
+ struct device_node *mtd_node;
+ struct device_node *ofpart_node;
+ struct device_node *pp;
+
+ mtd_node = mtd_get_of_node(master);
+ if (!mtd_node) {
+ res = -ENOENT;
+ goto out;
+ }
+
+ ofpart_node = of_get_child_by_name(mtd_node, "partitions");
+ if (!ofpart_node) {
+ pr_info("%s: 'partitions' subnode not found on %pOF.\n",
+ master->name, mtd_node);
+ res = -ENOENT;
+ goto out;
+ }
+
+ nr_scparts = scpart_find_partmap(master, &scpart_map);
+ if (nr_scparts <= 0) {
+ pr_info("No any partitions was found in 'SC PART MAP'.\n");
+ res = -ENOENT;
+ goto free;
+ }
+
+ parts = kcalloc(of_get_child_count(ofpart_node), sizeof(*parts),
+ GFP_KERNEL);
+ if (!parts) {
+ res = -ENOMEM;
+ goto free;
+ }
+
+ for_each_child_of_node(ofpart_node, pp) {
+ u32 scpart_id;
+
+ if (of_property_read_u32(pp, "sercomm,scpart-id", &scpart_id))
+ continue;
+
+ for (n = 0 ; n < nr_scparts ; n++)
+ if ((scpart_map[n].part_id != ID_ALREADY_FOUND) &&
+ (scpart_id == scpart_map[n].part_id))
+ break;
+ if (n >= nr_scparts)
+ /* not match */
+ continue;
+
+ /* add the partition found in OF into MTD partition array */
+ parts[nr_parts].offset = scpart_map[n].part_offs;
+ parts[nr_parts].size = scpart_map[n].part_bytes;
+ parts[nr_parts].of_node = pp;
+
+ if (!of_property_read_string(pp, "label", &partname))
+ parts[nr_parts].name = partname;
+ if (of_property_read_bool(pp, "read-only"))
+ parts[nr_parts].mask_flags |= MTD_WRITEABLE;
+ if (of_property_read_bool(pp, "lock"))
+ parts[nr_parts].mask_flags |= MTD_POWERUP_LOCK;
+
+ /* mark as 'done' */
+ scpart_map[n].part_id = ID_ALREADY_FOUND;
+
+ nr_parts++;
+ }
+
+ if (nr_parts > 0) {
+ *pparts = parts;
+ res = nr_parts;
+ } else
+ pr_info("No partition in OF matches partition ID with 'SC PART MAP'.\n");
+
+ of_node_put(pp);
+
+free:
+ kfree(scpart_map);
+ if (res <= 0)
+ kfree(parts);
+
+out:
+ return res;
+}
+
+static const struct of_device_id scpart_parser_of_match_table[] = {
+ { .compatible = "sercomm,sc-partitions" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, scpart_parser_of_match_table);
+
+static struct mtd_part_parser scpart_parser = {
+ .parse_fn = scpart_parse,
+ .name = "scpart",
+ .of_match_table = scpart_parser_of_match_table,
+};
+module_mtd_part_parser(scpart_parser);
+
+/* mtd parsers will request the module by parser name */
+MODULE_ALIAS("scpart");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("NOGUCHI Hiroshi <drvlabo@gmail.com>");
+MODULE_AUTHOR("Mikhail Zhilkin <csharper2005@gmail.com>");
+MODULE_DESCRIPTION("Sercomm partition parser");

View File

@ -3289,6 +3289,7 @@ CONFIG_MTD_REDBOOT_DIRECTORY_BLOCK=-1
# CONFIG_MTD_ROM is not set # CONFIG_MTD_ROM is not set
CONFIG_MTD_ROOTFS_ROOT_DEV=y CONFIG_MTD_ROOTFS_ROOT_DEV=y
# CONFIG_MTD_ROUTERBOOT_PARTS is not set # CONFIG_MTD_ROUTERBOOT_PARTS is not set
# CONFIG_MTD_SERCOMM_PARTS is not set
# CONFIG_MTD_SLRAM is not set # CONFIG_MTD_SLRAM is not set
# CONFIG_MTD_SM_COMMON is not set # CONFIG_MTD_SM_COMMON is not set
# CONFIG_MTD_SPINAND_MT29F is not set # CONFIG_MTD_SPINAND_MT29F is not set

View File

@ -16,10 +16,10 @@ Signed-off-by: Thibaut VARÈNE <hacks@slashdirt.org>
--- a/drivers/mtd/parsers/Kconfig --- a/drivers/mtd/parsers/Kconfig
+++ b/drivers/mtd/parsers/Kconfig +++ b/drivers/mtd/parsers/Kconfig
@@ -195,3 +195,12 @@ config MTD_REDBOOT_PARTS_READONLY @@ -204,3 +204,12 @@ config MTD_SERCOMM_PARTS
'FIS directory' images, enable this option. partition map. This partition table contains real partition
offsets, which may differ from device to device depending on the
endif # MTD_REDBOOT_PARTS number and location of bad blocks on NAND.
+ +
+config MTD_ROUTERBOOT_PARTS +config MTD_ROUTERBOOT_PARTS
+ tristate "RouterBoot flash partition parser" + tristate "RouterBoot flash partition parser"
@ -31,8 +31,8 @@ Signed-off-by: Thibaut VARÈNE <hacks@slashdirt.org>
+ formatted DTS. + formatted DTS.
--- a/drivers/mtd/parsers/Makefile --- a/drivers/mtd/parsers/Makefile
+++ b/drivers/mtd/parsers/Makefile +++ b/drivers/mtd/parsers/Makefile
@@ -13,3 +13,4 @@ obj-$(CONFIG_MTD_AFS_PARTS) += afs.o @@ -14,3 +14,4 @@ obj-$(CONFIG_MTD_PARSER_TRX) += parser_
obj-$(CONFIG_MTD_PARSER_TRX) += parser_trx.o obj-$(CONFIG_MTD_SERCOMM_PARTS) += scpart.o
obj-$(CONFIG_MTD_SHARPSL_PARTS) += sharpslpart.o obj-$(CONFIG_MTD_SHARPSL_PARTS) += sharpslpart.o
obj-$(CONFIG_MTD_REDBOOT_PARTS) += redboot.o obj-$(CONFIG_MTD_REDBOOT_PARTS) += redboot.o
+obj-$(CONFIG_MTD_ROUTERBOOT_PARTS) += routerbootpart.o +obj-$(CONFIG_MTD_ROUTERBOOT_PARTS) += routerbootpart.o

View File

@ -19,9 +19,9 @@ Link: https://lore.kernel.org/linux-mtd/20210104041137.113075-3-manivannan.sadha
--- a/drivers/mtd/parsers/Kconfig --- a/drivers/mtd/parsers/Kconfig
+++ b/drivers/mtd/parsers/Kconfig +++ b/drivers/mtd/parsers/Kconfig
@@ -196,6 +196,14 @@ config MTD_REDBOOT_PARTS_READONLY @@ -205,6 +205,14 @@ config MTD_SERCOMM_PARTS
offsets, which may differ from device to device depending on the
endif # MTD_REDBOOT_PARTS number and location of bad blocks on NAND.
+config MTD_QCOMSMEM_PARTS +config MTD_QCOMSMEM_PARTS
+ tristate "Qualcomm SMEM NAND flash partition parser" + tristate "Qualcomm SMEM NAND flash partition parser"
@ -36,8 +36,8 @@ Link: https://lore.kernel.org/linux-mtd/20210104041137.113075-3-manivannan.sadha
depends on MTD && OF depends on MTD && OF
--- a/drivers/mtd/parsers/Makefile --- a/drivers/mtd/parsers/Makefile
+++ b/drivers/mtd/parsers/Makefile +++ b/drivers/mtd/parsers/Makefile
@@ -13,4 +13,5 @@ obj-$(CONFIG_MTD_AFS_PARTS) += afs.o @@ -14,4 +14,5 @@ obj-$(CONFIG_MTD_PARSER_TRX) += parser_
obj-$(CONFIG_MTD_PARSER_TRX) += parser_trx.o obj-$(CONFIG_MTD_SERCOMM_PARTS) += scpart.o
obj-$(CONFIG_MTD_SHARPSL_PARTS) += sharpslpart.o obj-$(CONFIG_MTD_SHARPSL_PARTS) += sharpslpart.o
obj-$(CONFIG_MTD_REDBOOT_PARTS) += redboot.o obj-$(CONFIG_MTD_REDBOOT_PARTS) += redboot.o
+obj-$(CONFIG_MTD_QCOMSMEM_PARTS) += qcomsmempart.o +obj-$(CONFIG_MTD_QCOMSMEM_PARTS) += qcomsmempart.o

View File

@ -1,243 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-or-later OR MIT
/*
* Sercomm/Netgear FLASH partition table.
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/module.h>
#define MOD_NAME "scpart"
static const char sc_part_magic[] = {
'S', 'C', 'F', 'L', 'M', 'A', 'P', 'O', 'K', '\0',
};
#define PART_MAGIC_LEN sizeof(sc_part_magic)
/* assumes that all fields are set by CPU native endian */
struct sc_part_desc {
uint32_t part_id;
uint32_t part_offs;
uint32_t part_bytes;
};
#define ID_ALREADY_FOUND (0xFFFFFFFFUL)
#define MAP_OFFS_IN_BLK (0x800)
#define MAP_MIRROR_NUM (2)
static int scpart_desc_is_valid(struct sc_part_desc *pdesc)
{
return !!((pdesc->part_id != 0xFFFFFFFFUL) &&
(pdesc->part_offs != 0xFFFFFFFFUL) &&
(pdesc->part_bytes != 0xFFFFFFFFUL));
}
static int scpart_scan_partmap(struct mtd_info *master, loff_t partmap_offs,
struct sc_part_desc **ppdesc)
{
uint8_t *buf;
loff_t offs;
size_t retlen;
struct sc_part_desc *pdesc = NULL;
struct sc_part_desc *tmpdesc;
int cnt = 0;
int res2;
int res = 0;
buf = kzalloc(master->erasesize, GFP_KERNEL);
if (!buf) {
res = -ENOMEM;
goto out;
}
res2 = mtd_read(master, partmap_offs, master->erasesize, &retlen, buf);
if (res2 || (retlen != master->erasesize)) {
res = -EIO;
goto free;
}
offs = MAP_OFFS_IN_BLK;
while ((offs + sizeof(*tmpdesc)) < master->erasesize) {
tmpdesc = (struct sc_part_desc *)&(buf[offs]);
if (!scpart_desc_is_valid(tmpdesc))
break;
cnt++;
offs += sizeof(*tmpdesc);
}
if (cnt > 0) {
int bytes = cnt * sizeof(*pdesc);
pdesc = kzalloc(bytes, GFP_KERNEL);
if (!pdesc) {
res = -ENOMEM;
goto free;
}
memcpy(pdesc, &(buf[MAP_OFFS_IN_BLK]), bytes);
*ppdesc = pdesc;
res = cnt;
}
free:
kfree(buf);
out:
return res;
}
static int scpart_find_partmap(struct mtd_info *master,
struct sc_part_desc **ppdesc)
{
loff_t offs;
uint8_t rdbuf[PART_MAGIC_LEN];
size_t retlen;
int magic_found = 0;
int res2;
int res = 0;
offs = 0;
while ((magic_found < MAP_MIRROR_NUM) &&
(offs < master->size) && !mtd_block_isbad(master, offs)) {
res2 = mtd_read(master, offs, PART_MAGIC_LEN, &retlen, rdbuf);
if (res2 || (retlen != PART_MAGIC_LEN)) {
res = -EIO;
goto out;
}
if (!memcmp(rdbuf, sc_part_magic, PART_MAGIC_LEN)) {
pr_debug("%s: signature found at 0x%llx\n", MOD_NAME, offs);
magic_found++;
res = scpart_scan_partmap(master, offs, ppdesc);
if (res > 0)
goto out;
}
offs += master->erasesize;
}
out:
if (res > 0)
pr_info("%s: valid 'SC PART MAP' found (%d partitions)\n", MOD_NAME, res);
else
pr_info("%s: no valid 'SC PART MAP'\n", MOD_NAME);
return res;
}
static int scpart_parse(struct mtd_info *master,
const struct mtd_partition **pparts,
struct mtd_part_parser_data *data)
{
struct sc_part_desc *scpart_map = NULL;
struct mtd_partition *parts = NULL;
struct device_node *mtd_node;
struct device_node *ofpart_node;
struct device_node *pp;
const char *partname;
int nr_scparts;
int nr_parts = 0;
int n;
int res = 0;
mtd_node = mtd_get_of_node(master);
if (!mtd_node)
goto out;
ofpart_node = of_get_child_by_name(mtd_node, "partitions");
if (!ofpart_node)
goto out;
nr_scparts = scpart_find_partmap(master, &scpart_map);
if (nr_scparts <= 0) {
res = nr_scparts;
goto free;
}
for_each_child_of_node(ofpart_node, pp) {
u32 scpart_id;
struct mtd_partition *parts_tmp;
if (of_property_read_u32(pp, "scpart-id", &scpart_id))
continue;
for (n = 0 ; n < nr_scparts ; n++)
if ((scpart_map[n].part_id != ID_ALREADY_FOUND) &&
(scpart_id == scpart_map[n].part_id))
break;
if (n >= nr_scparts)
/* not match */
continue;
/* add the partition found in OF into MTD partition array */
/* reallocate partition array */
parts_tmp = parts;
parts = kzalloc((nr_parts + 1) * sizeof(*parts), GFP_KERNEL);
if (!parts) {
kfree(parts_tmp);
res = -ENOMEM;
goto free;
}
if (parts_tmp) {
memcpy(parts, parts_tmp, nr_parts * sizeof(*parts));
kfree(parts_tmp);
}
parts[nr_parts].offset = scpart_map[n].part_offs;
parts[nr_parts].size = scpart_map[n].part_bytes;
parts[nr_parts].of_node = pp;
if (!of_property_read_string(pp, "label", &partname) ||
!of_property_read_string(pp, "name", &partname))
parts[nr_parts].name = partname;
if (of_property_read_bool(pp, "read-only"))
parts[nr_parts].mask_flags |= MTD_WRITEABLE;
if (of_property_read_bool(pp, "lock"))
parts[nr_parts].mask_flags |= MTD_POWERUP_LOCK;
/* mark as 'done' */
scpart_map[n].part_id = ID_ALREADY_FOUND;
nr_parts++;
}
if (nr_parts > 0) {
*pparts = parts;
res = nr_parts;
} else
pr_info("%s: No partition in OF matches partition ID with 'SC PART MAP'.\n",
MOD_NAME);
of_node_put(pp);
free:
kfree(scpart_map);
if (res <= 0)
kfree(parts);
out:
return res;
}
static const struct of_device_id scpart_parser_of_match_table[] = {
{ .compatible = "sercomm,sc-partitions" },
{},
};
MODULE_DEVICE_TABLE(of, scpart_parser_of_match_table);
static struct mtd_part_parser scpart_parser = {
.parse_fn = scpart_parse,
.name = "scpart",
.of_match_table = scpart_parser_of_match_table,
};
module_mtd_part_parser(scpart_parser);
/* mtd parsers will request the module by parser name */
MODULE_ALIAS("scpart");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("NOGUCHI Hiroshi <drvlabo@gmail.com>");
MODULE_DESCRIPTION("Parsing code for Sercomm/Netgear partition table");

View File

@ -165,6 +165,7 @@ CONFIG_MTD_NAND_MT7621=y
CONFIG_MTD_PHYSMAP=y CONFIG_MTD_PHYSMAP=y
CONFIG_MTD_RAW_NAND=y CONFIG_MTD_RAW_NAND=y
CONFIG_MTD_ROUTERBOOT_PARTS=y CONFIG_MTD_ROUTERBOOT_PARTS=y
CONFIG_MTD_SERCOMM_PARTS=y
CONFIG_MTD_SPI_NOR=y CONFIG_MTD_SPI_NOR=y
CONFIG_MTD_SPLIT_FIT_FW=y CONFIG_MTD_SPLIT_FIT_FW=y
CONFIG_MTD_SPLIT_MINOR_FW=y CONFIG_MTD_SPLIT_MINOR_FW=y

View File

@ -1,19 +0,0 @@
--- a/drivers/mtd/parsers/Kconfig
+++ b/drivers/mtd/parsers/Kconfig
@@ -204,3 +204,9 @@ config MTD_ROUTERBOOT_PARTS
flash, some of which are fixed and some of which are located at
variable offsets. This parser handles both cases via properly
formatted DTS.
+
+config MTD_SERCOMM_PARTS
+ tristate "SERCOMM partitioning information support"
+ depends on MTD_OF_PARTS
+ help
+ This provides partition table parser for SERCOMM partition map
--- a/drivers/mtd/parsers/Makefile
+++ b/drivers/mtd/parsers/Makefile
@@ -14,3 +14,4 @@ obj-$(CONFIG_MTD_PARSER_TRX) += parser_
obj-$(CONFIG_MTD_SHARPSL_PARTS) += sharpslpart.o
obj-$(CONFIG_MTD_REDBOOT_PARTS) += redboot.o
obj-$(CONFIG_MTD_ROUTERBOOT_PARTS) += routerbootpart.o
+obj-$(CONFIG_MTD_SERCOMM_PARTS) += scpart.o