[PATCH 1/4] spl: ufs: Add partition support and flexible loading
Alexey Charkov
alchark at gmail.com
Thu Apr 9 17:39:00 CEST 2026
On Thu, Apr 9, 2026 at 6:43 PM Balaji Selvanathan
<balaji.selvanathan at oss.qualcomm.com> wrote:
>
> Add partition-based loading support to SPL UFS, similar to the
> existing implementation in spl_mmc.c. This allows loading from
> named partitions or partition numbers instead of hardcoded sector
> offsets.
>
> The loading strategy tries methods in order: absolute sector if
> configured, partition by name, partition by number, then filesystem
> mode (placeholder for future implementation).
>
> This brings UFS boot flexibility on par with MMC/SD boot.
>
> Signed-off-by: Balaji Selvanathan <balaji.selvanathan at oss.qualcomm.com>
> ---
> common/spl/Kconfig | 40 ++++++++++-
> common/spl/spl_ufs.c | 188 +++++++++++++++++++++++++++++++++++++++++++++------
> include/part.h | 3 +-
> include/spl.h | 4 ++
> 4 files changed, 214 insertions(+), 21 deletions(-)
>
> diff --git a/common/spl/Kconfig b/common/spl/Kconfig
> index a21b71ad5d1..b3c7a1d8aa3 100644
> --- a/common/spl/Kconfig
> +++ b/common/spl/Kconfig
> @@ -1637,9 +1637,47 @@ config SPL_UFS_RAW_U_BOOT_SECTOR
> depends on SPL_UFS_SUPPORT
> default 0x800 if ARCH_ROCKCHIP
> help
> - Address on the block device to load U-Boot from.
> + Absolute sector offset on the UFS LUN to load U-Boot from.
> + This is tried first before partition-based loading.
> + Set to 0x0 to skip absolute sector loading and use partition mode.
> Units: UFS sectors (1 sector = 4096 bytes).
Hi Balaji,
What if I want to load the FIT image from the start of a dedicated LUN?
> +config SPL_UFS_RAW_U_BOOT_USE_PARTITION
> + bool "Enable UFS partition support for raw mode"
> + depends on SPL_UFS_SUPPORT
> + select SPL_LIBDISK_SUPPORT
> + help
> + Enable support for loading from a specific partition on UFS
> + in raw mode. When enabled, you can specify either a partition
> + name or partition number to load from.
> +
> +config SPL_UFS_RAW_U_BOOT_PARTITION_NAME
> + string "Partition name to load U-Boot from"
> + depends on SPL_UFS_RAW_U_BOOT_USE_PARTITION
> + help
> + Name of the partition to load U-Boot from in UFS raw mode.
> + This is tried before partition number lookup.
> + Leave empty to skip name-based lookup.
> + Example: "boot", "uefi", "dtb_a"
> +
> +config SPL_UFS_RAW_U_BOOT_PARTITION_NUM
> + int "Partition number to load U-Boot from"
> + depends on SPL_UFS_RAW_U_BOOT_USE_PARTITION
> + help
> + Partition number to load U-Boot from when using UFS raw mode
> + with partition support. This is used if partition name is not
> + specified or not found.
> +
> +config SPL_UFS_FS
> + bool "Enable UFS filesystem boot mode"
> + depends on SPL_UFS_SUPPORT
> + help
> + Enable filesystem-based boot from UFS. This allows loading
> + U-Boot from FAT or EXT4 filesystems on UFS partitions.
> + This is tried as a fallback if raw mode loading fails.
> +
> + Note: Filesystem support is not yet fully implemented.
Maybe split those into separate commits for easier review?
> config SPL_WATCHDOG
> bool "Support watchdog drivers"
> imply SPL_WDT if !HW_WATCHDOG
> diff --git a/common/spl/spl_ufs.c b/common/spl/spl_ufs.c
> index cef1843f40f..c4e793bf701 100644
> --- a/common/spl/spl_ufs.c
> +++ b/common/spl/spl_ufs.c
> @@ -3,15 +3,19 @@
> * (C) Copyright 2025 Alexey Charkov <alchark at gmail.com>
> */
>
> +#include <dm.h>
> +#include <log.h>
> #include <spl.h>
> #include <spl_load.h>
> #include <scsi.h>
> -#include <errno.h>
> -#include <image.h>
> +#include <part.h>
> +#include <blk.h>
> #include <linux/compiler.h>
> -#include <log.h>
> +#include <errno.h>
>
> -static ulong spl_ufs_load_read(struct spl_load_info *load, ulong off, ulong size, void *buf)
> +/* Block read callback for spl_load framework */
> +static ulong h_spl_ufs_load_read(struct spl_load_info *load, ulong off,
> + ulong size, void *buf)
> {
> struct blk_desc *bd = load->priv;
> lbaint_t sector = off >> bd->log2blksz;
> @@ -20,30 +24,176 @@ static ulong spl_ufs_load_read(struct spl_load_info *load, ulong off, ulong size
> return blk_dread(bd, sector, count, buf) << bd->log2blksz;
> }
>
> -static int spl_ufs_load_image(struct spl_image_info *spl_image,
> - struct spl_boot_device *bootdev)
> +/* Load image from raw sector */
> +static int ufs_load_image_raw_sector(struct spl_image_info *spl_image,
> + struct spl_boot_device *bootdev,
> + struct blk_desc *bd,
> + unsigned long sector)
> {
> - unsigned long sector = CONFIG_SPL_UFS_RAW_U_BOOT_SECTOR;
> - int devnum = CONFIG_SPL_UFS_RAW_U_BOOT_DEVNUM;
> struct spl_load_info load;
> + int ret;
> +
> + debug("spl: ufs loading from sector 0x%lx\n", sector);
> +
> + spl_load_init(&load, h_spl_ufs_load_read, bd, bd->blksz);
> + ret = spl_load(spl_image, bootdev, &load, 0, sector << bd->log2blksz);
> +
> + if (ret) {
> + debug("spl: ufs load failed: %d\n", ret);
> + return ret;
> + }
> +
> + debug("spl: ufs load successful\n");
> + return 0;
> +}
> +
> +u32 __weak spl_ufs_boot_mode(const u32 boot_device)
> +{
> + return UFS_MODE_RAW;
> +}
> +
> +int spl_ufs_load(struct spl_image_info *spl_image,
> + struct spl_boot_device *bootdev,
> + const char *filename)
> +{
> + u32 boot_mode;
> + int ret = 0;
> + int devnum = CONFIG_SPL_UFS_RAW_U_BOOT_DEVNUM;
> struct blk_desc *bd;
> - int err;
> + unsigned long sector = 0;
> +
> + log_debug("spl: ufs devnum=%d\n", devnum);
> +
> + ret = scsi_scan(false);
> + if (ret) {
> + printf("spl: scsi scan failed: %d\n", ret);
> + return ret;
> + }
>
> - /* try to recognize storage devices immediately */
> - scsi_scan(false);
> bd = blk_get_devnum_by_uclass_id(UCLASS_SCSI, devnum);
> - if (!bd)
> + if (!bd) {
> + printf("spl: could not get UFS device %d\n", devnum);
> return -ENODEV;
> + }
>
> - spl_load_init(&load, spl_ufs_load_read, bd, bd->blksz);
> - err = spl_load(spl_image, bootdev, &load, 0, sector << bd->log2blksz);
> - if (err) {
> - puts("spl_ufs_load_image: ufs block read error\n");
> - log_debug("(error=%d)\n", err);
> - return err;
> + boot_mode = spl_ufs_boot_mode(bootdev->boot_device);
> +
> + switch (boot_mode) {
> + case UFS_MODE_RAW:
> + debug("spl: ufs raw mode\n");
> +
> + /* Step 1: Try absolute sector (if configured and non-zero) */
> +#ifdef CONFIG_SPL_UFS_RAW_U_BOOT_SECTOR
Perhaps if this and the below blocks are split out into helper
functions which compile to a no-op when the respective Kconfig option
is not set the code would be prettier :)
Preprocessor defines really break the flow visually by going across
the indentation of the surrounding code.
> + if (CONFIG_SPL_UFS_RAW_U_BOOT_SECTOR != 0) {
> + debug("spl: trying absolute sector 0x%x\n",
> + CONFIG_SPL_UFS_RAW_U_BOOT_SECTOR);
> +
> + ret = ufs_load_image_raw_sector(spl_image, bootdev, bd,
> + CONFIG_SPL_UFS_RAW_U_BOOT_SECTOR);
> +
> + if (!ret) {
> + debug("spl: loaded from absolute sector\n");
> + return 0;
> + }
> + debug("spl: absolute sector failed: %d\n", ret);
> + }
> +#endif
> +
> + /* Step 2-4: Try partition-based loading */
> +#ifdef CONFIG_SPL_UFS_RAW_U_BOOT_USE_PARTITION
> + {
> + struct disk_partition part_info;
> + int part_found = 0;
... especially since you want to use scoped blocks with local
variables. These really ask to go into a helper function instead.
> + /* Step 2: Try partition name (if configured) */
> +#ifdef CONFIG_SPL_UFS_RAW_U_BOOT_PARTITION_NAME
> + if (strlen(CONFIG_SPL_UFS_RAW_U_BOOT_PARTITION_NAME) > 0) {
> + debug("spl: trying partition name '%s'\n",
> + CONFIG_SPL_UFS_RAW_U_BOOT_PARTITION_NAME);
> +
> + ret = part_get_info_by_name(bd,
> + CONFIG_SPL_UFS_RAW_U_BOOT_PARTITION_NAME,
> + &part_info);
> +
> + if (ret >= 0) {
> + debug("spl: found partition '%s' at 0x%lx\n",
> + CONFIG_SPL_UFS_RAW_U_BOOT_PARTITION_NAME,
> + (ulong)part_info.start);
> + sector = part_info.start;
> + part_found = 1;
> + } else {
> + debug("spl: partition name not found: %d\n", ret);
> + }
> + }
> +#endif
> +
> + /* Step 3: Try partition number (if name not found) */
> +#ifdef CONFIG_SPL_UFS_RAW_U_BOOT_PARTITION_NUM
> + if (!part_found) {
> + debug("spl: trying partition number %d\n",
> + CONFIG_SPL_UFS_RAW_U_BOOT_PARTITION_NUM);
> +
> + ret = part_get_info(bd,
> + CONFIG_SPL_UFS_RAW_U_BOOT_PARTITION_NUM,
> + &part_info);
> +
> + if (ret >= 0) {
> + debug("spl: found partition %d at 0x%lx\n",
> + CONFIG_SPL_UFS_RAW_U_BOOT_PARTITION_NUM,
> + (ulong)part_info.start);
> + sector = part_info.start;
> + part_found = 1;
> + } else {
> + debug("spl: partition number not found: %d\n", ret);
> + }
> + }
> +#endif
> +
> + /* Load from partition if found */
> + if (part_found) {
> + ret = ufs_load_image_raw_sector(spl_image, bootdev, bd, sector);
> +
> + if (!ret) {
> + debug("spl: loaded from partition\n");
> + return 0;
> + }
> + debug("spl: partition load failed: %d\n", ret);
> + }
> + }
> +#endif /* CONFIG_SPL_UFS_RAW_U_BOOT_USE_PARTITION */
> +
> + /* Step 4: Fall through to FS mode if enabled */
Wouldn't a raw offset be a more natural fallback, given that it has
fewest dependencies out of all these options?
> + debug("spl: raw mode failed, trying fs\n");
> + fallthrough;
> +
> +#ifdef CONFIG_SPL_UFS_FS
> + case UFS_MODE_FS:
> + debug("spl: ufs fs mode\n");
> +
> + /* TODO: Implement filesystem support */
> + printf("spl: ufs filesystem boot not implemented\n");
> + ret = -ENOSYS;
> + break;
> +#endif
> +
> + default:
> + puts("spl: ufs: invalid boot mode\n");
> + ret = -EINVAL;
> }
>
> - return 0;
> + return ret;
> +}
> +
> +/* SPL load image entry point */
> +static int spl_ufs_load_image(struct spl_image_info *spl_image,
> + struct spl_boot_device *bootdev)
> +{
> + return spl_ufs_load(spl_image, bootdev,
> +#ifdef CONFIG_SPL_FS_LOAD_PAYLOAD_NAME
> + CONFIG_SPL_FS_LOAD_PAYLOAD_NAME);
> +#else
> + NULL);
> +#endif
How about config_enabled(...) instead? Gives fewer repetitions of the
same symbol.
Best regards,
Alexey
More information about the U-Boot
mailing list