mirror of
https://github.com/u-boot/u-boot.git
synced 2025-04-23 13:56:20 +00:00
cmd: mtd: OTP access support
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'. Link: https://lore.kernel.org/all/20240326223919.3781-1-avkrasnov@salutedevices.com Signed-off-by: Arseniy Krasnov <avkrasnov@salutedevices.com> Signed-off-by: Dario Binacchi <dario.binacchi@amarulasolutions.com>
This commit is contained in:
parent
cd6d71a2e9
commit
248fc16055
2 changed files with 241 additions and 0 deletions
|
@ -1361,6 +1361,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
|
||||
|
|
234
cmd/mtd.c
234
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),
|
||||
|
|
Loading…
Add table
Reference in a new issue