[PATCH v5] cmd: mtd: OTP access support
Dario Binacchi
dario.binacchi at amarulasolutions.com
Sun Apr 14 15:46:42 CEST 2024
Hi Arseniy,
On Tue, Mar 26, 2024 at 11:49 PM Arseniy Krasnov
<avkrasnov at 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 at 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
>
Applied to nand-next
Thanks and regards
Dario
--
Dario Binacchi
Senior Embedded Linux Developer
dario.binacchi at amarulasolutions.com
__________________________________
Amarula Solutions SRL
Via Le Canevare 30, 31100 Treviso, Veneto, IT
T. +39 042 243 5310
info at amarulasolutions.com
www.amarulasolutions.com
More information about the U-Boot
mailing list