On 14-09-2026 17:18, Michal Simek wrote:
Hi Mike,

subject: please do

mach-zynq: Support SPL boot from QSPI in memory-mapped mode


On 9/11/26 15:18, Mike Looijmans wrote:
Ever since this commit:
   Commit 9bb02f7f4533 ("mtd: spi-nor: Fix the spi_nor_read() when config SPI_STACKED_PARALLEL is enabled") booting a 7-series Zynq using the SPL flow is broken, the board is unable
to read u-boot.img from QSPI flash using the updated routines in SPL. In
U-Boot proper, reading QSPI flash works fine though.

I have tested SPL on zc702 and zc706 and I can't see any issue with it.

ff22b8227d25 (HEAD -> debian-sent3, wd/next) Merge patch series "fit: cipher: bounds checks on the ciphered image path"

That's why I don't agree with your description above. It can depend on different configuration but don't think it is valid in general.

Do these board have dual or single QSPI flash configurations?

I only have single QSPI configurations. The QSPI is clocked at its max speed of 100 MHz.

With any U-boot version after 9bb02f7f4533 I only get this output and then it stops:

U-Boot SPL 2026.07 (Sep 15 2026 - 06:58:22 +0200)
Silicon version:        3
Trying to boot from SPI

Reverting 9bb02f7f4533 solves it. In current master, reverting that commit isn't trivial any longer. I reported this at the time and I did quite some debugging to figure out what was wrong with it, but couldn't really find it. So I gave up on that path.

I'll remove that part of the description and avoid the dispute...


U-Boot SPL 2026.10-rc4-00395-gff22b8227d25 (Sep 14 2026 - 13:03:51 +0200)
Silicon version:    1
Trying to boot from SPI

U-Boot 2026.10-rc4-00395-gff22b8227d25 (Sep 14 2026 - 13:03:51 +0200)

CPU:   Zynq 7z045
Silicon: v1.0
Model: Xilinx ZC706 board
DRAM:  ECC disabled 1 GiB
Xilinx I2C FRU format at nvmem0:
 Manufacturer Name: XILINX
 Product Name: ZC706
 Serial No: 123456
 Part Number: 123
 File ID: U-Boot generator
 Revision Number: A
Core:  32 devices, 20 uclasses, devicetree: board
Flash: 0 Bytes
NAND:  0 MiB
MMC:   mmc@e0100000: 0
SF: Detected s25fl128s with page size 512 Bytes, erase size 128 KiB, total 32 MiB
*** Warning - bad CRC, using default environment

In:    serial@e0001000
Out:   serial@e0001000
Err:   serial@e0001000
Net:
ZYNQ GEM: e000b000, mdio bus e000b000, phyaddr 7, interface rgmii-id

Warning: ethernet@e000b000 (eth0) using random MAC address - be:e6:2e:6f:c0:62
eth0: ethernet@e000b000
Hit any key to stop autoboot: 0
Zynq>
Zynq>


U-Boot SPL 2026.10-rc4-00395-gff22b8227d25 (Sep 14 2026 - 13:12:16 +0200)
Silicon version:    0
Trying to boot from SPI


U-Boot 2026.10-rc4-00395-gff22b8227d25 (Sep 14 2026 - 13:12:16 +0200)

CPU:   Zynq 7z020
Silicon: v0.0
Model: Xilinx ZC702 board
DRAM:  ECC disabled 1 GiB
Core:  34 devices, 22 uclasses, devicetree: board
Flash: 0 Bytes
NAND:  0 MiB
MMC:   mmc@e0100000: 0
SF: Detected n25q128a11 with page size 256 Bytes, erase size 64 KiB, total 16 MiB
*** Warning - bad CRC, using default environment

In:    serial@e0001000
Out:   serial@e0001000
Err:   serial@e0001000
Net:
ZYNQ GEM: e000b000, mdio bus e000b000, phyaddr 7, interface rgmii-id

Warning: ethernet@e000b000 (eth0) using random MAC address - e2:94:ff:eb:eb:af
eth0: ethernet@e000b000
Hit any key to stop autoboot: 0
Zynq>


When the Zynq boots from QSPI NOR flash, the ROM will have set up the
QSPI NOR in linear mode, and the contents are now memory mapped (the first 16MB/32MB at least). All that needs to be done at this stage is to memcpy the u-boot code into DDR RAM. This is not only extremely simple, it's also
the fastest method to read QSPI flash on this platform.
This corresponds to a standard NOR boot, hence this mode is selected as
SPL_NOR_SUPPORT.

To accomplish this, first make the SPI support optional instead of
mandatory for the ZYNQ platform. In the bootmode detection, select the
NOR bootmode instead of SPI when SPL_NOR_SUPPORT has been enabled.

To boot from QSPI NOR, write "boot.bin" to offset 0 and write "u-boot.img"
to offset CONFIG_SPL_PAD_TO in the flash. Usually CONFIG_SPL_PAD_TO will
be 0x20000 as the SPL code easily fits in 128k. The offset must fit in the
accessible range for linear mode, which is 16MB for single and 32MB for
the dual chip modes.

Tested on topic_miami boards.


I have tested it on zc702 (silicon v1) and it is not working there.
But I have also tested it on zc706 with production silicon and it is working there.

I guess you won't really find pre-v3 silicon "in the wild"?

Might get it to work even on v1 silicon by setting the SPI registers in the boot.bin header.




Signed-off-by: Mike Looijmans <[email protected]>
---

Changes in v2:
Reworded and amended commit text
Add #define for magic QSPI linear mode address
Use old method by default, use SPL_NOR_SUPPORT to switch to mmap

  arch/arm/Kconfig              | 4 ++--
  arch/arm/mach-zynq/Kconfig    | 4 ++--
  arch/arm/mach-zynq/spl.c      | 8 +++++++-
  include/configs/zynq-common.h | 3 +++
  4 files changed, 14 insertions(+), 5 deletions(-)

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 514bf2000b4..f7eb9d94848 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -1346,8 +1346,8 @@ config ARCH_ZYNQ
      select SPL_SOC_INIT if SPL
      select SPL_CLK if SPL
      select SPL_DM if SPL
-    select SPL_DM_SPI if SPL
-    select SPL_DM_SPI_FLASH if SPL
+    select SPL_DM_SPI if SPL && !SPL_NOR_SUPPORT
+    select SPL_DM_SPI_FLASH if SPL && !SPL_NOR_SUPPORT
      select SPL_OF_CONTROL if SPL
      select SPL_SEPARATE_BSS if SPL
      select SPL_TIMER if SPL
diff --git a/arch/arm/mach-zynq/Kconfig b/arch/arm/mach-zynq/Kconfig
index c3f505fa15c..2e6aa09fb53 100644
--- a/arch/arm/mach-zynq/Kconfig
+++ b/arch/arm/mach-zynq/Kconfig
@@ -19,10 +19,10 @@ config SPL_SERIAL
      default y
    config SPL_SPI_FLASH_SUPPORT
-    default y if ZYNQ_QSPI
+    default y if ZYNQ_QSPI && !SPL_NOR_SUPPORT
    config SPL_SPI
-    default y if ZYNQ_QSPI
+    default y if ZYNQ_QSPI && !SPL_NOR_SUPPORT
    config ZYNQ_DDRC_INIT
      bool "Zynq DDRC initialization"
diff --git a/arch/arm/mach-zynq/spl.c b/arch/arm/mach-zynq/spl.c
index dc964dc2f9f..0332dcee402 100644
--- a/arch/arm/mach-zynq/spl.c
+++ b/arch/arm/mach-zynq/spl.c
@@ -47,10 +47,16 @@ u32 spl_boot_device(void)
      u32 mode;
        switch ((zynq_slcr_get_boot_mode()) & ZYNQ_BM_MASK) {
-#ifdef CONFIG_SPL_SPI
+#ifdef CONFIG_SPL_NOR_SUPPORT
+    case ZYNQ_BM_QSPI:
+        mode = BOOT_DEVICE_NOR;
+        break;
+#else
+# ifdef CONFIG_SPL_SPI

This is every likely elif



      case ZYNQ_BM_QSPI:
          mode = BOOT_DEVICE_SPI;
          break;
+# endif
  #endif
      case ZYNQ_BM_NAND:
          mode = BOOT_DEVICE_NAND;
diff --git a/include/configs/zynq-common.h b/include/configs/zynq-common.h
index 94273d0deb9..4e14c74e31f 100644
--- a/include/configs/zynq-common.h
+++ b/include/configs/zynq-common.h
@@ -35,6 +35,9 @@
  /* Ethernet driver */
    /* NOR */
+/* Load address is mmap address of QSPI flash, plus the offset */
+#define ZYNQ_QSPI0_LINEAR_ADDR    0xfc000000
+#define CFG_SYS_UBOOT_BASE    (ZYNQ_QSPI0_LINEAR_ADDR + CONFIG_SPL_PAD_TO)
    #ifdef CONFIG_USB_EHCI_ZYNQ
  # define DFU_DEFAULT_POLL_TIMEOUT    300
---
base-commit: fb2a4386029c5089c71d0d52caa3b09db9ee08ac
branch: upstream-topic-miami

I think that would be better to have choices which selects things which are necessary.

Something like this. Then every platform can decide if make sense to use it or not

choice
       prompt "SPL QSPI boot access method"
       depends on SPL && ZYNQ_QSPI
       default ZYNQ_SPL_QSPI_DM_SPI

config ZYNQ_SPL_QSPI_DM_SPI
       bool "DM SPI-NOR framework"
       select SPL_DM_SPI
       select SPL_DM_SPI_FLASH
       select SPL_SPI
       select SPL_SPI_FLASH_SUPPORT

config ZYNQ_SPL_QSPI_MMAP
       bool "Memory-mapped linear (NOR) mode"
       select SPL_NOR_SUPPORT

endchoice

Good suggestion. I'll try it.



Obviously I would like to see if there could be any check that U-Boot in spi is within that 16MB windows. I don't think that simple calculation will help here. Because SPL doesn't need to be at start. It means it is clear that it has to be till 16MB but start can vary. I would try to look at reading multiboot register. Start is clear which is SPL_PAD_TO and size is something what SPL has to know.

Did you test what it will happen when SPL tries to read above 16MB offset?

According to the TRM, the dual-flash configurations can map 32MB. So the The address range for the QSPI just ends there, so you'll get whatever happens to be on that memory location.

It's actually possible to map more of the chip, you'd have to put it back in SPI mode, set the EAR register and then back in memory mapped mode. One could even use the mem-to-mem DMA engine to transfer the data. That wouldn't bring any advantage in U-boot, but for the Linux driver it would relieve the CPU. I think there has been some work in that direction in Linux for a TI controller.

The SPL itself cannot be outside the 16MB range either, as the ROM only supports 24-bit access and uses the memory-mapped mode to read it. The requirement that U-boot proper reside in the same region isn't really out of the ordinary then.



Thanks,
Michal



--
Mike Looijmans
System Expert

TOPIC Embedded Products B.V.
Materiaalweg 4, 5681 RJ Best
The Netherlands

T: +31 (0) 499 33 69 69
E: [email protected]
W: www.topic.nl



Reply via email to