[PATCH v1] cmd: mtd: OTP access support

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

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

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

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

On 01.12.2023 11:28, Michael Walle wrote:
Hi,
+static int do_mtd_otp_write(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{
..
+ printf("Caution! OTP data bits can't be erased! Continue (y/n)?\n");
Please note, that with current SPI-NOR flashes this is not true and there is usually some kind of erase command for the OTP bits. Only the region lock is permanent and with that set, no more write or erase is possible.
I see, so may be just rephrase this message, like just "Continue? (y/n)?"
I don't have any strong opinion here. The code may or may not be used to write to the OTP of an SPI-NOR flash. Also, this patchset doesn't support the erase op (I guess u-boot's MTD won't support it either). So from an user's perspective these OTP data can't be erased ;)
Ok, anyway if SPI-NOR supports erasing of OTP, I'll remove these words from the message above. About erase-op - unfortunately I don't have SPI-NOR hardware at this moment (only NAND), so I can't test such implementation.
Thanks, Arseniy
-michael
participants (2)
-
Arseniy Krasnov
-
Michael Walle