diff mbox series

[v4] cmd: mtd: OTP access support

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

Commit Message

Arseniy Krasnov March 20, 2024, 7:04 p.m. UTC
Add access to OTP region. It supports info, dump, write and lock
operations. Usage example:

'mtd otpread nand0 u 0 1024' - dump 1024 bytes of user area starting
 from offset 0 of device 'nand0'.

'mtd otpwrite nand0 10 11223344' - write binary data 0x11, 0x22, 0x33,
 0x44 to offset 10 to user area of device 'nand0'.

'mtd otplock nand0 0 1024' - lock 1024 bytes of user area starting
 from offset 0 of device 'nand0'.

'mtd otpinfo nand0 f' - show info about factory area of device 'nand0'.

Signed-off-by: Arseniy Krasnov <avkrasnov@salutedevices.com>
Reviewed-by: Michael Trimarchi <michael@amarulasolutions.com>
---
 Changelog:
 v1 -> v2:
  * Remove warning that OTP can't be erased after write.
 v2 -> v3:
  * Commit message updated by adding usage.
  * R-b added.
 v3 -> v4:
  * Fix build failure due to invalid format strings for 'printf()'.
  * Rebase over latest version of cmd/mtd.c.

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

Comments

Arseniy Krasnov March 22, 2024, 8:04 a.m. UTC | #1
Hi,

On 22.03.2024 11:12, Michael Nazzareno Trimarchi wrote:
> Hi Arseniy
> 
> On Wed, Mar 20, 2024 at 8:14 PM Arseniy Krasnov
> <avkrasnov@salutedevices.com> wrote:
>>
>> Add access to OTP region. It supports info, dump, write and lock
>> operations. Usage example:
>>
>> 'mtd otpread nand0 u 0 1024' - dump 1024 bytes of user area starting
>>  from offset 0 of device 'nand0'.
>>
>> 'mtd otpwrite nand0 10 11223344' - write binary data 0x11, 0x22, 0x33,
>>  0x44 to offset 10 to user area of device 'nand0'.
>>
>> 'mtd otplock nand0 0 1024' - lock 1024 bytes of user area starting
>>  from offset 0 of device 'nand0'.
>>
>> 'mtd otpinfo nand0 f' - show info about factory area of device 'nand0'.
>>
>> Signed-off-by: Arseniy Krasnov <avkrasnov@salutedevices.com>
>> Reviewed-by: Michael Trimarchi <michael@amarulasolutions.com>
>> ---
>>  Changelog:
>>  v1 -> v2:
>>   * Remove warning that OTP can't be erased after write.
>>  v2 -> v3:
>>   * Commit message updated by adding usage.
>>   * R-b added.
>>  v3 -> v4:
>>   * Fix build failure due to invalid format strings for 'printf()'.
>>   * Rebase over latest version of cmd/mtd.c.
>>
>>  cmd/Kconfig |   1 +
>>  cmd/mtd.c   | 224 ++++++++++++++++++++++++++++++++++++++++++++++++++++
>>  2 files changed, 225 insertions(+)
>>
>> diff --git a/cmd/Kconfig b/cmd/Kconfig
>> index 7292a150f5..f218dc6cf2 100644
>> --- a/cmd/Kconfig
>> +++ b/cmd/Kconfig
>> @@ -1363,6 +1363,7 @@ config CMD_MTD
>>         bool "mtd"
>>         depends on MTD
>>         select MTD_PARTITIONS
>> +       select HEXDUMP
> 
> Make those change forced in increase the size of uboot and break this build
> 
> https://source.denx.de/u-boot/custodians/u-boot-nand-flash/-/jobs/802378
> 
> I think that those command could be optional and not forced to be
> included by default
> 

Ah, Ok:) I'll add it as config option which depends on CMD_MTD in the next version

Thanks

> Michael
> 
> 
>>         help
>>           MTD commands support.
>>
>> diff --git a/cmd/mtd.c b/cmd/mtd.c
>> index e63c011e79..2b03a85ef0 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, %zu 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%lx\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[])
>>  {
>> @@ -551,6 +765,10 @@ U_BOOT_LONGHELP(mtd,
>>         "\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"
>> @@ -561,10 +779,16 @@ U_BOOT_LONGHELP(mtd,
>>         "\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");
>>
>>  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),
>> --
>> 2.35.0
>>
> 
>
Arseniy Krasnov March 22, 2024, 8:08 a.m. UTC | #2
On 22.03.2024 11:17, Michael Nazzareno Trimarchi wrote:
> Hi Arseniy
> 
> On Fri, Mar 22, 2024 at 9:14 AM Arseniy Krasnov
> <avkrasnov@salutedevices.com> wrote:
>>
>> Hi,
>>
>> On 22.03.2024 11:12, Michael Nazzareno Trimarchi wrote:
>>> Hi Arseniy
>>>
>>> On Wed, Mar 20, 2024 at 8:14 PM Arseniy Krasnov
>>> <avkrasnov@salutedevices.com> wrote:
>>>>
>>>> Add access to OTP region. It supports info, dump, write and lock
>>>> operations. Usage example:
>>>>
>>>> 'mtd otpread nand0 u 0 1024' - dump 1024 bytes of user area starting
>>>>  from offset 0 of device 'nand0'.
>>>>
>>>> 'mtd otpwrite nand0 10 11223344' - write binary data 0x11, 0x22, 0x33,
>>>>  0x44 to offset 10 to user area of device 'nand0'.
>>>>
>>>> 'mtd otplock nand0 0 1024' - lock 1024 bytes of user area starting
>>>>  from offset 0 of device 'nand0'.
>>>>
>>>> 'mtd otpinfo nand0 f' - show info about factory area of device 'nand0'.
>>>>
>>>> Signed-off-by: Arseniy Krasnov <avkrasnov@salutedevices.com>
>>>> Reviewed-by: Michael Trimarchi <michael@amarulasolutions.com>
>>>> ---
>>>>  Changelog:
>>>>  v1 -> v2:
>>>>   * Remove warning that OTP can't be erased after write.
>>>>  v2 -> v3:
>>>>   * Commit message updated by adding usage.
>>>>   * R-b added.
>>>>  v3 -> v4:
>>>>   * Fix build failure due to invalid format strings for 'printf()'.
>>>>   * Rebase over latest version of cmd/mtd.c.
>>>>
>>>>  cmd/Kconfig |   1 +
>>>>  cmd/mtd.c   | 224 ++++++++++++++++++++++++++++++++++++++++++++++++++++
>>>>  2 files changed, 225 insertions(+)
>>>>
>>>> diff --git a/cmd/Kconfig b/cmd/Kconfig
>>>> index 7292a150f5..f218dc6cf2 100644
>>>> --- a/cmd/Kconfig
>>>> +++ b/cmd/Kconfig
>>>> @@ -1363,6 +1363,7 @@ config CMD_MTD
>>>>         bool "mtd"
>>>>         depends on MTD
>>>>         select MTD_PARTITIONS
>>>> +       select HEXDUMP
>>>
>>> Make those change forced in increase the size of uboot and break this build
>>>
>>> https://source.denx.de/u-boot/custodians/u-boot-nand-flash/-/jobs/802378
>>>
>>> I think that those command could be optional and not forced to be
>>> included by default
>>>
>>
>> Ah, Ok:) I'll add it as config option which depends on CMD_MTD in the next version
>>
> 
> I am really glad how you interact with the mailing list ;) and how you
> are fast on feedback
> 

Sure, thanks!

> 
> Michael
> 
>> Thanks
>>
>>> Michael
>>>
>>>
>>>>         help
>>>>           MTD commands support.
>>>>
>>>> diff --git a/cmd/mtd.c b/cmd/mtd.c
>>>> index e63c011e79..2b03a85ef0 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, %zu 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%lx\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[])
>>>>  {
>>>> @@ -551,6 +765,10 @@ U_BOOT_LONGHELP(mtd,
>>>>         "\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"
>>>> @@ -561,10 +779,16 @@ U_BOOT_LONGHELP(mtd,
>>>>         "\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");
>>>>
>>>>  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),
>>>> --
>>>> 2.35.0
>>>>
>>>
>>>
> 
> 
> 
> --
> Michael Nazzareno Trimarchi
> Co-Founder & Chief Executive Officer
> M. +39 347 913 2170
> michael@amarulasolutions.com
> __________________________________
> 
> Amarula Solutions BV
> Joop Geesinkweg 125, 1114 AB, Amsterdam, NL
> T. +31 (0)85 111 9172
> info@amarulasolutions.com
> www.amarulasolutions.com
Michael Nazzareno Trimarchi March 22, 2024, 8:12 a.m. UTC | #3
Hi Arseniy

On Wed, Mar 20, 2024 at 8:14 PM Arseniy Krasnov
<avkrasnov@salutedevices.com> wrote:
>
> Add access to OTP region. It supports info, dump, write and lock
> operations. Usage example:
>
> 'mtd otpread nand0 u 0 1024' - dump 1024 bytes of user area starting
>  from offset 0 of device 'nand0'.
>
> 'mtd otpwrite nand0 10 11223344' - write binary data 0x11, 0x22, 0x33,
>  0x44 to offset 10 to user area of device 'nand0'.
>
> 'mtd otplock nand0 0 1024' - lock 1024 bytes of user area starting
>  from offset 0 of device 'nand0'.
>
> 'mtd otpinfo nand0 f' - show info about factory area of device 'nand0'.
>
> Signed-off-by: Arseniy Krasnov <avkrasnov@salutedevices.com>
> Reviewed-by: Michael Trimarchi <michael@amarulasolutions.com>
> ---
>  Changelog:
>  v1 -> v2:
>   * Remove warning that OTP can't be erased after write.
>  v2 -> v3:
>   * Commit message updated by adding usage.
>   * R-b added.
>  v3 -> v4:
>   * Fix build failure due to invalid format strings for 'printf()'.
>   * Rebase over latest version of cmd/mtd.c.
>
>  cmd/Kconfig |   1 +
>  cmd/mtd.c   | 224 ++++++++++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 225 insertions(+)
>
> diff --git a/cmd/Kconfig b/cmd/Kconfig
> index 7292a150f5..f218dc6cf2 100644
> --- a/cmd/Kconfig
> +++ b/cmd/Kconfig
> @@ -1363,6 +1363,7 @@ config CMD_MTD
>         bool "mtd"
>         depends on MTD
>         select MTD_PARTITIONS
> +       select HEXDUMP

Make those change forced in increase the size of uboot and break this build

https://source.denx.de/u-boot/custodians/u-boot-nand-flash/-/jobs/802378

I think that those command could be optional and not forced to be
included by default

Michael


>         help
>           MTD commands support.
>
> diff --git a/cmd/mtd.c b/cmd/mtd.c
> index e63c011e79..2b03a85ef0 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, %zu 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%lx\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[])
>  {
> @@ -551,6 +765,10 @@ U_BOOT_LONGHELP(mtd,
>         "\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"
> @@ -561,10 +779,16 @@ U_BOOT_LONGHELP(mtd,
>         "\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");
>
>  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),
> --
> 2.35.0
>
Michael Nazzareno Trimarchi March 22, 2024, 8:17 a.m. UTC | #4
Hi Arseniy

On Fri, Mar 22, 2024 at 9:14 AM Arseniy Krasnov
<avkrasnov@salutedevices.com> wrote:
>
> Hi,
>
> On 22.03.2024 11:12, Michael Nazzareno Trimarchi wrote:
> > Hi Arseniy
> >
> > On Wed, Mar 20, 2024 at 8:14 PM Arseniy Krasnov
> > <avkrasnov@salutedevices.com> wrote:
> >>
> >> Add access to OTP region. It supports info, dump, write and lock
> >> operations. Usage example:
> >>
> >> 'mtd otpread nand0 u 0 1024' - dump 1024 bytes of user area starting
> >>  from offset 0 of device 'nand0'.
> >>
> >> 'mtd otpwrite nand0 10 11223344' - write binary data 0x11, 0x22, 0x33,
> >>  0x44 to offset 10 to user area of device 'nand0'.
> >>
> >> 'mtd otplock nand0 0 1024' - lock 1024 bytes of user area starting
> >>  from offset 0 of device 'nand0'.
> >>
> >> 'mtd otpinfo nand0 f' - show info about factory area of device 'nand0'.
> >>
> >> Signed-off-by: Arseniy Krasnov <avkrasnov@salutedevices.com>
> >> Reviewed-by: Michael Trimarchi <michael@amarulasolutions.com>
> >> ---
> >>  Changelog:
> >>  v1 -> v2:
> >>   * Remove warning that OTP can't be erased after write.
> >>  v2 -> v3:
> >>   * Commit message updated by adding usage.
> >>   * R-b added.
> >>  v3 -> v4:
> >>   * Fix build failure due to invalid format strings for 'printf()'.
> >>   * Rebase over latest version of cmd/mtd.c.
> >>
> >>  cmd/Kconfig |   1 +
> >>  cmd/mtd.c   | 224 ++++++++++++++++++++++++++++++++++++++++++++++++++++
> >>  2 files changed, 225 insertions(+)
> >>
> >> diff --git a/cmd/Kconfig b/cmd/Kconfig
> >> index 7292a150f5..f218dc6cf2 100644
> >> --- a/cmd/Kconfig
> >> +++ b/cmd/Kconfig
> >> @@ -1363,6 +1363,7 @@ config CMD_MTD
> >>         bool "mtd"
> >>         depends on MTD
> >>         select MTD_PARTITIONS
> >> +       select HEXDUMP
> >
> > Make those change forced in increase the size of uboot and break this build
> >
> > https://source.denx.de/u-boot/custodians/u-boot-nand-flash/-/jobs/802378
> >
> > I think that those command could be optional and not forced to be
> > included by default
> >
>
> Ah, Ok:) I'll add it as config option which depends on CMD_MTD in the next version
>

I am really glad how you interact with the mailing list ;) and how you
are fast on feedback


Michael

> Thanks
>
> > Michael
> >
> >
> >>         help
> >>           MTD commands support.
> >>
> >> diff --git a/cmd/mtd.c b/cmd/mtd.c
> >> index e63c011e79..2b03a85ef0 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, %zu 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%lx\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[])
> >>  {
> >> @@ -551,6 +765,10 @@ U_BOOT_LONGHELP(mtd,
> >>         "\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"
> >> @@ -561,10 +779,16 @@ U_BOOT_LONGHELP(mtd,
> >>         "\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");
> >>
> >>  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),
> >> --
> >> 2.35.0
> >>
> >
> >



--
Michael Nazzareno Trimarchi
Co-Founder & Chief Executive Officer
M. +39 347 913 2170
michael@amarulasolutions.com
diff mbox series

Patch

diff --git a/cmd/Kconfig b/cmd/Kconfig
index 7292a150f5..f218dc6cf2 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -1363,6 +1363,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 e63c011e79..2b03a85ef0 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, %zu 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%lx\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[])
 {
@@ -551,6 +765,10 @@  U_BOOT_LONGHELP(mtd,
 	"\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"
@@ -561,10 +779,16 @@  U_BOOT_LONGHELP(mtd,
 	"\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");
 
 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),