diff mbox series

[RFT] ramips: use partition table on Netgear NAND devices

Message ID 20200628232747.1367531-1-jan@3e8.eu
State Superseded, archived
Headers show
Series [RFT] ramips: use partition table on Netgear NAND devices | expand

Commit Message

Jan Hoffmann June 28, 2020, 11:27 p.m. UTC
There have been multiple reports of the partition table on these devices
being incorrect when there are bad blocks. This change should fix this by
parsing the partition table that is stored in the SC_PART_MAP partition.

This is essentially the same code as proposed in the original pull request
for Netgear R6350 support by NOGUCHI Hiroshi <drvlabo@gmail.com>:
https://github.com/openwrt/openwrt/pull/1318

At the time, it was rejected in favor of fixed partition offsets, as it
did not seem to work correctly. However, this was due the NAND driver
transparently shifting pages to hide bad blocks, which was fixed in
commit 527832e54bf3bc4d699a145ae66f34230246f0a9.

Signed-off-by: Jan Hoffmann <jan@3e8.eu>
---

So far this patch has only been tested on my own WAC124, which doesn't
have any bad blocks (i.e. the partition table that is read from flash 
matches the existing fixed offsets).

It needs further testing to verify that it actually fixes the issue on
devices that have bad blocks, and does not cause any additional issues.

This change applies to the following devices:
R6260, R6350, R6850, WAC124 (CHJ)
R6800, R6700-v2, R7200, Nighthawk AC2400 (BZV)


 .../dts/mt7621_netgear_sercomm_bzv.dtsi       | 169 +++++++++++-
 .../dts/mt7621_netgear_sercomm_chj.dtsi       | 169 +++++++++++-
 .../ramips/files/drivers/mtd/parsers/scpart.c | 257 ++++++++++++++++++
 target/linux/ramips/mt7621/config-5.4         |   1 +
 .../patches-5.4/303-mtd-scpart-parser.patch   |  19 ++
 5 files changed, 607 insertions(+), 8 deletions(-)
 create mode 100644 target/linux/ramips/files/drivers/mtd/parsers/scpart.c
 create mode 100644 target/linux/ramips/patches-5.4/303-mtd-scpart-parser.patch

Comments

Jost Menke Jan. 22, 2021, 12:25 p.m. UTC | #1
I successfully tested this patch on a Netgear R6800 with one bad 
eraseblock. Without the patch, the device is unable to correctly read 
the factory partition. It will generate random MAC addresses because the 
actual addresses are unreadable, and TX power will be limited to 6dBm, 
rendering it rather useless.
With the patch, everything works as expected.


Regards

Jost


Am 29.06.20 um 01:27 schrieb Jan Hoffmann:
> There have been multiple reports of the partition table on these devices
> being incorrect when there are bad blocks. This change should fix this by
> parsing the partition table that is stored in the SC_PART_MAP partition.
>
> This is essentially the same code as proposed in the original pull request
> for Netgear R6350 support by NOGUCHI Hiroshi <drvlabo@gmail.com>:
> https://github.com/openwrt/openwrt/pull/1318
>
> At the time, it was rejected in favor of fixed partition offsets, as it
> did not seem to work correctly. However, this was due the NAND driver
> transparently shifting pages to hide bad blocks, which was fixed in
> commit 527832e54bf3bc4d699a145ae66f34230246f0a9.
>
> Signed-off-by: Jan Hoffmann <jan@3e8.eu>
> ---
>
> So far this patch has only been tested on my own WAC124, which doesn't
> have any bad blocks (i.e. the partition table that is read from flash
> matches the existing fixed offsets).
>
> It needs further testing to verify that it actually fixes the issue on
> devices that have bad blocks, and does not cause any additional issues.
>
> This change applies to the following devices:
> R6260, R6350, R6850, WAC124 (CHJ)
> R6800, R6700-v2, R7200, Nighthawk AC2400 (BZV)
>
>
>   .../dts/mt7621_netgear_sercomm_bzv.dtsi       | 169 +++++++++++-
>   .../dts/mt7621_netgear_sercomm_chj.dtsi       | 169 +++++++++++-
>   .../ramips/files/drivers/mtd/parsers/scpart.c | 257 ++++++++++++++++++
>   target/linux/ramips/mt7621/config-5.4         |   1 +
>   .../patches-5.4/303-mtd-scpart-parser.patch   |  19 ++
>   5 files changed, 607 insertions(+), 8 deletions(-)
>   create mode 100644 target/linux/ramips/files/drivers/mtd/parsers/scpart.c
>   create mode 100644 target/linux/ramips/patches-5.4/303-mtd-scpart-parser.patch
>
> diff --git a/target/linux/ramips/dts/mt7621_netgear_sercomm_bzv.dtsi b/target/linux/ramips/dts/mt7621_netgear_sercomm_bzv.dtsi
> index d2e5502e48..2ab0b75862 100644
> --- a/target/linux/ramips/dts/mt7621_netgear_sercomm_bzv.dtsi
> +++ b/target/linux/ramips/dts/mt7621_netgear_sercomm_bzv.dtsi
> @@ -113,47 +113,208 @@
>   	status = "okay";
>   
>   	partitions {
> -		compatible = "fixed-partitions";
> +		compatible = "sercomm,sc-partitions", "fixed-partitions";
>   		#address-cells = <1>;
>   		#size-cells = <1>;
>   
>   		partition@0 {
>   			label = "u-boot";
>   			reg = <0x0 0x100000>;
> +			scpart-id = <0>;
>   			read-only;
>   		};
>   
>   		partition@100000 {
>   			label = "SC PART_MAP";
>   			reg = <0x100000 0x100000>;
> +			scpart-id = <1>;
>   			read-only;
>   		};
>   
>   		partition@200000 {
>   			label = "kernel";
>   			reg = <0x200000 0x400000>;
> +			scpart-id = <2>;
>   		};
>   
>   		partition@600000 {
>   			label = "ubi";
>   			reg = <0x600000 0x2800000>;
> +			scpart-id = <3>;
>   		};
>   
>   		partition@2e00000 {
> -			label = "reserved0";
> -			reg = <0x2e00000 0x1800000>;
> +			label = "English UI";
> +			reg = <0x2e00000 0x200000>;
> +			scpart-id = <4>;
> +			read-only;
> +		};
> +
> +		partition@3000000 {
> +			label = "ML1";
> +			reg = <0x3000000 0x200000>;
> +			scpart-id = <5>;
> +			read-only;
> +		};
> +
> +		partition@3200000 {
> +			label = "ML2";
> +			reg = <0x3200000 0x200000>;
> +			scpart-id = <6>;
> +			read-only;
> +		};
> +
> +		partition@3400000 {
> +			label = "ML3";
> +			reg = <0x3400000 0x200000>;
> +			scpart-id = <7>;
> +			read-only;
> +		};
> +
> +		partition@3600000 {
> +			label = "ML4";
> +			reg = <0x3600000 0x200000>;
> +			scpart-id = <8>;
> +			read-only;
> +		};
> +
> +		partition@3800000 {
> +			label = "ML5";
> +			reg = <0x3800000 0x200000>;
> +			scpart-id = <9>;
> +			read-only;
> +		};
> +
> +		partition@3a00000 {
> +			label = "ML6";
> +			reg = <0x3a00000 0x200000>;
> +			scpart-id = <10>;
> +			read-only;
> +		};
> +
> +		partition@3c00000 {
> +			label = "ML7";
> +			reg = <0x3c00000 0x200000>;
> +			scpart-id = <11>;
> +			read-only;
> +		};
> +
> +		partition@3e00000 {
> +			label = "ML8";
> +			reg = <0x3e00000 0x200000>;
> +			scpart-id = <12>;
> +			read-only;
> +		};
> +
> +		partition@4000000 {
> +			label = "ML9";
> +			reg = <0x4000000 0x200000>;
> +			scpart-id = <13>;
> +			read-only;
> +		};
> +
> +		partition@4200000 {
> +			label = "ML10";
> +			reg = <0x4200000 0x200000>;
> +			scpart-id = <14>;
> +			read-only;
> +		};
> +
> +		partition@4400000 {
> +			label = "ML11";
> +			reg = <0x4400000 0x200000>;
> +			scpart-id = <15>;
>   			read-only;
>   		};
>   
>   		factory: partition@4600000 {
>   			label = "factory";
>   			reg = <0x4600000 0x200000>;
> +			scpart-id = <16>;
>   			read-only;
>   		};
>   
>   		partition@4800000 {
> +			label = "SC Private Data";
> +			reg = <0x4800000 0x200000>;
> +			scpart-id = <17>;
> +			read-only;
> +		};
> +
> +		partition@4a00000 {
> +			label = "POT";
> +			reg = <0x4a00000 0x200000>;
> +			scpart-id = <18>;
> +			read-only;
> +		};
> +
> +		partition@4c00000 {
> +			label = "Traffic Meter";
> +			reg = <0x4c00000 0x200000>;
> +			scpart-id = <19>;
> +			read-only;
> +		};
> +
> +		partition@4e00000 {
> +			label = "SC PID";
> +			reg = <0x4e00000 0x200000>;
> +			scpart-id = <20>;
> +			read-only;
> +		};
> +
> +		partition@5000000 {
> +			label = "SC Nvram";
> +			reg = <0x5000000 0x200000>;
> +			scpart-id = <21>;
> +			read-only;
> +		};
> +
> +		partition@5200000 {
> +			label = "Ralink Nvram";
> +			reg = <0x5200000 0x200000>;
> +			scpart-id = <22>;
> +			read-only;
> +		};
> +
> +		partition@5400000 {
> +			label = "reserved0";
> +			reg = <0x5400000 0x200000>;
> +			scpart-id = <23>;
> +			read-only;
> +		};
> +
> +		partition@5600000 {
>   			label = "reserved1";
> -			reg = <0x4800000 0x3800000>;
> +			reg = <0x5600000 0x200000>;
> +			scpart-id = <24>;
> +			read-only;
> +		};
> +
> +		partition@5800000 {
> +			label = "reserved2";
> +			reg = <0x5800000 0x200000>;
> +			scpart-id = <25>;
> +			read-only;
> +		};
> +
> +		partition@5a00000 {
> +			label = "reserved3";
> +			reg = <0x5a00000 0x200000>;
> +			scpart-id = <26>;
> +			read-only;
> +		};
> +
> +		partition@5c00000 {
> +			label = "reserved4";
> +			reg = <0x5c00000 0x200000>;
> +			scpart-id = <27>;
> +			read-only;
> +		};
> +
> +		partition@5e00000 {
> +			label = "reserved5";
> +			reg = <0x5e00000 0x2180000>;
> +			scpart-id = <28>;
>   			read-only;
>   		};
>   	};
> diff --git a/target/linux/ramips/dts/mt7621_netgear_sercomm_chj.dtsi b/target/linux/ramips/dts/mt7621_netgear_sercomm_chj.dtsi
> index b781edf6be..b593ee0cd6 100644
> --- a/target/linux/ramips/dts/mt7621_netgear_sercomm_chj.dtsi
> +++ b/target/linux/ramips/dts/mt7621_netgear_sercomm_chj.dtsi
> @@ -141,47 +141,208 @@
>   	status = "okay";
>   
>   	partitions {
> -		compatible = "fixed-partitions";
> +		compatible = "sercomm,sc-partitions", "fixed-partitions";
>   		#address-cells = <1>;
>   		#size-cells = <1>;
>   
>   		partition@0 {
>   			label = "u-boot";
>   			reg = <0x0 0x100000>;
> +			scpart-id = <0>;
>   			read-only;
>   		};
>   
>   		partition@100000 {
>   			label = "SC PART_MAP";
>   			reg = <0x100000 0x100000>;
> +			scpart-id = <1>;
>   			read-only;
>   		};
>   
>   		partition@200000 {
>   			label = "kernel";
>   			reg = <0x200000 0x400000>;
> +			scpart-id = <2>;
>   		};
>   
>   		partition@600000 {
>   			label = "ubi";
>   			reg = <0x600000 0x2800000>;
> +			scpart-id = <3>;
>   		};
>   
>   		partition@2e00000 {
> -			label = "reserved0";
> -			reg = <0x2e00000 0x1800000>;
> +			label = "English UI";
> +			reg = <0x2e00000 0x200000>;
> +			scpart-id = <4>;
> +			read-only;
> +		};
> +
> +		partition@3000000 {
> +			label = "ML1";
> +			reg = <0x3000000 0x200000>;
> +			scpart-id = <5>;
> +			read-only;
> +		};
> +
> +		partition@3200000 {
> +			label = "ML2";
> +			reg = <0x3200000 0x200000>;
> +			scpart-id = <6>;
> +			read-only;
> +		};
> +
> +		partition@3400000 {
> +			label = "ML3";
> +			reg = <0x3400000 0x200000>;
> +			scpart-id = <7>;
> +			read-only;
> +		};
> +
> +		partition@3600000 {
> +			label = "ML4";
> +			reg = <0x3600000 0x200000>;
> +			scpart-id = <8>;
> +			read-only;
> +		};
> +
> +		partition@3800000 {
> +			label = "ML5";
> +			reg = <0x3800000 0x200000>;
> +			scpart-id = <9>;
> +			read-only;
> +		};
> +
> +		partition@3a00000 {
> +			label = "ML6";
> +			reg = <0x3a00000 0x200000>;
> +			scpart-id = <10>;
> +			read-only;
> +		};
> +
> +		partition@3c00000 {
> +			label = "ML7";
> +			reg = <0x3c00000 0x200000>;
> +			scpart-id = <11>;
> +			read-only;
> +		};
> +
> +		partition@3e00000 {
> +			label = "ML8";
> +			reg = <0x3e00000 0x200000>;
> +			scpart-id = <12>;
> +			read-only;
> +		};
> +
> +		partition@4000000 {
> +			label = "ML9";
> +			reg = <0x4000000 0x200000>;
> +			scpart-id = <13>;
> +			read-only;
> +		};
> +
> +		partition@4200000 {
> +			label = "ML10";
> +			reg = <0x4200000 0x200000>;
> +			scpart-id = <14>;
> +			read-only;
> +		};
> +
> +		partition@4400000 {
> +			label = "ML11";
> +			reg = <0x4400000 0x200000>;
> +			scpart-id = <15>;
>   			read-only;
>   		};
>   
>   		factory: partition@4600000 {
>   			label = "factory";
>   			reg = <0x4600000 0x200000>;
> +			scpart-id = <16>;
>   			read-only;
>   		};
>   
>   		partition@4800000 {
> +			label = "SC Private Data";
> +			reg = <0x4800000 0x200000>;
> +			scpart-id = <17>;
> +			read-only;
> +		};
> +
> +		partition@4a00000 {
> +			label = "POT";
> +			reg = <0x4a00000 0x200000>;
> +			scpart-id = <18>;
> +			read-only;
> +		};
> +
> +		partition@4c00000 {
> +			label = "Traffic Meter";
> +			reg = <0x4c00000 0x200000>;
> +			scpart-id = <19>;
> +			read-only;
> +		};
> +
> +		partition@4e00000 {
> +			label = "SC PID";
> +			reg = <0x4e00000 0x200000>;
> +			scpart-id = <20>;
> +			read-only;
> +		};
> +
> +		partition@5000000 {
> +			label = "SC Nvram";
> +			reg = <0x5000000 0x200000>;
> +			scpart-id = <21>;
> +			read-only;
> +		};
> +
> +		partition@5200000 {
> +			label = "Ralink Nvram";
> +			reg = <0x5200000 0x200000>;
> +			scpart-id = <22>;
> +			read-only;
> +		};
> +
> +		partition@5400000 {
> +			label = "reserved0";
> +			reg = <0x5400000 0x200000>;
> +			scpart-id = <23>;
> +			read-only;
> +		};
> +
> +		partition@5600000 {
>   			label = "reserved1";
> -			reg = <0x4800000 0x3800000>;
> +			reg = <0x5600000 0x200000>;
> +			scpart-id = <24>;
> +			read-only;
> +		};
> +
> +		partition@5800000 {
> +			label = "reserved2";
> +			reg = <0x5800000 0x200000>;
> +			scpart-id = <25>;
> +			read-only;
> +		};
> +
> +		partition@5a00000 {
> +			label = "reserved3";
> +			reg = <0x5a00000 0x200000>;
> +			scpart-id = <26>;
> +			read-only;
> +		};
> +
> +		partition@5c00000 {
> +			label = "reserved4";
> +			reg = <0x5c00000 0x200000>;
> +			scpart-id = <27>;
> +			read-only;
> +		};
> +
> +		partition@5e00000 {
> +			label = "reserved5";
> +			reg = <0x5e00000 0x2180000>;
> +			scpart-id = <28>;
>   			read-only;
>   		};
>   	};
> diff --git a/target/linux/ramips/files/drivers/mtd/parsers/scpart.c b/target/linux/ramips/files/drivers/mtd/parsers/scpart.c
> new file mode 100644
> index 0000000000..36c3c0bb36
> --- /dev/null
> +++ b/target/linux/ramips/files/drivers/mtd/parsers/scpart.c
> @@ -0,0 +1,257 @@
> +/*
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
> + *
> + * 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");
> diff --git a/target/linux/ramips/mt7621/config-5.4 b/target/linux/ramips/mt7621/config-5.4
> index b2c3ad5ba8..712ac388f1 100644
> --- a/target/linux/ramips/mt7621/config-5.4
> +++ b/target/linux/ramips/mt7621/config-5.4
> @@ -203,6 +203,7 @@ CONFIG_MODULES_USE_ELF_REL=y
>   CONFIG_MT7621_WDT=y
>   # CONFIG_MTD_CFI_INTELEXT is not set
>   CONFIG_MTD_CMDLINE_PARTS=y
> +CONFIG_MTD_SERCOMM_PARTS=y
>   CONFIG_MTD_NAND_CORE=y
>   CONFIG_MTD_NAND_ECC_SW_HAMMING=y
>   CONFIG_MTD_NAND_MT7621=y
> diff --git a/target/linux/ramips/patches-5.4/303-mtd-scpart-parser.patch b/target/linux/ramips/patches-5.4/303-mtd-scpart-parser.patch
> new file mode 100644
> index 0000000000..f125d9c6af
> --- /dev/null
> +++ b/target/linux/ramips/patches-5.4/303-mtd-scpart-parser.patch
> @@ -0,0 +1,19 @@
> +--- a/drivers/mtd/parsers/Kconfig
> ++++ b/drivers/mtd/parsers/Kconfig
> +@@ -185,3 +185,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
> +@@ -11,3 +11,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
Adrian Schmutzler Jan. 22, 2021, 12:47 p.m. UTC | #2
> -----Original Message-----
> From: openwrt-devel [mailto:openwrt-devel-bounces@lists.openwrt.org]
> On Behalf Of Jost Menke
> Sent: Freitag, 22. Januar 2021 13:25
> To: Jan Hoffmann <jan@3e8.eu>; openwrt-devel@lists.openwrt.org
> Cc: drvlabo@gmail.com; mail@david-bauer.net
> Subject: Re: [RFT PATCH] ramips: use partition table on Netgear NAND
> devices
> 
> I successfully tested this patch on a Netgear R6800 with one bad eraseblock.
> Without the patch, the device is unable to correctly read the factory
> partition. It will generate random MAC addresses because the actual
> addresses are unreadable, and TX power will be limited to 6dBm, rendering it
> rather useless.
> With the patch, everything works as expected.

Thanks for the test.

If this patch is revived somehow, it would be a good idea to split it into two parts, first adding the parser changes and then separately implementing it for the devices (DTS).

Best

Adrian

> 
> 
> Regards
> 
> Jost
> 
> 
> Am 29.06.20 um 01:27 schrieb Jan Hoffmann:
> > There have been multiple reports of the partition table on these
> > devices being incorrect when there are bad blocks. This change should
> > fix this by parsing the partition table that is stored in the SC_PART_MAP
> partition.
> >
> > This is essentially the same code as proposed in the original pull
> > request for Netgear R6350 support by NOGUCHI Hiroshi
> <drvlabo@gmail.com>:
> > https://github.com/openwrt/openwrt/pull/1318
> >
> > At the time, it was rejected in favor of fixed partition offsets, as
> > it did not seem to work correctly. However, this was due the NAND
> > driver transparently shifting pages to hide bad blocks, which was
> > fixed in commit 527832e54bf3bc4d699a145ae66f34230246f0a9.
> >
> > Signed-off-by: Jan Hoffmann <jan@3e8.eu>
> > ---
> >
> > So far this patch has only been tested on my own WAC124, which doesn't
> > have any bad blocks (i.e. the partition table that is read from flash
> > matches the existing fixed offsets).
> >
> > It needs further testing to verify that it actually fixes the issue on
> > devices that have bad blocks, and does not cause any additional issues.
> >
> > This change applies to the following devices:
> > R6260, R6350, R6850, WAC124 (CHJ)
> > R6800, R6700-v2, R7200, Nighthawk AC2400 (BZV)
> >
> >
> >   .../dts/mt7621_netgear_sercomm_bzv.dtsi       | 169 +++++++++++-
> >   .../dts/mt7621_netgear_sercomm_chj.dtsi       | 169 +++++++++++-
> >   .../ramips/files/drivers/mtd/parsers/scpart.c | 257
> ++++++++++++++++++
> >   target/linux/ramips/mt7621/config-5.4         |   1 +
> >   .../patches-5.4/303-mtd-scpart-parser.patch   |  19 ++
> >   5 files changed, 607 insertions(+), 8 deletions(-)
> >   create mode 100644
> target/linux/ramips/files/drivers/mtd/parsers/scpart.c
> >   create mode 100644
> > target/linux/ramips/patches-5.4/303-mtd-scpart-parser.patch
> >
> > diff --git a/target/linux/ramips/dts/mt7621_netgear_sercomm_bzv.dtsi
> > b/target/linux/ramips/dts/mt7621_netgear_sercomm_bzv.dtsi
> > index d2e5502e48..2ab0b75862 100644
> > --- a/target/linux/ramips/dts/mt7621_netgear_sercomm_bzv.dtsi
> > +++ b/target/linux/ramips/dts/mt7621_netgear_sercomm_bzv.dtsi
> > @@ -113,47 +113,208 @@
> >   	status = "okay";
> >
> >   	partitions {
> > -		compatible = "fixed-partitions";
> > +		compatible = "sercomm,sc-partitions", "fixed-partitions";
> >   		#address-cells = <1>;
> >   		#size-cells = <1>;
> >
> >   		partition@0 {
> >   			label = "u-boot";
> >   			reg = <0x0 0x100000>;
> > +			scpart-id = <0>;
> >   			read-only;
> >   		};
> >
> >   		partition@100000 {
> >   			label = "SC PART_MAP";
> >   			reg = <0x100000 0x100000>;
> > +			scpart-id = <1>;
> >   			read-only;
> >   		};
> >
> >   		partition@200000 {
> >   			label = "kernel";
> >   			reg = <0x200000 0x400000>;
> > +			scpart-id = <2>;
> >   		};
> >
> >   		partition@600000 {
> >   			label = "ubi";
> >   			reg = <0x600000 0x2800000>;
> > +			scpart-id = <3>;
> >   		};
> >
> >   		partition@2e00000 {
> > -			label = "reserved0";
> > -			reg = <0x2e00000 0x1800000>;
> > +			label = "English UI";
> > +			reg = <0x2e00000 0x200000>;
> > +			scpart-id = <4>;
> > +			read-only;
> > +		};
> > +
> > +		partition@3000000 {
> > +			label = "ML1";
> > +			reg = <0x3000000 0x200000>;
> > +			scpart-id = <5>;
> > +			read-only;
> > +		};
> > +
> > +		partition@3200000 {
> > +			label = "ML2";
> > +			reg = <0x3200000 0x200000>;
> > +			scpart-id = <6>;
> > +			read-only;
> > +		};
> > +
> > +		partition@3400000 {
> > +			label = "ML3";
> > +			reg = <0x3400000 0x200000>;
> > +			scpart-id = <7>;
> > +			read-only;
> > +		};
> > +
> > +		partition@3600000 {
> > +			label = "ML4";
> > +			reg = <0x3600000 0x200000>;
> > +			scpart-id = <8>;
> > +			read-only;
> > +		};
> > +
> > +		partition@3800000 {
> > +			label = "ML5";
> > +			reg = <0x3800000 0x200000>;
> > +			scpart-id = <9>;
> > +			read-only;
> > +		};
> > +
> > +		partition@3a00000 {
> > +			label = "ML6";
> > +			reg = <0x3a00000 0x200000>;
> > +			scpart-id = <10>;
> > +			read-only;
> > +		};
> > +
> > +		partition@3c00000 {
> > +			label = "ML7";
> > +			reg = <0x3c00000 0x200000>;
> > +			scpart-id = <11>;
> > +			read-only;
> > +		};
> > +
> > +		partition@3e00000 {
> > +			label = "ML8";
> > +			reg = <0x3e00000 0x200000>;
> > +			scpart-id = <12>;
> > +			read-only;
> > +		};
> > +
> > +		partition@4000000 {
> > +			label = "ML9";
> > +			reg = <0x4000000 0x200000>;
> > +			scpart-id = <13>;
> > +			read-only;
> > +		};
> > +
> > +		partition@4200000 {
> > +			label = "ML10";
> > +			reg = <0x4200000 0x200000>;
> > +			scpart-id = <14>;
> > +			read-only;
> > +		};
> > +
> > +		partition@4400000 {
> > +			label = "ML11";
> > +			reg = <0x4400000 0x200000>;
> > +			scpart-id = <15>;
> >   			read-only;
> >   		};
> >
> >   		factory: partition@4600000 {
> >   			label = "factory";
> >   			reg = <0x4600000 0x200000>;
> > +			scpart-id = <16>;
> >   			read-only;
> >   		};
> >
> >   		partition@4800000 {
> > +			label = "SC Private Data";
> > +			reg = <0x4800000 0x200000>;
> > +			scpart-id = <17>;
> > +			read-only;
> > +		};
> > +
> > +		partition@4a00000 {
> > +			label = "POT";
> > +			reg = <0x4a00000 0x200000>;
> > +			scpart-id = <18>;
> > +			read-only;
> > +		};
> > +
> > +		partition@4c00000 {
> > +			label = "Traffic Meter";
> > +			reg = <0x4c00000 0x200000>;
> > +			scpart-id = <19>;
> > +			read-only;
> > +		};
> > +
> > +		partition@4e00000 {
> > +			label = "SC PID";
> > +			reg = <0x4e00000 0x200000>;
> > +			scpart-id = <20>;
> > +			read-only;
> > +		};
> > +
> > +		partition@5000000 {
> > +			label = "SC Nvram";
> > +			reg = <0x5000000 0x200000>;
> > +			scpart-id = <21>;
> > +			read-only;
> > +		};
> > +
> > +		partition@5200000 {
> > +			label = "Ralink Nvram";
> > +			reg = <0x5200000 0x200000>;
> > +			scpart-id = <22>;
> > +			read-only;
> > +		};
> > +
> > +		partition@5400000 {
> > +			label = "reserved0";
> > +			reg = <0x5400000 0x200000>;
> > +			scpart-id = <23>;
> > +			read-only;
> > +		};
> > +
> > +		partition@5600000 {
> >   			label = "reserved1";
> > -			reg = <0x4800000 0x3800000>;
> > +			reg = <0x5600000 0x200000>;
> > +			scpart-id = <24>;
> > +			read-only;
> > +		};
> > +
> > +		partition@5800000 {
> > +			label = "reserved2";
> > +			reg = <0x5800000 0x200000>;
> > +			scpart-id = <25>;
> > +			read-only;
> > +		};
> > +
> > +		partition@5a00000 {
> > +			label = "reserved3";
> > +			reg = <0x5a00000 0x200000>;
> > +			scpart-id = <26>;
> > +			read-only;
> > +		};
> > +
> > +		partition@5c00000 {
> > +			label = "reserved4";
> > +			reg = <0x5c00000 0x200000>;
> > +			scpart-id = <27>;
> > +			read-only;
> > +		};
> > +
> > +		partition@5e00000 {
> > +			label = "reserved5";
> > +			reg = <0x5e00000 0x2180000>;
> > +			scpart-id = <28>;
> >   			read-only;
> >   		};
> >   	};
> > diff --git a/target/linux/ramips/dts/mt7621_netgear_sercomm_chj.dtsi
> > b/target/linux/ramips/dts/mt7621_netgear_sercomm_chj.dtsi
> > index b781edf6be..b593ee0cd6 100644
> > --- a/target/linux/ramips/dts/mt7621_netgear_sercomm_chj.dtsi
> > +++ b/target/linux/ramips/dts/mt7621_netgear_sercomm_chj.dtsi
> > @@ -141,47 +141,208 @@
> >   	status = "okay";
> >
> >   	partitions {
> > -		compatible = "fixed-partitions";
> > +		compatible = "sercomm,sc-partitions", "fixed-partitions";
> >   		#address-cells = <1>;
> >   		#size-cells = <1>;
> >
> >   		partition@0 {
> >   			label = "u-boot";
> >   			reg = <0x0 0x100000>;
> > +			scpart-id = <0>;
> >   			read-only;
> >   		};
> >
> >   		partition@100000 {
> >   			label = "SC PART_MAP";
> >   			reg = <0x100000 0x100000>;
> > +			scpart-id = <1>;
> >   			read-only;
> >   		};
> >
> >   		partition@200000 {
> >   			label = "kernel";
> >   			reg = <0x200000 0x400000>;
> > +			scpart-id = <2>;
> >   		};
> >
> >   		partition@600000 {
> >   			label = "ubi";
> >   			reg = <0x600000 0x2800000>;
> > +			scpart-id = <3>;
> >   		};
> >
> >   		partition@2e00000 {
> > -			label = "reserved0";
> > -			reg = <0x2e00000 0x1800000>;
> > +			label = "English UI";
> > +			reg = <0x2e00000 0x200000>;
> > +			scpart-id = <4>;
> > +			read-only;
> > +		};
> > +
> > +		partition@3000000 {
> > +			label = "ML1";
> > +			reg = <0x3000000 0x200000>;
> > +			scpart-id = <5>;
> > +			read-only;
> > +		};
> > +
> > +		partition@3200000 {
> > +			label = "ML2";
> > +			reg = <0x3200000 0x200000>;
> > +			scpart-id = <6>;
> > +			read-only;
> > +		};
> > +
> > +		partition@3400000 {
> > +			label = "ML3";
> > +			reg = <0x3400000 0x200000>;
> > +			scpart-id = <7>;
> > +			read-only;
> > +		};
> > +
> > +		partition@3600000 {
> > +			label = "ML4";
> > +			reg = <0x3600000 0x200000>;
> > +			scpart-id = <8>;
> > +			read-only;
> > +		};
> > +
> > +		partition@3800000 {
> > +			label = "ML5";
> > +			reg = <0x3800000 0x200000>;
> > +			scpart-id = <9>;
> > +			read-only;
> > +		};
> > +
> > +		partition@3a00000 {
> > +			label = "ML6";
> > +			reg = <0x3a00000 0x200000>;
> > +			scpart-id = <10>;
> > +			read-only;
> > +		};
> > +
> > +		partition@3c00000 {
> > +			label = "ML7";
> > +			reg = <0x3c00000 0x200000>;
> > +			scpart-id = <11>;
> > +			read-only;
> > +		};
> > +
> > +		partition@3e00000 {
> > +			label = "ML8";
> > +			reg = <0x3e00000 0x200000>;
> > +			scpart-id = <12>;
> > +			read-only;
> > +		};
> > +
> > +		partition@4000000 {
> > +			label = "ML9";
> > +			reg = <0x4000000 0x200000>;
> > +			scpart-id = <13>;
> > +			read-only;
> > +		};
> > +
> > +		partition@4200000 {
> > +			label = "ML10";
> > +			reg = <0x4200000 0x200000>;
> > +			scpart-id = <14>;
> > +			read-only;
> > +		};
> > +
> > +		partition@4400000 {
> > +			label = "ML11";
> > +			reg = <0x4400000 0x200000>;
> > +			scpart-id = <15>;
> >   			read-only;
> >   		};
> >
> >   		factory: partition@4600000 {
> >   			label = "factory";
> >   			reg = <0x4600000 0x200000>;
> > +			scpart-id = <16>;
> >   			read-only;
> >   		};
> >
> >   		partition@4800000 {
> > +			label = "SC Private Data";
> > +			reg = <0x4800000 0x200000>;
> > +			scpart-id = <17>;
> > +			read-only;
> > +		};
> > +
> > +		partition@4a00000 {
> > +			label = "POT";
> > +			reg = <0x4a00000 0x200000>;
> > +			scpart-id = <18>;
> > +			read-only;
> > +		};
> > +
> > +		partition@4c00000 {
> > +			label = "Traffic Meter";
> > +			reg = <0x4c00000 0x200000>;
> > +			scpart-id = <19>;
> > +			read-only;
> > +		};
> > +
> > +		partition@4e00000 {
> > +			label = "SC PID";
> > +			reg = <0x4e00000 0x200000>;
> > +			scpart-id = <20>;
> > +			read-only;
> > +		};
> > +
> > +		partition@5000000 {
> > +			label = "SC Nvram";
> > +			reg = <0x5000000 0x200000>;
> > +			scpart-id = <21>;
> > +			read-only;
> > +		};
> > +
> > +		partition@5200000 {
> > +			label = "Ralink Nvram";
> > +			reg = <0x5200000 0x200000>;
> > +			scpart-id = <22>;
> > +			read-only;
> > +		};
> > +
> > +		partition@5400000 {
> > +			label = "reserved0";
> > +			reg = <0x5400000 0x200000>;
> > +			scpart-id = <23>;
> > +			read-only;
> > +		};
> > +
> > +		partition@5600000 {
> >   			label = "reserved1";
> > -			reg = <0x4800000 0x3800000>;
> > +			reg = <0x5600000 0x200000>;
> > +			scpart-id = <24>;
> > +			read-only;
> > +		};
> > +
> > +		partition@5800000 {
> > +			label = "reserved2";
> > +			reg = <0x5800000 0x200000>;
> > +			scpart-id = <25>;
> > +			read-only;
> > +		};
> > +
> > +		partition@5a00000 {
> > +			label = "reserved3";
> > +			reg = <0x5a00000 0x200000>;
> > +			scpart-id = <26>;
> > +			read-only;
> > +		};
> > +
> > +		partition@5c00000 {
> > +			label = "reserved4";
> > +			reg = <0x5c00000 0x200000>;
> > +			scpart-id = <27>;
> > +			read-only;
> > +		};
> > +
> > +		partition@5e00000 {
> > +			label = "reserved5";
> > +			reg = <0x5e00000 0x2180000>;
> > +			scpart-id = <28>;
> >   			read-only;
> >   		};
> >   	};
> > diff --git a/target/linux/ramips/files/drivers/mtd/parsers/scpart.c
> > b/target/linux/ramips/files/drivers/mtd/parsers/scpart.c
> > new file mode 100644
> > index 0000000000..36c3c0bb36
> > --- /dev/null
> > +++ b/target/linux/ramips/files/drivers/mtd/parsers/scpart.c
> > @@ -0,0 +1,257 @@
> > +/*
> > + *
> > + * This program is free software; you can redistribute it and/or
> > +modify
> > + * it under the terms of the GNU General Public License as published
> > +by
> > + * the Free Software Foundation; either version 2 of the License, or
> > + * (at your option) any later version.
> > + *
> > + * This program is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> > + * GNU General Public License for more details.
> > + *
> > + * You should have received a copy of the GNU General Public License
> > + * along with this program; if not, write to the Free Software
> > + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
> > +02110-1301  USA
> > + *
> > + * 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");
> > diff --git a/target/linux/ramips/mt7621/config-5.4
> > b/target/linux/ramips/mt7621/config-5.4
> > index b2c3ad5ba8..712ac388f1 100644
> > --- a/target/linux/ramips/mt7621/config-5.4
> > +++ b/target/linux/ramips/mt7621/config-5.4
> > @@ -203,6 +203,7 @@ CONFIG_MODULES_USE_ELF_REL=y
> >   CONFIG_MT7621_WDT=y
> >   # CONFIG_MTD_CFI_INTELEXT is not set
> >   CONFIG_MTD_CMDLINE_PARTS=y
> > +CONFIG_MTD_SERCOMM_PARTS=y
> >   CONFIG_MTD_NAND_CORE=y
> >   CONFIG_MTD_NAND_ECC_SW_HAMMING=y
> >   CONFIG_MTD_NAND_MT7621=y
> > diff --git
> > a/target/linux/ramips/patches-5.4/303-mtd-scpart-parser.patch
> > b/target/linux/ramips/patches-5.4/303-mtd-scpart-parser.patch
> > new file mode 100644
> > index 0000000000..f125d9c6af
> > --- /dev/null
> > +++ b/target/linux/ramips/patches-5.4/303-mtd-scpart-parser.patch
> > @@ -0,0 +1,19 @@
> > +--- a/drivers/mtd/parsers/Kconfig
> > ++++ b/drivers/mtd/parsers/Kconfig
> > +@@ -185,3 +185,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
> > +@@ -11,3 +11,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
> 
> 
> _______________________________________________
> openwrt-devel mailing list
> openwrt-devel@lists.openwrt.org
> https://lists.openwrt.org/mailman/listinfo/openwrt-devel
diff mbox series

Patch

diff --git a/target/linux/ramips/dts/mt7621_netgear_sercomm_bzv.dtsi b/target/linux/ramips/dts/mt7621_netgear_sercomm_bzv.dtsi
index d2e5502e48..2ab0b75862 100644
--- a/target/linux/ramips/dts/mt7621_netgear_sercomm_bzv.dtsi
+++ b/target/linux/ramips/dts/mt7621_netgear_sercomm_bzv.dtsi
@@ -113,47 +113,208 @@ 
 	status = "okay";
 
 	partitions {
-		compatible = "fixed-partitions";
+		compatible = "sercomm,sc-partitions", "fixed-partitions";
 		#address-cells = <1>;
 		#size-cells = <1>;
 
 		partition@0 {
 			label = "u-boot";
 			reg = <0x0 0x100000>;
+			scpart-id = <0>;
 			read-only;
 		};
 
 		partition@100000 {
 			label = "SC PART_MAP";
 			reg = <0x100000 0x100000>;
+			scpart-id = <1>;
 			read-only;
 		};
 
 		partition@200000 {
 			label = "kernel";
 			reg = <0x200000 0x400000>;
+			scpart-id = <2>;
 		};
 
 		partition@600000 {
 			label = "ubi";
 			reg = <0x600000 0x2800000>;
+			scpart-id = <3>;
 		};
 
 		partition@2e00000 {
-			label = "reserved0";
-			reg = <0x2e00000 0x1800000>;
+			label = "English UI";
+			reg = <0x2e00000 0x200000>;
+			scpart-id = <4>;
+			read-only;
+		};
+
+		partition@3000000 {
+			label = "ML1";
+			reg = <0x3000000 0x200000>;
+			scpart-id = <5>;
+			read-only;
+		};
+
+		partition@3200000 {
+			label = "ML2";
+			reg = <0x3200000 0x200000>;
+			scpart-id = <6>;
+			read-only;
+		};
+
+		partition@3400000 {
+			label = "ML3";
+			reg = <0x3400000 0x200000>;
+			scpart-id = <7>;
+			read-only;
+		};
+
+		partition@3600000 {
+			label = "ML4";
+			reg = <0x3600000 0x200000>;
+			scpart-id = <8>;
+			read-only;
+		};
+
+		partition@3800000 {
+			label = "ML5";
+			reg = <0x3800000 0x200000>;
+			scpart-id = <9>;
+			read-only;
+		};
+
+		partition@3a00000 {
+			label = "ML6";
+			reg = <0x3a00000 0x200000>;
+			scpart-id = <10>;
+			read-only;
+		};
+
+		partition@3c00000 {
+			label = "ML7";
+			reg = <0x3c00000 0x200000>;
+			scpart-id = <11>;
+			read-only;
+		};
+
+		partition@3e00000 {
+			label = "ML8";
+			reg = <0x3e00000 0x200000>;
+			scpart-id = <12>;
+			read-only;
+		};
+
+		partition@4000000 {
+			label = "ML9";
+			reg = <0x4000000 0x200000>;
+			scpart-id = <13>;
+			read-only;
+		};
+
+		partition@4200000 {
+			label = "ML10";
+			reg = <0x4200000 0x200000>;
+			scpart-id = <14>;
+			read-only;
+		};
+
+		partition@4400000 {
+			label = "ML11";
+			reg = <0x4400000 0x200000>;
+			scpart-id = <15>;
 			read-only;
 		};
 
 		factory: partition@4600000 {
 			label = "factory";
 			reg = <0x4600000 0x200000>;
+			scpart-id = <16>;
 			read-only;
 		};
 
 		partition@4800000 {
+			label = "SC Private Data";
+			reg = <0x4800000 0x200000>;
+			scpart-id = <17>;
+			read-only;
+		};
+
+		partition@4a00000 {
+			label = "POT";
+			reg = <0x4a00000 0x200000>;
+			scpart-id = <18>;
+			read-only;
+		};
+
+		partition@4c00000 {
+			label = "Traffic Meter";
+			reg = <0x4c00000 0x200000>;
+			scpart-id = <19>;
+			read-only;
+		};
+
+		partition@4e00000 {
+			label = "SC PID";
+			reg = <0x4e00000 0x200000>;
+			scpart-id = <20>;
+			read-only;
+		};
+
+		partition@5000000 {
+			label = "SC Nvram";
+			reg = <0x5000000 0x200000>;
+			scpart-id = <21>;
+			read-only;
+		};
+
+		partition@5200000 {
+			label = "Ralink Nvram";
+			reg = <0x5200000 0x200000>;
+			scpart-id = <22>;
+			read-only;
+		};
+
+		partition@5400000 {
+			label = "reserved0";
+			reg = <0x5400000 0x200000>;
+			scpart-id = <23>;
+			read-only;
+		};
+
+		partition@5600000 {
 			label = "reserved1";
-			reg = <0x4800000 0x3800000>;
+			reg = <0x5600000 0x200000>;
+			scpart-id = <24>;
+			read-only;
+		};
+
+		partition@5800000 {
+			label = "reserved2";
+			reg = <0x5800000 0x200000>;
+			scpart-id = <25>;
+			read-only;
+		};
+
+		partition@5a00000 {
+			label = "reserved3";
+			reg = <0x5a00000 0x200000>;
+			scpart-id = <26>;
+			read-only;
+		};
+
+		partition@5c00000 {
+			label = "reserved4";
+			reg = <0x5c00000 0x200000>;
+			scpart-id = <27>;
+			read-only;
+		};
+
+		partition@5e00000 {
+			label = "reserved5";
+			reg = <0x5e00000 0x2180000>;
+			scpart-id = <28>;
 			read-only;
 		};
 	};
diff --git a/target/linux/ramips/dts/mt7621_netgear_sercomm_chj.dtsi b/target/linux/ramips/dts/mt7621_netgear_sercomm_chj.dtsi
index b781edf6be..b593ee0cd6 100644
--- a/target/linux/ramips/dts/mt7621_netgear_sercomm_chj.dtsi
+++ b/target/linux/ramips/dts/mt7621_netgear_sercomm_chj.dtsi
@@ -141,47 +141,208 @@ 
 	status = "okay";
 
 	partitions {
-		compatible = "fixed-partitions";
+		compatible = "sercomm,sc-partitions", "fixed-partitions";
 		#address-cells = <1>;
 		#size-cells = <1>;
 
 		partition@0 {
 			label = "u-boot";
 			reg = <0x0 0x100000>;
+			scpart-id = <0>;
 			read-only;
 		};
 
 		partition@100000 {
 			label = "SC PART_MAP";
 			reg = <0x100000 0x100000>;
+			scpart-id = <1>;
 			read-only;
 		};
 
 		partition@200000 {
 			label = "kernel";
 			reg = <0x200000 0x400000>;
+			scpart-id = <2>;
 		};
 
 		partition@600000 {
 			label = "ubi";
 			reg = <0x600000 0x2800000>;
+			scpart-id = <3>;
 		};
 
 		partition@2e00000 {
-			label = "reserved0";
-			reg = <0x2e00000 0x1800000>;
+			label = "English UI";
+			reg = <0x2e00000 0x200000>;
+			scpart-id = <4>;
+			read-only;
+		};
+
+		partition@3000000 {
+			label = "ML1";
+			reg = <0x3000000 0x200000>;
+			scpart-id = <5>;
+			read-only;
+		};
+
+		partition@3200000 {
+			label = "ML2";
+			reg = <0x3200000 0x200000>;
+			scpart-id = <6>;
+			read-only;
+		};
+
+		partition@3400000 {
+			label = "ML3";
+			reg = <0x3400000 0x200000>;
+			scpart-id = <7>;
+			read-only;
+		};
+
+		partition@3600000 {
+			label = "ML4";
+			reg = <0x3600000 0x200000>;
+			scpart-id = <8>;
+			read-only;
+		};
+
+		partition@3800000 {
+			label = "ML5";
+			reg = <0x3800000 0x200000>;
+			scpart-id = <9>;
+			read-only;
+		};
+
+		partition@3a00000 {
+			label = "ML6";
+			reg = <0x3a00000 0x200000>;
+			scpart-id = <10>;
+			read-only;
+		};
+
+		partition@3c00000 {
+			label = "ML7";
+			reg = <0x3c00000 0x200000>;
+			scpart-id = <11>;
+			read-only;
+		};
+
+		partition@3e00000 {
+			label = "ML8";
+			reg = <0x3e00000 0x200000>;
+			scpart-id = <12>;
+			read-only;
+		};
+
+		partition@4000000 {
+			label = "ML9";
+			reg = <0x4000000 0x200000>;
+			scpart-id = <13>;
+			read-only;
+		};
+
+		partition@4200000 {
+			label = "ML10";
+			reg = <0x4200000 0x200000>;
+			scpart-id = <14>;
+			read-only;
+		};
+
+		partition@4400000 {
+			label = "ML11";
+			reg = <0x4400000 0x200000>;
+			scpart-id = <15>;
 			read-only;
 		};
 
 		factory: partition@4600000 {
 			label = "factory";
 			reg = <0x4600000 0x200000>;
+			scpart-id = <16>;
 			read-only;
 		};
 
 		partition@4800000 {
+			label = "SC Private Data";
+			reg = <0x4800000 0x200000>;
+			scpart-id = <17>;
+			read-only;
+		};
+
+		partition@4a00000 {
+			label = "POT";
+			reg = <0x4a00000 0x200000>;
+			scpart-id = <18>;
+			read-only;
+		};
+
+		partition@4c00000 {
+			label = "Traffic Meter";
+			reg = <0x4c00000 0x200000>;
+			scpart-id = <19>;
+			read-only;
+		};
+
+		partition@4e00000 {
+			label = "SC PID";
+			reg = <0x4e00000 0x200000>;
+			scpart-id = <20>;
+			read-only;
+		};
+
+		partition@5000000 {
+			label = "SC Nvram";
+			reg = <0x5000000 0x200000>;
+			scpart-id = <21>;
+			read-only;
+		};
+
+		partition@5200000 {
+			label = "Ralink Nvram";
+			reg = <0x5200000 0x200000>;
+			scpart-id = <22>;
+			read-only;
+		};
+
+		partition@5400000 {
+			label = "reserved0";
+			reg = <0x5400000 0x200000>;
+			scpart-id = <23>;
+			read-only;
+		};
+
+		partition@5600000 {
 			label = "reserved1";
-			reg = <0x4800000 0x3800000>;
+			reg = <0x5600000 0x200000>;
+			scpart-id = <24>;
+			read-only;
+		};
+
+		partition@5800000 {
+			label = "reserved2";
+			reg = <0x5800000 0x200000>;
+			scpart-id = <25>;
+			read-only;
+		};
+
+		partition@5a00000 {
+			label = "reserved3";
+			reg = <0x5a00000 0x200000>;
+			scpart-id = <26>;
+			read-only;
+		};
+
+		partition@5c00000 {
+			label = "reserved4";
+			reg = <0x5c00000 0x200000>;
+			scpart-id = <27>;
+			read-only;
+		};
+
+		partition@5e00000 {
+			label = "reserved5";
+			reg = <0x5e00000 0x2180000>;
+			scpart-id = <28>;
 			read-only;
 		};
 	};
diff --git a/target/linux/ramips/files/drivers/mtd/parsers/scpart.c b/target/linux/ramips/files/drivers/mtd/parsers/scpart.c
new file mode 100644
index 0000000000..36c3c0bb36
--- /dev/null
+++ b/target/linux/ramips/files/drivers/mtd/parsers/scpart.c
@@ -0,0 +1,257 @@ 
+/*
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * 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");
diff --git a/target/linux/ramips/mt7621/config-5.4 b/target/linux/ramips/mt7621/config-5.4
index b2c3ad5ba8..712ac388f1 100644
--- a/target/linux/ramips/mt7621/config-5.4
+++ b/target/linux/ramips/mt7621/config-5.4
@@ -203,6 +203,7 @@  CONFIG_MODULES_USE_ELF_REL=y
 CONFIG_MT7621_WDT=y
 # CONFIG_MTD_CFI_INTELEXT is not set
 CONFIG_MTD_CMDLINE_PARTS=y
+CONFIG_MTD_SERCOMM_PARTS=y
 CONFIG_MTD_NAND_CORE=y
 CONFIG_MTD_NAND_ECC_SW_HAMMING=y
 CONFIG_MTD_NAND_MT7621=y
diff --git a/target/linux/ramips/patches-5.4/303-mtd-scpart-parser.patch b/target/linux/ramips/patches-5.4/303-mtd-scpart-parser.patch
new file mode 100644
index 0000000000..f125d9c6af
--- /dev/null
+++ b/target/linux/ramips/patches-5.4/303-mtd-scpart-parser.patch
@@ -0,0 +1,19 @@ 
+--- a/drivers/mtd/parsers/Kconfig
++++ b/drivers/mtd/parsers/Kconfig
+@@ -185,3 +185,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
+@@ -11,3 +11,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