* updated SNAND/SNFI driver brings support for MT7981 * add support for MediaTek NAND Memory bad Block Management (NMBM) (not used for any boards atm, but could be useful in future) * wire up NMBM support for MT7622, MT7629, MT7981 and MT7986 * replace some local patches with updated version from SDK * bring some legacy precompiler symbols which haven't been converted into Kconfig symbols in U-Boot 2022.07, remove when bumbping to U-Boot 2022.10: 100-28-include-configs-mt7986-h-from-SDK.patch Source: https://github.com/mtk-openwrt/u-boot Signed-off-by: Daniel Golle <daniel@makrotopia.org>
		
			
				
	
	
		
			371 lines
		
	
	
		
			8.7 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			371 lines
		
	
	
		
			8.7 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
From 0af8d0aac77f4df4bc7dadbcdea5d9a16f5f3e45 Mon Sep 17 00:00:00 2001
 | 
						|
From: Weijie Gao <weijie.gao@mediatek.com>
 | 
						|
Date: Mon, 25 Jul 2022 10:44:57 +0800
 | 
						|
Subject: [PATCH 43/71] cmd: add nmbm command
 | 
						|
 | 
						|
Add nmbm command for debugging, data operations and image-booting support
 | 
						|
 | 
						|
Signed-off-by: Weijie Gao <weijie.gao@mediatek.com>
 | 
						|
---
 | 
						|
 cmd/Kconfig  |   6 +
 | 
						|
 cmd/Makefile |   1 +
 | 
						|
 cmd/nmbm.c   | 327 +++++++++++++++++++++++++++++++++++++++++++++++++++
 | 
						|
 3 files changed, 334 insertions(+)
 | 
						|
 create mode 100644 cmd/nmbm.c
 | 
						|
 | 
						|
--- a/cmd/Kconfig
 | 
						|
+++ b/cmd/Kconfig
 | 
						|
@@ -1260,6 +1260,12 @@ config CMD_NAND_TORTURE
 | 
						|
 
 | 
						|
 endif # CMD_NAND
 | 
						|
 
 | 
						|
+config CMD_NMBM
 | 
						|
+	depends on NMBM_MTD
 | 
						|
+	bool "nmbm"
 | 
						|
+	help
 | 
						|
+	  NAND mapping block management (NMBM) utility
 | 
						|
+
 | 
						|
 config CMD_NVME
 | 
						|
 	bool "nvme"
 | 
						|
 	depends on NVME
 | 
						|
--- a/cmd/Makefile
 | 
						|
+++ b/cmd/Makefile
 | 
						|
@@ -114,6 +114,7 @@ obj-y += legacy-mtd-utils.o
 | 
						|
 endif
 | 
						|
 obj-$(CONFIG_CMD_MUX) += mux.o
 | 
						|
 obj-$(CONFIG_CMD_NAND) += nand.o
 | 
						|
+obj-$(CONFIG_CMD_NMBM) += nmbm.o
 | 
						|
 obj-$(CONFIG_CMD_NET) += net.o
 | 
						|
 obj-$(CONFIG_CMD_NVEDIT_EFI) += nvedit_efi.o
 | 
						|
 obj-$(CONFIG_CMD_ONENAND) += onenand.o
 | 
						|
--- /dev/null
 | 
						|
+++ b/cmd/nmbm.c
 | 
						|
@@ -0,0 +1,327 @@
 | 
						|
+// SPDX-License-Identifier: GPL-2.0
 | 
						|
+/*
 | 
						|
+ * Copyright (C) 2020 MediaTek Inc. All Rights Reserved.
 | 
						|
+ *
 | 
						|
+ * Author: Weijie Gao <weijie.gao@mediatek.com>
 | 
						|
+ */
 | 
						|
+
 | 
						|
+#include <command.h>
 | 
						|
+#include <image.h>
 | 
						|
+#include <stdbool.h>
 | 
						|
+#include <linux/types.h>
 | 
						|
+#include <linux/mtd/mtd.h>
 | 
						|
+#include <jffs2/load_kernel.h>
 | 
						|
+
 | 
						|
+#include <nmbm/nmbm-mtd.h>
 | 
						|
+
 | 
						|
+static int nmbm_parse_offset_size(struct mtd_info *mtd, char *off_str,
 | 
						|
+				  char *size_str, uint64_t *off,
 | 
						|
+				  uint64_t *size)
 | 
						|
+{
 | 
						|
+	char *end;
 | 
						|
+
 | 
						|
+	*off = simple_strtoull(off_str, &end, 16);
 | 
						|
+	if (end == off_str) {
 | 
						|
+		printf("Error: offset '%s' is invalid\n", off_str);
 | 
						|
+		return -EINVAL;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	if (*off >= mtd->size) {
 | 
						|
+		printf("Error: offset '0x%llx' is beyond the end of device\n",
 | 
						|
+		       *off);
 | 
						|
+		return -EINVAL;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	*size = simple_strtoull(size_str, &end, 16);
 | 
						|
+	if (end == off_str) {
 | 
						|
+		printf("Error: size '%s' is invalid\n", off_str);
 | 
						|
+		return -EINVAL;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	if (*off + *size > mtd->size) {
 | 
						|
+		printf("Error: size '0x%llx' is too large\n", *size);
 | 
						|
+		return -EINVAL;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int do_nmbm_erase(struct mtd_info *mtd, uint64_t offset, uint64_t size)
 | 
						|
+{
 | 
						|
+	struct erase_info ei;
 | 
						|
+	int ret;
 | 
						|
+
 | 
						|
+	memset(&ei, 0, sizeof(ei));
 | 
						|
+
 | 
						|
+	ei.mtd = mtd;
 | 
						|
+	ei.addr = offset;
 | 
						|
+	ei.len = size;
 | 
						|
+
 | 
						|
+	printf("Erasing from 0x%llx, size 0x%llx ...\n", offset, size);
 | 
						|
+
 | 
						|
+	ret = mtd_erase(mtd, &ei);
 | 
						|
+
 | 
						|
+	if (!ret) {
 | 
						|
+		printf("Succeeded\n");
 | 
						|
+		return CMD_RET_SUCCESS;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	printf("Failed at 0x%llx\n", ei.fail_addr);
 | 
						|
+
 | 
						|
+	return CMD_RET_FAILURE;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int do_nmbm_rw(int read, struct mtd_info *mtd, uintptr_t addr,
 | 
						|
+	       uint64_t offset, size_t size)
 | 
						|
+{
 | 
						|
+	size_t retlen;
 | 
						|
+	int ret;
 | 
						|
+
 | 
						|
+	printf("%s 0x%llx, size 0x%zx\n", read ? "Reading from" : "Writing to",
 | 
						|
+		offset, size);
 | 
						|
+
 | 
						|
+	if (read)
 | 
						|
+		ret = mtd_read(mtd, offset, size, &retlen, (void *)addr);
 | 
						|
+	else
 | 
						|
+		ret = mtd_write(mtd, offset, size, &retlen, (void *)addr);
 | 
						|
+
 | 
						|
+	if (!ret) {
 | 
						|
+		printf("Succeeded\n");
 | 
						|
+		return CMD_RET_SUCCESS;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	printf("Failed at 0x%llx\n", offset + retlen);
 | 
						|
+
 | 
						|
+	return CMD_RET_FAILURE;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int do_nmbm_mtd_boot(struct cmd_tbl *cmdtp, struct mtd_info *mtd,
 | 
						|
+			    int argc, char *const argv[])
 | 
						|
+{
 | 
						|
+	bool print_image_contents = true;
 | 
						|
+	uintptr_t loadaddr = image_load_addr;
 | 
						|
+	char *end, *image_name;
 | 
						|
+	const char *ep;
 | 
						|
+	size_t retlen;
 | 
						|
+	uint32_t size;
 | 
						|
+	uint64_t off;
 | 
						|
+	int ret;
 | 
						|
+
 | 
						|
+#if defined(CONFIG_CMD_MTDPARTS)
 | 
						|
+	struct mtd_device *partdev;
 | 
						|
+	struct mtd_info *partmtd;
 | 
						|
+	struct part_info *part;
 | 
						|
+	u8 pnum;
 | 
						|
+#endif
 | 
						|
+
 | 
						|
+	ep = env_get("autostart");
 | 
						|
+
 | 
						|
+	if (ep && !strcmp(ep, "yes"))
 | 
						|
+		print_image_contents = false;
 | 
						|
+
 | 
						|
+	if (argc == 2) {
 | 
						|
+		loadaddr = simple_strtoul(argv[0], &end, 0);
 | 
						|
+		if (*end || end == argv[0]) {
 | 
						|
+			printf("'%s' is not a valid address\n", argv[0]);
 | 
						|
+			return CMD_RET_FAILURE;
 | 
						|
+		}
 | 
						|
+
 | 
						|
+		argc--;
 | 
						|
+		argv++;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	off = simple_strtoull(argv[0], &end, 0);
 | 
						|
+	if (*end || end == argv[0]) {
 | 
						|
+#if defined(CONFIG_CMD_MTDPARTS)
 | 
						|
+		ret = mtdparts_init();
 | 
						|
+		if (ret)
 | 
						|
+			return CMD_RET_FAILURE;
 | 
						|
+
 | 
						|
+		ret = find_dev_and_part(argv[0], &partdev, &pnum, &part);
 | 
						|
+		if (ret)
 | 
						|
+			return CMD_RET_FAILURE;
 | 
						|
+
 | 
						|
+		if (partdev->id->type != MTD_DEV_TYPE_NMBM) {
 | 
						|
+			printf("'%s' is not a NMBM device partition\n",
 | 
						|
+				argv[0]);
 | 
						|
+			return CMD_RET_FAILURE;
 | 
						|
+		}
 | 
						|
+
 | 
						|
+		partmtd = nmbm_mtd_get_upper_by_index(partdev->id->num);
 | 
						|
+
 | 
						|
+		if (partmtd != mtd) {
 | 
						|
+			printf("'%s' does not belong to this device\n",
 | 
						|
+				argv[0]);
 | 
						|
+			return CMD_RET_FAILURE;
 | 
						|
+		}
 | 
						|
+
 | 
						|
+		off = part->offset;
 | 
						|
+#else
 | 
						|
+		printf("'%s' is not a valid offset\n", argv[0]);
 | 
						|
+		return CMD_RET_FAILURE;
 | 
						|
+#endif
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	ret = mtd_read(mtd, off, sizeof(image_header_t), &retlen,
 | 
						|
+		       (void *)loadaddr);
 | 
						|
+	if (ret || retlen != sizeof(image_header_t)) {
 | 
						|
+		printf("Failed to read NMBM at offset 0x%08llx\n", off);
 | 
						|
+		return CMD_RET_FAILURE;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	switch (genimg_get_format((void *)loadaddr)) {
 | 
						|
+#if defined(CONFIG_LEGACY_IMAGE_FORMAT)
 | 
						|
+	case IMAGE_FORMAT_LEGACY:
 | 
						|
+		size = image_get_image_size((image_header_t *)loadaddr);
 | 
						|
+		image_name = "legacy";
 | 
						|
+		break;
 | 
						|
+#endif
 | 
						|
+#if defined(CONFIG_FIT)
 | 
						|
+	case IMAGE_FORMAT_FIT:
 | 
						|
+		size = fit_get_size((const void *)loadaddr);
 | 
						|
+		image_name = "FIT";
 | 
						|
+		break;
 | 
						|
+#endif
 | 
						|
+	default:
 | 
						|
+		printf("Error: no Image found at offset 0x%08llx\n", off);
 | 
						|
+		return CMD_RET_FAILURE;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	printf("Loading %s image at offset 0x%llx to memory 0x%08lx, size 0x%x ...\n",
 | 
						|
+	       image_name, off, loadaddr, size);
 | 
						|
+
 | 
						|
+	ret = mtd_read(mtd, off, size, &retlen, (void *)loadaddr);
 | 
						|
+	if (ret || retlen != size) {
 | 
						|
+		printf("Error: Failed to load image at offset 0x%08llx\n",
 | 
						|
+		       off + retlen);
 | 
						|
+		return CMD_RET_FAILURE;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	switch (genimg_get_format((void *)loadaddr)) {
 | 
						|
+#if defined(CONFIG_LEGACY_IMAGE_FORMAT)
 | 
						|
+	case IMAGE_FORMAT_LEGACY:
 | 
						|
+		if (print_image_contents)
 | 
						|
+			image_print_contents((void *)loadaddr);
 | 
						|
+		break;
 | 
						|
+#endif
 | 
						|
+#if defined(CONFIG_FIT)
 | 
						|
+	case IMAGE_FORMAT_FIT:
 | 
						|
+		if (print_image_contents)
 | 
						|
+			fit_print_contents((void *)loadaddr);
 | 
						|
+		break;
 | 
						|
+#endif
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	image_load_addr = loadaddr;
 | 
						|
+
 | 
						|
+	return bootm_maybe_autostart(cmdtp, "nmbm");
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int do_nmbm(struct cmd_tbl *cmdtp, int flag, int argc,
 | 
						|
+		   char *const argv[])
 | 
						|
+{
 | 
						|
+	struct mtd_info *mtd;
 | 
						|
+	uint64_t offset, size;
 | 
						|
+	char *end;
 | 
						|
+	uintptr_t addr;
 | 
						|
+	int ret, all = 0;
 | 
						|
+
 | 
						|
+	if (argc == 1)
 | 
						|
+		return CMD_RET_USAGE;
 | 
						|
+
 | 
						|
+	if (!strcmp(argv[1], "list")) {
 | 
						|
+		nmbm_mtd_list_devices();
 | 
						|
+		return CMD_RET_SUCCESS;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	if (argc < 3)
 | 
						|
+		return CMD_RET_USAGE;
 | 
						|
+
 | 
						|
+	if (!strcmp(argv[2], "info"))
 | 
						|
+		return !!nmbm_mtd_print_info(argv[1]);
 | 
						|
+
 | 
						|
+	if (!strcmp(argv[2], "state"))
 | 
						|
+		return !!nmbm_mtd_print_states(argv[1]);
 | 
						|
+
 | 
						|
+	if (!strcmp(argv[2], "bad"))
 | 
						|
+		return !!nmbm_mtd_print_bad_blocks(argv[1]);
 | 
						|
+
 | 
						|
+	if (!strcmp(argv[2], "mapping")) {
 | 
						|
+		if (argc >= 4) {
 | 
						|
+			if (!strcmp(argv[3], "all"))
 | 
						|
+				all = 1;
 | 
						|
+		}
 | 
						|
+
 | 
						|
+		return nmbm_mtd_print_mappings(argv[1], all);
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	if (argc < 4)
 | 
						|
+		return CMD_RET_USAGE;
 | 
						|
+
 | 
						|
+	mtd = get_mtd_device_nm(argv[1]);
 | 
						|
+	if (IS_ERR(mtd)) {
 | 
						|
+		printf("Error: NMBM device '%s' not found\n", argv[1]);
 | 
						|
+		return CMD_RET_FAILURE;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	if (mtd->type != MTD_DEV_TYPE_NMBM) {
 | 
						|
+		printf("Error: '%s' is not a NMBM device\n", argv[1]);
 | 
						|
+		return CMD_RET_FAILURE;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	if (!strcmp(argv[2], "boot"))
 | 
						|
+		return do_nmbm_mtd_boot(cmdtp, mtd, argc - 3, argv + 3);
 | 
						|
+
 | 
						|
+	if (argc < 5)
 | 
						|
+		return CMD_RET_USAGE;
 | 
						|
+
 | 
						|
+	if (!strcmp(argv[2], "erase")) {
 | 
						|
+		ret = nmbm_parse_offset_size(mtd, argv[3], argv[4], &offset,
 | 
						|
+					     &size);
 | 
						|
+		if (ret)
 | 
						|
+			return CMD_RET_FAILURE;
 | 
						|
+
 | 
						|
+		return do_nmbm_erase(mtd, offset, size);
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	if (argc < 6)
 | 
						|
+		return CMD_RET_USAGE;
 | 
						|
+
 | 
						|
+	ret = nmbm_parse_offset_size(mtd, argv[4], argv[5], &offset, &size);
 | 
						|
+	if (ret)
 | 
						|
+		return CMD_RET_FAILURE;
 | 
						|
+
 | 
						|
+	if (size > SIZE_MAX) {
 | 
						|
+		printf("Error: size 0x%llx is too large\n", size);
 | 
						|
+		return -EINVAL;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	addr = simple_strtoul(argv[3], &end, 16);
 | 
						|
+	if (end == argv[3]) {
 | 
						|
+		printf("Error: addr '%s' is invalid\n", argv[3]);
 | 
						|
+		return -EINVAL;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	if (!strcmp(argv[2], "read"))
 | 
						|
+		return do_nmbm_rw(1, mtd, addr, offset, (size_t)size);
 | 
						|
+
 | 
						|
+	if (!strcmp(argv[2], "write"))
 | 
						|
+		return do_nmbm_rw(0, mtd, addr, offset, (size_t)size);
 | 
						|
+
 | 
						|
+	return CMD_RET_USAGE;
 | 
						|
+}
 | 
						|
+
 | 
						|
+U_BOOT_CMD(
 | 
						|
+	nmbm,	CONFIG_SYS_MAXARGS,	0,	do_nmbm,
 | 
						|
+	"NMBM utility commands",
 | 
						|
+	"\n"
 | 
						|
+	"nmbm list                                - List NMBM devices\n"
 | 
						|
+	"nmbm <name> info                         - Display NMBM information\n"
 | 
						|
+	"nmbm <name> state                        - Display block states\n"
 | 
						|
+	"nmbm <name> bad                          - Display bad blocks\n"
 | 
						|
+	"nmbm <name> boot <part | [loadaddr] offset>  - Boot from NMBM\n"
 | 
						|
+	"nmbm <name> mapping [all]                - Display block mapping\n"
 | 
						|
+	"nmbm <name> erase <offset> <size>        - Erase blocks\n"
 | 
						|
+	"nmbm <name> read <addr> <offset> <size>  - Read data\n"
 | 
						|
+	"nmbm <name> write <addr> <offset> <size> - Write data\n"
 | 
						|
+);
 |