Message ID | 20240320190443.1483274-1-avkrasnov@salutedevices.com |
---|---|
State | Superseded |
Delegated to: | Dario Binacchi |
Headers | show |
Series | [v4] cmd: mtd: OTP access support | expand |
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 >> > >
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
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 >
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 --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),