[PATCH 1/4] spl: ufs: Add partition support and flexible loading

Balaji Selvanathan balaji.selvanathan at oss.qualcomm.com
Thu Apr 9 18:04:01 CEST 2026


Hi Alexey,

Thanks for the feedbacks. Pls find replies below:

On 4/9/2026 9:09 PM, Alexey Charkov wrote:
> 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?
Yes, thats an issue, we can have sector as last step if partition name 
or number is not provided.
>
>> +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?
Okay.
>
>>   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.
Got it, will implement this.
>
>> +               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.
Okay.
>
>> +                       /* 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?
Yes, will do this.
>
>> +               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.
Okay.
>
> Best regards,
> Alexey


More information about the U-Boot mailing list