[PATCH v4] cmd: mtd: OTP access support
Arseniy Krasnov
avkrasnov at salutedevices.com
Wed Mar 20 20:04:43 CET 2024
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 at salutedevices.com>
Reviewed-by: Michael Trimarchi <michael at 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
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
More information about the U-Boot
mailing list