[PATCH 2/5] arm: mach-imx: Add command to expose QB functionality
Simona Toaca (OSS)
simona.toaca at oss.nxp.com
Tue Mar 10 12:54:21 CET 2026
From: Simona Toaca <simona.toaca at nxp.com>
This command exposes 3 methods:
- check -> checks if the data in volatile memory is valid
- save -> saves the data to non-volatile memory and
erases the data in volatile memory
- erase -> erases the data in non-volatile memory
cmd_qb can be used either directly in the U-Boot console
or in an uuu script to save the QB data during flashing.
It supports specifying a different boot medium than the
current boot device for saving the data.
Signed-off-by: Viorel Suman <viorel.suman at nxp.com>
Signed-off-by: Ye Li <ye.li at nxp.com>
Signed-off-by: Simona Toaca <simona.toaca at nxp.com>
---
arch/arm/mach-imx/Kconfig | 8 ++
arch/arm/mach-imx/Makefile | 1 +
arch/arm/mach-imx/cmd_qb.c | 132 ++++++++++++++++++++++++++++++++
arch/arm/mach-imx/imx9/Makefile | 4 +-
4 files changed, 144 insertions(+), 1 deletion(-)
create mode 100644 arch/arm/mach-imx/cmd_qb.c
diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig
index e4014226582..17aad696648 100644
--- a/arch/arm/mach-imx/Kconfig
+++ b/arch/arm/mach-imx/Kconfig
@@ -71,6 +71,14 @@ config CSF_SIZE
Define the maximum size for Command Sequence File (CSF) binary
this information is used to define the image boot data.
+config CMD_IMX_QB
+ bool "Support the 'qb' command"
+ default y
+ depends on IMX_SNPS_DDR_PHY_QB_GEN && (IMX95 || IMX94)
+ help
+ Enable qb command to write DDR quick boot training data
+ to boot device.
+
config CMD_BMODE
bool "Support the 'bmode' command"
default y
diff --git a/arch/arm/mach-imx/Makefile b/arch/arm/mach-imx/Makefile
index 0f6e737c0b9..dfa9eca43eb 100644
--- a/arch/arm/mach-imx/Makefile
+++ b/arch/arm/mach-imx/Makefile
@@ -82,6 +82,7 @@ obj-$(CONFIG_CMD_BMODE) += cmd_bmode.o
obj-$(CONFIG_CMD_HDMIDETECT) += cmd_hdmidet.o
obj-$(CONFIG_CMD_DEKBLOB) += cmd_dek.o
obj-$(CONFIG_CMD_NANDBCB) += cmd_nandbcb.o
+obj-$(CONFIG_CMD_IMX_QB) += cmd_qb.o
endif
ifeq ($(CONFIG_XPL_BUILD),y)
diff --git a/arch/arm/mach-imx/cmd_qb.c b/arch/arm/mach-imx/cmd_qb.c
new file mode 100644
index 00000000000..9e4532bc84c
--- /dev/null
+++ b/arch/arm/mach-imx/cmd_qb.c
@@ -0,0 +1,132 @@
+// SPDX-License-Identifier: GPL-2.0+
+/**
+ * Copyright 2024-2026 NXP
+ */
+#include <command.h>
+#include <spl.h>
+#include <stdlib.h>
+
+#include <asm/mach-imx/boot_mode.h>
+#include <asm/mach-imx/sys_proto.h>
+#include <asm/mach-imx/qb.h>
+
+static int get_board_boot_device(enum boot_device dev)
+{
+ switch (dev) {
+ case SD1_BOOT:
+ case MMC1_BOOT:
+ return BOOT_DEVICE_MMC1;
+ case SD2_BOOT:
+ case MMC2_BOOT:
+ return BOOT_DEVICE_MMC2;
+ case USB_BOOT:
+ return BOOT_DEVICE_BOARD;
+ case QSPI_BOOT:
+ return BOOT_DEVICE_SPI;
+ default:
+ return BOOT_DEVICE_NONE;
+ }
+}
+
+static void parse_qb_args(int argc, char * const argv[],
+ int *qb_dev, int qb_bootdev)
+{
+ long dev = -1;
+ char *interface = "";
+
+ if (argc >= 2) {
+ interface = argv[1];
+ } else {
+ /** qb save -> use boot device */
+ *qb_dev = qb_bootdev;
+ }
+
+ if (argc == 3)
+ dev = simple_strtol(argv[2], NULL, 10);
+
+ if (!strcmp(interface, "mmc") && dev >= 0 &&
+ dev <= (BOOT_DEVICE_MMC2_2 - BOOT_DEVICE_MMC1))
+ *qb_dev = BOOT_DEVICE_MMC1 + dev;
+
+ if (!strcmp(interface, "spi"))
+ *qb_dev = BOOT_DEVICE_SPI;
+}
+
+static int do_qb(struct cmd_tbl *cmdtp, int flag, int argc,
+ char * const argv[], bool save)
+{
+ int ret = CMD_RET_FAILURE;
+ enum boot_device boot_dev = UNKNOWN_BOOT;
+ int qb_dev = BOOT_DEVICE_NONE, qb_bootdev;
+
+ boot_dev = get_boot_device();
+ qb_bootdev = get_board_boot_device(boot_dev);
+
+ parse_qb_args(argc, argv, &qb_dev, qb_bootdev);
+
+ ret = qb(qb_dev, qb_bootdev, save);
+
+ return ret ? CMD_RET_FAILURE : CMD_RET_SUCCESS;
+}
+
+static int do_qb_check(struct cmd_tbl *cmdtp, int flag,
+ int argc, char * const argv[])
+{
+ return qb_check() ? CMD_RET_SUCCESS : CMD_RET_FAILURE;
+}
+
+static int do_qb_save(struct cmd_tbl *cmdtp, int flag,
+ int argc, char * const argv[])
+{
+ return do_qb(cmdtp, flag, argc, argv, true);
+}
+
+static int do_qb_erase(struct cmd_tbl *cmdtp, int flag,
+ int argc, char * const argv[])
+{
+ return do_qb(cmdtp, flag, argc, argv, false);
+}
+
+static struct cmd_tbl cmd_qb[] = {
+ U_BOOT_CMD_MKENT(check, 1, 1, do_qb_check, "", ""),
+ U_BOOT_CMD_MKENT(save, 3, 1, do_qb_save, "", ""),
+ U_BOOT_CMD_MKENT(erase, 3, 1, do_qb_erase, "", ""),
+};
+
+static int do_qbops(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct cmd_tbl *cp;
+
+ cp = find_cmd_tbl(argv[1], cmd_qb, ARRAY_SIZE(cmd_qb));
+
+ /* Drop the qb command */
+ argc--;
+ argv++;
+
+ if (!cp) {
+ printf("%s cp is null\n", __func__);
+ return CMD_RET_USAGE;
+ }
+
+ if (argc > cp->maxargs) {
+ printf("%s argc(%d) > cp->maxargs(%d)\n", __func__, argc, cp->maxargs);
+ return CMD_RET_USAGE;
+ }
+
+ if (flag == CMD_FLAG_REPEAT && !cmd_is_repeatable(cp)) {
+ printf("%s %s repeat flag set but command is not repeatable\n",
+ __func__, cp->name);
+ return CMD_RET_SUCCESS;
+ }
+
+ return cp->cmd(cmdtp, flag, argc, argv);
+}
+
+U_BOOT_CMD(
+ qb, 4, 1, do_qbops,
+ "DDR Quick Boot sub system",
+ "check - check if quick boot data is stored in mem by training flow\n"
+ "qb save [interface] [dev] - save quick boot data in NVM location => trigger quick boot flow\n"
+ "qb erase [interface] [dev] - erase quick boot data from NVM location => trigger training flow\n"
+);
diff --git a/arch/arm/mach-imx/imx9/Makefile b/arch/arm/mach-imx/imx9/Makefile
index 3018d128a36..7dee144e0f4 100644
--- a/arch/arm/mach-imx/imx9/Makefile
+++ b/arch/arm/mach-imx/imx9/Makefile
@@ -15,5 +15,7 @@ obj-y += imx_bootaux.o
endif
ifeq ($(CONFIG_IMX_SNPS_DDR_PHY_QB_GEN),y)
-obj-y += qb.o
+ifneq ($(CONFIG_XPL_BUILD),y)
+obj-$(CONFIG_CMD_IMX_QB) += qb.o
+endif
endif
--
2.43.0
More information about the U-Boot
mailing list