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