public inbox for u-boot@lists.denx.de
 help / color / mirror / Atom feed
* [PATCH 0/5] imx9{4,5}: Add Quickboot support
@ 2026-03-10 11:54 Simona Toaca (OSS)
  2026-03-10 11:54 ` [PATCH 1/5] imx9: Add support for saving DDR training data to NVM Simona Toaca (OSS)
                   ` (5 more replies)
  0 siblings, 6 replies; 15+ messages in thread
From: Simona Toaca (OSS) @ 2026-03-10 11:54 UTC (permalink / raw)
  To: uboot-imx, u-boot
  Cc: Stefano Babic, festevam, peng.fan, alice.guo, ye.li, simona.toaca,
	viorel.suman, marex, ravi, fedor.ross, sebastien.szymanski,
	joao.goncalves, ji.luo, semen.protsenko, sjg, tharvey, ping.bai,
	qijian.guo

From: Simona Toaca <simona.toaca@nxp.com>

This patch series adds support for saving DDR training
data to non-volatile memory on iMX94 and iMX95 platforms.
The purpose is running DDR Quickboot flow on next reboot.

The process is as follows:
- OEI runs Training flow for the DDRPHY
- OEI saves the data from training to volatile memory
- U-Boot can then save it to non-volatile memory (e.g. SD)
- OEI loads the data from NVM at cold reboot and runs Quickboot flow

By skipping training, a much lower boot time is achieved.

Simona Toaca (5):
  imx9: Add support for saving DDR training data to NVM
  arm: mach-imx: Add command to expose QB functionality
  imx9: Enable QB data saving for iMX9{4,5} EVK
  board: nxp: imx9{4,5}_evk: Add qb save option in SPL
  doc: board: nxp: Add Quickboot documentation

 arch/arm/include/asm/arch-imx9/ddr.h |  52 +++-
 arch/arm/include/asm/mach-imx/qb.h   |  15 +
 arch/arm/mach-imx/Kconfig            |  15 +
 arch/arm/mach-imx/Makefile           |   1 +
 arch/arm/mach-imx/cmd_qb.c           | 132 ++++++++
 arch/arm/mach-imx/imx9/Makefile      |  12 +-
 arch/arm/mach-imx/imx9/qb.c          | 439 +++++++++++++++++++++++++++
 arch/arm/mach-imx/imx9/scmi/soc.c    |   9 +
 board/nxp/imx94_evk/spl.c            |   6 +-
 board/nxp/imx95_evk/spl.c            |   6 +-
 configs/imx943_evk_defconfig         |   1 +
 configs/imx95_15x15_evk_defconfig    |   1 +
 configs/imx95_evk.config             |   1 +
 doc/board/nxp/index.rst              |   1 +
 doc/board/nxp/qb.rst                 |  33 ++
 drivers/ddr/imx/imx9/Kconfig         |   8 +
 drivers/ddr/imx/phy/Kconfig          |   7 +
 17 files changed, 734 insertions(+), 5 deletions(-)
 create mode 100644 arch/arm/include/asm/mach-imx/qb.h
 create mode 100644 arch/arm/mach-imx/cmd_qb.c
 create mode 100644 arch/arm/mach-imx/imx9/qb.c
 create mode 100644 doc/board/nxp/qb.rst

-- 
2.43.0


^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH 1/5] imx9: Add support for saving DDR training data to NVM
  2026-03-10 11:54 [PATCH 0/5] imx9{4,5}: Add Quickboot support Simona Toaca (OSS)
@ 2026-03-10 11:54 ` Simona Toaca (OSS)
  2026-03-10 11:54 ` [PATCH 2/5] arm: mach-imx: Add command to expose QB functionality Simona Toaca (OSS)
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 15+ messages in thread
From: Simona Toaca (OSS) @ 2026-03-10 11:54 UTC (permalink / raw)
  To: uboot-imx, u-boot
  Cc: Stefano Babic, festevam, peng.fan, alice.guo, ye.li, simona.toaca,
	viorel.suman, marex, ravi, fedor.ross, sebastien.szymanski,
	joao.goncalves, ji.luo, semen.protsenko, sjg, tharvey, ping.bai,
	qijian.guo

From: Simona Toaca <simona.toaca@nxp.com>

DDR training data can be saved to NVM and be available
to OEI at boot time, which will trigger QuickBoot flow.

Supported platforms: iMX94, iMX95
Supported storage types: eMMC, SD, SPI flash.

Signed-off-by: Viorel Suman <viorel.suman@nxp.com>
Signed-off-by: Ye Li <ye.li@nxp.com>
Signed-off-by: Simona Toaca <simona.toaca@nxp.com>
---
 arch/arm/include/asm/arch-imx9/ddr.h |  52 +++-
 arch/arm/include/asm/mach-imx/qb.h   |  13 +
 arch/arm/mach-imx/imx9/Makefile      |   8 +-
 arch/arm/mach-imx/imx9/qb.c          | 430 +++++++++++++++++++++++++++
 arch/arm/mach-imx/imx9/scmi/soc.c    |   9 +
 drivers/ddr/imx/imx9/Kconfig         |   8 +
 drivers/ddr/imx/phy/Kconfig          |   7 +
 7 files changed, 524 insertions(+), 3 deletions(-)
 create mode 100644 arch/arm/include/asm/mach-imx/qb.h
 create mode 100644 arch/arm/mach-imx/imx9/qb.c

diff --git a/arch/arm/include/asm/arch-imx9/ddr.h b/arch/arm/include/asm/arch-imx9/ddr.h
index a8e3f7354c7..a0eea6852d2 100644
--- a/arch/arm/include/asm/arch-imx9/ddr.h
+++ b/arch/arm/include/asm/arch-imx9/ddr.h
@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0+ */
 /*
- * Copyright 2022 NXP
+ * Copyright 2022-2025 NXP
  */
 
 #ifndef __ASM_ARCH_IMX8M_DDR_H
@@ -100,6 +100,56 @@ struct dram_timing_info {
 
 extern struct dram_timing_info dram_timing;
 
+#if IS_ENABLED(CONFIG_IMX95) || IS_ENABLED(CONFIG_IMX94) /* CONFIG_IMX95 || CONFIG_IMX94 */
+#if IS_ENABLED(CONFIG_IMX_SNPS_DDR_PHY_QB_GEN)
+/* Quick Boot related */
+#define DDRPHY_QB_CSR_SIZE	5168
+#define DDRPHY_QB_ACSM_SIZE	4 * 1024
+#define DDRPHY_QB_MSB_SIZE	0x200
+#define DDRPHY_QB_PSTATES	0
+#define DDRPHY_QB_PST_SIZE	DDRPHY_QB_PSTATES * 4 * 1024
+
+/**
+ * This structure needs to be aligned with the one in OEI.
+ */
+struct ddrphy_qb_state {
+	u32 crc;		  /** Used for ensuring integrity in DRAM */
+#define MAC_LENGTH              8 /** 256 bits, 32-bit aligned */
+	u32 mac[MAC_LENGTH];
+	u8 TrainedVREFCA_A0;
+	u8 TrainedVREFCA_A1;
+	u8 TrainedVREFCA_B0;
+	u8 TrainedVREFCA_B1;
+	u8 TrainedVREFDQ_A0;
+	u8 TrainedVREFDQ_A1;
+	u8 TrainedVREFDQ_B0;
+	u8 TrainedVREFDQ_B1;
+	u8 TrainedVREFDQU_A0;
+	u8 TrainedVREFDQU_A1;
+	u8 TrainedVREFDQU_B0;
+	u8 TrainedVREFDQU_B1;
+	u8 TrainedDRAMDFE_A0;
+	u8 TrainedDRAMDFE_A1;
+	u8 TrainedDRAMDFE_B0;
+	u8 TrainedDRAMDFE_B1;
+	u8 TrainedDRAMDCA_A0;
+	u8 TrainedDRAMDCA_A1;
+	u8 TrainedDRAMDCA_B0;
+	u8 TrainedDRAMDCA_B1;
+	u16 QBPllUPllProg0;
+	u16 QBPllUPllProg1;
+	u16 QBPllUPllProg2;
+	u16 QBPllUPllProg3;
+	u16 QBPllCtrl1;
+	u16 QBPllCtrl4;
+	u16 QBPllCtrl5;
+	u16 csr[DDRPHY_QB_CSR_SIZE];
+	u16 acsm[DDRPHY_QB_ACSM_SIZE];
+	u16 pst[DDRPHY_QB_PST_SIZE];
+};
+#endif /* #if IS_ENABLED(CONFIG_IMX_SNPS_DDR_PHY_QB_GEN)  */
+#endif /* #if IS_ENABLED(CONFIG_IMX95) || IS_ENABLED(CONFIG_IMX94) */
+
 void ddr_load_train_firmware(enum fw_type type);
 int ddr_init(struct dram_timing_info *timing_info);
 int ddr_cfg_phy(struct dram_timing_info *timing_info);
diff --git a/arch/arm/include/asm/mach-imx/qb.h b/arch/arm/include/asm/mach-imx/qb.h
new file mode 100644
index 00000000000..5efe68f0a60
--- /dev/null
+++ b/arch/arm/include/asm/mach-imx/qb.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright 2026 NXP
+ */
+
+#ifndef __IMX_QB_H__
+#define __IMX_QB_H__
+
+#include <stdbool.h>
+
+bool qb_check(void);
+int  qb(int qb_dev, int qb_bootdev, bool save);
+#endif
diff --git a/arch/arm/mach-imx/imx9/Makefile b/arch/arm/mach-imx/imx9/Makefile
index 53cc97c6b47..3018d128a36 100644
--- a/arch/arm/mach-imx/imx9/Makefile
+++ b/arch/arm/mach-imx/imx9/Makefile
@@ -1,6 +1,6 @@
 # SPDX-License-Identifier: GPL-2.0+
 #
-# Copyright 2022 NXP
+# Copyright 2022,2026 NXP
 
 obj-y += lowlevel_init.o
 
@@ -12,4 +12,8 @@ endif
 
 ifneq ($(CONFIG_SPL_BUILD),y)
 obj-y += imx_bootaux.o
-endif
\ No newline at end of file
+endif
+
+ifeq ($(CONFIG_IMX_SNPS_DDR_PHY_QB_GEN),y)
+obj-y += qb.o
+endif
diff --git a/arch/arm/mach-imx/imx9/qb.c b/arch/arm/mach-imx/imx9/qb.c
new file mode 100644
index 00000000000..fc01d8e22e9
--- /dev/null
+++ b/arch/arm/mach-imx/imx9/qb.c
@@ -0,0 +1,430 @@
+// SPDX-License-Identifier: GPL-2.0+
+/**
+ * Copyright 2024-2026 NXP
+ */
+#include <dm/device-internal.h>
+#include <errno.h>
+#include <imx_container.h>
+#include <linux/bitfield.h>
+#include <mmc.h>
+#include <spi_flash.h>
+#include <spl.h>
+#include <stdlib.h>
+
+#include <asm/arch/ddr.h>
+#include <asm/mach-imx/boot_mode.h>
+#include <asm/mach-imx/sys_proto.h>
+
+#define QB_STATE_LOAD_SIZE    SZ_64K
+
+#define MMC_DEV		0
+#define QSPI_DEV	1
+#define NAND_DEV	2
+#define QSPI_NOR_DEV	3
+#define ROM_API_DEV	4
+#define RAM_DEV		5
+
+#if CONFIG_IS_ENABLED(DM_MMC) && CONFIG_IS_ENABLED(MMC_WRITE)
+#define QB_MMC_EN	1
+#endif /** DM_MMC && MMC_WRITE */
+#if CONFIG_IS_ENABLED(SPI)
+#define QB_SPI_EN	1
+#endif /** SPI */
+
+#define IMG_FLAGS_IMG_TYPE_MASK   0xFU
+#define IMG_FLAGS_IMG_TYPE(x)     FIELD_GET(IMG_FLAGS_IMG_TYPE_MASK, x)
+
+#define IMG_TYPE_DDR_TDATA_DUMMY  0xDU   /* dummy DDR training data image */
+
+/**
+ * Table used to implement half-byte CRC
+ * Polynomial: 0xEDB88320
+ */
+static const u32 p_table[] = {
+	0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac,
+	0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c,
+	0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c,
+	0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c,
+};
+
+/**
+ * Implement half-byte CRC algorithm
+ */
+static u32 qb_crc32(const void *addr, u32 len)
+{
+	u32 crc = ~0x00, idx, i, val;
+	const u8 *chr = (const u8 *)addr;
+
+	for (i = 0; i < len; i++, chr++) {
+		val = (u32)(*chr);
+
+		idx = (crc ^ (val >> 0)) & 0x0F;
+		crc = p_table[idx] ^ (crc >> 4);
+		idx = (crc ^ (val >> 4)) & 0x0F;
+		crc = p_table[idx] ^ (crc >> 4);
+	}
+
+	return ~crc;
+}
+
+bool qb_check(void)
+{
+	struct ddrphy_qb_state *qb_state;
+	u32 size, crc;
+
+	/**
+	 * Ensure CRC is not empty, the reason is that
+	 * the data is invalidated after first save run
+	 * or after it is overwritten.
+	 */
+	qb_state = (struct ddrphy_qb_state *)CONFIG_SAVED_QB_STATE_BASE;
+	size = sizeof(struct ddrphy_qb_state) - sizeof(qb_state->crc);
+	crc = qb_crc32(qb_state->mac, size);
+
+	if (!qb_state->crc || crc != qb_state->crc)
+		return false;
+
+	return true;
+}
+
+static unsigned long get_boot_device_offset(void *dev, int dev_type, bool bootdev)
+{
+	unsigned long offset = 0;
+	struct mmc *mmc;
+
+	switch (dev_type) {
+	case ROM_API_DEV:
+		offset = (unsigned long)dev;
+		break;
+	case MMC_DEV:
+		mmc = (struct mmc *)dev;
+
+		if (IS_SD(mmc) || mmc->part_config == MMCPART_NOAVAILABLE) {
+			offset = CONTAINER_HDR_MMCSD_OFFSET;
+		} else {
+			u8 part = EXT_CSD_EXTRACT_BOOT_PART(mmc->part_config);
+
+			if (part == EMMC_BOOT_PART_BOOT1 || part == EMMC_BOOT_PART_BOOT2)
+				offset = CONTAINER_HDR_EMMC_OFFSET;
+			else
+				offset = CONTAINER_HDR_MMCSD_OFFSET;
+		}
+		break;
+	case QSPI_DEV:
+		offset = CONTAINER_HDR_QSPI_OFFSET;
+		break;
+	case NAND_DEV:
+		offset = CONTAINER_HDR_NAND_OFFSET;
+		break;
+	case QSPI_NOR_DEV:
+		offset = CONTAINER_HDR_QSPI_OFFSET + 0x08000000;
+		break;
+	case RAM_DEV:
+		offset = (unsigned long)dev + CONTAINER_HDR_MMCSD_OFFSET;
+		break;
+	}
+
+	return offset;
+}
+
+static int parse_container(void *addr, u32 *qb_data_off)
+{
+	struct container_hdr *phdr;
+	struct boot_img_t *img_entry;
+	u8 i = 0;
+	u32 img_type, img_end;
+
+	phdr = (struct container_hdr *)addr;
+	if (phdr->tag != 0x87 || (phdr->version != 0x0 && phdr->version != 0x2))
+		return -1;
+
+	img_entry = (struct boot_img_t *)(addr + sizeof(struct container_hdr));
+	for (i = 0; i < phdr->num_images; i++) {
+		img_type = IMG_FLAGS_IMG_TYPE(img_entry->hab_flags);
+		if (img_type == IMG_TYPE_DDR_TDATA_DUMMY && img_entry->size == 0) {
+			/** Image entry pointing to DDR Training Data */
+			(*qb_data_off) = img_entry->offset;
+			return 0;
+		}
+
+		img_end = img_entry->offset + img_entry->size;
+		if (i + 1 < phdr->num_images) {
+			img_entry++;
+			if (img_end + QB_STATE_LOAD_SIZE == img_entry->offset) {
+				/** hole detected */
+				(*qb_data_off) = img_end;
+				return 0;
+			}
+		}
+	}
+
+	return -1;
+}
+
+static int get_dev_qbdata_offset(void *dev, int dev_type, unsigned long offset, u32 *qbdata_offset)
+{
+	int ret = 0;
+	u16 ctnr_hdr_align = CONTAINER_HDR_ALIGNMENT;
+	void *buf = kmalloc(ctnr_hdr_align, GFP_KERNEL);
+
+	if (!buf) {
+		printf("kmalloc buffer failed\n");
+		return -ENOMEM;
+	}
+
+	switch (dev_type) {
+#ifdef QB_MMC_EN
+	case MMC_DEV:
+		unsigned long count = 0;
+		struct mmc *mmc = (struct mmc *)dev;
+
+		count = blk_dread(mmc_get_blk_desc(mmc),
+				  offset / mmc->read_bl_len,
+				  ctnr_hdr_align / mmc->read_bl_len,
+				  buf);
+		if (count == 0) {
+			printf("Read container image from MMC/SD failed\n");
+			free(buf);
+			return -EIO;
+		}
+		break;
+#endif /** QB_MMC_EN */
+#ifdef QB_SPI_EN
+	case QSPI_DEV:
+		struct spi_flash *flash = (struct spi_flash *)dev;
+
+		ret = spi_flash_read(flash, offset,
+				     ctnr_hdr_align, buf);
+		if (ret) {
+			printf("Read container header from QSPI failed\n");
+			free(buf);
+			return -EIO;
+		}
+		break;
+#endif /** QB_SPI_EN */
+	case QSPI_NOR_DEV:
+	case RAM_DEV:
+		memcpy(buf, (const void *)offset, ctnr_hdr_align);
+		break;
+	default:
+		printf("Support for device %d not enabled\n", dev_type);
+		free(buf);
+		return -EIO;
+	}
+
+	ret = parse_container(buf, qbdata_offset);
+
+	free(buf);
+
+	return ret;
+}
+
+static int get_qbdata_offset(void *dev, int dev_type, u32 *qbdata_offset, bool bootdev)
+{
+	u32 offset = get_boot_device_offset(dev, dev_type, bootdev);
+	u16 ctnr_hdr_align = CONTAINER_HDR_ALIGNMENT;
+	u32 cont_offset;
+	int ret, i;
+
+	for (i = 0; i < 3; i++) {
+		cont_offset = offset + i * ctnr_hdr_align;
+		ret = get_dev_qbdata_offset(dev, dev_type, cont_offset, qbdata_offset);
+		if (ret == 0) {
+			(*qbdata_offset) += cont_offset;
+			break;
+		}
+	}
+
+	return ret;
+}
+
+#ifdef QB_MMC_EN
+static int mmc_get_device_index(u32 dev)
+{
+	switch (dev) {
+	case BOOT_DEVICE_MMC1:
+		return 0;
+	case BOOT_DEVICE_MMC2:
+	case BOOT_DEVICE_MMC2_2:
+		return 1;
+	}
+
+	return -ENODEV;
+}
+
+static int mmc_find_device(struct mmc **mmcp, int mmc_dev)
+{
+	int err;
+
+	err = mmc_init_device(mmc_dev);
+	if (err)
+		return err;
+
+	*mmcp = find_mmc_device(mmc_dev);
+
+	return (*mmcp) ? 0 : -ENODEV;
+}
+
+static int do_qb_mmc(int dev, bool save, bool is_bootdev)
+{
+	struct mmc *mmc;
+	int ret = 0, mmc_dev;
+	bool has_hw_part;
+	u8 orig_part, part;
+	u32 offset;
+	void *buf;
+
+	mmc_dev = mmc_get_device_index(dev);
+	if (mmc_dev < 0)
+		return mmc_dev;
+
+	ret = mmc_find_device(&mmc, mmc_dev);
+	if (ret)
+		return ret;
+
+	if (!mmc->has_init)
+		ret = mmc_init(mmc);
+
+	if (ret)
+		return ret;
+
+	has_hw_part = !IS_SD(mmc) && mmc->part_config != MMCPART_NOAVAILABLE;
+
+	if (has_hw_part) {
+		orig_part = mmc_get_blk_desc(mmc)->hwpart;
+		part = EXT_CSD_EXTRACT_BOOT_PART(mmc->part_config);
+
+		/** Select the partition */
+		ret = mmc_switch_part(mmc, part);
+		if (ret)
+			return ret;
+	}
+
+	ret = get_qbdata_offset(mmc, MMC_DEV, &offset, is_bootdev);
+	if (ret)
+		return ret;
+
+	if (save) {
+		/** QB data is stored in DDR -> can use it as buf */
+		buf = (void *)CONFIG_SAVED_QB_STATE_BASE;
+		ret = blk_dwrite(mmc_get_blk_desc(mmc),
+				 offset / mmc->write_bl_len,
+				 QB_STATE_LOAD_SIZE / mmc->write_bl_len,
+				 (const void *)buf);
+	} else {
+		/** erase */
+		ret = blk_derase(mmc_get_blk_desc(mmc),
+				 offset / mmc->write_bl_len,
+				 QB_STATE_LOAD_SIZE / mmc->write_bl_len);
+	}
+
+	ret = (ret > 0) ? 0 : -1;
+
+	/** Return to original partition */
+	if (has_hw_part)
+		ret |= mmc_switch_part(mmc, orig_part);
+
+	return ret;
+}
+#else
+static int do_qb_mmc(int dev, bool save, bool is_bootdev)
+{
+	printf("Please enable MMC and MMC_WRITE\n");
+
+	return -EOPNOTSUPP;
+}
+#endif /** QB_MMC_EN */
+
+#ifdef QB_SPI_EN
+static int spi_find_device(struct spi_flash **dev)
+{
+	unsigned int sf_bus = CONFIG_SF_DEFAULT_BUS;
+	unsigned int sf_cs = CONFIG_SF_DEFAULT_CS;
+	struct spi_flash *flash;
+	int ret = 0;
+
+	flash = spi_flash_probe(sf_bus, sf_cs,
+				CONFIG_SF_DEFAULT_SPEED,
+				CONFIG_SF_DEFAULT_MODE);
+
+	if (!flash) {
+		puts("SPI probe failed.\n");
+		return -ENODEV;
+	}
+
+	*dev = flash;
+
+	return ret;
+}
+
+static int do_qb_spi(int dev, bool save, bool is_bootdev)
+{
+	int ret = 0;
+	u32 offset;
+	void *buf;
+	struct spi_flash *flash;
+
+	ret = spi_find_device(&flash);
+	if (ret)
+		return -ENODEV;
+
+	ret = get_qbdata_offset(flash, QSPI_DEV, &offset, is_bootdev);
+	if (ret)
+		return ret;
+
+	ret = spi_flash_erase(flash, offset,
+			      QB_STATE_LOAD_SIZE);
+
+	if (!ret && save) {
+		/** QB data is stored in DDR -> can use it as buf */
+		buf = (void *)CONFIG_SAVED_QB_STATE_BASE;
+		ret = spi_flash_write(flash, offset,
+				      QB_STATE_LOAD_SIZE, buf);
+	}
+
+	return ret;
+}
+#else
+static int do_qb_spi(int dev, bool save, bool is_bootdev)
+{
+	printf("Please enable SPI\n");
+
+	return -EOPNOTSUPP;
+}
+#endif /** QB_SPI_EN */
+
+int qb(int qb_dev, int qb_bootdev, bool save)
+{
+	int ret = -1;
+
+	if (save && !qb_check())
+		return ret;
+
+	switch (qb_dev) {
+	case BOOT_DEVICE_MMC1:
+	case BOOT_DEVICE_MMC2:
+	case BOOT_DEVICE_MMC2_2:
+		ret = do_qb_mmc(qb_dev, save, !!(qb_dev == qb_bootdev));
+		break;
+	case BOOT_DEVICE_SPI:
+		ret = do_qb_spi(qb_dev, save, !!(qb_dev == qb_bootdev));
+		break;
+	default:
+		printf("Unsupported quickboot device\n");
+		break;
+	}
+
+	if (ret)
+		return ret;
+
+	if (!save)
+		return 0;
+
+	/**
+	 * invalidate qb_state mem so that at next boot
+	 * the check function will fail and save won't happen
+	 */
+	memset((void *)CONFIG_SAVED_QB_STATE_BASE, 0, sizeof(struct ddrphy_qb_state));
+
+	return 0;
+}
diff --git a/arch/arm/mach-imx/imx9/scmi/soc.c b/arch/arm/mach-imx/imx9/scmi/soc.c
index 17269ddd2fc..eb9bfe19a69 100644
--- a/arch/arm/mach-imx/imx9/scmi/soc.c
+++ b/arch/arm/mach-imx/imx9/scmi/soc.c
@@ -281,6 +281,15 @@ static struct mm_region imx9_mem_map[] = {
 			 PTE_BLOCK_NON_SHARE |
 			 PTE_BLOCK_PXN | PTE_BLOCK_UXN
 	}, {
+#if IS_ENABLED(CONFIG_IMX_SNPS_DDR_PHY_QB_GEN)
+		/* QB data */
+		.virt = CONFIG_SAVED_QB_STATE_BASE,
+		.phys = CONFIG_SAVED_QB_STATE_BASE,
+		.size = 0x200000UL,	/* 2M */
+		.attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) |
+			 PTE_BLOCK_OUTER_SHARE
+	}, {
+#endif /* CONFIG_IMX_SNPS_DDR_PHY_QB_GEN */
 		/* empty entry to split table entry 5 if needed when TEEs are used */
 		0,
 	}, {
diff --git a/drivers/ddr/imx/imx9/Kconfig b/drivers/ddr/imx/imx9/Kconfig
index 0a45340ffb6..7c244ddb5dd 100644
--- a/drivers/ddr/imx/imx9/Kconfig
+++ b/drivers/ddr/imx/imx9/Kconfig
@@ -29,4 +29,12 @@ config SAVED_DRAM_TIMING_BASE
 	  info into memory for low power use.
 	default 0x2051C000
 
+config SAVED_QB_STATE_BASE
+	hex "Define the base address for saved QuickBoot state"
+	depends on IMX_SNPS_DDR_PHY_QB_GEN
+	help
+	  Once DRAM is trained, need to save the dram related timing
+	  info into memory in order to be reachable from U-Boot.
+	default 0x8fe00000
+
 endmenu
diff --git a/drivers/ddr/imx/phy/Kconfig b/drivers/ddr/imx/phy/Kconfig
index d3e589b23c4..e8d0c005689 100644
--- a/drivers/ddr/imx/phy/Kconfig
+++ b/drivers/ddr/imx/phy/Kconfig
@@ -2,3 +2,10 @@ config IMX_SNPS_DDR_PHY
 	bool "i.MX Snopsys DDR PHY"
 	help
 	  Select the DDR PHY driver support on i.MX8M and i.MX9 SOC.
+
+config IMX_SNPS_DDR_PHY_QB_GEN
+	bool "i.MX Synopsys DDR PHY training data saving for QuickBoot mode"
+	depends on IMX94 || IMX95
+	help
+	  Select the DDR PHY training data saving for
+	  QuickBoot support on i.MX9 SOC.
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 2/5] arm: mach-imx: Add command to expose QB functionality
  2026-03-10 11:54 [PATCH 0/5] imx9{4,5}: Add Quickboot support Simona Toaca (OSS)
  2026-03-10 11:54 ` [PATCH 1/5] imx9: Add support for saving DDR training data to NVM Simona Toaca (OSS)
@ 2026-03-10 11:54 ` Simona Toaca (OSS)
  2026-03-10 11:54 ` [PATCH 3/5] imx9: Enable QB data saving for iMX9{4,5} EVK Simona Toaca (OSS)
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 15+ messages in thread
From: Simona Toaca (OSS) @ 2026-03-10 11:54 UTC (permalink / raw)
  To: uboot-imx, u-boot
  Cc: Stefano Babic, festevam, peng.fan, alice.guo, ye.li, simona.toaca,
	viorel.suman, marex, ravi, fedor.ross, sebastien.szymanski,
	joao.goncalves, ji.luo, semen.protsenko, sjg, tharvey, ping.bai,
	qijian.guo

From: Simona Toaca <simona.toaca@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@nxp.com>
Signed-off-by: Ye Li <ye.li@nxp.com>
Signed-off-by: Simona Toaca <simona.toaca@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


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 3/5] imx9: Enable QB data saving for iMX9{4,5} EVK
  2026-03-10 11:54 [PATCH 0/5] imx9{4,5}: Add Quickboot support Simona Toaca (OSS)
  2026-03-10 11:54 ` [PATCH 1/5] imx9: Add support for saving DDR training data to NVM Simona Toaca (OSS)
  2026-03-10 11:54 ` [PATCH 2/5] arm: mach-imx: Add command to expose QB functionality Simona Toaca (OSS)
@ 2026-03-10 11:54 ` Simona Toaca (OSS)
  2026-03-10 11:54 ` [PATCH 4/5] board: nxp: imx9{4,5}_evk: Add qb save option in SPL Simona Toaca (OSS)
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 15+ messages in thread
From: Simona Toaca (OSS) @ 2026-03-10 11:54 UTC (permalink / raw)
  To: uboot-imx, u-boot
  Cc: Stefano Babic, festevam, peng.fan, alice.guo, ye.li, simona.toaca,
	viorel.suman, marex, ravi, fedor.ross, sebastien.szymanski,
	joao.goncalves, ji.luo, semen.protsenko, sjg, tharvey, ping.bai,
	qijian.guo

From: Simona Toaca <simona.toaca@nxp.com>

Add the necessary options for enabling DDR training
data saving to NVM and running QuickBoot flow.

Signed-off-by: Viorel Suman <viorel.suman@nxp.com>
Signed-off-by: Simona Toaca <simona.toaca@nxp.com>
---
 configs/imx943_evk_defconfig      | 1 +
 configs/imx95_15x15_evk_defconfig | 1 +
 configs/imx95_evk.config          | 1 +
 3 files changed, 3 insertions(+)

diff --git a/configs/imx943_evk_defconfig b/configs/imx943_evk_defconfig
index ef4f9a8fcbc..57d4240ba26 100644
--- a/configs/imx943_evk_defconfig
+++ b/configs/imx943_evk_defconfig
@@ -96,6 +96,7 @@ CONFIG_SPL_CLK_CCF=y
 CONFIG_CLK_CCF=y
 CONFIG_CLK_SCMI=y
 CONFIG_SPL_CLK_SCMI=y
+CONFIG_IMX_SNPS_DDR_PHY_QB_GEN=y
 CONFIG_SPL_FIRMWARE=y
 # CONFIG_SCMI_AGENT_SMCCC is not set
 CONFIG_IMX_RGPIO2P=y
diff --git a/configs/imx95_15x15_evk_defconfig b/configs/imx95_15x15_evk_defconfig
index 38a855417d0..03c9b71f8bd 100644
--- a/configs/imx95_15x15_evk_defconfig
+++ b/configs/imx95_15x15_evk_defconfig
@@ -100,6 +100,7 @@ CONFIG_SPL_CLK_CCF=y
 CONFIG_CLK_SCMI=y
 CONFIG_SPL_CLK_SCMI=y
 CONFIG_CLK_IMX95_BLKCTRL=y
+CONFIG_IMX_SNPS_DDR_PHY_QB_GEN=y
 CONFIG_DFU_MMC=y
 CONFIG_DFU_RAM=y
 CONFIG_SPL_FIRMWARE=y
diff --git a/configs/imx95_evk.config b/configs/imx95_evk.config
index 3db583cce59..b34d3f9c935 100644
--- a/configs/imx95_evk.config
+++ b/configs/imx95_evk.config
@@ -101,6 +101,7 @@ CONFIG_SPL_CLK_CCF=y
 CONFIG_CLK_CCF=y
 CONFIG_CLK_SCMI=y
 CONFIG_SPL_CLK_SCMI=y
+CONFIG_IMX_SNPS_DDR_PHY_QB_GEN=y
 CONFIG_DFU_MMC=y
 CONFIG_DFU_RAM=y
 CONFIG_SPL_FIRMWARE=y
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 4/5] board: nxp: imx9{4,5}_evk: Add qb save option in SPL
  2026-03-10 11:54 [PATCH 0/5] imx9{4,5}: Add Quickboot support Simona Toaca (OSS)
                   ` (2 preceding siblings ...)
  2026-03-10 11:54 ` [PATCH 3/5] imx9: Enable QB data saving for iMX9{4,5} EVK Simona Toaca (OSS)
@ 2026-03-10 11:54 ` Simona Toaca (OSS)
  2026-03-10 15:04   ` Emanuele Ghidoli
  2026-03-10 16:12   ` Fabio Estevam
  2026-03-10 11:54 ` [PATCH 5/5] doc: board: nxp: Add Quickboot documentation Simona Toaca (OSS)
  2026-03-11 15:42 ` [PATCH 0/5] imx9{4,5}: Add Quickboot support Marek Vasut
  5 siblings, 2 replies; 15+ messages in thread
From: Simona Toaca (OSS) @ 2026-03-10 11:54 UTC (permalink / raw)
  To: uboot-imx, u-boot
  Cc: Stefano Babic, festevam, peng.fan, alice.guo, ye.li, simona.toaca,
	viorel.suman, marex, ravi, fedor.ross, sebastien.szymanski,
	joao.goncalves, ji.luo, semen.protsenko, sjg, tharvey, ping.bai,
	qijian.guo

From: Simona Toaca <simona.toaca@nxp.com>

Call qb save automatically in the board-specific
spl_board_init(), if SPL_QB option is enabled. This
makes sure qb_save is called before any image loading
is done by the SPL.

Signed-off-by: Simona Toaca <simona.toaca@nxp.com>
---
 arch/arm/include/asm/mach-imx/qb.h | 2 ++
 arch/arm/mach-imx/Kconfig          | 7 +++++++
 arch/arm/mach-imx/imx9/Makefile    | 4 +++-
 arch/arm/mach-imx/imx9/qb.c        | 9 +++++++++
 board/nxp/imx94_evk/spl.c          | 6 +++++-
 board/nxp/imx95_evk/spl.c          | 6 +++++-
 6 files changed, 31 insertions(+), 3 deletions(-)

diff --git a/arch/arm/include/asm/mach-imx/qb.h b/arch/arm/include/asm/mach-imx/qb.h
index 5efe68f0a60..4f923d79e7a 100644
--- a/arch/arm/include/asm/mach-imx/qb.h
+++ b/arch/arm/include/asm/mach-imx/qb.h
@@ -10,4 +10,6 @@
 
 bool qb_check(void);
 int  qb(int qb_dev, int qb_bootdev, bool save);
+void spl_qb_save(void);
+
 #endif
diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig
index 17aad696648..069b25c6241 100644
--- a/arch/arm/mach-imx/Kconfig
+++ b/arch/arm/mach-imx/Kconfig
@@ -71,6 +71,13 @@ config CSF_SIZE
 	  Define the maximum size for Command Sequence File (CSF) binary
 	  this information is used to define the image boot data.
 
+config SPL_IMX_QB
+	bool "Run qb save during SPL"
+	default n
+	depends on SPL && IMX_SNPS_DDR_PHY_QB_GEN && (IMX95 || IMX94)
+	help
+	  Run qb save on bootdev automatically during SPL.
+
 config CMD_IMX_QB
 	bool "Support the 'qb' command"
 	default y
diff --git a/arch/arm/mach-imx/imx9/Makefile b/arch/arm/mach-imx/imx9/Makefile
index 7dee144e0f4..3207013812a 100644
--- a/arch/arm/mach-imx/imx9/Makefile
+++ b/arch/arm/mach-imx/imx9/Makefile
@@ -15,7 +15,9 @@ obj-y += imx_bootaux.o
 endif
 
 ifeq ($(CONFIG_IMX_SNPS_DDR_PHY_QB_GEN),y)
-ifneq ($(CONFIG_XPL_BUILD),y)
+ifeq ($(CONFIG_XPL_BUILD),y)
+obj-$(CONFIG_SPL_IMX_QB) += qb.o
+else
 obj-$(CONFIG_CMD_IMX_QB) += qb.o
 endif
 endif
diff --git a/arch/arm/mach-imx/imx9/qb.c b/arch/arm/mach-imx/imx9/qb.c
index fc01d8e22e9..79573741d39 100644
--- a/arch/arm/mach-imx/imx9/qb.c
+++ b/arch/arm/mach-imx/imx9/qb.c
@@ -428,3 +428,12 @@ int qb(int qb_dev, int qb_bootdev, bool save)
 
 	return 0;
 }
+
+void spl_qb_save(void)
+{
+	int dev = spl_boot_device();
+
+	/** Save QB data on current boot device */
+	if (qb(dev, dev, true))
+		printf("QB save failed\n");
+}
diff --git a/board/nxp/imx94_evk/spl.c b/board/nxp/imx94_evk/spl.c
index cc5b7f9ef0f..1d25795eb17 100644
--- a/board/nxp/imx94_evk/spl.c
+++ b/board/nxp/imx94_evk/spl.c
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0+
 /*
- * Copyright 2025 NXP
+ * Copyright 2025-2026 NXP
  */
 
 #include <hang.h>
@@ -14,6 +14,7 @@
 #include <asm/arch/sys_proto.h>
 #include <asm/mach-imx/boot_mode.h>
 #include <asm/mach-imx/ele_api.h>
+#include <asm/mach-imx/qb.h>
 
 DECLARE_GLOBAL_DATA_PTR;
 
@@ -44,6 +45,9 @@ void spl_board_init(void)
 	ret = ele_start_rng();
 	if (ret)
 		printf("Fail to start RNG: %d\n", ret);
+
+	if (IS_ENABLED(CONFIG_SPL_IMX_QB))
+		spl_qb_save();
 }
 
 /* SCMI support by default */
diff --git a/board/nxp/imx95_evk/spl.c b/board/nxp/imx95_evk/spl.c
index 761a1a4a0f6..35e4458f2b7 100644
--- a/board/nxp/imx95_evk/spl.c
+++ b/board/nxp/imx95_evk/spl.c
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0+
 /*
- * Copyright 2025 NXP
+ * Copyright 2025-2026 NXP
  */
 
 #include <hang.h>
@@ -13,6 +13,7 @@
 #include <asm/arch/sys_proto.h>
 #include <asm/mach-imx/boot_mode.h>
 #include <asm/mach-imx/ele_api.h>
+#include <asm/mach-imx/qb.h>
 
 DECLARE_GLOBAL_DATA_PTR;
 
@@ -41,6 +42,9 @@ void spl_board_init(void)
 	ret = ele_start_rng();
 	if (ret)
 		printf("Fail to start RNG: %d\n", ret);
+
+	if (IS_ENABLED(CONFIG_SPL_IMX_QB))
+		spl_qb_save();
 }
 
 void board_init_f(ulong dummy)
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 5/5] doc: board: nxp: Add Quickboot documentation
  2026-03-10 11:54 [PATCH 0/5] imx9{4,5}: Add Quickboot support Simona Toaca (OSS)
                   ` (3 preceding siblings ...)
  2026-03-10 11:54 ` [PATCH 4/5] board: nxp: imx9{4,5}_evk: Add qb save option in SPL Simona Toaca (OSS)
@ 2026-03-10 11:54 ` Simona Toaca (OSS)
  2026-03-11 15:42 ` [PATCH 0/5] imx9{4,5}: Add Quickboot support Marek Vasut
  5 siblings, 0 replies; 15+ messages in thread
From: Simona Toaca (OSS) @ 2026-03-10 11:54 UTC (permalink / raw)
  To: uboot-imx, u-boot
  Cc: Stefano Babic, festevam, peng.fan, alice.guo, ye.li, simona.toaca,
	viorel.suman, marex, ravi, fedor.ross, sebastien.szymanski,
	joao.goncalves, ji.luo, semen.protsenko, sjg, tharvey, ping.bai,
	qijian.guo

From: Simona Toaca <simona.toaca@nxp.com>

Add instructions on how to use U-Boot to save
DDR training data to NVM.

Signed-off-by: Simona Toaca <simona.toaca@nxp.com>
---
 doc/board/nxp/index.rst |  1 +
 doc/board/nxp/qb.rst    | 33 +++++++++++++++++++++++++++++++++
 2 files changed, 34 insertions(+)
 create mode 100644 doc/board/nxp/qb.rst

diff --git a/doc/board/nxp/index.rst b/doc/board/nxp/index.rst
index 01d3468a47d..7e3e5e29880 100644
--- a/doc/board/nxp/index.rst
+++ b/doc/board/nxp/index.rst
@@ -29,3 +29,4 @@ NXP Semiconductors
    mx6ullevk
    rproc
    psb
+   qb
diff --git a/doc/board/nxp/qb.rst b/doc/board/nxp/qb.rst
new file mode 100644
index 00000000000..ba673ecff24
--- /dev/null
+++ b/doc/board/nxp/qb.rst
@@ -0,0 +1,33 @@
+.. SPDX-License-Identifier: GPL-2.0+
+   Copyright 2026 NXP
+
+DDR QuickBoot flow
+------------------
+
+Some NXP boards (which use OEI - iMX943, iMX95) support saving DDR
+training data to non-volatile memory, which is then available to OEI
+at next cold reboot. OEI uses the saved data to run Quickboot flow and
+avoid training the DDR again. This significantly reduces the boot time.
+
+There are 2 ways to save this data:
+
+1. automatically, in SPL (by enabling CONFIG_SPL_IMX_QB)
+
+- this will save the data on the current boot device (e.g. SD)
+- other configs specific to the boot device need to be enabled (CONFIG_SPL_MMC_WRITE for saving to eMMC/SD)
+
+2. using qb command in U-Boot console (by enabling CONFIG_CMD_IMX_QB)
+
+- supports saving on the current boot device, or on another, specified device.
+- if flashing via uuu, the command can be added in an uuu script (boot device needs to be specified)
+- after save, the data in volatile memory is erased, so saving can happen only once per DDR training
+
+::
+
+        # To save/erase on current boot device
+        => qb save/erase
+
+        # To save/erase on other boot device
+        => qb save/erase mmc 0 # eMMC
+        => qb save/erase mmc 1 # SD
+        => qb save/erase spi   # NOR SPI
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* Re: [PATCH 4/5] board: nxp: imx9{4,5}_evk: Add qb save option in SPL
  2026-03-10 11:54 ` [PATCH 4/5] board: nxp: imx9{4,5}_evk: Add qb save option in SPL Simona Toaca (OSS)
@ 2026-03-10 15:04   ` Emanuele Ghidoli
  2026-03-10 15:27     ` Simona Toaca
  2026-03-10 16:12   ` Fabio Estevam
  1 sibling, 1 reply; 15+ messages in thread
From: Emanuele Ghidoli @ 2026-03-10 15:04 UTC (permalink / raw)
  To: Simona Toaca (OSS), uboot-imx, u-boot
  Cc: Stefano Babic, festevam, peng.fan, alice.guo, ye.li, simona.toaca,
	viorel.suman, marex, ravi, fedor.ross, sebastien.szymanski,
	joao.goncalves, ji.luo, semen.protsenko, sjg, tharvey, ping.bai,
	qijian.guo



On 3/10/26 12:54, Simona Toaca (OSS) wrote:
> From: Simona Toaca <simona.toaca@nxp.com>
> 
> Call qb save automatically in the board-specific
> spl_board_init(), if SPL_QB option is enabled. This
> makes sure qb_save is called before any image loading
> is done by the SPL.
> 
> Signed-off-by: Simona Toaca <simona.toaca@nxp.com>
> ---
>  arch/arm/include/asm/mach-imx/qb.h | 2 ++
>  arch/arm/mach-imx/Kconfig          | 7 +++++++
>  arch/arm/mach-imx/imx9/Makefile    | 4 +++-
>  arch/arm/mach-imx/imx9/qb.c        | 9 +++++++++
>  board/nxp/imx94_evk/spl.c          | 6 +++++-
>  board/nxp/imx95_evk/spl.c          | 6 +++++-
>  6 files changed, 31 insertions(+), 3 deletions(-)
> 
> diff --git a/arch/arm/include/asm/mach-imx/qb.h b/arch/arm/include/asm/mach-imx/qb.h
> index 5efe68f0a60..4f923d79e7a 100644
> --- a/arch/arm/include/asm/mach-imx/qb.h
> +++ b/arch/arm/include/asm/mach-imx/qb.h
> @@ -10,4 +10,6 @@
>  
>  bool qb_check(void);
>  int  qb(int qb_dev, int qb_bootdev, bool save);
> +void spl_qb_save(void);
> +
>  #endif
> diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig
> index 17aad696648..069b25c6241 100644
> --- a/arch/arm/mach-imx/Kconfig
> +++ b/arch/arm/mach-imx/Kconfig
> @@ -71,6 +71,13 @@ config CSF_SIZE
>  	  Define the maximum size for Command Sequence File (CSF) binary
>  	  this information is used to define the image boot data.
>  
> +config SPL_IMX_QB
> +	bool "Run qb save during SPL"
> +	default n
> +	depends on SPL && IMX_SNPS_DDR_PHY_QB_GEN && (IMX95 || IMX94)
> +	help
> +	  Run qb save on bootdev automatically during SPL.
> +
>  config CMD_IMX_QB
>  	bool "Support the 'qb' command"
>  	default y
> diff --git a/arch/arm/mach-imx/imx9/Makefile b/arch/arm/mach-imx/imx9/Makefile
> index 7dee144e0f4..3207013812a 100644
> --- a/arch/arm/mach-imx/imx9/Makefile
> +++ b/arch/arm/mach-imx/imx9/Makefile
> @@ -15,7 +15,9 @@ obj-y += imx_bootaux.o
>  endif
>  
>  ifeq ($(CONFIG_IMX_SNPS_DDR_PHY_QB_GEN),y)
> -ifneq ($(CONFIG_XPL_BUILD),y)
> +ifeq ($(CONFIG_XPL_BUILD),y)
> +obj-$(CONFIG_SPL_IMX_QB) += qb.o
> +else
>  obj-$(CONFIG_CMD_IMX_QB) += qb.o
>  endif
>  endif
> diff --git a/arch/arm/mach-imx/imx9/qb.c b/arch/arm/mach-imx/imx9/qb.c
> index fc01d8e22e9..79573741d39 100644
> --- a/arch/arm/mach-imx/imx9/qb.c
> +++ b/arch/arm/mach-imx/imx9/qb.c
> @@ -428,3 +428,12 @@ int qb(int qb_dev, int qb_bootdev, bool save)
>  
>  	return 0;
>  }
> +
> +void spl_qb_save(void)
> +{
> +	int dev = spl_boot_device();
> +
> +	/** Save QB data on current boot device */
> +	if (qb(dev, dev, true))
> +		printf("QB save failed\n");
> +}
> diff --git a/board/nxp/imx94_evk/spl.c b/board/nxp/imx94_evk/spl.c
> index cc5b7f9ef0f..1d25795eb17 100644
> --- a/board/nxp/imx94_evk/spl.c
> +++ b/board/nxp/imx94_evk/spl.c
> @@ -1,6 +1,6 @@
>  // SPDX-License-Identifier: GPL-2.0+
>  /*
> - * Copyright 2025 NXP
> + * Copyright 2025-2026 NXP
>   */
>  
>  #include <hang.h>
> @@ -14,6 +14,7 @@
>  #include <asm/arch/sys_proto.h>
>  #include <asm/mach-imx/boot_mode.h>
>  #include <asm/mach-imx/ele_api.h>
> +#include <asm/mach-imx/qb.h>
>  
>  DECLARE_GLOBAL_DATA_PTR;
>  
> @@ -44,6 +45,9 @@ void spl_board_init(void)
>  	ret = ele_start_rng();
>  	if (ret)
>  		printf("Fail to start RNG: %d\n", ret);
> +
> +	if (IS_ENABLED(CONFIG_SPL_IMX_QB))
> +		spl_qb_save();
Does it write to MMC/NOR/SD at every boot? Is that necessary if a quick boot
was performed and the data has not changed?
>  }
>  
>  /* SCMI support by default */
> diff --git a/board/nxp/imx95_evk/spl.c b/board/nxp/imx95_evk/spl.c
> index 761a1a4a0f6..35e4458f2b7 100644
> --- a/board/nxp/imx95_evk/spl.c
> +++ b/board/nxp/imx95_evk/spl.c
> @@ -1,6 +1,6 @@
>  // SPDX-License-Identifier: GPL-2.0+
>  /*
> - * Copyright 2025 NXP
> + * Copyright 2025-2026 NXP
>   */
>  
>  #include <hang.h>
> @@ -13,6 +13,7 @@
>  #include <asm/arch/sys_proto.h>
>  #include <asm/mach-imx/boot_mode.h>
>  #include <asm/mach-imx/ele_api.h>
> +#include <asm/mach-imx/qb.h>
>  
>  DECLARE_GLOBAL_DATA_PTR;
>  
> @@ -41,6 +42,9 @@ void spl_board_init(void)
>  	ret = ele_start_rng();
>  	if (ret)
>  		printf("Fail to start RNG: %d\n", ret);
> +
> +	if (IS_ENABLED(CONFIG_SPL_IMX_QB))
> +		spl_qb_save();
>  }
>  
>  void board_init_f(ulong dummy)

Kind regards,
Emanuele


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 4/5] board: nxp: imx9{4,5}_evk: Add qb save option in SPL
  2026-03-10 15:04   ` Emanuele Ghidoli
@ 2026-03-10 15:27     ` Simona Toaca
  2026-03-11  9:14       ` Francesco Dolcini
  0 siblings, 1 reply; 15+ messages in thread
From: Simona Toaca @ 2026-03-10 15:27 UTC (permalink / raw)
  To: Emanuele Ghidoli
  Cc: uboot-imx, u-boot, Stefano Babic, festevam, peng.fan, alice.guo,
	ye.li, simona.toaca, viorel.suman, marex, ravi, fedor.ross,
	sebastien.szymanski, joao.goncalves, ji.luo, semen.protsenko, sjg,
	tharvey, ping.bai, qijian.guo

Hi Emanuele,

On Tue, Mar 10, 2026 at 04:04:19PM +0100, Emanuele Ghidoli wrote:
> 
> 
> On 3/10/26 12:54, Simona Toaca (OSS) wrote:
> > From: Simona Toaca <simona.toaca@nxp.com>
> > 
> > Call qb save automatically in the board-specific
> > spl_board_init(), if SPL_QB option is enabled. This
> > makes sure qb_save is called before any image loading
> > is done by the SPL.
> > 
> > Signed-off-by: Simona Toaca <simona.toaca@nxp.com>
> > ---
> >  arch/arm/include/asm/mach-imx/qb.h | 2 ++
> >  arch/arm/mach-imx/Kconfig          | 7 +++++++
> >  arch/arm/mach-imx/imx9/Makefile    | 4 +++-
> >  arch/arm/mach-imx/imx9/qb.c        | 9 +++++++++
> >  board/nxp/imx94_evk/spl.c          | 6 +++++-
> >  board/nxp/imx95_evk/spl.c          | 6 +++++-
> >  6 files changed, 31 insertions(+), 3 deletions(-)
> > 
> > diff --git a/arch/arm/include/asm/mach-imx/qb.h b/arch/arm/include/asm/mach-imx/qb.h
> > index 5efe68f0a60..4f923d79e7a 100644
> > --- a/arch/arm/include/asm/mach-imx/qb.h
> > +++ b/arch/arm/include/asm/mach-imx/qb.h
> > @@ -10,4 +10,6 @@
> >  
> >  bool qb_check(void);
> >  int  qb(int qb_dev, int qb_bootdev, bool save);
> > +void spl_qb_save(void);
> > +
> >  #endif
> > diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig
> > index 17aad696648..069b25c6241 100644
> > --- a/arch/arm/mach-imx/Kconfig
> > +++ b/arch/arm/mach-imx/Kconfig
> > @@ -71,6 +71,13 @@ config CSF_SIZE
> >  	  Define the maximum size for Command Sequence File (CSF) binary
> >  	  this information is used to define the image boot data.
> >  
> > +config SPL_IMX_QB
> > +	bool "Run qb save during SPL"
> > +	default n
> > +	depends on SPL && IMX_SNPS_DDR_PHY_QB_GEN && (IMX95 || IMX94)
> > +	help
> > +	  Run qb save on bootdev automatically during SPL.
> > +
> >  config CMD_IMX_QB
> >  	bool "Support the 'qb' command"
> >  	default y
> > diff --git a/arch/arm/mach-imx/imx9/Makefile b/arch/arm/mach-imx/imx9/Makefile
> > index 7dee144e0f4..3207013812a 100644
> > --- a/arch/arm/mach-imx/imx9/Makefile
> > +++ b/arch/arm/mach-imx/imx9/Makefile
> > @@ -15,7 +15,9 @@ obj-y += imx_bootaux.o
> >  endif
> >  
> >  ifeq ($(CONFIG_IMX_SNPS_DDR_PHY_QB_GEN),y)
> > -ifneq ($(CONFIG_XPL_BUILD),y)
> > +ifeq ($(CONFIG_XPL_BUILD),y)
> > +obj-$(CONFIG_SPL_IMX_QB) += qb.o
> > +else
> >  obj-$(CONFIG_CMD_IMX_QB) += qb.o
> >  endif
> >  endif
> > diff --git a/arch/arm/mach-imx/imx9/qb.c b/arch/arm/mach-imx/imx9/qb.c
> > index fc01d8e22e9..79573741d39 100644
> > --- a/arch/arm/mach-imx/imx9/qb.c
> > +++ b/arch/arm/mach-imx/imx9/qb.c
> > @@ -428,3 +428,12 @@ int qb(int qb_dev, int qb_bootdev, bool save)
> >  
> >  	return 0;
> >  }
> > +
> > +void spl_qb_save(void)
> > +{
> > +	int dev = spl_boot_device();
> > +
> > +	/** Save QB data on current boot device */
> > +	if (qb(dev, dev, true))
> > +		printf("QB save failed\n");
> > +}
> > diff --git a/board/nxp/imx94_evk/spl.c b/board/nxp/imx94_evk/spl.c
> > index cc5b7f9ef0f..1d25795eb17 100644
> > --- a/board/nxp/imx94_evk/spl.c
> > +++ b/board/nxp/imx94_evk/spl.c
> > @@ -1,6 +1,6 @@
> >  // SPDX-License-Identifier: GPL-2.0+
> >  /*
> > - * Copyright 2025 NXP
> > + * Copyright 2025-2026 NXP
> >   */
> >  
> >  #include <hang.h>
> > @@ -14,6 +14,7 @@
> >  #include <asm/arch/sys_proto.h>
> >  #include <asm/mach-imx/boot_mode.h>
> >  #include <asm/mach-imx/ele_api.h>
> > +#include <asm/mach-imx/qb.h>
> >  
> >  DECLARE_GLOBAL_DATA_PTR;
> >  
> > @@ -44,6 +45,9 @@ void spl_board_init(void)
> >  	ret = ele_start_rng();
> >  	if (ret)
> >  		printf("Fail to start RNG: %d\n", ret);
> > +
> > +	if (IS_ENABLED(CONFIG_SPL_IMX_QB))
> > +		spl_qb_save();
> Does it write to MMC/NOR/SD at every boot? Is that necessary if a quick boot
> was performed and the data has not changed?
> >  }
No, the qb() method calls qb_check, which returns true only if OEI
saved valid data to volatile memory (meaning it has run Training flow).
So, if Quickboot flow is run, the data in volatile memory is not valid,
qb_check fails -> qb returns and does not write anything to the NVM.

Also, another thing to keep in mind is that after a successfull
qb save, the data in volatile memory is invalidated -> qb check will
fail as well even if OEI doesn't run (so on a warm reboot).

> >  
> >  /* SCMI support by default */
> > diff --git a/board/nxp/imx95_evk/spl.c b/board/nxp/imx95_evk/spl.c
> > index 761a1a4a0f6..35e4458f2b7 100644
> > --- a/board/nxp/imx95_evk/spl.c
> > +++ b/board/nxp/imx95_evk/spl.c
> > @@ -1,6 +1,6 @@
> >  // SPDX-License-Identifier: GPL-2.0+
> >  /*
> > - * Copyright 2025 NXP
> > + * Copyright 2025-2026 NXP
> >   */
> >  
> >  #include <hang.h>
> > @@ -13,6 +13,7 @@
> >  #include <asm/arch/sys_proto.h>
> >  #include <asm/mach-imx/boot_mode.h>
> >  #include <asm/mach-imx/ele_api.h>
> > +#include <asm/mach-imx/qb.h>
> >  
> >  DECLARE_GLOBAL_DATA_PTR;
> >  
> > @@ -41,6 +42,9 @@ void spl_board_init(void)
> >  	ret = ele_start_rng();
> >  	if (ret)
> >  		printf("Fail to start RNG: %d\n", ret);
> > +
> > +	if (IS_ENABLED(CONFIG_SPL_IMX_QB))
> > +		spl_qb_save();
> >  }
> >  
> >  void board_init_f(ulong dummy)
> 
> Kind regards,
> Emanuele
>
Best regards,
Simona

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 4/5] board: nxp: imx9{4,5}_evk: Add qb save option in SPL
  2026-03-10 11:54 ` [PATCH 4/5] board: nxp: imx9{4,5}_evk: Add qb save option in SPL Simona Toaca (OSS)
  2026-03-10 15:04   ` Emanuele Ghidoli
@ 2026-03-10 16:12   ` Fabio Estevam
  2026-03-11  7:54     ` Simona Toaca
  1 sibling, 1 reply; 15+ messages in thread
From: Fabio Estevam @ 2026-03-10 16:12 UTC (permalink / raw)
  To: Simona Toaca (OSS)
  Cc: uboot-imx, u-boot, Stefano Babic, peng.fan, alice.guo, ye.li,
	simona.toaca, viorel.suman, marex, ravi, fedor.ross,
	sebastien.szymanski, joao.goncalves, ji.luo, semen.protsenko, sjg,
	tharvey, ping.bai, qijian.guo

On Tue, Mar 10, 2026 at 8:44 AM Simona Toaca (OSS)
<simona.toaca@oss.nxp.com> wrote:
>
> From: Simona Toaca <simona.toaca@nxp.com>
>
> Call qb save automatically in the board-specific
> spl_board_init(), if SPL_QB option is enabled. This
> makes sure qb_save is called before any image loading
> is done by the SPL.

Why do we need this? What does "QB" mean?

Please improve the commit message by providing more context and an
explanation of what you want to achieve.

> Signed-off-by: Simona Toaca <simona.toaca@nxp.com>
> ---
>  arch/arm/include/asm/mach-imx/qb.h | 2 ++
>  arch/arm/mach-imx/Kconfig          | 7 +++++++
>  arch/arm/mach-imx/imx9/Makefile    | 4 +++-
>  arch/arm/mach-imx/imx9/qb.c        | 9 +++++++++
>  board/nxp/imx94_evk/spl.c          | 6 +++++-
>  board/nxp/imx95_evk/spl.c          | 6 +++++-
>  6 files changed, 31 insertions(+), 3 deletions(-)
>
> diff --git a/arch/arm/include/asm/mach-imx/qb.h b/arch/arm/include/asm/mach-imx/qb.h
> index 5efe68f0a60..4f923d79e7a 100644
> --- a/arch/arm/include/asm/mach-imx/qb.h
> +++ b/arch/arm/include/asm/mach-imx/qb.h
> @@ -10,4 +10,6 @@
>
>  bool qb_check(void);
>  int  qb(int qb_dev, int qb_bootdev, bool save);
> +void spl_qb_save(void);
> +
>  #endif
> diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig
> index 17aad696648..069b25c6241 100644
> --- a/arch/arm/mach-imx/Kconfig
> +++ b/arch/arm/mach-imx/Kconfig
> @@ -71,6 +71,13 @@ config CSF_SIZE
>           Define the maximum size for Command Sequence File (CSF) binary
>           this information is used to define the image boot data.
>
> +config SPL_IMX_QB
> +       bool "Run qb save during SPL"

What does "qb save" mean? Please explain here and in the commit message.

How does a user know if "qb save" should run in SPL or not?

> +       default n

No need to pass "default n". Drop it.

> +void spl_qb_save(void)
> +{
> +       int dev = spl_boot_device();
> +
> +       /** Save QB data on current boot device */

Please use a single /* instead of /**

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 4/5] board: nxp: imx9{4,5}_evk: Add qb save option in SPL
  2026-03-10 16:12   ` Fabio Estevam
@ 2026-03-11  7:54     ` Simona Toaca
  0 siblings, 0 replies; 15+ messages in thread
From: Simona Toaca @ 2026-03-11  7:54 UTC (permalink / raw)
  To: Fabio Estevam
  Cc: uboot-imx, u-boot, Stefano Babic, peng.fan, alice.guo, ye.li,
	simona.toaca, viorel.suman, marex, ravi, fedor.ross,
	sebastien.szymanski, joao.goncalves, ji.luo, semen.protsenko, sjg,
	tharvey, ping.bai, qijian.guo

Hi Fabio,

Thank you for the review, I will fix these issues in
the next version.

Best regards,
Simona

On Tue, Mar 10, 2026 at 01:12:28PM -0300, Fabio Estevam wrote:
> On Tue, Mar 10, 2026 at 8:44 AM Simona Toaca (OSS)
> <simona.toaca@oss.nxp.com> wrote:
> >
> > From: Simona Toaca <simona.toaca@nxp.com>
> >
> > Call qb save automatically in the board-specific
> > spl_board_init(), if SPL_QB option is enabled. This
> > makes sure qb_save is called before any image loading
> > is done by the SPL.
> 
> Why do we need this? What does "QB" mean?
> 
> Please improve the commit message by providing more context and an
> explanation of what you want to achieve.
> 
> > Signed-off-by: Simona Toaca <simona.toaca@nxp.com>
> > ---
> >  arch/arm/include/asm/mach-imx/qb.h | 2 ++
> >  arch/arm/mach-imx/Kconfig          | 7 +++++++
> >  arch/arm/mach-imx/imx9/Makefile    | 4 +++-
> >  arch/arm/mach-imx/imx9/qb.c        | 9 +++++++++
> >  board/nxp/imx94_evk/spl.c          | 6 +++++-
> >  board/nxp/imx95_evk/spl.c          | 6 +++++-
> >  6 files changed, 31 insertions(+), 3 deletions(-)
> >
> > diff --git a/arch/arm/include/asm/mach-imx/qb.h b/arch/arm/include/asm/mach-imx/qb.h
> > index 5efe68f0a60..4f923d79e7a 100644
> > --- a/arch/arm/include/asm/mach-imx/qb.h
> > +++ b/arch/arm/include/asm/mach-imx/qb.h
> > @@ -10,4 +10,6 @@
> >
> >  bool qb_check(void);
> >  int  qb(int qb_dev, int qb_bootdev, bool save);
> > +void spl_qb_save(void);
> > +
> >  #endif
> > diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig
> > index 17aad696648..069b25c6241 100644
> > --- a/arch/arm/mach-imx/Kconfig
> > +++ b/arch/arm/mach-imx/Kconfig
> > @@ -71,6 +71,13 @@ config CSF_SIZE
> >           Define the maximum size for Command Sequence File (CSF) binary
> >           this information is used to define the image boot data.
> >
> > +config SPL_IMX_QB
> > +       bool "Run qb save during SPL"
> 
> What does "qb save" mean? Please explain here and in the commit message.
> 
> How does a user know if "qb save" should run in SPL or not?
> 
> > +       default n
> 
> No need to pass "default n". Drop it.
> 
> > +void spl_qb_save(void)
> > +{
> > +       int dev = spl_boot_device();
> > +
> > +       /** Save QB data on current boot device */
> 
> Please use a single /* instead of /**

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 4/5] board: nxp: imx9{4,5}_evk: Add qb save option in SPL
  2026-03-10 15:27     ` Simona Toaca
@ 2026-03-11  9:14       ` Francesco Dolcini
  2026-03-11 14:58         ` Simona Toaca
  0 siblings, 1 reply; 15+ messages in thread
From: Francesco Dolcini @ 2026-03-11  9:14 UTC (permalink / raw)
  To: Emanuele Ghidoli, Simona Toaca
  Cc: uboot-imx, u-boot, Stefano Babic, festevam, peng.fan, alice.guo,
	ye.li, simona.toaca, viorel.suman, marex, ravi, fedor.ross,
	sebastien.szymanski, joao.goncalves, ji.luo, semen.protsenko, sjg,
	tharvey, ping.bai, qijian.guo

On Tue, Mar 10, 2026 at 05:27:45PM +0200, Simona Toaca wrote:
> On Tue, Mar 10, 2026 at 04:04:19PM +0100, Emanuele Ghidoli wrote:
> > On 3/10/26 12:54, Simona Toaca (OSS) wrote:
> > > From: Simona Toaca <simona.toaca@nxp.com>
> > > 
> > > Call qb save automatically in the board-specific
> > > spl_board_init(), if SPL_QB option is enabled. This
> > > makes sure qb_save is called before any image loading
> > > is done by the SPL.
> > > 
> > > Signed-off-by: Simona Toaca <simona.toaca@nxp.com>

...

> > > diff --git a/board/nxp/imx94_evk/spl.c b/board/nxp/imx94_evk/spl.c
> > > index cc5b7f9ef0f..1d25795eb17 100644
> > > --- a/board/nxp/imx94_evk/spl.c
> > > +++ b/board/nxp/imx94_evk/spl.c
> > > @@ -44,6 +45,9 @@ void spl_board_init(void)
> > >  	ret = ele_start_rng();
> > >  	if (ret)
> > >  		printf("Fail to start RNG: %d\n", ret);
> > > +
> > > +	if (IS_ENABLED(CONFIG_SPL_IMX_QB))
> > > +		spl_qb_save();
> > Does it write to MMC/NOR/SD at every boot? Is that necessary if a quick boot
> > was performed and the data has not changed?
> > >  }
> No, the qb() method calls qb_check, which returns true only if OEI
> saved valid data to volatile memory (meaning it has run Training flow).
> So, if Quickboot flow is run, the data in volatile memory is not valid,
> qb_check fails -> qb returns and does not write anything to the NVM.
> 
> Also, another thing to keep in mind is that after a successfull
> qb save, the data in volatile memory is invalidated -> qb check will
> fail as well even if OEI doesn't run (so on a warm reboot).

Do you think that this is something that should be generally enabled on
any i.MX95 boards? Emanuele?

Francesco


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 4/5] board: nxp: imx9{4,5}_evk: Add qb save option in SPL
  2026-03-11  9:14       ` Francesco Dolcini
@ 2026-03-11 14:58         ` Simona Toaca
  0 siblings, 0 replies; 15+ messages in thread
From: Simona Toaca @ 2026-03-11 14:58 UTC (permalink / raw)
  To: Francesco Dolcini
  Cc: Emanuele Ghidoli, uboot-imx, u-boot, Stefano Babic, festevam,
	peng.fan, alice.guo, ye.li, simona.toaca, viorel.suman, marex,
	ravi, fedor.ross, sebastien.szymanski, joao.goncalves, ji.luo,
	semen.protsenko, sjg, tharvey, ping.bai, qijian.guo

Hi Francesco,

On Wed, Mar 11, 2026 at 10:14:19AM +0100, Francesco Dolcini wrote:
> On Tue, Mar 10, 2026 at 05:27:45PM +0200, Simona Toaca wrote:
> > On Tue, Mar 10, 2026 at 04:04:19PM +0100, Emanuele Ghidoli wrote:
> > > On 3/10/26 12:54, Simona Toaca (OSS) wrote:
> > > > From: Simona Toaca <simona.toaca@nxp.com>
> > > > 
> > > > Call qb save automatically in the board-specific
> > > > spl_board_init(), if SPL_QB option is enabled. This
> > > > makes sure qb_save is called before any image loading
> > > > is done by the SPL.
> > > > 
> > > > Signed-off-by: Simona Toaca <simona.toaca@nxp.com>
> 
> ...
> 
> > > > diff --git a/board/nxp/imx94_evk/spl.c b/board/nxp/imx94_evk/spl.c
> > > > index cc5b7f9ef0f..1d25795eb17 100644
> > > > --- a/board/nxp/imx94_evk/spl.c
> > > > +++ b/board/nxp/imx94_evk/spl.c
> > > > @@ -44,6 +45,9 @@ void spl_board_init(void)
> > > >  	ret = ele_start_rng();
> > > >  	if (ret)
> > > >  		printf("Fail to start RNG: %d\n", ret);
> > > > +
> > > > +	if (IS_ENABLED(CONFIG_SPL_IMX_QB))
> > > > +		spl_qb_save();
> > > Does it write to MMC/NOR/SD at every boot? Is that necessary if a quick boot
> > > was performed and the data has not changed?
> > > >  }
> > No, the qb() method calls qb_check, which returns true only if OEI
> > saved valid data to volatile memory (meaning it has run Training flow).
> > So, if Quickboot flow is run, the data in volatile memory is not valid,
> > qb_check fails -> qb returns and does not write anything to the NVM.
> > 
> > Also, another thing to keep in mind is that after a successfull
> > qb save, the data in volatile memory is invalidated -> qb check will
> > fail as well even if OEI doesn't run (so on a warm reboot).
> 
> Do you think that this is something that should be generally enabled on
> any i.MX95 boards? Emanuele?
> 
> Francesco
> 

I think it depends on the usecase.

Enabling QB in SPL is useful for:
- automating the save
- being able to skip U-Boot proper

Enabling CMD_IMX_QB:
- lets the user save the data during flashing
- lets the user save the data to a different
boot device.

If SPL QB is enabled, the qb save command is only
useful in the uuu script.

I will add these comments to the documentation as well.

Another thing is that if SPL_IMX_QB is enabled, other config
options also have to be enabled depending on the boot device
(for example, as I have also written in the documentation,
SPL_MMC_WRITE for MMC boot devices).

I should mention that this functionality is tested
on latest iMX95 rev: B0 (if talking about 95) and
has to be in sync with OEI.

Best regards,
Simona

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 0/5] imx9{4,5}: Add Quickboot support
  2026-03-10 11:54 [PATCH 0/5] imx9{4,5}: Add Quickboot support Simona Toaca (OSS)
                   ` (4 preceding siblings ...)
  2026-03-10 11:54 ` [PATCH 5/5] doc: board: nxp: Add Quickboot documentation Simona Toaca (OSS)
@ 2026-03-11 15:42 ` Marek Vasut
  2026-03-12 11:47   ` Simona Toaca
  5 siblings, 1 reply; 15+ messages in thread
From: Marek Vasut @ 2026-03-11 15:42 UTC (permalink / raw)
  To: Simona Toaca (OSS), uboot-imx, u-boot
  Cc: Stefano Babic, festevam, peng.fan, alice.guo, ye.li, simona.toaca,
	viorel.suman, ravi, fedor.ross, sebastien.szymanski,
	joao.goncalves, ji.luo, semen.protsenko, sjg, tharvey, ping.bai,
	qijian.guo

On 3/10/26 12:54 PM, Simona Toaca (OSS) wrote:
> From: Simona Toaca <simona.toaca@nxp.com>
> 
> This patch series adds support for saving DDR training
> data to non-volatile memory on iMX94 and iMX95 platforms.
> The purpose is running DDR Quickboot flow on next reboot.
> 
> The process is as follows:
> - OEI runs Training flow for the DDRPHY
> - OEI saves the data from training to volatile memory
> - U-Boot can then save it to non-volatile memory (e.g. SD)
> - OEI loads the data from NVM at cold reboot and runs Quickboot flow
> 
> By skipping training, a much lower boot time is achieved.
How does this interact with authenticated boot ? I wonder if, in case 
the entire bootloader stack is authenticated, the attacker can tamper 
with the saved QB data (which do not seem to be authenticated) and cause 
bitflips to occur in DRAM, which could then allow them to bypass 
authentication in follow up stages which execute from DRAM ?

Are the QB data protected/authenticated in any way against tampering ?

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 0/5] imx9{4,5}: Add Quickboot support
  2026-03-11 15:42 ` [PATCH 0/5] imx9{4,5}: Add Quickboot support Marek Vasut
@ 2026-03-12 11:47   ` Simona Toaca
  2026-03-12 13:20     ` Marek Vasut
  0 siblings, 1 reply; 15+ messages in thread
From: Simona Toaca @ 2026-03-12 11:47 UTC (permalink / raw)
  To: Marek Vasut
  Cc: uboot-imx, u-boot, Stefano Babic, festevam, peng.fan, alice.guo,
	ye.li, simona.toaca, viorel.suman, ravi, fedor.ross,
	sebastien.szymanski, joao.goncalves, ji.luo, semen.protsenko, sjg,
	tharvey, ping.bai, qijian.guo

Hi Marek,

On Wed, Mar 11, 2026 at 04:42:54PM +0100, Marek Vasut wrote:
> On 3/10/26 12:54 PM, Simona Toaca (OSS) wrote:
> > From: Simona Toaca <simona.toaca@nxp.com>
> > 
> > This patch series adds support for saving DDR training
> > data to non-volatile memory on iMX94 and iMX95 platforms.
> > The purpose is running DDR Quickboot flow on next reboot.
> > 
> > The process is as follows:
> > - OEI runs Training flow for the DDRPHY
> > - OEI saves the data from training to volatile memory
> > - U-Boot can then save it to non-volatile memory (e.g. SD)
> > - OEI loads the data from NVM at cold reboot and runs Quickboot flow
> > 
> > By skipping training, a much lower boot time is achieved.
> How does this interact with authenticated boot ? I wonder if, in case the
> entire bootloader stack is authenticated, the attacker can tamper with the
> saved QB data (which do not seem to be authenticated) and cause bitflips to
> occur in DRAM, which could then allow them to bypass authentication in
> follow up stages which execute from DRAM ?
> 
> Are the QB data protected/authenticated in any way against tampering ?

In U-Boot there is no authentication for qb, it's just the CRC that is used
to ensure the qb data was not overwritten by another (non-malicious) entity.

OEI is the first boot component that runs during the bootflow and is
in charge of verifying if the data stored in NVM is valid or not. With
the exception of iMX95 A0/A1, which use a CRC as well for this purpose,
the other boards (iMX95 B0, iMX943, etc.) use ELE to verify a MAC stored
in the qb_state structure. This provides the authentication part.

If someone tampers with the data and it somehow gets saved to NVM, the worst
case scenario is that Training flow will run instead of the Quickboot flow.
I am not sure that it can have any other impact. Especially in the case where
SPL saves qb data, the DRAM address can then be used for image loading without
worrying about qb or what data had been written there.

Kind regards,
Simona


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 0/5] imx9{4,5}: Add Quickboot support
  2026-03-12 11:47   ` Simona Toaca
@ 2026-03-12 13:20     ` Marek Vasut
  0 siblings, 0 replies; 15+ messages in thread
From: Marek Vasut @ 2026-03-12 13:20 UTC (permalink / raw)
  To: Simona Toaca
  Cc: uboot-imx, u-boot, Stefano Babic, festevam, peng.fan, alice.guo,
	ye.li, simona.toaca, viorel.suman, ravi, fedor.ross,
	sebastien.szymanski, joao.goncalves, ji.luo, semen.protsenko, sjg,
	tharvey, ping.bai, qijian.guo

On 3/12/26 12:47 PM, Simona Toaca wrote:

Hello Simona,

> On Wed, Mar 11, 2026 at 04:42:54PM +0100, Marek Vasut wrote:
>> On 3/10/26 12:54 PM, Simona Toaca (OSS) wrote:
>>> From: Simona Toaca <simona.toaca@nxp.com>
>>>
>>> This patch series adds support for saving DDR training
>>> data to non-volatile memory on iMX94 and iMX95 platforms.
>>> The purpose is running DDR Quickboot flow on next reboot.
>>>
>>> The process is as follows:
>>> - OEI runs Training flow for the DDRPHY
>>> - OEI saves the data from training to volatile memory
>>> - U-Boot can then save it to non-volatile memory (e.g. SD)
>>> - OEI loads the data from NVM at cold reboot and runs Quickboot flow
>>>
>>> By skipping training, a much lower boot time is achieved.
>> How does this interact with authenticated boot ? I wonder if, in case the
>> entire bootloader stack is authenticated, the attacker can tamper with the
>> saved QB data (which do not seem to be authenticated) and cause bitflips to
>> occur in DRAM, which could then allow them to bypass authentication in
>> follow up stages which execute from DRAM ?
>>
>> Are the QB data protected/authenticated in any way against tampering ?
> 
> In U-Boot there is no authentication for qb, it's just the CRC that is used
> to ensure the qb data was not overwritten by another (non-malicious) entity.

Ouch

> OEI is the first boot component that runs during the bootflow and is
> in charge of verifying if the data stored in NVM is valid or not. With
> the exception of iMX95 A0/A1, which use a CRC as well for this purpose,
> the other boards (iMX95 B0, iMX943, etc.) use ELE to verify a MAC stored
> in the qb_state structure. This provides the authentication part.

I see, understood and thank you for clarifying this. This part kept 
coming up in various discussions, so it would be nice to have this at 
least in the commit message, for future reference.

Thank you !

^ permalink raw reply	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2026-03-12 16:24 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-10 11:54 [PATCH 0/5] imx9{4,5}: Add Quickboot support Simona Toaca (OSS)
2026-03-10 11:54 ` [PATCH 1/5] imx9: Add support for saving DDR training data to NVM Simona Toaca (OSS)
2026-03-10 11:54 ` [PATCH 2/5] arm: mach-imx: Add command to expose QB functionality Simona Toaca (OSS)
2026-03-10 11:54 ` [PATCH 3/5] imx9: Enable QB data saving for iMX9{4,5} EVK Simona Toaca (OSS)
2026-03-10 11:54 ` [PATCH 4/5] board: nxp: imx9{4,5}_evk: Add qb save option in SPL Simona Toaca (OSS)
2026-03-10 15:04   ` Emanuele Ghidoli
2026-03-10 15:27     ` Simona Toaca
2026-03-11  9:14       ` Francesco Dolcini
2026-03-11 14:58         ` Simona Toaca
2026-03-10 16:12   ` Fabio Estevam
2026-03-11  7:54     ` Simona Toaca
2026-03-10 11:54 ` [PATCH 5/5] doc: board: nxp: Add Quickboot documentation Simona Toaca (OSS)
2026-03-11 15:42 ` [PATCH 0/5] imx9{4,5}: Add Quickboot support Marek Vasut
2026-03-12 11:47   ` Simona Toaca
2026-03-12 13:20     ` Marek Vasut

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox