diff mbox series

[v2] cmd: mtd: OTP access support

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

Commit Message

Arseniy Krasnov Dec. 20, 2023, 7:36 p.m. UTC
Add access to OTP region. It supports info, dump, write and lock
operations.

Signed-off-by: Arseniy Krasnov <avkrasnov@salutedevices.com>
---
 Changelog:
 v1 -> v2:
  * Remove warning that OTP can't be erased after write.

 cmd/Kconfig |   1 +
 cmd/mtd.c   | 224 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 225 insertions(+)

Comments

Arseniy Krasnov Jan. 8, 2024, 6:33 p.m. UTC | #1
Sorry, pls ping

Thanks, Arseniy

On 20.12.2023 22:36, Arseniy Krasnov wrote:
> Add access to OTP region. It supports info, dump, write and lock
> operations.
> 
> Signed-off-by: Arseniy Krasnov <avkrasnov@salutedevices.com>
> ---
>  Changelog:
>  v1 -> v2:
>   * Remove warning that OTP can't be erased after write.
> 
>  cmd/Kconfig |   1 +
>  cmd/mtd.c   | 224 ++++++++++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 225 insertions(+)
> 
> 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..1ab69b108b 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("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),
Arseniy Krasnov Feb. 10, 2024, 11:16 p.m. UTC | #2
Sorry, pls ping

Thanks, Arseniy

On 08.01.2024 21:33, Arseniy Krasnov wrote:
> Sorry, pls ping
> 
> Thanks, Arseniy
> 
> On 20.12.2023 22:36, Arseniy Krasnov wrote:
>> Add access to OTP region. It supports info, dump, write and lock
>> operations.
>>
>> Signed-off-by: Arseniy Krasnov <avkrasnov@salutedevices.com>
>> ---
>>  Changelog:
>>  v1 -> v2:
>>   * Remove warning that OTP can't be erased after write.
>>
>>  cmd/Kconfig |   1 +
>>  cmd/mtd.c   | 224 ++++++++++++++++++++++++++++++++++++++++++++++++++++
>>  2 files changed, 225 insertions(+)
>>
>> 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..1ab69b108b 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("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),
Arseniy Krasnov March 13, 2024, 6:33 a.m. UTC | #3
Sorry, please ping

Thanks, Arseniy


On 11.02.2024 02:16, Arseniy Krasnov wrote:
> Sorry, pls ping
> 
> Thanks, Arseniy
> 
> On 08.01.2024 21:33, Arseniy Krasnov wrote:
>> Sorry, pls ping
>>
>> Thanks, Arseniy
>>
>> On 20.12.2023 22:36, Arseniy Krasnov wrote:
>>> Add access to OTP region. It supports info, dump, write and lock
>>> operations.
>>>
>>> Signed-off-by: Arseniy Krasnov <avkrasnov@salutedevices.com>
>>> ---
>>>  Changelog:
>>>  v1 -> v2:
>>>   * Remove warning that OTP can't be erased after write.
>>>
>>>  cmd/Kconfig |   1 +
>>>  cmd/mtd.c   | 224 ++++++++++++++++++++++++++++++++++++++++++++++++++++
>>>  2 files changed, 225 insertions(+)
>>>
>>> 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..1ab69b108b 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("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),
Michael Nazzareno Trimarchi March 13, 2024, 6:48 a.m. UTC | #4
Hi

On Wed, Mar 13, 2024 at 7:43 AM Arseniy Krasnov
<avkrasnov@salutedevices.com> wrote:
>
> Sorry, please ping
>
> Thanks, Arseniy
>
>
> On 11.02.2024 02:16, Arseniy Krasnov wrote:
> > Sorry, pls ping
> >
> > Thanks, Arseniy
> >
> > On 08.01.2024 21:33, Arseniy Krasnov wrote:
> >> Sorry, pls ping
> >>
> >> Thanks, Arseniy
> >>
> >> On 20.12.2023 22:36, Arseniy Krasnov wrote:
> >>> Add access to OTP region. It supports info, dump, write and lock
> >>> operations.
> >>>
> >>> Signed-off-by: Arseniy Krasnov <avkrasnov@salutedevices.com>
> >>> ---

Please extend the commit message with some example of the usage otherwise

Reviewed-by: Michael Trimarchi <michael@amarulasolutions.com>

> >>>  Changelog:
> >>>  v1 -> v2:
> >>>   * Remove warning that OTP can't be erased after write.
> >>>
> >>>  cmd/Kconfig |   1 +
> >>>  cmd/mtd.c   | 224 ++++++++++++++++++++++++++++++++++++++++++++++++++++
> >>>  2 files changed, 225 insertions(+)
> >>>
> >>> 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..1ab69b108b 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("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),
Arseniy Krasnov March 13, 2024, 7:18 a.m. UTC | #5
On 13.03.2024 09:48, Michael Nazzareno Trimarchi wrote:
> Hi
> 
> On Wed, Mar 13, 2024 at 7:43 AM Arseniy Krasnov
> <avkrasnov@salutedevices.com> wrote:
>>
>> Sorry, please ping
>>
>> Thanks, Arseniy
>>
>>
>> On 11.02.2024 02:16, Arseniy Krasnov wrote:
>>> Sorry, pls ping
>>>
>>> Thanks, Arseniy
>>>
>>> On 08.01.2024 21:33, Arseniy Krasnov wrote:
>>>> Sorry, pls ping
>>>>
>>>> Thanks, Arseniy
>>>>
>>>> On 20.12.2023 22:36, Arseniy Krasnov wrote:
>>>>> Add access to OTP region. It supports info, dump, write and lock
>>>>> operations.
>>>>>
>>>>> Signed-off-by: Arseniy Krasnov <avkrasnov@salutedevices.com>
>>>>> ---
> 
> Please extend the commit message with some example of the usage otherwise

Done in v3

Thanks, Arseniy

> 
> Reviewed-by: Michael Trimarchi <michael@amarulasolutions.com>
> 
>>>>>  Changelog:
>>>>>  v1 -> v2:
>>>>>   * Remove warning that OTP can't be erased after write.
>>>>>
>>>>>  cmd/Kconfig |   1 +
>>>>>  cmd/mtd.c   | 224 ++++++++++++++++++++++++++++++++++++++++++++++++++++
>>>>>  2 files changed, 225 insertions(+)
>>>>>
>>>>> 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..1ab69b108b 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("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),
> 
> 
>
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..1ab69b108b 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("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),