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 <avkras...@salutedevices.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.
 v4 -> v5:
  * Implement commands from this patch as config option due to too big
    final size of the uboot image.
  * R-b removed because of patch update.

 cmd/Kconfig |   7 ++
 cmd/mtd.c   | 234 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 241 insertions(+)

diff --git a/cmd/Kconfig b/cmd/Kconfig
index 7292a150f5..832098e66e 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -1366,6 +1366,13 @@ config CMD_MTD
        help
          MTD commands support.
 
+config CMD_MTD_OTP
+       bool "mtd otp"
+       depends on CMD_MTD
+       select HEXDUMP
+       help
+         MTD commands for OTP access.
+
 config CMD_MUX
        bool "mux"
        depends on MULTIPLEXER
diff --git a/cmd/mtd.c b/cmd/mtd.c
index e63c011e79..c66105e373 100644
--- a/cmd/mtd.c
+++ b/cmd/mtd.c
@@ -11,6 +11,9 @@
 #include <command.h>
 #include <common.h>
 #include <console.h>
+#if CONFIG_IS_ENABLED(CMD_MTD_OTP)
+#include <hexdump.h>
+#endif
 #include <malloc.h>
 #include <mapmem.h>
 #include <mtd.h>
@@ -202,6 +205,221 @@ static bool mtd_oob_write_is_empty(struct mtd_oob_ops *op)
        return true;
 }
 
+#if CONFIG_IS_ENABLED(CMD_MTD_OTP)
+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;
+}
+#endif
+
 static int do_mtd_list(struct cmd_tbl *cmdtp, int flag, int argc,
                       char *const argv[])
 {
@@ -551,6 +769,12 @@ U_BOOT_LONGHELP(mtd,
        "\n"
        "Specific functions:\n"
        "mtd bad                               <name>\n"
+#if CONFIG_IS_ENABLED(CMD_MTD_OTP)
+       "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"
+#endif
        "\n"
        "With:\n"
        "\t<name>: NAND partition/chip name (or corresponding DM device name or 
OF path)\n"
@@ -561,10 +785,20 @@ 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"
+#if CONFIG_IS_ENABLED(CMD_MTD_OTP)
+       "\t<hex string>: hex string without '0x' and spaces. Example: 
ABCD1234\n"
+       "\t[u|f]: user or factory OTP region\n"
+#endif
        "\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,
+#if CONFIG_IS_ENABLED(CMD_MTD_OTP)
+               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),
+#endif
                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

Reply via email to