Merge tag 'v2025.07-rc3' into next

Prepare v2025.07-rc3
diff --git a/.azure-pipelines.yml b/.azure-pipelines.yml
index 9c13651..15520c4 100644
--- a/.azure-pipelines.yml
+++ b/.azure-pipelines.yml
@@ -92,7 +92,7 @@
           set -e
           python3 -m venv /tmp/venvhtml
           . /tmp/venvhtml/bin/activate
-          pip install -r doc/sphinx/requirements.txt pytest
+          pip install -r doc/sphinx/requirements.txt -r test/py/requirements.txt
           make htmldocs KDOC_WERROR=1
           make infodocs
 
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 7aadd5d..49f3c58 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -164,7 +164,7 @@
   script:
     - python3 -m venv /tmp/venvhtml
     - . /tmp/venvhtml/bin/activate
-    - pip install -r doc/sphinx/requirements.txt pytest
+    - pip install -r doc/sphinx/requirements.txt -r test/py/requirements.txt
     - make htmldocs KDOC_WERROR=1
     - make infodocs
 
diff --git a/.readthedocs.yml b/.readthedocs.yml
index 16418f2..9b6d251 100644
--- a/.readthedocs.yml
+++ b/.readthedocs.yml
@@ -22,3 +22,4 @@
 python:
   install:
     - requirements: doc/sphinx/requirements.txt
+    - requirements: test/py/requirements.txt
diff --git a/Makefile b/Makefile
index f3278e3..f886849 100644
--- a/Makefile
+++ b/Makefile
@@ -3,7 +3,7 @@
 VERSION = 2025
 PATCHLEVEL = 07
 SUBLEVEL =
-EXTRAVERSION = -rc2
+EXTRAVERSION = -rc3
 NAME =
 
 # *DOCUMENTATION*
diff --git a/README b/README
index 334bbcf..88b6e6f 100644
--- a/README
+++ b/README
@@ -482,18 +482,6 @@
 			for your device
 			- CONFIG_USBD_PRODUCTID 0xFFFF
 
-- ULPI Layer Support:
-		The ULPI (UTMI Low Pin (count) Interface) PHYs are supported via
-		the generic ULPI layer. The generic layer accesses the ULPI PHY
-		via the platform viewport, so you need both the genric layer and
-		the viewport enabled. Currently only Chipidea/ARC based
-		viewport is supported.
-		To enable the ULPI layer support, define CONFIG_USB_ULPI and
-		CONFIG_USB_ULPI_VIEWPORT in your board configuration file.
-		If your ULPI phy needs a different reference clock than the
-		standard 24 MHz then you have to define CFG_ULPI_REF_CLK to
-		the appropriate value in Hz.
-
 - MMC Support:
 		CONFIG_SH_MMCIF
 		Support for Renesas on-chip MMCIF controller
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index fedfdb2..79f60eb 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -1416,7 +1416,7 @@
 	select DM_SERIAL
 	select DM_GPIO
 	select MMC
-	imply OF_HAS_PRIOR_STAGE
+	imply OF_HAS_PRIOR_STAGE if !BLOBLIST
 	imply MISC_INIT_R
 
 config TARGET_LS2080A_EMU
diff --git a/arch/arm/dts/Makefile b/arch/arm/dts/Makefile
index 32b698a..976dbda 100644
--- a/arch/arm/dts/Makefile
+++ b/arch/arm/dts/Makefile
@@ -918,8 +918,7 @@
 	imx8mq-librem5-r4.dtb
 
 dtb-$(CONFIG_ARCH_IMX9) += \
-	imx93-var-som-symphony.dtb \
-	imx93-phyboard-segin.dtb
+	imx93-var-som-symphony.dtb
 
 dtb-$(CONFIG_ARCH_IMXRT) += imxrt1020-evk.dtb \
 	imxrt1170-evk.dtb \
diff --git a/arch/arm/dts/imx6ulz-bsh-smm-m2-u-boot.dtsi b/arch/arm/dts/imx6ulz-bsh-smm-m2-u-boot.dtsi
index 7730bb6..faf5962 100644
--- a/arch/arm/dts/imx6ulz-bsh-smm-m2-u-boot.dtsi
+++ b/arch/arm/dts/imx6ulz-bsh-smm-m2-u-boot.dtsi
@@ -5,8 +5,12 @@
  * Author: Michael Trimarchi <michael@amarulasolutions.com>
  */
 
-&{/soc} {
-	bootph-all;
+/ {
+	wdt-reboot {
+		compatible = "wdt-reboot";
+		wdt = <&wdog1>;
+		bootph-pre-ram;
+	};
 };
 
 &aips2 {
diff --git a/arch/arm/dts/imx93-phyboard-segin.dts b/arch/arm/dts/imx93-phyboard-segin.dts
deleted file mode 100644
index 85fb188..0000000
--- a/arch/arm/dts/imx93-phyboard-segin.dts
+++ /dev/null
@@ -1,117 +0,0 @@
-// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
-/*
- * Copyright (C) 2023 PHYTEC Messtechnik GmbH
- * Author: Wadim Egorov <w.egorov@phytec.de>, Christoph Stoidner <c.stoidner@phytec.de>
- * Copyright (C) 2024 Mathieu Othacehe <m.othacehe@gmail.com>
- *
- * Product homepage:
- * phyBOARD-Segin carrier board is reused for the i.MX93 design.
- * https://www.phytec.eu/en/produkte/single-board-computer/phyboard-segin-imx6ul/
- */
-/dts-v1/;
-
-#include "imx93-phycore-som.dtsi"
-
-/{
-	model = "PHYTEC phyBOARD-Segin-i.MX93";
-	compatible = "phytec,imx93-phyboard-segin", "phytec,imx93-phycore-som",
-		     "fsl,imx93";
-
-	chosen {
-		stdout-path = &lpuart1;
-	};
-
-	reg_usdhc2_vmmc: regulator-usdhc2 {
-		compatible = "regulator-fixed";
-		enable-active-high;
-		gpio = <&gpio3 7 GPIO_ACTIVE_HIGH>;
-		pinctrl-names = "default";
-		pinctrl-0 = <&pinctrl_reg_usdhc2_vmmc>;
-		regulator-min-microvolt = <3300000>;
-		regulator-max-microvolt = <3300000>;
-		regulator-name = "VCC_SD";
-	};
-};
-
-/* Console */
-&lpuart1 {
-	pinctrl-names = "default";
-	pinctrl-0 = <&pinctrl_uart1>;
-	status = "okay";
-};
-
-/* eMMC */
-&usdhc1 {
-	no-1-8-v;
-};
-
-/* SD-Card */
-&usdhc2 {
-	pinctrl-names = "default", "state_100mhz", "state_200mhz";
-	pinctrl-0 = <&pinctrl_usdhc2_default>, <&pinctrl_usdhc2_cd>;
-	pinctrl-1 = <&pinctrl_usdhc2_100mhz>, <&pinctrl_usdhc2_cd>;
-	pinctrl-2 = <&pinctrl_usdhc2_200mhz>, <&pinctrl_usdhc2_cd>;
-	bus-width = <4>;
-	cd-gpios = <&gpio3 0 GPIO_ACTIVE_LOW>;
-	no-mmc;
-	no-sdio;
-	vmmc-supply = <&reg_usdhc2_vmmc>;
-	status = "okay";
-};
-
-&iomuxc {
-	pinctrl_uart1: uart1grp {
-		fsl,pins = <
-			MX93_PAD_UART1_RXD__LPUART1_RX		0x31e
-			MX93_PAD_UART1_TXD__LPUART1_TX		0x30e
-		>;
-	};
-
-	pinctrl_reg_usdhc2_vmmc: regusdhc2vmmcgrp {
-		fsl,pins = <
-			MX93_PAD_SD2_RESET_B__GPIO3_IO07	0x31e
-		>;
-	};
-
-	pinctrl_usdhc2_cd: usdhc2cdgrp {
-		fsl,pins = <
-			MX93_PAD_SD2_CD_B__GPIO3_IO00		0x31e
-		>;
-	};
-
-	pinctrl_usdhc2_default: usdhc2grp {
-		fsl,pins = <
-			MX93_PAD_SD2_CLK__USDHC2_CLK		0x179e
-			MX93_PAD_SD2_CMD__USDHC2_CMD		0x139e
-			MX93_PAD_SD2_DATA0__USDHC2_DATA0	0x138e
-			MX93_PAD_SD2_DATA1__USDHC2_DATA1	0x138e
-			MX93_PAD_SD2_DATA2__USDHC2_DATA2	0x138e
-			MX93_PAD_SD2_DATA3__USDHC2_DATA3	0x139e
-			MX93_PAD_SD2_VSELECT__USDHC2_VSELECT	0x51e
-		>;
-	};
-
-	pinctrl_usdhc2_100mhz: usdhc2grp {
-		fsl,pins = <
-			MX93_PAD_SD2_CLK__USDHC2_CLK            0x179e
-			MX93_PAD_SD2_CMD__USDHC2_CMD            0x139e
-			MX93_PAD_SD2_DATA0__USDHC2_DATA0        0x138e
-			MX93_PAD_SD2_DATA1__USDHC2_DATA1        0x138e
-			MX93_PAD_SD2_DATA2__USDHC2_DATA2        0x139e
-			MX93_PAD_SD2_DATA3__USDHC2_DATA3        0x139e
-			MX93_PAD_SD2_VSELECT__USDHC2_VSELECT    0x51e
-		>;
-	};
-
-	pinctrl_usdhc2_200mhz: usdhc2grp {
-		fsl,pins = <
-			MX93_PAD_SD2_CLK__USDHC2_CLK            0x178e
-			MX93_PAD_SD2_CMD__USDHC2_CMD            0x139e
-			MX93_PAD_SD2_DATA0__USDHC2_DATA0        0x139e
-			MX93_PAD_SD2_DATA1__USDHC2_DATA1        0x139e
-			MX93_PAD_SD2_DATA2__USDHC2_DATA2        0x139e
-			MX93_PAD_SD2_DATA3__USDHC2_DATA3        0x139e
-			MX93_PAD_SD2_VSELECT__USDHC2_VSELECT    0x51e
-		>;
-	};
-};
diff --git a/arch/arm/dts/imx93-phycore-som.dtsi b/arch/arm/dts/imx93-phycore-som.dtsi
deleted file mode 100644
index 88c2657..0000000
--- a/arch/arm/dts/imx93-phycore-som.dtsi
+++ /dev/null
@@ -1,126 +0,0 @@
-// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
-/*
- * Copyright (C) 2023 PHYTEC Messtechnik GmbH
- * Author: Wadim Egorov <w.egorov@phytec.de>, Christoph Stoidner <c.stoidner@phytec.de>
- * Copyright (C) 2024 Mathieu Othacehe <m.othacehe@gmail.com>
- *
- * Product homepage:
- * https://www.phytec.eu/en/produkte/system-on-modules/phycore-imx-91-93/
- */
-
-#include <dt-bindings/leds/common.h>
-
-#include "imx93.dtsi"
-
-/{
-	model = "PHYTEC phyCORE-i.MX93";
-	compatible = "phytec,imx93-phycore-som", "fsl,imx93";
-
-	reserved-memory {
-		ranges;
-		#address-cells = <2>;
-		#size-cells = <2>;
-
-		linux,cma {
-			compatible = "shared-dma-pool";
-			reusable;
-			alloc-ranges = <0 0x80000000 0 0x40000000>;
-			size = <0 0x10000000>;
-			linux,cma-default;
-		};
-	};
-
-	leds {
-		compatible = "gpio-leds";
-		pinctrl-names = "default";
-		pinctrl-0 = <&pinctrl_leds>;
-
-		led-0 {
-			color = <LED_COLOR_ID_GREEN>;
-			function = LED_FUNCTION_HEARTBEAT;
-			gpios = <&gpio1 1 GPIO_ACTIVE_HIGH>;
-			linux,default-trigger = "heartbeat";
-		};
-	};
-};
-
-/* Ethernet */
-&fec {
-	pinctrl-names = "default";
-	pinctrl-0 = <&pinctrl_fec>;
-	phy-mode = "rmii";
-	phy-handle = <&ethphy1>;
-	fsl,magic-packet;
-	assigned-clocks = <&clk IMX93_CLK_ENET_TIMER1>,
-			  <&clk IMX93_CLK_ENET_REF>,
-			  <&clk IMX93_CLK_ENET_REF_PHY>;
-	assigned-clock-parents = <&clk IMX93_CLK_SYS_PLL_PFD1_DIV2>,
-				 <&clk IMX93_CLK_SYS_PLL_PFD1_DIV2>,
-				 <&clk IMX93_CLK_SYS_PLL_PFD1_DIV2>;
-	assigned-clock-rates = <100000000>, <50000000>, <50000000>;
-	status = "okay";
-
-	mdio: mdio {
-		clock-frequency = <5000000>;
-		#address-cells = <1>;
-		#size-cells = <0>;
-
-		ethphy1: ethernet-phy@1 {
-			compatible = "ethernet-phy-ieee802.3-c22";
-			reg = <1>;
-		};
-	};
-};
-
-/* eMMC */
-&usdhc1 {
-	pinctrl-names = "default";
-	pinctrl-0 = <&pinctrl_usdhc1>;
-	bus-width = <8>;
-	non-removable;
-	status = "okay";
-};
-
-/* Watchdog */
-&wdog3 {
-	status = "okay";
-};
-
-&iomuxc {
-	pinctrl_fec: fecgrp {
-		fsl,pins = <
-			MX93_PAD_ENET2_MDC__ENET1_MDC			0x50e
-			MX93_PAD_ENET2_MDIO__ENET1_MDIO			0x502
-			MX93_PAD_ENET2_RD0__ENET1_RGMII_RD0		0x57e
-			MX93_PAD_ENET2_RD1__ENET1_RGMII_RD1		0x57e
-			MX93_PAD_ENET2_RXC__ENET1_RX_ER			0x5fe
-			MX93_PAD_ENET2_RX_CTL__ENET1_RGMII_RX_CTL	0x57e
-			MX93_PAD_ENET2_TD0__ENET1_RGMII_TD0		0x50e
-			MX93_PAD_ENET2_TD1__ENET1_RGMII_TD1		0x50e
-			MX93_PAD_ENET2_TX_CTL__ENET1_RGMII_TX_CTL	0x50e
-			MX93_PAD_ENET2_TD2__ENET1_TX_CLK		0x4000050e
-		>;
-	};
-
-	pinctrl_leds: ledsgrp {
-		fsl,pins = <
-			MX93_PAD_I2C1_SDA__GPIO1_IO01		0x31e
-		>;
-	};
-
-	pinctrl_usdhc1: usdhc1grp {
-		fsl,pins = <
-			MX93_PAD_SD1_CLK__USDHC1_CLK		0x179e
-			MX93_PAD_SD1_CMD__USDHC1_CMD		0x1386
-			MX93_PAD_SD1_DATA0__USDHC1_DATA0	0x138e
-			MX93_PAD_SD1_DATA1__USDHC1_DATA1	0x1386
-			MX93_PAD_SD1_DATA2__USDHC1_DATA2	0x138e
-			MX93_PAD_SD1_DATA3__USDHC1_DATA3	0x1386
-			MX93_PAD_SD1_DATA4__USDHC1_DATA4	0x1386
-			MX93_PAD_SD1_DATA5__USDHC1_DATA5	0x1386
-			MX93_PAD_SD1_DATA6__USDHC1_DATA6	0x1386
-			MX93_PAD_SD1_DATA7__USDHC1_DATA7	0x1386
-			MX93_PAD_SD1_STROBE__USDHC1_STROBE	0x179e
-		>;
-	};
-};
diff --git a/arch/arm/dts/imxrt1170-evk.dts b/arch/arm/dts/imxrt1170-evk.dts
index 0d8e701..3543524 100644
--- a/arch/arm/dts/imxrt1170-evk.dts
+++ b/arch/arm/dts/imxrt1170-evk.dts
@@ -234,6 +234,34 @@
 					(IMX_PAD_SION | 8)	/* SEMC_DQS */
 			>;
 		};
+
+		pinctrl_flexspi1: flexspi1grp {
+			fsl,pins = <
+				IOMUXC_GPIO_SD_B2_05_FLEXSPI1_A_DQS     0xa
+				IOMUXC_GPIO_SD_B2_06_FLEXSPI1_A_SS0_B   0xa
+				IOMUXC_GPIO_SD_B2_07_FLEXSPI1_A_SCLK    0xa
+				IOMUXC_GPIO_SD_B2_08_FLEXSPI1_A_DATA00  0xa
+				IOMUXC_GPIO_SD_B2_09_FLEXSPI1_A_DATA01  0xa
+				IOMUXC_GPIO_SD_B2_10_FLEXSPI1_A_DATA02  0xa
+				IOMUXC_GPIO_SD_B2_11_FLEXSPI1_A_DATA03  0xa
+			>;
+		};
+	};
+};
+
+&flexspi1 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_flexspi1>;
+	status = "okay";
+
+	flash@0 {
+		compatible = "jedec,spi-nor";
+		reg = <0>;
+		#address-cells = <1>;
+		#size-cells = <1>;
+		spi-max-frequency = <250000000>;
+		spi-tx-bus-width = <4>;
+		spi-rx-bus-width = <4>;
 	};
 };
 
diff --git a/arch/arm/dts/imxrt1170.dtsi b/arch/arm/dts/imxrt1170.dtsi
index 2de775f..08665ea 100644
--- a/arch/arm/dts/imxrt1170.dtsi
+++ b/arch/arm/dts/imxrt1170.dtsi
@@ -246,6 +246,19 @@
 			#interrupt-cells = <2>;
 		};
 
+		flexspi1: spi@400cc000 {
+			compatible = "nxp,imxrt1170-fspi";
+			reg = <0x400cc000 0x800>, <0x30000000 0x10000000>;
+			reg-names = "fspi_base", "fspi_mmap";
+			#address-cells = <1>;
+			#size-cells = <0>;
+			interrupts = <130>;
+			clocks = <&clks IMXRT1170_CLK_DUMMY>,
+				<&clks IMXRT1170_CLK_FLEXSPI1>;
+			clock-names = "fspi_en", "fspi";
+			status = "disabled";
+		};
+
 		gpt1: gpt1@400ec000 {
 			compatible = "fsl,imxrt-gpt";
 			reg = <0x400ec000 0x4000>;
diff --git a/arch/arm/mach-imx/imx8m/soc.c b/arch/arm/mach-imx/imx8m/soc.c
index 567e8e9..3cdb71a 100644
--- a/arch/arm/mach-imx/imx8m/soc.c
+++ b/arch/arm/mach-imx/imx8m/soc.c
@@ -38,7 +38,7 @@
 
 DECLARE_GLOBAL_DATA_PTR;
 
-#if defined(CONFIG_IMX_HAB)
+#if IS_ENABLED(CONFIG_IMX_HAB)
 struct imx_fuse const imx_sec_config_fuse = {
 	.bank = 1,
 	.word = 3,
@@ -52,7 +52,7 @@
 
 int timer_init(void)
 {
-#ifdef CONFIG_XPL_BUILD
+#if IS_ENABLED(CONFIG_XPL_BUILD)
 	struct sctr_regs *sctr = (struct sctr_regs *)SYSCNT_CTRL_BASE_ADDR;
 	unsigned long freq = readl(&sctr->cntfid0);
 
@@ -110,7 +110,7 @@
 	setbits_le16(&wdog->wcr, WDOG_WDT_MASK | WDOG_WDZST_MASK);
 }
 
-#ifdef CONFIG_ARMV8_PSCI
+#if IS_ENABLED(CONFIG_ARMV8_PSCI)
 #define PTE_MAP_NS	PTE_BLOCK_NS
 #else
 #define PTE_MAP_NS	0
@@ -700,11 +700,11 @@
 	return 0;
 }
 
-#if defined(CONFIG_IMX8MN) || defined(CONFIG_IMX8MP)
+#if IS_ENABLED(CONFIG_IMX8MN) || IS_ENABLED(CONFIG_IMX8MP)
 struct rom_api *g_rom_api = (struct rom_api *)0x980;
 #endif
 
-#if defined(CONFIG_IMX8M)
+#if IS_ENABLED(CONFIG_IMX8M)
 #include <spl.h>
 int imx8m_detect_secondary_image_boot(void)
 {
@@ -790,8 +790,8 @@
 }
 #endif
 
-#if defined(CONFIG_IMX8MN) || defined(CONFIG_IMX8MP)
-#ifdef SYS_MMCSD_RAW_MODE_U_BOOT_USE_PARTITION
+#if IS_ENABLED(CONFIG_IMX8MN) || IS_ENABLED(CONFIG_IMX8MP)
+#if IS_ENABLED(CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_USE_PARTITION)
 #define IMG_CNTN_SET1_OFFSET	GENMASK(22, 19)
 unsigned long arch_spl_mmc_get_uboot_raw_sector(struct mmc *mmc,
 						unsigned long raw_sect)
@@ -826,7 +826,7 @@
 
 	return raw_sect;
 }
-#endif /* SYS_MMCSD_RAW_MODE_U_BOOT_USE_PARTITION */
+#endif /* CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_USE_PARTITION */
 #endif
 
 bool is_usb_boot(void)
@@ -834,7 +834,7 @@
 	return get_boot_device() == USB_BOOT;
 }
 
-#ifdef CONFIG_OF_SYSTEM_SETUP
+#if IS_ENABLED(CONFIG_OF_SYSTEM_SETUP)
 bool check_fdt_new_path(void *blob)
 {
 	const char *soc_path = "/soc@0";
@@ -880,7 +880,7 @@
 	return 0;
 }
 
-#ifdef CONFIG_IMX8MQ
+#if IS_ENABLED(CONFIG_IMX8MQ)
 bool check_dcss_fused(void)
 {
 	struct ocotp_regs *ocotp = (struct ocotp_regs *)OCOTP_BASE_ADDR;
@@ -1026,7 +1026,7 @@
 		return -EPERM;
 }
 
-#ifdef CONFIG_IMX8MN_LOW_DRIVE_MODE
+#if IS_ENABLED(CONFIG_IMX8MN_LOW_DRIVE_MODE)
 static int low_drive_gpu_freq(void *blob)
 {
 	static const char *nodes_path_8mn[] = {
@@ -1311,7 +1311,7 @@
 		"/cpus/cpu@3",
 	};
 
-#ifdef CONFIG_IMX8MQ
+#if IS_ENABLED(CONFIG_IMX8MQ)
 	int i = 0;
 	int rc;
 	int nodeoff;
@@ -1387,7 +1387,7 @@
 	if (is_imx8md())
 		disable_cpu_nodes(blob, nodes_path, 2, 4);
 
-#elif defined(CONFIG_IMX8MM)
+#elif IS_ENABLED(CONFIG_IMX8MM)
 	if (is_imx8mml() || is_imx8mmdl() ||  is_imx8mmsl())
 		disable_vpu_nodes(blob);
 
@@ -1396,10 +1396,10 @@
 	else if (is_imx8mms() || is_imx8mmsl())
 		disable_cpu_nodes(blob, nodes_path, 3, 4);
 
-#elif defined(CONFIG_IMX8MN)
+#elif IS_ENABLED(CONFIG_IMX8MN)
 	if (is_imx8mnl() || is_imx8mndl() ||  is_imx8mnsl())
 		disable_gpu_nodes(blob);
-#ifdef CONFIG_IMX8MN_LOW_DRIVE_MODE
+#if IS_ENABLED(CONFIG_IMX8MN_LOW_DRIVE_MODE)
 	else {
 		int ldm_gpu = low_drive_gpu_freq(blob);
 
@@ -1415,7 +1415,7 @@
 	else if (is_imx8mns() || is_imx8mnsl() || is_imx8mnus())
 		disable_cpu_nodes(blob, nodes_path, 3, 4);
 
-#elif defined(CONFIG_IMX8MP)
+#elif IS_ENABLED(CONFIG_IMX8MP)
 	if (is_imx8mpul()) {
 		/* Disable GPU */
 		disable_gpu_nodes(blob);
@@ -1471,7 +1471,7 @@
 }
 #endif
 
-#if defined(CONFIG_ARCH_MISC_INIT)
+#if IS_ENABLED(CONFIG_ARCH_MISC_INIT)
 int arch_misc_init(void)
 {
 	if (IS_ENABLED(CONFIG_FSL_CAAM)) {
@@ -1487,8 +1487,8 @@
 }
 #endif
 
-#if defined(CONFIG_XPL_BUILD)
-#if defined(CONFIG_IMX8MQ) || defined(CONFIG_IMX8MM) || defined(CONFIG_IMX8MN)
+#if IS_ENABLED(CONFIG_XPL_BUILD)
+#if IS_ENABLED(CONFIG_IMX8MQ) || IS_ENABLED(CONFIG_IMX8MM) || IS_ENABLED(CONFIG_IMX8MN)
 bool serror_need_skip = true;
 
 void do_error(struct pt_regs *pt_regs)
@@ -1523,7 +1523,7 @@
 #endif
 #endif
 
-#if defined(CONFIG_IMX8MN) || defined(CONFIG_IMX8MP)
+#if IS_ENABLED(CONFIG_IMX8MN) || IS_ENABLED(CONFIG_IMX8MP)
 enum env_location arch_env_get_location(enum env_operation op, int prio)
 {
 	enum boot_device dev = get_boot_device();
@@ -1571,7 +1571,7 @@
 
 #endif
 
-#ifdef CONFIG_IMX_BOOTAUX
+#if IS_ENABLED(CONFIG_IMX_BOOTAUX)
 const struct rproc_att hostmap[] = {
 	/* aux core , host core,  size */
 	{ 0x00000000, 0x007e0000, 0x00020000 },
diff --git a/arch/arm/mach-imx/imx9/Kconfig b/arch/arm/mach-imx/imx9/Kconfig
index 0fd82dc..e6cafdc 100644
--- a/arch/arm/mach-imx/imx9/Kconfig
+++ b/arch/arm/mach-imx/imx9/Kconfig
@@ -73,6 +73,7 @@
 	bool "phycore_imx93"
 	select IMX93
 	select IMX9_LPDDR4X
+	imply OF_UPSTREAM
 	select OF_BOARD_FIXUP
 	select OF_BOARD_SETUP
 
diff --git a/arch/arm/mach-imx/spl_imx_romapi.c b/arch/arm/mach-imx/spl_imx_romapi.c
index 3982f4c..b7008df 100644
--- a/arch/arm/mach-imx/spl_imx_romapi.c
+++ b/arch/arm/mach-imx/spl_imx_romapi.c
@@ -35,12 +35,10 @@
 {
 	u32 sector = 0;
 
-	/*
-	 * Some boards use this value even though MMC is not enabled in SPL, for
-	 * example imx8mn_bsh_smm_s2
-	 */
-#ifdef CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_USE_SECTOR
+#if IS_ENABLED(CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_USE_SECTOR)
 	sector = CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_SECTOR;
+#elif IS_ENABLED(CONFIG_SPL_NAND_RAW_U_BOOT_USE_SECTOR)
+	sector = CONFIG_SPL_NAND_RAW_U_BOOT_SECTOR;
 #endif
 
 	return image_offset + sector * 512 - 0x8000;
diff --git a/arch/arm/mach-kirkwood/Kconfig b/arch/arm/mach-kirkwood/Kconfig
index 6761a9c..f1ccedb 100644
--- a/arch/arm/mach-kirkwood/Kconfig
+++ b/arch/arm/mach-kirkwood/Kconfig
@@ -50,11 +50,13 @@
 	bool "Synology DS109"
 	select KW88F6281
 	select SHEEVA_88SV131
+	select KIRKWOOD_COMMON
 
 config TARGET_GURUPLUG
 	bool "GuruPlug Board"
 	select KW88F6281
 	select SHEEVA_88SV131
+	select KIRKWOOD_COMMON
 
 config TARGET_SHEEVAPLUG
 	bool "SheevaPlug Board"
@@ -86,6 +88,7 @@
 	bool "dns325 Board"
 	select FEROCEON_88FR131
 	select KW88F6281
+	select KIRKWOOD_COMMON
 
 config TARGET_ICONNECT
 	bool "iconnect Board"
@@ -103,15 +106,18 @@
 	bool "LaCie 2Big Network v2 NAS Board"
 	select FEROCEON_88FR131
 	select KW88F6281
+	select KIRKWOOD_COMMON
 
 config TARGET_NETSPACE_V2
 	bool "LaCie netspace_v2 Board"
 	select FEROCEON_88FR131
+	select KIRKWOOD_COMMON
 
 config TARGET_IB62X0
 	bool "ib62x0 Board"
 	select FEROCEON_88FR131
 	select KW88F6281
+	select KIRKWOOD_COMMON
 
 config TARGET_DOCKSTAR
 	bool "Dockstar Board"
@@ -129,6 +135,7 @@
 	bool "BlackArmor NAS220"
 	select FEROCEON_88FR131
 	select KW88F6192
+	select KIRKWOOD_COMMON
 
 config TARGET_NSA310S
 	bool "Zyxel NSA310S"
@@ -146,11 +153,13 @@
 	bool "Allied Telesis SBx81GS24/SBx81GT40/SBx81XS6/SBx81XS16"
 	select FEROCEON_88FR131
 	select KW88F6281
+	select KIRKWOOD_COMMON
 
 config TARGET_SBx81LIFXCAT
 	bool "Allied Telesis SBx81GP24/SBx81GT24"
 	select FEROCEON_88FR131
 	select KW88F6281
+	select KIRKWOOD_COMMON
 
 endchoice
 
diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
index b246235..8c6feae 100644
--- a/arch/riscv/Kconfig
+++ b/arch/riscv/Kconfig
@@ -77,6 +77,14 @@
 	help
 	  Do not enable data cache in U-Boot.
 
+config SYS_CACHE_THEAD_CMO
+	bool "THEAD non-standard cache operations"
+	depends on !SYS_DCACHE_OFF
+	default n
+	help
+	  Support for non-standard cache management operations on SoCs based on
+	  T-Head C906/C910 cores.
+
 config SPL_SYS_DCACHE_OFF
 	bool "Do not enable dcache in SPL"
 	depends on SPL
@@ -118,6 +126,7 @@
 source "arch/riscv/cpu/jh7110/Kconfig"
 source "arch/riscv/cpu/k1/Kconfig"
 source "arch/riscv/cpu/k230/Kconfig"
+source "arch/riscv/cpu/th1520/Kconfig"
 
 # architecture-specific options below
 
diff --git a/arch/riscv/cpu/cpu.c b/arch/riscv/cpu/cpu.c
index 5b31da6..15c4e14 100644
--- a/arch/riscv/cpu/cpu.c
+++ b/arch/riscv/cpu/cpu.c
@@ -18,6 +18,7 @@
 #include <asm/hwcap.h>
 #include <asm/cpufeature.h>
 #include <asm/cache.h>
+#include <asm/global_data.h>
 #include <dm/uclass-internal.h>
 #include <linux/bitops.h>
 #include <linux/log2.h>
@@ -746,3 +747,8 @@
 
 	return 0;
 }
+
+void arch_setup_gd(gd_t *new_gd)
+{
+	set_gd(new_gd);
+}
diff --git a/arch/riscv/cpu/cv1800b/Kconfig b/arch/riscv/cpu/cv1800b/Kconfig
index 7225b12..57f724a 100644
--- a/arch/riscv/cpu/cv1800b/Kconfig
+++ b/arch/riscv/cpu/cv1800b/Kconfig
@@ -6,6 +6,7 @@
 	bool
 	select ARCH_EARLY_INIT_R
 	select SYS_CACHE_SHIFT_6
+	select SYS_CACHE_THEAD_CMO
 	imply CPU
 	imply CPU_RISCV
 	imply RISCV_TIMER
diff --git a/arch/riscv/cpu/cv1800b/Makefile b/arch/riscv/cpu/cv1800b/Makefile
index 95beb34..da12e0f 100644
--- a/arch/riscv/cpu/cv1800b/Makefile
+++ b/arch/riscv/cpu/cv1800b/Makefile
@@ -4,4 +4,3 @@
 
 obj-y += dram.o
 obj-y += cpu.o
-obj-y += cache.o
diff --git a/arch/riscv/cpu/th1520/Kconfig b/arch/riscv/cpu/th1520/Kconfig
new file mode 100644
index 0000000..4d44191
--- /dev/null
+++ b/arch/riscv/cpu/th1520/Kconfig
@@ -0,0 +1,22 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Copyright (C) 2018, Bin Meng <bmeng.cn@gmail.com>
+# Copyright (C) 2025, Yao Zi <ziyao@disroot.org>
+
+config THEAD_TH1520
+	bool
+	select ARCH_EARLY_INIT_R
+	select SYS_CACHE_SHIFT_6
+	select SUPPORT_SPL
+	select BINMAN if SPL
+	select SYS_CACHE_THEAD_CMO
+	select CLK_THEAD
+	imply CPU
+	imply CPU_RISCV
+	imply RISCV_TIMER if (RISCV_SMODE || SPL_RISCV_SMODE)
+	imply RISCV_ACLINT if RISCV_MMODE
+	imply SPL_RISCV_ACLINT if SPL_RISCV_MMODE
+	imply CMD_CPU
+	imply SPL_CPU
+	imply SPL_OPENSBI
+	imply SPL_LOAD_FIT
diff --git a/arch/riscv/cpu/th1520/Makefile b/arch/riscv/cpu/th1520/Makefile
new file mode 100644
index 0000000..5d806c0
--- /dev/null
+++ b/arch/riscv/cpu/th1520/Makefile
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Copyright (C) 2025, Yao Zi <ziyao@disroot.org>
+
+obj-y += cache.o
+obj-y += cpu.o
+obj-y += dram.o
+obj-y += spl.o
diff --git a/arch/riscv/cpu/th1520/cache.c b/arch/riscv/cpu/th1520/cache.c
new file mode 100644
index 0000000..08aa1f7
--- /dev/null
+++ b/arch/riscv/cpu/th1520/cache.c
@@ -0,0 +1,32 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2025 Yao Zi <ziyao@disroot.org>
+ */
+
+#include <asm/io.h>
+#include <cpu_func.h>
+#include <linux/bitops.h>
+
+#define CSR_MHCR		0x7c1
+#define  CSR_MHCR_IE		BIT(0)
+#define  CSR_MHCR_DE		BIT(1)
+
+void icache_enable(void)
+{
+	csr_write(CSR_MHCR, csr_read(CSR_MHCR) | CSR_MHCR_IE);
+}
+
+void dcache_enable(void)
+{
+	csr_write(CSR_MHCR, csr_read(CSR_MHCR) | CSR_MHCR_DE);
+}
+
+int icache_status(void)
+{
+	return (csr_read(CSR_MHCR) & CSR_MHCR_IE) != 0;
+}
+
+int dcache_status(void)
+{
+	return (csr_read(CSR_MHCR) & CSR_MHCR_DE) != 0;
+}
diff --git a/arch/riscv/cpu/th1520/cpu.c b/arch/riscv/cpu/th1520/cpu.c
new file mode 100644
index 0000000..b83f127
--- /dev/null
+++ b/arch/riscv/cpu/th1520/cpu.c
@@ -0,0 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2025 Yao Zi <ziyao@disroot.org>
+ *
+ * TH1520 SoC has a set of undocumented customized PMP registers that are
+ * configured through MMIO operation. It must be disabled before entering
+ * the DRAM region, or an exception will be raised.
+ */
+
+#include <asm/io.h>
+#include <cpu_func.h>
+
+#define TH1520_PMP_BASE		(void *)0xffdc020000
+
+void th1520_invalidate_pmp(void)
+{
+	/* Invalidate the PMP configuration as in vendor U-Boot code */
+	writel(0x0, TH1520_PMP_BASE + 0x0);
+
+	invalidate_icache_all();
+}
diff --git a/arch/riscv/cpu/th1520/dram.c b/arch/riscv/cpu/th1520/dram.c
new file mode 100644
index 0000000..91007c0
--- /dev/null
+++ b/arch/riscv/cpu/th1520/dram.c
@@ -0,0 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2018, Bin Meng <bmeng.cn@gmail.com>
+ */
+
+#include <fdtdec.h>
+#include <init.h>
+#include <asm/global_data.h>
+#include <linux/sizes.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+int dram_init(void)
+{
+	return fdtdec_setup_mem_size_base();
+}
+
+int dram_init_banksize(void)
+{
+	return fdtdec_setup_memory_banksize();
+}
diff --git a/arch/riscv/cpu/th1520/spl.c b/arch/riscv/cpu/th1520/spl.c
new file mode 100644
index 0000000..362fe89
--- /dev/null
+++ b/arch/riscv/cpu/th1520/spl.c
@@ -0,0 +1,96 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2025 Yao Zi <ziyao@disroot.org>
+ */
+#include <asm/arch/iopmp.h>
+#include <asm/io.h>
+#include <dm.h>
+#include <linux/sizes.h>
+#include <log.h>
+#include <init.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#define TH1520_SUBSYS_CLK		(void __iomem *)(0xffff011000 + 0x220)
+#define  TH1520_SUBSYS_CLK_VO_EN	BIT(2)
+#define  TH1520_SUBSYS_CLK_VI_EN	BIT(1)
+#define  TH1520_SUBSYS_CLK_DSP_EN	BIT(0)
+#define TH1520_SUBSYS_RST		(void __iomem *)(0xffff015000 + 0x220)
+#define  TH1520_SUBSYS_RST_VP_N		BIT(3)
+#define  TH1520_SUBSYS_RST_VO_N		BIT(2)
+#define  TH1520_SUBSYS_RST_VI_N		BIT(1)
+#define  TH1520_SUBSYS_RST_DSP_N	BIT(0)
+
+int spl_dram_init(void)
+{
+	int ret;
+	struct udevice *dev;
+
+	ret = fdtdec_setup_mem_size_base();
+	if (ret) {
+		printf("failed to setup memory size and base: %d\n", ret);
+		return ret;
+	}
+
+	/* DDR init */
+	ret = uclass_get_device(UCLASS_RAM, 0, &dev);
+	if (ret) {
+		printf("DRAM init failed: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static void __iomem *th1520_iopmp_regs[] = {
+	TH1520_IOPMP_EMMC,
+	TH1520_IOPMP_SDIO0,
+	TH1520_IOPMP_SDIO1,
+	TH1520_IOPMP_USB0,
+	TH1520_IOPMP_AO,
+	TH1520_IOPMP_AUD,
+	TH1520_IOPMP_CHIP_DBG,
+	TH1520_IOPMP_EIP120I,
+	TH1520_IOPMP_EIP120II,
+	TH1520_IOPMP_EIP120III,
+	TH1520_IOPMP_ISP0,
+	TH1520_IOPMP_ISP1,
+	TH1520_IOPMP_DW200,
+	TH1520_IOPMP_VIPRE,
+	TH1520_IOPMP_VENC,
+	TH1520_IOPMP_VDEC,
+	TH1520_IOPMP_G2D,
+	TH1520_IOPMP_FCE,
+	TH1520_IOPMP_NPU,
+	TH1520_IOPMP_DPU0,
+	TH1520_IOPMP_DPU1,
+	TH1520_IOPMP_GPU,
+	TH1520_IOPMP_GMAC1,
+	TH1520_IOPMP_GMAC2,
+	TH1520_IOPMP_DMAC,
+	TH1520_IOPMP_TEE_DMAC,
+	TH1520_IOPMP_DSP0,
+	TH1520_IOPMP_DSP1,
+};
+
+void harts_early_init(void)
+{
+	int i;
+
+	/*
+	 * Set IOPMPs to the default attribute, allowing the application
+	 * processor to access various peripherals. Subsystem clocks should be
+	 * enabled and resets should be deasserted ahead of time, or the HART
+	 * will hang when configuring corresponding IOPMP entries.
+	 */
+	setbits_le32(TH1520_SUBSYS_CLK, TH1520_SUBSYS_CLK_VO_EN |
+					TH1520_SUBSYS_CLK_VI_EN |
+					TH1520_SUBSYS_CLK_DSP_EN);
+	setbits_le32(TH1520_SUBSYS_RST, TH1520_SUBSYS_RST_VP_N |
+					TH1520_SUBSYS_RST_VO_N |
+					TH1520_SUBSYS_RST_VI_N |
+					TH1520_SUBSYS_RST_DSP_N);
+
+	for (i = 0; i < ARRAY_SIZE(th1520_iopmp_regs); i++)
+		writel(TH1520_IOPMP_DEFAULT_ATTR, th1520_iopmp_regs[i]);
+}
diff --git a/arch/riscv/dts/binman.dtsi b/arch/riscv/dts/binman.dtsi
index 5aeeedd..c5b0464 100644
--- a/arch/riscv/dts/binman.dtsi
+++ b/arch/riscv/dts/binman.dtsi
@@ -5,6 +5,12 @@
 
 #include <config.h>
 
+#ifdef CONFIG_64BIT
+#define ARCH "riscv64"
+#else
+#define ARCH "riscv"
+
+#endif
 / {
 	binman: binman {
 		multiple-images;
@@ -31,12 +37,11 @@
 					description = "U-Boot";
 					type = "standalone";
 					os = "U-Boot";
-					arch = "riscv";
+					arch = ARCH;
 					compression = "none";
 					load = /bits/ 64 <CONFIG_TEXT_BASE>;
 
 					uboot_blob: u-boot-nodtb {
-						filename = "u-boot-nodtb.bin";
 					};
 				};
 #else
@@ -44,7 +49,7 @@
 					description = "Linux";
 					type = "standalone";
 					os = "Linux";
-					arch = "riscv";
+					arch = ARCH;
 					compression = "none";
 					load = /bits/ 64 <CONFIG_TEXT_BASE>;
 
@@ -57,7 +62,7 @@
 				tee {
 					description = "OP-TEE";
 					type = "tee";
-					arch = "riscv";
+					arch = ARCH;
 					compression = "none";
 					os = "tee";
 					load = /bits/ 64 <CONFIG_SPL_OPTEE_LOAD_ADDR>;
@@ -71,7 +76,7 @@
 					description = "OpenSBI fw_dynamic Firmware";
 					type = "firmware";
 					os = "opensbi";
-					arch = "riscv";
+					arch = ARCH;
 					compression = "none";
 					load = /bits/ 64 <CONFIG_SPL_OPENSBI_LOAD_ADDR>;
 					entry = /bits/ 64 <CONFIG_SPL_OPENSBI_LOAD_ADDR>;
diff --git a/arch/riscv/dts/jh7110-common-u-boot.dtsi b/arch/riscv/dts/jh7110-common-u-boot.dtsi
index 6d85b2d..049b0a7 100644
--- a/arch/riscv/dts/jh7110-common-u-boot.dtsi
+++ b/arch/riscv/dts/jh7110-common-u-boot.dtsi
@@ -27,7 +27,6 @@
 	bootph-pre-ram;
 	reg-offset = <0>;
 	current-speed = <115200>;
-	clock-frequency = <24000000>;
 };
 
 &mmc0 {
diff --git a/arch/riscv/dts/jh7110-u-boot.dtsi b/arch/riscv/dts/jh7110-u-boot.dtsi
index a9e318c..f8d1327 100644
--- a/arch/riscv/dts/jh7110-u-boot.dtsi
+++ b/arch/riscv/dts/jh7110-u-boot.dtsi
@@ -6,46 +6,6 @@
 #include <dt-bindings/reset/starfive,jh7110-crg.h>
 
 / {
-	cpus: cpus {
-		bootph-pre-ram;
-
-		S7_0: cpu@0 {
-			bootph-pre-ram;
-			status = "okay";
-			cpu0_intc: interrupt-controller {
-				bootph-pre-ram;
-			};
-		};
-
-		U74_1: cpu@1 {
-			bootph-pre-ram;
-			cpu1_intc: interrupt-controller {
-				bootph-pre-ram;
-			};
-		};
-
-		U74_2: cpu@2 {
-			bootph-pre-ram;
-			cpu2_intc: interrupt-controller {
-				bootph-pre-ram;
-			};
-		};
-
-		U74_3: cpu@3 {
-			bootph-pre-ram;
-			cpu3_intc: interrupt-controller {
-				bootph-pre-ram;
-			};
-		};
-
-		U74_4: cpu@4 {
-			bootph-pre-ram;
-			cpu4_intc: interrupt-controller {
-				bootph-pre-ram;
-			};
-		};
-	};
-
 	timer {
 		compatible = "riscv,timer";
 		interrupts-extended = <&cpu0_intc 5>,
@@ -58,10 +18,6 @@
 	soc {
 		bootph-pre-ram;
 
-		clint: timer@2000000 {
-			bootph-pre-ram;
-		};
-
 		dmc: dmc@15700000 {
 			bootph-pre-ram;
 			compatible = "starfive,jh7110-dmc";
@@ -78,6 +34,34 @@
 	};
 };
 
+&clint {
+	bootph-pre-ram;
+};
+
+&cpu0_intc {
+	bootph-pre-ram;
+};
+
+&cpu1_intc {
+	bootph-pre-ram;
+};
+
+&cpu2_intc {
+	bootph-pre-ram;
+};
+
+&cpu3_intc {
+	bootph-pre-ram;
+};
+
+&cpu4_intc {
+	bootph-pre-ram;
+};
+
+&cpus {
+	bootph-pre-ram;
+};
+
 &osc {
 	bootph-pre-ram;
 };
@@ -107,6 +91,7 @@
 };
 
 &syscrg {
+	assigned-clock-rates = <0>; /* cpufreq not implemented, use defaults */
 	bootph-pre-ram;
 };
 
diff --git a/arch/riscv/dts/th1520-lichee-module-4a.dtsi b/arch/riscv/dts/th1520-lichee-module-4a.dtsi
index 86a81bd..9b255f8 100644
--- a/arch/riscv/dts/th1520-lichee-module-4a.dtsi
+++ b/arch/riscv/dts/th1520-lichee-module-4a.dtsi
@@ -14,6 +14,7 @@
 	memory@0 {
 		device_type = "memory";
 		reg = <0x0 0x00000000 0x2 0x00000000>;
+		bootph-pre-ram;
 	};
 };
 
@@ -25,14 +26,6 @@
 	clock-frequency = <32768>;
 };
 
-&apb_clk {
-	clock-frequency = <62500000>;
-};
-
-&uart_sclk {
-	clock-frequency = <100000000>;
-};
-
 &emmc {
 	bus-width = <8>;
 	max-frequency = <198000000>;
diff --git a/arch/riscv/dts/th1520-lichee-pi-4a.dts b/arch/riscv/dts/th1520-lichee-pi-4a.dts
index a1248b2..49af88b 100644
--- a/arch/riscv/dts/th1520-lichee-pi-4a.dts
+++ b/arch/riscv/dts/th1520-lichee-pi-4a.dts
@@ -4,6 +4,7 @@
  */
 
 #include "th1520-lichee-module-4a.dtsi"
+#include "thead-th1520-binman.dtsi"
 
 / {
 	model = "Sipeed Lichee Pi 4A";
diff --git a/arch/riscv/dts/th1520.dtsi b/arch/riscv/dts/th1520.dtsi
index cbe3481..28107a9 100644
--- a/arch/riscv/dts/th1520.dtsi
+++ b/arch/riscv/dts/th1520.dtsi
@@ -4,6 +4,7 @@
  * Copyright (C) 2023 Jisheng Zhang <jszhang@kernel.org>
  */
 
+#include <dt-bindings/clock/thead,th1520-clk-ap.h>
 #include <dt-bindings/interrupt-controller/irq.h>
 
 / {
@@ -14,6 +15,7 @@
 	cpus: cpus {
 		#address-cells = <1>;
 		#size-cells = <0>;
+		bootph-pre-ram;
 		timebase-frequency = <3000000>;
 
 		c910_0: cpu@0 {
@@ -21,6 +23,7 @@
 			device_type = "cpu";
 			riscv,isa = "rv64imafdc";
 			reg = <0>;
+			bootph-pre-ram;
 			i-cache-block-size = <64>;
 			i-cache-size = <65536>;
 			i-cache-sets = <512>;
@@ -42,6 +45,7 @@
 			device_type = "cpu";
 			riscv,isa = "rv64imafdc";
 			reg = <1>;
+			bootph-pre-ram;
 			i-cache-block-size = <64>;
 			i-cache-size = <65536>;
 			i-cache-sets = <512>;
@@ -63,6 +67,7 @@
 			device_type = "cpu";
 			riscv,isa = "rv64imafdc";
 			reg = <2>;
+			bootph-pre-ram;
 			i-cache-block-size = <64>;
 			i-cache-size = <65536>;
 			i-cache-sets = <512>;
@@ -84,6 +89,7 @@
 			device_type = "cpu";
 			riscv,isa = "rv64imafdc";
 			reg = <3>;
+			bootph-pre-ram;
 			i-cache-block-size = <64>;
 			i-cache-size = <65536>;
 			i-cache-sets = <512>;
@@ -122,25 +128,6 @@
 		#clock-cells = <0>;
 	};
 
-	apb_clk: apb-clk-clock {
-		compatible = "fixed-clock";
-		clock-output-names = "apb_clk";
-		#clock-cells = <0>;
-	};
-
-	uart_sclk: uart-sclk-clock {
-		compatible = "fixed-clock";
-		clock-output-names = "uart_sclk";
-		#clock-cells = <0>;
-	};
-
-	sdhci_clk: sdhci-clock {
-		compatible = "fixed-clock";
-		clock-frequency = <198000000>;
-		clock-output-names = "sdhci_clk";
-		#clock-cells = <0>;
-	};
-
 	soc {
 		compatible = "simple-bus";
 		interrupt-parent = <&plic>;
@@ -173,8 +160,10 @@
 		uart0: serial@ffe7014000 {
 			compatible = "snps,dw-apb-uart";
 			reg = <0xff 0xe7014000 0x0 0x100>;
+			bootph-pre-ram;
 			interrupts = <36 IRQ_TYPE_LEVEL_HIGH>;
-			clocks = <&uart_sclk>;
+			clocks = <&clk CLK_UART_SCLK>, <&clk CLK_UART0_PCLK>;
+			clock-names = "buadclk", "apb_pclk";
 			reg-shift = <2>;
 			reg-io-width = <4>;
 			status = "disabled";
@@ -184,7 +173,7 @@
 			compatible = "thead,th1520-dwcmshc";
 			reg = <0xff 0xe7080000 0x0 0x10000>;
 			interrupts = <62 IRQ_TYPE_LEVEL_HIGH>;
-			clocks = <&sdhci_clk>;
+			clocks = <&clk CLK_EMMC_SDIO>;
 			clock-names = "core";
 			status = "disabled";
 		};
@@ -193,7 +182,7 @@
 			compatible = "thead,th1520-dwcmshc";
 			reg = <0xff 0xe7090000 0x0 0x10000>;
 			interrupts = <64 IRQ_TYPE_LEVEL_HIGH>;
-			clocks = <&sdhci_clk>;
+			clocks = <&clk CLK_EMMC_SDIO>;
 			clock-names = "core";
 			status = "disabled";
 		};
@@ -202,7 +191,7 @@
 			compatible = "thead,th1520-dwcmshc";
 			reg = <0xff 0xe70a0000 0x0 0x10000>;
 			interrupts = <71 IRQ_TYPE_LEVEL_HIGH>;
-			clocks = <&sdhci_clk>;
+			clocks = <&clk CLK_EMMC_SDIO>;
 			clock-names = "core";
 			status = "disabled";
 		};
@@ -211,7 +200,8 @@
 			compatible = "snps,dw-apb-uart";
 			reg = <0xff 0xe7f00000 0x0 0x100>;
 			interrupts = <37 IRQ_TYPE_LEVEL_HIGH>;
-			clocks = <&uart_sclk>;
+			clocks = <&clk CLK_UART_SCLK>, <&clk CLK_UART1_PCLK>;
+			clock-names = "buadclk", "apb_pclk";
 			reg-shift = <2>;
 			reg-io-width = <4>;
 			status = "disabled";
@@ -221,7 +211,8 @@
 			compatible = "snps,dw-apb-uart";
 			reg = <0xff 0xe7f04000 0x0 0x100>;
 			interrupts = <39 IRQ_TYPE_LEVEL_HIGH>;
-			clocks = <&uart_sclk>;
+			clocks = <&clk CLK_UART_SCLK>, <&clk CLK_UART3_PCLK>;
+			clock-names = "buadclk", "apb_pclk";
 			reg-shift = <2>;
 			reg-io-width = <4>;
 			status = "disabled";
@@ -230,6 +221,8 @@
 		gpio2: gpio@ffe7f34000 {
 			compatible = "snps,dw-apb-gpio";
 			reg = <0xff 0xe7f34000 0x0 0x1000>;
+			clocks = <&clk CLK_GPIO2>;
+			clock-names = "bus";
 			#address-cells = <1>;
 			#size-cells = <0>;
 
@@ -248,6 +241,8 @@
 		gpio3: gpio@ffe7f38000 {
 			compatible = "snps,dw-apb-gpio";
 			reg = <0xff 0xe7f38000 0x0 0x1000>;
+			clocks = <&clk CLK_GPIO3>;
+			clock-names = "bus";
 			#address-cells = <1>;
 			#size-cells = <0>;
 
@@ -266,6 +261,8 @@
 		gpio0: gpio@ffec005000 {
 			compatible = "snps,dw-apb-gpio";
 			reg = <0xff 0xec005000 0x0 0x1000>;
+			clocks = <&clk CLK_GPIO0>;
+			clock-names = "bus";
 			#address-cells = <1>;
 			#size-cells = <0>;
 
@@ -284,6 +281,8 @@
 		gpio1: gpio@ffec006000 {
 			compatible = "snps,dw-apb-gpio";
 			reg = <0xff 0xec006000 0x0 0x1000>;
+			clocks = <&clk CLK_GPIO1>;
+			clock-names = "bus";
 			#address-cells = <1>;
 			#size-cells = <0>;
 
@@ -303,16 +302,24 @@
 			compatible = "snps,dw-apb-uart";
 			reg = <0xff 0xec010000 0x0 0x4000>;
 			interrupts = <38 IRQ_TYPE_LEVEL_HIGH>;
-			clocks = <&uart_sclk>;
+			clocks = <&clk CLK_UART_SCLK>, <&clk CLK_UART2_PCLK>;
+			clock-names = "buadclk", "apb_pclk";
 			reg-shift = <2>;
 			reg-io-width = <4>;
 			status = "disabled";
 		};
 
+		clk: clock-controller@ffef010000 {
+			compatible = "thead,th1520-clk-ap";
+			reg = <0xff 0xef010000 0x0 0x1000>;
+			clocks = <&osc>;
+			#clock-cells = <1>;
+		};
+
 		timer0: timer@ffefc32000 {
 			compatible = "snps,dw-apb-timer";
 			reg = <0xff 0xefc32000 0x0 0x14>;
-			clocks = <&apb_clk>;
+			clocks = <&clk CLK_PERI_APB_PCLK>;
 			clock-names = "timer";
 			interrupts = <16 IRQ_TYPE_LEVEL_HIGH>;
 			status = "disabled";
@@ -321,7 +328,7 @@
 		timer1: timer@ffefc32014 {
 			compatible = "snps,dw-apb-timer";
 			reg = <0xff 0xefc32014 0x0 0x14>;
-			clocks = <&apb_clk>;
+			clocks = <&clk CLK_PERI_APB_PCLK>;
 			clock-names = "timer";
 			interrupts = <17 IRQ_TYPE_LEVEL_HIGH>;
 			status = "disabled";
@@ -330,7 +337,7 @@
 		timer2: timer@ffefc32028 {
 			compatible = "snps,dw-apb-timer";
 			reg = <0xff 0xefc32028 0x0 0x14>;
-			clocks = <&apb_clk>;
+			clocks = <&clk CLK_PERI_APB_PCLK>;
 			clock-names = "timer";
 			interrupts = <18 IRQ_TYPE_LEVEL_HIGH>;
 			status = "disabled";
@@ -339,7 +346,7 @@
 		timer3: timer@ffefc3203c {
 			compatible = "snps,dw-apb-timer";
 			reg = <0xff 0xefc3203c 0x0 0x14>;
-			clocks = <&apb_clk>;
+			clocks = <&clk CLK_PERI_APB_PCLK>;
 			clock-names = "timer";
 			interrupts = <19 IRQ_TYPE_LEVEL_HIGH>;
 			status = "disabled";
@@ -349,7 +356,8 @@
 			compatible = "snps,dw-apb-uart";
 			reg = <0xff 0xf7f08000 0x0 0x4000>;
 			interrupts = <40 IRQ_TYPE_LEVEL_HIGH>;
-			clocks = <&uart_sclk>;
+			clocks = <&clk CLK_UART_SCLK>, <&clk CLK_UART4_PCLK>;
+			clock-names = "buadclk", "apb_pclk";
 			reg-shift = <2>;
 			reg-io-width = <4>;
 			status = "disabled";
@@ -359,16 +367,27 @@
 			compatible = "snps,dw-apb-uart";
 			reg = <0xff 0xf7f0c000 0x0 0x4000>;
 			interrupts = <41 IRQ_TYPE_LEVEL_HIGH>;
-			clocks = <&uart_sclk>;
+			clocks = <&clk CLK_UART_SCLK>, <&clk CLK_UART5_PCLK>;
+			clock-names = "buadclk", "apb_pclk";
 			reg-shift = <2>;
 			reg-io-width = <4>;
 			status = "disabled";
 		};
 
+		ddrc: ddrc@fffd000000 {
+			compatible = "thead,th1520-ddrc";
+			reg = <0xff 0xfd000000 0x0 0x1000000>,
+			      <0xff 0xfe000000 0x0 0x1000000>,
+			      <0xff 0xff000000 0x0 0x4000>,
+			      <0xff 0xff005000 0x0 0x1000>;
+			reg-names = "phy-0", "phy-1", "ctrl", "sys";
+			bootph-pre-ram;
+		};
+
 		timer4: timer@ffffc33000 {
 			compatible = "snps,dw-apb-timer";
 			reg = <0xff 0xffc33000 0x0 0x14>;
-			clocks = <&apb_clk>;
+			clocks = <&clk CLK_PERI_APB_PCLK>;
 			clock-names = "timer";
 			interrupts = <20 IRQ_TYPE_LEVEL_HIGH>;
 			status = "disabled";
@@ -377,7 +396,7 @@
 		timer5: timer@ffffc33014 {
 			compatible = "snps,dw-apb-timer";
 			reg = <0xff 0xffc33014 0x0 0x14>;
-			clocks = <&apb_clk>;
+			clocks = <&clk CLK_PERI_APB_PCLK>;
 			clock-names = "timer";
 			interrupts = <21 IRQ_TYPE_LEVEL_HIGH>;
 			status = "disabled";
@@ -386,7 +405,7 @@
 		timer6: timer@ffffc33028 {
 			compatible = "snps,dw-apb-timer";
 			reg = <0xff 0xffc33028 0x0 0x14>;
-			clocks = <&apb_clk>;
+			clocks = <&clk CLK_PERI_APB_PCLK>;
 			clock-names = "timer";
 			interrupts = <22 IRQ_TYPE_LEVEL_HIGH>;
 			status = "disabled";
@@ -395,7 +414,7 @@
 		timer7: timer@ffffc3303c {
 			compatible = "snps,dw-apb-timer";
 			reg = <0xff 0xffc3303c 0x0 0x14>;
-			clocks = <&apb_clk>;
+			clocks = <&clk CLK_PERI_APB_PCLK>;
 			clock-names = "timer";
 			interrupts = <23 IRQ_TYPE_LEVEL_HIGH>;
 			status = "disabled";
diff --git a/arch/riscv/dts/thead-th1520-binman.dtsi b/arch/riscv/dts/thead-th1520-binman.dtsi
new file mode 100644
index 0000000..f060639
--- /dev/null
+++ b/arch/riscv/dts/thead-th1520-binman.dtsi
@@ -0,0 +1,55 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2025 Yao Zi <ziyao@disroot.org>
+ */
+
+#include <config.h>
+
+/ {
+	binman: binman {
+	};
+};
+
+&binman {
+	filename = "u-boot-with-spl.bin";
+
+	u-boot-spl {
+	};
+
+	ddr-fw {
+		filename = "th1520-ddr-firmware.bin";
+		type = "blob-ext";
+	};
+
+	fit {
+		offset = <CONFIG_SPL_PAD_TO>;
+
+		description = "Configuration to load M-mode U-Boot";
+
+		#address-cells = <2>;
+		fit,fdt-list = "of-list";
+
+		images {
+			uboot {
+				description = "U-Boot";
+				type = "standalone";
+				os = "U-boot";
+				arch = "riscv";
+				compression = "none";
+				load = /bits/ 64 <CONFIG_TEXT_BASE>;
+
+				uboot_blob: u-boot {
+				};
+			};
+		};
+
+		configurations {
+			default = "conf-th1520-lichee-pi-4a";
+
+			conf-th1520-lichee-pi-4a {
+				description = "th1520-lichee-pi-4a";
+				loadables = "uboot";
+			};
+		};
+	};
+};
diff --git a/arch/riscv/include/asm/arch-th1520/cpu.h b/arch/riscv/include/asm/arch-th1520/cpu.h
new file mode 100644
index 0000000..837f0b8
--- /dev/null
+++ b/arch/riscv/include/asm/arch-th1520/cpu.h
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (c) 2025 Yao Zi <ziyao@disroot.org>
+ */
+
+#ifndef _ASM_TH1520_CPU_H_
+#define _ASM_TH1520_CPU_H_
+void th1520_invalidate_pmp(void);
+#endif /* _ASM_TH1520_CPU_H_ */
diff --git a/arch/riscv/include/asm/arch-th1520/iopmp.h b/arch/riscv/include/asm/arch-th1520/iopmp.h
new file mode 100644
index 0000000..3dc766b
--- /dev/null
+++ b/arch/riscv/include/asm/arch-th1520/iopmp.h
@@ -0,0 +1,42 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2025 Yao Zi <ziyao@disroot.org>
+ */
+#ifndef _ASM_ARCH_TH1520_IOPMP_H_
+#define _ASM_ARCH_TH1520_IOPMP_H_
+
+#define TH1520_IOPMP_EMMC		(void *)0xfffc0280c0
+#define TH1520_IOPMP_SDIO0		(void *)0xfffc0290c0
+#define TH1520_IOPMP_SDIO1		(void *)0xfffc02a0c0
+#define TH1520_IOPMP_USB0		(void *)0xfffc02e0c0
+#define TH1520_IOPMP_AO			(void *)0xffffc210c0
+#define TH1520_IOPMP_AUD		(void *)0xffffc220c0
+#define TH1520_IOPMP_CHIP_DBG		(void *)0xffffc370c0
+#define TH1520_IOPMP_EIP120I		(void *)0xffff2200c0
+#define TH1520_IOPMP_EIP120II		(void *)0xffff2300c0
+#define TH1520_IOPMP_EIP120III		(void *)0xffff2400c0
+#define TH1520_IOPMP_ISP0		(void *)0xfff40800c0
+#define TH1520_IOPMP_ISP1		(void *)0xfff40810c0
+#define TH1520_IOPMP_DW200		(void *)0xfff40820c0
+#define TH1520_IOPMP_VIPRE		(void *)0xfff40830c0
+#define TH1520_IOPMP_VENC		(void *)0xfffcc600c0
+#define TH1520_IOPMP_VDEC		(void *)0xfffcc610c0
+#define TH1520_IOPMP_G2D		(void *)0xfffcc620c0
+#define TH1520_IOPMP_FCE		(void *)0xfffcc630c0
+#define TH1520_IOPMP_NPU		(void *)0xffff01c0c0
+#define TH1520_IOPMP_DPU0		(void *)0xffff5200c0
+#define TH1520_IOPMP_DPU1		(void *)0xffff5210c0
+#define TH1520_IOPMP_GPU		(void *)0xffff5220c0
+#define TH1520_IOPMP_GMAC1		(void *)0xfffc0010c0
+#define TH1520_IOPMP_GMAC2		(void *)0xfffc0020c0
+#define TH1520_IOPMP_DMAC		(void *)0xffffc200c0
+#define TH1520_IOPMP_TEE_DMAC		(void *)0xffff2500c0
+#define TH1520_IOPMP_DSP0		(void *)0xffff0580c0
+#define TH1520_IOPMP_DSP1		(void *)0xffff0590c0
+#define TH1520_IOPMP_AUDIO		(void *)0xffffc220c0
+#define TH1520_IOPMP_AUDIO0		(void *)0xffcb02e0c0
+#define TH1520_IOPMP_AUDIO1		(void *)0xffcb02f0c0
+
+#define TH1520_IOPMP_DEFAULT_ATTR	0xffffffff
+
+#endif // _ASM_ARCH_TH1520_IOPMP_H_
diff --git a/arch/riscv/include/asm/arch-th1520/spl.h b/arch/riscv/include/asm/arch-th1520/spl.h
new file mode 100644
index 0000000..59aed8c
--- /dev/null
+++ b/arch/riscv/include/asm/arch-th1520/spl.h
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2025 Yao Zi <ziyao@disroot.org>
+ */
+#ifndef _ASM_ARCH_TH1520_SPL_H_
+#define _ASM_ARCH_TH1520_SPL_H_
+
+void spl_dram_init(void);
+
+#endif // _ASM_ARCH_TH1520_SPL_H_
diff --git a/arch/riscv/include/asm/global_data.h b/arch/riscv/include/asm/global_data.h
index d356752..47b5e2c 100644
--- a/arch/riscv/include/asm/global_data.h
+++ b/arch/riscv/include/asm/global_data.h
@@ -14,6 +14,7 @@
 #include <asm/smp.h>
 #include <asm/u-boot.h>
 #include <compiler.h>
+#include <config.h>
 
 /* Architecture-specific global data */
 struct arch_global_data {
@@ -47,8 +48,26 @@
 
 #include <asm-generic/global_data.h>
 
+#if defined(__clang__) || CONFIG_IS_ENABLED(LTO)
+
+#define DECLARE_GLOBAL_DATA_PTR
+#define gd			get_gd()
+
+static inline gd_t *get_gd(void)
+{
+	gd_t *gd_ptr;
+
+	__asm__ volatile ("mv %0, gp\n" : "=r" (gd_ptr));
+
+	return gd_ptr;
+}
+
+#else
+
 #define DECLARE_GLOBAL_DATA_PTR register gd_t *gd asm ("gp")
 
+#endif
+
 static inline void set_gd(volatile gd_t *gd_ptr)
 {
 #ifdef CONFIG_64BIT
diff --git a/arch/riscv/include/asm/insn-def.h b/arch/riscv/include/asm/insn-def.h
index 19a10ca..1869342 100644
--- a/arch/riscv/include/asm/insn-def.h
+++ b/arch/riscv/include/asm/insn-def.h
@@ -5,8 +5,8 @@
  * Ported from linux insn-def.h.
  */
 
-#ifndef _ASM_RISCV_BARRIER_H
-#define _ASM_RISCV_BARRIER_H
+#ifndef _ASM_RISCV_INSN_DEF_H
+#define _ASM_RISCV_INSN_DEF_H
 
 #define INSN_I_SIMM12_SHIFT		20
 #define INSN_I_RS1_SHIFT		15
@@ -36,4 +36,4 @@
 	__INSN_I(RV_##opcode, RV_##func3, RV_##rd,		\
 		 RV_##rs1, RV_##simm12)
 
-#endif /* _ASM_RISCV_BARRIER_H */
+#endif /* _ASM_RISCV_INSN_DEF_H */
diff --git a/arch/riscv/include/asm/u-boot.h b/arch/riscv/include/asm/u-boot.h
index d5e1d5f..a90cc4c 100644
--- a/arch/riscv/include/asm/u-boot.h
+++ b/arch/riscv/include/asm/u-boot.h
@@ -23,6 +23,10 @@
 #include <asm/u-boot-riscv.h>
 
 /* For image.h:image_check_target_arch() */
+#ifdef CONFIG_64BIT
+#define IH_ARCH_DEFAULT IH_ARCH_RISCV64
+#else
 #define IH_ARCH_DEFAULT IH_ARCH_RISCV
+#endif
 
 #endif	/* _U_BOOT_H_ */
diff --git a/arch/riscv/lib/Makefile b/arch/riscv/lib/Makefile
index 189b35c..db8d235 100644
--- a/arch/riscv/lib/Makefile
+++ b/arch/riscv/lib/Makefile
@@ -11,6 +11,7 @@
 obj-$(CONFIG_CMD_GO) += boot.o
 obj-y	+= cache.o
 obj-$(CONFIG_SIFIVE_CACHE) += sifive_cache.o
+obj-$(CONFIG_SYS_CACHE_THEAD_CMO) += thead_cmo.o
 ifeq ($(CONFIG_$(PHASE_)RISCV_MMODE),y)
 obj-$(CONFIG_$(PHASE_)RISCV_ACLINT) += aclint_ipi.o
 obj-$(CONFIG_ANDES_PLICSW) += andes_plicsw.o
diff --git a/arch/riscv/lib/bootm.c b/arch/riscv/lib/bootm.c
index 9544907..c98c5e7 100644
--- a/arch/riscv/lib/bootm.c
+++ b/arch/riscv/lib/bootm.c
@@ -90,6 +90,10 @@
 	announce_and_cleanup(fake);
 
 	if (!fake) {
+		if (images->os.arch != IH_ARCH_DEFAULT) {
+			printf("Image arch not compatible with host arch.\n");
+			hang();
+		}
 		if (CONFIG_IS_ENABLED(OF_LIBFDT) && images->ft_len) {
 #ifdef CONFIG_SMP
 			ret = smp_call_function(images->ep,
diff --git a/arch/riscv/cpu/cv1800b/cache.c b/arch/riscv/lib/thead_cmo.c
similarity index 100%
rename from arch/riscv/cpu/cv1800b/cache.c
rename to arch/riscv/lib/thead_cmo.c
diff --git a/arch/x86/lib/tables.c b/arch/x86/lib/tables.c
index ec52992..d7f183f 100644
--- a/arch/x86/lib/tables.c
+++ b/arch/x86/lib/tables.c
@@ -126,6 +126,8 @@
 			use_high = true;
 			if (!gd->arch.table_start_high)
 				gd->arch.table_start_high = rom_addr;
+			if (table->tag == BLOBLISTT_SMBIOS_TABLES)
+				gd_set_smbios_start(rom_addr);
 		}
 		rom_table_end = table->write(rom_addr);
 		if (!rom_table_end) {
diff --git a/board/Synology/ds109/ds109.c b/board/Synology/ds109/ds109.c
index 4f39757..f3a914c 100644
--- a/board/Synology/ds109/ds109.c
+++ b/board/Synology/ds109/ds109.c
@@ -97,24 +97,6 @@
 	return 0;
 }
 
-/* Synology reset uses UART */
-#include <ns16550.h>
-#define SOFTWARE_SHUTDOWN   0x31
-#define SOFTWARE_REBOOT     0x43
-#define CFG_SYS_NS16550_COM2		KW_UART1_BASE
-void reset_misc(void)
-{
-	int b_d;
-	printf("Synology reset...");
-	udelay(50000);
-
-	b_d = ns16550_calc_divisor((struct ns16550 *)CFG_SYS_NS16550_COM2,
-				   CFG_SYS_NS16550_CLK, 9600);
-	ns16550_init((struct ns16550 *)CFG_SYS_NS16550_COM2, b_d);
-	ns16550_putc((struct ns16550 *)CFG_SYS_NS16550_COM2,
-		     SOFTWARE_REBOOT);
-}
-
 #ifdef CONFIG_RESET_PHY_R
 /* Configure and enable MV88E1116 PHY */
 void reset_phy(void)
diff --git a/board/armltd/total_compute/Makefile b/board/armltd/total_compute/Makefile
index f1ef5a0..615c787 100644
--- a/board/armltd/total_compute/Makefile
+++ b/board/armltd/total_compute/Makefile
@@ -4,4 +4,4 @@
 # Usama Arif <usama.arif@arm.com>
 
 obj-y	:= total_compute.o
-obj-y	+= lowlevel_init.o
+obj-$(CONFIG_OF_HAS_PRIOR_STAGE)	+= lowlevel_init.o
diff --git a/board/armltd/total_compute/total_compute.c b/board/armltd/total_compute/total_compute.c
index 75ba3c3..75bc6b0 100644
--- a/board/armltd/total_compute/total_compute.c
+++ b/board/armltd/total_compute/total_compute.c
@@ -31,6 +31,7 @@
 
 struct mm_region *mem_map = total_compute_mem_map;
 
+#ifdef CONFIG_OF_HAS_PRIOR_STAGE
 /*
  * Push the variable into the .data section so that it
  * does not get cleared later.
@@ -45,14 +46,16 @@
 	*fdtp = (void *)fw_dtb_pointer;
 	return 0;
 }
+#endif
 
 int misc_init_r(void)
 {
 	size_t base;
 
+#ifdef CONFIG_OF_HAS_PRIOR_STAGE
 	if (!env_get("fdt_addr_r"))
 		env_set_hex("fdt_addr_r", fw_dtb_pointer);
-
+#endif
 	if (!env_get("kernel_addr_r")) {
 		/*
 		 * The kernel has to be 2M aligned and the first 64K at the
diff --git a/board/armltd/total_compute/total_compute.env b/board/armltd/total_compute/total_compute.env
index 7924632..84d5a10 100644
--- a/board/armltd/total_compute/total_compute.env
+++ b/board/armltd/total_compute/total_compute.env
@@ -11,6 +11,12 @@
         blk_dev=mmc;
     fi;
     echo block device is ${blk_dev};
+    if test -n "${fdt_addr_r}"; then
+        echo "Custom FDT at ${fdt_addr_r}";
+    else;
+        setenv fdt_addr_r ${fdtcontroladdr};
+        echo "FDT address is now set to ${fdt_addr_r}";
+    fi;
     if part number ${blk_dev} 0 vbmeta is_avb; then
         echo '${blk_dev} with vbmeta partition detected.';
         echo 'Starting Android Verified boot...';
diff --git a/board/bsh/imx6ulz_smm_m2/Makefile b/board/bsh/imx6ulz_smm_m2/Makefile
index 5987041..4f4d67f 100644
--- a/board/bsh/imx6ulz_smm_m2/Makefile
+++ b/board/bsh/imx6ulz_smm_m2/Makefile
@@ -2,5 +2,4 @@
 # (C) Copyright 2021 Amarula Solutions B.V.
 
 obj-y  := imx6ulz_smm_m2.o
-obj-$(CONFIG_XPL_BUILD) += spl.o
-
+obj-$(CONFIG_XPL_BUILD) += spl.o ddr3l_timing_512m.o ddr3l_timing_256m.o ddr3l_timing_128m.o
diff --git a/board/bsh/imx6ulz_smm_m2/ddr3l_timing_128m.c b/board/bsh/imx6ulz_smm_m2/ddr3l_timing_128m.c
new file mode 100644
index 0000000..66c3483
--- /dev/null
+++ b/board/bsh/imx6ulz_smm_m2/ddr3l_timing_128m.c
@@ -0,0 +1,169 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#include "spl_mtypes.h"
+
+static const struct dram_cfg_param ddr_ddrc_cfg_128mb[] = {
+	/* IOMUX */
+
+	/* DDR IO Type: */
+	{0x020e04b4, 0x000C0000},	/* IOMUXC_SW_PAD_CTL_GRP_DDR_TYPE */
+	{0x020e04ac, 0x00000000},	/* IOMUXC_SW_PAD_CTL_GRP_DDRPKE */
+
+	/* Clock: */
+	{0x020e027c, 0x00000028},	/* IOMUXC_SW_PAD_CTL_PAD_DRAM_SDCLK_0 */
+
+	/* Address: */
+	{0x020e0250, 0x00000028},	/* IOMUXC_SW_PAD_CTL_PAD_DRAM_CAS */
+	{0x020e024c, 0x00000028},	/* IOMUXC_SW_PAD_CTL_PAD_DRAM_RAS */
+	{0x020e0490, 0x00000028},	/* IOMUXC_SW_PAD_CTL_GRP_ADDDS */
+
+	/* Control: */
+	{0x020e0288, 0x000C0028},	/* IOMUXC_SW_PAD_CTL_PAD_DRAM_RESET */
+	{0x020e0270, 0x00000000},	/*
+					 * IOMUXC_SW_PAD_CTL_PAD_DRAM_SDBA2 - DSE can be configured
+					 * using Group Control Register IOMUXC_SW_PAD_CTL_GRP_CTLDS
+					 */
+
+	{0x020e0260, 0x00000028},	/* IOMUXC_SW_PAD_CTL_PAD_DRAM_SDODT0 */
+	{0x020e0264, 0x00000028},	/* IOMUXC_SW_PAD_CTL_PAD_DRAM_SDODT1 */
+	{0x020e04a0, 0x00000028},	/* IOMUXC_SW_PAD_CTL_GRP_CTLDS */
+
+	/* Data Strobes: */
+	{0x020e0494, 0x00020000},	/* IOMUXC_SW_PAD_CTL_GRP_DDRMODE_CTL */
+	{0x020e0280, 0x00000028},	/* IOMUXC_SW_PAD_CTL_PAD_DRAM_SDQS0 */
+	{0x020e0284, 0x00000028},	/* IOMUXC_SW_PAD_CTL_PAD_DRAM_SDQS1 */
+
+	/* Data: */
+	{0x020e04b0, 0x00020000},	/* IOMUXC_SW_PAD_CTL_GRP_DDRMODE */
+	{0x020e0498, 0x00000028},	/* IOMUXC_SW_PAD_CTL_GRP_B0DS */
+	{0x020e04a4, 0x00000028},	/* IOMUXC_SW_PAD_CTL_GRP_B1DS */
+
+	{0x020e0244, 0x00000028},	/* IOMUXC_SW_PAD_CTL_PAD_DRAM_DQM0 */
+	{0x020e0248, 0x00000028},	/* IOMUXC_SW_PAD_CTL_PAD_DRAM_DQM1 */
+
+	/*
+	 * =============================================================================
+	 * DDR Controller Registers
+	 * =============================================================================
+	 * Manufacturer:ISSI
+	 * Device Part Number:IS43TR16640BL-125JBLI
+	 * Clock Freq.: 400MHz
+	 * Density per CS in Gb: 1
+	 * Chip Selects used:1
+	 * Number of Banks:8
+	 * Row address:    13
+	 * Column address: 10
+	 * Data bus width16
+	 * =============================================================================
+	 */
+	{0x021b001c, 0x00008000},	/*
+					 * MMDC0_MDSCR, set the Configuration request bit
+					 * during MMDC set up
+					 */
+
+	/*
+	 * =============================================================================
+	 * Calibration setup.
+	 * =============================================================================
+	 */
+	{0x021b0800, 0xA1390003},	/*
+					 * DDR_PHY_P0_MPZQHWCTRL, enable both one-time & periodic
+					 * HW ZQ calibration.
+					 */
+
+	/*
+	 * For target board, may need to run write leveling calibration to fine tune
+	 * these settings.
+	 */
+	{0x021b080c, 0x00000000},
+
+	/* Read DQS Gating calibration */
+	{0x021b083c, 0x41480148},	/* MPDGCTRL0 PHY0 */
+
+	/* Read calibration */
+	{0x021b0848, 0x40403A3E},	/* MPRDDLCTL PHY0 */
+
+	/* Write calibration */
+	{0x021b0850, 0x4040362E},	/* MPWRDLCTL PHY0 */
+
+	/*
+	 * Read data bit delay: 3 is the recommended default value, although out of reset
+	 * value is 0.
+	 */
+	{0x021b081c, 0x33333333},	/* MMDC_MPRDDQBY0DL */
+	{0x021b0820, 0x33333333},	/* MMDC_MPRDDQBY1DL */
+
+	/* Write data bit delay: */
+	{0x021b082c, 0xF3333333},	/* MMDC_MPWRDQBY0DL */
+	{0x021b0830, 0xF3333333},	/* MMDC_MPWRDQBY1DL */
+
+	/* DQS&CLK Duty Cycle */
+	{0x021b08c0, 0x00944009},	/* [MMDC_MPDCCR] MMDC Duty Cycle Control Register */
+
+	/* Complete calibration by forced measurement: */
+	{0x021b08b8, 0x00000800},	/* DDR_PHY_P0_MPMUR0, frc_msr */
+
+	/*
+	 * =============================================================================
+	 * Calibration setup end
+	 * =============================================================================
+	 */
+
+	/* MMDC init: */
+	{0x021b0004, 0x0002002D},	/* MMDC0_MDPDC */
+	{0x021b0008, 0x1B333030},	/* MMDC0_MDOTC */
+	{0x021b000c, 0x2B2F52F3},	/* MMDC0_MDCFG0 */
+	{0x021b0010, 0xB66D0B63},	/* MMDC0_MDCFG1 */
+	{0x021b0014, 0x01FF00DB},	/* MMDC0_MDCFG2 */
+
+	/*
+	 * MDMISC: RALAT kept to the high level of 5.
+	 * MDMISC: consider reducing RALAT if your 528MHz board design allow that.
+	 * Lower RALAT benefits:
+	 * a. better operation at low frequency, for LPDDR2 freq < 100MHz, change RALAT to 3
+	 * b. Small performance improvement
+	 */
+	{0x021b0018, 0x00211740},	/* MMDC0_MDMISC */
+	{0x021b001c, 0x00008000},	/*
+					 * MMDC0_MDSCR, set the Configuration request bit during
+					 * MMDC set up
+					 */
+	{0x021b002c, 0x000026D2},	/* MMDC0_MDRWD */
+	{0x021b0030, 0x002F1023},	/* MMDC0_MDOR */
+	{0x021b0040, 0x00000043},	/* Chan0 CS0_END */
+	{0x021b0000, 0x82180000},	/* MMDC0_MDCTL */
+
+	{0x021b0890, 0x00400000},	/* MPPDCMPR2 */
+
+	/* Mode register writes */
+	{0x021b001c, 0x02808032},	/* MMDC0_MDSCR, MR2 write, CS0 */
+	{0x021b001c, 0x00008033},	/* MMDC0_MDSCR, MR3 write, CS0 */
+	{0x021b001c, 0x00048031},	/* MMDC0_MDSCR, MR1 write, CS0 */
+	{0x021b001c, 0x15208030},	/* MMDC0_MDSCR, MR0write, CS0 */
+	{0x021b001c, 0x04008040},	/*
+					 * MMDC0_MDSCR, ZQ calibration command sent to device
+					 * on CS0
+					 */
+
+	{0x021b0020, 0x00007800},	/* MMDC0_MDREF */
+
+	{0x021b0818, 0x00000227},	/* DDR_PHY_P0_MPODTCTRL */
+
+	{0x021b0004, 0x0002552D},	/* MMDC0_MDPDC now SDCTL power down enabled */
+
+	{0x021b0404, 0x00011006},	/*
+					 * MMDC0_MAPSR ADOPT power down enabled,
+					 * MMDC will enter automatically to self-refresh
+					 * while the number of idle cycle reached.
+					 */
+
+	{0x021b001c, 0x00000000},	/*
+					 * MMDC0_MDSCR, clear this register (especially the
+					 * configuration bit as initialization is complete)
+					 */
+};
+
+struct dram_timing_info bsh_dram_timing_128mb = {
+	.ddrc_cfg = ddr_ddrc_cfg_128mb,
+	.ddrc_cfg_num = ARRAY_SIZE(ddr_ddrc_cfg_128mb),
+};
diff --git a/board/bsh/imx6ulz_smm_m2/ddr3l_timing_256m.c b/board/bsh/imx6ulz_smm_m2/ddr3l_timing_256m.c
new file mode 100644
index 0000000..0fe5b90
--- /dev/null
+++ b/board/bsh/imx6ulz_smm_m2/ddr3l_timing_256m.c
@@ -0,0 +1,168 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#include "spl_mtypes.h"
+
+static const struct dram_cfg_param ddr_ddrc_cfg_256mb[] = {
+	/* IOMUX */
+
+	/* DDR IO Type: */
+	{0x020e04b4, 0x000C0000},	/* IOMUXC_SW_PAD_CTL_GRP_DDR_TYPE */
+	{0x020e04ac, 0x00000000},	/* IOMUXC_SW_PAD_CTL_GRP_DDRPKE */
+
+	/* Clock: */
+	{0x020e027c, 0x00000028},	/* IOMUXC_SW_PAD_CTL_PAD_DRAM_SDCLK_0 */
+
+	/* Address: */
+	{0x020e0250, 0x00000028},	/* IOMUXC_SW_PAD_CTL_PAD_DRAM_CAS */
+	{0x020e024c, 0x00000028},	/* IOMUXC_SW_PAD_CTL_PAD_DRAM_RAS */
+	{0x020e0490, 0x00000028},	/* IOMUXC_SW_PAD_CTL_GRP_ADDDS */
+
+	/* Control: */
+	{0x020e0288, 0x000C0028},	/* IOMUXC_SW_PAD_CTL_PAD_DRAM_RESET */
+	{0x020e0270, 0x00000000},	/*
+					 * IOMUXC_SW_PAD_CTL_PAD_DRAM_SDBA2 - DSE can be configured
+					 * using Group Control Register: IOMUXC_SW_PAD_CTL_GRP_CTLDS
+					 */
+
+	{0x020e0260, 0x00000028},	/* IOMUXC_SW_PAD_CTL_PAD_DRAM_SDODT0 */
+	{0x020e0264, 0x00000028},	/* IOMUXC_SW_PAD_CTL_PAD_DRAM_SDODT1 */
+	{0x020e04a0, 0x00000028},	/* IOMUXC_SW_PAD_CTL_GRP_CTLDS */
+
+	/* Data Strobes: */
+	{0x020e0494, 0x00020000},	/* IOMUXC_SW_PAD_CTL_GRP_DDRMODE_CTL */
+	{0x020e0280, 0x00000028},	/* IOMUXC_SW_PAD_CTL_PAD_DRAM_SDQS0 */
+	{0x020e0284, 0x00000028},	/* IOMUXC_SW_PAD_CTL_PAD_DRAM_SDQS1 */
+
+	/* Data: */
+	{0x020e04b0, 0x00020000},	/* IOMUXC_SW_PAD_CTL_GRP_DDRMODE */
+	{0x020e0498, 0x00000028},	/* IOMUXC_SW_PAD_CTL_GRP_B0DS */
+	{0x020e04a4, 0x00000028},	/* IOMUXC_SW_PAD_CTL_GRP_B1DS */
+
+	{0x020e0244, 0x00000028},	/* IOMUXC_SW_PAD_CTL_PAD_DRAM_DQM0 */
+	{0x020e0248, 0x00000028},	/* IOMUXC_SW_PAD_CTL_PAD_DRAM_DQM1 */
+
+	/*
+	 * =============================================================================
+	 * DDR Controller Registers
+	 * =============================================================================
+	 * Manufacturer:ISSI
+	 * Device Part Number:IS43TR16640BL-125JBLI
+	 * Clock Freq.: 400MHz
+	 * Density per CS in Gb: 2
+	 * Chip Selects used:1
+	 * Number of Banks:8
+	 * Row address:    14
+	 * Column address: 10
+	 * Data bus width16
+	 * =============================================================================
+	 */
+	{0x021b001c, 0x00008000},	/*
+					 * MMDC0_MDSCR, set the Configuration request bit during
+					 * MMDC set up
+					 */
+
+	/*
+	 * =============================================================================
+	 * Calibration setup.
+	 * =============================================================================
+	 */
+	{0x021b0800, 0xA1390003},	/*
+					 * DDR_PHY_P0_MPZQHWCTRL, enable both one-time & periodic
+					 * HW ZQ calibration
+					 */
+
+	/*
+	 * For target board, may need to run write leveling calibration to fine tune these settings
+	 */
+	{0x021b080c, 0x00050005},
+
+	/* Read DQS Gating calibration */
+	{0x021b083c, 0x01480144},	/* MPDGCTRL0 PHY0 */
+
+	/* Read calibration */
+	{0x021b0848, 0x4040363A},	/* MPRDDLCTL PHY0 */
+
+	/* Write calibration */
+	{0x021b0850, 0x40402E2C},	/* MPWRDLCTL PHY0 */
+
+	/*
+	 * Read data bit delay: 3 is the reccommended default value, although out of reset value
+	 * is 0
+	 */
+	{0x021b081c, 0x33333333},	/* MMDC_MPRDDQBY0DL */
+	{0x021b0820, 0x33333333},	/* MMDC_MPRDDQBY1DL */
+
+	/* Write data bit delay: */
+	{0x021b082c, 0xF3333333},	/* MMDC_MPWRDQBY0DL */
+	{0x021b0830, 0xF3333333},	/* MMDC_MPWRDQBY1DL */
+
+	/* DQS&CLK Duty Cycle */
+	{0x021b08c0, 0x00944009},	/* [MMDC_MPDCCR] MMDC Duty Cycle Control Register */
+
+	/* Complete calibration by forced measurement: */
+	{0x021b08b8, 0x00000800},	/* DDR_PHY_P0_MPMUR0, frc_msr */
+
+	/*
+	 * =============================================================================
+	 * Calibration setup end
+	 * =============================================================================
+	 */
+
+	/* MMDC init: */
+	{0x021b0004, 0x0002002D},	/* MMDC0_MDPDC */
+	{0x021b0008, 0x1B333030},	/* MMDC0_MDOTC */
+	{0x021b000c, 0x3F435333},	/* MMDC0_MDCFG0 */
+	{0x021b0010, 0xB68E0B63},	/* MMDC0_MDCFG1 */
+	{0x021b0014, 0x01FF00DB},	/* MMDC0_MDCFG2 */
+
+	/*
+	 * MDMISC: RALAT kept to the high level of 5.
+	 * MDMISC: consider reducing RALAT if your 528MHz board design allow that.
+	 * Lower RALAT benefits:
+	 * a. better operation at low frequency, for LPDDR2 freq < 100MHz, change RALAT to 3
+	 * b. Small performence improvment
+	 */
+	{0x021b0018, 0x00211740},	/* MMDC0_MDMISC */
+	{0x021b001c, 0x00008000},	/*
+					 * MMDC0_MDSCR, set the Configuration request bit during
+					 * MMDC set up
+					 */
+	{0x021b002c, 0x000026D2},	/* MMDC0_MDRWD */
+	{0x021b0030, 0x00431023},	/* MMDC0_MDOR */
+	{0x021b0040, 0x00000047},	/* Chan0 CS0_END */
+	{0x021b0000, 0x83180000},	/* MMDC0_MDCTL */
+
+	{0x021b0890, 0x00400000},	/* MPPDCMPR2 */
+
+	/* Mode register writes */
+	{0x021b001c, 0x02808032},	/* MMDC0_MDSCR, MR2 write, CS0 */
+	{0x021b001c, 0x00008033},	/* MMDC0_MDSCR, MR3 write, CS0 */
+	{0x021b001c, 0x00048031},	/* MMDC0_MDSCR, MR1 write, CS0 */
+	{0x021b001c, 0x15208030},	/* MMDC0_MDSCR, MR0write, CS0 */
+	{0x021b001c, 0x04008040},	/*
+					 * MMDC0_MDSCR, ZQ calibration command sent to device
+					 * on CS0
+					 */
+
+	{0x021b0020, 0x00007800},	/* MMDC0_MDREF */
+
+	{0x021b0818, 0x00000227},	/* DDR_PHY_P0_MPODTCTRL */
+
+	{0x021b0004, 0x0002552D},	/* MMDC0_MDPDC now SDCTL power down enabled */
+
+	{0x021b0404, 0x00011006},	/*
+					 * MMDC0_MAPSR ADOPT power down enabled, MMDC will enter
+					 * automatically to self-refresh while the number of idle
+					 * cycle reached
+					 */
+
+	{0x021b001c, 0x00000000},	/*
+					 * MMDC0_MDSCR, clear this register (especially the
+					 * configuration bit as initialization is complete)
+					 */
+};
+
+struct dram_timing_info bsh_dram_timing_256mb = {
+	.ddrc_cfg = ddr_ddrc_cfg_256mb,
+	.ddrc_cfg_num = ARRAY_SIZE(ddr_ddrc_cfg_256mb),
+};
diff --git a/board/bsh/imx6ulz_smm_m2/ddr3l_timing_512m.c b/board/bsh/imx6ulz_smm_m2/ddr3l_timing_512m.c
new file mode 100644
index 0000000..f598938
--- /dev/null
+++ b/board/bsh/imx6ulz_smm_m2/ddr3l_timing_512m.c
@@ -0,0 +1,168 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#include "spl_mtypes.h"
+
+static const struct dram_cfg_param ddr_ddrc_cfg_512mb[] = {
+	/*
+	 * =============================================================================
+	 * IOMUX
+	 * =============================================================================
+	 */
+
+	/* DDR IO Type: */
+	{0x020e04b4, 0x000C0000}, /* IOMUXC_SW_PAD_CTL_GRP_DDR_TYPE */
+	{0x020e04ac, 0x00000000}, /* IOMUXC_SW_PAD_CTL_GRP_DDRPKE */
+
+	/* Clock: */
+	{0x020e027c, 0x00000028}, /* IOMUXC_SW_PAD_CTL_PAD_DRAM_SDCLK_0 */
+
+	/* Address: */
+	{0x020e0250, 0x00000028}, /* IOMUXC_SW_PAD_CTL_PAD_DRAM_CAS */
+	{0x020e024c, 0x00000028}, /* IOMUXC_SW_PAD_CTL_PAD_DRAM_RAS */
+	{0x020e0490, 0x00000028}, /* IOMUXC_SW_PAD_CTL_GRP_ADDDS */
+
+	/* Control: */
+	{0x020e0288, 0x000C0028}, /* IOMUXC_SW_PAD_CTL_PAD_DRAM_RESET */
+	{0x020e0270, 0x00000000}, /*
+				   * IOMUXC_SW_PAD_CTL_PAD_DRAM_SDBA2 - DSE can be configured using
+				   * Group Control Register: IOMUXC_SW_PAD_CTL_GRP_CTLDS
+				   */
+	{0x020e0260, 0x00000028}, /* IOMUXC_SW_PAD_CTL_PAD_DRAM_SDODT0 */
+	{0x020e0264, 0x00000028}, /* IOMUXC_SW_PAD_CTL_PAD_DRAM_SDODT1 */
+	{0x020e04a0, 0x00000028}, /* IOMUXC_SW_PAD_CTL_GRP_CTLDS */
+
+	/* Data Strobes: */
+	{0x020e0494, 0x00020000}, /* IOMUXC_SW_PAD_CTL_GRP_DDRMODE_CTL */
+	{0x020e0280, 0x00000028}, /* IOMUXC_SW_PAD_CTL_PAD_DRAM_SDQS0 */
+	{0x020e0284, 0x00000028}, /* IOMUXC_SW_PAD_CTL_PAD_DRAM_SDQS1 */
+
+	/* Data: */
+	{0x020e04b0, 0x00020000}, /* IOMUXC_SW_PAD_CTL_GRP_DDRMODE */
+	{0x020e0498, 0x00000028}, /* IOMUXC_SW_PAD_CTL_GRP_B0DS */
+	{0x020e04a4, 0x00000028}, /* IOMUXC_SW_PAD_CTL_GRP_B1DS */
+
+	{0x020e0244, 0x00000028}, /* IOMUXC_SW_PAD_CTL_PAD_DRAM_DQM0 */
+	{0x020e0248, 0x00000028}, /* IOMUXC_SW_PAD_CTL_PAD_DRAM_DQM1 */
+
+	/*
+	 * =============================================================================
+	 * DDR Controller Registers
+	 * =============================================================================
+	 * Manufacturer:ISSI
+	 * Device Part Number:IS43TR16640BL-125JBLI
+	 * Clock Freq.: 400MHz
+	 * Density per CS in Gb: 2
+	 * Chip Selects used:1
+	 * Number of Banks:8
+	 * Row address:    14
+	 * Column address: 10
+	 * Data bus width16
+	 * =============================================================================
+	 */
+	{0x021b001c, 0x00008000}, /*
+				   * MMDC0_MDSCR, set the Configuration request bit during
+				   * MMDC set up
+				   */
+
+	/*
+	 * =============================================================================
+	 * Calibration setup.
+	 * =============================================================================
+	 */
+	{0x021b0800, 0xA1390003}, /*
+				   * DDR_PHY_P0_MPZQHWCTRL, enable both one-time & periodic
+				   * HW ZQ calibration
+				   */
+
+	/*
+	 * For target board may need to run write leveling calibration to fine tune these settings
+	 */
+	{0x021b080c, 0x00000000},
+
+	/* Read DQS Gating calibration */
+	{0x021b083c, 0x01440140}, /* MPDGCTRL0 PHY0 */
+
+	/* Read calibration */
+	{0x021b0848, 0x40403A3E}, /* MPRDDLCTL PHY0 */
+
+	/* Write calibration */
+	{0x021b0850, 0x4040322A}, /* MPWRDLCTL PHY0 */
+
+	/*
+	 * Read data bit delay: 3 is the reccommended default value, although out of reset value
+	 * is 0
+	 */
+	{0x021b081c, 0x33333333}, /* MMDC_MPRDDQBY0DL */
+	{0x021b0820, 0x33333333}, /* MMDC_MPRDDQBY1DL */
+
+	/* Write data bit delay: */
+	{0x021b082c, 0xF3333333}, /* MMDC_MPWRDQBY0DL */
+	{0x021b0830, 0xF3333333}, /* MMDC_MPWRDQBY1DL */
+
+	/* DQS&CLK Duty Cycle */
+	{0x021b08c0, 0x00944009}, /* [MMDC_MPDCCR] MMDC Duty Cycle Control Register */
+
+	/* Complete calibration by forced measurement: */
+	{0x021b08b8, 0x00000800}, /* DDR_PHY_P0_MPMUR0, frc_msr */
+
+	/*
+	 * =============================================================================
+	 * Calibration setup end
+	 * =============================================================================
+	 */
+
+	/* MMDC init: */
+	{0x021b0004, 0x0002002D}, /* MMDC0_MDPDC */
+	{0x021b0008, 0x1B333030}, /* MMDC0_MDOTC */
+	{0x021b000c, 0x3F435333}, /* MMDC0_MDCFG0 */
+	{0x021b0010, 0xB68E0B63}, /* MMDC0_MDCFG1 */
+	{0x021b0014, 0x01FF00DB}, /* MMDC0_MDCFG2 */
+
+	/*
+	 * MDMISC: RALAT kept to the high level of 5.
+	 * MDMISC: consider reducing RALAT if your 528MHz board design allow that.
+	 * Lower RALAT benefits:
+	 * a. better operation at low frequency, for LPDDR2 freq < 100MHz, change RALAT to 3
+	 * b. Small performence improvment
+	 */
+	{0x021b0018, 0x00211740}, /* MMDC0_MDMISC */
+	{0x021b001c, 0x00008000}, /*
+				   * MMDC0_MDSCR set the Configuration request bit during
+				   * MMDC set up
+				   */
+	{0x021b002c, 0x000026D2}, /* MMDC0_MDRWD */
+	{0x021b0030, 0x00431023}, /* MMDC0_MDOR */
+	{0x021b0040, 0x0000004F}, /* Chan0 CS0_END */
+	{0x021b0000, 0x84180000}, /* MMDC0_MDCTL */
+
+	{0x021b0890, 0x00400000}, /* MPPDCMPR2 */
+
+	/* Mode register writes */
+	{0x021b001c, 0x02808032}, /* MMDC0_MDSCR, MR2 write, CS0 */
+	{0x021b001c, 0x00008033}, /* MMDC0_MDSCR, MR3 write, CS0 */
+	{0x021b001c, 0x00048031}, /* MMDC0_MDSCR, MR1 write, CS0 */
+	{0x021b001c, 0x15208030}, /* MMDC0_MDSCR, MR0write, CS0 */
+	{0x021b001c, 0x04008040}, /* MMDC0_MDSCR, ZQ calibration command sent to device on CS0 */
+
+	{0x021b0020, 0x00007800}, /* MMDC0_MDREF */
+
+	{0x021b0818, 0x00000227}, /* DDR_PHY_P0_MPODTCTRL */
+
+	{0x021b0004, 0x0002552D}, /* MMDC0_MDPDC now SDCTL power down enabled */
+
+	{0x021b0404, 0x00011006}, /*
+				   * MMDC0_MAPSR ADOPT power down enabled, MMDC will enter
+				   * automatically to self-refresh while the number of idle
+				   * cycle reached
+				   */
+
+	{0x021b001c, 0x00000000}, /*
+				   * MMDC0_MDSCR, clear this register (especially the configuration
+				   * bit as initialization is complete)
+				   */
+};
+
+struct dram_timing_info bsh_dram_timing_512mb = {
+	.ddrc_cfg = ddr_ddrc_cfg_512mb,
+	.ddrc_cfg_num = ARRAY_SIZE(ddr_ddrc_cfg_512mb),
+};
diff --git a/board/bsh/imx6ulz_smm_m2/spl.c b/board/bsh/imx6ulz_smm_m2/spl.c
index 724841b..c330e4d 100644
--- a/board/bsh/imx6ulz_smm_m2/spl.c
+++ b/board/bsh/imx6ulz_smm_m2/spl.c
@@ -13,10 +13,13 @@
 #include <asm/gpio.h>
 #include <asm/mach-imx/iomux-v3.h>
 #include <asm/mach-imx/boot_mode.h>
+#include <linux/delay.h>
 #include <linux/libfdt.h>
 #include <spl.h>
 #include <asm/arch/mx6-ddr.h>
 
+#include "spl_mtypes.h"
+
 #define UART_PAD_CTRL (PAD_CTL_PKE | PAD_CTL_PUE | \
 		       PAD_CTL_PUS_100K_UP | PAD_CTL_SPEED_MED | \
 		       PAD_CTL_DSE_40ohm | PAD_CTL_SRE_FAST | PAD_CTL_HYS)
@@ -31,69 +34,48 @@
 	imx_iomux_v3_setup_multiple_pads(uart4_pads, ARRAY_SIZE(uart4_pads));
 }
 
-static struct mx6ul_iomux_grp_regs mx6_grp_ioregs = {
-	.grp_addds		= 0x00000028,
-	.grp_ddrmode_ctl	= 0x00020000,
-	.grp_b0ds		= 0x00000028,
-	.grp_ctlds		= 0x00000028,
-	.grp_b1ds		= 0x00000028,
-	.grp_ddrpke		= 0x00000000,
-	.grp_ddrmode		= 0x00020000,
-	.grp_ddr_type		= 0x000c0000,
-};
+static void ddr_cfg_write(const struct dram_timing_info *dram_timing_info)
+{
+	int i;
+	const struct dram_cfg_param *ddrc_cfg = dram_timing_info->ddrc_cfg;
+	const int ddrc_cfg_num = dram_timing_info->ddrc_cfg_num;
+	struct mmdc_p_regs *mmdc0 = (struct mmdc_p_regs *)MMDC_P0_BASE_ADDR;
 
-static struct mx6ul_iomux_ddr_regs mx6_ddr_ioregs = {
-	.dram_dqm0		= 0x00000028,
-	.dram_dqm1		= 0x00000028,
-	.dram_ras		= 0x00000028,
-	.dram_cas		= 0x00000028,
-	.dram_odt0		= 0x00000028,
-	.dram_odt1		= 0x00000028,
-	.dram_sdba2		= 0x00000000,
-	.dram_sdclk_0		= 0x00000028,
-	.dram_sdqs0		= 0x00000028,
-	.dram_sdqs1		= 0x00000028,
-	.dram_reset		= 0x000c0028,
-};
+	clrbits_le32(&mmdc0->mdctl, 1 << 31);	/* clear SDE_0 */
+	clrbits_le32(&mmdc0->mdctl, 1 << 30);	/* clear SDE_1 */
 
-static struct mx6_mmdc_calibration mx6_mmcd_calib = {
-	.p0_mpwldectrl0		= 0x00000000,
-	.p0_mpwldectrl1		= 0x00100010,
-	.p0_mpdgctrl0		= 0x414c014c,
-	.p0_mpdgctrl1		= 0x00000000,
-	.p0_mprddlctl		= 0x40403a42,
-	.p0_mpwrdlctl		= 0x4040342e,
-};
+	for (i = 0; i < ddrc_cfg_num; i++) {
+		debug("Writing 0x%x to register 0x%x\n", ddrc_cfg->val,
+		      ddrc_cfg->reg);
+		writel(ddrc_cfg->val, ddrc_cfg->reg);
+		ddrc_cfg++;
+	}
+}
 
-static struct mx6_ddr_sysinfo ddr_sysinfo = {
-	.dsize			= 0,
-	.cs1_mirror		= 0,
-	.cs_density		= 32,
-	.ncs			= 1,
-	.bi_on			= 1,
-	.rtt_nom		= 1,
-	.rtt_wr			= 0,
-	.ralat			= 5,
-	.walat			= 1,
-	.mif3_mode		= 3,
-	.rst_to_cke		= 0x23,	/* 33 cycles (JEDEC value for DDR3) - total of 500 us */
-	.sde_to_rst		= 0x10,	/* 14 cycles (JEDEC value for DDR3) - total of 200 us */
-	.refsel			= 1,
-	.refr			= 3,
-};
+static void spl_dram_init(void)
+{
+	/* Configure memory to maximum supported size for detection */
+	ddr_cfg_write(&bsh_dram_timing_512mb);
 
-static struct mx6_ddr3_cfg mem_ddr = {
-	.mem_speed		= 1333,
-	.density		= 2,
-	.width			= 16,
-	.banks			= 8,
-	.rowaddr		= 13,
-	.coladdr		= 10,
-	.pagesz			= 2,
-	.trcd			= 1350,
-	.trcmin			= 4950,
-	.trasmin		= 3600,
-};
+	/* Detect memory physically present */
+	gd->ram_size = get_ram_size((void *)CFG_SYS_SDRAM_BASE, SZ_512M);
+
+	/* Reconfigure memory for actual detected size */
+	switch (gd->ram_size) {
+	case SZ_512M:
+		/* Already configured, nothing to do */
+		break;
+	case SZ_256M:
+		udelay(1);
+		ddr_cfg_write(&bsh_dram_timing_256mb);
+		break;
+	case SZ_128M:
+	default:
+		udelay(1);
+		ddr_cfg_write(&bsh_dram_timing_128mb);
+		break;
+	}
+}
 
 static void ccgr_init(void)
 {
@@ -108,20 +90,17 @@
 	writel(0xFFFFFFFF, &ccm->CCGR6);
 }
 
-static void imx6ul_spl_dram_cfg(void)
-{
-	mx6ul_dram_iocfg(mem_ddr.width, &mx6_ddr_ioregs, &mx6_grp_ioregs);
-	mx6_dram_cfg(&ddr_sysinfo, &mx6_mmcd_calib, &mem_ddr);
-}
-
 void board_init_f(ulong dummy)
 {
 	ccgr_init();
+
+	/* DDR initialization */
+	spl_dram_init();
+
 	arch_cpu_init();
 	timer_init();
 	setup_iomux_uart();
 	preloader_console_init();
-	imx6ul_spl_dram_cfg();
 }
 
 void reset_cpu(void)
diff --git a/board/bsh/imx6ulz_smm_m2/spl_mtypes.h b/board/bsh/imx6ulz_smm_m2/spl_mtypes.h
new file mode 100644
index 0000000..8da5988
--- /dev/null
+++ b/board/bsh/imx6ulz_smm_m2/spl_mtypes.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright 2025 BSH Hausgeraete GmbH
+ *
+ * Written by: Simon Holesch <simon.holesch@bshg.com>
+ */
+
+#ifndef SPL_MTYPES_H
+#define SPL_MTYPES_H
+
+#include <spl.h>
+
+struct dram_cfg_param {
+	unsigned int reg;
+	unsigned int val;
+};
+
+struct dram_timing_info {
+	const struct dram_cfg_param *ddrc_cfg;
+	unsigned int ddrc_cfg_num;
+};
+
+extern struct dram_timing_info bsh_dram_timing_128mb;
+extern struct dram_timing_info bsh_dram_timing_256mb;
+extern struct dram_timing_info bsh_dram_timing_512mb;
+
+#endif /* SPL_MTYPES_H */
diff --git a/board/bsh/imx8mn_smm_s2/spl.c b/board/bsh/imx8mn_smm_s2/spl.c
index 5a77d28..d36ddd2 100644
--- a/board/bsh/imx8mn_smm_s2/spl.c
+++ b/board/bsh/imx8mn_smm_s2/spl.c
@@ -43,8 +43,6 @@
 
 int board_early_init_f(void)
 {
-	init_uart_clk(3);
-
 	if (IS_ENABLED(CONFIG_NAND_MXS)) {
 		init_nand_clk();
 	}
diff --git a/board/emulation/qemu-x86/Kconfig b/board/emulation/qemu-x86/Kconfig
index b2a4e08..c1564fb 100644
--- a/board/emulation/qemu-x86/Kconfig
+++ b/board/emulation/qemu-x86/Kconfig
@@ -23,5 +23,6 @@
 	imply VIRTIO_PCI
 	imply VIRTIO_NET
 	imply VIRTIO_BLK
+	imply CMD_SMBIOS
 
 endif
diff --git a/board/freescale/imx8mn_evk/spl.c b/board/freescale/imx8mn_evk/spl.c
index 231b928..f96f5c4 100644
--- a/board/freescale/imx8mn_evk/spl.c
+++ b/board/freescale/imx8mn_evk/spl.c
@@ -115,8 +115,6 @@
 
 	arch_cpu_init();
 
-	init_uart_clk(1);
-
 	timer_init();
 
 	/* Clear the BSS. */
diff --git a/board/phytec/phycore_imx93/MAINTAINERS b/board/phytec/phycore_imx93/MAINTAINERS
index 718f89a..7393061 100644
--- a/board/phytec/phycore_imx93/MAINTAINERS
+++ b/board/phytec/phycore_imx93/MAINTAINERS
@@ -3,8 +3,6 @@
 R:      Christoph Stoidner <c.stoidner@phytec.de>
 W:      https://www.phytec.eu/en/produkte/system-on-modules/phycore-imx-91-93/
 S:      Maintained
-F:      arch/arm/dts/imx93-phyboard-segin.dts
-F:      arch/arm/dts/imx93-phycore-som.dtsi
 F:      arch/arm/dts/imx93-phyboard-segin-u-boot.dtsi
 F:      board/phytec/phycore_imx93/
 F:      board/phytec/common/imx93_som_detection.c
diff --git a/board/thead/th1520_lpi4a/Kconfig b/board/thead/th1520_lpi4a/Kconfig
index 6222461..f139d5f 100644
--- a/board/thead/th1520_lpi4a/Kconfig
+++ b/board/thead/th1520_lpi4a/Kconfig
@@ -11,7 +11,7 @@
 	default "thead"
 
 config SYS_CPU
-	default "generic"
+	default "th1520"
 
 config SYS_CONFIG_NAME
 	default "th1520_lpi4a"
@@ -22,7 +22,7 @@
 	default 0x01c00000 if RISCV_SMODE
 
 config SPL_TEXT_BASE
-	default 0x08000000
+	default 0xffe0000000
 
 config SPL_OPENSBI_LOAD_ADDR
 	default 0x80000000
@@ -30,6 +30,7 @@
 config BOARD_SPECIFIC_OPTIONS
 	def_bool y
 	select ARCH_EARLY_INIT_R
+	select THEAD_TH1520
 	imply CPU
 	imply CPU_RISCV
 	imply RISCV_TIMER if RISCV_SMODE
diff --git a/board/thead/th1520_lpi4a/Makefile b/board/thead/th1520_lpi4a/Makefile
index 9671b3b..a7ddfc4 100644
--- a/board/thead/th1520_lpi4a/Makefile
+++ b/board/thead/th1520_lpi4a/Makefile
@@ -3,3 +3,4 @@
 # Copyright (c) 2023, Yixun Lan <dlan@gentoo.org>
 
 obj-y += board.o
+obj-$(CONFIG_XPL_BUILD) += spl.o
diff --git a/board/thead/th1520_lpi4a/spl.c b/board/thead/th1520_lpi4a/spl.c
new file mode 100644
index 0000000..25dfa38
--- /dev/null
+++ b/board/thead/th1520_lpi4a/spl.c
@@ -0,0 +1,48 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2025, Yao Zi <ziyao@disroot.org>
+ */
+
+#include <asm/io.h>
+#include <asm/spl.h>
+#include <asm/arch/cpu.h>
+#include <asm/arch/spl.h>
+#include <cpu_func.h>
+#include <dm.h>
+#include <hang.h>
+#include <spl.h>
+
+u32 spl_boot_device(void)
+{
+	/*
+	 * We don't bother to load proper U-Boot from an external device as
+	 * it fits in the integrated SRAM nicely.
+	 */
+	return BOOT_DEVICE_RAM;
+}
+
+void board_init_f(ulong dummy)
+{
+	int ret = spl_early_init();
+	struct udevice *dev;
+
+	if (ret)
+		panic("spl_early_init() failed %d\n", ret);
+
+	preloader_console_init();
+
+	/*
+	 * Manually bind CPU ahead of time to make sure in-core timers are
+	 * available in SPL.
+	 */
+	ret = uclass_get_device(UCLASS_CPU, 0, &dev);
+	if (ret)
+		panic("failed to bind CPU: %d\n", ret);
+
+	spl_dram_init();
+
+	icache_enable();
+	dcache_enable();
+
+	th1520_invalidate_pmp();
+}
diff --git a/boot/image.c b/boot/image.c
index 139c5bd..45299a7 100644
--- a/boot/image.c
+++ b/boot/image.c
@@ -92,7 +92,8 @@
 	{	IH_ARCH_ARC,		"arc",		"ARC",		},
 	{	IH_ARCH_X86_64,		"x86_64",	"AMD x86_64",	},
 	{	IH_ARCH_XTENSA,		"xtensa",	"Xtensa",	},
-	{	IH_ARCH_RISCV,		"riscv",	"RISC-V",	},
+	{	IH_ARCH_RISCV,		"riscv",	"RISC-V 32 Bit",},
+	{	IH_ARCH_RISCV64,	"riscv64",	"RISC-V 64 Bit",},
 	{	-1,			"",		"",		},
 };
 
diff --git a/cmd/bootefi.c b/cmd/bootefi.c
index cea6d35..8e87521 100644
--- a/cmd/bootefi.c
+++ b/cmd/bootefi.c
@@ -9,6 +9,7 @@
 
 #include <command.h>
 #include <efi.h>
+#include <efi_device_path.h>
 #include <efi_loader.h>
 #include <exports.h>
 #include <log.h>
diff --git a/cmd/booti.c b/cmd/booti.c
index 1a57fe9..ced2c10 100644
--- a/cmd/booti.c
+++ b/cmd/booti.c
@@ -130,8 +130,11 @@
 	bootm_disable_interrupts();
 
 	images.os.os = IH_OS_LINUX;
-	if (IS_ENABLED(CONFIG_RISCV_SMODE))
-		images.os.arch = IH_ARCH_RISCV;
+	if (IS_ENABLED(CONFIG_RISCV))
+		if (IS_ENABLED(CONFIG_64BIT))
+			images.os.arch = IH_ARCH_RISCV64;
+		else
+			images.os.arch = IH_ARCH_RISCV;
 	else if (IS_ENABLED(CONFIG_ARM64))
 		images.os.arch = IH_ARCH_ARM64;
 
diff --git a/cmd/eficonfig.c b/cmd/eficonfig.c
index 629bf1b..6e14d34 100644
--- a/cmd/eficonfig.c
+++ b/cmd/eficonfig.c
@@ -8,6 +8,7 @@
 #include <ansi.h>
 #include <cli.h>
 #include <charset.h>
+#include <efi_device_path.h>
 #include <efi_loader.h>
 #include <efi_load_initrd.h>
 #include <efi_config.h>
@@ -514,7 +515,7 @@
 	struct efi_device_path_file_path *fp;
 
 	fp_size = sizeof(struct efi_device_path) + u16_strsize(current_path);
-	buf = calloc(1, fp_size + sizeof(END));
+	buf = calloc(1, fp_size + sizeof(EFI_DP_END));
 	if (!buf)
 		return NULL;
 
@@ -526,7 +527,7 @@
 
 	p = buf;
 	p += fp_size;
-	*((struct efi_device_path *)p) = END;
+	*((struct efi_device_path *)p) = EFI_DP_END;
 
 	dp = efi_dp_shorten(dp_volume);
 	if (!dp)
diff --git a/cmd/efidebug.c b/cmd/efidebug.c
index 2461425..109496d 100644
--- a/cmd/efidebug.c
+++ b/cmd/efidebug.c
@@ -8,6 +8,7 @@
 #include <charset.h>
 #include <command.h>
 #include <dm/device.h>
+#include <efi_device_path.h>
 #include <efi_dt_fixup.h>
 #include <efi_load_initrd.h>
 #include <efi_loader.h>
@@ -812,7 +813,7 @@
 	lo->label = label;
 
 	uridp_len = sizeof(struct efi_device_path) + strlen(argv[3]) + 1;
-	uridp = efi_alloc(uridp_len + sizeof(END));
+	uridp = efi_alloc(uridp_len + sizeof(EFI_DP_END));
 	if (!uridp) {
 		log_err("Out of memory\n");
 		return CMD_RET_FAILURE;
@@ -822,10 +823,10 @@
 	uridp->dp.length = uridp_len;
 	strcpy(uridp->uri, argv[3]);
 	pos = (char *)uridp + uridp_len;
-	memcpy(pos, &END, sizeof(END));
+	memcpy(pos, &EFI_DP_END, sizeof(EFI_DP_END));
 
 	*file_path = &uridp->dp;
-	*fp_size += uridp_len + sizeof(END);
+	*fp_size += uridp_len + sizeof(EFI_DP_END);
 
 	return CMD_RET_SUCCESS;
 }
diff --git a/cmd/tlv_eeprom.c b/cmd/tlv_eeprom.c
index 0aec752..d7c229e 100644
--- a/cmd/tlv_eeprom.c
+++ b/cmd/tlv_eeprom.c
@@ -476,6 +476,7 @@
 
 		printf("EEPROM data loaded from device to memory.\n");
 		has_been_read = 1;
+		return 0;
 	}
 
 	// Subsequent commands require that the EEPROM has already been read.
diff --git a/common/board_r.c b/common/board_r.c
index 65ff5fb..980bd38 100644
--- a/common/board_r.c
+++ b/common/board_r.c
@@ -814,7 +814,9 @@
 	if (CONFIG_IS_ENABLED(X86_64) && !IS_ENABLED(CONFIG_EFI_APP))
 		arch_setup_gd(new_gd);
 
-#if !defined(CONFIG_X86) && !defined(CONFIG_ARM) && !defined(CONFIG_ARM64)
+#if defined(CONFIG_RISCV)
+	set_gd(new_gd);
+#elif !defined(CONFIG_X86) && !defined(CONFIG_ARM) && !defined(CONFIG_ARM64)
 	gd = new_gd;
 #endif
 	gd->flags &= ~GD_FLG_LOG_READY;
diff --git a/common/cyclic.c b/common/cyclic.c
index b695f09..ec952a0 100644
--- a/common/cyclic.c
+++ b/common/cyclic.c
@@ -28,9 +28,23 @@
 	return (struct hlist_head *)&gd->cyclic_list;
 }
 
+static bool cyclic_is_registered(const struct cyclic_info *cyclic)
+{
+	const struct cyclic_info *c;
+
+	hlist_for_each_entry(c, cyclic_get_list(), list) {
+		if (c == cyclic)
+			return true;
+	}
+
+	return false;
+}
+
 void cyclic_register(struct cyclic_info *cyclic, cyclic_func_t func,
 		     uint64_t delay_us, const char *name)
 {
+	cyclic_unregister(cyclic);
+
 	memset(cyclic, 0, sizeof(*cyclic));
 
 	/* Store values in struct */
@@ -43,6 +57,9 @@
 
 void cyclic_unregister(struct cyclic_info *cyclic)
 {
+	if (!cyclic_is_registered(cyclic))
+		return;
+
 	hlist_del(&cyclic->list);
 }
 
diff --git a/common/init/board_init.c b/common/init/board_init.c
index a06ec1c..2a6f39f 100644
--- a/common/init/board_init.c
+++ b/common/init/board_init.c
@@ -13,8 +13,11 @@
 
 DECLARE_GLOBAL_DATA_PTR;
 
-/* Unfortunately x86 or ARM can't compile this code as gd cannot be assigned */
-#if !defined(CONFIG_X86) && !defined(CONFIG_ARM)
+/*
+ * Unfortunately x86, ARM and RISC-V can't compile this code as gd is defined
+ * as macro and cannot be assigned.
+ */
+#if !defined(CONFIG_X86) && !defined(CONFIG_ARM) && !defined(CONFIG_RISCV)
 __weak void arch_setup_gd(struct global_data *gd_ptr)
 {
 	gd = gd_ptr;
diff --git a/common/spl/Kconfig b/common/spl/Kconfig
index a84a0f8..be87b3e 100644
--- a/common/spl/Kconfig
+++ b/common/spl/Kconfig
@@ -974,6 +974,21 @@
 	  This enables the drivers in drivers/mtd/nand/raw as part of an SPL
 	  build.
 
+config SPL_NAND_RAW_U_BOOT_USE_SECTOR
+	bool "NAND raw mode: by sector"
+	depends on SPL_NAND_SUPPORT
+	select SPL_LOAD_BLOCK
+	help
+	  Use sector number for specifying U-Boot location on NAND in
+	  raw mode.
+
+config SPL_NAND_RAW_U_BOOT_SECTOR
+	hex "Address on the NAND to load U-Boot from"
+	depends on SPL_NAND_RAW_U_BOOT_USE_SECTOR
+	help
+	  Address on the NAND to load U-Boot from, when the NAND is being used
+	  in raw mode. Units: NAND disk sectors (1 sector = 512 bytes).
+
 config SPL_NAND_RAW_ONLY
 	bool "Support to boot only raw u-boot.bin images"
 	depends on SPL_NAND_SUPPORT
diff --git a/configs/ds109_defconfig b/configs/ds109_defconfig
index ef805ec..1dafef3 100644
--- a/configs/ds109_defconfig
+++ b/configs/ds109_defconfig
@@ -54,8 +54,6 @@
 CONFIG_MII=y
 CONFIG_DM_RTC=y
 CONFIG_RTC_MV=y
-CONFIG_SYS_NS16550_SERIAL=y
-CONFIG_SYS_NS16550_REG_SIZE=-4
 CONFIG_SPI=y
 CONFIG_DM_SPI=y
 CONFIG_KIRKWOOD_SPI=y
diff --git a/configs/imx6ulz_smm_m2_defconfig b/configs/imx6ulz_smm_m2_defconfig
index 15a3ec5..436bfb7 100644
--- a/configs/imx6ulz_smm_m2_defconfig
+++ b/configs/imx6ulz_smm_m2_defconfig
@@ -64,6 +64,8 @@
 CONFIG_DM_REGULATOR=y
 CONFIG_DM_REGULATOR_FIXED=y
 CONFIG_MXC_UART=y
+CONFIG_SYSRESET=y
+CONFIG_SYSRESET_WATCHDOG=y
 CONFIG_IMX_THERMAL=y
 CONFIG_USB=y
 CONFIG_SPL_USB_HOST=y
@@ -75,3 +77,4 @@
 CONFIG_CI_UDC=y
 CONFIG_SDP_LOADADDR=0x877fffc0
 CONFIG_SPL_USB_SDP_SUPPORT=y
+CONFIG_IMX_WATCHDOG=y
diff --git a/configs/imx8mn_bsh_smm_s2_defconfig b/configs/imx8mn_bsh_smm_s2_defconfig
index 9ffcd3b..206d216 100644
--- a/configs/imx8mn_bsh_smm_s2_defconfig
+++ b/configs/imx8mn_bsh_smm_s2_defconfig
@@ -15,7 +15,6 @@
 CONFIG_SPL_SERIAL=y
 CONFIG_SPL_DRIVERS_MISC=y
 CONFIG_SPL_STACK=0x980000
-CONFIG_SPL_SYS_MALLOC_F_LEN=0x2000
 CONFIG_SPL_TEXT_BASE=0x912000
 CONFIG_SPL_HAS_BSS_LINKER_SECTION=y
 CONFIG_SPL_BSS_START_ADDR=0x950000
@@ -47,6 +46,8 @@
 CONFIG_SPL_I2C=y
 CONFIG_SPL_MTD=y
 CONFIG_SPL_NAND_SUPPORT=y
+CONFIG_SPL_NAND_RAW_U_BOOT_USE_SECTOR=y
+CONFIG_SPL_NAND_RAW_U_BOOT_SECTOR=0x300
 CONFIG_SPL_NAND_BASE=y
 CONFIG_SPL_NAND_IDENT=y
 CONFIG_SPL_POWER=y
diff --git a/configs/imx8mn_bsh_smm_s2pro_defconfig b/configs/imx8mn_bsh_smm_s2pro_defconfig
index d2010d1..ef486f5 100644
--- a/configs/imx8mn_bsh_smm_s2pro_defconfig
+++ b/configs/imx8mn_bsh_smm_s2pro_defconfig
@@ -16,7 +16,6 @@
 CONFIG_SPL_SERIAL=y
 CONFIG_SPL_DRIVERS_MISC=y
 CONFIG_SPL_STACK=0x980000
-CONFIG_SPL_SYS_MALLOC_F_LEN=0x2000
 CONFIG_SPL_TEXT_BASE=0x912000
 CONFIG_SPL_HAS_BSS_LINKER_SECTION=y
 CONFIG_SPL_BSS_START_ADDR=0x950000
diff --git a/configs/imx93-phycore_defconfig b/configs/imx93-phycore_defconfig
index 7187fd7..66a4312 100644
--- a/configs/imx93-phycore_defconfig
+++ b/configs/imx93-phycore_defconfig
@@ -12,7 +12,7 @@
 CONFIG_ENV_OFFSET=0x700000
 CONFIG_IMX_CONFIG="arch/arm/mach-imx/imx9/imximage.cfg"
 CONFIG_DM_GPIO=y
-CONFIG_DEFAULT_DEVICE_TREE="imx93-phyboard-segin"
+CONFIG_DEFAULT_DEVICE_TREE="freescale/imx93-phyboard-segin"
 CONFIG_AHAB_BOOT=y
 CONFIG_TARGET_PHYCORE_IMX93=y
 CONFIG_OF_LIBFDT_OVERLAY=y
diff --git a/configs/imx95_19x19_evk_defconfig b/configs/imx95_19x19_evk_defconfig
index a40d93d..32aee1f 100644
--- a/configs/imx95_19x19_evk_defconfig
+++ b/configs/imx95_19x19_evk_defconfig
@@ -41,6 +41,7 @@
 CONFIG_ARCH_MISC_INIT=y
 CONFIG_BOARD_EARLY_INIT_F=y
 CONFIG_BOARD_LATE_INIT=y
+CONFIG_PCI_INIT_R=y
 CONFIG_SPL_MAX_SIZE=0x20000
 CONFIG_SPL_BOARD_INIT=y
 CONFIG_SPL_LOAD_IMX_CONTAINER=y
diff --git a/configs/imxrt1170-evk_defconfig b/configs/imxrt1170-evk_defconfig
index 32107fa..a57c35b 100644
--- a/configs/imxrt1170-evk_defconfig
+++ b/configs/imxrt1170-evk_defconfig
@@ -37,6 +37,8 @@
 # CONFIG_BOOTM_PLAN9 is not set
 # CONFIG_BOOTM_RTEMS is not set
 # CONFIG_BOOTM_VXWORKS is not set
+CONFIG_CMD_MTD=y
+CONFIG_CMD_SPI=y
 # CONFIG_CMD_MII is not set
 # CONFIG_SPL_DOS_PARTITION is not set
 # CONFIG_ISO_PARTITION is not set
@@ -58,6 +60,15 @@
 CONFIG_MXC_GPIO=y
 # CONFIG_INPUT is not set
 CONFIG_FSL_USDHC=y
+CONFIG_MTD=y
+CONFIG_DM_MTD=y
+CONFIG_MTD_BLOCK=y
+CONFIG_CFI_FLASH=y
+CONFIG_FLASH_CFI_MTD=y
+CONFIG_DM_SPI_FLASH=y
+CONFIG_SPI_FLASH_SOFT_RESET=y
+CONFIG_SPI_FLASH_ISSI=y
+CONFIG_SPI_FLASH_MTD=y
 CONFIG_PINCTRL=y
 CONFIG_SPL_PINCTRL=y
 CONFIG_PINCTRL_IMXRT=y
@@ -65,6 +76,10 @@
 CONFIG_SPL_RAM=y
 CONFIG_IMXRT_SDRAM=y
 CONFIG_FSL_LPUART=y
+CONFIG_SPI=y
+CONFIG_DM_SPI=y
+CONFIG_SPI_DIRMAP=y
+CONFIG_NXP_FSPI=y
 CONFIG_TIMER=y
 CONFIG_SPL_TIMER=y
 CONFIG_IMX_GPT_TIMER=y
diff --git a/configs/mvebu_espressobin_ultra-88f3720_defconfig b/configs/mvebu_espressobin_ultra-88f3720_defconfig
index 1cbeee8..fb8efbd 100644
--- a/configs/mvebu_espressobin_ultra-88f3720_defconfig
+++ b/configs/mvebu_espressobin_ultra-88f3720_defconfig
@@ -97,3 +97,5 @@
 # CONFIG_WATCHDOG_AUTOSTART is not set
 CONFIG_WDT=y
 CONFIG_WDT_ARMADA_37XX=y
+CONFIG_DM_RNG=y
+CONFIG_RNG_TURRIS_RWTM=y
diff --git a/configs/th1520_lpi4a_defconfig b/configs/th1520_lpi4a_defconfig
index d13c974..b19dc00 100644
--- a/configs/th1520_lpi4a_defconfig
+++ b/configs/th1520_lpi4a_defconfig
@@ -90,3 +90,21 @@
 CONFIG_BZIP2=y
 CONFIG_ZSTD=y
 CONFIG_LIB_RATIONAL=y
+CONFIG_SPL=y
+# CONFIG_SPL_SHARES_INIT_SP_ADDR is not set
+CONFIG_SPL_LOAD_FIT_ADDRESS=0xffe0040000
+CONFIG_SPL_HAVE_INIT_STACK=y
+CONFIG_SPL_STACK=0xffe0170000
+CONFIG_SPL_BSS_START_ADDR=0xffe0160000
+CONFIG_SPL_BSS_MAX_SIZE=0x10000
+CONFIG_SPL_MAX_SIZE=0x40000
+CONFIG_SPL_RAM_DEVICE=y
+CONFIG_RAM=y
+CONFIG_SPL_RAM=y
+CONFIG_SPL_THEAD_TH1520_DDR=y
+CONFIG_SPL_GPIO=y
+CONFIG_SPL_MMC_y
+CONFIG_SPL_SYS_MALLOC=y
+CONFIG_SPL_HAS_CUSTOM_MALLOC_START=y
+CONFIG_SPL_CUSTOM_SYS_MALLOC_ADDR=0x10000000
+CONFIG_SPL_SYS_MALLOC_SIZE=0x400000
diff --git a/configs/total_compute_defconfig b/configs/total_compute_defconfig
index 70bec3b..e66c011 100644
--- a/configs/total_compute_defconfig
+++ b/configs/total_compute_defconfig
@@ -55,3 +55,6 @@
 CONFIG_SYS_MAX_FLASH_SECT=256
 # CONFIG_RANDOM_UUID is not set
 CONFIG_LIBAVB=y
+CONFIG_BLOBLIST=y
+CONFIG_BLOBLIST_PASSAGE_MANDATORY=y
+CONFIG_BLOBLIST_SIZE_RELOC=0x10000
diff --git a/doc/README.omap-ulpi-viewport b/doc/README.omap-ulpi-viewport
deleted file mode 100644
index a5240b9..0000000
--- a/doc/README.omap-ulpi-viewport
+++ /dev/null
@@ -1,27 +0,0 @@
-Reference code ""drivers/usb/ulpi/omap-ulpi-viewport.c"
-
-Contains the ulpi read write api's to perform
-any ulpi phy port access on omap platform.
-
-On omap ehci reg map contains INSNREG05_ULPI
-register which offers the ulpi phy access so
-any ulpi phy commands should be passsed using this
-register.
-
-omap-ulpi-viewport.c is a low level function
-implementation of "drivers/usb/ulpi/ulpi.c"
-
-To enable and use omap-ulpi-viewport.c
-we require CONFIG_USB_ULPI_VIEWPORT_OMAP and
-CONFIG_USB_ULPI be enabled in config file.
-
-Any ulpi ops request can be done with ulpi.c
-and soc specific binding and usage is done with
-omap-ulpi-viewport implementation.
-
-Ex: scenario:
-omap-ehci driver code requests for ulpi phy reset if
-ehci is used in phy mode, which will call ulpi phy reset
-the ulpi phy reset does ulpi_read/write from viewport
-implementation which will do ulpi reset using the
-INSNREG05_ULPI register.
diff --git a/doc/api/efi.rst b/doc/api/efi.rst
index 43d6f93..a98298f 100644
--- a/doc/api/efi.rst
+++ b/doc/api/efi.rst
@@ -178,6 +178,12 @@
 .. kernel-doc:: include/efi_driver.h
    :internal:
 
+Device paths
+------------
+
+.. kernel-doc:: include/efi_device_path.h
+   :internal:
+
 Unit testing
 ------------
 
diff --git a/doc/board/thead/lpi4a.rst b/doc/board/thead/lpi4a.rst
index e395c6a..7e4c4ea 100644
--- a/doc/board/thead/lpi4a.rst
+++ b/doc/board/thead/lpi4a.rst
@@ -32,6 +32,8 @@
 The support for following drivers are already enabled:
 
 1. ns16550 UART Driver.
+2. eMMC and SD card
+
 
 Building
 ~~~~~~~~
@@ -43,15 +45,32 @@
 
    export CROSS_COMPILE=<riscv64 toolchain prefix>
 
+3. Build DDR firmware
+
+DDR driver requires a firmware to function, to build it:
+
+.. code-block:: bash
+
+	git clone --depth 1 https://github.com/ziyao233/th1520-firmware
+	cd th1520-firmware
+	lua5.4 ddr-generate.lua src/<CONFIGURATION_NAME>.lua th1520-ddr-firmware.bin
+
-The U-Boot is capable of running in M-Mode, so we can directly build it.
+4. Build U-Boot images
+
+The U-Boot is capable of running in M-Mode, so we can directly build it without
+OpenSBI. The DDR firmware should be copied to U-Boot source directory before
+building.
 
 .. code-block:: console
 
 	cd <U-Boot-dir>
+	cp <path-to-ddr-firmware> th1520-ddr-firmware.bin
 	make th1520_lpi4a_defconfig
 	make
 
-This will generate u-boot-dtb.bin
+This will generate u-boot-dtb.bin and u-boot-with-spl.bin. The former contains
+only proper U-Boot and is for chainloading; the later contains also SPL and
+DDR firmware and is ready for booting by BROM directly.
 
 Booting
 ~~~~~~~
@@ -61,7 +80,7 @@
 then bootup from it.
 
 Sample boot log from Lichee PI 4A board via tftp
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 .. code-block:: none
 
@@ -127,3 +146,36 @@
         Err:   serial@ffe7014000
         Model: Sipeed Lichee Pi 4A
         LPI4A=>
+
+SPL support is still in an early stage and not all of the functionalities are
+available when booting from mainline SPL. When using mainline SPL,
+u-boot-with-spl.bin should be loaded to SRAM through fastboot.
+
+Sample boot log from Lichee PI 4A board via fastboot and mainline SPL
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. code-block:: none
+
+	brom_ver 8
+	[APP][E] protocol_connect failed, exit.
+	Starting download of 636588 bytes
+
+	downloading of 636588 bytes finished
+
+	U-Boot SPL 2025.04-rc2-00049-geaa9fc99d4cd-dirty (Apr 26 2025 - 13:31:41 +0000)
+	Trying to boot from RAM
+
+
+	U-Boot 2025.04-rc2-00049-geaa9fc99d4cd-dirty (Apr 26 2025 - 13:31:41 +0000)
+
+	CPU:   thead,c910
+	Model: Sipeed Lichee Pi 4A
+	DRAM:  8 GiB
+	Core:  30 devices, 9 uclasses, devicetree: separate
+	MMC:   mmc@ffe7080000: 0, mmc@ffe7090000: 1
+	Loading Environment from <NULL>... OK
+	In:    serial@ffe7014000
+	Out:   serial@ffe7014000
+	Err:   serial@ffe7014000
+	Model: Sipeed Lichee Pi 4A
+	LPI4A=>
diff --git a/doc/develop/cyclic.rst b/doc/develop/cyclic.rst
index 6f1da6f..a99b170 100644
--- a/doc/develop/cyclic.rst
+++ b/doc/develop/cyclic.rst
@@ -54,3 +54,16 @@
 common schedule() function. This guarantees that cyclic_run() is
 executed very often, which is necessary for the cyclic functions to
 get scheduled and executed at their configured periods.
+
+Idempotence
+-----------
+
+Both the cyclic_register() and cyclic_unregister() functions are safe
+to call on any struct cyclic_info, regardless of whether that instance
+is already registered or not.
+
+More specifically, calling cyclic_unregister() with a cyclic_info
+which is not currently registered is a no-op, while calling
+cyclic_register() with a cyclic_info which is currently registered
+results in it being automatically unregistered, and then registered
+with the new callback function and timeout parameters.
diff --git a/doc/develop/pytest/index.rst b/doc/develop/pytest/index.rst
index 0a7c1b2..dce8a96 100644
--- a/doc/develop/pytest/index.rst
+++ b/doc/develop/pytest/index.rst
@@ -16,12 +16,6 @@
 
 .. toctree::
    :maxdepth: 1
+   :glob:
 
-   test_000_version
-   test_bind
-   test_bootmenu
-   test_bootstage
-   test_button
-   test_efi_loader
-   test_net
-   test_net_boot
+   test_*
diff --git a/doc/develop/pytest/usage.rst b/doc/develop/pytest/usage.rst
index 49d269d..779b2db 100644
--- a/doc/develop/pytest/usage.rst
+++ b/doc/develop/pytest/usage.rst
@@ -522,3 +522,27 @@
 Build configuration values (from `.config`) may be accessed via the dictionary
 `ubman.config.buildconfig`, with keys equal to the Kconfig variable
 names.
+
+A required configuration setting can be defined via a buildconfigspec()
+annotation. The name of the configuration option is specified in lower case. The
+following annotation for a test requires CONFIG_EFI_LOADER=y:
+
+.. code-block:: python
+
+    @pytest.mark.buildconfigspec('efi_loader')
+
+Sometimes multiple configuration option supply the same functionality. If
+multiple arguments are passed to buildconfigspec(), only one of the
+configuration options needs to be set. The following annotation requires that
+either of CONFIG_NET or CONFIG_NET_LWIP is set:
+
+.. code-block:: python
+
+    @pytest.mark.buildconfigspec('net', 'net lwip')
+
+The notbuildconfigspec() annotation can be used to require a configuration
+option not to be set. The following annotation requires CONFIG_RISCV=n:
+
+.. code-block:: python
+
+    @pytest.mark.notbuildconfigspec('riscv')
diff --git a/doc/develop/release_cycle.rst b/doc/develop/release_cycle.rst
index b68b462..36bb905 100644
--- a/doc/develop/release_cycle.rst
+++ b/doc/develop/release_cycle.rst
@@ -75,7 +75,7 @@
 
 * U-Boot |next_ver|-rc2 was released on Mon 12 May 2025.
 
-.. * U-Boot |next_ver|-rc3 was released on Mon 26 May 2025.
+* U-Boot |next_ver|-rc3 was released on Mon 26 May 2025.
 
 .. * U-Boot |next_ver|-rc4 was released on Mon 09 June 2025.
 
diff --git a/doc/usage/cmd/wget.rst b/doc/usage/cmd/wget.rst
index cc82e49..44033aa 100644
--- a/doc/usage/cmd/wget.rst
+++ b/doc/usage/cmd/wget.rst
@@ -141,9 +141,9 @@
    Bytes transferred = 1864 (748 hex)
    # Another server not signed against Digicert will fail
    => wget https://www.google.com/
-   Certificate verification failed
 
    HTTP client error 4
+   Certificate verification failed
    # Disable authentication to allow the command to proceed anyways
    => wget cacert none
    => wget https://www.google.com/
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 18bd640..19aa2ff 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -271,6 +271,7 @@
 source "drivers/clk/stm32/Kconfig"
 source "drivers/clk/tegra/Kconfig"
 source "drivers/clk/ti/Kconfig"
+source "drivers/clk/thead/Kconfig"
 source "drivers/clk/uniphier/Kconfig"
 
 endmenu
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 8411205..5f0c0d8 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -20,6 +20,7 @@
 obj-$(CONFIG_CLK_JH7110) += starfive/
 obj-y += tegra/
 obj-y += ti/
+obj-$(CONFIG_CLK_THEAD) += thead/
 obj-$(CONFIG_$(PHASE_)CLK_INTEL) += intel/
 obj-$(CONFIG_ARCH_ASPEED) += aspeed/
 obj-$(CONFIG_ARCH_MEDIATEK) += mediatek/
diff --git a/drivers/clk/imx/clk-imxrt1170.c b/drivers/clk/imx/clk-imxrt1170.c
index 3f55d0d..bfd5dd6 100644
--- a/drivers/clk/imx/clk-imxrt1170.c
+++ b/drivers/clk/imx/clk-imxrt1170.c
@@ -105,6 +105,8 @@
 "pll2_pfd2", "pll2_pfd0", "pll1_div5", "pll_arm"};
 static const char * const semc_sels[] = {"rcosc48M_div2", "osc", "rcosc400M", "rcosc16M",
 "pll1_div5", "pll2_sys", "pll2_pfd2", "pll3_pfd0"};
+static const char * const flexspi1_sels[] = {"rcosc48M_div2", "osc", "rcosc400M", "rcosc16M",
+"pll3_pdf0", "pll2_clk", "pll2_pfd2", "pll3_clk"};
 
 static int imxrt1170_clk_probe(struct udevice *dev)
 {
@@ -163,6 +165,13 @@
 	       imx_clk_divider(dev, "lpuart1", "lpuart1_sel",
 			       base + (25 * 0x80), 0, 8));
 
+	clk_dm(IMXRT1170_CLK_FLEXSPI1_SEL,
+	       imx_clk_mux(dev, "flexspi1_sel", base + (20 * 0x80), 8, 3,
+			   flexspi1_sels, ARRAY_SIZE(flexspi1_sels)));
+	clk_dm(IMXRT1170_CLK_FLEXSPI1,
+	       imx_clk_divider(dev, "flexspi1", "flexspi1_sel",
+			       base + (20 * 0x80), 0, 8));
+
 	clk_dm(IMXRT1170_CLK_USDHC1_SEL,
 	       imx_clk_mux(dev, "usdhc1_sel", base + (58 * 0x80), 8, 3,
 			   usdhc1_sels, ARRAY_SIZE(usdhc1_sels)));
diff --git a/drivers/clk/thead/Kconfig b/drivers/clk/thead/Kconfig
new file mode 100644
index 0000000..e815286
--- /dev/null
+++ b/drivers/clk/thead/Kconfig
@@ -0,0 +1,19 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# Copyright (c) 2025, Yao Zi <ziyao@disroot.org>
+
+config CLK_THEAD
+	bool "Clock support for T-Head SoCs"
+	depends on CLK
+
+if CLK_THEAD
+
+config CLK_THEAD_TH1520_AP
+	bool "T-Head TH1520 AP clock support"
+	select CLK_CCF
+	default THEAD_TH1520
+	help
+	  This enables support clock driver for T-Head TH1520 Application
+	  processor.
+
+endif
diff --git a/drivers/clk/thead/Makefile b/drivers/clk/thead/Makefile
new file mode 100644
index 0000000..8cc05ed
--- /dev/null
+++ b/drivers/clk/thead/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# Copyright (C) 2025 Yao Zi <ziyao@disroot.org>
+
+obj-$(CONFIG_CLK_THEAD_TH1520_AP) += clk-th1520-ap.o
diff --git a/drivers/clk/thead/clk-th1520-ap.c b/drivers/clk/thead/clk-th1520-ap.c
new file mode 100644
index 0000000..b80ad05
--- /dev/null
+++ b/drivers/clk/thead/clk-th1520-ap.c
@@ -0,0 +1,1031 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Jisheng Zhang <jszhang@kernel.org>
+ * Copyright (C) 2023 Vivo Communication Technology Co. Ltd.
+ * Copyright (C) 2025 Yao Zi <ziyao@disroot.org>
+ *  Authors: Yangtao Li <frank.li@vivo.com>
+ */
+
+#include <asm/io.h>
+#include <dm.h>
+#include <linux/bitfield.h>
+#include <linux/clk-provider.h>
+
+#include <dt-bindings/clock/thead,th1520-clk-ap.h>
+
+#define TH1520_PLL_POSTDIV2	GENMASK(26, 24)
+#define TH1520_PLL_POSTDIV1	GENMASK(22, 20)
+#define TH1520_PLL_FBDIV	GENMASK(19, 8)
+#define TH1520_PLL_REFDIV	GENMASK(5, 0)
+#define TH1520_PLL_BYPASS	BIT(30)
+#define TH1520_PLL_DSMPD	BIT(24)
+#define TH1520_PLL_FRAC		GENMASK(23, 0)
+#define TH1520_PLL_FRAC_BITS    24
+
+static const char ccu_osc_name_to_be_filled[] = "TO BE FILLED";
+
+struct ccu_internal {
+	u8 shift;
+	u8 width;
+};
+
+struct ccu_div_internal {
+	u8 shift;
+	u8 width;
+};
+
+struct ccu_common {
+	void __iomem *reg;
+	const char *name;
+	struct clk clk;
+	int clkid;
+	u16 cfg0;
+	u16 cfg1;
+};
+
+struct ccu_mux {
+	struct ccu_common common;
+	struct ccu_internal mux;
+	const char **parents;
+	size_t num_parents;
+};
+
+struct ccu_gate {
+	struct ccu_common common;
+	const char *parent;
+	u32 enable;
+};
+
+struct ccu_div {
+	struct ccu_div_internal div;
+	struct ccu_common common;
+	struct ccu_internal mux;
+	const char **parents;
+	size_t num_parents;
+	u32 enable;
+};
+
+struct ccu_pll {
+	struct ccu_common	common;
+};
+
+#define TH_CCU_ARG(_shift, _width)					\
+	{								\
+		.shift	= _shift,					\
+		.width	= _width,					\
+	}
+
+#define TH_CCU_DIV_FLAGS(_shift, _width, _flags)			\
+	{								\
+		.shift	= _shift,					\
+		.width	= _width,					\
+	}
+
+#define CCU_GATE(_clkid, _struct, _name, _parent, _reg, _gate, _flags)	\
+	struct ccu_gate _struct = {					\
+		.parent = _parent,					\
+		.enable	= _gate,					\
+		.common	= {						\
+			.clkid		= _clkid,			\
+			.cfg0		= _reg,				\
+			.name		= _name,			\
+		}							\
+	}
+
+static inline struct ccu_common *clk_to_ccu_common(struct clk *clk)
+{
+	return container_of(clk, struct ccu_common, clk);
+}
+
+static inline struct ccu_mux *clk_to_ccu_mux(struct clk *clk)
+{
+	struct ccu_common *common = clk_to_ccu_common(clk);
+
+	return container_of(common, struct ccu_mux, common);
+}
+
+static inline struct ccu_pll *clk_to_ccu_pll(struct clk *clk)
+{
+	struct ccu_common *common = clk_to_ccu_common(clk);
+
+	return container_of(common, struct ccu_pll, common);
+}
+
+static inline struct ccu_div *clk_to_ccu_div(struct clk *clk)
+{
+	struct ccu_common *common = clk_to_ccu_common(clk);
+
+	return container_of(common, struct ccu_div, common);
+}
+
+static inline struct ccu_gate *clk_to_ccu_gate(struct clk *clk)
+{
+	struct ccu_common *common = clk_to_ccu_common(clk);
+
+	return container_of(common, struct ccu_gate, common);
+}
+
+static int ccu_set_parent_helper(struct ccu_common *common,
+				 struct ccu_internal *mux,
+				 u8 index)
+{
+	clrsetbits_le32(common->reg + common->cfg0,
+			GENMASK(mux->width - 1, 0) << mux->shift,
+			index << mux->shift);
+
+	return 0;
+}
+
+static void ccu_disable_helper(struct ccu_common *common, u32 gate)
+{
+	if (!gate)
+		return;
+
+	clrsetbits_le32(common->reg + common->cfg0,
+			gate, ~gate);
+}
+
+static int ccu_enable_helper(struct ccu_common *common, u32 gate)
+{
+	u32 val;
+
+	if (!gate)
+		return 0;
+
+	clrsetbits_le32(common->reg + common->cfg0, gate, gate);
+	val = readl(common->reg + common->cfg0);
+
+	return 0;
+}
+
+static int ccu_get_parent_index_helper(const char * const *parents,
+				       int num_parents, struct clk *parent)
+{
+	const char *parent_name = parent->dev->name;
+	unsigned int index;
+
+	for (index = 0; index < num_parents; index++) {
+		if (!strcmp(parents[index], parent_name))
+			return index;
+	}
+
+	return -ENOENT;
+}
+
+static unsigned long ccu_div_get_rate(struct clk *clk)
+{
+	struct ccu_div *cd = clk_to_ccu_div(clk);
+	unsigned long rate;
+	unsigned int val;
+
+	val = readl(cd->common.reg + cd->common.cfg0);
+	val = val >> cd->div.shift;
+	val &= GENMASK(cd->div.width - 1, 0);
+	rate = divider_recalc_rate(clk, clk_get_parent_rate(clk), val, NULL,
+				   0, cd->div.width);
+
+	return rate;
+}
+
+static int ccu_div_get_parent(struct ccu_div *cd)
+{
+	u32 val = readl(cd->common.reg + cd->common.cfg0);
+
+	return (val >> cd->mux.shift) & GENMASK(cd->mux.width - 1, 0);
+}
+
+static int ccu_div_set_parent(struct clk *clk, struct clk *parent)
+{
+	struct ccu_div *cd = clk_to_ccu_div(clk);
+	u8 id;
+
+	id = ccu_get_parent_index_helper(cd->parents, cd->num_parents, parent);
+	if (id < 0)
+		return id;
+
+	return ccu_set_parent_helper(&cd->common, &cd->mux, id);
+}
+
+static int ccu_div_disable(struct clk *clk)
+{
+	struct ccu_div *cd = clk_to_ccu_div(clk);
+
+	ccu_disable_helper(&cd->common, cd->enable);
+
+	return 0;
+}
+
+static int ccu_div_enable(struct clk *clk)
+{
+	struct ccu_div *cd = clk_to_ccu_div(clk);
+
+	return ccu_enable_helper(&cd->common, cd->enable);
+}
+
+static const struct clk_ops ccu_div_ops = {
+	.disable	= ccu_div_disable,
+	.enable		= ccu_div_enable,
+	.set_parent	= ccu_div_set_parent,
+	.get_rate	= ccu_div_get_rate,
+};
+
+U_BOOT_DRIVER(th1520_clk_div) = {
+	.name	= "th1520_clk_div",
+	.id	= UCLASS_CLK,
+	.ops	= &ccu_div_ops,
+};
+
+static unsigned long th1520_pll_vco_recalc_rate(struct clk *clk,
+						unsigned long parent_rate)
+{
+	struct ccu_pll *pll = clk_to_ccu_pll(clk);
+	unsigned long div, mul, frac;
+	unsigned int cfg0, cfg1;
+	u64 rate = parent_rate;
+
+	cfg0 = readl(pll->common.reg + pll->common.cfg0);
+	cfg1 = readl(pll->common.reg + pll->common.cfg1);
+
+	mul = FIELD_GET(TH1520_PLL_FBDIV, cfg0);
+	div = FIELD_GET(TH1520_PLL_REFDIV, cfg0);
+	if (!(cfg1 & TH1520_PLL_DSMPD)) {
+		mul <<= TH1520_PLL_FRAC_BITS;
+		frac = FIELD_GET(TH1520_PLL_FRAC, cfg1);
+		mul += frac;
+		div <<= TH1520_PLL_FRAC_BITS;
+	}
+
+	rate = parent_rate * mul;
+	rate = rate / div;
+
+	return rate;
+}
+
+static unsigned long th1520_pll_postdiv_recalc_rate(struct clk *clk,
+						    unsigned long parent_rate)
+{
+	struct ccu_pll *pll = clk_to_ccu_pll(clk);
+	unsigned long div, rate = parent_rate;
+	unsigned int cfg0, cfg1;
+
+	cfg0 = readl(pll->common.reg + pll->common.cfg0);
+	cfg1 = readl(pll->common.reg + pll->common.cfg1);
+
+	if (cfg1 & TH1520_PLL_BYPASS)
+		return rate;
+
+	div = FIELD_GET(TH1520_PLL_POSTDIV1, cfg0) *
+	      FIELD_GET(TH1520_PLL_POSTDIV2, cfg0);
+
+	rate = rate / div;
+
+	return rate;
+}
+
+static unsigned long ccu_pll_get_rate(struct clk *clk)
+{
+	unsigned long rate = clk_get_parent_rate(clk);
+
+	rate = th1520_pll_vco_recalc_rate(clk, rate);
+	rate = th1520_pll_postdiv_recalc_rate(clk, rate);
+
+	return rate;
+}
+
+static const struct clk_ops clk_pll_ops = {
+	.get_rate	= ccu_pll_get_rate,
+};
+
+U_BOOT_DRIVER(th1520_clk_pll) = {
+	.name	= "th1520_clk_pll",
+	.id	= UCLASS_CLK,
+	.ops	= &clk_pll_ops,
+};
+
+static struct ccu_pll cpu_pll0_clk = {
+	.common		= {
+		.clkid		= CLK_CPU_PLL0,
+		.cfg0		= 0x000,
+		.cfg1		= 0x004,
+		.name		= "cpu-pll0",
+	},
+};
+
+static struct ccu_pll cpu_pll1_clk = {
+	.common		= {
+		.clkid		= CLK_CPU_PLL1,
+		.cfg0		= 0x010,
+		.cfg1		= 0x014,
+		.name		= "cpu-pll1",
+	},
+};
+
+static struct ccu_pll gmac_pll_clk = {
+	.common		= {
+		.clkid		= CLK_GMAC_PLL,
+		.cfg0		= 0x020,
+		.cfg1		= 0x024,
+		.name		= "gmac-pll",
+	},
+};
+
+static const char *gmac_pll_clk_parent[] = {
+	"gmac-pll",
+};
+
+static struct ccu_pll video_pll_clk = {
+	.common		= {
+		.clkid		= CLK_VIDEO_PLL,
+		.cfg0		= 0x030,
+		.cfg1		= 0x034,
+		.name		= "video-pll",
+	},
+};
+
+static const char *video_pll_clk_parent[] = {
+	"video-pll",
+};
+
+static struct ccu_pll dpu0_pll_clk = {
+	.common		= {
+		.clkid		= CLK_DPU0_PLL,
+		.cfg0		= 0x040,
+		.cfg1		= 0x044,
+		.name		= "dpu0-pll",
+	},
+};
+
+static const char *dpu0_pll_clk_parent[] = {
+	"dpu0-pll",
+};
+
+static struct ccu_pll dpu1_pll_clk = {
+	.common		= {
+		.clkid		= CLK_DPU1_PLL,
+		.cfg0		= 0x050,
+		.cfg1		= 0x054,
+		.name		= "dpu1-pll",
+	},
+};
+
+static const char *dpu1_pll_clk_parent[] = {
+	"dpu1-pll",
+};
+
+static struct ccu_pll tee_pll_clk = {
+	.common		= {
+		.clkid		= CLK_TEE_PLL,
+		.cfg0		= 0x060,
+		.cfg1		= 0x064,
+		.name		= "tee-pll",
+	},
+};
+
+static const char *c910_i0_parents[] = {
+	"cpu-pll0", ccu_osc_name_to_be_filled,
+};
+
+static struct ccu_mux c910_i0_clk = {
+	.parents	= c910_i0_parents,
+	.num_parents	= ARRAY_SIZE(c910_i0_parents),
+	.mux		= TH_CCU_ARG(1, 1),
+	.common		= {
+		.clkid		= CLK_C910_I0,
+		.cfg0		= 0x100,
+		.name		= "c910-i0",
+	}
+};
+
+static const char *c910_parents[] = {
+	"c910-i0", "cpu-pll1",
+};
+
+static struct ccu_mux c910_clk = {
+	.parents	= c910_parents,
+	.num_parents	= ARRAY_SIZE(c910_parents),
+	.mux		= TH_CCU_ARG(0, 1),
+	.common		= {
+		.clkid		= CLK_C910,
+		.cfg0		= 0x100,
+		.name		= "c910",
+	}
+};
+
+static const char *ahb2_cpusys_parents[] = {
+	"gmac-pll", ccu_osc_name_to_be_filled,
+};
+
+static struct ccu_div ahb2_cpusys_hclk = {
+	.parents	= ahb2_cpusys_parents,
+	.num_parents	= ARRAY_SIZE(ahb2_cpusys_parents),
+	.div		= TH_CCU_DIV_FLAGS(0, 3, CLK_DIVIDER_ONE_BASED),
+	.mux		= TH_CCU_ARG(5, 1),
+	.common		= {
+		.clkid          = CLK_AHB2_CPUSYS_HCLK,
+		.cfg0		= 0x120,
+		.name		= "ahb2-cpusys-hclk",
+	},
+};
+
+static const char *ahb2_cpusys_hclk_parents[] = {
+	"ahb2-cpusys-hclk",
+};
+
+static struct ccu_div apb3_cpusys_pclk = {
+	.parents	= ahb2_cpusys_hclk_parents,
+	.num_parents	= ARRAY_SIZE(ahb2_cpusys_hclk_parents),
+	.div		= TH_CCU_ARG(0, 3),
+	.common		= {
+		.clkid          = CLK_APB3_CPUSYS_PCLK,
+		.cfg0		= 0x130,
+		.name		= "apb3-cpusys-pclk",
+	},
+};
+
+static struct ccu_div axi4_cpusys2_aclk = {
+	.parents	= gmac_pll_clk_parent,
+	.num_parents	= ARRAY_SIZE(gmac_pll_clk_parent),
+	.div		= TH_CCU_DIV_FLAGS(0, 3, CLK_DIVIDER_ONE_BASED),
+	.common		= {
+		.clkid          = CLK_AXI4_CPUSYS2_ACLK,
+		.cfg0		= 0x134,
+		.name		= "axi4-cpusys2-aclk",
+	},
+};
+
+static const char *axi_parents[] = {
+	"video-pll", ccu_osc_name_to_be_filled,
+};
+
+static struct ccu_div axi_aclk = {
+	.parents	= axi_parents,
+	.num_parents	= ARRAY_SIZE(axi_parents),
+	.div		= TH_CCU_DIV_FLAGS(0, 4, CLK_DIVIDER_ONE_BASED),
+	.mux		= TH_CCU_ARG(5, 1),
+	.common		= {
+		.clkid          = CLK_AXI_ACLK,
+		.cfg0		= 0x138,
+		.name		= "axi-aclk",
+	},
+};
+
+static const char *perisys_ahb_hclk_parents[] = {
+	"gmac-pll", ccu_osc_name_to_be_filled,
+};
+
+static struct ccu_div perisys_ahb_hclk = {
+	.parents	= perisys_ahb_hclk_parents,
+	.num_parents	= ARRAY_SIZE(perisys_ahb_hclk_parents),
+	.enable		= BIT(6),
+	.div		= TH_CCU_DIV_FLAGS(0, 4, CLK_DIVIDER_ONE_BASED),
+	.mux		= TH_CCU_ARG(5, 1),
+	.common		= {
+		.clkid          = CLK_PERI_AHB_HCLK,
+		.cfg0		= 0x140,
+		.name		= "perisys-ahb-hclk",
+	},
+};
+
+static const char *perisys_ahb_hclk_parent[] = {
+	"perisys-ahb-hclk",
+};
+
+static struct ccu_div perisys_apb_pclk = {
+	.parents	= perisys_ahb_hclk_parent,
+	.num_parents	= ARRAY_SIZE(perisys_ahb_hclk_parent),
+	.div		= TH_CCU_ARG(0, 3),
+	.common		= {
+		.clkid          = CLK_PERI_APB_PCLK,
+		.cfg0		= 0x150,
+		.name		= "perisys-apb-pclk",
+	},
+};
+
+static struct ccu_div peri2sys_apb_pclk = {
+	.parents	= gmac_pll_clk_parent,
+	.num_parents	= ARRAY_SIZE(gmac_pll_clk_parent),
+	.div		= TH_CCU_DIV_FLAGS(4, 3, CLK_DIVIDER_ONE_BASED),
+	.common		= {
+		.clkid          = CLK_PERI2APB_PCLK,
+		.cfg0		= 0x150,
+		.name		= "peri2sys-apb-pclk",
+	},
+};
+
+static const char *apb_parents[] = {
+	"gmac-pll", ccu_osc_name_to_be_filled,
+};
+
+static struct ccu_div apb_pclk = {
+	.parents	= apb_parents,
+	.num_parents	= ARRAY_SIZE(apb_parents),
+	.enable		= BIT(5),
+	.div		= TH_CCU_DIV_FLAGS(0, 4, CLK_DIVIDER_ONE_BASED),
+	.mux		= TH_CCU_ARG(7, 1),
+	.common		= {
+		.clkid          = CLK_APB_PCLK,
+		.cfg0		= 0x1c4,
+		.name		= "apb-pclk",
+	},
+};
+
+static const char *npu_parents[] = {
+	"gmac-pll", "video-pll",
+};
+
+static struct ccu_div npu_clk = {
+	.parents	= npu_parents,
+	.num_parents	= ARRAY_SIZE(npu_parents),
+	.enable		= BIT(4),
+	.div		= TH_CCU_DIV_FLAGS(0, 3, CLK_DIVIDER_ONE_BASED),
+	.mux		= TH_CCU_ARG(6, 1),
+	.common		= {
+		.clkid          = CLK_NPU,
+		.cfg0		= 0x1c8,
+		.name		= "npu",
+	},
+};
+
+static struct ccu_div vi_clk = {
+	.parents	= video_pll_clk_parent,
+	.num_parents	= ARRAY_SIZE(video_pll_clk_parent),
+	.div		= TH_CCU_DIV_FLAGS(16, 4, CLK_DIVIDER_ONE_BASED),
+	.common		= {
+		.clkid          = CLK_VI,
+		.cfg0		= 0x1d0,
+		.name		= "vi",
+	},
+};
+
+static struct ccu_div vi_ahb_clk = {
+	.parents	= video_pll_clk_parent,
+	.num_parents	= ARRAY_SIZE(video_pll_clk_parent),
+	.div		= TH_CCU_DIV_FLAGS(0, 4, CLK_DIVIDER_ONE_BASED),
+	.common		= {
+		.clkid          = CLK_VI_AHB,
+		.cfg0		= 0x1d0,
+		.name		= "vi-ahb",
+	},
+};
+
+static struct ccu_div vo_axi_clk = {
+	.parents	= video_pll_clk_parent,
+	.num_parents	= ARRAY_SIZE(video_pll_clk_parent),
+	.enable		= BIT(5),
+	.div		= TH_CCU_DIV_FLAGS(0, 4, CLK_DIVIDER_ONE_BASED),
+	.common		= {
+		.clkid          = CLK_VO_AXI,
+		.cfg0		= 0x1dc,
+		.name		= "vo-axi",
+	},
+};
+
+static struct ccu_div vp_apb_clk = {
+	.parents	= gmac_pll_clk_parent,
+	.num_parents	= ARRAY_SIZE(gmac_pll_clk_parent),
+	.div		= TH_CCU_DIV_FLAGS(0, 3, CLK_DIVIDER_ONE_BASED),
+	.common		= {
+		.clkid          = CLK_VP_APB,
+		.cfg0		= 0x1e0,
+		.name		= "vp-apb",
+	},
+};
+
+static struct ccu_div vp_axi_clk = {
+	.parents	= video_pll_clk_parent,
+	.num_parents	= ARRAY_SIZE(video_pll_clk_parent),
+	.enable		= BIT(15),
+	.div		= TH_CCU_DIV_FLAGS(8, 4, CLK_DIVIDER_ONE_BASED),
+	.common		= {
+		.clkid          = CLK_VP_AXI,
+		.cfg0		= 0x1e0,
+		.name		= "vp-axi",
+	},
+};
+
+static struct ccu_div venc_clk = {
+	.parents	= gmac_pll_clk_parent,
+	.num_parents	= ARRAY_SIZE(gmac_pll_clk_parent),
+	.enable		= BIT(5),
+	.div		= TH_CCU_DIV_FLAGS(0, 3, CLK_DIVIDER_ONE_BASED),
+	.common		= {
+		.clkid          = CLK_VENC,
+		.cfg0		= 0x1e4,
+		.name		= "venc",
+	},
+};
+
+static struct ccu_div dpu0_clk = {
+	.parents	= dpu0_pll_clk_parent,
+	.num_parents	= ARRAY_SIZE(dpu0_pll_clk_parent),
+	.div		= TH_CCU_DIV_FLAGS(0, 8, CLK_DIVIDER_ONE_BASED),
+	.common		= {
+		.clkid          = CLK_DPU0,
+		.cfg0		= 0x1e8,
+		.name		= "dpu0",
+	},
+};
+
+static struct ccu_div dpu1_clk = {
+	.parents	= dpu1_pll_clk_parent,
+	.num_parents	= ARRAY_SIZE(dpu1_pll_clk_parent),
+	.div		= TH_CCU_DIV_FLAGS(0, 8, CLK_DIVIDER_ONE_BASED),
+	.common		= {
+		.clkid          = CLK_DPU1,
+		.cfg0		= 0x1ec,
+		.name		= "dpu1",
+	},
+};
+
+static CCU_GATE(CLK_BROM, brom_clk, "brom", "ahb2-cpusys-hclk", 0x100, BIT(4), 0);
+static CCU_GATE(CLK_BMU, bmu_clk, "bmu", "axi4-cpusys2-aclk", 0x100, BIT(5), 0);
+static CCU_GATE(CLK_AON2CPU_A2X, aon2cpu_a2x_clk, "aon2cpu-a2x", "axi4-cpusys2-aclk",
+		0x134, BIT(8), 0);
+static CCU_GATE(CLK_X2X_CPUSYS, x2x_cpusys_clk, "x2x-cpusys", "axi4-cpusys2-aclk",
+		0x134, BIT(7), 0);
+static CCU_GATE(CLK_CPU2AON_X2H, cpu2aon_x2h_clk, "cpu2aon-x2h", "axi-aclk", 0x138, BIT(8), 0);
+static CCU_GATE(CLK_CPU2PERI_X2H, cpu2peri_x2h_clk, "cpu2peri-x2h", "axi4-cpusys2-aclk",
+		0x140, BIT(9), CLK_IGNORE_UNUSED);
+static CCU_GATE(CLK_PERISYS_APB1_HCLK, perisys_apb1_hclk, "perisys-apb1-hclk", "perisys-ahb-hclk",
+		0x150, BIT(9), 0);
+static CCU_GATE(CLK_PERISYS_APB2_HCLK, perisys_apb2_hclk, "perisys-apb2-hclk", "perisys-ahb-hclk",
+		0x150, BIT(10), CLK_IGNORE_UNUSED);
+static CCU_GATE(CLK_PERISYS_APB3_HCLK, perisys_apb3_hclk, "perisys-apb3-hclk", "perisys-ahb-hclk",
+		0x150, BIT(11), CLK_IGNORE_UNUSED);
+static CCU_GATE(CLK_PERISYS_APB4_HCLK, perisys_apb4_hclk, "perisys-apb4-hclk", "perisys-ahb-hclk",
+		0x150, BIT(12), 0);
+static CCU_GATE(CLK_NPU_AXI, npu_axi_clk, "npu-axi", "axi-aclk", 0x1c8, BIT(5), 0);
+static CCU_GATE(CLK_CPU2VP, cpu2vp_clk, "cpu2vp", "axi-aclk", 0x1e0, BIT(13), 0);
+static CCU_GATE(CLK_EMMC_SDIO, emmc_sdio_clk, "emmc-sdio", "emmc-sdio-ref", 0x204, BIT(30), 0);
+static CCU_GATE(CLK_GMAC1, gmac1_clk, "gmac1", "gmac-pll", 0x204, BIT(26), 0);
+static CCU_GATE(CLK_PADCTRL1, padctrl1_clk, "padctrl1", "perisys-apb-pclk", 0x204, BIT(24), 0);
+static CCU_GATE(CLK_DSMART, dsmart_clk, "dsmart", "perisys-apb-pclk", 0x204, BIT(23), 0);
+static CCU_GATE(CLK_PADCTRL0, padctrl0_clk, "padctrl0", "perisys-apb-pclk", 0x204, BIT(22), 0);
+static CCU_GATE(CLK_GMAC_AXI, gmac_axi_clk, "gmac-axi", "axi4-cpusys2-aclk", 0x204, BIT(21), 0);
+static CCU_GATE(CLK_GPIO3, gpio3_clk, "gpio3-clk", "peri2sys-apb-pclk", 0x204, BIT(20), 0);
+static CCU_GATE(CLK_GMAC0, gmac0_clk, "gmac0", "gmac-pll", 0x204, BIT(19), 0);
+static CCU_GATE(CLK_PWM, pwm_clk, "pwm", "perisys-apb-pclk", 0x204, BIT(18), 0);
+static CCU_GATE(CLK_QSPI0, qspi0_clk, "qspi0", "video-pll", 0x204, BIT(17), 0);
+static CCU_GATE(CLK_QSPI1, qspi1_clk, "qspi1", "video-pll", 0x204, BIT(16), 0);
+static CCU_GATE(CLK_SPI, spi_clk, "spi", "video-pll", 0x204, BIT(15), 0);
+static CCU_GATE(CLK_UART0_PCLK, uart0_pclk, "uart0-pclk", "perisys-apb-pclk", 0x204, BIT(14), 0);
+static CCU_GATE(CLK_UART1_PCLK, uart1_pclk, "uart1-pclk", "perisys-apb-pclk", 0x204, BIT(13), 0);
+static CCU_GATE(CLK_UART2_PCLK, uart2_pclk, "uart2-pclk", "perisys-apb-pclk", 0x204, BIT(12), 0);
+static CCU_GATE(CLK_UART3_PCLK, uart3_pclk, "uart3-pclk", "perisys-apb-pclk", 0x204, BIT(11), 0);
+static CCU_GATE(CLK_UART4_PCLK, uart4_pclk, "uart4-pclk", "perisys-apb-pclk", 0x204, BIT(10), 0);
+static CCU_GATE(CLK_UART5_PCLK, uart5_pclk, "uart5-pclk", "perisys-apb-pclk", 0x204, BIT(9), 0);
+static CCU_GATE(CLK_GPIO0, gpio0_clk, "gpio0-clk", "perisys-apb-pclk", 0x204, BIT(8), 0);
+static CCU_GATE(CLK_GPIO1, gpio1_clk, "gpio1-clk", "perisys-apb-pclk", 0x204, BIT(7), 0);
+static CCU_GATE(CLK_GPIO2, gpio2_clk, "gpio2-clk", "peri2sys-apb-pclk", 0x204, BIT(6), 0);
+static CCU_GATE(CLK_I2C0, i2c0_clk, "i2c0", "perisys-apb-pclk", 0x204, BIT(5), 0);
+static CCU_GATE(CLK_I2C1, i2c1_clk, "i2c1", "perisys-apb-pclk", 0x204, BIT(4), 0);
+static CCU_GATE(CLK_I2C2, i2c2_clk, "i2c2", "perisys-apb-pclk", 0x204, BIT(3), 0);
+static CCU_GATE(CLK_I2C3, i2c3_clk, "i2c3", "perisys-apb-pclk", 0x204, BIT(2), 0);
+static CCU_GATE(CLK_I2C4, i2c4_clk, "i2c4", "perisys-apb-pclk", 0x204, BIT(1), 0);
+static CCU_GATE(CLK_I2C5, i2c5_clk, "i2c5", "perisys-apb-pclk", 0x204, BIT(0), 0);
+static CCU_GATE(CLK_SPINLOCK, spinlock_clk, "spinlock", "ahb2-cpusys-hclk", 0x208, BIT(10), 0);
+static CCU_GATE(CLK_DMA, dma_clk, "dma", "axi4-cpusys2-aclk", 0x208, BIT(8), 0);
+static CCU_GATE(CLK_MBOX0, mbox0_clk, "mbox0", "apb3-cpusys-pclk", 0x208, BIT(7), 0);
+static CCU_GATE(CLK_MBOX1, mbox1_clk, "mbox1", "apb3-cpusys-pclk", 0x208, BIT(6), 0);
+static CCU_GATE(CLK_MBOX2, mbox2_clk, "mbox2", "apb3-cpusys-pclk", 0x208, BIT(5), 0);
+static CCU_GATE(CLK_MBOX3, mbox3_clk, "mbox3", "apb3-cpusys-pclk", 0x208, BIT(4), 0);
+static CCU_GATE(CLK_WDT0, wdt0_clk, "wdt0", "apb3-cpusys-pclk", 0x208, BIT(3), 0);
+static CCU_GATE(CLK_WDT1, wdt1_clk, "wdt1", "apb3-cpusys-pclk", 0x208, BIT(2), 0);
+static CCU_GATE(CLK_TIMER0, timer0_clk, "timer0", "apb3-cpusys-pclk", 0x208, BIT(1), 0);
+static CCU_GATE(CLK_TIMER1, timer1_clk, "timer1", "apb3-cpusys-pclk", 0x208, BIT(0), 0);
+static CCU_GATE(CLK_SRAM0, sram0_clk, "sram0", "axi-aclk", 0x20c, BIT(4), 0);
+static CCU_GATE(CLK_SRAM1, sram1_clk, "sram1", "axi-aclk", 0x20c, BIT(3), 0);
+static CCU_GATE(CLK_SRAM2, sram2_clk, "sram2", "axi-aclk", 0x20c, BIT(2), 0);
+static CCU_GATE(CLK_SRAM3, sram3_clk, "sram3", "axi-aclk", 0x20c, BIT(1), 0);
+
+static const char *uart_sclk_parents[] = {
+	"gmac-pll-clk-100m", ccu_osc_name_to_be_filled,
+};
+
+static struct ccu_mux uart_sclk = {
+	.parents	= uart_sclk_parents,
+	.num_parents	= ARRAY_SIZE(uart_sclk_parents),
+	.mux		= TH_CCU_ARG(0, 1),
+	.common		= {
+		.clkid          = CLK_UART_SCLK,
+		.cfg0		= 0x210,
+		.name		= "uart-sclk",
+	}
+};
+
+static struct ccu_common *th1520_pll_clks[] = {
+	&cpu_pll0_clk.common,
+	&cpu_pll1_clk.common,
+	&gmac_pll_clk.common,
+	&video_pll_clk.common,
+	&dpu0_pll_clk.common,
+	&dpu1_pll_clk.common,
+	&tee_pll_clk.common,
+};
+
+static struct ccu_common *th1520_div_clks[] = {
+	&ahb2_cpusys_hclk.common,
+	&apb3_cpusys_pclk.common,
+	&axi4_cpusys2_aclk.common,
+	&perisys_ahb_hclk.common,
+	&perisys_apb_pclk.common,
+	&axi_aclk.common,
+	&peri2sys_apb_pclk.common,
+	&apb_pclk.common,
+	&npu_clk.common,
+	&vi_clk.common,
+	&vi_ahb_clk.common,
+	&vo_axi_clk.common,
+	&vp_apb_clk.common,
+	&vp_axi_clk.common,
+	&venc_clk.common,
+	&dpu0_clk.common,
+	&dpu1_clk.common,
+};
+
+static struct ccu_common *th1520_mux_clks[] = {
+	&c910_i0_clk.common,
+	&c910_clk.common,
+	&uart_sclk.common,
+};
+
+static struct ccu_common *th1520_gate_clks[] = {
+	&emmc_sdio_clk.common,
+	&aon2cpu_a2x_clk.common,
+	&x2x_cpusys_clk.common,
+	&brom_clk.common,
+	&bmu_clk.common,
+	&cpu2aon_x2h_clk.common,
+	&cpu2peri_x2h_clk.common,
+	&cpu2vp_clk.common,
+	&perisys_apb1_hclk.common,
+	&perisys_apb2_hclk.common,
+	&perisys_apb3_hclk.common,
+	&perisys_apb4_hclk.common,
+	&npu_axi_clk.common,
+	&gmac1_clk.common,
+	&padctrl1_clk.common,
+	&dsmart_clk.common,
+	&padctrl0_clk.common,
+	&gmac_axi_clk.common,
+	&gpio3_clk.common,
+	&gmac0_clk.common,
+	&pwm_clk.common,
+	&qspi0_clk.common,
+	&qspi1_clk.common,
+	&spi_clk.common,
+	&uart0_pclk.common,
+	&uart1_pclk.common,
+	&uart2_pclk.common,
+	&uart3_pclk.common,
+	&uart4_pclk.common,
+	&uart5_pclk.common,
+	&gpio0_clk.common,
+	&gpio1_clk.common,
+	&gpio2_clk.common,
+	&i2c0_clk.common,
+	&i2c1_clk.common,
+	&i2c2_clk.common,
+	&i2c3_clk.common,
+	&i2c4_clk.common,
+	&i2c5_clk.common,
+	&spinlock_clk.common,
+	&dma_clk.common,
+	&mbox0_clk.common,
+	&mbox1_clk.common,
+	&mbox2_clk.common,
+	&mbox3_clk.common,
+	&wdt0_clk.common,
+	&wdt1_clk.common,
+	&timer0_clk.common,
+	&timer1_clk.common,
+	&sram0_clk.common,
+	&sram1_clk.common,
+	&sram2_clk.common,
+	&sram3_clk.common,
+};
+
+static void th1520_clk_fill_osc_name(const char **names, size_t name_num,
+				     const char *osc_name)
+{
+	size_t i;
+
+	for (i = 0; i < name_num; i++) {
+		if (names[i] == ccu_osc_name_to_be_filled)
+			names[i] = osc_name;
+	}
+}
+
+static int th1520_clk_probe(struct udevice *dev)
+{
+	struct clk *clk, osc_clk;
+	const char *osc_name;
+	void __iomem *base;
+	fdt_addr_t addr;
+	int ret, i;
+
+	addr = dev_read_addr(dev);
+	if (addr == FDT_ADDR_T_NONE)
+		return -EINVAL;
+
+	base = (void __iomem *)addr;
+
+	ret = clk_get_by_index(dev, 0, &osc_clk);
+	if (ret) {
+		pr_err("failed to get osc clock: %d\n", ret);
+		return ret;
+	}
+
+	osc_name = clk_hw_get_name(&osc_clk);
+
+	for (i = 0; i < ARRAY_SIZE(th1520_pll_clks); i++) {
+		struct ccu_common *common = th1520_pll_clks[i];
+
+		common->reg = base;
+
+		ret = clk_register(&common->clk, "th1520_clk_pll",
+				   common->name, osc_name);
+		if (ret) {
+			pr_err("failed to register PLL %s: %d\n",
+			       common->name, ret);
+			return ret;
+		}
+
+		common->clk.id = common->clkid;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(th1520_div_clks); i++) {
+		struct ccu_div *cd = container_of(th1520_div_clks[i],
+						  struct ccu_div, common);
+		const char *current_parent;
+
+		cd->common.reg = base;
+		th1520_clk_fill_osc_name(cd->parents, cd->num_parents,
+					 osc_name);
+
+		if (cd->num_parents > 1)
+			current_parent = cd->parents[ccu_div_get_parent(cd)];
+		else
+			current_parent = cd->parents[0];
+
+		ret = clk_register(&cd->common.clk, "th1520_clk_div",
+				   cd->common.name,
+				   current_parent);
+
+		if (ret) {
+			pr_err("failed to register div clock %s: %d\n",
+			       cd->common.name, ret);
+			return ret;
+		}
+
+		cd->common.clk.id = cd->common.clkid;
+	}
+
+	clk = clk_register_fixed_factor(dev, "gmac-pll-clk-100m", "gmac-pll",
+					0, 1, 10);
+	if (IS_ERR(clk)) {
+		pr_err("failed to register gmac-pll-clk-100m: %d\n",
+		       (int)PTR_ERR(clk));
+		return PTR_ERR(clk);
+	}
+	clk->id = CLK_PLL_GMAC_100M;
+
+	clk = clk_register_fixed_factor(dev, "emmc-sdio-ref", "video-pll",
+					0, 1, 4);
+	if (IS_ERR(clk)) {
+		pr_err("failed to register emmc-sdio-ref: %d\n",
+		       (int)PTR_ERR(clk));
+		return PTR_ERR(clk);
+	}
+
+	for (i = 0; i < ARRAY_SIZE(th1520_mux_clks); i++) {
+		struct ccu_mux *cm = container_of(th1520_mux_clks[i],
+						  struct ccu_mux, common);
+
+		th1520_clk_fill_osc_name(cm->parents, cm->num_parents,
+					 osc_name);
+
+		clk = clk_register_mux(dev, cm->common.name,
+				       cm->parents, cm->num_parents,
+				       0,
+				       base + cm->common.cfg0,
+				       cm->mux.shift, cm->mux.width,
+				       0);
+		if (IS_ERR(clk)) {
+			pr_err("failed to register mux clock %s: %d\n",
+			       cm->common.name, (int)PTR_ERR(clk));
+			return PTR_ERR(clk);
+		}
+
+		clk->id = cm->common.clkid;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(th1520_gate_clks); i++) {
+		struct ccu_gate *cg = container_of(th1520_gate_clks[i],
+						   struct ccu_gate, common);
+
+		th1520_clk_fill_osc_name(&cg->parent, 1, osc_name);
+
+		clk = clk_register_gate(dev, cg->common.name,
+					cg->parent,
+					0,
+					base + cg->common.cfg0,
+					ffs(cg->enable) - 1, 0, NULL);
+		if (IS_ERR(clk)) {
+			pr_err("failed to register gate clock %s: %d\n",
+			       cg->common.name, (int)PTR_ERR(clk));
+			return PTR_ERR(clk);
+		}
+
+		clk->id = cg->common.clkid;
+	}
+
+	return 0;
+}
+
+static const struct udevice_id th1520_clk_match[] = {
+	{
+		.compatible = "thead,th1520-clk-ap",
+	},
+	{ /* sentinel */ },
+};
+
+static int th1520_clk_enable(struct clk *clk)
+{
+	struct clk *c;
+	int ret;
+
+	ret = clk_get_by_id(clk->id, &c);
+	if (ret)
+		return ret;
+
+	return clk_enable(c);
+}
+
+static int th1520_clk_disable(struct clk *clk)
+{
+	struct clk *c;
+	int ret;
+
+	ret = clk_get_by_id(clk->id, &c);
+	if (ret)
+		return ret;
+
+	return clk_disable(c);
+}
+
+static ulong th1520_clk_get_rate(struct clk *clk)
+{
+	struct clk *c;
+	int ret;
+
+	ret = clk_get_by_id(clk->id, &c);
+	if (ret)
+		return ret;
+
+	return clk_get_rate(c);
+}
+
+static ulong th1520_clk_set_rate(struct clk *clk, ulong rate)
+{
+	struct clk *c;
+	int ret;
+
+	ret = clk_get_by_id(clk->id, &c);
+	if (ret)
+		return ret;
+
+	return clk_set_rate(c, rate);
+}
+
+static int th1520_clk_set_parent(struct clk *clk, struct clk *parent)
+{
+	struct clk *c, *p;
+	int ret;
+
+	ret = clk_get_by_id(clk->id, &c);
+	if (ret)
+		return ret;
+
+	ret = clk_get_by_id(parent->id, &p);
+	if (ret)
+		return ret;
+
+	return clk_set_parent(c, p);
+}
+
+static const struct clk_ops th1520_clk_ops = {
+	.enable		= th1520_clk_enable,
+	.disable	= th1520_clk_disable,
+	.get_rate	= th1520_clk_get_rate,
+	.set_rate	= th1520_clk_set_rate,
+	.set_parent	= th1520_clk_set_parent,
+};
+
+U_BOOT_DRIVER(th1520_clk) = {
+	.name		= "th1520-clk",
+	.id		= UCLASS_CLK,
+	.of_match	= th1520_clk_match,
+	.probe		= th1520_clk_probe,
+	.ops		= &th1520_clk_ops,
+};
diff --git a/drivers/crypto/fsl/jr.c b/drivers/crypto/fsl/jr.c
index 8f7a821..1d95d2b 100644
--- a/drivers/crypto/fsl/jr.c
+++ b/drivers/crypto/fsl/jr.c
@@ -217,13 +217,6 @@
 
 	jr->head = (head + 1) & (jr->size - 1);
 
-	/* Invalidate output ring */
-	start = (unsigned long)jr->output_ring &
-					~(ARCH_DMA_MINALIGN - 1);
-	end = ALIGN((unsigned long)jr->output_ring + jr->op_size,
-		    ARCH_DMA_MINALIGN);
-	invalidate_dcache_range(start, end);
-
 	sec_out32(&regs->irja, 1);
 
 	return 0;
@@ -243,6 +236,7 @@
 #else
 	uint32_t *addr;
 #endif
+	unsigned long start, end;
 
 	while (sec_in32(&regs->orsf) && CIRC_CNT(jr->head, jr->tail,
 						 jr->size)) {
@@ -250,6 +244,11 @@
 		found = 0;
 
 		caam_dma_addr_t op_desc;
+
+		/* Invalidate output ring */
+		start = (unsigned long)jr->output_ring & ~(ARCH_DMA_MINALIGN - 1);
+		end = ALIGN((unsigned long)jr->output_ring + jr->op_size, ARCH_DMA_MINALIGN);
+		invalidate_dcache_range(start, end);
 	#ifdef CONFIG_CAAM_64BIT
 		/* Read the 64 bit Descriptor address from Output Ring.
 		 * The 32 bit hign and low part of the address will
@@ -283,8 +282,13 @@
 		}
 
 		/* Error condition if match not found */
-		if (!found)
+		if (!found) {
+			int slots_full = sec_in32(&regs->orsf);
+
+			jr->tail = (jr->tail + slots_full) & (jr->size - 1);
+			sec_out32(&regs->orjr, slots_full);
 			return -1;
+		}
 
 		jr->info[idx].op_done = 1;
 		callback = (void *)jr->info[idx].callback;
@@ -296,14 +300,14 @@
 		 */
 		if (idx == tail)
 			do {
+				jr->info[tail].op_done = 0;
 				tail = (tail + 1) & (jr->size - 1);
 			} while (jr->info[tail].op_done);
 
 		jr->tail = tail;
-		jr->read_idx = (jr->read_idx + 1) & (jr->size - 1);
+
 
 		sec_out32(&regs->orjr, 1);
-		jr->info[idx].op_done = 0;
 
 		callback(status, arg);
 	}
@@ -378,7 +382,6 @@
 
 	jr->head = 0;
 	jr->tail = 0;
-	jr->read_idx = 0;
 	jr->write_idx = 0;
 	memset(jr->info, 0, sizeof(jr->info));
 	memset(jr->input_ring, 0, jr->size * sizeof(caam_dma_addr_t));
diff --git a/drivers/crypto/fsl/jr.h b/drivers/crypto/fsl/jr.h
index b136cd8..8d5ca03 100644
--- a/drivers/crypto/fsl/jr.h
+++ b/drivers/crypto/fsl/jr.h
@@ -83,10 +83,6 @@
 	 * in-order job completion
 	 */
 	int tail;
-	/* Read index of the output ring. It may not match with tail in case
-	 * of out of order completetion
-	 */
-	int read_idx;
 	/* Write index to input ring. Would be always equal to head */
 	int write_idx;
 	/* Size of the rings. */
diff --git a/drivers/mmc/cv1800b_sdhci.c b/drivers/mmc/cv1800b_sdhci.c
index 4e75051..377e6a8 100644
--- a/drivers/mmc/cv1800b_sdhci.c
+++ b/drivers/mmc/cv1800b_sdhci.c
@@ -31,6 +31,7 @@
 		udelay(10);
 }
 
+#if CONFIG_IS_ENABLED(MMC_SUPPORTS_TUNING)
 static int cv1800b_execute_tuning(struct mmc *mmc, u8 opcode)
 {
 	struct sdhci_host *host = dev_get_priv(mmc->dev);
@@ -61,9 +62,12 @@
 
 	return 0;
 }
+#endif
 
 const struct sdhci_ops cv1800b_sdhci_sd_ops = {
+#if CONFIG_IS_ENABLED(MMC_SUPPORTS_TUNING)
 	.platform_execute_tuning = cv1800b_execute_tuning,
+#endif
 };
 
 static int cv1800b_sdhci_bind(struct udevice *dev)
diff --git a/drivers/net/fsl_enetc.c b/drivers/net/fsl_enetc.c
index 52fa820..97cccda 100644
--- a/drivers/net/fsl_enetc.c
+++ b/drivers/net/fsl_enetc.c
@@ -473,13 +473,15 @@
 /* Apply protocol specific configuration to MAC, serdes as needed */
 static void enetc_start_pcs(struct udevice *dev)
 {
+	struct enetc_data *data = (struct enetc_data *)dev_get_driver_data(dev);
 	struct enetc_priv *priv = dev_get_priv(dev);
 
 	/* register internal MDIO for debug purposes */
 	if (enetc_read_pcapr_mdio(dev)) {
 		priv->imdio.read = enetc_mdio_read;
 		priv->imdio.write = enetc_mdio_write;
-		priv->imdio.priv = priv->port_regs + ENETC_PM_IMDIO_BASE;
+		priv->imdio.priv = priv->port_regs + data->reg_offset_mac +
+		                   ENETC_PM_IMDIO_BASE;
 		strlcpy(priv->imdio.name, dev->name, MDIO_NAME_LEN);
 		if (!miiphy_get_dev_by_name(priv->imdio.name))
 			mdio_register(&priv->imdio);
diff --git a/drivers/ram/Kconfig b/drivers/ram/Kconfig
index 2a40b0c..39d03e8 100644
--- a/drivers/ram/Kconfig
+++ b/drivers/ram/Kconfig
@@ -135,3 +135,4 @@
 source "drivers/ram/stm32mp1/Kconfig"
 source "drivers/ram/starfive/Kconfig"
 source "drivers/ram/sunxi/Kconfig"
+source "drivers/ram/thead/Kconfig"
diff --git a/drivers/ram/Makefile b/drivers/ram/Makefile
index f92e86e..82afd5f 100644
--- a/drivers/ram/Makefile
+++ b/drivers/ram/Makefile
@@ -30,3 +30,7 @@
 
 obj-$(CONFIG_ARCH_RENESAS) += renesas/
 obj-$(CONFIG_CADENCE_DDR_CTRL) += cadence/
+
+ifdef CONFIG_XPL_BUILD
+obj-$(CONFIG_SPL_THEAD_TH1520_DDR) += thead/
+endif
diff --git a/drivers/ram/thead/Kconfig b/drivers/ram/thead/Kconfig
new file mode 100644
index 0000000..7b05abb
--- /dev/null
+++ b/drivers/ram/thead/Kconfig
@@ -0,0 +1,5 @@
+config SPL_THEAD_TH1520_DDR
+	bool "T-Head TH1520 DDR driver in SPL"
+	depends on SPL_RAM && THEAD_TH1520
+	help
+	  This enables DDR support for T-Head TH1520 platforms.
diff --git a/drivers/ram/thead/Makefile b/drivers/ram/thead/Makefile
new file mode 100644
index 0000000..ad4d053
--- /dev/null
+++ b/drivers/ram/thead/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_SPL_THEAD_TH1520_DDR) += th1520_ddr.o
diff --git a/drivers/ram/thead/th1520_ddr.c b/drivers/ram/thead/th1520_ddr.c
new file mode 100644
index 0000000..bb4736b
--- /dev/null
+++ b/drivers/ram/thead/th1520_ddr.c
@@ -0,0 +1,787 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2017-2024 Alibaba Group Holding Limited
+ * Copyright (C) 2025 Yao Zi <ziyao@disroot.org>
+ */
+
+#include <binman.h>
+#include <binman_sym.h>
+#include <dm.h>
+#include <init.h>
+#include <linux/bitfield.h>
+#include <linux/iopoll.h>
+#include <ram.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#pragma pack(push, 1)
+
+struct th1520_ddr_fw {
+	u64 magic;
+	u8 type, ranknum, bitwidth, freq;
+	u8 reserved[8];
+
+	u32 cfgnum;
+	union th1520_ddr_cfg {
+		u32 opaddr;
+
+		struct th1520_ddr_phy {
+			u32 opaddr;
+			u16 data;
+		} phy;
+
+		struct th1520_ddr_range {
+			u32 opaddr;
+			u32 num;
+			u16 data[];
+		} range;
+	} cfgs[];
+};
+
+#pragma pack(pop)
+
+/* Firmware constants */
+#define TH1520_DDR_MAGIC	0x4452444445415448
+
+#define TH1520_DDR_TYPE_LPDDR4	0
+#define TH1520_DDR_TYPE_LPDDR4X	1
+
+#define TH1520_DDR_FREQ_2133	0
+#define TH1520_DDR_FREQ_3200	1
+#define TH1520_DDR_FREQ_3733	2
+#define TH1520_DDR_FREQ_4266	3
+
+#define TH1520_DDR_CFG_OP	GENMASK(31, 24)
+#define TH1520_DDR_CFG_ADDR	GENMASK(23, 0)
+
+#define TH1520_DDR_CFG_PHY0	0
+#define TH1520_DDR_CFG_PHY1	1
+#define TH1520_DDR_CFG_PHY	2
+#define TH1520_DDR_CFG_RANGE	3
+#define TH1520_DDR_CFG_WAITFW0	4
+#define TH1520_DDR_CFG_WAITFW1	5
+
+/* Driver constants */
+#define TH1520_SYS_PLL_TIMEOUT_US	30
+#define TH1520_CTRL_INIT_TIMEOUT_US	1000000
+#define TH1520_PHY_MSG_TIMEOUT_US	1000000
+
+/* System configuration registers */
+#define TH1520_SYS_DDR_CFG0			0x00
+#define  TH1520_SYS_DDR_CFG0_APB_RSTN		BIT(4)
+#define  TH1520_SYS_DDR_CFG0_CTRL_RSTN		BIT(5)
+#define  TH1520_SYS_DDR_CFG0_PHY_PWROK_RSTN	BIT(6)
+#define  TH1520_SYS_DDR_CFG0_PHY_CORE_RSTN	BIT(7)
+#define  TH1520_SYS_DDR_CFG0_APB_PORT_RSTN(n)	BIT(n + 4 + 4)
+#define TH1520_SYS_DDR_CFG1			0x04
+#define TH1520_SYS_PLL_CFG0			0x08
+#define  TH1520_SYS_PLL_CFG0_POSTDIV2		GENMASK(26, 24)
+#define  TH1520_SYS_PLL_CFG0_POSTDIV1		GENMASK(22, 20)
+#define  TH1520_SYS_PLL_CFG0_FBDIV		GENMASK(19, 8)
+#define  TH1520_SYS_PLL_CFG0_REFDIV		GENMASK(5, 0)
+#define TH1520_SYS_PLL_CFG1			0x0c
+#define  TH1520_SYS_PLL_CFG1_RST		BIT(30)
+#define  TH1520_SYS_PLL_CFG1_FOUTPOSTDIVPD	BIT(27)
+#define  TH1520_SYS_PLL_CFG1_FOUT4PHASEPD	BIT(25)
+#define  Th1520_SYS_PLL_CFG1_DACPD		BIT(24)
+#define TH1520_SYS_PLL_CFG2		0x10
+#define TH1520_SYS_PLL_CFG3		0x14
+#define TH1520_SYS_PLL_STS		0x18
+#define  TH1520_SYS_PLL_STS_EN		BIT(16)
+#define  TH1520_SYS_PLL_STS_LOCKED	BIT(0)
+
+/* DDR Controller Registers */
+#define TH1520_CTRL_MSTR			0x0000
+#define TH1520_CTRL_STAT			0x0004
+#define TH1520_CTRL_MRCTRL0			0x0010
+#define TH1520_CTRL_MRCTRL1			0x0014
+#define TH1520_CTRL_MRSTAT			0x0018
+#define TH1520_CTRL_DERATEEN			0x0020
+#define TH1520_CTRL_DERATEINT			0x0024
+#define TH1520_CTRL_DERATECTL			0x002c
+#define TH1520_CTRL_PWRCTL			0x0030
+#define TH1520_CTRL_PWRTMG			0x0034
+#define TH1520_CTRL_HWLPCTL			0x0038
+#define TH1520_CTRL_RFSHCTL0			0x0050
+#define TH1520_CTRL_RFSHCTL1			0x0054
+#define TH1520_CTRL_RFSHCTL3			0x0060
+#define TH1520_CTRL_RFSHTMG			0x0064
+#define TH1520_CTRL_RFSHTMG1			0x0068
+#define TH1520_CTRL_CRCPARCTL0			0x00c0
+#define TH1520_CTRL_CRCPARSTAT			0x00cc
+#define TH1520_CTRL_INIT0			0x00d0
+#define TH1520_CTRL_INIT1			0x00d4
+#define TH1520_CTRL_INIT2			0x00d8
+#define TH1520_CTRL_INIT3			0x00dc
+#define TH1520_CTRL_INIT4			0x00e0
+#define TH1520_CTRL_INIT5			0x00e4
+#define TH1520_CTRL_INIT6			0x00e8
+#define TH1520_CTRL_INIT7			0x00ec
+#define TH1520_CTRL_DIMMCTL			0x00f0
+#define TH1520_CTRL_RANKCTL			0x00f4
+#define TH1520_CTRL_RANKCTL1			0x00f8
+#define TH1520_CTRL_DRAMTMG0			0x0100
+#define TH1520_CTRL_DRAMTMG1			0x0104
+#define TH1520_CTRL_DRAMTMG2			0x0108
+#define TH1520_CTRL_DRAMTMG3			0x010c
+#define TH1520_CTRL_DRAMTMG4			0x0110
+#define TH1520_CTRL_DRAMTMG5			0x0114
+#define TH1520_CTRL_DRAMTMG6			0x0118
+#define TH1520_CTRL_DRAMTMG7			0x011c
+#define TH1520_CTRL_DRAMTMG8			0x0120
+#define TH1520_CTRL_DRAMTMG12			0x0130
+#define TH1520_CTRL_DRAMTMG13			0x0134
+#define TH1520_CTRL_DRAMTMG14			0x0138
+#define TH1520_CTRL_DRAMTMG17			0x0144
+#define TH1520_CTRL_ZQCTL0			0x0180
+#define TH1520_CTRL_ZQCTL1			0x0184
+#define TH1520_CTRL_ZQCTL2			0x0188
+#define TH1520_CTRL_ZQSTAT			0x018c
+#define TH1520_CTRL_DFITMG0			0x0190
+#define TH1520_CTRL_DFITMG1			0x0194
+#define TH1520_CTRL_DFILPCFG0			0x0198
+#define TH1520_CTRL_DFIUPD0			0x01a0
+#define TH1520_CTRL_DFIUPD1			0x01a4
+#define TH1520_CTRL_DFIUPD2			0x01a8
+#define TH1520_CTRL_DFIMISC			0x01b0
+#define TH1520_CTRL_DFITMG2			0x01b4
+#define TH1520_CTRL_DFISTAT			0x01bc
+#define TH1520_CTRL_DBICTL			0x01c0
+#define TH1520_CTRL_DFIPHYMSTR			0x01c4
+#define TH1520_CTRL_ADDRMAP0			0x0200
+#define TH1520_CTRL_ADDRMAP1			0x0204
+#define TH1520_CTRL_ADDRMAP2			0x0208
+#define TH1520_CTRL_ADDRMAP3			0x020c
+#define TH1520_CTRL_ADDRMAP4			0x0210
+#define TH1520_CTRL_ADDRMAP5			0x0214
+#define TH1520_CTRL_ADDRMAP6			0x0218
+#define TH1520_CTRL_ADDRMAP7			0x021c
+#define TH1520_CTRL_ADDRMAP8			0x0220
+#define TH1520_CTRL_ADDRMAP9			0x0224
+#define TH1520_CTRL_ADDRMAP10			0x0228
+#define TH1520_CTRL_ADDRMAP11			0x022c
+#define TH1520_CTRL_ODTCFG			0x0240
+#define TH1520_CTRL_ODTMAP			0x0244
+#define TH1520_CTRL_SCHED			0x0250
+#define TH1520_CTRL_SCHED1			0x0254
+#define TH1520_CTRL_PERFHPR1			0x025c
+#define TH1520_CTRL_PERFLPR1			0x0264
+#define TH1520_CTRL_PERFWR1			0x026c
+#define TH1520_CTRL_SCHED3			0x0270
+#define TH1520_CTRL_SCHED4			0x0274
+#define TH1520_CTRL_DBG0			0x0300
+#define TH1520_CTRL_DBG1			0x0304
+#define TH1520_CTRL_DBGCAM			0x0308
+#define TH1520_CTRL_DBGCMD			0x030c
+#define TH1520_CTRL_DBGSTAT			0x0310
+#define TH1520_CTRL_SWCTL			0x0320
+#define TH1520_CTRL_SWSTAT			0x0324
+#define TH1520_CTRL_SWCTLSTATIC			0x0328
+#define TH1520_CTRL_POISONCFG			0x036c
+#define TH1520_CTRL_POISONSTAT			0x0370
+#define TH1520_CTRL_DERATESTAT			0x03f0
+#define TH1520_CTRL_PSTAT			0x03fc
+#define TH1520_CTRL_PCCFG			0x0400
+#define TH1520_CTRL_PCFGR_0			0x0404
+#define TH1520_CTRL_PCFGW_0			0x0408
+#define TH1520_CTRL_PCTRL_0			0x0490
+#define TH1520_CTRL_PCFGQOS0_0			0x0494
+#define TH1520_CTRL_PCFGQOS1_0			0x0498
+#define TH1520_CTRL_PCFGWQOS0_0			0x049c
+#define TH1520_CTRL_PCFGWQOS1_0			0x04a0
+#define TH1520_CTRL_PCFGR_1			0x04b4
+#define TH1520_CTRL_PCFGW_1			0x04b8
+#define TH1520_CTRL_PCTRL_1			0x0540
+#define TH1520_CTRL_PCFGQOS0_1			0x0544
+#define TH1520_CTRL_PCFGQOS1_1			0x0548
+#define TH1520_CTRL_PCFGWQOS0_1			0x054c
+#define TH1520_CTRL_PCFGWQOS1_1			0x0550
+#define TH1520_CTRL_PCFGR_2			0x0564
+#define TH1520_CTRL_PCFGW_2			0x0568
+#define TH1520_CTRL_PCTRL_2			0x05f0
+#define TH1520_CTRL_PCFGQOS0_2			0x05f4
+#define TH1520_CTRL_PCFGQOS1_2			0x05f8
+#define TH1520_CTRL_PCFGWQOS0_2			0x05fc
+#define TH1520_CTRL_PCFGWQOS1_2			0x0600
+#define TH1520_CTRL_PCFGR_3			0x0614
+#define TH1520_CTRL_PCFGW_3			0x0618
+#define TH1520_CTRL_PCTRL_3			0x06a0
+#define TH1520_CTRL_PCFGQOS0_3			0x06a4
+#define TH1520_CTRL_PCFGQOS1_3			0x06a8
+#define TH1520_CTRL_PCFGWQOS0_3			0x06ac
+#define TH1520_CTRL_PCFGWQOS1_3			0x06b0
+#define TH1520_CTRL_PCFGR_4			0x06c4
+#define TH1520_CTRL_PCFGW_4			0x06c8
+#define TH1520_CTRL_PCTRL_4			0x0750
+#define TH1520_CTRL_PCFGQOS0_4			0x0754
+#define TH1520_CTRL_PCFGQOS1_4			0x0758
+#define TH1520_CTRL_PCFGWQOS0_4			0x075c
+#define TH1520_CTRL_PCFGWQOS1_4			0x0760
+#define TH1520_CTRL_UMCTL2_VER_NUMBER		0x0ff0
+#define TH1520_CTRL_UMCTL2_VER_TYPE		0x0ff4
+#define TH1520_CTRL_DCH1_STAT			0x1b04
+#define TH1520_CTRL_DCH1_MRCTRL0		0x1b10
+#define TH1520_CTRL_DCH1_MRCTRL1		0x1b14
+#define TH1520_CTRL_DCH1_MRSTAT			0x1b18
+#define TH1520_CTRL_DCH1_DERATECTL		0x1b2c
+#define TH1520_CTRL_DCH1_PWRCTL			0x1b30
+#define TH1520_CTRL_DCH1_HWLPCTL		0x1b38
+#define TH1520_CTRL_DCH1_CRCPARCTL0		0x1bc0
+#define TH1520_CTRL_DCH1_ZQCTL2			0x1c88
+#define TH1520_CTRL_DCH1_DFISTAT		0x1cbc
+#define TH1520_CTRL_DCH1_ODTMAP			0x1d44
+#define TH1520_CTRL_DCH1_DBG1			0x1e04
+#define TH1520_CTRL_DCH1_DBGCMD			0x1e0c
+#define TH1520_CTRL_DCH1_DBGCAM			0x1e08
+
+/* PHY configuration registers */
+#define TH1520_DDR_PHY_REG(regid)	((regid) * 2)
+
+/* UctShadowRegs */
+#define TH1520_PHY_MSG_STATUS		TH1520_DDR_PHY_REG(0xd0004)
+#define  TH1520_PHY_MSG_STATUS_EMPTY	BIT(0)
+/* DctWriteProt */
+#define TH1520_PHY_MSG_ACK		TH1520_DDR_PHY_REG(0xd0031)
+#define  TH1520_PHY_MSG_ACK_EN		BIT(0)
+/* UctWriteOnlyShadow */
+#define TH1520_PHY_MSG_ID		TH1520_DDR_PHY_REG(0xd0032)
+#define  TH1520_PHY_MSG_ID_COMPLETION	0x7
+#define  TH1520_PHY_MSG_ID_ERROR	0xff
+/* UctDatWriteOnlyShadow */
+#define TH1520_PHY_MSG_DATA		TH1520_DDR_PHY_REG(0xd0034)
+
+struct th1520_ddr_priv {
+	void __iomem *phy0;
+	void __iomem *phy1;
+	void __iomem *ctrl;
+	void __iomem *sys;
+};
+
+binman_sym_declare(ulong, ddr_fw, image_pos);
+
+static int th1520_ddr_pll_config(void __iomem *sysreg, unsigned int frequency)
+{
+	u32 tmp;
+	int ret;
+
+	tmp = TH1520_SYS_PLL_CFG1_RST			|
+	      TH1520_SYS_PLL_CFG1_FOUTPOSTDIVPD		|
+	      TH1520_SYS_PLL_CFG1_FOUT4PHASEPD		|
+	      Th1520_SYS_PLL_CFG1_DACPD;
+	writel(tmp, sysreg + TH1520_SYS_PLL_CFG1);
+
+	switch (frequency) {
+	case TH1520_DDR_FREQ_3733:
+		writel(FIELD_PREP(TH1520_SYS_PLL_CFG0_REFDIV, 1)	|
+		       FIELD_PREP(TH1520_SYS_PLL_CFG0_FBDIV, 77)	|
+		       FIELD_PREP(TH1520_SYS_PLL_CFG0_POSTDIV1, 2)	|
+		       FIELD_PREP(TH1520_SYS_PLL_CFG0_POSTDIV2, 1),
+		       sysreg + TH1520_SYS_PLL_CFG0);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	udelay(2);
+	tmp &= ~TH1520_SYS_PLL_CFG1_RST;
+	writel(tmp, sysreg + TH1520_SYS_PLL_CFG1);
+
+	ret = readl_poll_timeout(sysreg + TH1520_SYS_PLL_STS, tmp,
+				 tmp & TH1520_SYS_PLL_STS_LOCKED,
+				 TH1520_SYS_PLL_TIMEOUT_US);
+
+	writel(TH1520_SYS_PLL_STS_EN, sysreg + TH1520_SYS_PLL_STS);
+
+	return ret;
+}
+
+static int th1520_ddr_ctrl_init(void __iomem *ctrlreg, struct th1520_ddr_fw *fw)
+{
+	int ret;
+	u32 tmp;
+
+	writel(0x00000001, ctrlreg + TH1520_CTRL_DBG1);
+	writel(0x00000001, ctrlreg + TH1520_CTRL_PWRCTL);
+
+	ret = readl_poll_timeout(ctrlreg + TH1520_CTRL_STAT, tmp,
+				 tmp == 0x00000000,
+				 TH1520_CTRL_INIT_TIMEOUT_US);
+	if (ret)
+		return ret;
+
+	if (fw->ranknum == 2)
+		writel(0x03080020, ctrlreg + TH1520_CTRL_MSTR);
+	else
+		return -EINVAL;
+
+	writel(0x00003030, ctrlreg + TH1520_CTRL_MRCTRL0);
+	writel(0x0002d90f, ctrlreg + TH1520_CTRL_MRCTRL1);
+
+	switch (fw->freq) {
+	case TH1520_DDR_FREQ_3733:
+		writel(0x000013f3, ctrlreg + TH1520_CTRL_DERATEEN);
+		writel(0x40000000, ctrlreg + TH1520_CTRL_DERATEINT);
+		writel(0x00000001, ctrlreg + TH1520_CTRL_DERATECTL);
+		writel(0x00000020, ctrlreg + TH1520_CTRL_PWRCTL);
+		writel(0x0040ae04, ctrlreg + TH1520_CTRL_PWRTMG);
+		writel(0x00430000, ctrlreg + TH1520_CTRL_HWLPCTL);
+		writel(0x00210004, ctrlreg + TH1520_CTRL_RFSHCTL0);
+		writel(0x000d0021, ctrlreg + TH1520_CTRL_RFSHCTL1);
+		writel(0x00000001, ctrlreg + TH1520_CTRL_RFSHCTL3);
+		writel(0x81c00084, ctrlreg + TH1520_CTRL_RFSHTMG);
+		writel(0x00540000, ctrlreg + TH1520_CTRL_RFSHTMG1);
+		writel(0x00000000, ctrlreg + TH1520_CTRL_CRCPARCTL0);
+		writel(0xc0020002, ctrlreg + TH1520_CTRL_INIT0);
+		writel(0x00010002, ctrlreg + TH1520_CTRL_INIT1);
+		writel(0x00001f00, ctrlreg + TH1520_CTRL_INIT2);
+		writel(0x00640036, ctrlreg + TH1520_CTRL_INIT3);
+		writel(0x00f20008, ctrlreg + TH1520_CTRL_INIT4);
+		writel(0x0004000b, ctrlreg + TH1520_CTRL_INIT5);
+		writel(0x00440012, ctrlreg + TH1520_CTRL_INIT6);
+		writel(0x0004001a, ctrlreg + TH1520_CTRL_INIT7);
+		writel(0x00000000, ctrlreg + TH1520_CTRL_DIMMCTL);
+		writel(0x0000ab9f, ctrlreg + TH1520_CTRL_RANKCTL);
+		writel(0x00000017, ctrlreg + TH1520_CTRL_RANKCTL1);
+		writel(0x1f263f28, ctrlreg + TH1520_CTRL_DRAMTMG0);
+		writel(0x00080839, ctrlreg + TH1520_CTRL_DRAMTMG1);
+		writel(0x08121d17, ctrlreg + TH1520_CTRL_DRAMTMG2);
+		writel(0x00d0e000, ctrlreg + TH1520_CTRL_DRAMTMG3);
+		writel(0x11040a12, ctrlreg + TH1520_CTRL_DRAMTMG4);
+		writel(0x02050e0e, ctrlreg + TH1520_CTRL_DRAMTMG5);
+		writel(0x01010008, ctrlreg + TH1520_CTRL_DRAMTMG6);
+		writel(0x00000502, ctrlreg + TH1520_CTRL_DRAMTMG7);
+		writel(0x00000101, ctrlreg + TH1520_CTRL_DRAMTMG8);
+		writel(0x00020000, ctrlreg + TH1520_CTRL_DRAMTMG12);
+		writel(0x0d100002, ctrlreg + TH1520_CTRL_DRAMTMG13);
+		writel(0x0000010c, ctrlreg + TH1520_CTRL_DRAMTMG14);
+		writel(0x03a50021, ctrlreg + TH1520_CTRL_ZQCTL0);
+		writel(0x02f00800, ctrlreg + TH1520_CTRL_ZQCTL1);
+		writel(0x00000000, ctrlreg + TH1520_CTRL_ZQCTL2);
+		writel(0x059f820c, ctrlreg + TH1520_CTRL_DFITMG0);
+		writel(0x000c0303, ctrlreg + TH1520_CTRL_DFITMG1);
+		writel(0x0351a101, ctrlreg + TH1520_CTRL_DFILPCFG0);
+		writel(0x00000011, ctrlreg + TH1520_CTRL_DFIMISC);
+		writel(0x00001f0c, ctrlreg + TH1520_CTRL_DFITMG2);
+		writel(0x00000007, ctrlreg + TH1520_CTRL_DBICTL);
+		writel(0x14000001, ctrlreg + TH1520_CTRL_DFIPHYMSTR);
+		writel(0x06090b40, ctrlreg + TH1520_CTRL_ODTCFG);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	writel(0x00400018, ctrlreg + TH1520_CTRL_DFIUPD0);
+	writel(0x00280032, ctrlreg + TH1520_CTRL_DFIUPD1);
+	writel(0x00000000, ctrlreg + TH1520_CTRL_DFIUPD2);
+	writel(0x00000000, ctrlreg + TH1520_CTRL_ODTMAP);
+	writel(0x1f829b1c, ctrlreg + TH1520_CTRL_SCHED);
+	writel(0x4400b00f, ctrlreg + TH1520_CTRL_SCHED1);
+	writel(0x0f000001, ctrlreg + TH1520_CTRL_PERFHPR1);
+	writel(0x0f00007f, ctrlreg + TH1520_CTRL_PERFLPR1);
+	writel(0x0f00007f, ctrlreg + TH1520_CTRL_PERFWR1);
+	writel(0x00000208, ctrlreg + TH1520_CTRL_SCHED3);
+	writel(0x08400810, ctrlreg + TH1520_CTRL_SCHED4);
+	writel(0x00000000, ctrlreg + TH1520_CTRL_DBG0);
+	writel(0x00000000, ctrlreg + TH1520_CTRL_DBG1);
+	writel(0x00000000, ctrlreg + TH1520_CTRL_DBGCMD);
+	writel(0x00000001, ctrlreg + TH1520_CTRL_SWCTL);
+	writel(0x00000000, ctrlreg + TH1520_CTRL_SWCTLSTATIC);
+	writel(0x00000001, ctrlreg + TH1520_CTRL_POISONCFG);
+	writel(0x00000001, ctrlreg + TH1520_CTRL_PCTRL_0);
+	writel(0x00000001, ctrlreg + TH1520_CTRL_PCTRL_1);
+	writel(0x00000001, ctrlreg + TH1520_CTRL_PCTRL_2);
+	writel(0x00000001, ctrlreg + TH1520_CTRL_PCTRL_3);
+	writel(0x00000001, ctrlreg + TH1520_CTRL_PCTRL_4);
+	writel(0x00003030, ctrlreg + TH1520_CTRL_DCH1_MRCTRL0);
+	writel(0x0002d90f, ctrlreg + TH1520_CTRL_DCH1_MRCTRL1);
+	writel(0x00000001, ctrlreg + TH1520_CTRL_DCH1_DERATECTL);
+	writel(0x00000020, ctrlreg + TH1520_CTRL_DCH1_PWRCTL);
+	writel(0x00430002, ctrlreg + TH1520_CTRL_DCH1_HWLPCTL);
+	writel(0x00000000, ctrlreg + TH1520_CTRL_DCH1_CRCPARCTL0);
+	writel(0x00000000, ctrlreg + TH1520_CTRL_DCH1_ZQCTL2);
+	writel(0x00000000, ctrlreg + TH1520_CTRL_DCH1_ODTMAP);
+	writel(0x00000000, ctrlreg + TH1520_CTRL_DCH1_DBG1);
+	writel(0x00000000, ctrlreg + TH1520_CTRL_DCH1_DBGCMD);
+
+	ret = readl_poll_timeout(ctrlreg + TH1520_CTRL_RFSHCTL3, tmp,
+				 tmp == 0x00000001,
+				 TH1520_CTRL_INIT_TIMEOUT_US);
+	if (ret)
+		return ret;
+
+	writel(0x00000010, ctrlreg + TH1520_CTRL_PCCFG);
+	writel(0x0000500f, ctrlreg + TH1520_CTRL_PCFGR_0);
+	writel(0x0000500f, ctrlreg + TH1520_CTRL_PCFGW_0);
+	writel(0x00005020, ctrlreg + TH1520_CTRL_PCFGR_1);
+	writel(0x0000501f, ctrlreg + TH1520_CTRL_PCFGW_1);
+	writel(0x0000501f, ctrlreg + TH1520_CTRL_PCFGR_2);
+	writel(0x0000503f, ctrlreg + TH1520_CTRL_PCFGW_2);
+	writel(0x000051ff, ctrlreg + TH1520_CTRL_PCFGR_3);
+	writel(0x000051ff, ctrlreg + TH1520_CTRL_PCFGW_3);
+	writel(0x0000503f, ctrlreg + TH1520_CTRL_PCFGR_4);
+	writel(0x0000503f, ctrlreg + TH1520_CTRL_PCFGW_4);
+
+	ret = readl_poll_timeout(ctrlreg + TH1520_CTRL_PWRCTL, tmp,
+				 tmp == 0x00000020,
+				 TH1520_CTRL_INIT_TIMEOUT_US);
+	if (ret)
+		return ret;
+
+	writel(0x00000020, ctrlreg + TH1520_CTRL_PWRCTL);
+
+	ret = readl_poll_timeout(ctrlreg + TH1520_CTRL_DCH1_PWRCTL, tmp,
+				 tmp == 0x00000020,
+				 TH1520_CTRL_INIT_TIMEOUT_US);
+	if (ret)
+		return ret;
+
+	writel(0x00000020, ctrlreg + TH1520_CTRL_DCH1_PWRCTL);
+	writel(0x00000000, ctrlreg + TH1520_CTRL_DBG1);
+
+	ret = readl_poll_timeout(ctrlreg + TH1520_CTRL_PWRCTL, tmp,
+				 tmp == 0x00000020,
+				 TH1520_CTRL_INIT_TIMEOUT_US);
+	if (ret)
+		return ret;
+
+	writel(0x00000020, ctrlreg + TH1520_CTRL_PWRCTL);
+
+	ret = readl_poll_timeout(ctrlreg + TH1520_CTRL_PWRCTL, tmp,
+				 tmp == 0x00000020,
+				 TH1520_CTRL_INIT_TIMEOUT_US);
+	if (ret)
+		return ret;
+
+	writel(0x00000020, ctrlreg + TH1520_CTRL_PWRCTL);
+	writel(0x00000000, ctrlreg + TH1520_CTRL_DCH1_DBG1);
+
+	ret = readl_poll_timeout(ctrlreg + TH1520_CTRL_DCH1_PWRCTL, tmp,
+				 tmp == 0x00000020,
+				 TH1520_CTRL_INIT_TIMEOUT_US);
+	if (ret)
+		return ret;
+
+	writel(0x00000020, ctrlreg + TH1520_CTRL_DCH1_PWRCTL);
+
+	ret = readl_poll_timeout(ctrlreg + TH1520_CTRL_DCH1_PWRCTL, tmp,
+				 tmp == 0x00000020,
+				 TH1520_CTRL_INIT_TIMEOUT_US);
+	if (ret)
+		return ret;
+
+	writel(0x00000020, ctrlreg + TH1520_CTRL_DCH1_PWRCTL);
+	writel(0x14000001, ctrlreg + TH1520_CTRL_DFIPHYMSTR);
+	writel(0x00000000, ctrlreg + TH1520_CTRL_SWCTL);
+	writel(0x00000010, ctrlreg + TH1520_CTRL_DFIMISC);
+	writel(0x00000010, ctrlreg + TH1520_CTRL_DFIMISC);
+	writel(0x00000002, ctrlreg + TH1520_CTRL_DBG1);
+	writel(0x00000002, ctrlreg + TH1520_CTRL_DCH1_DBG1);
+
+	switch (fw->bitwidth) {
+	case 64:
+		writel(0x00040018, ctrlreg + TH1520_CTRL_ADDRMAP0);
+		writel(0x00090909, ctrlreg + TH1520_CTRL_ADDRMAP1);
+		writel(0x00000000, ctrlreg + TH1520_CTRL_ADDRMAP2);
+		writel(0x01010101, ctrlreg + TH1520_CTRL_ADDRMAP3);
+		writel(0x00001f1f, ctrlreg + TH1520_CTRL_ADDRMAP4);
+		writel(0x080f0808, ctrlreg + TH1520_CTRL_ADDRMAP5);
+		writel(0x08080808, ctrlreg + TH1520_CTRL_ADDRMAP6);
+		writel(0x00000f0f, ctrlreg + TH1520_CTRL_ADDRMAP7);
+		writel(0x08080808, ctrlreg + TH1520_CTRL_ADDRMAP9);
+		writel(0x08080808, ctrlreg + TH1520_CTRL_ADDRMAP10);
+		writel(0x00000008, ctrlreg + TH1520_CTRL_ADDRMAP11);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int th1520_ddr_read_msg(void __iomem *phyreg, u16 *id, u16 *data)
+{
+	u32 tmp;
+	int ret;
+
+	ret = readw_poll_timeout(phyreg + TH1520_PHY_MSG_STATUS, tmp,
+				 !(tmp & TH1520_PHY_MSG_STATUS_EMPTY),
+				 TH1520_PHY_MSG_TIMEOUT_US);
+	if (ret)
+		return ret;
+
+	*id   = readw(phyreg + TH1520_PHY_MSG_ID);
+	*data = readw(phyreg + TH1520_PHY_MSG_DATA);
+
+	writew(0, phyreg + TH1520_PHY_MSG_ACK);
+
+	ret = readw_poll_timeout(phyreg + TH1520_PHY_MSG_STATUS, tmp,
+				 tmp & TH1520_PHY_MSG_STATUS_EMPTY,
+				 TH1520_PHY_MSG_TIMEOUT_US);
+	if (ret)
+		return ret;
+
+	writew(TH1520_PHY_MSG_ACK_EN, phyreg + TH1520_PHY_MSG_ACK);
+
+	return 0;
+}
+
+static int th1520_phy_wait_pmu_completion(void __iomem *phyreg)
+{
+	u16 id, data;
+	int ret;
+
+	do {
+		ret = th1520_ddr_read_msg(phyreg, &id, &data);
+
+		if (ret)
+			return ret;
+	} while (id != TH1520_PHY_MSG_ID_COMPLETION	&&
+		 id != TH1520_PHY_MSG_ID_ERROR		&&
+		 !ret);
+
+	return id == TH1520_PHY_MSG_ID_COMPLETION ? ret : -EIO;
+}
+
+static int lpddr4_load_firmware(struct th1520_ddr_priv *priv,
+				struct th1520_ddr_fw *fw)
+{
+	union th1520_ddr_cfg *cfg;
+	size_t i, j;
+	int ret;
+
+	for (cfg = fw->cfgs, i = 0; i < fw->cfgnum; i++) {
+		u32 addr = FIELD_GET(TH1520_DDR_CFG_ADDR, cfg->opaddr) * 2;
+		u32 op = FIELD_GET(TH1520_DDR_CFG_OP, cfg->opaddr);
+
+		switch (op) {
+		case TH1520_DDR_CFG_PHY0:
+			writew(cfg->phy.data, priv->phy0 + addr);
+			break;
+		case TH1520_DDR_CFG_PHY1:
+			writew(cfg->phy.data, priv->phy1 + addr);
+			break;
+		case TH1520_DDR_CFG_PHY:
+			writew(cfg->phy.data, priv->phy0 + addr);
+			writew(cfg->phy.data, priv->phy1 + addr);
+			break;
+		case TH1520_DDR_CFG_RANGE:
+			for (j = 0; j < cfg->range.num; j++) {
+				writew(cfg->range.data[j],
+				       priv->phy0 + addr + j * 2);
+				writew(cfg->range.data[j],
+				       priv->phy1 + addr + j * 2);
+			}
+			break;
+		case TH1520_DDR_CFG_WAITFW0:
+			ret = th1520_phy_wait_pmu_completion(priv->phy0);
+
+			if (ret) {
+				pr_err("phy 0 training failed: %d\n", ret);
+				return ret;
+			}
+
+			break;
+		case TH1520_DDR_CFG_WAITFW1:
+			ret = th1520_phy_wait_pmu_completion(priv->phy1);
+
+			if (ret) {
+				pr_err("phy 1 training failed: %d\n", ret);
+				return ret;
+			}
+
+			break;
+		default:
+			pr_err("Unknown DRAM configuration %d\n", op);
+
+			return -EOPNOTSUPP;
+		}
+
+		if (op == TH1520_DDR_CFG_RANGE)
+			cfg = (void *)cfg + sizeof(cfg->range) +
+				      cfg->range.num * sizeof(u16);
+		else
+			cfg = (union th1520_ddr_cfg *)(&cfg->phy + 1);
+	}
+
+	return 0;
+}
+
+static int th1520_ddr_ctrl_enable(void __iomem *ctrlreg,
+				  struct th1520_ddr_fw *fw)
+{
+	u32 tmp;
+	int ret;
+
+	writel(0x00000030, ctrlreg + TH1520_CTRL_DFIMISC);
+
+	ret = readl_poll_timeout(ctrlreg + TH1520_CTRL_DFISTAT, tmp,
+				 tmp == 0x00000001,
+				 TH1520_CTRL_INIT_TIMEOUT_US);
+	if (ret)
+		return ret;
+
+	ret = readl_poll_timeout(ctrlreg + TH1520_CTRL_DCH1_DFISTAT, tmp,
+				 tmp == 0x00000001,
+				 TH1520_CTRL_INIT_TIMEOUT_US);
+	if (ret)
+		return ret;
+
+	writel(0x00000010, ctrlreg + TH1520_CTRL_DFIMISC);
+	writel(0x00000011, ctrlreg + TH1520_CTRL_DFIMISC);
+	writel(0x0000000a, ctrlreg + TH1520_CTRL_PWRCTL);
+	writel(0x0000000a, ctrlreg + TH1520_CTRL_DCH1_PWRCTL);
+	writel(0x00000001, ctrlreg + TH1520_CTRL_SWCTL);
+
+	ret = readl_poll_timeout(ctrlreg + TH1520_CTRL_SWSTAT, tmp,
+				 tmp == 0x00000001,
+				 TH1520_CTRL_INIT_TIMEOUT_US);
+	if (ret)
+		return ret;
+
+	ret = readl_poll_timeout(ctrlreg + TH1520_CTRL_STAT, tmp,
+				 tmp == 0x00000001,
+				 TH1520_CTRL_INIT_TIMEOUT_US);
+	if (ret)
+		return ret;
+
+	ret = readl_poll_timeout(ctrlreg + TH1520_CTRL_DCH1_STAT, tmp,
+				 tmp == 0x00000001,
+				 TH1520_CTRL_INIT_TIMEOUT_US);
+	if (ret)
+		return ret;
+
+	writel(0x14000001, ctrlreg + TH1520_CTRL_DFIPHYMSTR);
+	writel(0x00000000, ctrlreg + TH1520_CTRL_SWCTL);
+	writel(0x00020002, ctrlreg + TH1520_CTRL_INIT0);
+	writel(0x00000001, ctrlreg + TH1520_CTRL_SWCTL);
+
+	ret = readl_poll_timeout(ctrlreg + TH1520_CTRL_SWSTAT, tmp,
+				 tmp == 0x00000001,
+				 TH1520_CTRL_INIT_TIMEOUT_US);
+
+	if (ret)
+		return ret;
+
+	writel(0x00000000, ctrlreg + TH1520_CTRL_DBG1);
+	writel(0x00000000, ctrlreg + TH1520_CTRL_DCH1_DBG1);
+
+	return 0;
+}
+
+static void th1520_ddr_enable_self_refresh(void __iomem *ctrlreg,
+					   void __iomem *sysreg)
+{
+	writel(0x00000000, ctrlreg + TH1520_CTRL_RFSHCTL3);
+
+	writel(0x000a0000, sysreg + TH1520_SYS_DDR_CFG1);
+
+	writel(0x00000000, ctrlreg + TH1520_CTRL_SWCTL);
+	writel(0x00000001, ctrlreg + TH1520_CTRL_SWCTLSTATIC);
+	writel(0x0040ae04, ctrlreg + TH1520_CTRL_PWRTMG);
+	writel(0x00430003, ctrlreg + TH1520_CTRL_HWLPCTL);
+	writel(0x00430003, ctrlreg + TH1520_CTRL_DCH1_HWLPCTL);
+	writel(0x00000001, ctrlreg + TH1520_CTRL_SWCTL);
+	writel(0x00000000, ctrlreg + TH1520_CTRL_SWCTLSTATIC);
+	writel(0x0000000b, ctrlreg + TH1520_CTRL_PWRCTL);
+	writel(0x0000000b, ctrlreg + TH1520_CTRL_DCH1_PWRCTL);
+}
+
+static int th1520_ddr_init(struct th1520_ddr_priv *priv)
+{
+	struct th1520_ddr_fw *fw = (void *)binman_sym(ulong, ddr_fw, image_pos);
+	u32 reset;
+	int ret;
+
+	ret = th1520_ddr_pll_config(priv->sys, fw->freq);
+	if (ret) {
+		pr_err("failed to configure PLL: %d\n", ret);
+		return ret;
+	}
+
+	reset = TH1520_SYS_DDR_CFG0_PHY_PWROK_RSTN;
+	writel(reset, priv->sys + TH1520_SYS_DDR_CFG0);
+	reset |= TH1520_SYS_DDR_CFG0_PHY_CORE_RSTN;
+	writel(reset, priv->sys + TH1520_SYS_DDR_CFG0);
+	reset |= TH1520_SYS_DDR_CFG0_APB_RSTN;
+	writel(reset, priv->sys + TH1520_SYS_DDR_CFG0);
+
+	ret = th1520_ddr_ctrl_init(priv->ctrl, fw);
+	if (ret) {
+		pr_err("failed to initialize DDR controller: %d\n", ret);
+		return ret;
+	}
+
+	reset |= TH1520_SYS_DDR_CFG0_APB_PORT_RSTN(0) |
+		 TH1520_SYS_DDR_CFG0_APB_PORT_RSTN(1) |
+		 TH1520_SYS_DDR_CFG0_APB_PORT_RSTN(2) |
+		 TH1520_SYS_DDR_CFG0_APB_PORT_RSTN(3) |
+		 TH1520_SYS_DDR_CFG0_APB_PORT_RSTN(4) |
+		 TH1520_SYS_DDR_CFG0_CTRL_RSTN;
+	writel(reset, priv->sys + TH1520_SYS_DDR_CFG0);
+
+	lpddr4_load_firmware(priv, fw);
+
+	ret = th1520_ddr_ctrl_enable(priv->ctrl, fw);
+	if (ret) {
+		pr_err("failed to enable DDR controller: %d\n", ret);
+		return ret;
+	}
+
+	th1520_ddr_enable_self_refresh(priv->ctrl, priv->sys);
+
+	return 0;
+}
+
+static int th1520_ddr_probe(struct udevice *dev)
+{
+	struct th1520_ddr_priv *priv = dev_get_priv(dev);
+	fdt_addr_t addr;
+
+	addr = dev_read_addr_name(dev, "phy-0");
+	priv->phy0 = (void __iomem *)addr;
+	if (addr == FDT_ADDR_T_NONE)
+		return -EINVAL;
+
+	addr = dev_read_addr_name(dev, "phy-1");
+	priv->phy1 = (void __iomem *)addr;
+	if (addr == FDT_ADDR_T_NONE)
+		return -EINVAL;
+
+	addr = dev_read_addr_name(dev, "ctrl");
+	priv->ctrl = (void __iomem *)addr;
+	if (addr == FDT_ADDR_T_NONE)
+		return -EINVAL;
+
+	addr = dev_read_addr_name(dev, "sys");
+	priv->sys = (void __iomem *)addr;
+	if (addr == FDT_ADDR_T_NONE)
+		return -EINVAL;
+
+	return th1520_ddr_init(priv);
+}
+
+static int th1520_ddr_get_info(struct udevice *dev, struct ram_info *info)
+{
+	info->base = gd->ram_base;
+	info->size = gd->ram_size;
+
+	return 0;
+}
+
+static struct ram_ops th1520_ddr_ops = {
+	.get_info = th1520_ddr_get_info,
+};
+
+static const struct udevice_id th1520_ddr_ids[] = {
+	{ .compatible = "thead,th1520-ddrc" },
+	{ }
+};
+
+U_BOOT_DRIVER(th1520_ddr) = {
+	.name = "th1520_ddr",
+	.id = UCLASS_RAM,
+	.ops = &th1520_ddr_ops,
+	.of_match = th1520_ddr_ids,
+	.probe = th1520_ddr_probe,
+	.priv_auto = sizeof(struct th1520_ddr_priv),
+};
diff --git a/drivers/spi/nxp_fspi.c b/drivers/spi/nxp_fspi.c
index 7489c89..6d97b8e 100644
--- a/drivers/spi/nxp_fspi.c
+++ b/drivers/spi/nxp_fspi.c
@@ -52,13 +52,6 @@
 #include <linux/bug.h>
 #include <linux/err.h>
 
-/*
- * The driver only uses one single LUT entry, that is updated on
- * each call of exec_op(). Index 0 is preset at boot with a basic
- * read operation, so let's use the last entry (31).
- */
-#define	SEQID_LUT			31
-
 /* Registers used by the driver */
 #define FSPI_MCR0			0x00
 #define FSPI_MCR0_AHB_TIMEOUT(x)	((x) << 24)
@@ -242,9 +235,6 @@
 #define FSPI_TFDR			0x180
 
 #define FSPI_LUT_BASE			0x200
-#define FSPI_LUT_OFFSET			(SEQID_LUT * 4 * 4)
-#define FSPI_LUT_REG(idx) \
-	(FSPI_LUT_BASE + FSPI_LUT_OFFSET + (idx) * 4)
 
 /* register map end */
 
@@ -316,6 +306,7 @@
 	unsigned int txfifo;
 	unsigned int ahb_buf_size;
 	unsigned int quirks;
+	unsigned int lut_num;
 	bool little_endian;
 };
 
@@ -324,6 +315,7 @@
 	.txfifo = SZ_1K,        /* (128 * 64 bits)  */
 	.ahb_buf_size = SZ_2K,  /* (256 * 64 bits)  */
 	.quirks = 0,
+	.lut_num = 32,
 	.little_endian = true,  /* little-endian    */
 };
 
@@ -332,9 +324,19 @@
 	.txfifo = SZ_1K,        /* (128 * 64 bits)  */
 	.ahb_buf_size = SZ_2K,  /* (256 * 64 bits)  */
 	.quirks = 0,
+	.lut_num = 32,
 	.little_endian = true,  /* little-endian    */
 };
 
+static struct nxp_fspi_devtype_data imxrt1170_data = {
+	.rxfifo = SZ_256,
+	.txfifo = SZ_256,
+	.ahb_buf_size = SZ_4K,
+	.quirks = 0,
+	.lut_num = 16,
+	.little_endian = true,
+};
+
 struct nxp_fspi {
 	struct udevice *dev;
 	void __iomem *iobase;
@@ -486,6 +488,8 @@
 	void __iomem *base = f->iobase;
 	u32 lutval[4] = {};
 	int lutidx = 1, i;
+	u32 lut_offset = (f->devtype_data->lut_num - 1) * 4 * 4;
+	u32 target_lut_reg;
 
 	/* cmd */
 	lutval[0] |= LUT_DEF(0, LUT_CMD, LUT_PAD(op->cmd.buswidth),
@@ -530,8 +534,10 @@
 	fspi_writel(f, FSPI_LCKER_UNLOCK, f->iobase + FSPI_LCKCR);
 
 	/* fill LUT */
-	for (i = 0; i < ARRAY_SIZE(lutval); i++)
-		fspi_writel(f, lutval[i], base + FSPI_LUT_REG(i));
+	for (i = 0; i < ARRAY_SIZE(lutval); i++) {
+		target_lut_reg = FSPI_LUT_BASE + lut_offset + i * 4;
+		fspi_writel(f, lutval[i], base + target_lut_reg);
+	}
 
 	dev_dbg(f->dev, "CMD[%x] lutval[0:%x \t 1:%x \t 2:%x \t 3:%x], size: 0x%08x\n",
 		op->cmd.opcode, lutval[0], lutval[1], lutval[2], lutval[3], op->data.nbytes);
@@ -731,7 +737,7 @@
 	void __iomem *base = f->iobase;
 	int seqnum = 0;
 	int err = 0;
-	u32 reg;
+	u32 reg, seqid_lut;
 
 	reg = fspi_readl(f, base + FSPI_IPRXFCR);
 	/* invalid RXFIFO first */
@@ -745,8 +751,9 @@
 	 * the LUT at each exec_op() call. And also specify the DATA
 	 * length, since it's has not been specified in the LUT.
 	 */
+	seqid_lut = f->devtype_data->lut_num - 1;
 	fspi_writel(f, op->data.nbytes |
-		 (SEQID_LUT << FSPI_IPCR1_SEQID_SHIFT) |
+		 (seqid_lut << FSPI_IPCR1_SEQID_SHIFT) |
 		 (seqnum << FSPI_IPCR1_SEQNUM_SHIFT),
 		 base + FSPI_IPCR1);
 
@@ -862,7 +869,7 @@
 {
 	void __iomem *base = f->iobase;
 	int ret, i;
-	u32 reg;
+	u32 reg, seqid_lut;
 
 #if CONFIG_IS_ENABLED(CLK)
 	/* the default frequency, we will change it later if necessary. */
@@ -933,11 +940,17 @@
 	fspi_writel(f, reg, base + FSPI_FLSHB1CR1);
 	fspi_writel(f, reg, base + FSPI_FLSHB2CR1);
 
+	/*
+	 * The driver only uses one single LUT entry, that is updated on
+	 * each call of exec_op(). Index 0 is preset at boot with a basic
+	 * read operation, so let's use the last entry.
+	 */
+	seqid_lut = f->devtype_data->lut_num - 1;
 	/* AHB Read - Set lut sequence ID for all CS. */
-	fspi_writel(f, SEQID_LUT, base + FSPI_FLSHA1CR2);
-	fspi_writel(f, SEQID_LUT, base + FSPI_FLSHA2CR2);
-	fspi_writel(f, SEQID_LUT, base + FSPI_FLSHB1CR2);
-	fspi_writel(f, SEQID_LUT, base + FSPI_FLSHB2CR2);
+	fspi_writel(f, seqid_lut, base + FSPI_FLSHA1CR2);
+	fspi_writel(f, seqid_lut, base + FSPI_FLSHA2CR2);
+	fspi_writel(f, seqid_lut, base + FSPI_FLSHB1CR2);
+	fspi_writel(f, seqid_lut, base + FSPI_FLSHB2CR2);
 
 	return 0;
 }
@@ -1035,7 +1048,8 @@
 	}
 #endif
 
-	dev_dbg(bus, "iobase=<0x%llx>, ahb_addr=<0x%llx>\n", iobase, ahb_addr);
+	dev_dbg(bus, "iobase=<0x%llx>, ahb_addr=<0x%llx>\n",
+			(long long)iobase, (long long)ahb_addr);
 
 	return 0;
 }
@@ -1057,6 +1071,7 @@
 	{ .compatible = "nxp,lx2160a-fspi", .data = (ulong)&lx2160a_data, },
 	{ .compatible = "nxp,imx8mm-fspi", .data = (ulong)&imx8mm_data, },
 	{ .compatible = "nxp,imx8mp-fspi", .data = (ulong)&imx8mm_data, },
+	{ .compatible = "nxp,imxrt1170-fspi", .data = (ulong)&imxrt1170_data, },
 	{ }
 };
 
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index a35b8c2..847fa1f 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -23,6 +23,7 @@
 #include <linux/delay.h>
 #include <linux/dma-mapping.h>
 #include <linux/err.h>
+#include <linux/iopoll.h>
 #include <linux/ioport.h>
 #include <dm.h>
 #include <generic-phy.h>
@@ -587,7 +588,6 @@
  */
 static int dwc3_core_init(struct dwc3 *dwc)
 {
-	unsigned long		timeout;
 	u32			hwparams4 = dwc->hwparams.hwparams4;
 	u32			reg;
 	int			ret;
@@ -610,15 +610,11 @@
 	}
 
 	/* issue device SoftReset too */
-	timeout = 5000;
 	dwc3_writel(dwc->regs, DWC3_DCTL, DWC3_DCTL_CSFTRST);
-	while (timeout--) {
-		reg = dwc3_readl(dwc->regs, DWC3_DCTL);
-		if (!(reg & DWC3_DCTL_CSFTRST))
-			break;
-	};
-
-	if (!timeout) {
+	ret = read_poll_timeout(dwc3_readl, reg,
+				!(reg & DWC3_DCTL_CSFTRST),
+				1, 5000, dwc->regs, DWC3_DCTL);
+	if (ret) {
 		dev_err(dwc->dev, "Reset Timed Out\n");
 		ret = -ETIMEDOUT;
 		goto err0;
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index 3dc7977..5c9e8fc 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -262,7 +262,7 @@
 config USB_EHCI_MSM
 	bool "Support for Qualcomm on-chip EHCI USB controller"
 	depends on DM_USB
-	select USB_ULPI_VIEWPORT
+	select USB_ULPI
 	select MSM8916_USB_PHY
 	---help---
 	  Enables support for the on-chip EHCI controller on Qualcomm
@@ -279,7 +279,10 @@
 	depends on ARCH_TEGRA
 	select USB_EHCI_IS_TDI
 	---help---
-	  Enable support for Tegra on-chip EHCI USB controller
+	  Enable support for Tegra on-chip EHCI USB controller. If you enable
+	  ULPI and your PHY needs a different reference clock than the standard
+	  24 MHz then you have to define CFG_ULPI_REF_CLK to the appropriate
+	  value in Hz.
 
 config USB_EHCI_ZYNQ
 	bool "Support for Xilinx Zynq on-chip EHCI USB controller"
diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c
index 7c73eb6..89b8788 100644
--- a/drivers/usb/host/ehci-tegra.c
+++ b/drivers/usb/host/ehci-tegra.c
@@ -25,13 +25,6 @@
 #define HOSTPC1_DEVLC	0x84
 #define HOSTPC1_PSPD(x)		(((x) >> 25) & 0x3)
 
-#ifdef CONFIG_USB_ULPI
-	#ifndef CONFIG_USB_ULPI_VIEWPORT
-	#error	"To use CONFIG_USB_ULPI on Tegra Boards you have to also \
-		define CONFIG_USB_ULPI_VIEWPORT"
-	#endif
-#endif
-
 /* Parameters we need for USB */
 enum {
 	PARAM_DIVN,                     /* PLL FEEDBACK DIVIDer */
diff --git a/drivers/usb/ulpi/Kconfig b/drivers/usb/ulpi/Kconfig
index 001564d..d969360 100644
--- a/drivers/usb/ulpi/Kconfig
+++ b/drivers/usb/ulpi/Kconfig
@@ -1,8 +1,18 @@
 comment "ULPI drivers"
 
+config USB_ULPI
+	bool "ULPI support"
+	depends on USB_HOST
+	help
+	  Select to commnicate with USB PHY via ULPI interface.
+	  The ULPI (UTMI Low Pin (count) Interface) is a wrapper on UTMI+ core
+	  that is used as PHY Transreceiver for USB controllers.
+
+	  This driver uses ULPI viewports that are specific for each SoC.
+
 choice
 	prompt "ULPI Viewport type"
-	optional
+	depends on USB_ULPI
 	help
 	  Select ULPI viewport (SoC-side interface to ULPI) implementation
 	  appropriate for the device if you want to communicate with
@@ -10,23 +20,9 @@
 
 config USB_ULPI_VIEWPORT
 	bool "Generic ULPI Viewport"
+	depends on USB_ULPI
 	help
 	  Support generic ULPI Viewport implementation that is used on
 	  some Tegra and Snapdragon devices.
 
-config USB_ULPI_VIEWPORT_OMAP
-	bool "OMAP ULPI Viewport"
-	help
-	  Support ULPI Viewport implementation that is used on OMAP devices.
-
 endchoice
-
-config USB_ULPI
-	bool "ULPI support"
-	depends on (USB_ULPI_VIEWPORT || USB_ULPI_VIEWPORT_OMAP)
-	help
-	  Select to commnicate with USB PHY via ULPI interface.
-	  ULPI is wrapper on UTMI+ core that is used as
-	  PHY Transreceiver for USB controllers.
-
-	  This driver uses ULPI viewports that are specific for each SoC.
diff --git a/drivers/usb/ulpi/Makefile b/drivers/usb/ulpi/Makefile
index f05b774..5565948 100644
--- a/drivers/usb/ulpi/Makefile
+++ b/drivers/usb/ulpi/Makefile
@@ -4,4 +4,3 @@
 
 obj-$(CONFIG_USB_ULPI)		+= ulpi.o
 obj-$(CONFIG_USB_ULPI_VIEWPORT)	+= ulpi-viewport.o
-obj-$(CONFIG_USB_ULPI_VIEWPORT_OMAP)	+= omap-ulpi-viewport.o
diff --git a/drivers/usb/ulpi/omap-ulpi-viewport.c b/drivers/usb/ulpi/omap-ulpi-viewport.c
deleted file mode 100644
index 6f0c3eb..0000000
--- a/drivers/usb/ulpi/omap-ulpi-viewport.c
+++ /dev/null
@@ -1,71 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * OMAP ulpi viewport support
- * Based on drivers/usb/ulpi/ulpi-viewport.c
- *
- * Copyright (C) 2011 Texas Instruments Incorporated - https://www.ti.com
- * Author: Govindraj R <govindraj.raja@ti.com>
- */
-
-#include <log.h>
-#include <asm/io.h>
-#include <linux/delay.h>
-#include <usb/ulpi.h>
-
-#define OMAP_ULPI_WR_OPSEL	(2 << 22)
-#define OMAP_ULPI_RD_OPSEL	(3 << 22)
-#define OMAP_ULPI_START		(1 << 31)
-
-/*
- * Wait for having ulpi in done state
- */
-static int ulpi_wait(struct ulpi_viewport *ulpi_vp, u32 mask)
-{
-	int timeout = CFG_USB_ULPI_TIMEOUT;
-
-	while (--timeout) {
-		if (!(readl(ulpi_vp->viewport_addr) & mask))
-			return 0;
-
-		udelay(1);
-	}
-
-	return ULPI_ERROR;
-}
-
-/*
- * Issue a ULPI read/write request
- */
-static int ulpi_request(struct ulpi_viewport *ulpi_vp, u32 value)
-{
-	int err;
-
-	writel(value, ulpi_vp->viewport_addr);
-
-	err = ulpi_wait(ulpi_vp, OMAP_ULPI_START);
-	if (err)
-		debug("ULPI request timed out\n");
-
-	return err;
-}
-
-int ulpi_write(struct ulpi_viewport *ulpi_vp, u8 *reg, u32 value)
-{
-	u32 val = OMAP_ULPI_START | (((ulpi_vp->port_num + 1) & 0xf) << 24) |
-			OMAP_ULPI_WR_OPSEL | ((u32)reg << 16) | (value & 0xff);
-
-	return ulpi_request(ulpi_vp, val);
-}
-
-u32 ulpi_read(struct ulpi_viewport *ulpi_vp, u8 *reg)
-{
-	int err;
-	u32 val = OMAP_ULPI_START | (((ulpi_vp->port_num + 1) & 0xf) << 24) |
-			 OMAP_ULPI_RD_OPSEL | ((u32)reg << 16);
-
-	err = ulpi_request(ulpi_vp, val);
-	if (err)
-		return err;
-
-	return readl(ulpi_vp->viewport_addr) & 0xff;
-}
diff --git a/env/Kconfig b/env/Kconfig
index 9f5ec44..d7a7e81 100644
--- a/env/Kconfig
+++ b/env/Kconfig
@@ -389,7 +389,7 @@
 
 config ENV_IS_IN_MTD
 	bool "Environment is in MTD flash"
-	depends on !CHAIN_OF_TRUST && (SPI_FLASH || DM_SPI_FLASH)
+	depends on !CHAIN_OF_TRUST && MTD
 	default y if ARCH_AIROHA
 	help
 	  Define this if you have a MTD Flash memory device which you
diff --git a/env/mtd.c b/env/mtd.c
index 721faeb..d7ec30e 100644
--- a/env/mtd.c
+++ b/env/mtd.c
@@ -32,7 +32,7 @@
 
 static int env_mtd_save(void)
 {
-	char *saved_buf, *write_buf, *tmp;
+	char *saved_buf = NULL, *write_buf, *tmp;
 	struct erase_info ei = { };
 	struct mtd_info *mtd_env;
 	u32 sect_size, sect_num;
@@ -105,7 +105,7 @@
 	}
 
 	offset = CONFIG_ENV_OFFSET;
-	remaining = sect_size;
+	remaining = write_size;
 	tmp = write_buf;
 
 	puts("Writing to MTD...");
diff --git a/fs/ext4/Kconfig b/fs/ext4/Kconfig
index 1a913d2..8ddaebe 100644
--- a/fs/ext4/Kconfig
+++ b/fs/ext4/Kconfig
@@ -1,5 +1,6 @@
 config FS_EXT4
 	bool "Enable ext4 filesystem support"
+	select CRC16
 	help
 	  This provides support for reading images from the ext4 filesystem.
 	  ext4 is a widely used general-purpose filesystem for Linux.
diff --git a/fs/ext4/ext4fs.c b/fs/ext4/ext4fs.c
index bd058a0..51944b6 100644
--- a/fs/ext4/ext4fs.c
+++ b/fs/ext4/ext4fs.c
@@ -102,7 +102,7 @@
 	blockcnt = lldiv(((len + pos) + blocksize - 1), blocksize);
 
 	for (i = lldiv(pos, blocksize); i < blockcnt; i++) {
-		long int blknr;
+		lbaint_t blknr;
 		int blockoff = pos - (blocksize * i);
 		int blockend = blocksize;
 		int skipfirst = 0;
diff --git a/fs/fs_internal.c b/fs/fs_internal.c
index ab4847a..ff27c56 100644
--- a/fs/fs_internal.c
+++ b/fs/fs_internal.c
@@ -28,7 +28,7 @@
 	/* Check partition boundaries */
 	if ((sector + ((byte_offset + byte_len - 1) >> log2blksz))
 	    >= partition->size) {
-		log_debug("read outside partition " LBAFU "\n", sector);
+		log_err("** Read outside partition " LBAFU "\n", sector);
 		return 0;
 	}
 
diff --git a/include/configs/imx6ulz_smm_m2.h b/include/configs/imx6ulz_smm_m2.h
index 44a3fc0..87aede2 100644
--- a/include/configs/imx6ulz_smm_m2.h
+++ b/include/configs/imx6ulz_smm_m2.h
@@ -60,10 +60,8 @@
 	BOOTENV
 
 /* Physical Memory Map */
-#define PHYS_SDRAM			MMDC0_ARB_BASE_ADDR
-#define PHYS_SDRAM_SIZE			SZ_128M
-
-#define CFG_SYS_SDRAM_BASE		PHYS_SDRAM
+#define PHYS_SDRAM		MMDC0_ARB_BASE_ADDR
+#define CFG_SYS_SDRAM_BASE	PHYS_SDRAM
 #define CFG_SYS_INIT_RAM_ADDR	IRAM_BASE_ADDR
 #define CFG_SYS_INIT_RAM_SIZE	IRAM_SIZE
 
diff --git a/include/configs/starfive-visionfive2.h b/include/configs/starfive-visionfive2.h
index 049b0a0..e7001b2 100644
--- a/include/configs/starfive-visionfive2.h
+++ b/include/configs/starfive-visionfive2.h
@@ -39,4 +39,6 @@
 	"partitions=" PARTS_DEFAULT "\0" \
 	"fdtfile=" CONFIG_DEFAULT_FDT_FILE "\0"
 
+#define CFG_SYS_NS16550_CLK		24000000
+
 #endif /* _STARFIVE_VISIONFIVE2_H */
diff --git a/include/configs/th1520_lpi4a.h b/include/configs/th1520_lpi4a.h
index 87496a5..7a9b70a 100644
--- a/include/configs/th1520_lpi4a.h
+++ b/include/configs/th1520_lpi4a.h
@@ -9,6 +9,7 @@
 
 #include <linux/sizes.h>
 
+#define CFG_SYS_NS16550_CLK		100000000
 #define CFG_SYS_SDRAM_BASE		0x00000000
 
 #define UART_BASE	0xffe7014000
diff --git a/include/dt-bindings/clock/imxrt1170-clock.h b/include/dt-bindings/clock/imxrt1170-clock.h
index 8ab8018..d3d21cf 100644
--- a/include/dt-bindings/clock/imxrt1170-clock.h
+++ b/include/dt-bindings/clock/imxrt1170-clock.h
@@ -43,6 +43,8 @@
 #define IMXRT1170_CLK_GPT1			33
 #define IMXRT1170_CLK_SEMC_SEL			34
 #define IMXRT1170_CLK_SEMC			35
-#define IMXRT1170_CLK_END			36
+#define IMXRT1170_CLK_FLEXSPI1_SEL		36
+#define IMXRT1170_CLK_FLEXSPI1			37
+#define IMXRT1170_CLK_END			38
 
 #endif /* __DT_BINDINGS_CLOCK_IMXRT1170_H */
diff --git a/include/efi_device_path.h b/include/efi_device_path.h
new file mode 100644
index 0000000..aae8522
--- /dev/null
+++ b/include/efi_device_path.h
@@ -0,0 +1,421 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * EFI device path functions
+ *
+ * (C) Copyright 2017 Rob Clark
+ */
+
+#ifndef EFI_DEVICE_PATH_H
+#define EFI_DEVICE_PATH_H
+
+#include <efi.h>
+
+struct blk_desc;
+struct efi_load_option;
+struct udevice;
+
+/*
+ * EFI_DP_END - Template end node for EFI device paths.
+ *
+ * Represents the terminating node of an EFI device path.
+ * It has a type of DEVICE_PATH_TYPE_END and sub_type DEVICE_PATH_SUB_TYPE_END
+ */
+extern const struct efi_device_path EFI_DP_END;
+
+/**
+ * efi_dp_next() - Iterate to next block in device-path
+ *
+ * Advance to the next node in an EFI device path.
+ *
+ * @dp: Pointer to the current device path node.
+ * Return: Pointer to the next device path node, or NULL if at the end
+ * or if input is NULL.
+ */
+struct efi_device_path *efi_dp_next(const struct efi_device_path *dp);
+
+/**
+ * efi_dp_match() - Compare two device-paths
+ *
+ * Compare two device paths node by node. The comparison stops when an End
+ * node is reached in the shorter of the two paths. This is useful, for example,
+ * to compare a device-path representing a device with one representing a file
+ * on that device, or a device with a parent device.
+ *
+ * @a: Pointer to the first device path.
+ * @b: Pointer to the second device path.
+ * Return: An integer less than, equal to, or greater than zero if the first
+ * differing node in 'a' is found, respectively, to be less than,
+ * to match, or be greater than the corresponding node in 'b'. Returns 0
+ * if they match up to the end of the shorter path. Compares length first,
+ * then content.
+ */
+int efi_dp_match(const struct efi_device_path *a,
+		 const struct efi_device_path *b);
+
+/**
+ * efi_dp_shorten() - shorten device-path
+ *
+ * When creating a short-boot option we want to use a device-path that is
+ * independent of the location where the block device is plugged in.
+ *
+ * UsbWwi() nodes contain a serial number, hard drive paths a partition
+ * UUID. Both should be unique.
+ *
+ * See UEFI spec, section 3.1.2 for "short-form device path".
+ *
+ * @dp:     original device-path
+ * Return:  shortened device-path or NULL
+ */
+struct efi_device_path *efi_dp_shorten(struct efi_device_path *dp);
+
+/**
+ * efi_dp_find_obj() - find handle by device path
+ *
+ * If @rem is provided, the handle with the longest partial match is returned.
+ *
+ * @dp:     device path to search
+ * @guid:   GUID of protocol that must be installed on path or NULL
+ * @rem:    pointer to receive remaining device path
+ * Return:  matching handle
+ */
+efi_handle_t efi_dp_find_obj(struct efi_device_path *dp, const efi_guid_t *guid,
+			     struct efi_device_path **rem);
+
+/**
+ * efi_dp_last_node() - Determine the last device path node before the end node
+ *
+ * Iterate through the device path to find the very last node before
+ * the terminating EFI_DP_END node.
+ *
+ * @dp: Pointer to the device path.
+ * Return: Pointer to the last actual data node before the end node if it exists
+ * otherwise NULL (e.g., if dp is NULL or only an EFI_DP_END node).
+ */
+const struct efi_device_path *efi_dp_last_node(const struct efi_device_path *dp);
+
+/**
+ * efi_dp_instance_size() - Get size of the first device path instance
+ *
+ * Calculate the total length of all nodes in the first instance of a
+ * (potentially multi-instance) device path. The size of the instance-specific
+ * end node (if any) or the final device path. The end node is not included.
+ *
+ * @dp: Pointer to the device path.
+ * Return: Size in bytes of the first instance, or 0 if dp is NULL or an
+ * EFI_DP_END node
+ */
+efi_uintn_t efi_dp_instance_size(const struct efi_device_path *dp);
+
+/**
+ * efi_dp_size() - Get size of multi-instance device path excluding end node
+ *
+ * Calculate the total size of the entire device path structure, traversing
+ * through all instances, up to but not including the final
+ * END_ENTIRE_DEVICE_PATH node.
+ *
+ * @dp: Pointer to the device path.
+ * Return: Total size in bytes of all nodes in the device path (excluding the
+ * final EFI_DP_END node), or 0 if dp is NULL.
+ */
+efi_uintn_t efi_dp_size(const struct efi_device_path *dp);
+
+/**
+ * efi_dp_dup() - Copy multi-instance device path
+ *
+ * Duplicate the given device path, including its end node(s).
+ * The caller is responsible for freeing the allocated memory (e.g.,
+ * using efi_free()).
+ *
+ * @dp: Pointer to the device path to duplicate.
+ * Return: Pointer to the newly allocated and copied device path, or NULL on
+ * allocation failure or if dp is NULL.
+ */
+struct efi_device_path *efi_dp_dup(const struct efi_device_path *dp);
+
+/**
+ * efi_dp_concat() - Concatenate two device paths and terminate the result
+ *
+ * @dp1:        First device path
+ * @dp2:        Second device path
+ * @split_end_node:
+ * - 0 to concatenate (dp1 is assumed not to have an end node or it's ignored,
+ *   dp2 is appended, then one EFI_DP_END node)
+ * - 1 to concatenate with end node added as separator (dp1, END_THIS_INSTANCE,
+ *   dp2, END_ENTIRE)
+ *
+ * Size of dp1 excluding last end node to concatenate with end node as
+ * separator in case dp1 contains an end node (dp1 (partial), END_THIS_INSTANCE,
+ * dp2, END_ENTIRE)
+ *
+ * Return:
+ * concatenated device path or NULL. Caller must free the returned value.
+ */
+struct efi_device_path *efi_dp_concat(const struct efi_device_path *dp1,
+				      const struct efi_device_path *dp2,
+				      size_t split_end_node);
+
+/**
+ * efi_dp_append_node() - Append a single node to a device path
+ *
+ * Create a new device path by appending a given node to an existing
+ * device path.
+ * If the original device path @dp is NULL, a new path is created
+ * with the given @node followed by an EFI_DP_END node.
+ * If the @node is NULL and @dp is not NULL, the original path @dp is
+ * duplicated.
+ * If both @dp and @node are NULL, a path with only an EFI_DP_END node is
+ * returned.
+ * The caller must free the returned path (e.g., using efi_free()).
+ *
+ * @dp:   Original device path (can be NULL).
+ * @node: Node to append (can be NULL).
+ * Return: New device path with the node appended, or NULL on allocation
+ * failure.
+ */
+struct efi_device_path *efi_dp_append_node(const struct efi_device_path *dp,
+					   const struct efi_device_path *node);
+
+/**
+ * efi_dp_create_device_node() - Create a new device path node
+ *
+ * Allocate and initialise the header of a new EFI device path node with the
+ * given type, sub-type, and length. The content of the node beyond the basic
+ * efi_device_path header is zeroed by efi_alloc.
+ *
+ * @type:     Device path type.
+ * @sub_type: Device path sub-type.
+ * @length:   Length of the node (must be >= sizeof(struct efi_device_path)).
+ * Return: Pointer to the new device path node, or NULL on allocation failure
+ * or if length is invalid.
+ */
+struct efi_device_path *efi_dp_create_device_node(const u8 type,
+						  const u8 sub_type,
+						  const u16 length);
+
+/**
+ * efi_dp_append_instance() - Append a device path instance to another
+ *
+ * Concatenate two device paths, treating the second path (@dpi) as a new
+ * instance appended to the first path (@dp). An END_THIS_INSTANCE node is
+ * inserted between @dp and @dpi if @dp is not NULL.
+ * If @dp is NULL, @dpi is duplicated (and terminated appropriately).
+ * @dpi must not be NULL.
+ * The caller is responsible for freeing the returned path (e.g., using
+ * efi_free()).
+ *
+ * @dp:  The base device path. If NULL, @dpi is duplicated.
+ * @dpi: The device path instance to append. Must not be NULL.
+ * Return: A new device path with @dpi appended as a new instance, or NULL on
+ * error  (e.g. allocation failure, @dpi is NULL).
+ */
+struct efi_device_path *
+efi_dp_append_instance(const struct efi_device_path *dp,
+		       const struct efi_device_path *dpi);
+
+/**
+ * efi_dp_get_next_instance() - Extract the next dp instance
+ *
+ * Given a pointer to a pointer to a device path (@dp), this function extracts
+ * the first instance from the path. It allocates a new path for this extracted
+ * instance (including its instance-specific EFI_DP_END node). The input pointer
+ * (*@dp) is then updated to point to the start of the next instance in the
+ * original path, or set to NULL if no more instances remain.
+ * The caller is responsible for freeing the returned instance path (e.g.,
+ * using efi_free()).
+ *
+ * @dp:  On input, a pointer to a pointer to the multi-instance device path.
+ * On output, *@dp is updated to point to the start of the next instance,
+ * or NULL if no more instances.
+ * @size: Optional pointer to an efi_uintn_t variable that will receive the size
+ * of the extracted instance path (including its EFI_DP_END node).
+ * Return: Pointer to a newly allocated device path for the extracted instance,
+ * or NULL if no instance could be extracted or an error occurred (e.g.,
+ * allocation failure).
+ */
+struct efi_device_path *efi_dp_get_next_instance(struct efi_device_path **dp,
+						 efi_uintn_t *size);
+
+/**
+ * efi_dp_is_multi_instance() - Check if a device path is multi-instance
+ *
+ * Traverse the device path to its end. It is considered multi-instance if an
+ * END_THIS_INSTANCE_DEVICE_PATH node (type DEVICE_PATH_TYPE_END, sub-type
+ * DEVICE_PATH_SUB_TYPE_INSTANCE_END) is encountered before the final
+ * END_ENTIRE_DEVICE_PATH node.
+ *
+ * @dp: The device path to check.
+ * Return: True if the device path contains multiple instances, false otherwise
+ * (including if @dp is NULL).
+ */
+bool efi_dp_is_multi_instance(const struct efi_device_path *dp);
+
+/**
+ * efi_dp_from_part() - Construct a dp from a partition on a block device
+ *
+ * Create a full device path for a specified partition on a given block device.
+ * If the partition number @part is 0, the path is for the block device itself.
+ * The caller is responsible for freeing the allocated memory (e.g., using
+ * efi_free()).
+ *
+ * @desc: Pointer to the block device descriptor.
+ * @part: Partition number (0 for the whole device, >0 for a specific
+ * partition).
+ * Return: Pointer to the newly created device path, or NULL on allocation
+ * failure or if the device/partition is not found or invalid.
+ */
+struct efi_device_path *efi_dp_from_part(struct blk_desc *desc, int part);
+
+/**
+ * efi_dp_part_node() - Create a device node for a block device partition
+ *
+ * Creates a single device path node representing a specific partition
+ * (e.g., HardDrivePath or CDROMPath, depending on desc->part_type).
+ * It does not create the full path from the root, only the partition-specific
+ * node. The caller is responsible for freeing the allocated memory (e.g.,
+ * using efi_free()).
+ *
+ * @desc: Pointer to the block device descriptor.
+ * @part: Partition number (must be > 0 and correspond to a valid partition on
+ * the device).
+ * Return: Pointer to the new device path node for the partition, or NULL on
+ * allocation * failure or error in getting partition information.
+ */
+struct efi_device_path *efi_dp_part_node(struct blk_desc *desc, int part);
+
+/**
+ * efi_dp_from_file() - append file path node to device path.
+ *
+ * @dp:     device path or NULL
+ * @path:   file path or NULL
+ * Return:  device path or NULL in case of an error
+ */
+struct efi_device_path *efi_dp_from_file(const struct efi_device_path *dp,
+					 const char *path);
+
+/**
+ * efi_dp_from_uart() - Create a device path for a UART device.
+ *
+ * Construct a device path representing the system's default UART,
+ * typically based on the U-Boot device model root and a UART messaging node.
+ * The caller is responsible for freeing the allocated memory (e.g., using
+ * efi_free()).
+ *
+ * Return: Pointer to the new UART device path, or NULL on allocation failure.
+ */
+struct efi_device_path *efi_dp_from_uart(void);
+
+/**
+ * efi_dp_from_eth() - Create a device path for an Ethernet device
+ *
+ * Construct a device path representing the given device. The caller is
+ * responsible for freeing the allocated memory (e.g. using efi_free())
+ *
+ * @dev: UCLASS_ETH device to process
+ *
+ * Return: Pointer to the new Ethernet device path, or NULL on allocation
+ * failure
+ */
+struct efi_device_path *efi_dp_from_eth(struct udevice *dev);
+
+/**
+ * efi_dp_from_mem() - Construct a device-path for a memory-mapped region
+ *
+ * Create an EFI device path representing a specific memory region, defined
+ * by its type, start address, and size.
+ * The caller is responsible for freeing the allocated memory (e.g.,
+ * using efi_free()).
+ *
+ * @memory_type: EFI memory type (e.g., EFI_RESERVED_MEMORY_TYPE).
+ * @start_address: Starting address of the memory region.
+ * @size: Size of the memory region in bytes.
+ * Return: Pointer to the new memory device path, or NULL on allocation failure
+ */
+struct efi_device_path *efi_dp_from_mem(u32 memory_type, u64 start_address,
+					size_t size);
+
+/**
+ * efi_dp_split_file_path() - split of relative file path from device path
+ *
+ * Given a device path indicating a file on a device, separate the device
+ * path in two: the device path of the actual device and the file path
+ * relative to this device.
+ *
+ * @full_path:      device path including device and file path
+ * @device_path:    path of the device
+ * @file_path:      relative path of the file or NULL if there is none
+ * Return:      status code
+ */
+efi_status_t efi_dp_split_file_path(struct efi_device_path *full_path,
+				    struct efi_device_path **device_path,
+				    struct efi_device_path **file_path);
+
+/**
+ * efi_dp_from_name() - convert U-Boot device and file path to device path
+ *
+ * @dev:    U-Boot device, e.g. 'mmc'
+ * @devnr:  U-Boot device number, e.g. 1 for 'mmc:1'
+ * @path:   file path relative to U-Boot device, may be NULL
+ * @device: pointer to receive device path of the device
+ * @file:   pointer to receive device path for the file
+ * Return:  status code
+ */
+efi_status_t efi_dp_from_name(const char *dev, const char *devnr,
+			      const char *path, struct efi_device_path **device,
+			      struct efi_device_path **file);
+
+/**
+ * efi_dp_check_length() - check length of a device path
+ *
+ * @dp:     pointer to device path
+ * @maxlen: maximum length of the device path
+ * Return:
+ * * length of the device path if it is less or equal @maxlen
+ * * -1 if the device path is longer then @maxlen
+ * * -1 if a device path node has a length of less than 4
+ * * -EINVAL if maxlen exceeds SSIZE_MAX
+ */
+ssize_t efi_dp_check_length(const struct efi_device_path *dp,
+			    const size_t maxlen);
+
+/**
+ * efi_dp_from_lo() - get device-path from load option
+ *
+ * The load options in U-Boot may contain multiple concatenated device-paths.
+ * The first device-path indicates the EFI binary to execute. Subsequent
+ * device-paths start with a VenMedia node where the GUID identifies the
+ * function (initrd or fdt).
+ *
+ * @lo:     EFI load option containing a valid device path
+ * @guid:   GUID identifying device-path or NULL for the EFI binary
+ *
+ * Return:
+ * device path excluding the matched VenMedia node or NULL.
+ * Caller must free the returned value.
+ */
+struct efi_device_path *efi_dp_from_lo(struct efi_load_option *lo,
+				       const efi_guid_t *guid);
+
+/**
+ * search_gpt_dp_node() - search gpt device path node
+ *
+ * @device_path:    device path
+ *
+ * Return:  pointer to the gpt device path node
+ */
+struct efi_device_path *search_gpt_dp_node(struct efi_device_path *device_path);
+
+/**
+ * efi_dp_from_http() - set device path from http
+ *
+ * Set the device path to an IPv4 path as provided by efi_dp_from_ipv4
+ * concatenated with a device path of subtype DEVICE_PATH_SUB_TYPE_MSG_URI,
+ * and an EFI_DP_END node.
+ *
+ * @server:	URI of remote server
+ * @dev:	net udevice
+ * Return:	pointer to HTTP device path, NULL on error
+ */
+struct efi_device_path *efi_dp_from_http(const char *server,
+					 struct udevice *dev);
+
+#endif /* EFI_DEVICE_PATH_H */
diff --git a/include/efi_loader.h b/include/efi_loader.h
index 84e8cfe..b3beda5 100644
--- a/include/efi_loader.h
+++ b/include/efi_loader.h
@@ -9,6 +9,7 @@
 #define _EFI_LOADER_H 1
 
 #include <blk.h>
+#include <efi_device_path.h>
 #include <event.h>
 #include <log.h>
 #include <part_efi.h>
@@ -588,8 +589,27 @@
 efi_status_t efi_bootmgr_run(void *fdt);
 /* search the boot option index in BootOrder */
 bool efi_search_bootorder(u16 *bootorder, efi_uintn_t num, u32 target, u32 *index);
-/* Set up console modes */
+
+/**
+ * efi_setup_console_size() - update the mode table.
+ *
+ * By default the only mode available is 80x25. If the console has at least 50
+ * lines, enable mode 80x50. If we can query the console size and it is neither
+ * 80x25 nor 80x50, set it as an additional mode.
+ */
 void efi_setup_console_size(void);
+
+/**
+ * efi_console_set_ansi() - Set whether ANSI escape-characters should be emitted
+ *
+ * These characters mess up tests which use ut_assert_nextline(). Call this
+ * function to tell efi_loader not to emit these characters when starting up the
+ * terminal
+ *
+ * @allow_ansi: Allow emitting ANSI escape-characters
+ */
+void efi_console_set_ansi(bool allow_ansi);
+
 /* Set up load options from environment variable */
 efi_status_t efi_env_set_load_options(efi_handle_t handle, const char *env_var,
 				      u16 **load_options);
@@ -916,66 +936,10 @@
 #define EFI_LOADER_BOUNCE_BUFFER_SIZE (64 * 1024 * 1024)
 #endif
 
-/* shorten device path */
-struct efi_device_path *efi_dp_shorten(struct efi_device_path *dp);
-struct efi_device_path *efi_dp_next(const struct efi_device_path *dp);
-int efi_dp_match(const struct efi_device_path *a,
-		 const struct efi_device_path *b);
-efi_handle_t efi_dp_find_obj(struct efi_device_path *dp,
-			     const efi_guid_t *guid,
-			     struct efi_device_path **rem);
-/* get size of the first device path instance excluding end node */
-efi_uintn_t efi_dp_instance_size(const struct efi_device_path *dp);
-/* size of multi-instance device path excluding end node */
-efi_uintn_t efi_dp_size(const struct efi_device_path *dp);
-struct efi_device_path *efi_dp_dup(const struct efi_device_path *dp);
-struct efi_device_path *efi_dp_append_node(const struct efi_device_path *dp,
-					   const struct efi_device_path *node);
-/* Create a device path node of given type, sub-type, length */
-struct efi_device_path *efi_dp_create_device_node(const u8 type,
-						  const u8 sub_type,
-						  const u16 length);
-/* Append device path instance */
-struct efi_device_path *efi_dp_append_instance(
-		const struct efi_device_path *dp,
-		const struct efi_device_path *dpi);
-/* Get next device path instance */
-struct efi_device_path *efi_dp_get_next_instance(struct efi_device_path **dp,
-						 efi_uintn_t *size);
-/* Check if a device path contains muliple instances */
-bool efi_dp_is_multi_instance(const struct efi_device_path *dp);
-
-struct efi_device_path *efi_dp_from_part(struct blk_desc *desc, int part);
-/* Create a device node for a block device partition. */
-struct efi_device_path *efi_dp_part_node(struct blk_desc *desc, int part);
-struct efi_device_path *efi_dp_from_file(const struct efi_device_path *dp,
-					 const char *path);
-struct efi_device_path *efi_dp_from_eth(struct udevice *dev);
-struct efi_device_path *efi_dp_from_http(const char *server, struct udevice *dev);
-struct efi_device_path *efi_dp_from_mem(uint32_t mem_type,
-					uint64_t start_address,
-					size_t size);
-/* Determine the last device path node that is not the end node. */
-const struct efi_device_path *efi_dp_last_node(
-			const struct efi_device_path *dp);
-efi_status_t efi_dp_split_file_path(struct efi_device_path *full_path,
-				    struct efi_device_path **device_path,
-				    struct efi_device_path **file_path);
-struct efi_device_path *efi_dp_from_uart(void);
-efi_status_t efi_dp_from_name(const char *dev, const char *devnr,
-			      const char *path,
-			      struct efi_device_path **device,
-			      struct efi_device_path **file);
-ssize_t efi_dp_check_length(const struct efi_device_path *dp,
-			    const size_t maxlen);
-
 #define EFI_DP_TYPE(_dp, _type, _subtype) \
 	(((_dp)->type == DEVICE_PATH_TYPE_##_type) && \
 	 ((_dp)->sub_type == DEVICE_PATH_SUB_TYPE_##_subtype))
 
-/* template END node: */
-extern const struct efi_device_path END;
-
 /* Indicate supported runtime services */
 efi_status_t efi_init_runtime_supported(void);
 
diff --git a/include/image.h b/include/image.h
index c1db838..4620782 100644
--- a/include/image.h
+++ b/include/image.h
@@ -138,7 +138,8 @@
 	IH_ARCH_ARC,			/* Synopsys DesignWare ARC */
 	IH_ARCH_X86_64,			/* AMD x86_64, Intel and Via */
 	IH_ARCH_XTENSA,			/* Xtensa	*/
-	IH_ARCH_RISCV,			/* RISC-V */
+	IH_ARCH_RISCV,			/* RISC-V 32 bit*/
+	IH_ARCH_RISCV64,		/* RISC-V 64 bit*/
 
 	IH_ARCH_COUNT,
 };
diff --git a/include/net-common.h b/include/net-common.h
index e536968..7853612 100644
--- a/include/net-common.h
+++ b/include/net-common.h
@@ -570,6 +570,7 @@
  *			Filled by client.
  * @hdr_cont_len:	content length according to headers. Filled by wget
  * @headers:		buffer for headers. Filled by wget.
+ * @silent:		do not print anything to the console. Filled by client.
  */
 struct wget_http_info {
 	enum wget_http_method method;
@@ -580,6 +581,7 @@
 	bool check_buffer_size;
 	u32 hdr_cont_len;
 	char *headers;
+	bool silent;
 };
 
 extern struct wget_http_info default_wget_info;
diff --git a/lib/efi_driver/efi_uclass.c b/lib/efi_driver/efi_uclass.c
index 495be53..7392c60 100644
--- a/lib/efi_driver/efi_uclass.c
+++ b/lib/efi_driver/efi_uclass.c
@@ -20,6 +20,7 @@
 #define LOG_CATEGORY LOGC_EFI
 
 #include <dm.h>
+#include <efi_device_path.h>
 #include <efi_driver.h>
 #include <log.h>
 #include <malloc.h>
diff --git a/lib/efi_loader/efi_bootbin.c b/lib/efi_loader/efi_bootbin.c
index 94ba7c5..b394f0d 100644
--- a/lib/efi_loader/efi_bootbin.c
+++ b/lib/efi_loader/efi_bootbin.c
@@ -10,6 +10,7 @@
 #include <charset.h>
 #include <dm.h>
 #include <efi.h>
+#include <efi_device_path.h>
 #include <efi_loader.h>
 #include <env.h>
 #include <image.h>
diff --git a/lib/efi_loader/efi_bootmgr.c b/lib/efi_loader/efi_bootmgr.c
index c0df5cb..1a3461f 100644
--- a/lib/efi_loader/efi_bootmgr.c
+++ b/lib/efi_loader/efi_bootmgr.c
@@ -12,6 +12,7 @@
 #include <charset.h>
 #include <dm.h>
 #include <efi.h>
+#include <efi_device_path.h>
 #include <log.h>
 #include <malloc.h>
 #include <net.h>
@@ -479,6 +480,13 @@
 	if (!ctx)
 		return EFI_OUT_OF_RESOURCES;
 
+	s = env_get("ipaddr");
+	if (!s && dhcp_run(0, NULL, false)) {
+		log_err("Error: Can't find a valid IP address\n");
+		ret = EFI_DEVICE_ERROR;
+		goto err;
+	}
+
 	s = env_get("loadaddr");
 	if (!s) {
 		log_err("Error: loadaddr is not set\n");
@@ -527,7 +535,7 @@
 		 * will be freed in return_to_efibootmgr event callback.
 		 */
 		loaded_dp = efi_dp_from_mem(EFI_RESERVED_MEMORY_TYPE,
-					    (uintptr_t)image_addr, image_size);
+					    image_addr, image_size);
 		ret = efi_install_multiple_protocol_interfaces(
 			&mem_handle, &efi_guid_device_path, loaded_dp, NULL);
 		if (ret != EFI_SUCCESS)
@@ -855,7 +863,8 @@
 		lo.label = dev_name;
 		lo.attributes = LOAD_OPTION_ACTIVE;
 		lo.file_path = device_path;
-		lo.file_path_length = efi_dp_size(device_path) + sizeof(END);
+		lo.file_path_length = efi_dp_size(device_path) +
+			sizeof(EFI_DP_END);
 		/*
 		 * Set the dedicated guid to optional_data, it is used to identify
 		 * the boot option that automatically generated by the bootmenu.
diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c
index dbebb37..24b0e52 100644
--- a/lib/efi_loader/efi_boottime.c
+++ b/lib/efi_loader/efi_boottime.c
@@ -11,6 +11,7 @@
 #include <div64.h>
 #include <dm/device.h>
 #include <dm/root.h>
+#include <efi_device_path.h>
 #include <efi_loader.h>
 #include <irq_func.h>
 #include <log.h>
diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c
index 1aa52ac..f19e78a 100644
--- a/lib/efi_loader/efi_capsule.c
+++ b/lib/efi_loader/efi_capsule.c
@@ -8,6 +8,7 @@
 
 #define LOG_CATEGORY LOGC_EFI
 
+#include <efi_device_path.h>
 #include <efi_loader.h>
 #include <efi_variable.h>
 #include <env.h>
diff --git a/lib/efi_loader/efi_console.c b/lib/efi_loader/efi_console.c
index 9d9f786..953f683 100644
--- a/lib/efi_loader/efi_console.c
+++ b/lib/efi_loader/efi_console.c
@@ -9,6 +9,7 @@
 
 #include <ansi.h>
 #include <charset.h>
+#include <efi_device_path.h>
 #include <malloc.h>
 #include <time.h>
 #include <dm/device.h>
@@ -30,6 +31,17 @@
 
 __maybe_unused static struct efi_object uart_obj;
 
+/*
+ * suppress emission of ANSI escape-characters for use by unit tests. Leave it
+ * as 0 for the default behaviour
+ */
+static bool no_ansi;
+
+void efi_console_set_ansi(bool allow_ansi)
+{
+	no_ansi = !allow_ansi;
+}
+
 static struct cout_mode efi_cout_modes[] = {
 	/* EFI Mode 0 is 80x25 and always present */
 	{
@@ -348,13 +360,6 @@
 	return 0;
 }
 
-/**
- * efi_setup_console_size() - update the mode table.
- *
- * By default the only mode available is 80x25. If the console has at least 50
- * lines, enable mode 80x50. If we can query the console size and it is neither
- * 80x25 nor 80x50, set it as an additional mode.
- */
 void efi_setup_console_size(void)
 {
 	int rows = 25, cols = 80;
@@ -362,8 +367,12 @@
 
 	if (IS_ENABLED(CONFIG_VIDEO))
 		ret = query_vidconsole(&rows, &cols);
-	if (ret)
-		ret = query_console_serial(&rows, &cols);
+	if (ret) {
+		if (no_ansi)
+			ret = 0;
+		else
+			ret = query_console_serial(&rows, &cols);
+	}
 	if (ret)
 		return;
 
diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c
index c9bf272..7316a76 100644
--- a/lib/efi_loader/efi_device_path.c
+++ b/lib/efi_loader/efi_device_path.c
@@ -10,6 +10,7 @@
 #include <blk.h>
 #include <dm.h>
 #include <dm/root.h>
+#include <efi_device_path.h>
 #include <log.h>
 #include <net.h>
 #include <usb.h>
@@ -21,11 +22,11 @@
 #include <asm-generic/unaligned.h>
 #include <linux/compat.h> /* U16_MAX */
 
-/* template END node: */
-const struct efi_device_path END = {
+/* template EFI_DP_END node: */
+const struct efi_device_path EFI_DP_END = {
 	.type     = DEVICE_PATH_TYPE_END,
 	.sub_type = DEVICE_PATH_SUB_TYPE_END,
-	.length   = sizeof(END),
+	.length   = sizeof(EFI_DP_END),
 };
 
 #if defined(CONFIG_MMC)
@@ -46,10 +47,6 @@
 }
 #endif
 
-/*
- * Iterate to next block in device-path, terminating (returning NULL)
- * at /End* node.
- */
 struct efi_device_path *efi_dp_next(const struct efi_device_path *dp)
 {
 	if (dp == NULL)
@@ -62,12 +59,6 @@
 	return (struct efi_device_path *)dp;
 }
 
-/*
- * Compare two device-paths, stopping when the shorter of the two hits
- * an End* node. This is useful to, for example, compare a device-path
- * representing a device with one representing a file on the device, or
- * a device with a parent device.
- */
 int efi_dp_match(const struct efi_device_path *a,
 		 const struct efi_device_path *b)
 {
@@ -90,20 +81,6 @@
 	}
 }
 
-/**
- * efi_dp_shorten() - shorten device-path
- *
- * When creating a short boot option we want to use a device-path that is
- * independent of the location where the block device is plugged in.
- *
- * UsbWwi() nodes contain a serial number, hard drive paths a partition
- * UUID. Both should be unique.
- *
- * See UEFI spec, section 3.1.2 for "short-form device path".
- *
- * @dp:		original device-path
- * Return:	shortened device-path or NULL
- */
 struct efi_device_path *efi_dp_shorten(struct efi_device_path *dp)
 {
 	while (dp) {
@@ -180,16 +157,6 @@
 	return best_handle;
 }
 
-/**
- * efi_dp_find_obj() - find handle by device path
- *
- * If @rem is provided, the handle with the longest partial match is returned.
- *
- * @dp:		device path to search
- * @guid:	GUID of protocol that must be installed on path or NULL
- * @rem:	pointer to receive remaining device path
- * Return:	matching handle
- */
 efi_handle_t efi_dp_find_obj(struct efi_device_path *dp,
 			     const efi_guid_t *guid,
 			     struct efi_device_path **rem)
@@ -204,13 +171,6 @@
 	return handle;
 }
 
-/*
- * Determine the last device path node that is not the end node.
- *
- * @dp		device path
- * Return:	last node before the end node if it exists
- *		otherwise NULL
- */
 const struct efi_device_path *efi_dp_last_node(const struct efi_device_path *dp)
 {
 	struct efi_device_path *ret;
@@ -224,7 +184,6 @@
 	return ret;
 }
 
-/* get size of the first device path instance excluding end node */
 efi_uintn_t efi_dp_instance_size(const struct efi_device_path *dp)
 {
 	efi_uintn_t sz = 0;
@@ -239,7 +198,6 @@
 	return sz;
 }
 
-/* get size of multi-instance device path excluding end node */
 efi_uintn_t efi_dp_size(const struct efi_device_path *dp)
 {
 	const struct efi_device_path *p = dp;
@@ -253,11 +211,10 @@
 	return (void *)p - (void *)dp;
 }
 
-/* copy multi-instance device path */
 struct efi_device_path *efi_dp_dup(const struct efi_device_path *dp)
 {
 	struct efi_device_path *ndp;
-	size_t sz = efi_dp_size(dp) + sizeof(END);
+	size_t sz = efi_dp_size(dp) + sizeof(EFI_DP_END);
 
 	if (!dp)
 		return NULL;
@@ -270,21 +227,6 @@
 	return ndp;
 }
 
-/**
- * efi_dp_concat() - Concatenate two device paths and add and terminate them
- *                   with an end node.
- *
- * @dp1:	    First device path
- * @dp2:	    Second device path
- * @split_end_node:
- * * 0 to concatenate
- * * 1 to concatenate with end node added as separator
- * * size of dp1 excluding last end node to concatenate with end node as
- *   separator in case dp1 contains an end node
- *
- * Return:
- * concatenated device path or NULL. Caller must free the returned value
- */
 struct
 efi_device_path *efi_dp_concat(const struct efi_device_path *dp1,
 			       const struct efi_device_path *dp2,
@@ -295,7 +237,7 @@
 
 	if (!dp1 && !dp2) {
 		/* return an end node */
-		ret = efi_dp_dup(&END);
+		ret = efi_dp_dup(&EFI_DP_END);
 	} else if (!dp1) {
 		ret = efi_dp_dup(dp2);
 	} else if (!dp2) {
@@ -312,9 +254,9 @@
 			sz1 = split_end_node;
 
 		if (split_end_node)
-			end_size = 2 * sizeof(END);
+			end_size = 2 * sizeof(EFI_DP_END);
 		else
-			end_size = sizeof(END);
+			end_size = sizeof(EFI_DP_END);
 		p = efi_alloc(sz1 + sz2 + end_size);
 		if (!p)
 			return NULL;
@@ -323,14 +265,14 @@
 		p += sz1;
 
 		if (split_end_node) {
-			memcpy(p, &END, sizeof(END));
-			p += sizeof(END);
+			memcpy(p, &EFI_DP_END, sizeof(EFI_DP_END));
+			p += sizeof(EFI_DP_END);
 		}
 
 		/* the end node of the second device path has to be retained */
 		memcpy(p, dp2, sz2);
 		p += sz2;
-		memcpy(p, &END, sizeof(END));
+		memcpy(p, &EFI_DP_END, sizeof(EFI_DP_END));
 	}
 
 	return ret;
@@ -342,26 +284,26 @@
 	struct efi_device_path *ret;
 
 	if (!node && !dp) {
-		ret = efi_dp_dup(&END);
+		ret = efi_dp_dup(&EFI_DP_END);
 	} else if (!node) {
 		ret = efi_dp_dup(dp);
 	} else if (!dp) {
 		size_t sz = node->length;
-		void *p = efi_alloc(sz + sizeof(END));
+		void *p = efi_alloc(sz + sizeof(EFI_DP_END));
 		if (!p)
 			return NULL;
 		memcpy(p, node, sz);
-		memcpy(p + sz, &END, sizeof(END));
+		memcpy(p + sz, &EFI_DP_END, sizeof(EFI_DP_END));
 		ret = p;
 	} else {
 		/* both dp and node are non-null */
 		size_t sz = efi_dp_size(dp);
-		void *p = efi_alloc(sz + node->length + sizeof(END));
+		void *p = efi_alloc(sz + node->length + sizeof(EFI_DP_END));
 		if (!p)
 			return NULL;
 		memcpy(p, dp, sz);
 		memcpy(p + sz, node, node->length);
-		memcpy(p + sz + node->length, &END, sizeof(END));
+		memcpy(p + sz + node->length, &EFI_DP_END, sizeof(EFI_DP_END));
 		ret = p;
 	}
 
@@ -399,17 +341,17 @@
 		return efi_dp_dup(dpi);
 	sz = efi_dp_size(dp);
 	szi = efi_dp_instance_size(dpi);
-	p = efi_alloc(sz + szi + 2 * sizeof(END));
+	p = efi_alloc(sz + szi + 2 * sizeof(EFI_DP_END));
 	if (!p)
 		return NULL;
 	ret = p;
-	memcpy(p, dp, sz + sizeof(END));
+	memcpy(p, dp, sz + sizeof(EFI_DP_END));
 	p = (void *)p + sz;
 	p->sub_type = DEVICE_PATH_SUB_TYPE_INSTANCE_END;
-	p = (void *)p + sizeof(END);
+	p = (void *)p + sizeof(EFI_DP_END);
 	memcpy(p, dpi, szi);
 	p = (void *)p + szi;
-	memcpy(p, &END, sizeof(END));
+	memcpy(p, &EFI_DP_END, sizeof(EFI_DP_END));
 	return ret;
 }
 
@@ -424,17 +366,17 @@
 	if (!dp || !*dp)
 		return NULL;
 	sz = efi_dp_instance_size(*dp);
-	p = efi_alloc(sz + sizeof(END));
+	p = efi_alloc(sz + sizeof(EFI_DP_END));
 	if (!p)
 		return NULL;
-	memcpy(p, *dp, sz + sizeof(END));
+	memcpy(p, *dp, sz + sizeof(EFI_DP_END));
 	*dp = (void *)*dp + sz;
 	if ((*dp)->sub_type == DEVICE_PATH_SUB_TYPE_INSTANCE_END)
-		*dp = (void *)*dp + sizeof(END);
+		*dp = (void *)*dp + sizeof(EFI_DP_END);
 	else
 		*dp = NULL;
 	if (size)
-		*size = sz + sizeof(END);
+		*size = sz + sizeof(EFI_DP_END);
 	return p;
 }
 
@@ -449,9 +391,6 @@
 	return p->sub_type == DEVICE_PATH_SUB_TYPE_INSTANCE_END;
 }
 
-/* size of device-path not including END node for device and all parents
- * up to the root device.
- */
 __maybe_unused static unsigned int dp_size(struct udevice *dev)
 {
 	if (!dev || !dev->driver)
@@ -820,29 +759,21 @@
 	return dp_part_node(buf, desc, part);
 }
 
-/* Construct a device-path from a partition on a block device: */
 struct efi_device_path *efi_dp_from_part(struct blk_desc *desc, int part)
 {
 	void *buf, *start;
 
-	start = buf = efi_alloc(dp_part_size(desc, part) + sizeof(END));
-	if (!buf)
+	start = efi_alloc(dp_part_size(desc, part) + sizeof(EFI_DP_END));
+	if (!start)
 		return NULL;
 
-	buf = dp_part_fill(buf, desc, part);
+	buf = dp_part_fill(start, desc, part);
 
-	*((struct efi_device_path *)buf) = END;
+	*((struct efi_device_path *)buf) = EFI_DP_END;
 
 	return start;
 }
 
-/*
- * Create a device node for a block device partition.
- *
- * @buf		buffer to which the device path is written
- * @desc	block device descriptor
- * @part	partition number, 0 identifies a block device
- */
 struct efi_device_path *efi_dp_part_node(struct blk_desc *desc, int part)
 {
 	efi_uintn_t dpsize;
@@ -892,13 +823,6 @@
 	*pos = 0;
 }
 
-/**
- * efi_dp_from_file() - append file path node to device path.
- *
- * @dp:		device path or NULL
- * @path:	file path or NULL
- * Return:	device path or NULL in case of an error
- */
 struct efi_device_path *efi_dp_from_file(const struct efi_device_path *dp,
 					 const char *path)
 {
@@ -912,7 +836,7 @@
 	if (fpsize > U16_MAX)
 		return NULL;
 
-	buf = efi_alloc(dpsize + fpsize + sizeof(END));
+	buf = efi_alloc(dpsize + fpsize + sizeof(EFI_DP_END));
 	if (!buf)
 		return NULL;
 
@@ -929,7 +853,7 @@
 		pos += fpsize;
 	}
 
-	memcpy(pos, &END, sizeof(END));
+	memcpy(pos, &EFI_DP_END, sizeof(EFI_DP_END));
 
 	return buf;
 }
@@ -938,7 +862,7 @@
 {
 	void *buf, *pos;
 	struct efi_device_path_uart *uart;
-	size_t dpsize = dp_size(dm_root()) + sizeof(*uart) + sizeof(END);
+	size_t dpsize = dp_size(dm_root()) + sizeof(*uart) + sizeof(EFI_DP_END);
 
 	buf = efi_alloc(dpsize);
 	if (!buf)
@@ -949,7 +873,7 @@
 	uart->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_UART;
 	uart->dp.length = sizeof(*uart);
 	pos += sizeof(*uart);
-	memcpy(pos, &END, sizeof(END));
+	memcpy(pos, &EFI_DP_END, sizeof(EFI_DP_END));
 
 	return buf;
 }
@@ -963,13 +887,13 @@
 
 	dpsize += dp_size(dev);
 
-	start = buf = efi_alloc(dpsize + sizeof(END));
-	if (!buf)
+	start = efi_alloc(dpsize + sizeof(EFI_DP_END));
+	if (!start)
 		return NULL;
 
-	buf = dp_fill(buf, dev);
+	buf = dp_fill(start, dev);
 
-	*((struct efi_device_path *)buf) = END;
+	*((struct efi_device_path *)buf) = EFI_DP_END;
 
 	return start;
 }
@@ -979,7 +903,7 @@
  *
  * Set the device path to an ethernet device path as provided by
  * efi_dp_from_eth() concatenated with a device path of subtype
- * DEVICE_PATH_SUB_TYPE_MSG_IPV4, and an END node.
+ * DEVICE_PATH_SUB_TYPE_MSG_IPV4, and an EFI_DP_END node.
  *
  * @ip:		IPv4 local address
  * @mask:	network mask
@@ -1010,7 +934,7 @@
 	if (srv)
 		memcpy(&dp.ipv4dp.remote_ip_address, srv, sizeof(*srv));
 	pos = &dp.end;
-	memcpy(pos, &END, sizeof(END));
+	memcpy(pos, &EFI_DP_END, sizeof(EFI_DP_END));
 
 	dp1 = efi_dp_from_eth(dev);
 	if (!dp1)
@@ -1023,17 +947,6 @@
 	return dp2;
 }
 
-/**
- * efi_dp_from_http() - set device path from http
- *
- * Set the device path to an IPv4 path as provided by efi_dp_from_ipv4
- * concatenated with a device path of subtype DEVICE_PATH_SUB_TYPE_MSG_URI,
- * and an END node.
- *
- * @server:	URI of remote server
- * @dev:	net udevice
- * Return:	pointer to HTTP device path, NULL on error
- */
 struct efi_device_path *efi_dp_from_http(const char *server, struct udevice *dev)
 {
 	struct efi_device_path *dp1, *dp2;
@@ -1066,7 +979,7 @@
 	}
 
 	uridp_len = sizeof(struct efi_device_path) + strlen(tmp) + 1;
-	uridp = efi_alloc(uridp_len + sizeof(END));
+	uridp = efi_alloc(uridp_len + sizeof(EFI_DP_END));
 	if (!uridp) {
 		log_err("Out of memory\n");
 		return NULL;
@@ -1078,7 +991,7 @@
 	memcpy(uridp->uri, tmp, strlen(tmp) + 1);
 
 	pos = (char *)uridp + uridp_len;
-	memcpy(pos, &END, sizeof(END));
+	memcpy(pos, &EFI_DP_END, sizeof(EFI_DP_END));
 
 	dp2 = efi_dp_concat(dp1, (const struct efi_device_path *)uridp, 0);
 
@@ -1096,11 +1009,11 @@
 	struct efi_device_path_memory *mdp;
 	void *buf, *start;
 
-	start = buf = efi_alloc(sizeof(*mdp) + sizeof(END));
-	if (!buf)
+	start = efi_alloc(sizeof(*mdp) + sizeof(EFI_DP_END));
+	if (!start)
 		return NULL;
 
-	mdp = buf;
+	mdp = start;
 	mdp->dp.type = DEVICE_PATH_TYPE_HARDWARE_DEVICE;
 	mdp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MEMORY;
 	mdp->dp.length = sizeof(*mdp);
@@ -1109,7 +1022,7 @@
 	mdp->end_address = start_address + size;
 	buf = &mdp[1];
 
-	*((struct efi_device_path *)buf) = END;
+	*((struct efi_device_path *)buf) = EFI_DP_END;
 
 	return start;
 }
diff --git a/lib/efi_loader/efi_device_path_utilities.c b/lib/efi_loader/efi_device_path_utilities.c
index 552c5bb..87d52df 100644
--- a/lib/efi_loader/efi_device_path_utilities.c
+++ b/lib/efi_loader/efi_device_path_utilities.c
@@ -7,6 +7,7 @@
 
 #define LOG_CATEGORY LOGC_EFI
 
+#include <efi_device_path.h>
 #include <efi_loader.h>
 
 const efi_guid_t efi_guid_device_path_utilities_protocol =
@@ -31,7 +32,7 @@
 	efi_uintn_t sz = 0;
 
 	EFI_ENTRY("%pD", device_path);
-	/* size includes the END node: */
+	/* size includes the EFI_DP_END node: */
 	if (device_path)
 		sz = efi_dp_size(device_path) + sizeof(struct efi_device_path);
 	return EFI_EXIT(sz);
diff --git a/lib/efi_loader/efi_disk.c b/lib/efi_loader/efi_disk.c
index 5452640..47b583c 100644
--- a/lib/efi_loader/efi_disk.c
+++ b/lib/efi_loader/efi_disk.c
@@ -11,6 +11,7 @@
 #include <dm.h>
 #include <dm/device-internal.h>
 #include <dm/tag.h>
+#include <efi_device_path.h>
 #include <event.h>
 #include <efi_driver.h>
 #include <efi_loader.h>
diff --git a/lib/efi_loader/efi_fdt.c b/lib/efi_loader/efi_fdt.c
index 1ba6641..bfaa9cf 100644
--- a/lib/efi_loader/efi_fdt.c
+++ b/lib/efi_loader/efi_fdt.c
@@ -8,6 +8,7 @@
 
 #define LOG_CATEGORY LOGC_EFI
 
+#include <efi_device_path.h>
 #include <efi_loader.h>
 #include <env.h>
 #include <errno.h>
diff --git a/lib/efi_loader/efi_helper.c b/lib/efi_loader/efi_helper.c
index 19fb5d0..44b806a 100644
--- a/lib/efi_loader/efi_helper.c
+++ b/lib/efi_loader/efi_helper.c
@@ -7,6 +7,7 @@
 
 #include <blkmap.h>
 #include <bootm.h>
+#include <efi_device_path.h>
 #include <env.h>
 #include <image.h>
 #include <log.h>
@@ -199,7 +200,7 @@
 		efi_free_pool(tmp_dp);
 		if (!*dp)
 			return EFI_OUT_OF_RESOURCES;
-		*dp_size += efi_dp_size(initrd_dp) + sizeof(END);
+		*dp_size += efi_dp_size(initrd_dp) + sizeof(EFI_DP_END);
 	}
 
 	if (fdt_dp) {
@@ -209,10 +210,10 @@
 		efi_free_pool(tmp_dp);
 		if (!*dp)
 			return EFI_OUT_OF_RESOURCES;
-		*dp_size += efi_dp_size(fdt_dp) + sizeof(END);
+		*dp_size += efi_dp_size(fdt_dp) + sizeof(EFI_DP_END);
 	}
 
-	*dp_size += sizeof(END);
+	*dp_size += sizeof(EFI_DP_END);
 
 	return EFI_SUCCESS;
 }
diff --git a/lib/efi_loader/efi_net.c b/lib/efi_loader/efi_net.c
index b3291b4..8e708d8 100644
--- a/lib/efi_loader/efi_net.c
+++ b/lib/efi_loader/efi_net.c
@@ -17,6 +17,7 @@
 
 #define LOG_CATEGORY LOGC_EFI
 
+#include <efi_device_path.h>
 #include <efi_loader.h>
 #include <dm.h>
 #include <linux/sizes.h>
@@ -51,7 +52,7 @@
 static struct wget_http_info efi_wget_info = {
 	.set_bootdev = false,
 	.check_buffer_size = true,
-
+	.silent = true,
 };
 #endif
 
diff --git a/lib/efi_loader/efi_tcg2.c b/lib/efi_loader/efi_tcg2.c
index 210a846..1832eeb 100644
--- a/lib/efi_loader/efi_tcg2.c
+++ b/lib/efi_loader/efi_tcg2.c
@@ -10,6 +10,7 @@
 #define LOG_CATEGORY LOGC_EFI
 
 #include <dm.h>
+#include <efi_device_path.h>
 #include <efi_loader.h>
 #include <efi_variable.h>
 #include <efi_tcg2.h>
diff --git a/lib/lwip/lwip/src/apps/altcp_tls/altcp_tls_mbedtls.c b/lib/lwip/lwip/src/apps/altcp_tls/altcp_tls_mbedtls.c
index ef51a5a..7459bfa 100644
--- a/lib/lwip/lwip/src/apps/altcp_tls/altcp_tls_mbedtls.c
+++ b/lib/lwip/lwip/src/apps/altcp_tls/altcp_tls_mbedtls.c
@@ -60,6 +60,8 @@
 
 #if LWIP_ALTCP_TLS && LWIP_ALTCP_TLS_MBEDTLS
 
+#include "lwip/errno.h"
+
 #include "lwip/altcp.h"
 #include "lwip/altcp_tls.h"
 #include "lwip/priv/altcp_priv.h"
@@ -299,7 +301,8 @@
       LWIP_DEBUGF(ALTCP_MBEDTLS_DEBUG, ("mbedtls_ssl_handshake failed: %d\n", ret));
       /* handshake failed, connection has to be closed */
       if (ret == MBEDTLS_ERR_X509_CERT_VERIFY_FAILED) {
-        printf("Certificate verification failed\n");
+	/* provide a cause for why the connection is closed to the called */
+        errno = EPERM;
       }
       if (conn->err) {
         conn->err(conn->arg, ERR_CLSD);
@@ -844,9 +847,6 @@
     altcp_mbedtls_free_config(conf);
     return NULL;
   }
-  if (authmode == MBEDTLS_SSL_VERIFY_NONE) {
-     printf("WARNING: no CA certificates, HTTPS connections not authenticated\n");
-  }
   mbedtls_ssl_conf_authmode(&conf->conf, authmode);
 
   mbedtls_ssl_conf_rng(&conf->conf, mbedtls_ctr_drbg_random, &altcp_tls_entropy_rng->ctr_drbg);
diff --git a/net/lwip/tftp.c b/net/lwip/tftp.c
index 4f9b204..fae701b 100644
--- a/net/lwip/tftp.c
+++ b/net/lwip/tftp.c
@@ -8,6 +8,7 @@
 #include <efi_loader.h>
 #include <image.h>
 #include <linux/delay.h>
+#include <linux/kconfig.h>
 #include <lwip/apps/tftp_client.h>
 #include <lwip/timeouts.h>
 #include <mapmem.h>
@@ -15,6 +16,8 @@
 #include <time.h>
 
 #define PROGRESS_PRINT_STEP_BYTES (10 * 1024)
+/* Max time to wait for first data packet from server */
+#define NO_RSP_TIMEOUT_MS 10000
 
 enum done_state {
 	NOT_DONE = 0,
@@ -31,6 +34,47 @@
 	enum done_state done;
 };
 
+/**
+ * store_block() - copy received data
+ *
+ * This function is called by the receive callback to copy a block of data
+ * into its final location (ctx->daddr). Before doing so, it checks if the copy
+ * is allowed.
+ *
+ * @ctx: the context for the current transfer
+ * @src: the data received from the TCP stack
+ * @len: the length of the data
+ */
+static int store_block(struct tftp_ctx *ctx, void *src, u16_t len)
+{
+	ulong store_addr = ctx->daddr;
+	void *ptr;
+
+	if (CONFIG_IS_ENABLED(LMB)) {
+		if (store_addr + len < store_addr ||
+		    lmb_read_check(store_addr, len)) {
+			puts("\nTFTP error: ");
+			puts("trying to overwrite reserved memory...\n");
+			return -1;
+		}
+	}
+
+	ptr = map_sysmem(store_addr, len);
+	memcpy(ptr, src, len);
+	unmap_sysmem(ptr);
+
+	ctx->daddr += len;
+	ctx->size += len;
+	ctx->block_count++;
+	if (ctx->block_count % 10 == 0) {
+		putc('#');
+		if (ctx->block_count % (65 * 10) == 0)
+			puts("\n\t ");
+	}
+
+	return 0;
+}
+
 static void *tftp_open(const char *fname, const char *mode, u8_t is_write)
 {
 	return NULL;
@@ -71,17 +115,9 @@
 	struct tftp_ctx *ctx = handle;
 	struct pbuf *q;
 
-	for (q = p; q; q = q->next) {
-		memcpy((void *)ctx->daddr, q->payload, q->len);
-		ctx->daddr += q->len;
-		ctx->size += q->len;
-		ctx->block_count++;
-		if (ctx->block_count % 10 == 0) {
-			putc('#');
-			if (ctx->block_count % (65 * 10) == 0)
-				puts("\n\t ");
-		}
-	}
+	for (q = p; q; q = q->next)
+		if (store_block(ctx, q->payload, q->len) < 0)
+			return -1;
 
 	return 0;
 }
@@ -106,6 +142,17 @@
 	tftp_error
 };
 
+static void no_response(void *arg)
+{
+	struct tftp_ctx *ctx = (struct tftp_ctx *)arg;
+
+	if (ctx->size)
+		return;
+
+	printf("Timeout!\n");
+	ctx->done = FAILURE;
+}
+
 static int tftp_loop(struct udevice *udev, ulong addr, char *fname,
 		     ip_addr_t srvip, uint16_t srvport)
 {
@@ -150,6 +197,7 @@
 		return -1;
 	}
 
+	sys_timeout(NO_RSP_TIMEOUT_MS, no_response, &ctx);
 	while (!ctx.done) {
 		net_lwip_rx(udev, netif);
 		sys_check_timeouts();
@@ -159,6 +207,7 @@
 			break;
 		}
 	}
+	sys_untimeout(no_response, (void *)&ctx);
 
 	tftp_cleanup();
 
diff --git a/net/lwip/wget.c b/net/lwip/wget.c
index a3b8290..ea1113e 100644
--- a/net/lwip/wget.c
+++ b/net/lwip/wget.c
@@ -6,8 +6,10 @@
 #include <display_options.h>
 #include <efi_loader.h>
 #include <image.h>
+#include <linux/kconfig.h>
 #include <lwip/apps/http_client.h>
 #include "lwip/altcp_tls.h"
+#include <lwip/errno.h>
 #include <lwip/timeouts.h>
 #include <rng.h>
 #include <mapmem.h>
@@ -201,11 +203,58 @@
 	return 0;
 }
 
+/**
+ * store_block() - copy received data
+ *
+ * This function is called by the receive callback to copy a block of data
+ * into its final location (ctx->daddr). Before doing so, it checks if the copy
+ * is allowed.
+ *
+ * @ctx: the context for the current transfer
+ * @src: the data received from the TCP stack
+ * @len: the length of the data
+ */
+static int store_block(struct wget_ctx *ctx, void *src, u16_t len)
+{
+	ulong store_addr = ctx->daddr;
+	uchar *ptr;
+
+	/* Avoid overflow */
+	if (wget_info->buffer_size && wget_info->buffer_size < ctx->size + len)
+		return -1;
+
+	if (CONFIG_IS_ENABLED(LMB) && wget_info->set_bootdev) {
+		if (store_addr + len < store_addr ||
+		    lmb_read_check(store_addr, len)) {
+			if (!wget_info->silent) {
+				printf("\nwget error: ");
+				printf("trying to overwrite reserved memory\n");
+			}
+			return -1;
+		}
+	}
+
+	ptr = map_sysmem(store_addr, len);
+	memcpy(ptr, src, len);
+	unmap_sysmem(ptr);
+
+	ctx->daddr += len;
+	ctx->size += len;
+	if (ctx->size - ctx->prevsize > PROGRESS_PRINT_STEP_BYTES) {
+		if (!wget_info->silent)
+			printf("#");
+		ctx->prevsize = ctx->size;
+	}
+
+	return 0;
+}
+
 static err_t httpc_recv_cb(void *arg, struct altcp_pcb *pcb, struct pbuf *pbuf,
 			   err_t err)
 {
 	struct wget_ctx *ctx = arg;
 	struct pbuf *buf;
+	err_t ret;
 
 	if (!pbuf)
 		return ERR_BUF;
@@ -214,18 +263,17 @@
 		ctx->start_time = get_timer(0);
 
 	for (buf = pbuf; buf; buf = buf->next) {
-		memcpy((void *)ctx->daddr, buf->payload, buf->len);
-		ctx->daddr += buf->len;
-		ctx->size += buf->len;
-		if (ctx->size - ctx->prevsize > PROGRESS_PRINT_STEP_BYTES) {
-			printf("#");
-			ctx->prevsize = ctx->size;
+		if (store_block(ctx, buf->payload, buf->len) < 0) {
+			altcp_abort(pcb);
+			ret = ERR_BUF;
+			goto out;
 		}
 	}
-
 	altcp_recved(pcb, pbuf->tot_len);
+	ret = ERR_OK;
+out:
 	pbuf_free(pbuf);
-	return ERR_OK;
+	return ret;
 }
 
 static void httpc_result_cb(void *arg, httpc_result_t httpc_result,
@@ -255,11 +303,15 @@
 	elapsed = get_timer(ctx->start_time);
 	if (!elapsed)
 		elapsed = 1;
-	if (rx_content_len > PROGRESS_PRINT_STEP_BYTES)
-		printf("\n");
-	printf("%u bytes transferred in %lu ms (", rx_content_len, elapsed);
-	print_size(rx_content_len / elapsed * 1000, "/s)\n");
-	printf("Bytes transferred = %lu (%lx hex)\n", ctx->size, ctx->size);
+	if (!wget_info->silent) {
+		if (rx_content_len > PROGRESS_PRINT_STEP_BYTES)
+			printf("\n");
+		printf("%u bytes transferred in %lu ms (", rx_content_len,
+		       elapsed);
+		print_size(rx_content_len / elapsed * 1000, "/s)\n");
+		printf("Bytes transferred = %lu (%lx hex)\n", ctx->size,
+		       ctx->size);
+	}
 	if (wget_info->set_bootdev)
 		efi_set_bootdev("Http", ctx->server_name, ctx->path, map_sysmem(ctx->saved_daddr, 0),
 				rx_content_len);
@@ -339,7 +391,8 @@
 	mbedtls_x509_crt_init(&crt);
 	ret = mbedtls_x509_crt_parse(&crt, cacert, cacert_size);
 	if (ret) {
-		printf("Could not parse certificates (%d)\n", ret);
+		if (!wget_info->silent)
+			printf("Could not parse certificates (%d)\n", ret);
 		free(cacert);
 		cacert = NULL;
 		cacert_size = 0;
@@ -372,13 +425,14 @@
 #endif
 #endif  /* CONFIG_WGET_CACERT || CONFIG_WGET_BUILTIN_CACERT */
 
-static int wget_loop(struct udevice *udev, ulong dst_addr, char *uri)
+int wget_do_request(ulong dst_addr, char *uri)
 {
 #if CONFIG_IS_ENABLED(WGET_HTTPS)
 	altcp_allocator_t tls_allocator;
 #endif
 	httpc_connection_t conn;
 	httpc_state_t *state;
+	struct udevice *udev;
 	struct netif *netif;
 	struct wget_ctx ctx;
 	char *path;
@@ -394,6 +448,14 @@
 	if (parse_url(uri, ctx.server_name, &ctx.port, &path, &is_https))
 		return CMD_RET_USAGE;
 
+	if (net_lwip_eth_start() < 0)
+		return CMD_RET_FAILURE;
+
+	if (!wget_info)
+		wget_info = &default_wget_info;
+
+	udev = eth_get_dev();
+
 	netif = net_lwip_new_netif(udev);
 	if (!netif)
 		return -1;
@@ -413,9 +475,10 @@
 
 		if (cacert_auth_mode == AUTH_REQUIRED) {
 			if (!ca || !ca_sz) {
-				printf("Error: cacert authentication mode is "
-				       "'required' but no CA certificates "
-				       "given\n");
+				if (!wget_info->silent)
+					printf("Error: cacert authentication "
+					       "mode is 'required' but no CA "
+					       "certificates given\n");
 				return CMD_RET_FAILURE;
 		       }
 		} else if (cacert_auth_mode == AUTH_NONE) {
@@ -430,6 +493,10 @@
 			 */
 		}
 
+		if (!ca && !wget_info->silent) {
+			printf("WARNING: no CA certificates, ");
+			printf("HTTPS connections not authenticated\n");
+		}
 		tls_allocator.alloc = &altcp_tls_alloc;
 		tls_allocator.arg =
 			altcp_tls_create_config_client(ca, ca_sz,
@@ -454,6 +521,8 @@
 		return CMD_RET_FAILURE;
 	}
 
+	errno = 0;
+
 	while (!ctx.done) {
 		net_lwip_rx(udev, netif);
 		sys_check_timeouts();
@@ -466,21 +535,10 @@
 	if (ctx.done == SUCCESS)
 		return 0;
 
-	return -1;
-}
-
-int wget_do_request(ulong dst_addr, char *uri)
-{
-	int ret;
-
-	ret = net_lwip_eth_start();
-	if (ret < 0)
-		return ret;
+	if (errno == EPERM && !wget_info->silent)
+		printf("Certificate verification failed\n");
 
-	if (!wget_info)
-		wget_info = &default_wget_info;
-
-	return wget_loop(eth_get_dev(), dst_addr, uri);
+	return -1;
 }
 
 int do_wget(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[])
diff --git a/net/wget.c b/net/wget.c
index c73836c..3c0fff4 100644
--- a/net/wget.c
+++ b/net/wget.c
@@ -59,8 +59,10 @@
 	if (CONFIG_IS_ENABLED(LMB) && wget_info->set_bootdev) {
 		if (store_addr < image_load_addr ||
 		    lmb_read_check(store_addr, len)) {
-			printf("\nwget error: ");
-			printf("trying to overwrite reserved memory...\n");
+			if (!wget_info->silent) {
+				printf("\nwget error: ");
+				printf("trying to overwrite reserved memory\n");
+			}
 			return -1;
 		}
 	}
@@ -76,6 +78,9 @@
 {
 	int cnt;
 
+	if (wget_info->silent)
+		return;
+
 	if (content_length != -1) {
 		if (net_boot_file_size > content_length)
 			content_length = net_boot_file_size;
@@ -101,11 +106,15 @@
 	net_set_state(wget_loop_state);
 	if (wget_loop_state != NETLOOP_SUCCESS) {
 		net_boot_file_size = 0;
-		printf("\nwget: Transfer Fail, TCP status - %d\n", tcp->status);
+		if (!wget_info->silent)
+			printf("\nwget: Transfer Fail, TCP status - %d\n",
+			       tcp->status);
 		return;
 	}
 
-	printf("\nPackets received %d, Transfer Successful\n", tcp->rx_packets);
+	if (!wget_info->silent)
+		printf("\nPackets received %d, Transfer Successful\n",
+		       tcp->rx_packets);
 	wget_info->file_size = net_boot_file_size;
 	if (wget_info->method == WGET_HTTP_METHOD_GET && wget_info->set_bootdev) {
 		efi_set_bootdev("Http", NULL, image_url,
@@ -139,7 +148,8 @@
 		    tcp->state == TCP_ESTABLISHED)
 			goto end;
 
-		printf("ERROR: misssed HTTP header\n");
+		if (!wget_info->silent)
+			printf("ERROR: misssed HTTP header\n");
 		tcp_stream_close(tcp);
 		goto end;
 	}
@@ -346,7 +356,8 @@
 	tcp_stream_set_on_create_handler(tcp_stream_on_create);
 	tcp = tcp_stream_connect(web_server_ip, server_port);
 	if (!tcp) {
-		printf("No free tcp streams\n");
+		if (!wget_info->silent)
+			printf("No free tcp streams\n");
 		net_set_state(NETLOOP_FAIL);
 		return;
 	}
diff --git a/test/lib/efi_device_path.c b/test/lib/efi_device_path.c
index 5cc001e..5a358dd 100644
--- a/test/lib/efi_device_path.c
+++ b/test/lib/efi_device_path.c
@@ -5,6 +5,7 @@
  * Copyright (c) 2020 Heinrich Schuchardt <xypron.glpk@gmx.de>
  */
 
+#include <efi_device_path.h>
 #include <efi_loader.h>
 #include <test/lib.h>
 #include <test/test.h>
diff --git a/test/py/conftest.py b/test/py/conftest.py
index 5aea856..6c3ac67 100644
--- a/test/py/conftest.py
+++ b/test/py/conftest.py
@@ -711,9 +711,13 @@
     """
 
     for options in item.iter_markers('buildconfigspec'):
-        option = options.args[0]
-        if not ubconfig.buildconfig.get('config_' + option.lower(), None):
-            pytest.skip('.config feature "%s" not enabled' % option.lower())
+        nomatch = True
+        for arg in options.args:
+            if ubconfig.buildconfig.get('config_' + arg.lower(), None):
+                nomatch = False
+        if nomatch:
+            argsString = ', '.join(options.args)
+            pytest.skip(f'.config features "{argsString}" not enabled')
     for options in item.iter_markers('notbuildconfigspec'):
         option = options.args[0]
         if ubconfig.buildconfig.get('config_' + option.lower(), None):
diff --git a/test/py/tests/test_efi_loader.py b/test/py/tests/test_efi_loader.py
index 91f151d..dc58c0d 100644
--- a/test/py/tests/test_efi_loader.py
+++ b/test/py/tests/test_efi_loader.py
@@ -98,7 +98,7 @@
     global net_set_up
     net_set_up = True
 
-@pytest.mark.buildconfigspec('net')
+@pytest.mark.buildconfigspec('net', 'net_lwip')
 def test_efi_setup_static(ubman):
     """Set up the network using a static IP configuration.
 
diff --git a/test/py/tests/test_fpga.py b/test/py/tests/test_fpga.py
index 74cd42b..299a865 100644
--- a/test/py/tests/test_fpga.py
+++ b/test/py/tests/test_fpga.py
@@ -506,7 +506,7 @@
 @pytest.mark.buildconfigspec('cmd_fpga_load_secure')
 @pytest.mark.buildconfigspec('cmd_net')
 @pytest.mark.buildconfigspec('cmd_dhcp')
-@pytest.mark.buildconfigspec('net')
+@pytest.mark.buildconfigspec('net', 'net_lwip')
 def test_fpga_secure_bit_auth(ubman):
 
     test_net.test_net_dhcp(ubman)
@@ -534,7 +534,7 @@
 @pytest.mark.buildconfigspec('cmd_fpga_load_secure')
 @pytest.mark.buildconfigspec('cmd_net')
 @pytest.mark.buildconfigspec('cmd_dhcp')
-@pytest.mark.buildconfigspec('net')
+@pytest.mark.buildconfigspec('net', 'net_lwip')
 def test_fpga_secure_bit_img_auth_kup(ubman):
 
     test_net.test_net_dhcp(ubman)
diff --git a/test/py/tests/test_net.py b/test/py/tests/test_net.py
index 27cdd73..6ef02e5 100644
--- a/test/py/tests/test_net.py
+++ b/test/py/tests/test_net.py
@@ -201,7 +201,7 @@
     global net6_set_up
     net6_set_up = True
 
-@pytest.mark.buildconfigspec('net')
+@pytest.mark.buildconfigspec('net', 'net_lwip')
 def test_net_setup_static(ubman):
     """Set up a static IP configuration.