1 From 9b78ef0c7997052e9eaa0f7a4513d546fa17358c Mon Sep 17 00:00:00 2001
2 From: Mikhail Zhilkin <csharper2005@gmail.com>
3 Date: Sun, 29 May 2022 11:07:14 +0000
4 Subject: [PATCH] mtd: parsers: add support for Sercomm partitions
6 This adds an MTD partition parser for the Sercomm partition table that
7 is used in some Beeline, Netgear and Sercomm routers.
9 The Sercomm partition map table contains real partition offsets, which
10 may differ from device to device depending on the number and location of
13 Original patch (proposed by NOGUCHI Hiroshi):
14 Link: https://github.com/openwrt/openwrt/pull/1318#issuecomment-420607394
16 Signed-off-by: NOGUCHI Hiroshi <drvlabo@gmail.com>
17 Signed-off-by: Mikhail Zhilkin <csharper2005@gmail.com>
18 Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
19 Link: https://lore.kernel.org/linux-mtd/20220529110714.189732-1-csharper2005@gmail.com
21 drivers/mtd/parsers/Kconfig | 9 ++
22 drivers/mtd/parsers/Makefile | 1 +
23 drivers/mtd/parsers/scpart.c | 248 +++++++++++++++++++++++++++++++++++
24 3 files changed, 258 insertions(+)
25 create mode 100644 drivers/mtd/parsers/scpart.c
27 --- a/drivers/mtd/parsers/Kconfig
28 +++ b/drivers/mtd/parsers/Kconfig
29 @@ -179,3 +179,12 @@ config MTD_REDBOOT_PARTS_READONLY
30 'FIS directory' images, enable this option.
32 endif # MTD_REDBOOT_PARTS
34 +config MTD_SERCOMM_PARTS
35 + tristate "Sercomm partition table parser"
36 + depends on MTD && RALINK
38 + This provides partitions table parser for devices with Sercomm
39 + partition map. This partition table contains real partition
40 + offsets, which may differ from device to device depending on the
41 + number and location of bad blocks on NAND.
42 --- a/drivers/mtd/parsers/Makefile
43 +++ b/drivers/mtd/parsers/Makefile
44 @@ -10,5 +10,6 @@ ofpart-$(CONFIG_MTD_OF_PARTS_LINKSYS_NS)
45 obj-$(CONFIG_MTD_PARSER_IMAGETAG) += parser_imagetag.o
46 obj-$(CONFIG_MTD_AFS_PARTS) += afs.o
47 obj-$(CONFIG_MTD_PARSER_TRX) += parser_trx.o
48 +obj-$(CONFIG_MTD_SERCOMM_PARTS) += scpart.o
49 obj-$(CONFIG_MTD_SHARPSL_PARTS) += sharpslpart.o
50 obj-$(CONFIG_MTD_REDBOOT_PARTS) += redboot.o
52 +++ b/drivers/mtd/parsers/scpart.c
54 +// SPDX-License-Identifier: GPL-2.0-or-later
56 + * drivers/mtd/scpart.c: Sercomm Partition Parser
58 + * Copyright (C) 2018 NOGUCHI Hiroshi
59 + * Copyright (C) 2022 Mikhail Zhilkin
62 +#include <linux/kernel.h>
63 +#include <linux/slab.h>
64 +#include <linux/mtd/mtd.h>
65 +#include <linux/mtd/partitions.h>
66 +#include <linux/module.h>
68 +#define MOD_NAME "scpart"
74 +#define pr_fmt(fmt) MOD_NAME ": " fmt
76 +#define ID_ALREADY_FOUND 0xffffffffUL
78 +#define MAP_OFFS_IN_BLK 0x800
79 +#define MAP_MIRROR_NUM 2
81 +static const char sc_part_magic[] = {
82 + 'S', 'C', 'F', 'L', 'M', 'A', 'P', 'O', 'K', '\0',
84 +#define PART_MAGIC_LEN sizeof(sc_part_magic)
86 +/* assumes that all fields are set by CPU native endian */
87 +struct sc_part_desc {
90 + uint32_t part_bytes;
93 +static uint32_t scpart_desc_is_valid(struct sc_part_desc *pdesc)
95 + return ((pdesc->part_id != 0xffffffffUL) &&
96 + (pdesc->part_offs != 0xffffffffUL) &&
97 + (pdesc->part_bytes != 0xffffffffUL));
100 +static int scpart_scan_partmap(struct mtd_info *master, loff_t partmap_offs,
101 + struct sc_part_desc **ppdesc)
108 + struct sc_part_desc *pdesc = NULL;
109 + struct sc_part_desc *tmpdesc;
112 + buf = kzalloc(master->erasesize, GFP_KERNEL);
118 + res2 = mtd_read(master, partmap_offs, master->erasesize, &retlen, buf);
119 + if (res2 || retlen != master->erasesize) {
124 + for (offs = MAP_OFFS_IN_BLK;
125 + offs < master->erasesize - sizeof(*tmpdesc);
126 + offs += sizeof(*tmpdesc)) {
127 + tmpdesc = (struct sc_part_desc *)&buf[offs];
128 + if (!scpart_desc_is_valid(tmpdesc))
134 + int bytes = cnt * sizeof(*pdesc);
136 + pdesc = kcalloc(cnt, sizeof(*pdesc), GFP_KERNEL);
141 + memcpy(pdesc, &(buf[MAP_OFFS_IN_BLK]), bytes);
154 +static int scpart_find_partmap(struct mtd_info *master,
155 + struct sc_part_desc **ppdesc)
157 + int magic_found = 0;
162 + uint8_t rdbuf[PART_MAGIC_LEN];
164 + while ((magic_found < MAP_MIRROR_NUM) &&
165 + (offs < master->size) &&
166 + !mtd_block_isbad(master, offs)) {
167 + res2 = mtd_read(master, offs, PART_MAGIC_LEN, &retlen, rdbuf);
168 + if (res2 || retlen != PART_MAGIC_LEN) {
172 + if (!memcmp(rdbuf, sc_part_magic, PART_MAGIC_LEN)) {
173 + pr_debug("Signature found at 0x%llx\n", offs);
175 + res = scpart_scan_partmap(master, offs, ppdesc);
179 + offs += master->erasesize;
184 + pr_info("Valid 'SC PART MAP' (%d partitions) found at 0x%llx\n", res, offs);
186 + pr_info("No valid 'SC PART MAP' was found\n");
191 +static int scpart_parse(struct mtd_info *master,
192 + const struct mtd_partition **pparts,
193 + struct mtd_part_parser_data *data)
195 + const char *partname;
200 + struct sc_part_desc *scpart_map = NULL;
201 + struct mtd_partition *parts = NULL;
202 + struct device_node *mtd_node;
203 + struct device_node *ofpart_node;
204 + struct device_node *pp;
206 + mtd_node = mtd_get_of_node(master);
212 + ofpart_node = of_get_child_by_name(mtd_node, "partitions");
213 + if (!ofpart_node) {
214 + pr_info("%s: 'partitions' subnode not found on %pOF.\n",
215 + master->name, mtd_node);
220 + nr_scparts = scpart_find_partmap(master, &scpart_map);
221 + if (nr_scparts <= 0) {
222 + pr_info("No any partitions was found in 'SC PART MAP'.\n");
227 + parts = kcalloc(of_get_child_count(ofpart_node), sizeof(*parts),
234 + for_each_child_of_node(ofpart_node, pp) {
237 + if (of_property_read_u32(pp, "sercomm,scpart-id", &scpart_id))
240 + for (n = 0 ; n < nr_scparts ; n++)
241 + if ((scpart_map[n].part_id != ID_ALREADY_FOUND) &&
242 + (scpart_id == scpart_map[n].part_id))
244 + if (n >= nr_scparts)
248 + /* add the partition found in OF into MTD partition array */
249 + parts[nr_parts].offset = scpart_map[n].part_offs;
250 + parts[nr_parts].size = scpart_map[n].part_bytes;
251 + parts[nr_parts].of_node = pp;
253 + if (!of_property_read_string(pp, "label", &partname))
254 + parts[nr_parts].name = partname;
255 + if (of_property_read_bool(pp, "read-only"))
256 + parts[nr_parts].mask_flags |= MTD_WRITEABLE;
257 + if (of_property_read_bool(pp, "lock"))
258 + parts[nr_parts].mask_flags |= MTD_POWERUP_LOCK;
260 + /* mark as 'done' */
261 + scpart_map[n].part_id = ID_ALREADY_FOUND;
266 + if (nr_parts > 0) {
270 + pr_info("No partition in OF matches partition ID with 'SC PART MAP'.\n");
283 +static const struct of_device_id scpart_parser_of_match_table[] = {
284 + { .compatible = "sercomm,sc-partitions" },
287 +MODULE_DEVICE_TABLE(of, scpart_parser_of_match_table);
289 +static struct mtd_part_parser scpart_parser = {
290 + .parse_fn = scpart_parse,
292 + .of_match_table = scpart_parser_of_match_table,
294 +module_mtd_part_parser(scpart_parser);
296 +/* mtd parsers will request the module by parser name */
297 +MODULE_ALIAS("scpart");
298 +MODULE_LICENSE("GPL");
299 +MODULE_AUTHOR("NOGUCHI Hiroshi <drvlabo@gmail.com>");
300 +MODULE_AUTHOR("Mikhail Zhilkin <csharper2005@gmail.com>");
301 +MODULE_DESCRIPTION("Sercomm partition parser");