diff mbox series

[v1] cmd: mtd: OTP access support

Message ID 20231130112817.98827-1-avkrasnov@salutedevices.com
State Superseded
Delegated to: Dario Binacchi
Headers show
Series [v1] cmd: mtd: OTP access support | expand

Commit Message

Arseniy Krasnov Nov. 30, 2023, 11:28 a.m. UTC
Add access to OTP region. It supports info, dump, write and lock
operations.

Signed-off-by: Arseniy Krasnov <avkrasnov@salutedevices.com>
---
 cmd/Kconfig |   1 +
 cmd/mtd.c   | 224 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 225 insertions(+)

Comments

Arseniy Krasnov Nov. 30, 2023, 4:30 p.m. UTC | #1
On 30.11.2023 16:35, Michael Walle wrote:
>> +static int do_mtd_otp_write(struct cmd_tbl *cmdtp, int flag, int argc,
>> +                char *const argv[])
>> +{
> ..
> 
>> +    printf("Caution! OTP data bits can't be erased! Continue (y/n)?\n");
> 
> Please note, that with current SPI-NOR flashes this is not true and
> there is usually some kind of erase command for the OTP bits. Only
> the region lock is permanent and with that set, no more write or erase
> is possible.

I see, so may be just rephrase this message, like just "Continue? (y/n)?"

Thanks, Arseniy

> 
> -michael
Michael Walle Dec. 1, 2023, 8:28 a.m. UTC | #2
Hi,

>>> +static int do_mtd_otp_write(struct cmd_tbl *cmdtp, int flag, int 
>>> argc,
>>> +                char *const argv[])
>>> +{
>> ..
>> 
>>> +    printf("Caution! OTP data bits can't be erased! Continue 
>>> (y/n)?\n");
>> 
>> Please note, that with current SPI-NOR flashes this is not true and
>> there is usually some kind of erase command for the OTP bits. Only
>> the region lock is permanent and with that set, no more write or erase
>> is possible.
> 
> I see, so may be just rephrase this message, like just "Continue? 
> (y/n)?"

I don't have any strong opinion here. The code may or may not be used
to write to the OTP of an SPI-NOR flash. Also, this patchset doesn't
support the erase op (I guess u-boot's MTD won't support it either). So
from an user's perspective these OTP data can't be erased ;)

-michael
Arseniy Krasnov Dec. 1, 2023, 10:43 a.m. UTC | #3
On 01.12.2023 11:28, Michael Walle wrote:
> Hi,
> 
>>>> +static int do_mtd_otp_write(struct cmd_tbl *cmdtp, int flag, int argc,
>>>> +                char *const argv[])
>>>> +{
>>> ..
>>>
>>>> +    printf("Caution! OTP data bits can't be erased! Continue (y/n)?\n");
>>>
>>> Please note, that with current SPI-NOR flashes this is not true and
>>> there is usually some kind of erase command for the OTP bits. Only
>>> the region lock is permanent and with that set, no more write or erase
>>> is possible.
>>
>> I see, so may be just rephrase this message, like just "Continue? (y/n)?"
> 
> I don't have any strong opinion here. The code may or may not be used
> to write to the OTP of an SPI-NOR flash. Also, this patchset doesn't
> support the erase op (I guess u-boot's MTD won't support it either). So
> from an user's perspective these OTP data can't be erased ;)

Ok, anyway if SPI-NOR supports erasing of OTP, I'll remove these words
from the message above. About erase-op - unfortunately I don't have SPI-NOR
hardware at this moment (only NAND), so I can't test such implementation.

Thanks, Arseniy

> 
> -michael
diff mbox series

Patch

diff --git a/cmd/Kconfig b/cmd/Kconfig
index 90e4ef93e0..c47523a03b 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -1354,6 +1354,7 @@  config CMD_MTD
 	bool "mtd"
 	depends on MTD
 	select MTD_PARTITIONS
+	select HEXDUMP
 	help
 	  MTD commands support.
 
diff --git a/cmd/mtd.c b/cmd/mtd.c
index eb6e2d6892..3ad15b0f8f 100644
--- a/cmd/mtd.c
+++ b/cmd/mtd.c
@@ -11,6 +11,7 @@ 
 #include <command.h>
 #include <common.h>
 #include <console.h>
+#include <hexdump.h>
 #include <malloc.h>
 #include <mapmem.h>
 #include <mtd.h>
@@ -202,6 +203,219 @@  static bool mtd_oob_write_is_empty(struct mtd_oob_ops *op)
 	return true;
 }
 
+static int do_mtd_otp_read(struct cmd_tbl *cmdtp, int flag, int argc,
+			   char *const argv[])
+{
+	struct mtd_info *mtd;
+	size_t retlen;
+	off_t from;
+	size_t len;
+	bool user;
+	int ret;
+	u8 *buf;
+
+	if (argc != 5)
+		return CMD_RET_USAGE;
+
+	if (!strcmp(argv[2], "u"))
+		user = true;
+	else if (!strcmp(argv[2], "f"))
+		user = false;
+	else
+		return CMD_RET_USAGE;
+
+	mtd = get_mtd_by_name(argv[1]);
+	if (IS_ERR_OR_NULL(mtd))
+		return CMD_RET_FAILURE;
+
+	from = simple_strtoul(argv[3], NULL, 0);
+	len = simple_strtoul(argv[4], NULL, 0);
+
+	ret = CMD_RET_FAILURE;
+
+	buf = malloc(len);
+	if (!buf)
+		goto put_mtd;
+
+	printf("Reading %s OTP from 0x%lx, %lu bytes\n",
+	       user ? "user" : "factory", from, len);
+
+	if (user)
+		ret = mtd_read_user_prot_reg(mtd, from, len, &retlen, buf);
+	else
+		ret = mtd_read_fact_prot_reg(mtd, from, len, &retlen, buf);
+	if (ret) {
+		free(buf);
+		pr_err("OTP read failed: %d\n", ret);
+		ret = CMD_RET_FAILURE;
+		goto put_mtd;
+	}
+
+	if (retlen != len)
+		pr_err("OTP read returns %zu, but %zu expected\n",
+		       retlen, len);
+
+	print_hex_dump("", 0, 16, 1, buf, retlen, true);
+
+	free(buf);
+
+	ret = CMD_RET_SUCCESS;
+
+put_mtd:
+	put_mtd_device(mtd);
+
+	return ret;
+}
+
+static int do_mtd_otp_lock(struct cmd_tbl *cmdtp, int flag, int argc,
+			   char *const argv[])
+{
+	struct mtd_info *mtd;
+	off_t from;
+	size_t len;
+	int ret;
+
+	if (argc != 4)
+		return CMD_RET_USAGE;
+
+	mtd = get_mtd_by_name(argv[1]);
+	if (IS_ERR_OR_NULL(mtd))
+		return CMD_RET_FAILURE;
+
+	from = simple_strtoul(argv[2], NULL, 0);
+	len = simple_strtoul(argv[3], NULL, 0);
+
+	ret = mtd_lock_user_prot_reg(mtd, from, len);
+	if (ret) {
+		pr_err("OTP lock failed: %d\n", ret);
+		ret = CMD_RET_FAILURE;
+		goto put_mtd;
+	}
+
+	ret = CMD_RET_SUCCESS;
+
+put_mtd:
+	put_mtd_device(mtd);
+
+	return ret;
+}
+
+static int do_mtd_otp_write(struct cmd_tbl *cmdtp, int flag, int argc,
+			    char *const argv[])
+{
+	struct mtd_info *mtd;
+	size_t retlen;
+	size_t binlen;
+	u8 *binbuf;
+	off_t from;
+	int ret;
+
+	if (argc != 4)
+		return CMD_RET_USAGE;
+
+	mtd = get_mtd_by_name(argv[1]);
+	if (IS_ERR_OR_NULL(mtd))
+		return CMD_RET_FAILURE;
+
+	from = simple_strtoul(argv[2], NULL, 0);
+	binlen = strlen(argv[3]) / 2;
+
+	ret = CMD_RET_FAILURE;
+	binbuf = malloc(binlen);
+	if (!binbuf)
+		goto put_mtd;
+
+	hex2bin(binbuf, argv[3], binlen);
+
+	printf("Will write:\n");
+
+	print_hex_dump("", 0, 16, 1, binbuf, binlen, true);
+
+	printf("to 0x%zx\n", from);
+
+	printf("Caution! OTP data bits can't be erased! Continue (y/n)?\n");
+
+	if (confirm_yesno() != 1) {
+		pr_err("OTP write canceled\n");
+		ret = CMD_RET_SUCCESS;
+		goto put_mtd;
+	}
+
+	ret = mtd_write_user_prot_reg(mtd, from, binlen, &retlen, binbuf);
+	if (ret) {
+		pr_err("OTP write failed: %d\n", ret);
+		ret = CMD_RET_FAILURE;
+		goto put_mtd;
+	}
+
+	if (retlen != binlen)
+		pr_err("OTP write returns %zu, but %zu expected\n",
+		       retlen, binlen);
+
+	ret = CMD_RET_SUCCESS;
+
+put_mtd:
+	free(binbuf);
+	put_mtd_device(mtd);
+
+	return ret;
+}
+
+static int do_mtd_otp_info(struct cmd_tbl *cmdtp, int flag, int argc,
+			   char *const argv[])
+{
+	struct otp_info otp_info;
+	struct mtd_info *mtd;
+	size_t retlen;
+	bool user;
+	int ret;
+
+	if (argc != 3)
+		return CMD_RET_USAGE;
+
+	if (!strcmp(argv[2], "u"))
+		user = true;
+	else if (!strcmp(argv[2], "f"))
+		user = false;
+	else
+		return CMD_RET_USAGE;
+
+	mtd = get_mtd_by_name(argv[1]);
+	if (IS_ERR_OR_NULL(mtd))
+		return CMD_RET_FAILURE;
+
+	if (user)
+		ret = mtd_get_user_prot_info(mtd, sizeof(otp_info), &retlen,
+					     &otp_info);
+	else
+		ret = mtd_get_fact_prot_info(mtd, sizeof(otp_info), &retlen,
+					     &otp_info);
+	if (ret) {
+		pr_err("OTP info failed: %d\n", ret);
+		ret = CMD_RET_FAILURE;
+		goto put_mtd;
+	}
+
+	if (retlen != sizeof(otp_info)) {
+		pr_err("OTP info returns %zu, but %zu expected\n",
+		       retlen, sizeof(otp_info));
+		ret = CMD_RET_FAILURE;
+		goto put_mtd;
+	}
+
+	printf("%s OTP region info:\n", user ? "User" : "Factory");
+	printf("\tstart: %u\n", otp_info.start);
+	printf("\tlength: %u\n", otp_info.length);
+	printf("\tlocked: %u\n", otp_info.locked);
+
+	ret = CMD_RET_SUCCESS;
+
+put_mtd:
+	put_mtd_device(mtd);
+
+	return ret;
+}
+
 static int do_mtd_list(struct cmd_tbl *cmdtp, int flag, int argc,
 		       char *const argv[])
 {
@@ -552,6 +766,10 @@  static char mtd_help_text[] =
 	"\n"
 	"Specific functions:\n"
 	"mtd bad                               <name>\n"
+	"mtd otpread                           <name> [u|f] <off> <size>\n"
+	"mtd otpwrite                          <name> <off> <hex string>\n"
+	"mtd otplock                           <name> <off> <size>\n"
+	"mtd otpinfo                           <name> [u|f]\n"
 	"\n"
 	"With:\n"
 	"\t<name>: NAND partition/chip name (or corresponding DM device name or OF path)\n"
@@ -562,11 +780,17 @@  static char mtd_help_text[] =
 	"\t<size>: length of the operation in bytes (default: the entire device)\n"
 	"\t\t* must be a multiple of a block for erase\n"
 	"\t\t* must be a multiple of a page otherwise (special case: default is a page with dump)\n"
+	"\t<hex string>: hex string without '0x' and spaces. Example: ABCD1234\n"
+	"\t[u|f]: user or factory OTP region\n"
 	"\n"
 	"The .dontskipff option forces writing empty pages, don't use it if unsure.\n";
 #endif
 
 U_BOOT_CMD_WITH_SUBCMDS(mtd, "MTD utils", mtd_help_text,
+		U_BOOT_SUBCMD_MKENT(otpread, 5, 1, do_mtd_otp_read),
+		U_BOOT_SUBCMD_MKENT(otpwrite, 4, 1, do_mtd_otp_write),
+		U_BOOT_SUBCMD_MKENT(otplock, 4, 1, do_mtd_otp_lock),
+		U_BOOT_SUBCMD_MKENT(otpinfo, 3, 1, do_mtd_otp_info),
 		U_BOOT_SUBCMD_MKENT(list, 1, 1, do_mtd_list),
 		U_BOOT_SUBCMD_MKENT_COMPLETE(read, 5, 0, do_mtd_io,
 					     mtd_name_complete),