Hi Lok,

On 8/7/2026 9:12 am, Chen Huei Lok wrote:
From: Tien Fong Chee <[email protected]>

Enhanced the generic firmware loader to support QSPI RAW partition
loading. This would enable FPGA configuration bitstream loading
from QSPI RAW partition to program FPGA.

Signed-off-by: Tien Fong Chee <[email protected]>
Signed-off-by: Chen Huei Lok <[email protected]>
---
  doc/device-tree-bindings/misc/fs_loader.txt |  8 +++
  drivers/misc/fs_loader.c                    | 79 ++++++++++++++++++---
  include/fs_loader.h                         | 31 ++++++++
  3 files changed, 107 insertions(+), 11 deletions(-)

diff --git a/doc/device-tree-bindings/misc/fs_loader.txt 
b/doc/device-tree-bindings/misc/fs_loader.txt
index 542be4b25a0..8ee59db24d5 100644
--- a/doc/device-tree-bindings/misc/fs_loader.txt
+++ b/doc/device-tree-bindings/misc/fs_loader.txt
@@ -12,6 +12,7 @@ Required properties:
           required for ubi and mounting.
  - ubivol: which volume of ubi the image loading from, this property is 
required
          for ubi and mounting.
+- sfconfig: should only contain "bus number" and "chip selection".
Example of storage device and partition search set for mmc, usb, sata and
  ubi in device tree source as shown in below:
@@ -46,3 +47,10 @@ ubi in device tree source as shown in below:
                mtdpart = "UBI",
                ubivol = "ubi0";
        };
+
+       Example for qspi:
+       fs_loader4: fs-loader@4 {
+               bootph-all;
+               compatible = "u-boot,fs-loader";
+               sfconfig = <0 0>;
+       };
diff --git a/drivers/misc/fs_loader.c b/drivers/misc/fs_loader.c
index 6af4c7f15e7..d4804d649e6 100644
--- a/drivers/misc/fs_loader.c
+++ b/drivers/misc/fs_loader.c
@@ -19,6 +19,7 @@
  #include <mapmem.h>
  #include <malloc.h>
  #include <spl.h>
+#include <spi_flash.h>
#ifdef CONFIG_CMD_UBIFS
  #include <ubi_uboot.h>
@@ -67,6 +68,11 @@ static int mount_ubifs(char *mtdpart, char *ubivol)
  }
  #endif
+__weak struct blk_desc *blk_get_by_device(struct udevice *dev)
+{
+       return NULL;
+}


 Adding a weak stub next to an existing strong implementation is confusing and can mask missing BLK linkage in unusual configs.

 Drop the weak stub; include <blk.h>.


+
  static int select_fs_dev(struct device_plat *plat)
  {
        int ret;
@@ -121,16 +127,26 @@ static int _request_firmware_prepare(struct udevice *dev,
                                    const char *name, void *dbuf,
                                    size_t size, u32 offset)
  {
-       if (!name || name[0] == '\0')
-               return -EINVAL;


RAW mode legitimately encodes a hex offset in `name`, but FS mode still needs a non-empty filename for fs_read().

Recommend to keep empty-name rejection for DATA_FS; only parse hex offsets in DATA_RAW (and still reject parse failure).

-
        struct firmware *firmwarep = dev_get_priv(dev);
+       struct device_plat *plat = dev_get_plat(dev);
+       char *endptr;
+       u32 fw_offset;
if (!firmwarep)
                return -ENOMEM;
firmwarep->name = name;
-       firmwarep->offset = offset;
+
+       if (plat->data_type == DATA_RAW) {
+               fw_offset = simple_strtoul(firmwarep->name, &endptr, 16);
+               if (firmwarep->name == endptr || *endptr != '\0')
+                       return -EINVAL;
+
+               firmwarep->offset = fw_offset + offset;
+       } else {
+               firmwarep->offset = offset;
+       }
+
        firmwarep->data = dbuf;
        firmwarep->size = size;
@@ -147,7 +163,8 @@ static int fw_get_filesystem_firmware(struct udevice *dev)
  {
        loff_t actread = 0;
        char *storage_interface, *dev_part, *ubi_mtdpart, *ubi_volume;
-       int ret;
+       int ret = 0;
+       struct device_plat *plat = dev_get_plat(dev);
storage_interface = env_get("storage_interface");
        dev_part = env_get("fw_dev_part");
@@ -167,7 +184,8 @@ static int fw_get_filesystem_firmware(struct udevice *dev)
                else
                        ret = -ENODEV;
        } else {
-               ret = select_fs_dev(dev_get_plat(dev));
+               if (plat->data_type == DATA_FS)
+                       ret = select_fs_dev(dev_get_plat(dev));
        }
if (ret)
@@ -178,8 +196,17 @@ static int fw_get_filesystem_firmware(struct udevice *dev)
        if (!firmwarep)
                return -ENOMEM;
- ret = fs_read(firmwarep->name, (ulong)map_to_sysmem(firmwarep->data),
-                       firmwarep->offset, firmwarep->size, &actread);
+       if (plat->data_type == DATA_FS)
+               ret = fs_read(firmwarep->name,
+                             (ulong)map_to_sysmem(firmwarep->data),
+                             firmwarep->offset, firmwarep->size, &actread);
+       else if (plat->data_type == DATA_RAW) {
+#ifdef CONFIG_SPI_FLASH
+               ret = spi_flash_read_dm(plat->flash, firmwarep->offset,
+                                       firmwarep->size, (void 
*)firmwarep->data);
+               actread = firmwarep->size;
+#endif


Current with SPI_FLASH off, ret stays 0 and the function returns actread==0 as success.

Recommend return -ENOSYS / -EINVAL when DATA_RAW is selected but SPI flash support is unavailable.


+       }
if (ret) {
                debug("Error: %d Failed to read %s from flash %lld != %zu.\n",
@@ -228,6 +255,7 @@ int request_firmware_into_buf(struct udevice *dev,
  static int fs_loader_of_to_plat(struct udevice *dev)
  {
        u32 phandlepart[2];
+       u32 sfconfig[2];
ofnode fs_loader_node = dev_ofnode(dev); @@ -247,6 +275,15 @@ static int fs_loader_of_to_plat(struct udevice *dev) plat->ubivol = (char *)ofnode_read_string(
                                 fs_loader_node, "ubivol");
+
+               if (!ofnode_read_u32_array(fs_loader_node, "sfconfig", sfconfig,
+                                          2)) {
+                       plat->data_type = DATA_RAW;
+                       plat->sfconfig.bus = sfconfig[0];
+                       plat->sfconfig.cs = sfconfig[1];
+               } else {
+                       plat->data_type = DATA_FS;
+               }
        }
return 0;
@@ -254,10 +291,30 @@ static int fs_loader_of_to_plat(struct udevice *dev)
static int fs_loader_probe(struct udevice *dev)
  {
-#if CONFIG_IS_ENABLED(DM) && CONFIG_IS_ENABLED(BLK)
-       int ret;
+       int ret = 0;
        struct device_plat *plat = dev_get_plat(dev);
+#ifdef CONFIG_SPI_FLASH
+       if (!plat->flash) {
+               debug("bus = %d\ncs = %d\n",
+                     plat->sfconfig.bus, plat->sfconfig.cs);
+
+               ret = spi_flash_probe_bus_cs(plat->sfconfig.bus,
+                                            plat->sfconfig.cs,
+                                            &plat->flash);
+               if (ret) {
+                       debug("fs_loader: Failed to initialize SPI flash at ");
+                       debug("%u:%u (error %d)\n", plat->sfconfig.bus,
+                             plat->sfconfig.cs, ret);
+                       return -ENODEV;
+               }

of_to_plat sets data_type=DATA_FS when sfconfig is absent, but probe still calls spi_flash_probe_bus_cs(bus=0,cs=0) for those devices whenever CONFIG_SPI_FLASH=y. Existing MMC/UBI fs-loader nodes on other boards will fail probe with -ENODEV if SPI is not ready at that bus/cs, regressing non-N5X users of a generic driver.

Recommend to gate the SPI probe on plat->data_type == DATA_RAW (and/or successful sfconfig parse). Leave DATA_FS probe behavior unchanged.

Best regards,

Tien Fong

Reply via email to