summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--MAINTAINERS1
-rw-r--r--arch/arm/include/asm/spl.h1
-rw-r--r--common/spl/Kconfig30
-rw-r--r--common/spl/Makefile1
-rw-r--r--common/spl/spl_ufs.c49
-rw-r--r--drivers/Makefile1
-rw-r--r--drivers/scsi/Makefile3
-rw-r--r--drivers/ufs/Kconfig4
-rw-r--r--drivers/ufs/ufs-rockchip.c31
-rw-r--r--drivers/ufs/ufs-rockchip.h1
-rw-r--r--drivers/ufs/ufs-uclass.c36
-rw-r--r--drivers/ufs/ufs.h10
-rw-r--r--lib/Makefile1
13 files changed, 152 insertions, 17 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index d2508d90c33..d125bbfb519 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1890,6 +1890,7 @@ M: Neil Armstrong <[email protected]>
M: Bhupesh Sharma <[email protected]>
M: Neha Malcom Francis <[email protected]>
S: Maintained
+F: common/spl/spl_ufs.c
F: drivers/ufs/
UPL
diff --git a/arch/arm/include/asm/spl.h b/arch/arm/include/asm/spl.h
index ee79a19c05c..dd462ea6ad8 100644
--- a/arch/arm/include/asm/spl.h
+++ b/arch/arm/include/asm/spl.h
@@ -30,6 +30,7 @@ enum {
BOOT_DEVICE_XIP,
BOOT_DEVICE_BOOTROM,
BOOT_DEVICE_SMH,
+ BOOT_DEVICE_UFS,
BOOT_DEVICE_NONE
};
#endif
diff --git a/common/spl/Kconfig b/common/spl/Kconfig
index 996c9b8db4f..dafc0cb91bd 100644
--- a/common/spl/Kconfig
+++ b/common/spl/Kconfig
@@ -1613,6 +1613,36 @@ config SPL_THERMAL
automatic power-off when the temperature gets too high or low. Other
devices may be discrete but connected on a suitable bus.
+config SPL_UFS_SUPPORT
+ bool "Support loading from UFS"
+ depends on UFS
+ select SPL_LOAD_BLOCK
+ help
+ Enable support for UFS in SPL. This allows
+ use of UFS devices such as hard drives and flash drivers for
+ loading U-Boot.
+
+config SPL_UFS_RAW_U_BOOT_DEVNUM
+ int "SCSI device number of the UFS device to load U-Boot from"
+ depends on SPL_UFS_SUPPORT
+ default 0
+ help
+ UFS devices are usually configured with multiple LUNs, which present
+ themselves as sequentially numbered SCSI devices. Usually one would
+ get a default LUN 0 taking up most of the space on the device, with
+ a number of smaller LUNs following it. This option controls which of
+ them the SPL will attempt to load U-Boot from. Note that this is the
+ SCSI device number, which might differ from the UFS LUN if you have
+ multiple SCSI devices attached and recognized by the SPL.
+
+config SPL_UFS_RAW_U_BOOT_SECTOR
+ hex "Address on the UFS to load U-Boot from"
+ depends on SPL_UFS_SUPPORT
+ default 0x800 if ARCH_ROCKCHIP
+ help
+ Address on the block device to load U-Boot from.
+ Units: UFS sectors (1 sector = 4096 bytes).
+
config SPL_WATCHDOG
bool "Support watchdog drivers"
imply SPL_WDT if !HW_WATCHDOG
diff --git a/common/spl/Makefile b/common/spl/Makefile
index 4c9482bd309..e18f3cf0948 100644
--- a/common/spl/Makefile
+++ b/common/spl/Makefile
@@ -37,6 +37,7 @@ obj-$(CONFIG_$(PHASE_)DFU) += spl_dfu.o
obj-$(CONFIG_$(PHASE_)SPI_LOAD) += spl_spi.o
obj-$(CONFIG_$(PHASE_)RAM_SUPPORT) += spl_ram.o
obj-$(CONFIG_$(PHASE_)USB_SDP_SUPPORT) += spl_sdp.o
+obj-$(CONFIG_$(PHASE_)UFS_SUPPORT) += spl_ufs.o
endif
obj-$(CONFIG_$(PHASE_)UPL) += spl_upl.o
diff --git a/common/spl/spl_ufs.c b/common/spl/spl_ufs.c
new file mode 100644
index 00000000000..cef1843f40f
--- /dev/null
+++ b/common/spl/spl_ufs.c
@@ -0,0 +1,49 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2025 Alexey Charkov <[email protected]>
+ */
+
+#include <spl.h>
+#include <spl_load.h>
+#include <scsi.h>
+#include <errno.h>
+#include <image.h>
+#include <linux/compiler.h>
+#include <log.h>
+
+static ulong 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;
+ lbaint_t count = size >> bd->log2blksz;
+
+ 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)
+{
+ unsigned long sector = CONFIG_SPL_UFS_RAW_U_BOOT_SECTOR;
+ int devnum = CONFIG_SPL_UFS_RAW_U_BOOT_DEVNUM;
+ struct spl_load_info load;
+ struct blk_desc *bd;
+ int err;
+
+ /* try to recognize storage devices immediately */
+ scsi_scan(false);
+ bd = blk_get_devnum_by_uclass_id(UCLASS_SCSI, devnum);
+ if (!bd)
+ 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;
+ }
+
+ return 0;
+}
+
+SPL_LOAD_IMAGE_METHOD("UFS", 0, BOOT_DEVICE_UFS, spl_ufs_load_image);
diff --git a/drivers/Makefile b/drivers/Makefile
index de993ae42ac..43d0ba33281 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -73,6 +73,7 @@ obj-$(CONFIG_SPL_USB_HOST) += usb/host/
obj-$(CONFIG_SPL_SATA) += ata/ scsi/
obj-$(CONFIG_SPL_LEGACY_BLOCK) += block/
obj-$(CONFIG_SPL_THERMAL) += thermal/
+obj-$(CONFIG_SPL_UFS_SUPPORT) += scsi/ ufs/
endif
endif
diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile
index b76de1b22a8..c9af60d5d03 100644
--- a/drivers/scsi/Makefile
+++ b/drivers/scsi/Makefile
@@ -16,4 +16,7 @@ ifdef CONFIG_XPL_BUILD
ifdef CONFIG_SPL_SATA
obj-$(CONFIG_SCSI) += scsi.o scsi-uclass.o
endif
+ifdef CONFIG_SPL_UFS_SUPPORT
+obj-$(CONFIG_SCSI) += scsi.o scsi-uclass.o
+endif
endif
diff --git a/drivers/ufs/Kconfig b/drivers/ufs/Kconfig
index 6c75bb2a079..49472933de3 100644
--- a/drivers/ufs/Kconfig
+++ b/drivers/ufs/Kconfig
@@ -76,6 +76,10 @@ config UFS_RENESAS_GEN5
config UFS_ROCKCHIP
bool "Rockchip specific hooks to UFS controller platform driver"
depends on UFS
+ depends on DM_GPIO
+ depends on RESET_ROCKCHIP
+ depends on SPL_DM_GPIO || !SPL_UFS_SUPPORT
+ depends on SPL_RESET_ROCKCHIP || !SPL_UFS_SUPPORT
help
This selects the Rockchip specific additions to UFSHCD platform driver.
diff --git a/drivers/ufs/ufs-rockchip.c b/drivers/ufs/ufs-rockchip.c
index 643a6ffb9bc..a13236c7f76 100644
--- a/drivers/ufs/ufs-rockchip.c
+++ b/drivers/ufs/ufs-rockchip.c
@@ -5,6 +5,7 @@
* Copyright (C) 2025 Rockchip Electronics Co.Ltd.
*/
+#include <asm/gpio.h>
#include <asm/io.h>
#include <clk.h>
#include <dm.h>
@@ -29,12 +30,9 @@ static int ufs_rockchip_hce_enable_notify(struct ufs_hba *hba,
ufshcd_dme_reset(hba);
ufshcd_dme_enable(hba);
- if (hba->ops->phy_initialization) {
- err = hba->ops->phy_initialization(hba);
- if (err)
- dev_err(hba->dev,
- "Phy init failed (%d)\n", err);
- }
+ err = ufshcd_ops_phy_initialization(hba);
+ if (err)
+ dev_err(hba->dev, "Phy init failed (%d)\n", err);
return err;
}
@@ -152,11 +150,31 @@ static int ufs_rockchip_common_init(struct ufs_hba *hba)
return err;
}
+ err = gpio_request_by_name(dev, "reset-gpios", 0, &host->device_reset,
+ GPIOD_IS_OUT | GPIOD_ACTIVE_LOW);
+ if (err) {
+ dev_err(dev, "Cannot get reset GPIO\n");
+ return err;
+ }
+
host->hba = hba;
return 0;
}
+static int ufs_rockchip_device_reset(struct ufs_hba *hba)
+{
+ struct ufs_rockchip_host *host = dev_get_priv(hba->dev);
+
+ dm_gpio_set_value(&host->device_reset, true);
+ udelay(20);
+
+ dm_gpio_set_value(&host->device_reset, false);
+ udelay(20);
+
+ return 0;
+}
+
static int ufs_rockchip_rk3576_init(struct ufs_hba *hba)
{
int ret = 0;
@@ -174,6 +192,7 @@ static struct ufs_hba_ops ufs_hba_rk3576_vops = {
.init = ufs_rockchip_rk3576_init,
.phy_initialization = ufs_rockchip_rk3576_phy_init,
.hce_enable_notify = ufs_rockchip_hce_enable_notify,
+ .device_reset = ufs_rockchip_device_reset,
};
static const struct udevice_id ufs_rockchip_of_match[] = {
diff --git a/drivers/ufs/ufs-rockchip.h b/drivers/ufs/ufs-rockchip.h
index 3dcb80f5702..50c2539da78 100644
--- a/drivers/ufs/ufs-rockchip.h
+++ b/drivers/ufs/ufs-rockchip.h
@@ -72,6 +72,7 @@ struct ufs_rockchip_host {
void __iomem *ufs_sys_ctrl;
void __iomem *mphy_base;
struct reset_ctl_bulk rsts;
+ struct gpio_desc device_reset;
struct clk ref_out_clk;
uint64_t caps;
uint32_t phy_config_mode;
diff --git a/drivers/ufs/ufs-uclass.c b/drivers/ufs/ufs-uclass.c
index 7a80a9d5664..bb997aace8f 100644
--- a/drivers/ufs/ufs-uclass.c
+++ b/drivers/ufs/ufs-uclass.c
@@ -127,11 +127,6 @@ static void ufshcd_print_pwr_info(struct ufs_hba *hba)
hba->pwr_info.hs_rate);
}
-static void ufshcd_device_reset(struct ufs_hba *hba)
-{
- ufshcd_vops_device_reset(hba);
-}
-
/**
* ufshcd_ready_for_uic_cmd - Check if controller is ready
* to accept UIC commands
@@ -512,7 +507,9 @@ static int ufshcd_link_startup(struct ufs_hba *hba)
int retries = DME_LINKSTARTUP_RETRIES;
do {
- ufshcd_ops_link_startup_notify(hba, PRE_CHANGE);
+ ret = ufshcd_ops_link_startup_notify(hba, PRE_CHANGE);
+ if (ret)
+ goto out;
ret = ufshcd_dme_link_startup(hba);
@@ -598,12 +595,18 @@ static inline void ufshcd_hba_start(struct ufs_hba *hba)
static int ufshcd_hba_enable(struct ufs_hba *hba)
{
int retry;
+ int ret;
if (!ufshcd_is_hba_active(hba))
/* change controller state to "reset state" */
ufshcd_hba_stop(hba);
- ufshcd_ops_hce_enable_notify(hba, PRE_CHANGE);
+ ret = ufshcd_ops_hce_enable_notify(hba, PRE_CHANGE);
+ if (ret) {
+ dev_err(hba->dev, "Controller enable notify PRE_CHANGE failed: %i\n",
+ ret);
+ return ret;
+ }
/* start controller initialization sequence */
ufshcd_hba_start(hba);
@@ -635,7 +638,12 @@ static int ufshcd_hba_enable(struct ufs_hba *hba)
/* enable UIC related interrupts */
ufshcd_enable_intr(hba, UFSHCD_UIC_MASK);
- ufshcd_ops_hce_enable_notify(hba, POST_CHANGE);
+ ret = ufshcd_ops_hce_enable_notify(hba, POST_CHANGE);
+ if (ret) {
+ dev_err(hba->dev, "Controller enable notify POST_CHANGE failed: %i\n",
+ ret);
+ return ret;
+ }
return 0;
}
@@ -2184,7 +2192,11 @@ int ufshcd_probe(struct udevice *ufs_dev, struct ufs_hba_ops *hba_ops)
/* Set descriptor lengths to specification defaults */
ufshcd_def_desc_sizes(hba);
- ufshcd_ops_init(hba);
+ err = ufshcd_ops_init(hba);
+ if (err) {
+ dev_err(hba->dev, "Host controller init failed: %i\n", err);
+ return err;
+ }
/* Read capabilities registers */
hba->capabilities = ufshcd_readl(hba, REG_CONTROLLER_CAPABILITIES);
@@ -2228,7 +2240,11 @@ int ufshcd_probe(struct udevice *ufs_dev, struct ufs_hba_ops *hba_ops)
mb(); /* flush previous writes */
/* Reset the attached device */
- ufshcd_device_reset(hba);
+ err = ufshcd_vops_device_reset(hba);
+ if (err) {
+ dev_err(hba->dev, "Failed to reset attached device: %i\n", err);
+ return err;
+ }
err = ufshcd_hba_enable(hba);
if (err) {
diff --git a/drivers/ufs/ufs.h b/drivers/ufs/ufs.h
index bc839a43704..0f6c93fbce7 100644
--- a/drivers/ufs/ufs.h
+++ b/drivers/ufs/ufs.h
@@ -509,7 +509,7 @@ struct ufs_query {
};
/**
- * struct ufs_dev_cmd - all assosiated fields with device management commands
+ * struct ufs_dev_cmd - all associated fields with device management commands
* @type: device management command type - Query, NOP OUT
* @tag_wq: wait queue until free command slot is available
*/
@@ -756,6 +756,14 @@ static inline int ufshcd_ops_link_startup_notify(struct ufs_hba *hba,
return 0;
}
+static inline int ufshcd_ops_phy_initialization(struct ufs_hba *hba)
+{
+ if (hba->ops && hba->ops->phy_initialization)
+ return hba->ops->phy_initialization(hba);
+
+ return 0;
+}
+
static inline int ufshcd_vops_device_reset(struct ufs_hba *hba)
{
if (hba->ops && hba->ops->device_reset)
diff --git a/lib/Makefile b/lib/Makefile
index 70667f3728c..d0ffabc2b47 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -147,6 +147,7 @@ else
obj-$(CONFIG_$(PHASE_)SPRINTF) += vsprintf.o
endif
obj-$(CONFIG_$(PHASE_)STRTO) += strto.o
+obj-$(CONFIG_$(PHASE_)UFS_SUPPORT) += charset.o
else
# Main U-Boot always uses the full printf support
obj-y += vsprintf.o strto.o