Merge tag 'efi-2024-04-rc1-4' of https://source.denx.de/u-boot/custodians/u-boot-efi

Pull request efi-2024-04-rc1-4

Documentation:

* uefi: remove ".py" suffix for pytest.py command
* correct date in migration plan for CONFIG_DM

UEFI:

* fix conflicting SPDX license in RISC-V EFI linker scripts
* page align EFI binary section in RISC-V EFI binaries
* separate .data and .text sections of RISC-V EFI binaries
* use common function to get EFI configuration table
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index abd7c6c..59e4d4d 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -1160,14 +1160,14 @@
 	select SPL_SEPARATE_BSS if SPL
 	select SPL_STACK_R if SPL
 	select SPL_SYS_MALLOC_SIMPLE if SPL
-	select SPL_SYS_THUMB_BUILD if !ARM64
+	select SPL_SYS_THUMB_BUILD if SPL && !ARM64
 	select SUNXI_GPIO
 	select SYS_NS16550
 	select SYS_THUMB_BUILD if !ARM64
 	select USB if DISTRO_DEFAULTS
 	select USB_KEYBOARD if DISTRO_DEFAULTS && USB_HOST
 	select USB_STORAGE if DISTRO_DEFAULTS && USB_HOST
-	select SPL_USE_TINY_PRINTF
+	select SPL_USE_TINY_PRINTF if SPL
 	select USE_PREBOOT
 	select SYS_RELOC_GD_ENV_ADDR
 	imply BOARD_LATE_INIT
diff --git a/arch/arm/dts/Makefile b/arch/arm/dts/Makefile
index b5c588c..50f35e3 100644
--- a/arch/arm/dts/Makefile
+++ b/arch/arm/dts/Makefile
@@ -838,6 +838,7 @@
 dtb-$(CONFIG_MACH_SUN50I_H616) += \
 	sun50i-h616-orangepi-zero2.dtb \
 	sun50i-h618-orangepi-zero3.dtb \
+	sun50i-h618-transpeed-8k618-t.dtb \
 	sun50i-h616-x96-mate.dtb
 dtb-$(CONFIG_MACH_SUN50I) += \
 	sun50i-a64-amarula-relic.dtb \
diff --git a/arch/arm/dts/sun50i-h616-bigtreetech-cb1-manta.dts b/arch/arm/dts/sun50i-h616-bigtreetech-cb1-manta.dts
new file mode 100644
index 0000000..dbce61b
--- /dev/null
+++ b/arch/arm/dts/sun50i-h616-bigtreetech-cb1-manta.dts
@@ -0,0 +1,35 @@
+// SPDX-License-Identifier: (GPL-2.0+ or MIT)
+/*
+ * Copyright (C) 2023 Martin Botka <martin.botka@somainline.org>.
+ */
+
+/dts-v1/;
+
+#include "sun50i-h616-bigtreetech-cb1.dtsi"
+
+/ {
+	model = "BigTreeTech CB1";
+	compatible = "bigtreetech,cb1-manta", "bigtreetech,cb1", "allwinner,sun50i-h616";
+
+	aliases {
+		serial0 = &uart0;
+	};
+
+	chosen {
+		stdout-path = "serial0:115200n8";
+	};
+};
+
+&ehci1 {
+	status = "okay";
+};
+
+&ohci1 {
+	status = "okay";
+};
+
+&uart0 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&uart0_ph_pins>;
+	status = "okay";
+};
diff --git a/arch/arm/dts/sun50i-h616-bigtreetech-cb1.dtsi b/arch/arm/dts/sun50i-h616-bigtreetech-cb1.dtsi
new file mode 100644
index 0000000..1fed2b4
--- /dev/null
+++ b/arch/arm/dts/sun50i-h616-bigtreetech-cb1.dtsi
@@ -0,0 +1,138 @@
+// SPDX-License-Identifier: (GPL-2.0+ or MIT)
+/*
+ * Copyright (C) 2023 Martin Botka <martin.botka@somainline.org>.
+ */
+
+/dts-v1/;
+
+#include "sun50i-h616.dtsi"
+
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+#include <dt-bindings/leds/common.h>
+
+/ {
+	aliases {
+		ethernet0 = &rtl8189ftv;
+	};
+
+	leds {
+		compatible = "gpio-leds";
+
+		led-0 {
+			function = LED_FUNCTION_STATUS;
+			color = <LED_COLOR_ID_GREEN>;
+			gpios = <&pio 7 5 GPIO_ACTIVE_HIGH>; /* PH5 */
+		};
+	};
+
+	reg_vcc5v: regulator-vcc5v {
+		/* board wide 5V supply from carrier boards */
+		compatible = "regulator-fixed";
+		regulator-name = "vcc-5v";
+		regulator-min-microvolt = <5000000>;
+		regulator-max-microvolt = <5000000>;
+		regulator-always-on;
+	};
+
+	reg_vcc33_wifi: vcc33-wifi {
+		compatible = "regulator-fixed";
+		regulator-name = "vcc33-wifi";
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+		regulator-always-on;
+		vin-supply = <&reg_vcc5v>;
+	};
+
+	reg_vcc_wifi_io: vcc-wifi-io {
+		compatible = "regulator-fixed";
+		regulator-name = "vcc-wifi-io";
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <1800000>;
+		regulator-always-on;
+		vin-supply = <&reg_vcc33_wifi>;
+	};
+
+	wifi_pwrseq: wifi-pwrseq {
+		compatible = "mmc-pwrseq-simple";
+		clocks = <&rtc 1>;
+		clock-names = "ext_clock";
+		reset-gpios = <&pio 6 18 GPIO_ACTIVE_LOW>; /* PG18 */
+		post-power-on-delay-ms = <200>;
+	};
+};
+
+&mmc0 {
+	vmmc-supply = <&reg_dldo1>;
+	/* Card detection pin is not connected */
+	broken-cd;
+	bus-width = <4>;
+	status = "okay";
+};
+
+&mmc1 {
+	vmmc-supply = <&reg_vcc33_wifi>;
+	vqmmc-supply = <&reg_vcc_wifi_io>;
+	mmc-pwrseq = <&wifi_pwrseq>;
+	bus-width = <4>;
+	non-removable;
+	mmc-ddr-1_8v;
+	status = "okay";
+
+	rtl8189ftv: wifi@1 {
+		reg = <1>;
+	};
+};
+
+&r_i2c {
+	status = "okay";
+
+	axp313a: pmic@36 {
+		compatible = "x-powers,axp313a";
+		reg = <0x36>;
+		interrupt-controller;
+		#interrupt-cells = <1>;
+
+		regulators{
+			reg_dcdc1: dcdc1 {
+				regulator-name = "vdd-gpu-sys";
+				regulator-min-microvolt = <810000>;
+				regulator-max-microvolt = <990000>;
+				regulator-always-on;
+			};
+
+			reg_dcdc2: dcdc2 {
+				regulator-name = "vdd-cpu";
+				regulator-min-microvolt = <810000>;
+				regulator-max-microvolt = <1100000>;
+				regulator-ramp-delay = <200>;
+				regulator-always-on;
+			};
+
+			reg_dcdc3: dcdc3 {
+				regulator-name = "vcc-dram";
+				regulator-min-microvolt = <1350000>;
+				regulator-max-microvolt = <1500000>;
+				regulator-always-on;
+			};
+
+			reg_aldo1: aldo1 {
+				regulator-name = "vcc-1v8-pll";
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <1800000>;
+				regulator-always-on;
+			};
+
+			reg_dldo1: dldo1 {
+				regulator-name = "vcc-3v3-io";
+				regulator-min-microvolt = <3300000>;
+				regulator-max-microvolt = <3300000>;
+				regulator-always-on;
+			};
+		};
+	};
+};
+
+&usbphy {
+	status = "okay";
+};
diff --git a/arch/arm/dts/sun50i-h616-bigtreetech-pi.dts b/arch/arm/dts/sun50i-h616-bigtreetech-pi.dts
new file mode 100644
index 0000000..832f08b
--- /dev/null
+++ b/arch/arm/dts/sun50i-h616-bigtreetech-pi.dts
@@ -0,0 +1,63 @@
+// SPDX-License-Identifier: (GPL-2.0+ or MIT)
+/*
+ * Copyright (C) 2023 Martin Botka <martin@biqu3d.com>.
+ */
+
+/dts-v1/;
+
+#include "sun50i-h616-bigtreetech-cb1.dtsi"
+
+/ {
+	model = "BigTreeTech Pi";
+	compatible = "bigtreetech,pi", "allwinner,sun50i-h616";
+
+	aliases {
+		serial0 = &uart0;
+	};
+
+	chosen {
+		stdout-path = "serial0:115200n8";
+	};
+};
+
+&ehci0 {
+	status = "okay";
+};
+
+&ehci1 {
+	status = "okay";
+};
+
+&ehci2 {
+	status = "okay";
+};
+
+&ehci3 {
+	status = "okay";
+};
+
+&ir {
+	status = "okay";
+};
+
+&ohci0 {
+	status = "okay";
+};
+
+&ohci1 {
+	status = "okay";
+};
+
+&ohci2 {
+	status = "okay";
+};
+
+&ohci3 {
+	status = "okay";
+};
+
+&uart0 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&uart0_ph_pins>;
+	status = "okay";
+};
diff --git a/arch/arm/dts/sun50i-h616.dtsi b/arch/arm/dts/sun50i-h616.dtsi
index 74aed0d..d549d27 100644
--- a/arch/arm/dts/sun50i-h616.dtsi
+++ b/arch/arm/dts/sun50i-h616.dtsi
@@ -133,6 +133,13 @@
 			#reset-cells = <1>;
 		};
 
+		sid: efuse@3006000 {
+			compatible = "allwinner,sun50i-h616-sid", "allwinner,sun50i-a64-sid";
+			reg = <0x03006000 0x1000>;
+			#address-cells = <1>;
+			#size-cells = <1>;
+		};
+
 		watchdog: watchdog@30090a0 {
 			compatible = "allwinner,sun50i-h616-wdt",
 				     "allwinner,sun6i-a31-wdt";
diff --git a/arch/arm/dts/sun50i-h618-orangepi-zero2w.dts b/arch/arm/dts/sun50i-h618-orangepi-zero2w.dts
new file mode 100644
index 0000000..21ca197
--- /dev/null
+++ b/arch/arm/dts/sun50i-h618-orangepi-zero2w.dts
@@ -0,0 +1,176 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Copyright (C) 2023 Arm Ltd.
+ */
+
+/dts-v1/;
+
+#include "sun50i-h616.dtsi"
+
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+#include <dt-bindings/leds/common.h>
+
+/ {
+	model = "OrangePi Zero 2W";
+	compatible = "xunlong,orangepi-zero2w", "allwinner,sun50i-h618";
+
+	aliases {
+		serial0 = &uart0;
+	};
+
+	chosen {
+		stdout-path = "serial0:115200n8";
+	};
+
+	leds {
+		compatible = "gpio-leds";
+
+		led-0 {
+			function = LED_FUNCTION_STATUS;
+			color = <LED_COLOR_ID_GREEN>;
+			gpios = <&pio 2 13 GPIO_ACTIVE_HIGH>; /* PC13 */
+		};
+	};
+
+	reg_vcc5v: vcc5v {
+		/* board wide 5V supply directly from the USB-C socket */
+		compatible = "regulator-fixed";
+		regulator-name = "vcc-5v";
+		regulator-min-microvolt = <5000000>;
+		regulator-max-microvolt = <5000000>;
+		regulator-always-on;
+	};
+
+	reg_vcc3v3: vcc3v3 {
+		/* SY8089 DC/DC converter */
+		compatible = "regulator-fixed";
+		regulator-name = "vcc-3v3";
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+		vin-supply = <&reg_vcc5v>;
+		regulator-always-on;
+	};
+};
+
+&ehci1 {
+	status = "okay";
+};
+
+/* USB 2 & 3 are on the FPC connector (or the exansion board) */
+
+&mmc0 {
+	cd-gpios = <&pio 5 6 GPIO_ACTIVE_LOW>;	/* PF6 */
+	bus-width = <4>;
+	vmmc-supply = <&reg_vcc3v3>;
+	status = "okay";
+};
+
+&ohci1 {
+	status = "okay";
+};
+
+&pio {
+	vcc-pc-supply = <&reg_dldo1>;
+	vcc-pf-supply = <&reg_dldo1>;	/* internally via VCC-IO */
+	vcc-pg-supply = <&reg_aldo1>;
+	vcc-ph-supply = <&reg_dldo1>;	/* internally via VCC-IO */
+	vcc-pi-supply = <&reg_dldo1>;
+};
+
+&r_i2c {
+	status = "okay";
+
+	axp313: pmic@36 {
+		compatible = "x-powers,axp313a";
+		reg = <0x36>;
+		#interrupt-cells = <1>;
+		interrupt-controller;
+		interrupt-parent = <&pio>;
+		interrupts = <2 9 IRQ_TYPE_LEVEL_LOW>;	/* PC9 */
+
+		vin1-supply = <&reg_vcc5v>;
+		vin2-supply = <&reg_vcc5v>;
+		vin3-supply = <&reg_vcc5v>;
+
+		regulators {
+			/* Supplies VCC-PLL and DRAM */
+			reg_aldo1: aldo1 {
+				regulator-always-on;
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <1800000>;
+				regulator-name = "vcc1v8";
+			};
+
+			/* Supplies VCC-IO, so needs to be always on. */
+			reg_dldo1: dldo1 {
+				regulator-always-on;
+				regulator-min-microvolt = <3300000>;
+				regulator-max-microvolt = <3300000>;
+				regulator-name = "vcc3v3";
+			};
+
+			reg_dcdc1: dcdc1 {
+				regulator-always-on;
+				regulator-min-microvolt = <810000>;
+				regulator-max-microvolt = <990000>;
+				regulator-name = "vdd-gpu-sys";
+			};
+
+			reg_dcdc2: dcdc2 {
+				regulator-always-on;
+				regulator-min-microvolt = <810000>;
+				regulator-max-microvolt = <1100000>;
+				regulator-name = "vdd-cpu";
+			};
+
+			reg_dcdc3: dcdc3 {
+				regulator-always-on;
+				regulator-min-microvolt = <1100000>;
+				regulator-max-microvolt = <1100000>;
+				regulator-name = "vdd-dram";
+			};
+		};
+	};
+};
+
+&spi0  {
+	status = "okay";
+	pinctrl-names = "default";
+	pinctrl-0 = <&spi0_pins>, <&spi0_cs0_pin>;
+
+	flash@0 {
+		#address-cells = <1>;
+		#size-cells = <1>;
+		compatible = "jedec,spi-nor";
+		reg = <0>;
+		spi-max-frequency = <40000000>;
+	};
+};
+
+&uart0 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&uart0_ph_pins>;
+	status = "okay";
+};
+
+&usbotg {
+	/*
+	 * PHY0 pins are connected to a USB-C socket, but a role switch
+	 * is not implemented: both CC pins are pulled to GND.
+	 * The VBUS pins power the device, so a fixed peripheral mode
+	 * is the best choice.
+	 * The board can be powered via GPIOs, in this case port0 *can*
+	 * act as a host (with a cable/adapter ignoring CC), as VBUS is
+	 * then provided by the GPIOs. Any user of this setup would
+	 * need to adjust the DT accordingly: dr_mode set to "host",
+	 * enabling OHCI0 and EHCI0.
+	 */
+	dr_mode = "peripheral";
+	status = "okay";
+};
+
+&usbphy {
+	usb1_vbus-supply = <&reg_vcc5v>;
+	status = "okay";
+};
diff --git a/arch/arm/dts/sun50i-h618-transpeed-8k618-t.dts b/arch/arm/dts/sun50i-h618-transpeed-8k618-t.dts
new file mode 100644
index 0000000..8ea1fd4
--- /dev/null
+++ b/arch/arm/dts/sun50i-h618-transpeed-8k618-t.dts
@@ -0,0 +1,161 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Copyright (C) 2023 Arm Ltd.
+ */
+
+/dts-v1/;
+
+#include "sun50i-h616.dtsi"
+
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+
+/ {
+	model = "Transpeed 8K618-T";
+	compatible = "transpeed,8k618-t", "allwinner,sun50i-h618";
+
+	aliases {
+		serial0 = &uart0;
+	};
+
+	chosen {
+		stdout-path = "serial0:115200n8";
+	};
+
+	reg_vcc5v: vcc5v {
+		/* board wide 5V supply directly from the DC input */
+		compatible = "regulator-fixed";
+		regulator-name = "vcc-5v";
+		regulator-min-microvolt = <5000000>;
+		regulator-max-microvolt = <5000000>;
+		regulator-always-on;
+	};
+
+	reg_vcc3v3: vcc3v3 {
+		/* discrete 3.3V regulator */
+		compatible = "regulator-fixed";
+		regulator-name = "vcc-3v3";
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+		regulator-always-on;
+	};
+};
+
+&ehci0 {
+	status = "okay";
+};
+
+&ehci1 {
+	status = "okay";
+};
+
+&ir {
+	status = "okay";
+};
+
+&mmc0 {
+	vmmc-supply = <&reg_dldo1>;
+	cd-gpios = <&pio 8 16 GPIO_ACTIVE_LOW>;	/* PI16 */
+	bus-width = <4>;
+	status = "okay";
+};
+
+&mmc2 {
+	vmmc-supply = <&reg_dldo1>;
+	vqmmc-supply = <&reg_aldo1>;
+	bus-width = <8>;
+	non-removable;
+	cap-mmc-hw-reset;
+	mmc-ddr-1_8v;
+	mmc-hs200-1_8v;
+	status = "okay";
+};
+
+&ohci0 {
+	status = "okay";
+};
+
+&ohci1 {
+	status = "okay";
+};
+
+&r_i2c {
+	status = "okay";
+
+	axp313: pmic@36 {
+		compatible = "x-powers,axp313a";
+		reg = <0x36>;
+		#interrupt-cells = <1>;
+		interrupt-controller;
+
+		vin1-supply = <&reg_vcc5v>;
+		vin2-supply = <&reg_vcc5v>;
+		vin3-supply = <&reg_vcc5v>;
+
+		regulators {
+			reg_aldo1: aldo1 {
+				regulator-always-on;
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <1800000>;
+				regulator-name = "vcc-1v8-pll";
+			};
+
+			reg_dldo1: dldo1 {
+				regulator-always-on;
+				regulator-min-microvolt = <3300000>;
+				regulator-max-microvolt = <3300000>;
+				regulator-name = "vcc-3v3-io-mmc";
+			};
+
+			reg_dcdc1: dcdc1 {
+				regulator-always-on;
+				regulator-min-microvolt = <810000>;
+				regulator-max-microvolt = <990000>;
+				regulator-name = "vdd-gpu-sys";
+			};
+
+			reg_dcdc2: dcdc2 {
+				regulator-always-on;
+				regulator-min-microvolt = <810000>;
+				regulator-max-microvolt = <1100000>;
+				regulator-name = "vdd-cpu";
+			};
+
+			reg_dcdc3: dcdc3 {
+				regulator-always-on;
+				regulator-min-microvolt = <1360000>;
+				regulator-max-microvolt = <1360000>;
+				regulator-name = "vdd-dram";
+			};
+		};
+	};
+};
+
+&pio {
+	vcc-pc-supply = <&reg_aldo1>;
+	vcc-pg-supply = <&reg_dldo1>;
+	vcc-ph-supply = <&reg_dldo1>;
+	vcc-pi-supply = <&reg_dldo1>;
+};
+
+&uart0 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&uart0_ph_pins>;
+	status = "okay";
+};
+
+&uart1 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&uart1_pins>, <&uart1_rts_cts_pins>;
+	uart-has-rtscts;
+	status = "okay";
+};
+
+&usbotg {
+	dr_mode = "host";	/* USB A type receptable */
+	status = "okay";
+};
+
+&usbphy {
+	status = "okay";
+};
diff --git a/arch/arm/dts/sun8i-r40.dtsi b/arch/arm/dts/sun8i-r40.dtsi
index 4ef26d8..a5b1f1e 100644
--- a/arch/arm/dts/sun8i-r40.dtsi
+++ b/arch/arm/dts/sun8i-r40.dtsi
@@ -338,6 +338,8 @@
 			resets = <&ccu RST_BUS_VE>;
 			interrupts = <GIC_SPI 53 IRQ_TYPE_LEVEL_HIGH>;
 			allwinner,sram = <&ve_sram 1>;
+			interconnects = <&mbus 4>;
+			interconnect-names = "dma-mem";
 		};
 
 		mmc0: mmc@1c0f000 {
diff --git a/arch/arm/dts/sun8i-v3s-anbernic-rg-nano.dts b/arch/arm/dts/sun8i-v3s-anbernic-rg-nano.dts
new file mode 100644
index 0000000..f34dfdf
--- /dev/null
+++ b/arch/arm/dts/sun8i-v3s-anbernic-rg-nano.dts
@@ -0,0 +1,276 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+
+/dts-v1/;
+#include <dt-bindings/input/linux-event-codes.h>
+#include "sun8i-v3s.dtsi"
+#include "sunxi-common-regulators.dtsi"
+
+/ {
+	model = "Anbernic RG Nano";
+	compatible = "anbernic,rg-nano", "allwinner,sun8i-v3s";
+
+	aliases {
+		rtc0 = &pcf8563;
+		rtc1 = &rtc;
+		serial0 = &uart0;
+	};
+
+	backlight: backlight {
+		compatible = "pwm-backlight";
+		brightness-levels = <0 1 2 3 8 14 21 32 46 60 80 100>;
+		default-brightness-level = <11>;
+		power-supply = <&reg_vcc5v0>;
+		pwms = <&pwm 0 40000 1>;
+	};
+
+	chosen {
+		stdout-path = "serial0:115200n8";
+	};
+
+	gpio_keys: gpio-keys {
+		compatible = "gpio-keys";
+
+		button-a {
+			gpios = <&gpio_expander 12 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>;
+			label = "BTN-A";
+			linux,code = <BTN_EAST>;
+		};
+
+		button-b {
+			gpios = <&gpio_expander 14 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>;
+			label = "BTN-B";
+			linux,code = <BTN_SOUTH>;
+		};
+
+		button-down {
+			gpios = <&gpio_expander 1 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>;
+			label = "DPAD-DOWN";
+			linux,code = <BTN_DPAD_DOWN>;
+		};
+
+		button-left {
+			gpios = <&gpio_expander 4 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>;
+			label = "DPAD-LEFT";
+			linux,code = <BTN_DPAD_LEFT>;
+		};
+
+		button-right {
+			gpios = <&gpio_expander 0 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>;
+			label = "DPAD-RIGHT";
+			linux,code = <BTN_DPAD_RIGHT>;
+		};
+
+		button-se {
+			gpios = <&gpio_expander 7 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>;
+			label = "BTN-SELECT";
+			linux,code = <BTN_SELECT>;
+		};
+
+		button-st {
+			gpios = <&gpio_expander 6 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>;
+			label = "BTN-START";
+			linux,code = <BTN_START>;
+		};
+
+		button-tl {
+			gpios = <&gpio_expander 2 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>;
+			label = "BTN-L";
+			linux,code = <BTN_TL>;
+		};
+
+		button-tr {
+			gpios = <&gpio_expander 15 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>;
+			label = "BTN-R";
+			linux,code = <BTN_TR>;
+		};
+
+		button-up {
+			gpios = <&gpio_expander 3 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>;
+			label = "DPAD-UP";
+			linux,code = <BTN_DPAD_UP>;
+		};
+
+		button-x {
+			gpios = <&gpio_expander 11 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>;
+			label = "BTN-X";
+			linux,code = <BTN_NORTH>;
+		};
+
+		button-y {
+			gpios = <&gpio_expander 13 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>;
+			label = "BTN-Y";
+			linux,code = <BTN_WEST>;
+		};
+	};
+};
+
+&codec {
+	allwinner,audio-routing = "Speaker", "HP",
+				  "MIC1", "Mic",
+				  "Mic", "HBIAS";
+	allwinner,pa-gpios = <&pio 5 6 (GPIO_ACTIVE_HIGH | GPIO_PULL_UP)>; /* PF6 */
+	status = "okay";
+};
+
+&ehci {
+	status = "okay";
+};
+
+&i2c0 {
+	status = "okay";
+
+	gpio_expander: gpio@20 {
+		compatible = "nxp,pcal6416";
+		reg = <0x20>;
+		gpio-controller;
+		#gpio-cells = <2>;
+		#interrupt-cells = <2>;
+		interrupt-controller;
+		interrupt-parent = <&pio>;
+		interrupts = <1 3 IRQ_TYPE_EDGE_BOTH>; /* PB3/EINT3 */
+		vcc-supply = <&reg_vcc3v3>;
+	};
+
+	axp209: pmic@34 {
+		reg = <0x34>;
+		interrupt-parent = <&pio>;
+		interrupts = <1 5 IRQ_TYPE_EDGE_FALLING>; /* PB5/EINT5 */
+	};
+
+	pcf8563: rtc@51 {
+		compatible = "nxp,pcf8563";
+		reg = <0x51>;
+	};
+};
+
+#include "axp209.dtsi"
+
+&battery_power_supply {
+	status = "okay";
+};
+
+&mmc0 {
+	broken-cd;
+	bus-width = <4>;
+	disable-wp;
+	vmmc-supply = <&reg_vcc3v3>;
+	vqmmc-supply = <&reg_vcc3v3>;
+	status = "okay";
+};
+
+&ohci {
+	status = "okay";
+};
+
+&pio {
+	vcc-pb-supply = <&reg_vcc3v3>;
+	vcc-pc-supply = <&reg_vcc3v3>;
+	vcc-pf-supply = <&reg_vcc3v3>;
+	vcc-pg-supply = <&reg_vcc3v3>;
+
+	spi0_no_miso_pins: spi0-no-miso-pins {
+		pins = "PC1", "PC2", "PC3";
+		function = "spi0";
+	};
+};
+
+&pwm {
+	pinctrl-0 = <&pwm0_pin>;
+	pinctrl-names = "default";
+	status = "okay";
+};
+
+/* DCDC2 wired into vdd-cpu, vdd-sys, and vdd-ephy. */
+&reg_dcdc2 {
+	regulator-always-on;
+	regulator-max-microvolt = <1250000>;
+	regulator-min-microvolt = <1250000>;
+	regulator-name = "vdd-cpu";
+};
+
+/* DCDC3 wired into every 3.3v input that isn't the RTC. */
+&reg_dcdc3 {
+	regulator-always-on;
+	regulator-max-microvolt = <3300000>;
+	regulator-min-microvolt = <3300000>;
+	regulator-name = "vcc-io";
+};
+
+/* LDO1 wired into RTC, voltage is hard-wired at 3.3v. */
+&reg_ldo1 {
+	regulator-always-on;
+	regulator-name = "vcc-rtc";
+};
+
+/* LDO2 wired into VCC-PLL and audio codec. */
+&reg_ldo2 {
+	regulator-always-on;
+	regulator-max-microvolt = <3000000>;
+	regulator-min-microvolt = <3000000>;
+	regulator-name = "vcc-pll";
+};
+
+/* LDO3, LDO4, and LDO5 unused. */
+&reg_ldo3 {
+	status = "disabled";
+};
+
+&reg_ldo4 {
+	status = "disabled";
+};
+
+/* RTC uses internal oscillator */
+&rtc {
+	/delete-property/ clocks;
+};
+
+&spi0 {
+	pinctrl-0 = <&spi0_no_miso_pins>;
+	pinctrl-names = "default";
+	status = "okay";
+
+	display@0 {
+		compatible = "saef,sftc154b", "panel-mipi-dbi-spi";
+		reg = <0>;
+		backlight = <&backlight>;
+		dc-gpios = <&pio 2 0 GPIO_ACTIVE_HIGH>; /* PC0 */
+		reset-gpios = <&pio 1 2 GPIO_ACTIVE_HIGH>; /* PB2 */
+		spi-max-frequency = <100000000>;
+
+		height-mm = <39>;
+		width-mm = <39>;
+
+		/* Set hb-porch to compensate for non-visible area */
+		panel-timing {
+			hactive = <240>;
+			vactive = <240>;
+			hback-porch = <80>;
+			vback-porch = <0>;
+			clock-frequency = <0>;
+			hfront-porch = <0>;
+			hsync-len = <0>;
+			vfront-porch = <0>;
+			vsync-len = <0>;
+		};
+	};
+};
+
+&uart0 {
+	pinctrl-0 = <&uart0_pb_pins>;
+	pinctrl-names = "default";
+	status = "okay";
+};
+
+&usb_otg {
+	dr_mode = "otg";
+	status = "okay";
+};
+
+&usb_power_supply {
+	status = "okay";
+};
+
+&usbphy {
+	usb0_id_det-gpios = <&pio 6 5 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; /* PG5 */
+	status = "okay";
+};
diff --git a/arch/arm/dts/sun8i-v3s.dtsi b/arch/arm/dts/sun8i-v3s.dtsi
index 3b9a282..e8a0447 100644
--- a/arch/arm/dts/sun8i-v3s.dtsi
+++ b/arch/arm/dts/sun8i-v3s.dtsi
@@ -319,6 +319,29 @@
 			#phy-cells = <1>;
 		};
 
+		ehci: usb@1c1a000 {
+			compatible = "allwinner,sun8i-v3s-ehci", "generic-ehci";
+			reg = <0x01c1a000 0x100>;
+			interrupts = <GIC_SPI 72 IRQ_TYPE_LEVEL_HIGH>;
+			clocks = <&ccu CLK_BUS_EHCI0>, <&ccu CLK_BUS_OHCI0>;
+			resets = <&ccu RST_BUS_EHCI0>, <&ccu RST_BUS_OHCI0>;
+			phys = <&usbphy 0>;
+			phy-names = "usb";
+			status = "disabled";
+		};
+
+		ohci: usb@1c1a400 {
+			compatible = "allwinner,sun8i-v3s-ohci", "generic-ohci";
+			reg = <0x01c1a400 0x100>;
+			interrupts = <GIC_SPI 73 IRQ_TYPE_LEVEL_HIGH>;
+			clocks = <&ccu CLK_BUS_EHCI0>, <&ccu CLK_BUS_OHCI0>,
+				 <&ccu CLK_USB_OHCI0>;
+			resets = <&ccu RST_BUS_EHCI0>, <&ccu RST_BUS_OHCI0>;
+			phys = <&usbphy 0>;
+			phy-names = "usb";
+			status = "disabled";
+		};
+
 		ccu: clock@1c20000 {
 			compatible = "allwinner,sun8i-v3s-ccu";
 			reg = <0x01c20000 0x400>;
@@ -414,6 +437,18 @@
 				bias-pull-up;
 			};
 
+			/omit-if-no-ref/
+			pwm0_pin: pwm0-pin {
+				pins = "PB4";
+				function = "pwm0";
+			};
+
+			/omit-if-no-ref/
+			pwm1_pin: pwm1-pin {
+				pins = "PB5";
+				function = "pwm1";
+			};
+
 			spi0_pins: spi0-pins {
 				pins = "PC0", "PC1", "PC2", "PC3";
 				function = "spi0";
diff --git a/arch/arm/dts/sunxi-u-boot.dtsi b/arch/arm/dts/sunxi-u-boot.dtsi
index a0c8abb..0909a67 100644
--- a/arch/arm/dts/sunxi-u-boot.dtsi
+++ b/arch/arm/dts/sunxi-u-boot.dtsi
@@ -1,13 +1,9 @@
 #include <config.h>
 
-#ifdef CONFIG_MACH_SUN50I_H6
-#define BL31_ADDR 0x104000
-#define  SCP_ADDR 0x114000
-#elif defined(CONFIG_MACH_SUN50I_H616)
-#define BL31_ADDR 0x40000000
+#ifdef CONFIG_ARM64
+#define ARCH "arm64"
 #else
-#define BL31_ADDR  0x44000
-#define  SCP_ADDR  0x50000
+#define ARCH "arm"
 #endif
 
 / {
@@ -44,47 +40,52 @@
 			filename = "spl/sunxi-spl.bin";
 		};
 
-#ifdef CONFIG_ARM64
+#ifdef CONFIG_SPL_LOAD_FIT
 		fit {
-			description = "Configuration to load ATF before U-Boot";
+			description = "Configuration to load U-Boot and firmware";
 			#address-cells = <1>;
 			fit,fdt-list = "of-list";
 
 			images {
 				uboot {
-					description = "U-Boot (64-bit)";
+					description = "U-Boot";
 					type = "standalone";
 					os = "u-boot";
-					arch = "arm64";
+					arch = ARCH;
 					compression = "none";
 					load = <CONFIG_TEXT_BASE>;
+#if CONFIG_SUNXI_BL31_BASE == 0
+					entry = <CONFIG_TEXT_BASE>;
+#endif
 
 					u-boot-nodtb {
 					};
 				};
 
+#if CONFIG_SUNXI_BL31_BASE
 				atf {
 					description = "ARM Trusted Firmware";
 					type = "firmware";
 					os = "arm-trusted-firmware";
-					arch = "arm64";
+					arch = ARCH;
 					compression = "none";
-					load = <BL31_ADDR>;
-					entry = <BL31_ADDR>;
+					load = <CONFIG_SUNXI_BL31_BASE>;
+					entry = <CONFIG_SUNXI_BL31_BASE>;
 
 					atf-bl31 {
 						filename = "bl31.bin";
 						missing-msg = "atf-bl31-sunxi";
 					};
 				};
+#endif
 
-#ifdef SCP_ADDR
+#if CONFIG_SUNXI_SCP_BASE
 				scp {
 					description = "SCP firmware";
 					type = "firmware";
 					arch = "or1k";
 					compression = "none";
-					load = <SCP_ADDR>;
+					load = <CONFIG_SUNXI_SCP_BASE>;
 
 					scp {
 						filename = "scp.bin";
@@ -105,11 +106,15 @@
 
 				@config-SEQ {
 					description = "NAME";
+#if CONFIG_SUNXI_BL31_BASE
 					firmware = "atf";
-#ifndef SCP_ADDR
-					loadables = "uboot";
 #else
+					firmware = "uboot";
+#endif
+#if CONFIG_SUNXI_SCP_BASE
 					loadables = "scp", "uboot";
+#else
+					loadables = "uboot";
 #endif
 					fdt = "fdt-SEQ";
 				};
diff --git a/arch/arm/include/asm/arch-sunxi/cpu_sun4i.h b/arch/arm/include/asm/arch-sunxi/cpu_sun4i.h
index 3daee2f..f023a4c 100644
--- a/arch/arm/include/asm/arch-sunxi/cpu_sun4i.h
+++ b/arch/arm/include/asm/arch-sunxi/cpu_sun4i.h
@@ -36,58 +36,20 @@
 
 #define SUNXI_SRAMC_BASE		0x01c00000
 #define SUNXI_DRAMC_BASE		0x01c01000
-#define SUNXI_DMA_BASE			0x01c02000
 #define SUNXI_NFC_BASE			0x01c03000
-#define SUNXI_TS_BASE			0x01c04000
-#define SUNXI_SPI0_BASE			0x01c05000
-#define SUNXI_SPI1_BASE			0x01c06000
-#define SUNXI_MS_BASE			0x01c07000
-#define SUNXI_TVD_BASE			0x01c08000
-#define SUNXI_CSI0_BASE			0x01c09000
 #ifndef CONFIG_MACH_SUNXI_H3_H5
 #define SUNXI_TVE0_BASE			0x01c0a000
 #endif
-#define SUNXI_EMAC_BASE			0x01c0b000
 #define SUNXI_LCD0_BASE			0x01c0C000
 #define SUNXI_LCD1_BASE			0x01c0d000
-#define SUNXI_VE_BASE			0x01c0e000
 #define SUNXI_MMC0_BASE			0x01c0f000
 #define SUNXI_MMC1_BASE			0x01c10000
 #define SUNXI_MMC2_BASE			0x01c11000
 #define SUNXI_MMC3_BASE			0x01c12000
-#ifdef CONFIG_SUNXI_GEN_SUN4I
-#define SUNXI_USB0_BASE			0x01c13000
-#define SUNXI_USB1_BASE			0x01c14000
-#endif
 #define SUNXI_SS_BASE			0x01c15000
 #if !defined(CONFIG_MACH_SUNXI_H3_H5) && !defined(CONFIG_MACH_SUN50I)
 #define SUNXI_HDMI_BASE			0x01c16000
 #endif
-#define SUNXI_SPI2_BASE			0x01c17000
-#define SUNXI_SATA_BASE			0x01c18000
-#ifdef CONFIG_SUNXI_GEN_SUN4I
-#define SUNXI_PATA_BASE			0x01c19000
-#define SUNXI_ACE_BASE			0x01c1a000
-#define SUNXI_TVE1_BASE			0x01c1b000
-#define SUNXI_USB2_BASE			0x01c1c000
-#endif
-#ifdef CONFIG_SUNXI_GEN_SUN6I
-#if defined(CONFIG_MACH_SUNXI_H3_H5) || defined(CONFIG_MACH_SUN50I)
-#define SUNXI_USBPHY_BASE		0x01c19000
-#define SUNXI_USB0_BASE			SUNXI_USBPHY_BASE
-#define SUNXI_USB1_BASE			0x01c1a000
-#define SUNXI_USB2_BASE			0x01c1b000
-#define SUNXI_USB3_BASE			0x01c1c000
-#define SUNXI_USB4_BASE			0x01c1d000
-#else
-#define SUNXI_USB0_BASE			0x01c19000
-#define SUNXI_USB1_BASE			0x01c1a000
-#define SUNXI_USB2_BASE			0x01c1b000
-#endif
-#endif
-#define SUNXI_CSI1_BASE			0x01c1d000
-#define SUNXI_TZASC_BASE		0x01c1e000
-#define SUNXI_SPI3_BASE			0x01c1f000
 
 #define SUNXI_CCM_BASE			0x01c20000
 #define SUNXI_INTC_BASE			0x01c20400
@@ -177,8 +139,6 @@
 #else
 #define SUNXI_TVE0_BASE			0x01e40000
 #endif
-#define SUNXI_MP_BASE			0x01e80000
-#define SUNXI_AVG_BASE			0x01ea0000
 
 #if defined(CONFIG_MACH_SUNXI_H3_H5) || defined(CONFIG_MACH_SUN50I)
 #define SUNXI_HDMI_BASE			0x01ee0000
@@ -197,13 +157,6 @@
 #define SUN6I_P2WI_BASE			0x01f03400
 #define SUNXI_RSB_BASE			0x01f03400
 
-/* CoreSight Debug Module */
-#define SUNXI_CSDM_BASE			0x3f500000
-
-#define SUNXI_DDRII_DDRIII_BASE		0x40000000	/* 2 GiB */
-
-#define SUNXI_BROM_BASE			0xffff0000	/* 32 kiB */
-
 #define SUNXI_CPU_CFG			(SUNXI_TIMER_BASE + 0x13c)
 
 /* SS bonding ids used for cpu identification */
diff --git a/arch/arm/include/asm/arch-sunxi/cpu_sun50i_h6.h b/arch/arm/include/asm/arch-sunxi/cpu_sun50i_h6.h
index 15ee092..8a3f465 100644
--- a/arch/arm/include/asm/arch-sunxi/cpu_sun50i_h6.h
+++ b/arch/arm/include/asm/arch-sunxi/cpu_sun50i_h6.h
@@ -7,25 +7,14 @@
 #ifndef _SUNXI_CPU_SUN50I_H6_H
 #define _SUNXI_CPU_SUN50I_H6_H
 
-#define SUNXI_SRAM_A1_BASE		CONFIG_SUNXI_SRAM_ADDRESS
-#define SUNXI_SRAM_C_BASE		0x00028000
-#define SUNXI_SRAM_A2_BASE		0x00100000
-
-#define SUNXI_DE3_BASE			0x01000000
-#define SUNXI_SS_BASE			0x01904000
-#define SUNXI_EMCE_BASE			0x01905000
-
 #define SUNXI_SRAMC_BASE		0x03000000
 #define SUNXI_CCM_BASE			0x03001000
-#define SUNXI_DMA_BASE			0x03002000
 /* SID address space starts at 0x03006000, but e-fuse is at offset 0x200 */
 #define SUNXI_SIDC_BASE			0x03006000
 #define SUNXI_SID_BASE			0x03006200
 #define SUNXI_TIMER_BASE		0x03009000
-#define SUNXI_PSI_BASE			0x0300C000
 
 #define SUNXI_GIC400_BASE		0x03020000
-#define SUNXI_IOMMU_BASE		0x030F0000
 
 #ifdef CONFIG_MACH_SUN50I_H6
 #define SUNXI_DRAM_COM_BASE		0x04002000
@@ -46,18 +35,8 @@
 #define SUNXI_TWI1_BASE			0x05002400
 #define SUNXI_TWI2_BASE			0x05002800
 #define SUNXI_TWI3_BASE			0x05002C00
-#define SUNXI_SPI0_BASE			0x05010000
-#define SUNXI_SPI1_BASE			0x05011000
-#define SUNXI_GMAC_BASE			0x05020000
-#define SUNXI_USB0_BASE			0x05100000
-#define SUNXI_XHCI_BASE			0x05200000
-#define SUNXI_USB3_BASE			0x05311000
-#define SUNXI_PCIE_BASE			0x05400000
 
 #define SUNXI_HDMI_BASE			0x06000000
-#define SUNXI_TCON_TOP_BASE		0x06510000
-#define SUNXI_TCON_LCD0_BASE		0x06511000
-#define SUNXI_TCON_TV0_BASE		0x06515000
 
 #define SUNXI_RTC_BASE			0x07000000
 #define SUNXI_R_CPUCFG_BASE		0x07000400
diff --git a/arch/arm/include/asm/arch-sunxi/cpu_sun9i.h b/arch/arm/include/asm/arch-sunxi/cpu_sun9i.h
index 2bf2675..73de470 100644
--- a/arch/arm/include/asm/arch-sunxi/cpu_sun9i.h
+++ b/arch/arm/include/asm/arch-sunxi/cpu_sun9i.h
@@ -20,7 +20,6 @@
 
 /* AHB0 Module */
 #define SUNXI_NFC_BASE			(REGS_AHB0_BASE + 0x3000)
-#define SUNXI_TSC_BASE			(REGS_AHB0_BASE + 0x4000)
 
 #define SUNXI_GTBUS_BASE		(REGS_AHB0_BASE + 0x9000)
 /* SID address space starts at 0x01ce000, but e-fuse is at offset 0x200 */
@@ -32,14 +31,7 @@
 #define SUNXI_MMC3_BASE			(REGS_AHB0_BASE + 0x12000)
 #define SUNXI_MMC_COMMON_BASE		(REGS_AHB0_BASE + 0x13000)
 
-#define SUNXI_SPI0_BASE			(REGS_AHB0_BASE + 0x1A000)
-#define SUNXI_SPI1_BASE			(REGS_AHB0_BASE + 0x1B000)
-#define SUNXI_SPI2_BASE			(REGS_AHB0_BASE + 0x1C000)
-#define SUNXI_SPI3_BASE			(REGS_AHB0_BASE + 0x1D000)
-
 #define SUNXI_GIC400_BASE		(REGS_AHB0_BASE + 0x40000)
-#define SUNXI_ARMA9_GIC_BASE		(REGS_AHB0_BASE + 0x41000)
-#define SUNXI_ARMA9_CPUIF_BASE		(REGS_AHB0_BASE + 0x42000)
 
 #define SUNXI_DRAM_COM_BASE		(REGS_AHB0_BASE + 0x62000)
 #define SUNXI_DRAM_CTL0_BASE		(REGS_AHB0_BASE + 0x63000)
@@ -47,59 +39,26 @@
 #define SUNXI_DRAM_PHY0_BASE		(REGS_AHB0_BASE + 0x65000)
 #define SUNXI_DRAM_PHY1_BASE		(REGS_AHB0_BASE + 0x66000)
 
-/* AHB1 Module */
-#define SUNXI_DMA_BASE			(REGS_AHB1_BASE + 0x002000)
-#define SUNXI_USBOTG_BASE		(REGS_AHB1_BASE + 0x100000)
-#define SUNXI_USBEHCI0_BASE		(REGS_AHB1_BASE + 0x200000)
-#define SUNXI_USBEHCI1_BASE		(REGS_AHB1_BASE + 0x201000)
-#define SUNXI_USBEHCI2_BASE		(REGS_AHB1_BASE + 0x202000)
-
-/* AHB2 Module */
-#define SUNXI_DE_SYS_BASE		(REGS_AHB2_BASE + 0x000000)
-#define SUNXI_DISP_SYS_BASE		(REGS_AHB2_BASE + 0x010000)
 #define SUNXI_DE_FE0_BASE		(REGS_AHB2_BASE + 0x100000)
-#define SUNXI_DE_FE1_BASE		(REGS_AHB2_BASE + 0x140000)
-#define SUNXI_DE_FE2_BASE		(REGS_AHB2_BASE + 0x180000)
-
 #define SUNXI_DE_BE0_BASE		(REGS_AHB2_BASE + 0x200000)
-#define SUNXI_DE_BE1_BASE		(REGS_AHB2_BASE + 0x240000)
-#define SUNXI_DE_BE2_BASE		(REGS_AHB2_BASE + 0x280000)
-
-#define SUNXI_DE_DEU0_BASE		(REGS_AHB2_BASE + 0x300000)
-#define SUNXI_DE_DEU1_BASE		(REGS_AHB2_BASE + 0x340000)
-#define SUNXI_DE_DRC0_BASE		(REGS_AHB2_BASE + 0x400000)
-#define SUNXI_DE_DRC1_BASE		(REGS_AHB2_BASE + 0x440000)
-
 #define SUNXI_LCD0_BASE			(REGS_AHB2_BASE + 0xC00000)
 #define SUNXI_LCD1_BASE			(REGS_AHB2_BASE + 0xC10000)
 #define SUNXI_LCD2_BASE			(REGS_AHB2_BASE + 0xC20000)
-#define SUNXI_MIPI_DSI0_BASE		(REGS_AHB2_BASE + 0xC40000)
-/* Also seen as SUNXI_MIPI_DSI0_DPHY_BASE 0x01ca1000 */
-#define SUNXI_MIPI_DSI0_DPHY_BASE	(REGS_AHB2_BASE + 0xC40100)
 #define SUNXI_HDMI_BASE			(REGS_AHB2_BASE + 0xD00000)
 
 /* APB0 Module */
 #define SUNXI_CCM_BASE			(REGS_APB0_BASE + 0x0000)
-#define SUNXI_CCMMODULE_BASE		(REGS_APB0_BASE + 0x0400)
 #define SUNXI_TIMER_BASE		(REGS_APB0_BASE + 0x0C00)
 #define SUNXI_PWM_BASE			(REGS_APB0_BASE + 0x1400)
-#define SUNXI_LRADC_BASE		(REGS_APB0_BASE + 0x1800)
 
 /* APB1 Module */
 #define SUNXI_TWI0_BASE			(REGS_APB1_BASE + 0x2800)
 #define SUNXI_TWI1_BASE			(REGS_APB1_BASE + 0x2C00)
-#define SUNXI_TWI2_BASE			(REGS_APB1_BASE + 0x3000)
-#define SUNXI_TWI3_BASE			(REGS_APB1_BASE + 0x3400)
-#define SUNXI_TWI4_BASE			(REGS_APB1_BASE + 0x3800)
 
 /* RCPUS Module */
 #define SUNXI_PRCM_BASE			(REGS_RCPUS_BASE + 0x1400)
 #define SUNXI_RSB_BASE			(REGS_RCPUS_BASE + 0x3400)
 
-/* Misc. */
-#define SUNXI_BROM_BASE			0xFFFF0000 /* 32K */
-#define SUNXI_CPU_CFG			(SUNXI_TIMER_BASE + 0x13c)
-
 #ifndef __ASSEMBLY__
 void sunxi_board_init(void);
 void sunxi_reset(void);
diff --git a/arch/arm/mach-k3/common.c b/arch/arm/mach-k3/common.c
index d5db805..f411366 100644
--- a/arch/arm/mach-k3/common.c
+++ b/arch/arm/mach-k3/common.c
@@ -525,19 +525,26 @@
 void spl_enable_cache(void)
 {
 #if !(defined(CONFIG_SYS_ICACHE_OFF) && defined(CONFIG_SYS_DCACHE_OFF))
-	phys_addr_t ram_top = CFG_SYS_SDRAM_BASE;
+	gd->ram_top = CFG_SYS_SDRAM_BASE;
+	int ret = 0;
 
 	dram_init();
 
 	/* reserve TLB table */
 	gd->arch.tlb_size = PGTABLE_SIZE;
 
-	ram_top += get_effective_memsize();
+	gd->ram_top += get_effective_memsize();
 	/* keep ram_top in the 32-bit address space */
-	if (ram_top >= 0x100000000)
-		ram_top = (phys_addr_t) 0x100000000;
+	if (gd->ram_top >= 0x100000000)
+		gd->ram_top = (phys_addr_t)0x100000000;
+
+	gd->relocaddr = gd->ram_top;
+
+	ret = spl_reserve_video_from_ram_top();
+	if (ret)
+		panic("Failed to reserve framebuffer memory (%d)\n", ret);
 
-	gd->arch.tlb_addr = ram_top - gd->arch.tlb_size;
+	gd->arch.tlb_addr = gd->relocaddr - gd->arch.tlb_size;
 	gd->arch.tlb_addr &= ~(0x10000 - 1);
 	debug("TLB table from %08lx to %08lx\n", gd->arch.tlb_addr,
 	      gd->arch.tlb_addr + gd->arch.tlb_size);
diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig
index a4a8d8e..fe89aec 100644
--- a/arch/arm/mach-sunxi/Kconfig
+++ b/arch/arm/mach-sunxi/Kconfig
@@ -161,6 +161,23 @@
 	for all other SoCs, so the content of the SRAM_VER_REG becomes
 	irrelevant there, and we can use the same code.
 
+config SUNXI_BL31_BASE
+	hex
+	default 0x00044000 if MACH_SUN50I || MACH_SUN50I_H5
+	default 0x00104000 if MACH_SUN50I_H6
+	default 0x40000000 if MACH_SUN50I_H616
+	default 0x0
+	help
+	  Address where BL31 (TF-A) is loaded, or zero if BL31 is not used.
+
+config SUNXI_SCP_BASE
+	hex
+	default 0x00050000 if MACH_SUN50I || MACH_SUN50I_H5
+	default 0x00114000 if MACH_SUN50I_H6
+	default 0x0
+	help
+	  Address where SCP firmware is loaded, or zero if it is not used.
+
 config SUNXI_A64_TIMER_ERRATUM
 	bool
 
@@ -182,7 +199,7 @@
 config SUN50I_GEN_H6
 	bool
 	select FIT
-	select SPL_LOAD_FIT
+	select SPL_LOAD_FIT if SPL
 	select MMC_SUNXI_HAS_NEW_MODE
 	select SUPPORT_SPL
 	---help---
@@ -272,7 +289,7 @@
 	select ARCH_SUPPORT_PSCI
 	select SPL_ARMV7_SET_CORTEX_SMPEN
 	select DRAM_SUN6I
-	select SPL_I2C
+	select SPL_I2C if SPL
 	select SUN6I_PRCM
 	select SUNXI_GEN_SUN6I
 	select SUPPORT_SPL
@@ -300,7 +317,7 @@
 	select CPU_V7_HAS_VIRT
 	select ARCH_SUPPORT_PSCI
 	select DRAM_SUN8I_A23
-	select SPL_I2C
+	select SPL_I2C if SPL
 	select SUNXI_GEN_SUN6I
 	select SUPPORT_SPL
 	select SYS_I2C_SUN8I_RSB
@@ -313,7 +330,7 @@
 	select CPU_V7_HAS_VIRT
 	select ARCH_SUPPORT_PSCI
 	select DRAM_SUN8I_A33
-	select SPL_I2C
+	select SPL_I2C if SPL
 	select SUNXI_GEN_SUN6I
 	select SUPPORT_SPL
 	select SYS_I2C_SUN8I_RSB
@@ -323,7 +340,7 @@
 	bool "sun8i (Allwinner A83T)"
 	select CPU_V7A
 	select DRAM_SUN8I_A83T
-	select SPL_I2C
+	select SPL_I2C if SPL
 	select SUNXI_GEN_SUN6I
 	select MMC_SUNXI_HAS_NEW_MODE
 	select MMC_SUNXI_HAS_MODE_SWITCH
@@ -382,7 +399,7 @@
 	select CPU_V7A
 	select SPL_ARMV7_SET_CORTEX_SMPEN
 	select DRAM_SUN9I
-	select SPL_I2C
+	select SPL_I2C if SPL
 	select SUN6I_PRCM
 	select SUNXI_GEN_SUN6I
 	select SUPPORT_SPL
@@ -398,7 +415,7 @@
 	select SUNXI_DRAM_DW
 	select SUNXI_DRAM_DW_32BIT
 	select FIT
-	select SPL_LOAD_FIT
+	select SPL_LOAD_FIT if SPL
 	select SUNXI_A64_TIMER_ERRATUM
 
 config MACH_SUN50I_H5
@@ -407,7 +424,7 @@
 	select MACH_SUNXI_H3_H5
 	select MMC_SUNXI_HAS_NEW_MODE
 	select FIT
-	select SPL_LOAD_FIT
+	select SPL_LOAD_FIT if SPL
 
 config MACH_SUN50I_H6
 	bool "sun50i (Allwinner H6)"
diff --git a/arch/arm/mach-sunxi/board.c b/arch/arm/mach-sunxi/board.c
index 11a4941..f4dbb2a 100644
--- a/arch/arm/mach-sunxi/board.c
+++ b/arch/arm/mach-sunxi/board.c
@@ -9,7 +9,6 @@
  * Some init for sunxi platform.
  */
 
-#include <common.h>
 #include <cpu_func.h>
 #include <init.h>
 #include <log.h>
diff --git a/arch/arm/mach-sunxi/clock.c b/arch/arm/mach-sunxi/clock.c
index da3a0eb..b6c68c9 100644
--- a/arch/arm/mach-sunxi/clock.c
+++ b/arch/arm/mach-sunxi/clock.c
@@ -7,7 +7,6 @@
  * (C) Copyright 2013 Luke Kenneth Casson Leighton <lkcl@lkcl.net>
  */
 
-#include <common.h>
 #include <asm/io.h>
 #include <asm/arch/clock.h>
 #include <asm/arch/prcm.h>
diff --git a/arch/arm/mach-sunxi/clock_sun4i.c b/arch/arm/mach-sunxi/clock_sun4i.c
index 4716097..8f1d1b6 100644
--- a/arch/arm/mach-sunxi/clock_sun4i.c
+++ b/arch/arm/mach-sunxi/clock_sun4i.c
@@ -9,7 +9,6 @@
  * (C) Copyright 2013 Luke Kenneth Casson Leighton <lkcl@lkcl.net>
  */
 
-#include <common.h>
 #include <asm/io.h>
 #include <asm/arch/clock.h>
 #include <asm/arch/sys_proto.h>
diff --git a/arch/arm/mach-sunxi/clock_sun50i_h6.c b/arch/arm/mach-sunxi/clock_sun50i_h6.c
index bea91c7..dac3663 100644
--- a/arch/arm/mach-sunxi/clock_sun50i_h6.c
+++ b/arch/arm/mach-sunxi/clock_sun50i_h6.c
@@ -1,4 +1,3 @@
-#include <common.h>
 #include <asm/io.h>
 #include <asm/arch/cpu.h>
 #include <asm/arch/clock.h>
diff --git a/arch/arm/mach-sunxi/clock_sun6i.c b/arch/arm/mach-sunxi/clock_sun6i.c
index 6bd75a1..aad9df2 100644
--- a/arch/arm/mach-sunxi/clock_sun6i.c
+++ b/arch/arm/mach-sunxi/clock_sun6i.c
@@ -9,7 +9,6 @@
  * (C) Copyright 2013 Luke Kenneth Casson Leighton <lkcl@lkcl.net>
  */
 
-#include <common.h>
 #include <asm/io.h>
 #include <asm/arch/clock.h>
 #include <asm/arch/prcm.h>
diff --git a/arch/arm/mach-sunxi/clock_sun8i_a83t.c b/arch/arm/mach-sunxi/clock_sun8i_a83t.c
index 31e4281..198fe9d 100644
--- a/arch/arm/mach-sunxi/clock_sun8i_a83t.c
+++ b/arch/arm/mach-sunxi/clock_sun8i_a83t.c
@@ -9,7 +9,6 @@
  * (C) Copyright 2015 Vishnu Patekar <vishnupatekar0510@gmail.com>
  */
 
-#include <common.h>
 #include <asm/io.h>
 #include <asm/arch/clock.h>
 #include <asm/arch/prcm.h>
diff --git a/arch/arm/mach-sunxi/clock_sun9i.c b/arch/arm/mach-sunxi/clock_sun9i.c
index 8ba4802..edaff9a 100644
--- a/arch/arm/mach-sunxi/clock_sun9i.c
+++ b/arch/arm/mach-sunxi/clock_sun9i.c
@@ -9,7 +9,6 @@
  *                    Philipp Tomsich <philipp.tomsich@theobroma-systems.com>
  */
 
-#include <common.h>
 #include <asm/io.h>
 #include <asm/arch/clock.h>
 #include <asm/arch/prcm.h>
diff --git a/arch/arm/mach-sunxi/cpu_info.c b/arch/arm/mach-sunxi/cpu_info.c
index 7fecc3b..310dca0 100644
--- a/arch/arm/mach-sunxi/cpu_info.c
+++ b/arch/arm/mach-sunxi/cpu_info.c
@@ -5,7 +5,6 @@
  * Tom Cubie <tangliang@allwinnertech.com>
  */
 
-#include <common.h>
 #include <init.h>
 #include <asm/io.h>
 #include <asm/arch/cpu.h>
diff --git a/arch/arm/mach-sunxi/dram_helpers.c b/arch/arm/mach-sunxi/dram_helpers.c
index cdf2750..4a867df 100644
--- a/arch/arm/mach-sunxi/dram_helpers.c
+++ b/arch/arm/mach-sunxi/dram_helpers.c
@@ -5,8 +5,9 @@
  * (C) Copyright 2015 Hans de Goede <hdegoede@redhat.com>
  */
 
-#include <common.h>
+#include <config.h>
 #include <time.h>
+#include <vsprintf.h>
 #include <asm/barriers.h>
 #include <asm/io.h>
 #include <asm/arch/dram.h>
diff --git a/arch/arm/mach-sunxi/dram_sun4i.c b/arch/arm/mach-sunxi/dram_sun4i.c
index 80a6c4b..2cce381 100644
--- a/arch/arm/mach-sunxi/dram_sun4i.c
+++ b/arch/arm/mach-sunxi/dram_sun4i.c
@@ -20,7 +20,6 @@
  * rather undocumented and full of magic.
  */
 
-#include <common.h>
 #include <init.h>
 #include <asm/io.h>
 #include <asm/arch/clock.h>
diff --git a/arch/arm/mach-sunxi/dram_sun50i_h6.c b/arch/arm/mach-sunxi/dram_sun50i_h6.c
index 62bc2a0..e7862bd 100644
--- a/arch/arm/mach-sunxi/dram_sun50i_h6.c
+++ b/arch/arm/mach-sunxi/dram_sun50i_h6.c
@@ -5,7 +5,6 @@
  * (C) Copyright 2017      Icenowy Zheng <icenowy@aosc.io>
  *
  */
-#include <common.h>
 #include <init.h>
 #include <log.h>
 #include <asm/io.h>
diff --git a/arch/arm/mach-sunxi/dram_sun50i_h616.c b/arch/arm/mach-sunxi/dram_sun50i_h616.c
index e62d571..37c139e 100644
--- a/arch/arm/mach-sunxi/dram_sun50i_h616.c
+++ b/arch/arm/mach-sunxi/dram_sun50i_h616.c
@@ -12,7 +12,6 @@
  * (C) Copyright 2020 Jernej Skrabec <jernej.skrabec@siol.net>
  *
  */
-#include <common.h>
 #include <init.h>
 #include <log.h>
 #include <asm/io.h>
diff --git a/arch/arm/mach-sunxi/dram_sun6i.c b/arch/arm/mach-sunxi/dram_sun6i.c
index 0590110..c023845 100644
--- a/arch/arm/mach-sunxi/dram_sun6i.c
+++ b/arch/arm/mach-sunxi/dram_sun6i.c
@@ -9,7 +9,6 @@
  *
  * (C) Copyright 2014 Hans de Goede <hdegoede@redhat.com>
  */
-#include <common.h>
 #include <errno.h>
 #include <init.h>
 #include <asm/io.h>
diff --git a/arch/arm/mach-sunxi/dram_sun8i_a23.c b/arch/arm/mach-sunxi/dram_sun8i_a23.c
index 056cb03..1c3c6d8 100644
--- a/arch/arm/mach-sunxi/dram_sun8i_a23.c
+++ b/arch/arm/mach-sunxi/dram_sun8i_a23.c
@@ -19,7 +19,6 @@
  * This may be used as a (possible) reference for future work / cleanups.
  */
 
-#include <common.h>
 #include <errno.h>
 #include <init.h>
 #include <asm/io.h>
diff --git a/arch/arm/mach-sunxi/dram_sun8i_a33.c b/arch/arm/mach-sunxi/dram_sun8i_a33.c
index 367b740..0d08b6a 100644
--- a/arch/arm/mach-sunxi/dram_sun8i_a33.c
+++ b/arch/arm/mach-sunxi/dram_sun8i_a33.c
@@ -7,7 +7,6 @@
  * (C) Copyright 2015      Vishnu Patekar <vishnupatekar0510@gmail.com>
  * (C) Copyright 2015      Hans de Goede <hdegoede@redhat.com>
  */
-#include <common.h>
 #include <errno.h>
 #include <init.h>
 #include <asm/io.h>
diff --git a/arch/arm/mach-sunxi/dram_sun8i_a83t.c b/arch/arm/mach-sunxi/dram_sun8i_a83t.c
index a3f833d..ef83332 100644
--- a/arch/arm/mach-sunxi/dram_sun8i_a83t.c
+++ b/arch/arm/mach-sunxi/dram_sun8i_a83t.c
@@ -7,7 +7,6 @@
  * (C) Copyright 2015      Vishnu Patekar <vishnupatekar0510@gmail.com>
  * (C) Copyright 2015      Hans de Goede <hdegoede@redhat.com>
  */
-#include <common.h>
 #include <errno.h>
 #include <init.h>
 #include <asm/io.h>
diff --git a/arch/arm/mach-sunxi/dram_sun9i.c b/arch/arm/mach-sunxi/dram_sun9i.c
index 14be212..002b6df 100644
--- a/arch/arm/mach-sunxi/dram_sun9i.c
+++ b/arch/arm/mach-sunxi/dram_sun9i.c
@@ -10,7 +10,6 @@
  *                    Philipp Tomsich <philipp.tomsich@theobroma-systems.com>
  */
 
-#include <common.h>
 #include <dm.h>
 #include <errno.h>
 #include <init.h>
diff --git a/arch/arm/mach-sunxi/dram_suniv.c b/arch/arm/mach-sunxi/dram_suniv.c
index 9e583e1..640f872 100644
--- a/arch/arm/mach-sunxi/dram_suniv.c
+++ b/arch/arm/mach-sunxi/dram_suniv.c
@@ -9,7 +9,7 @@
  * Copyright(c) 2007-2018 Jianjun Jiang <8192542@qq.com>
  */
 
-#include <common.h>
+#include <config.h>
 #include <asm/io.h>
 #include <asm/arch/clock.h>
 #include <asm/arch/dram.h>
diff --git a/arch/arm/mach-sunxi/dram_sunxi_dw.c b/arch/arm/mach-sunxi/dram_sunxi_dw.c
index daef051..3bfcc63 100644
--- a/arch/arm/mach-sunxi/dram_sunxi_dw.c
+++ b/arch/arm/mach-sunxi/dram_sunxi_dw.c
@@ -8,7 +8,6 @@
  * (C) Copyright 2015      Hans de Goede <hdegoede@redhat.com>
  * (C) Copyright 2015      Jens Kuske <jenskuske@gmail.com>
  */
-#include <common.h>
 #include <init.h>
 #include <log.h>
 #include <asm/io.h>
diff --git a/arch/arm/mach-sunxi/gtbus_sun9i.c b/arch/arm/mach-sunxi/gtbus_sun9i.c
index 5624621..a058fea 100644
--- a/arch/arm/mach-sunxi/gtbus_sun9i.c
+++ b/arch/arm/mach-sunxi/gtbus_sun9i.c
@@ -6,7 +6,6 @@
  *                    Philipp Tomsich <philipp.tomsich@theobroma-systems.com>
  */
 
-#include <common.h>
 #include <asm/io.h>
 #include <asm/arch/cpu.h>
 #include <asm/arch/gtbus_sun9i.h>
diff --git a/arch/arm/mach-sunxi/pmic_bus.c b/arch/arm/mach-sunxi/pmic_bus.c
index 8e7625f..87df312 100644
--- a/arch/arm/mach-sunxi/pmic_bus.c
+++ b/arch/arm/mach-sunxi/pmic_bus.c
@@ -9,7 +9,6 @@
  */
 
 #include <axp_pmic.h>
-#include <common.h>
 #include <dm.h>
 #include <asm/arch/p2wi.h>
 #include <asm/arch/rsb.h>
diff --git a/arch/arm/mach-sunxi/prcm.c b/arch/arm/mach-sunxi/prcm.c
index 71a2e44..ef7c46e 100644
--- a/arch/arm/mach-sunxi/prcm.c
+++ b/arch/arm/mach-sunxi/prcm.c
@@ -13,7 +13,6 @@
  * Tom Cubie <tangliang@allwinnertech.com>
  */
 
-#include <common.h>
 #include <errno.h>
 #include <asm/io.h>
 #include <asm/arch/cpu.h>
diff --git a/arch/arm/mach-sunxi/spl_spi_sunxi.c b/arch/arm/mach-sunxi/spl_spi_sunxi.c
index 267cb0b..72faa71 100644
--- a/arch/arm/mach-sunxi/spl_spi_sunxi.c
+++ b/arch/arm/mach-sunxi/spl_spi_sunxi.c
@@ -3,7 +3,6 @@
  * Copyright (C) 2016 Siarhei Siamashka <siarhei.siamashka@gmail.com>
  */
 
-#include <common.h>
 #include <image.h>
 #include <log.h>
 #include <spl.h>
diff --git a/arch/arm/mach-sunxi/timer.c b/arch/arm/mach-sunxi/timer.c
index 9a6f6c0..1bbfad5 100644
--- a/arch/arm/mach-sunxi/timer.c
+++ b/arch/arm/mach-sunxi/timer.c
@@ -5,7 +5,6 @@
  * Tom Cubie <tangliang@allwinnertech.com>
  */
 
-#include <common.h>
 #include <init.h>
 #include <time.h>
 #include <asm/global_data.h>
diff --git a/board/sunxi/MAINTAINERS b/board/sunxi/MAINTAINERS
index f556857..a2da6a4 100644
--- a/board/sunxi/MAINTAINERS
+++ b/board/sunxi/MAINTAINERS
@@ -348,6 +348,11 @@
 S:	Maintained
 F:	configs/libretech_all_h5_cc_h5_defconfig
 
+NANOPI DUO2 BOARD
+M:	Chuanhong Guo <gch981213@gmail.com>
+S:	Maintained
+F:	configs/nanopi_duo2_defconfig
+
 NANOPI-M1 BOARD
 M:	Mylène Josserand <mylene.josserand@free-electrons.com>
 S:	Maintained
@@ -550,6 +555,11 @@
 S:	Maintained
 F:	configs/tbs_a711_defconfig
 
+TRANSPEED 8K618-T BOARD
+M:	Nick Alilovic <nickalilovic@gmail.com>
+S:	Maintained
+F:	configs/transpeed-8k618-t_defconfig
+
 WEXLER-TAB7200 BOARD
 M:	Aleksei Mamlin <mamlinav@gmail.com>
 S:	Maintained
diff --git a/board/ti/am62x/evm.c b/board/ti/am62x/evm.c
index ad93908..88e0215 100644
--- a/board/ti/am62x/evm.c
+++ b/board/ti/am62x/evm.c
@@ -60,27 +60,9 @@
 }
 
 #if defined(CONFIG_SPL_BUILD)
-static int video_setup(void)
-{
-	if (CONFIG_IS_ENABLED(VIDEO)) {
-		ulong addr;
-		int ret;
-
-		addr = gd->relocaddr;
-		ret = video_reserve(&addr);
-		if (ret)
-			return ret;
-		debug("Reserving %luk for video at: %08lx\n",
-		      ((unsigned long)gd->relocaddr - addr) >> 10, addr);
-		gd->relocaddr = addr;
-	}
-
-	return 0;
-}
 
 void spl_board_init(void)
 {
-	video_setup();
 	enable_caches();
 	if (IS_ENABLED(CONFIG_SPL_SPLASH_SCREEN) && IS_ENABLED(CONFIG_SPL_BMP))
 		splash_display();
diff --git a/common/board_f.c b/common/board_f.c
index d4d7d01..442b834 100644
--- a/common/board_f.c
+++ b/common/board_f.c
@@ -403,17 +403,47 @@
 	return 0;
 }
 
-static int reserve_video(void)
+static int reserve_video_from_videoblob(void)
 {
 	if (IS_ENABLED(CONFIG_SPL_VIDEO_HANDOFF) && spl_phase() > PHASE_SPL) {
 		struct video_handoff *ho;
+		int ret = 0;
 
 		ho = bloblist_find(BLOBLISTT_U_BOOT_VIDEO, sizeof(*ho));
 		if (!ho)
-			return log_msg_ret("blf", -ENOENT);
-		video_reserve_from_bloblist(ho);
-		gd->relocaddr = ho->fb;
-	} else if (CONFIG_IS_ENABLED(VIDEO)) {
+			return log_msg_ret("Missing video bloblist", -ENOENT);
+
+		ret = video_reserve_from_bloblist(ho);
+		if (ret)
+			return log_msg_ret("Invalid Video handoff info", ret);
+
+		/* Sanity check fb from blob is before current relocaddr */
+		if (likely(gd->relocaddr > (unsigned long)ho->fb))
+			gd->relocaddr = ho->fb;
+	}
+
+	return 0;
+}
+
+/*
+ * Check if any bloblist received specifying reserved areas from previous stage and adjust
+ * gd->relocaddr accordingly, so that we start reserving after pre-reserved areas
+ * from previous stage.
+ *
+ * NOTE:
+ * IT is recommended that all bloblists from previous stage are reserved from ram_top
+ * as next stage will simply start reserving further regions after them.
+ */
+static int setup_relocaddr_from_bloblist(void)
+{
+	reserve_video_from_videoblob();
+
+	return 0;
+}
+
+static int reserve_video(void)
+{
+	if (CONFIG_IS_ENABLED(VIDEO)) {
 		ulong addr;
 		int ret;
 
@@ -923,6 +953,7 @@
 	reserve_pram,
 #endif
 	reserve_round_4k,
+	setup_relocaddr_from_bloblist,
 	arch_reserve_mmu,
 	reserve_video,
 	reserve_trace,
diff --git a/common/spl/Kconfig b/common/spl/Kconfig
index e7b84fc..6a4772e 100644
--- a/common/spl/Kconfig
+++ b/common/spl/Kconfig
@@ -97,8 +97,7 @@
 	default 0x31000 if ARCH_MX6 && MX6_OCRAM_256KB
 	default 0x11000 if ARCH_MX7 || (ARCH_MX6 && !MX6_OCRAM_256KB)
 	default 0x10000 if ARCH_KEYSTONE
-	default 0x8000 if ARCH_SUNXI && !MACH_SUN50I_H616
-	default 0x0 if ARCH_MTMIPS
+	default 0x0 if ARCH_MTMIPS || ARCH_SUNXI
 	default TPL_MAX_SIZE if TPL_MAX_SIZE > SPL_MAX_SIZE
 	default SPL_MAX_SIZE
 	help
@@ -585,8 +584,7 @@
 config SPL_FIT_IMAGE_TINY
 	bool "Remove functionality from SPL FIT loading to reduce size"
 	depends on SPL_FIT
-	default y if MACH_SUN50I || MACH_SUN50I_H5 || SUN50I_GEN_H6
-	default y if ARCH_IMX8M || ARCH_IMX9
+	default y if ARCH_IMX8M || ARCH_IMX9 || ARCH_SUNXI
 	help
 	  Enable this to reduce the size of the FIT image loading code
 	  in SPL, if space for the SPL binary is very tight.
diff --git a/common/spl/spl.c b/common/spl/spl.c
index 3ce5bfe..b65c439 100644
--- a/common/spl/spl.c
+++ b/common/spl/spl.c
@@ -42,6 +42,7 @@
 #include <fdt_support.h>
 #include <bootcount.h>
 #include <wdt.h>
+#include <video.h>
 
 DECLARE_GLOBAL_DATA_PTR;
 DECLARE_BINMAN_MAGIC_SYM;
@@ -152,6 +153,24 @@
 #endif
 }
 
+int spl_reserve_video_from_ram_top(void)
+{
+	if (CONFIG_IS_ENABLED(VIDEO)) {
+		ulong addr;
+		int ret;
+
+		addr = gd->ram_top;
+		ret = video_reserve(&addr);
+		if (ret)
+			return ret;
+		debug("Reserving %luk for video at: %08lx\n",
+		      ((unsigned long)gd->relocaddr - addr) >> 10, addr);
+		gd->relocaddr = addr;
+	}
+
+	return 0;
+}
+
 ulong spl_get_image_pos(void)
 {
 	if (!CONFIG_IS_ENABLED(BINMAN_UBOOT_SYMBOLS))
diff --git a/common/splash_source.c b/common/splash_source.c
index 7223a1a..2ce0768 100644
--- a/common/splash_source.c
+++ b/common/splash_source.c
@@ -216,7 +216,7 @@
 	}
 }
 
-#ifdef CONFIG_CMD_UBIFS
+#if defined(CONFIG_CMD_UBIFS) && !defined(CONFIG_SPL_BUILD)
 static int splash_mount_ubifs(struct splash_location *location)
 {
 	int res;
diff --git a/configs/nanopi_duo2_defconfig b/configs/nanopi_duo2_defconfig
new file mode 100644
index 0000000..beb2f92
--- /dev/null
+++ b/configs/nanopi_duo2_defconfig
@@ -0,0 +1,12 @@
+CONFIG_ARM=y
+CONFIG_ARCH_SUNXI=y
+CONFIG_DEFAULT_DEVICE_TREE="sun8i-h3-nanopi-duo2"
+CONFIG_SPL=y
+CONFIG_MACH_SUN8I_H3=y
+CONFIG_DRAM_CLK=408
+# CONFIG_VIDEO_DE2 is not set
+# CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set
+CONFIG_CONSOLE_MUX=y
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_OHCI_HCD=y
+CONFIG_USB_MUSB_GADGET=y
diff --git a/configs/transpeed-8k618-t_defconfig b/configs/transpeed-8k618-t_defconfig
new file mode 100644
index 0000000..020d397
--- /dev/null
+++ b/configs/transpeed-8k618-t_defconfig
@@ -0,0 +1,27 @@
+CONFIG_ARM=y
+CONFIG_ARCH_SUNXI=y
+CONFIG_DEFAULT_DEVICE_TREE="sun50i-h618-transpeed-8k618-t"
+CONFIG_SPL=y
+CONFIG_DRAM_SUN50I_H616_DX_ODT=0x03030303
+CONFIG_DRAM_SUN50I_H616_DX_DRI=0x0e0e0e0e
+CONFIG_DRAM_SUN50I_H616_CA_DRI=0x1f12
+CONFIG_DRAM_SUN50I_H616_TPR0=0xc0001002
+CONFIG_DRAM_SUN50I_H616_TPR10=0x2f1107
+CONFIG_DRAM_SUN50I_H616_TPR11=0xddddcccc
+CONFIG_DRAM_SUN50I_H616_TPR12=0xeddc7665
+CONFIG_MACH_SUN50I_H616=y
+CONFIG_SUNXI_DRAM_H616_DDR3_1333=y
+CONFIG_DRAM_CLK=648
+CONFIG_MMC_SUNXI_SLOT_EXTRA=2
+CONFIG_R_I2C_ENABLE=y
+# CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set
+CONFIG_SPL_I2C=y
+CONFIG_SPL_SYS_I2C_LEGACY=y
+CONFIG_SYS_I2C_MVTWSI=y
+CONFIG_SYS_I2C_SLAVE=0x7f
+CONFIG_SYS_I2C_SPEED=400000
+CONFIG_SUPPORT_EMMC_BOOT=y
+CONFIG_AXP313_POWER=y
+CONFIG_AXP_DCDC3_VOLT=1360
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_OHCI_HCD=y
diff --git a/doc/develop/spl.rst b/doc/develop/spl.rst
index 76e87f0..0a3e572 100644
--- a/doc/develop/spl.rst
+++ b/doc/develop/spl.rst
@@ -65,6 +65,15 @@
 CONFIG_SPL_SPI_LOAD (drivers/mtd/spi/spi_spl_load.o)
 CONFIG_SPL_RAM_DEVICE (common/spl/spl.c)
 CONFIG_SPL_WATCHDOG (drivers/watchdog/libwatchdog.o)
+CONFIG_SPL_SYSCON (drivers/core/syscon-uclass.o)
+CONFIG_SPL_GZIP (lib/gzip.o)
+CONFIG_SPL_VIDEO (drivers/video/video-uclass.o drivers/video/vidconsole-uclass.o)
+CONFIG_SPL_SPLASH_SCREEN (common/splash.o)
+CONFIG_SPL_SPLASH_SOURCE (common/splash_source.o)
+CONFIG_SPL_GPIO (drivers/gpio)
+CONFIG_SPL_DM_GPIO (drivers/gpio/gpio-uclass.o)
+CONFIG_SPL_BMP (drivers/video/bmp.o)
+CONFIG_SPL_BLOBLIST (common/bloblist.o)
 
 Adding SPL-specific code
 ------------------------
@@ -164,3 +173,31 @@
 the config files and picks functions based on #ifdef.  Parsing the '.i'
 files instead introduces another set of headaches.  These warnings are
 not usually important to understanding the flow, however.
+
+
+Reserving memory in SPL
+-----------------------
+
+If memory needs to be reserved in RAM during SPL stage with the requirement that
+the SPL reserved memory remains preserved across further boot stages too
+then it needs to be reserved mandatorily starting from end of RAM. This is to
+ensure that further stages can simply skip this region before carrying out
+further reservations or updating the relocation address.
+
+Also out of these regions which are to be preserved across further stages of
+boot, video framebuffer memory region must be reserved first starting from
+end of RAM for which helper function spl_reserve_video_from_ram_top is provided
+which makes sure that video memory is placed at top of reservation area with
+further reservations below it.
+
+The corresponding information of reservation for those regions can be passed to
+further boot stages using a bloblist. For e.g. the information for
+framebuffer area reserved by SPL can be passed onto U-boot using
+BLOBLISTT_U_BOOT_VIDEO.
+
+The further boot stages need to parse each of the bloblist passed from SPL stage
+starting from video bloblist and skip this whole SPL reserved memory area from
+end of RAM as per the bloblists received, before carrying out further
+reservations or updating the relocation address. For e.g, U-boot proper uses
+function "setup_relocaddr_from_bloblist" to parse the bloblists passed from
+previous stage and skip the memory reserved from previous stage accordingly.
diff --git a/drivers/mtd/spi/spi-nor-core.c b/drivers/mtd/spi/spi-nor-core.c
index 3f5f3c8..f86003c 100644
--- a/drivers/mtd/spi/spi-nor-core.c
+++ b/drivers/mtd/spi/spi-nor-core.c
@@ -331,18 +331,42 @@
 				 u8 *val)
 {
 	struct spi_mem_op op =
-		SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDAR, 1),
-			   SPI_MEM_OP_ADDR(nor->addr_mode_nbytes, addr, 1),
-			   SPI_MEM_OP_DUMMY(dummy / 8, 1),
-			   SPI_MEM_OP_DATA_IN(1, NULL, 1));
+		SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RD_ANY_REG, 0),
+			   SPI_MEM_OP_ADDR(nor->addr_mode_nbytes, addr, 0),
+			   SPI_MEM_OP_DUMMY(dummy, 0),
+			   SPI_MEM_OP_DATA_IN(1, NULL, 0));
+	u8 buf[2];
+	int ret;
+
+	spi_nor_setup_op(nor, &op, nor->reg_proto);
 
-	return spi_nor_read_write_reg(nor, &op, val);
+	/*
+	 * In Octal DTR mode, the number of address bytes is always 4 regardless
+	 * of addressing mode setting.
+	 */
+	if (nor->reg_proto == SNOR_PROTO_8_8_8_DTR)
+		op.addr.nbytes = 4;
+
+	/*
+	 * We don't want to read only one byte in DTR mode. So, read 2 and then
+	 * discard the second byte.
+	 */
+	if (spi_nor_protocol_is_dtr(nor->reg_proto))
+		op.data.nbytes = 2;
+
+	ret = spi_nor_read_write_reg(nor, &op, buf);
+	if (ret)
+		return ret;
+
+	*val = buf[0];
+
+	return 0;
 }
 
 static int spansion_write_any_reg(struct spi_nor *nor, u32 addr, u8 val)
 {
 	struct spi_mem_op op =
-		SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRAR, 1),
+		SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WR_ANY_REG, 1),
 			   SPI_MEM_OP_ADDR(nor->addr_mode_nbytes, addr, 1),
 			   SPI_MEM_OP_NO_DUMMY,
 			   SPI_MEM_OP_DATA_OUT(1, NULL, 1));
@@ -714,7 +738,7 @@
  */
 static int spansion_sr_ready(struct spi_nor *nor, u32 addr_base, u8 dummy)
 {
-	u32 reg_addr = addr_base + SPINOR_REG_ADDR_STR1V;
+	u32 reg_addr = addr_base + SPINOR_REG_CYPRESS_STR1V;
 	u8 sr;
 	int ret;
 
@@ -728,7 +752,7 @@
 		else
 			dev_dbg(nor->dev, "Programming Error occurred\n");
 
-		nor->write_reg(nor, SPINOR_OP_CLSR, NULL, 0);
+		nor->write_reg(nor, SPINOR_OP_CYPRESS_CLPEF, NULL, 0);
 		return -EIO;
 	}
 
@@ -1856,7 +1880,7 @@
 static int spansion_quad_enable_volatile(struct spi_nor *nor, u32 addr_base,
 					 u8 dummy)
 {
-	u32 addr = addr_base + SPINOR_REG_ADDR_CFR1V;
+	u32 addr = addr_base + SPINOR_REG_CYPRESS_CFR1V;
 
 	u8 cr;
 	int ret;
@@ -3293,11 +3317,11 @@
 	 * Read CR3V to check if uniform sector is selected. If not, assign an
 	 * erase hook that supports non-uniform erase.
 	 */
-	ret = spansion_read_any_reg(nor, SPINOR_REG_ADDR_CFR3V,
+	ret = spansion_read_any_reg(nor, SPINOR_REG_CYPRESS_CFR3V,
 				    S25FS_S_RDAR_DUMMY, &cfr3v);
 	if (ret)
 		return ret;
-	if (!(cfr3v & CFR3V_UNHYSA))
+	if (!(cfr3v & SPINOR_REG_CYPRESS_CFR3_UNISECT))
 		nor->erase = s25fs_s_erase_non_uniform;
 
 	return spi_nor_default_setup(nor, info, params);
@@ -3346,13 +3370,13 @@
 	.post_sfdp = s25fs_s_post_sfdp_fixup,
 };
 
-static int s25_mdp_ready(struct spi_nor *nor)
+static int s25_s28_mdp_ready(struct spi_nor *nor)
 {
 	u32 addr;
 	int ret;
 
 	for (addr = 0; addr < nor->mtd.size; addr += SZ_128M) {
-		ret = spansion_sr_ready(nor, addr, 0);
+		ret = spansion_sr_ready(nor, addr, nor->rdsr_dummy);
 		if (!ret)
 			return ret;
 	}
@@ -3374,15 +3398,15 @@
 	return 0;
 }
 
-static int s25_erase_non_uniform(struct spi_nor *nor, loff_t addr)
+static int s25_s28_erase_non_uniform(struct spi_nor *nor, loff_t addr)
 {
 	/* Support 32 x 4KB sectors at bottom */
 	return spansion_erase_non_uniform(nor, addr, SPINOR_OP_BE_4K_4B, 0,
 					  SZ_128K);
 }
 
-static int s25_setup(struct spi_nor *nor, const struct flash_info *info,
-		     const struct spi_nor_flash_parameter *params)
+static int s25_s28_setup(struct spi_nor *nor, const struct flash_info *info,
+			 const struct spi_nor_flash_parameter *params)
 {
 	int ret;
 	u8 cr;
@@ -3396,7 +3420,8 @@
 	 * uniform 128KB only due to complexity of non-uniform layout.
 	 */
 	if (nor->info->id[4] == S25FS256T_ID4) {
-		ret = spansion_read_any_reg(nor, SPINOR_REG_ADDR_ARCFN, 8, &cr);
+		ret = spansion_read_any_reg(nor, SPINOR_REG_CYPRESS_ARCFN, 8,
+					    &cr);
 		if (ret)
 			return ret;
 
@@ -3410,31 +3435,31 @@
 	 * Read CFR3V to check if uniform sector is selected. If not, assign an
 	 * erase hook that supports non-uniform erase.
 	 */
-	ret = spansion_read_any_reg(nor, SPINOR_REG_ADDR_CFR3V, 0, &cr);
+	ret = spansion_read_any_reg(nor, SPINOR_REG_CYPRESS_CFR3V, 0, &cr);
 	if (ret)
 		return ret;
-	if (!(cr & CFR3V_UNHYSA))
-		nor->erase = s25_erase_non_uniform;
+	if (!(cr & SPINOR_REG_CYPRESS_CFR3_UNISECT))
+		nor->erase = s25_s28_erase_non_uniform;
 
 	/*
 	 * For the multi-die package parts, the ready() hook is needed to check
 	 * all dies' status via read any register.
 	 */
 	if (nor->mtd.size > SZ_128M)
-		nor->ready = s25_mdp_ready;
+		nor->ready = s25_s28_mdp_ready;
 
 	return spi_nor_default_setup(nor, info, params);
 }
 
 static void s25_default_init(struct spi_nor *nor)
 {
-	nor->setup = s25_setup;
+	nor->setup = s25_s28_setup;
 }
 
-static int s25_post_bfpt_fixup(struct spi_nor *nor,
-			       const struct sfdp_parameter_header *header,
-			       const struct sfdp_bfpt *bfpt,
-			       struct spi_nor_flash_parameter *params)
+static int s25_s28_post_bfpt_fixup(struct spi_nor *nor,
+				   const struct sfdp_parameter_header *header,
+				   const struct sfdp_bfpt *bfpt,
+				   struct spi_nor_flash_parameter *params)
 {
 	int ret;
 	u32 addr;
@@ -3474,12 +3499,13 @@
 	 * dies are configured to 512B buffer.
 	 */
 	for (addr = 0; addr < params->size; addr += SZ_128M) {
-		ret = spansion_read_any_reg(nor, addr + SPINOR_REG_ADDR_CFR3V,
-					    0, &cfr3v);
+		ret = spansion_read_any_reg(nor,
+					    addr + SPINOR_REG_CYPRESS_CFR3V, 0,
+					    &cfr3v);
 		if (ret)
 			return ret;
 
-		if (!(cfr3v & CFR3V_PGMBUF)) {
+		if (!(cfr3v & SPINOR_REG_CYPRESS_CFR3_PGSZ)) {
 			params->page_size = 256;
 			return 0;
 		}
@@ -3507,7 +3533,7 @@
 
 static struct spi_nor_fixups s25_fixups = {
 	.default_init = s25_default_init,
-	.post_bfpt = s25_post_bfpt_fixup,
+	.post_bfpt = s25_s28_post_bfpt_fixup,
 	.post_sfdp = s25_post_sfdp_fixup,
 };
 
@@ -3539,97 +3565,57 @@
  */
 static int spi_nor_cypress_octal_dtr_enable(struct spi_nor *nor)
 {
-	struct spi_mem_op op;
+	u32 addr;
 	u8 buf;
-	u8 addr_width = 3;
 	int ret;
 
-	/* Use 24 dummy cycles for memory array reads. */
 	ret = write_enable(nor);
 	if (ret)
 		return ret;
 
-	buf = SPINOR_REG_CYPRESS_CFR2_MEMLAT_11_24;
-	op = (struct spi_mem_op)SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WR_ANY_REG, 1),
-			SPI_MEM_OP_ADDR(addr_width, SPINOR_REG_CYPRESS_CFR2V, 1),
-			SPI_MEM_OP_NO_DUMMY,
-			SPI_MEM_OP_DATA_OUT(1, &buf, 1));
-	ret = spi_mem_exec_op(nor->spi, &op);
-	if (ret) {
-		dev_warn(nor->dev,
-			 "failed to set default memory latency value: %d\n",
-			 ret);
-		return ret;
-	}
-	ret = spi_nor_wait_till_ready(nor);
-	if (ret)
-		return ret;
+	/* Use 24 dummy cycles for memory array reads. */
+	for (addr = 0; addr < nor->mtd.size; addr += SZ_128M) {
+		ret = spansion_read_any_reg(nor,
+					    addr + SPINOR_REG_CYPRESS_CFR2V, 0,
+					    &buf);
+		if (ret)
+			return ret;
 
+		buf &= ~SPINOR_REG_CYPRESS_CFR2_MEMLAT_MASK;
+		buf |= SPINOR_REG_CYPRESS_CFR2_MEMLAT_11_24;
+		ret = spansion_write_any_reg(nor,
+					     addr + SPINOR_REG_CYPRESS_CFR2V,
+					     buf);
+		if (ret) {
+			dev_warn(nor->dev, "failed to set default memory latency value: %d\n", ret);
+			return ret;
+		}
+	}
 	nor->read_dummy = 24;
 
-	/* Set the octal and DTR enable bits. */
 	ret = write_enable(nor);
 	if (ret)
 		return ret;
 
+	/* Set the octal and DTR enable bits. */
 	buf = SPINOR_REG_CYPRESS_CFR5_OCT_DTR_EN;
-	op = (struct spi_mem_op)SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WR_ANY_REG, 1),
-			SPI_MEM_OP_ADDR(addr_width, SPINOR_REG_CYPRESS_CFR5V, 1),
-			SPI_MEM_OP_NO_DUMMY,
-			SPI_MEM_OP_DATA_OUT(1, &buf, 1));
-	ret = spi_mem_exec_op(nor->spi, &op);
-	if (ret) {
-		dev_warn(nor->dev, "Failed to enable octal DTR mode\n");
-		return ret;
+	for (addr = 0; addr < nor->mtd.size; addr += SZ_128M) {
+		ret = spansion_write_any_reg(nor,
+					     addr + SPINOR_REG_CYPRESS_CFR5V,
+					     buf);
+		if (ret) {
+			dev_warn(nor->dev, "Failed to enable octal DTR mode\n");
+			return ret;
+		}
 	}
 
 	return 0;
 }
 
-static int s28hx_t_erase_non_uniform(struct spi_nor *nor, loff_t addr)
-{
-	/* Factory default configuration: 32 x 4 KiB sectors at bottom. */
-	return spansion_erase_non_uniform(nor, addr, SPINOR_OP_S28_SE_4K,
-					  0, SZ_128K);
-}
-
-static int s28hx_t_setup(struct spi_nor *nor, const struct flash_info *info,
-			 const struct spi_nor_flash_parameter *params)
-{
-	struct spi_mem_op op;
-	u8 buf;
-	u8 addr_width = 3;
-	int ret;
-
-	ret = spi_nor_wait_till_ready(nor);
-	if (ret)
-		return ret;
-
-	/*
-	 * Check CFR3V to check if non-uniform sector mode is selected. If it
-	 * is, set the erase hook to the non-uniform erase procedure.
-	 */
-	op = (struct spi_mem_op)
-		SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RD_ANY_REG, 1),
-			   SPI_MEM_OP_ADDR(addr_width,
-					   SPINOR_REG_CYPRESS_CFR3V, 1),
-			   SPI_MEM_OP_NO_DUMMY,
-			   SPI_MEM_OP_DATA_IN(1, &buf, 1));
-
-	ret = spi_mem_exec_op(nor->spi, &op);
-	if (ret)
-		return ret;
-
-	if (!(buf & SPINOR_REG_CYPRESS_CFR3_UNISECT))
-		nor->erase = s28hx_t_erase_non_uniform;
-
-	return spi_nor_default_setup(nor, info, params);
-}
-
 static void s28hx_t_default_init(struct spi_nor *nor)
 {
 	nor->octal_dtr_enable = spi_nor_cypress_octal_dtr_enable;
-	nor->setup = s28hx_t_setup;
+	nor->setup = s25_s28_setup;
 }
 
 static void s28hx_t_post_sfdp_fixup(struct spi_nor *nor,
@@ -3663,50 +3649,10 @@
 	params->rdsr_addr_nbytes = 4;
 }
 
-static int s28hx_t_post_bfpt_fixup(struct spi_nor *nor,
-				   const struct sfdp_parameter_header *bfpt_header,
-				   const struct sfdp_bfpt *bfpt,
-				   struct spi_nor_flash_parameter *params)
-{
-	struct spi_mem_op op;
-	u8 buf;
-	u8 addr_width = 3;
-	int ret;
-
-	/*
-	 * The BFPT table advertises a 512B page size but the page size is
-	 * actually configurable (with the default being 256B). Read from
-	 * CFR3V[4] and set the correct size.
-	 */
-	op = (struct spi_mem_op)
-		SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RD_ANY_REG, 1),
-			   SPI_MEM_OP_ADDR(addr_width, SPINOR_REG_CYPRESS_CFR3V, 1),
-			   SPI_MEM_OP_NO_DUMMY,
-			   SPI_MEM_OP_DATA_IN(1, &buf, 1));
-	ret = spi_mem_exec_op(nor->spi, &op);
-	if (ret)
-		return ret;
-
-	if (buf & SPINOR_REG_CYPRESS_CFR3_PGSZ)
-		params->page_size = 512;
-	else
-		params->page_size = 256;
-
-	/*
-	 * The BFPT advertises that it supports 4k erases, and the datasheet
-	 * says the same. But 4k erases did not work when testing. So, use 256k
-	 * erases for now.
-	 */
-	nor->erase_opcode = SPINOR_OP_SE_4B;
-	nor->mtd.erasesize = 0x40000;
-
-	return 0;
-}
-
 static struct spi_nor_fixups s28hx_t_fixups = {
 	.default_init = s28hx_t_default_init,
 	.post_sfdp = s28hx_t_post_sfdp_fixup,
-	.post_bfpt = s28hx_t_post_bfpt_fixup,
+	.post_bfpt = s25_s28_post_bfpt_fixup,
 };
 #endif /* CONFIG_SPI_FLASH_S28HX_T */
 
diff --git a/drivers/mtd/spi/spi-nor-ids.c b/drivers/mtd/spi/spi-nor-ids.c
index 8db522f..38a2874 100644
--- a/drivers/mtd/spi/spi-nor-ids.c
+++ b/drivers/mtd/spi/spi-nor-ids.c
@@ -239,6 +239,8 @@
 			SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
 	{ INFO("is25wx256",  0x9d5b19, 0, 128 * 1024, 256,
 			SECT_4K | USE_FSR | SPI_NOR_OCTAL_READ | SPI_NOR_4B_OPCODES) },
+	{ INFO("is25lx512",  0x9d5a1a, 0, 64 * 1024, 1024,
+			SECT_4K | USE_FSR | SPI_NOR_4B_OPCODES | SPI_NOR_HAS_TB) },
 #endif
 #ifdef CONFIG_SPI_FLASH_MACRONIX	/* MACRONIX */
 	/* Macronix */
@@ -381,6 +383,7 @@
 	{ INFO("s28hl01gt",  0x345a1b,      0, 256 * 1024, 512, SPI_NOR_OCTAL_DTR_READ) },
 	{ INFO("s28hs512t",  0x345b1a,      0, 256 * 1024, 256, SPI_NOR_OCTAL_DTR_READ) },
 	{ INFO("s28hs01gt",  0x345b1b,      0, 256 * 1024, 512, SPI_NOR_OCTAL_DTR_READ) },
+	{ INFO("s28hs02gt",  0x345b1c,      0, 256 * 1024, 1024, SPI_NOR_OCTAL_DTR_READ) },
 #endif
 #endif
 #ifdef CONFIG_SPI_FLASH_SST		/* SST */
@@ -554,6 +557,10 @@
 	{ INFO("XM25QH64C", 0x204017, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
 	{ INFO("XM25QH128A", 0x207018, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
 	{ INFO("XM25QU128C", 0x204118, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+	{ INFO("XM25QH256C", 0x204019, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },
+	{ INFO("XM25QU256C", 0x204119, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },
+	{ INFO("XM25QH512C", 0x204020, 0, 64 * 1024, 1024, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },
+	{ INFO("XM25QU512C", 0x204120, 0, 64 * 1024, 1024, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },
 #endif
 #ifdef CONFIG_SPI_FLASH_XTX
 	/* XTX Technology Limited */
diff --git a/drivers/net/sun8i_emac.c b/drivers/net/sun8i_emac.c
index a12f7e3..8bff4fe 100644
--- a/drivers/net/sun8i_emac.c
+++ b/drivers/net/sun8i_emac.c
@@ -833,11 +833,8 @@
 	priv->use_internal_phy = false;
 
 	offset = fdtdec_lookup_phandle(gd->fdt_blob, node, "phy-handle");
-	if (offset < 0) {
-		debug("%s: Cannot find PHY address\n", __func__);
-		return -EINVAL;
-	}
-	priv->phyaddr = fdtdec_get_int(gd->fdt_blob, offset, "reg", -1);
+	if (offset >= 0)
+		priv->phyaddr = fdtdec_get_int(gd->fdt_blob, offset, "reg", -1);
 
 	pdata->phy_interface = dev_read_phy_mode(dev);
 	debug("phy interface %d\n", pdata->phy_interface);
diff --git a/drivers/spi/cadence_ospi_versal.c b/drivers/spi/cadence_ospi_versal.c
index e02a3b3..c2be307 100644
--- a/drivers/spi/cadence_ospi_versal.c
+++ b/drivers/spi/cadence_ospi_versal.c
@@ -18,9 +18,6 @@
 #include "cadence_qspi.h"
 #include <dt-bindings/power/xlnx-versal-power.h>
 
-#define CMD_4BYTE_READ  0x13
-#define CMD_4BYTE_FAST_READ  0x0C
-
 int cadence_qspi_apb_dma_read(struct cadence_spi_priv *priv,
 			      const struct spi_mem_op *op)
 {
diff --git a/drivers/spi/cadence_qspi.h b/drivers/spi/cadence_qspi.h
index 12825f8..693474a 100644
--- a/drivers/spi/cadence_qspi.h
+++ b/drivers/spi/cadence_qspi.h
@@ -33,6 +33,10 @@
 #define CQSPI_DUMMY_BYTES_MAX                   4
 #define CQSPI_DUMMY_CLKS_MAX                    31
 
+#define CMD_4BYTE_FAST_READ			0x0C
+#define CMD_4BYTE_OCTAL_READ			0x7c
+#define CMD_4BYTE_READ				0x13
+
 /****************************************************************************
  * Controller's configuration and status register (offset from QSPI_BASE)
  ****************************************************************************/
diff --git a/drivers/spi/cadence_qspi_apb.c b/drivers/spi/cadence_qspi_apb.c
index d033184..fb90532 100644
--- a/drivers/spi/cadence_qspi_apb.c
+++ b/drivers/spi/cadence_qspi_apb.c
@@ -469,6 +469,9 @@
 	else
 		opcode = op->cmd.opcode;
 
+	if (opcode == CMD_4BYTE_OCTAL_READ && !priv->dtr)
+		opcode = CMD_4BYTE_FAST_READ;
+
 	reg = opcode << CQSPI_REG_CMDCTRL_OPCODE_LSB;
 
 	/* Set up dummy cycles. */
diff --git a/drivers/spi/designware_spi.c b/drivers/spi/designware_spi.c
index 1c7d0ca..98908c5 100644
--- a/drivers/spi/designware_spi.c
+++ b/drivers/spi/designware_spi.c
@@ -111,6 +111,9 @@
 #define SR_TX_ERR			BIT(5)
 #define SR_DCOL				BIT(6)
 
+/* Bit field in RISR */
+#define RISR_INT_RXOI			BIT(3)
+
 #define RX_TIMEOUT			1000		/* timeout in ms */
 
 struct dw_spi_plat {
@@ -588,7 +591,7 @@
 	struct dw_spi_priv *priv = dev_get_priv(bus);
 	u8 op_len = op->cmd.nbytes + op->addr.nbytes + op->dummy.nbytes;
 	u8 op_buf[op_len];
-	u32 cr0;
+	u32 cr0, sts;
 
 	if (read)
 		priv->tmode = CTRLR0_TMOD_EPROMREAD;
@@ -632,12 +635,21 @@
 	 * them to fail because we are not reading/writing the fifo fast enough.
 	 */
 	if (read) {
-		priv->rx = op->data.buf.in;
+		void *prev_rx = priv->rx = op->data.buf.in;
 		priv->rx_end = priv->rx + op->data.nbytes;
 
 		dw_write(priv, DW_SPI_SER, 1 << spi_chip_select(slave->dev));
-		while (priv->rx != priv->rx_end)
+		while (priv->rx != priv->rx_end) {
 			dw_reader(priv);
+			if (prev_rx == priv->rx) {
+				sts = dw_read(priv, DW_SPI_RISR);
+				if (sts & RISR_INT_RXOI) {
+					dev_err(bus, "FIFO overflow on Rx\n");
+					return -EIO;
+				}
+			}
+			prev_rx = priv->rx;
+		}
 	} else {
 		u32 val;
 
diff --git a/drivers/video/console_core.c b/drivers/video/console_core.c
index d17764d..9393636 100644
--- a/drivers/video/console_core.c
+++ b/drivers/video/console_core.c
@@ -225,7 +225,7 @@
 {
 	info->name = fonts[seq].name;
 
-	return 0;
+	return info->name ? 0 : -ENOENT;
 }
 
 int console_simple_select_font(struct udevice *dev, const char *name, uint size)
diff --git a/drivers/video/console_truetype.c b/drivers/video/console_truetype.c
index 14fb81e..547e5a8 100644
--- a/drivers/video/console_truetype.c
+++ b/drivers/video/console_truetype.c
@@ -397,7 +397,10 @@
 
 					if (vid_priv->colour_bg)
 						val = 255 - val;
-					out = val | val << 8 | val << 16;
+					if (vid_priv->format == VIDEO_X2R10G10B10)
+						out = val << 2 | val << 12 | val << 22;
+					else
+						out = val | val << 8 | val << 16;
 					if (vid_priv->colour_fg)
 						*dst++ |= out;
 					else
@@ -911,7 +914,10 @@
 				for (i = 0; i < width; i++) {
 					int out;
 
-					out = val | val << 8 | val << 16;
+					if (vid_priv->format == VIDEO_X2R10G10B10)
+						out = val << 2 | val << 12 | val << 22;
+					else
+						out = val | val << 8 | val << 16;
 					if (vid_priv->colour_fg)
 						*dst++ |= out;
 					else
diff --git a/drivers/video/tidss/tidss_drv.c b/drivers/video/tidss/tidss_drv.c
index e285f25..1380c6b 100644
--- a/drivers/video/tidss/tidss_drv.c
+++ b/drivers/video/tidss/tidss_drv.c
@@ -107,7 +107,7 @@
 
 	.num_planes = 2,
 	/* note: vid is plane_id 0 and vidl1 is plane_id 1 */
-	.vid_name = { "vidl1", "vid1" },
+	.vid_name = { "vidl1", "vid" },
 	.vid_lite = { true, false },
 	.vid_order = { 1, 0 },
 };
@@ -814,13 +814,13 @@
 		priv->bus_format = &dss_bus_formats[8];
 
 	/* Common address */
-	priv->base_common = dev_remap_addr_index(dev, 0);
+	priv->base_common = dev_remap_addr_name(dev, priv->feat->common);
 	if (!priv->base_common)
 		return -EINVAL;
 
 	/* plane address setup and enable */
 	for (i = 0; i < priv->feat->num_planes; i++) {
-		priv->base_vid[i] = dev_remap_addr_index(dev, i + 2);
+		priv->base_vid[i] = dev_remap_addr_name(dev, priv->feat->vid_name[i]);
 		if (!priv->base_vid[i])
 			return -EINVAL;
 	}
@@ -841,8 +841,8 @@
 
 	/* video port address clocks and enable */
 	for (i = 0; i < priv->feat->num_vps; i++) {
-		priv->base_ovr[i] = dev_remap_addr_index(dev, i + 4);
-		priv->base_vp[i] = dev_remap_addr_index(dev, i + 6);
+		priv->base_ovr[i] = dev_remap_addr_name(dev, priv->feat->ovr_name[i]);
+		priv->base_vp[i] = dev_remap_addr_name(dev, priv->feat->vp_name[i]);
 	}
 
 	ret = clk_get_by_name(dev, "vp1", &priv->vp_clk[0]);
diff --git a/drivers/video/video-uclass.c b/drivers/video/video-uclass.c
index f743ed7..3571e62 100644
--- a/drivers/video/video-uclass.c
+++ b/drivers/video/video-uclass.c
@@ -123,6 +123,9 @@
 	struct udevice *dev;
 	ulong size;
 
+	if (IS_ENABLED(CONFIG_SPL_VIDEO_HANDOFF) && spl_phase() == PHASE_BOARD_F)
+		return 0;
+
 	gd->video_top = *addrp;
 	for (uclass_find_first_device(UCLASS_VIDEO, &dev);
 	     dev;
@@ -141,16 +144,6 @@
 	debug("Video frame buffers from %lx to %lx\n", gd->video_bottom,
 	      gd->video_top);
 
-	if (spl_phase() == PHASE_SPL && CONFIG_IS_ENABLED(VIDEO_HANDOFF)) {
-		struct video_handoff *ho;
-
-		ho = bloblist_add(BLOBLISTT_U_BOOT_VIDEO, sizeof(*ho), 0);
-		if (!ho)
-			return log_msg_ret("blf", -ENOENT);
-		ho->fb = *addrp;
-		ho->size = size;
-	}
-
 	return 0;
 }
 
@@ -208,11 +201,14 @@
 
 int video_reserve_from_bloblist(struct video_handoff *ho)
 {
+	if (!ho->fb || ho->size == 0)
+		return -ENOENT;
+
 	gd->video_bottom = ho->fb;
 	gd->fb_base = ho->fb;
 	gd->video_top = ho->fb + ho->size;
-	debug("Reserving %luk for video using blob at: %08x\n",
-	      ((unsigned long)ho->size) >> 10, (u32)ho->fb);
+	debug("%s: Reserving %lx bytes at %08x as per bloblist received\n",
+	      __func__, (unsigned long)ho->size, (u32)ho->fb);
 
 	return 0;
 }
@@ -546,6 +542,26 @@
 
 	priv->fb_size = priv->line_length * priv->ysize;
 
+	/*
+	 * Set up video handoff fields for passing video blob to next stage
+	 * NOTE:
+	 * This assumes that reserved video memory only uses a single framebuffer
+	 */
+	if (spl_phase() == PHASE_SPL && CONFIG_IS_ENABLED(BLOBLIST)) {
+		struct video_handoff *ho;
+
+		ho = bloblist_add(BLOBLISTT_U_BOOT_VIDEO, sizeof(*ho), 0);
+		if (!ho)
+			return log_msg_ret("blf", -ENOENT);
+		ho->fb = gd->video_bottom;
+		/* Fill aligned size here as calculated in video_reserve() */
+		ho->size = gd->video_top - gd->video_bottom;
+		ho->xsize = priv->xsize;
+		ho->ysize = priv->ysize;
+		ho->line_length = priv->line_length;
+		ho->bpix = priv->bpix;
+	}
+
 	if (IS_ENABLED(CONFIG_VIDEO_COPY) && plat->copy_base)
 		priv->copy_fb = map_sysmem(plat->copy_base, plat->size);
 
diff --git a/env/sf.c b/env/sf.c
index a425ecc..8f5c03b 100644
--- a/env/sf.c
+++ b/env/sf.c
@@ -210,8 +210,10 @@
 		saved_size = sect_size - CONFIG_ENV_SIZE;
 		saved_offset = CONFIG_ENV_OFFSET + CONFIG_ENV_SIZE;
 		saved_buffer = malloc(saved_size);
-		if (!saved_buffer)
+		if (!saved_buffer) {
+			ret = -ENOMEM;
 			goto done;
+		}
 
 		ret = spi_flash_read(env_flash, saved_offset,
 			saved_size, saved_buffer);
diff --git a/include/configs/sunxi-common.h b/include/configs/sunxi-common.h
index b8ca77d..b29a25d 100644
--- a/include/configs/sunxi-common.h
+++ b/include/configs/sunxi-common.h
@@ -14,8 +14,9 @@
 
 #include <linux/stringify.h>
 
-/* Serial & console */
-/* ns16550 reg in the low bits of cpu reg */
+/****************************************************************************
+ *                  base addresses for the SPL UART driver                  *
+ ****************************************************************************/
 #ifdef CONFIG_MACH_SUNIV
 /* suniv doesn't have apb2 and uart is connected to apb1 */
 #define CFG_SYS_NS16550_CLK		100000000
@@ -31,8 +32,9 @@
 # define CFG_SYS_NS16550_COM5		SUNXI_R_UART_BASE
 #endif
 
-/* CPU */
-
+/****************************************************************************
+ *                             DRAM base address                            *
+ ****************************************************************************/
 /*
  * The DRAM Base differs between some models. We cannot use macros for the
  * CONFIG_FOO defines which contain the DRAM base address since they end
@@ -52,16 +54,6 @@
 /* V3s do not have enough memory to place code at 0x4a000000 */
 #endif
 
-/*
- * The A80's A1 sram starts at 0x00010000 rather then at 0x00000000 and is
- * slightly bigger. Note that it is possible to map the first 32 KiB of the
- * A1 at 0x00000000 like with older SoCs by writing 0x16aa0001 to the
- * undocumented 0x008000e0 SYS_CTRL register. Where the 16aa is a key and
- * the 1 actually activates the mapping of the first 32 KiB to 0x00000000.
- * A64 and H5 also has SRAM A1 at 0x00010000, but no magic remap register
- * is known yet.
- * H6 has SRAM A1 at 0x00020000.
- */
 #define CFG_SYS_INIT_RAM_ADDR	CONFIG_SUNXI_SRAM_ADDRESS
 /* FIXME: this may be larger on some SoCs */
 #define CFG_SYS_INIT_RAM_SIZE	0x8000 /* 32 KiB */
@@ -69,36 +61,13 @@
 #define PHYS_SDRAM_0			CFG_SYS_SDRAM_BASE
 #define PHYS_SDRAM_0_SIZE		0x80000000 /* 2 GiB */
 
-/*
- * Miscellaneous configurable options
- */
-
-/* FLASH and environment organization */
-
+/****************************************************************************
+ *           environment variables holding default load addresses           *
+ ****************************************************************************/
 /*
  * We cannot use expressions here, because expressions won't be evaluated in
  * autoconf.mk.
  */
-#if CONFIG_SUNXI_SRAM_ADDRESS == 0x10000
-#ifdef CONFIG_ARM64
-/* end of SRAM A2 for now, as SRAM A1 is pretty tight for an ARM64 build */
-#define LOW_LEVEL_SRAM_STACK		0x00054000
-#else
-#define LOW_LEVEL_SRAM_STACK		0x00018000
-#endif /* !CONFIG_ARM64 */
-#elif CONFIG_SUNXI_SRAM_ADDRESS == 0x20000
-#ifdef CONFIG_MACH_SUN50I_H616
-#define LOW_LEVEL_SRAM_STACK		0x52a00		/* below FEL buffers */
-#else
-/* end of SRAM A2 on H6 for now */
-#define LOW_LEVEL_SRAM_STACK		0x00118000
-#endif
-#else
-#define LOW_LEVEL_SRAM_STACK		0x00008000	/* End of sram */
-#endif
-
-/* Ethernet support */
-
 #ifdef CONFIG_ARM64
 /*
  * Boards seem to come with at least 512MB of DRAM.
@@ -174,15 +143,11 @@
 	"ramdisk_addr_r=" RAMDISK_ADDR_R "\0"
 
 #ifdef CONFIG_ARM64
-
 #define MEM_LAYOUT_ENV_EXTRA_SETTINGS \
 	"kernel_comp_addr_r=" KERNEL_COMP_ADDR_R "\0" \
 	"kernel_comp_size=" KERNEL_COMP_SIZE "\0"
-
 #else
-
 #define MEM_LAYOUT_ENV_EXTRA_SETTINGS ""
-
 #endif
 
 #define DFU_ALT_INFO_RAM \
@@ -191,6 +156,9 @@
 	"fdt ram " FDT_ADDR_R " 0x100000;" \
 	"ramdisk ram " RAMDISK_ADDR_R " 0x4000000\0"
 
+/****************************************************************************
+ *                  definitions for the distro boot system                  *
+ ****************************************************************************/
 #ifdef CONFIG_MMC
 #if CONFIG_MMC_SUNXI_SLOT_EXTRA != -1
 #define BOOTENV_DEV_MMC_AUTO(devtypeu, devtypel, instance)		\
diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
index 2861b73..d1dbf3e 100644
--- a/include/linux/mtd/spi-nor.h
+++ b/include/linux/mtd/spi-nor.h
@@ -136,14 +136,6 @@
 #define SPINOR_OP_BRRD		0x16	/* Bank register read */
 #define SPINOR_OP_CLSR		0x30	/* Clear status register 1 */
 #define SPINOR_OP_EX4B_CYPRESS	0xB8	/* Exit 4-byte mode */
-#define SPINOR_OP_RDAR		0x65	/* Read any register */
-#define SPINOR_OP_WRAR		0x71	/* Write any register */
-#define SPINOR_REG_ADDR_STR1V	0x00800000
-#define SPINOR_REG_ADDR_CFR1V	0x00800002
-#define SPINOR_REG_ADDR_CFR3V	0x00800004
-#define SPINOR_REG_ADDR_ARCFN	0x00000006
-#define CFR3V_UNHYSA		BIT(3)	/* Uniform sectors or not */
-#define CFR3V_PGMBUF		BIT(4)	/* Program buffer size */
 
 /* Used for Micron flashes only. */
 #define SPINOR_OP_RD_EVCR	0x65	/* Read EVCR register */
@@ -188,8 +180,12 @@
 /* For Cypress flash. */
 #define SPINOR_OP_RD_ANY_REG			0x65	/* Read any register */
 #define SPINOR_OP_WR_ANY_REG			0x71	/* Write any register */
-#define SPINOR_OP_S28_SE_4K			0x21
+#define SPINOR_OP_CYPRESS_CLPEF			0x82	/* Clear P/E err flag */
+#define SPINOR_REG_CYPRESS_ARCFN		0x00000006
+#define SPINOR_REG_CYPRESS_STR1V		0x00800000
+#define SPINOR_REG_CYPRESS_CFR1V		0x00800002
 #define SPINOR_REG_CYPRESS_CFR2V		0x00800003
+#define SPINOR_REG_CYPRESS_CFR2_MEMLAT_MASK	GENMASK(3, 0)
 #define SPINOR_REG_CYPRESS_CFR2_MEMLAT_11_24	0xb
 #define SPINOR_REG_CYPRESS_CFR3V		0x00800004
 #define SPINOR_REG_CYPRESS_CFR3_PGSZ		BIT(4) /* Page size. */
diff --git a/include/spl.h b/include/spl.h
index 0952188..043875f 100644
--- a/include/spl.h
+++ b/include/spl.h
@@ -889,6 +889,16 @@
 
 int spl_ymodem_load_image(struct spl_image_info *spl_image,
 			  struct spl_boot_device *bootdev);
+/**
+ * spl_reserve_video_from_ram_top() - Reserve framebuffer memory from end of RAM
+ *
+ * This enforces framebuffer reservation at SPL stage from end of RAM so that
+ * next stage can directly skip this pre-reserved area before carrying out
+ * further reservations. The allocation address is stored in struct video_uc_plat.
+ *
+ * Return: 0 on success, otherwise error code
+ */
+int spl_reserve_video_from_ram_top(void);
 
 /**
  * spl_invoke_atf - boot using an ARM trusted firmware image
diff --git a/test/py/requirements.txt b/test/py/requirements.txt
index c3cc916..0f67c3c 100644
--- a/test/py/requirements.txt
+++ b/test/py/requirements.txt
@@ -11,10 +11,10 @@
 packaging==23.2
 pbr==5.4.3
 pluggy==0.13.0
-py==1.10.0
+py==1.11.0
 pycryptodomex==3.19.1
 pyelftools==0.27
-pygit2==1.9.2
+pygit2==1.13.3
 pyparsing==3.0.7
 pytest==6.2.5
 pytest-xdist==2.5.0
diff --git a/test/py/tests/test_gpio.py b/test/py/tests/test_gpio.py
index 0af186f..3e16e63 100644
--- a/test/py/tests/test_gpio.py
+++ b/test/py/tests/test_gpio.py
@@ -85,6 +85,13 @@
         'gpio_ip_pin_clear':'66',
         'gpio_clear_value': 'value is 0',
         'gpio_set_value': 'value is 1',
+        # GPIO pin list to test gpio functionality for each pins, pin should be
+        # pin names (str)
+        'gpio_pin_list': ['gpio@1000031', 'gpio@1000032', 'gpio@20000033'],
+        # GPIO input output list for shorted gpio pins to test gpio
+        # functionality for each of pairs, where the first element is
+        # configured as input and second as output
+        'gpio_ip_op_list': [['gpio0', 'gpio1'], ['gpio2', 'gpio3']],
 }
 """
 
@@ -223,3 +230,86 @@
     response = u_boot_console.run_command(cmd)
     good_response = gpio_set_value
     assert good_response in response
+
+@pytest.mark.buildconfigspec('cmd_gpio')
+def test_gpio_pins_generic(u_boot_console):
+    """Test various gpio related functionality, such as the input, set, clear,
+       and toggle for the set of gpio pin list.
+
+       Specific set of gpio pins (by mentioning gpio pin name) configured as
+       input (mentioned as 'gpio_pin_list') to be tested for multiple gpio
+       commands.
+    """
+
+    f = u_boot_console.config.env.get('env__gpio_dev_config', False)
+    if not f:
+        pytest.skip('gpio not configured')
+
+    gpio_pins = f.get('gpio_pin_list', None)
+    if not gpio_pins:
+        pytest.skip('gpio pin list are not configured')
+
+    for gpin in gpio_pins:
+        # gpio input
+        u_boot_console.run_command(f'gpio input {gpin}')
+        expected_response = f'{gpin}: input:'
+        response = u_boot_console.run_command(f'gpio status -a {gpin}')
+        assert expected_response in response
+
+        # gpio set
+        u_boot_console.run_command(f'gpio set {gpin}')
+        expected_response = f'{gpin}: output: 1'
+        response = u_boot_console.run_command(f'gpio status -a {gpin}')
+        assert expected_response in response
+
+        # gpio clear
+        u_boot_console.run_command(f'gpio clear {gpin}')
+        expected_response = f'{gpin}: output: 0'
+        response = u_boot_console.run_command(f'gpio status -a {gpin}')
+        assert expected_response in response
+
+        # gpio toggle
+        u_boot_console.run_command(f'gpio toggle {gpin}')
+        expected_response = f'{gpin}: output: 1'
+        response = u_boot_console.run_command(f'gpio status -a {gpin}')
+        assert expected_response in response
+
+@pytest.mark.buildconfigspec('cmd_gpio')
+def test_gpio_pins_input_output_generic(u_boot_console):
+    """Test gpio related functionality such as input and output for the list of
+       shorted gpio pins provided as a pair of input and output pins. This test
+       will fail, if the gpio pins are not shorted properly.
+
+       Specific set of shorted gpio pins (by mentioning gpio pin name)
+       configured as input and output (mentioned as 'gpio_ip_op_list') as a
+       pair to be tested for gpio input output case.
+    """
+
+    f = u_boot_console.config.env.get('env__gpio_dev_config', False)
+    if not f:
+        pytest.skip('gpio not configured')
+
+    gpio_pins = f.get('gpio_ip_op_list', None)
+    if not gpio_pins:
+        pytest.skip('gpio pin list for input and output are not configured')
+
+    for gpins in gpio_pins:
+        u_boot_console.run_command(f'gpio input {gpins[0]}')
+        expected_response = f'{gpins[0]}: input:'
+        response = u_boot_console.run_command(f'gpio status -a {gpins[0]}')
+        assert expected_response in response
+
+        u_boot_console.run_command(f'gpio set {gpins[1]}')
+        expected_response = f'{gpins[1]}: output:'
+        response = u_boot_console.run_command(f'gpio status -a {gpins[1]}')
+        assert expected_response in response
+
+        u_boot_console.run_command(f'gpio clear {gpins[1]}')
+        expected_response = f'{gpins[0]}: input: 0'
+        response = u_boot_console.run_command(f'gpio status -a {gpins[0]}')
+        assert expected_response in response
+
+        u_boot_console.run_command(f'gpio set {gpins[1]}')
+        expected_response = f'{gpins[0]}: input: 1'
+        response = u_boot_console.run_command(f'gpio status -a {gpins[0]}')
+        assert expected_response in response
diff --git a/test/py/tests/test_mmc.py b/test/py/tests/test_mmc.py
new file mode 100644
index 0000000..a96c4e8
--- /dev/null
+++ b/test/py/tests/test_mmc.py
@@ -0,0 +1,671 @@
+# SPDX-License-Identifier: GPL-2.0
+# (C) Copyright 2023, Advanced Micro Devices, Inc.
+
+import pytest
+import random
+import re
+import u_boot_utils
+
+"""
+Note: This test doesn't rely on boardenv_* configuration values but it can
+change the test behavior. To test MMC file system cases (fat32, ext2, ext4),
+MMC device should be formatted and valid partitions should be created for
+different file system, otherwise it may leads to failure. This test will be
+skipped if the MMC device is not detected.
+
+For example:
+
+# Setup env__mmc_device_test_skip to not skipping the test. By default, its
+# value is set to True. Set it to False to run all tests for MMC device.
+env__mmc_device_test_skip = False
+"""
+
+mmc_set_up = False
+controllers = 0
+devices = {}
+
+def setup_mmc(u_boot_console):
+    if u_boot_console.config.env.get('env__mmc_device_test_skip', True):
+        pytest.skip('MMC device test is not enabled')
+
+@pytest.mark.buildconfigspec('cmd_mmc')
+def test_mmc_list(u_boot_console):
+    setup_mmc(u_boot_console)
+    output = u_boot_console.run_command('mmc list')
+    if 'No MMC device available' in output:
+        pytest.skip('No SD/MMC/eMMC controller available')
+
+    if 'Card did not respond to voltage select' in output:
+        pytest.skip('No SD/MMC card present')
+
+    array = output.split()
+    global devices
+    global controllers
+    controllers = int(len(array) / 2)
+    for x in range(0, controllers):
+        y = x * 2
+        devices[x] = {}
+        devices[x]['name'] = array[y]
+
+    global mmc_set_up
+    mmc_set_up = True
+
+@pytest.mark.buildconfigspec('cmd_mmc')
+def test_mmc_dev(u_boot_console):
+    if not mmc_set_up:
+        pytest.skip('No SD/MMC/eMMC controller available')
+
+    fail = 0
+    for x in range(0, controllers):
+        devices[x]['detected'] = 'yes'
+        output = u_boot_console.run_command('mmc dev %d' % x)
+
+        # Some sort of switch here
+        if 'Card did not respond to voltage select' in output:
+            fail = 1
+            devices[x]['detected'] = 'no'
+
+        if 'no mmc device at slot' in output:
+            devices[x]['detected'] = 'no'
+
+        if 'MMC: no card present' in output:
+            devices[x]['detected'] = 'no'
+
+    if fail:
+        pytest.fail('Card not present')
+
+@pytest.mark.buildconfigspec('cmd_mmc')
+def test_mmcinfo(u_boot_console):
+    if not mmc_set_up:
+        pytest.skip('No SD/MMC/eMMC controller available')
+
+    for x in range(0, controllers):
+        if devices[x]['detected'] == 'yes':
+            u_boot_console.run_command('mmc dev %d' % x)
+            output = u_boot_console.run_command('mmcinfo')
+            if 'busy timeout' in output:
+                pytest.skip('No SD/MMC/eMMC device present')
+
+            obj = re.search(r'Capacity: (\d+|\d+[\.]?\d)', output)
+            try:
+                capacity = float(obj.groups()[0])
+                print(capacity)
+                devices[x]['capacity'] = capacity
+                print('Capacity of dev %d is: %g GiB' % (x, capacity))
+            except ValueError:
+                pytest.fail('MMC capacity not recognized')
+
+@pytest.mark.buildconfigspec('cmd_mmc')
+def test_mmc_info(u_boot_console):
+    if not mmc_set_up:
+        pytest.skip('No SD/MMC/eMMC controller available')
+
+    for x in range(0, controllers):
+        if devices[x]['detected'] == 'yes':
+            u_boot_console.run_command('mmc dev %d' % x)
+
+            output = u_boot_console.run_command('mmc info')
+
+            obj = re.search(r'Capacity: (\d+|\d+[\.]?\d)', output)
+            try:
+                capacity = float(obj.groups()[0])
+                print(capacity)
+                if devices[x]['capacity'] != capacity:
+                    pytest.fail("MMC capacity doesn't match mmcinfo")
+
+            except ValueError:
+                pytest.fail('MMC capacity not recognized')
+
+@pytest.mark.buildconfigspec('cmd_mmc')
+def test_mmc_rescan(u_boot_console):
+    if not mmc_set_up:
+        pytest.skip('No SD/MMC/eMMC controller available')
+
+    if not devices:
+        pytest.skip('No devices detected')
+
+    for x in range(0, controllers):
+        if devices[x]['detected'] == 'yes':
+            u_boot_console.run_command('mmc dev %d' % x)
+            output = u_boot_console.run_command('mmc rescan')
+            if output:
+                pytest.fail('mmc rescan has something to check')
+            output = u_boot_console.run_command('echo $?')
+            assert output.endswith('0')
+
+@pytest.mark.buildconfigspec('cmd_mmc')
+def test_mmc_part(u_boot_console):
+    if not mmc_set_up:
+        pytest.skip('No SD/MMC/eMMC controller available')
+
+    if not devices:
+        pytest.skip('No devices detected')
+
+    for x in range(0, controllers):
+        if devices[x]['detected'] == 'yes':
+            u_boot_console.run_command('mmc dev %d' % x)
+            output = u_boot_console.run_command('mmc part')
+
+            lines = output.split('\n')
+            part_fat = []
+            part_ext = []
+            for line in lines:
+                obj = re.search(
+                        r'(\d)\s+\d+\s+\d+\s+\w+\d+\w+-\d+\s+(\d+\w+)', line)
+                if obj:
+                    part_id = int(obj.groups()[0])
+                    part_type = obj.groups()[1]
+                    print('part_id:%d, part_type:%s' % (part_id, part_type))
+
+                    if part_type in ['0c', '0b', '0e']:
+                        print('Fat detected')
+                        part_fat.append(part_id)
+                    elif part_type == '83':
+                        print('ext detected')
+                        part_ext.append(part_id)
+                    else:
+                        pytest.fail('Unsupported Filesystem on device %d' % x)
+            devices[x]['ext4'] = part_ext
+            devices[x]['ext2'] = part_ext
+            devices[x]['fat'] = part_fat
+
+            if not part_ext and not part_fat:
+                pytest.fail('No partition detected on device %d' % x)
+
+@pytest.mark.buildconfigspec('cmd_mmc')
+@pytest.mark.buildconfigspec('cmd_fat')
+def test_mmc_fatls_fatinfo(u_boot_console):
+    if not mmc_set_up:
+        pytest.skip('No SD/MMC/eMMC controller available')
+
+    if not devices:
+        pytest.skip('No devices detected')
+
+    part_detect = 0
+    fs = 'fat'
+    for x in range(0, controllers):
+        if devices[x]['detected'] == 'yes':
+            u_boot_console.run_command('mmc dev %d' % x)
+            try:
+                partitions = devices[x][fs]
+            except:
+                print('No %s table on this device' % fs.upper())
+                continue
+
+            for part in partitions:
+                output = u_boot_console.run_command(
+                        'fatls mmc %d:%s' % (x, part))
+                if 'Unrecognized filesystem type' in output:
+                    partitions.remove(part)
+                    pytest.fail('Unrecognized filesystem')
+
+                if not re.search(r'\d file\(s\), \d dir\(s\)', output):
+                    pytest.fail('%s read failed on device %d' % (fs.upper, x))
+                output = u_boot_console.run_command(
+                        'fatinfo mmc %d:%s' % (x, part))
+                string = 'Filesystem: %s' % fs.upper
+                if re.search(string, output):
+                    pytest.fail('%s FS failed on device %d' % (fs.upper(), x))
+                part_detect = 1
+
+    if not part_detect:
+        pytest.skip('No %s partition detected' % fs.upper())
+
+
+@pytest.mark.buildconfigspec('cmd_mmc')
+@pytest.mark.buildconfigspec('cmd_fat')
+@pytest.mark.buildconfigspec('cmd_memory')
+def test_mmc_fatload_fatwrite(u_boot_console):
+    if not mmc_set_up:
+        pytest.skip('No SD/MMC/eMMC controller available')
+
+    if not devices:
+        pytest.skip('No devices detected')
+
+    part_detect = 0
+    fs = 'fat'
+    for x in range(0, controllers):
+        if devices[x]['detected'] == 'yes':
+            u_boot_console.run_command('mmc dev %d' % x)
+            try:
+                partitions = devices[x][fs]
+            except:
+                print('No %s table on this device' % fs.upper())
+                continue
+
+            for part in partitions:
+                part_detect = 1
+                addr = u_boot_utils.find_ram_base(u_boot_console)
+                devices[x]['addr_%d' % part] = addr
+                size = random.randint(4, 1 * 1024 * 1024)
+                devices[x]['size_%d' % part] = size
+                # count CRC32
+                output = u_boot_console.run_command('crc32 %x %x' % (addr, size))
+                m = re.search('==> (.+?)', output)
+                if not m:
+                    pytest.fail('CRC32 failed')
+                expected_crc32 = m.group(1)
+                devices[x]['expected_crc32_%d' % part] = expected_crc32
+                # do write
+                file = '%s_%d' % ('uboot_test', size)
+                devices[x]['file_%d' % part] = file
+                output = u_boot_console.run_command(
+                    '%swrite mmc %d:%s %x %s %x' % (fs, x, part, addr, file, size)
+                )
+                assert 'Unable to write' not in output
+                assert 'Error' not in output
+                assert 'overflow' not in output
+                expected_text = '%d bytes written' % size
+                assert expected_text in output
+
+                alignment = int(
+                    u_boot_console.config.buildconfig.get(
+                        'config_sys_cacheline_size', 128
+                    )
+                )
+                offset = random.randrange(alignment, 1024, alignment)
+                output = u_boot_console.run_command(
+                    '%sload mmc %d:%s %x %s' % (fs, x, part, addr + offset, file)
+                )
+                assert 'Invalid FAT entry' not in output
+                assert 'Unable to read file' not in output
+                assert 'Misaligned buffer address' not in output
+                expected_text = '%d bytes read' % size
+                assert expected_text in output
+
+                output = u_boot_console.run_command(
+                    'crc32 %x $filesize' % (addr + offset)
+                )
+                assert expected_crc32 in output
+
+    if not part_detect:
+        pytest.skip('No %s partition detected' % fs.upper())
+
+@pytest.mark.buildconfigspec('cmd_mmc')
+@pytest.mark.buildconfigspec('cmd_ext4')
+def test_mmc_ext4ls(u_boot_console):
+    if not mmc_set_up:
+        pytest.skip('No SD/MMC/eMMC controller available')
+
+    if not devices:
+        pytest.skip('No devices detected')
+
+    part_detect = 0
+    fs = 'ext4'
+    for x in range(0, controllers):
+        if devices[x]['detected'] == 'yes':
+            try:
+                partitions = devices[x][fs]
+            except:
+                print('No %s table on this device' % fs.upper())
+                continue
+
+            u_boot_console.run_command('mmc dev %d' % x)
+            for part in partitions:
+                output = u_boot_console.run_command('%sls mmc %d:%s' % (fs, x, part))
+                if 'Unrecognized filesystem type' in output:
+                    partitions.remove(part)
+                    pytest.fail('Unrecognized filesystem')
+                part_detect = 1
+
+    if not part_detect:
+        pytest.skip('No %s partition detected' % fs.upper())
+
+@pytest.mark.buildconfigspec('cmd_mmc')
+@pytest.mark.buildconfigspec('cmd_ext4')
+@pytest.mark.buildconfigspec('ext4_write')
+@pytest.mark.buildconfigspec('cmd_memory')
+def test_mmc_ext4load_ext4write(u_boot_console):
+    if not mmc_set_up:
+        pytest.skip('No SD/MMC/eMMC controller available')
+
+    if not devices:
+        pytest.skip('No devices detected')
+
+    part_detect = 0
+    fs = 'ext4'
+    for x in range(0, controllers):
+        if devices[x]['detected'] == 'yes':
+            u_boot_console.run_command('mmc dev %d' % x)
+            try:
+                partitions = devices[x][fs]
+            except:
+                print('No %s table on this device' % fs.upper())
+                continue
+
+            for part in partitions:
+                part_detect = 1
+                addr = u_boot_utils.find_ram_base(u_boot_console)
+                devices[x]['addr_%d' % part] = addr
+                size = random.randint(4, 1 * 1024 * 1024)
+                devices[x]['size_%d' % part] = size
+                # count CRC32
+                output = u_boot_console.run_command('crc32 %x %x' % (addr, size))
+                m = re.search('==> (.+?)', output)
+                if not m:
+                    pytest.fail('CRC32 failed')
+                expected_crc32 = m.group(1)
+                devices[x]['expected_crc32_%d' % part] = expected_crc32
+                # do write
+
+                file = '%s_%d' % ('uboot_test', size)
+                devices[x]['file_%d' % part] = file
+                output = u_boot_console.run_command(
+                    '%swrite mmc %d:%s %x /%s %x' % (fs, x, part, addr, file, size)
+                )
+                assert 'Unable to write' not in output
+                assert 'Error' not in output
+                assert 'overflow' not in output
+                expected_text = '%d bytes written' % size
+                assert expected_text in output
+
+                offset = random.randrange(128, 1024, 128)
+                output = u_boot_console.run_command(
+                    '%sload mmc %d:%s %x /%s' % (fs, x, part, addr + offset, file)
+                )
+                expected_text = '%d bytes read' % size
+                assert expected_text in output
+
+                output = u_boot_console.run_command(
+                    'crc32 %x $filesize' % (addr + offset)
+                )
+                assert expected_crc32 in output
+
+    if not part_detect:
+        pytest.skip('No %s partition detected' % fs.upper())
+
+@pytest.mark.buildconfigspec('cmd_mmc')
+@pytest.mark.buildconfigspec('cmd_ext2')
+def test_mmc_ext2ls(u_boot_console):
+    if not mmc_set_up:
+        pytest.skip('No SD/MMC/eMMC controller available')
+
+    if not devices:
+        pytest.skip('No devices detected')
+
+    part_detect = 0
+    fs = 'ext2'
+    for x in range(0, controllers):
+        if devices[x]['detected'] == 'yes':
+            u_boot_console.run_command('mmc dev %d' % x)
+            try:
+                partitions = devices[x][fs]
+            except:
+                print('No %s table on this device' % fs.upper())
+                continue
+
+            for part in partitions:
+                part_detect = 1
+                output = u_boot_console.run_command('%sls mmc %d:%s' % (fs, x, part))
+                if 'Unrecognized filesystem type' in output:
+                    partitions.remove(part)
+                    pytest.fail('Unrecognized filesystem')
+                part_detect = 1
+
+    if not part_detect:
+        pytest.skip('No %s partition detected' % fs.upper())
+
+@pytest.mark.buildconfigspec('cmd_mmc')
+@pytest.mark.buildconfigspec('cmd_ext2')
+@pytest.mark.buildconfigspec('cmd_ext4')
+@pytest.mark.buildconfigspec('ext4_write')
+@pytest.mark.buildconfigspec('cmd_memory')
+def test_mmc_ext2load(u_boot_console):
+    if not mmc_set_up:
+        pytest.skip('No SD/MMC/eMMC controller available')
+
+    if not devices:
+        pytest.skip('No devices detected')
+
+    part_detect = 0
+    fs = 'ext2'
+    for x in range(0, controllers):
+        if devices[x]['detected'] == 'yes':
+            u_boot_console.run_command('mmc dev %d' % x)
+            try:
+                partitions = devices[x][fs]
+            except:
+                print('No %s table on this device' % fs.upper())
+                continue
+
+            for part in partitions:
+                part_detect = 1
+                addr = devices[x]['addr_%d' % part]
+                size = devices[x]['size_%d' % part]
+                expected_crc32 = devices[x]['expected_crc32_%d' % part]
+                file = devices[x]['file_%d' % part]
+
+                offset = random.randrange(128, 1024, 128)
+                output = u_boot_console.run_command(
+                    '%sload mmc %d:%s %x /%s' % (fs, x, part, addr + offset, file)
+                )
+                expected_text = '%d bytes read' % size
+                assert expected_text in output
+
+                output = u_boot_console.run_command(
+                    'crc32 %x $filesize' % (addr + offset)
+                )
+                assert expected_crc32 in output
+
+    if not part_detect:
+        pytest.skip('No %s partition detected' % fs.upper())
+
+@pytest.mark.buildconfigspec('cmd_mmc')
+@pytest.mark.buildconfigspec('cmd_fs_generic')
+def test_mmc_ls(u_boot_console):
+    if not mmc_set_up:
+        pytest.skip('No SD/MMC/eMMC controller available')
+
+    if not devices:
+        pytest.skip('No devices detected')
+
+    part_detect = 0
+    for x in range(0, controllers):
+        if devices[x]['detected'] == 'yes':
+            u_boot_console.run_command('mmc dev %d' % x)
+            for fs in ['fat', 'ext4']:
+                try:
+                    partitions = devices[x][fs]
+                except:
+                    print('No %s table on this device' % fs.upper())
+                    continue
+
+                for part in partitions:
+                    part_detect = 1
+                    output = u_boot_console.run_command('ls mmc %d:%s' % (x, part))
+                    if re.search(r'No \w+ table on this device', output):
+                        pytest.fail(
+                            '%s: Partition table not found %d' % (fs.upper(), x)
+                        )
+
+    if not part_detect:
+        pytest.skip('No partition detected')
+
+@pytest.mark.buildconfigspec('cmd_mmc')
+@pytest.mark.buildconfigspec('cmd_fs_generic')
+def test_mmc_load(u_boot_console):
+    if not mmc_set_up:
+        pytest.skip('No SD/MMC/eMMC controller available')
+
+    if not devices:
+        pytest.skip('No devices detected')
+
+    part_detect = 0
+    for x in range(0, controllers):
+        if devices[x]['detected'] == 'yes':
+            u_boot_console.run_command('mmc dev %d' % x)
+            for fs in ['fat', 'ext4']:
+                try:
+                    partitions = devices[x][fs]
+                except:
+                    print('No %s table on this device' % fs.upper())
+                    continue
+
+                for part in partitions:
+                    part_detect = 1
+                    addr = devices[x]['addr_%d' % part]
+                    size = devices[x]['size_%d' % part]
+                    expected_crc32 = devices[x]['expected_crc32_%d' % part]
+                    file = devices[x]['file_%d' % part]
+
+                    offset = random.randrange(128, 1024, 128)
+                    output = u_boot_console.run_command(
+                        'load mmc %d:%s %x /%s' % (x, part, addr + offset, file)
+                    )
+                    expected_text = '%d bytes read' % size
+                    assert expected_text in output
+
+                    output = u_boot_console.run_command(
+                        'crc32 %x $filesize' % (addr + offset)
+                    )
+                    assert expected_crc32 in output
+
+    if not part_detect:
+        pytest.skip('No partition detected')
+
+@pytest.mark.buildconfigspec('cmd_mmc')
+@pytest.mark.buildconfigspec('cmd_fs_generic')
+def test_mmc_save(u_boot_console):
+    if not mmc_set_up:
+        pytest.skip('No SD/MMC/eMMC controller available')
+
+    if not devices:
+        pytest.skip('No devices detected')
+
+    part_detect = 0
+    for x in range(0, controllers):
+        if devices[x]['detected'] == 'yes':
+            u_boot_console.run_command('mmc dev %d' % x)
+            for fs in ['fat', 'ext4']:
+                try:
+                    partitions = devices[x][fs]
+                except:
+                    print('No %s table on this device' % fs.upper())
+                    continue
+
+                for part in partitions:
+                    part_detect = 1
+                    addr = devices[x]['addr_%d' % part]
+                    size = 0
+                    file = devices[x]['file_%d' % part]
+
+                    offset = random.randrange(128, 1024, 128)
+                    output = u_boot_console.run_command(
+                        'save mmc %d:%s %x /%s %d'
+                        % (x, part, addr + offset, file, size)
+                    )
+                    expected_text = '%d bytes written' % size
+                    assert expected_text in output
+
+    if not part_detect:
+        pytest.skip('No partition detected')
+
+@pytest.mark.buildconfigspec('cmd_mmc')
+@pytest.mark.buildconfigspec('cmd_fat')
+@pytest.mark.buildconfigspec('cmd_memory')
+def test_mmc_fat_read_write_files(u_boot_console):
+    test_mmc_list(u_boot_console)
+    test_mmc_dev(u_boot_console)
+    test_mmcinfo(u_boot_console)
+    test_mmc_part(u_boot_console)
+    if not mmc_set_up:
+        pytest.skip('No SD/MMC/eMMC controller available')
+
+    if not devices:
+        pytest.skip('No devices detected')
+
+    part_detect = 0
+    fs = 'fat'
+
+    # Number of files to be written/read in MMC card
+    num_files = 100
+
+    for x in range(0, controllers):
+        if devices[x]['detected'] == 'yes':
+            u_boot_console.run_command('mmc dev %d' % x)
+            try:
+                partitions = devices[x][fs]
+            except:
+                print('No %s table on this device' % fs.upper())
+                continue
+
+            for part in partitions:
+                part_detect = 1
+                addr = u_boot_utils.find_ram_base(u_boot_console)
+                count_f = 0
+                addr_l = []
+                size_l = []
+                file_l = []
+                crc32_l = []
+                offset_l = []
+                addr_l.append(addr)
+
+                while count_f < num_files:
+                    size_l.append(random.randint(4, 1 * 1024 * 1024))
+
+                    # CRC32 count
+                    output = u_boot_console.run_command(
+                        'crc32 %x %x' % (addr_l[count_f], size_l[count_f])
+                    )
+                    m = re.search('==> (.+?)', output)
+                    if not m:
+                        pytest.fail('CRC32 failed')
+                    crc32_l.append(m.group(1))
+
+                    # Write operation
+                    file_l.append('%s_%d_%d' % ('uboot_test', count_f, size_l[count_f]))
+                    output = u_boot_console.run_command(
+                        '%swrite mmc %d:%s %x %s %x'
+                        % (
+                            fs,
+                            x,
+                            part,
+                            addr_l[count_f],
+                            file_l[count_f],
+                            size_l[count_f],
+                        )
+                    )
+                    assert 'Unable to write' not in output
+                    assert 'Error' not in output
+                    assert 'overflow' not in output
+                    expected_text = '%d bytes written' % size_l[count_f]
+                    assert expected_text in output
+
+                    addr_l.append(addr_l[count_f] + size_l[count_f] + 1048576)
+                    count_f += 1
+
+                count_f = 0
+                while count_f < num_files:
+                    alignment = int(
+                        u_boot_console.config.buildconfig.get(
+                            'config_sys_cacheline_size', 128
+                        )
+                    )
+                    offset_l.append(random.randrange(alignment, 1024, alignment))
+
+                    # Read operation
+                    output = u_boot_console.run_command(
+                        '%sload mmc %d:%s %x %s'
+                        % (
+                            fs,
+                            x,
+                            part,
+                            addr_l[count_f] + offset_l[count_f],
+                            file_l[count_f],
+                        )
+                    )
+                    assert 'Invalid FAT entry' not in output
+                    assert 'Unable to read file' not in output
+                    assert 'Misaligned buffer address' not in output
+                    expected_text = '%d bytes read' % size_l[count_f]
+                    assert expected_text in output
+
+                    output = u_boot_console.run_command(
+                        'crc32 %x $filesize' % (addr_l[count_f] + offset_l[count_f])
+                    )
+                    assert crc32_l[count_f] in output
+
+                    count_f += 1
+
+    if not part_detect:
+        pytest.skip('No %s partition detected' % fs.upper())
diff --git a/test/py/tests/test_scsi.py b/test/py/tests/test_scsi.py
new file mode 100644
index 0000000..be2e283
--- /dev/null
+++ b/test/py/tests/test_scsi.py
@@ -0,0 +1,92 @@
+# SPDX-License-Identifier: GPL-2.0
+# (C) Copyright 2023, Advanced Micro Devices, Inc.
+
+import pytest
+
+"""
+Note: This test relies on boardenv_* containing configuration values to define
+the SCSI device number, type and capacity. This test will be automatically
+skipped without this.
+
+For example:
+
+# Setup env__scsi_device_test to set the SCSI device number/slot, the type of
+device, and the device capacity in MB.
+env__scsi_device_test = {
+    'dev_num': 0,
+    'device_type': 'Hard Disk',
+    'device_capacity': '476940.0 MB',
+}
+"""
+
+def scsi_setup(u_boot_console):
+    f = u_boot_console.config.env.get('env__scsi_device_test', None)
+    if not f:
+        pytest.skip('No SCSI device to test')
+
+    dev_num = f.get('dev_num', None)
+    if not isinstance(dev_num, int):
+        pytest.skip('No device number specified in env file to read')
+
+    dev_type = f.get('device_type')
+    if not dev_type:
+        pytest.skip('No device type specified in env file to read')
+
+    dev_size = f.get('device_capacity')
+    if not dev_size:
+        pytest.skip('No device capacity specified in env file to read')
+
+    return dev_num, dev_type, dev_size
+
+@pytest.mark.buildconfigspec('cmd_scsi')
+def test_scsi_reset(u_boot_console):
+    dev_num, dev_type, dev_size = scsi_setup(u_boot_console)
+    output = u_boot_console.run_command('scsi reset')
+    assert f'Device {dev_num}:' in output
+    assert f'Type: {dev_type}' in output
+    assert f'Capacity: {dev_size}' in output
+    output = u_boot_console.run_command('echo $?')
+    assert output.endswith('0')
+
+@pytest.mark.buildconfigspec('cmd_scsi')
+def test_scsi_info(u_boot_console):
+    dev_num, dev_type, dev_size = scsi_setup(u_boot_console)
+    output = u_boot_console.run_command('scsi info')
+    assert f'Device {dev_num}:' in output
+    assert f'Type: {dev_type}' in output
+    assert f'Capacity: {dev_size}' in output
+    output = u_boot_console.run_command('echo $?')
+    assert output.endswith('0')
+
+@pytest.mark.buildconfigspec('cmd_scsi')
+def test_scsi_scan(u_boot_console):
+    dev_num, dev_type, dev_size = scsi_setup(u_boot_console)
+    output = u_boot_console.run_command('scsi scan')
+    assert f'Device {dev_num}:' in output
+    assert f'Type: {dev_type}' in output
+    assert f'Capacity: {dev_size}' in output
+    output = u_boot_console.run_command('echo $?')
+    assert output.endswith('0')
+
+@pytest.mark.buildconfigspec('cmd_scsi')
+def test_scsi_dev(u_boot_console):
+    dev_num, dev_type, dev_size = scsi_setup(u_boot_console)
+    output = u_boot_console.run_command('scsi device')
+    assert 'no scsi devices available' not in output
+    assert f'device {dev_num}:' in output
+    assert f'Type: {dev_type}' in output
+    assert f'Capacity: {dev_size}' in output
+    output = u_boot_console.run_command('echo $?')
+    assert output.endswith('0')
+    output = u_boot_console.run_command('scsi device %d' % dev_num)
+    assert 'is now current device' in output
+    output = u_boot_console.run_command('echo $?')
+    assert output.endswith('0')
+
+@pytest.mark.buildconfigspec('cmd_scsi')
+def test_scsi_part(u_boot_console):
+    test_scsi_dev(u_boot_console)
+    output = u_boot_console.run_command('scsi part')
+    assert 'Partition Map for SCSI device' in output
+    output = u_boot_console.run_command('echo $?')
+    assert output.endswith('0')
diff --git a/test/py/tests/test_usb.py b/test/py/tests/test_usb.py
new file mode 100644
index 0000000..fb3d20f
--- /dev/null
+++ b/test/py/tests/test_usb.py
@@ -0,0 +1,626 @@
+# SPDX-License-Identifier: GPL-2.0
+# (C) Copyright 2023, Advanced Micro Devices, Inc.
+
+import pytest
+import random
+import re
+import u_boot_utils
+
+"""
+Note: This test doesn't rely on boardenv_* configuration values but it can
+change the test behavior. To test USB file system cases (fat32, ext2, ext4),
+USB device should be formatted and valid partitions should be created for
+different file system, otherwise it may leads to failure. This test will be
+skipped if the USB device is not detected.
+
+For example:
+
+# Setup env__usb_device_test_skip to not skipping the test. By default, its
+# value is set to True. Set it to False to run all tests for USB device.
+env__usb_device_test_skip = False
+"""
+
+def setup_usb(u_boot_console):
+    if u_boot_console.config.env.get('env__usb_device_test_skip', True):
+        pytest.skip('USB device test is not enabled')
+
+@pytest.mark.buildconfigspec('cmd_usb')
+def test_usb_start(u_boot_console):
+    setup_usb(u_boot_console)
+    output = u_boot_console.run_command('usb start')
+
+    # if output is empty, usb start may already run as part of preboot command
+    # re-start the usb, in that case
+    if not output:
+        u_boot_console.run_command('usb stop')
+        output = u_boot_console.run_command('usb start')
+
+    if 'No USB device found' in output:
+        pytest.skip('No USB controller available')
+
+    if 'Card did not respond to voltage select' in output:
+        pytest.skip('No USB device present')
+
+    controllers = 0
+    storage_device = 0
+    obj = re.search(r'\d USB Device\(s\) found', output)
+    controllers = int(obj.group()[0])
+
+    if not controllers:
+        pytest.skip('No USB device present')
+
+    obj = re.search(r'\d Storage Device\(s\) found', output)
+    storage_device = int(obj.group()[0])
+
+    if not storage_device:
+        pytest.skip('No USB storage device present')
+
+    assert 'USB init failed' not in output
+    assert 'starting USB...' in output
+
+    if 'Starting the controller' in output:
+        assert 'USB XHCI' in output
+
+    output = u_boot_console.run_command('echo $?')
+    assert output.endswith('0')
+    return controllers, storage_device
+
+@pytest.mark.buildconfigspec('cmd_usb')
+def test_usb_stop(u_boot_console):
+    setup_usb(u_boot_console)
+    output = u_boot_console.run_command('usb stop')
+    assert 'stopping USB..' in output
+
+    output = u_boot_console.run_command('echo $?')
+    assert output.endswith('0')
+
+    output = u_boot_console.run_command('usb dev')
+    assert "USB is stopped. Please issue 'usb start' first." in output
+
+@pytest.mark.buildconfigspec('cmd_usb')
+def test_usb_reset(u_boot_console):
+    setup_usb(u_boot_console)
+    output = u_boot_console.run_command('usb reset')
+
+    if 'No USB device found' in output:
+        pytest.skip('No USB controller available')
+
+    if 'Card did not respond to voltage select' in output:
+        pytest.skip('No USB device present')
+
+    obj = re.search(r'\d USB Device\(s\) found', output)
+    usb_dev_num = int(obj.group()[0])
+
+    if not usb_dev_num:
+        pytest.skip('No USB device present')
+
+    obj = re.search(r'\d Storage Device\(s\) found', output)
+    usb_stor_num = int(obj.group()[0])
+
+    if not usb_stor_num:
+        pytest.skip('No USB storage device present')
+
+    assert 'BUG' not in output
+    assert 'USB init failed' not in output
+    assert 'resetting USB...' in output
+
+    if 'Starting the controller' in output:
+        assert 'USB XHCI' in output
+
+    output = u_boot_console.run_command('echo $?')
+    assert output.endswith('0')
+
+@pytest.mark.buildconfigspec('cmd_usb')
+def test_usb_info(u_boot_console):
+    controllers, storage_device = test_usb_start(u_boot_console)
+    output = u_boot_console.run_command('usb info')
+
+    num_controller = len(re.findall(': Hub,', output))
+    num_mass_storage = len(re.findall(': Mass Storage,', output))
+
+    assert num_controller == controllers - 1
+    assert num_mass_storage == storage_device
+
+    output = u_boot_console.run_command('echo $?')
+    assert output.endswith('0')
+
+    for i in range(0, storage_device + controllers - 1):
+        output = u_boot_console.run_command('usb info %d' % i)
+        num_controller = len(re.findall(': Hub,', output))
+        num_mass_storage = len(re.findall(': Mass Storage,', output))
+        assert num_controller + num_mass_storage == 1
+        assert 'No device available' not in output
+        output = u_boot_console.run_command('echo $?')
+        assert output.endswith('0')
+
+@pytest.mark.buildconfigspec('cmd_usb')
+def test_usb_tree(u_boot_console):
+    controllers, storage_device = test_usb_start(u_boot_console)
+    output = u_boot_console.run_command('usb tree')
+
+    num_controller = len(re.findall('Hub', output))
+    num_mass_storage = len(re.findall('Mass Storage', output))
+
+    assert num_controller == controllers - 1
+    assert num_mass_storage == storage_device
+
+    output = u_boot_console.run_command('echo $?')
+    assert output.endswith('0')
+
+@pytest.mark.buildconfigspec('cmd_usb')
+@pytest.mark.buildconfigspec('usb_storage')
+def test_usb_storage(u_boot_console):
+    controllers, storage_device = test_usb_start(u_boot_console)
+    output = u_boot_console.run_command('usb storage')
+
+    obj = re.findall(r'Capacity: (\d+|\d+[\.]?\d)', output)
+    devices = {}
+
+    for key in range(int(storage_device)):
+        devices[key] = {}
+
+    for x in range(int(storage_device)):
+        try:
+            capacity = float(obj[x].split()[0])
+            devices[x]['capacity'] = capacity
+            print('USB storage device %d capacity is: %g MB' % (x, capacity))
+        except ValueError:
+            pytest.fail('USB storage device capacity not recognized')
+
+    output = u_boot_console.run_command('echo $?')
+    assert output.endswith('0')
+
+@pytest.mark.buildconfigspec('cmd_usb')
+def test_usb_dev(u_boot_console):
+    controllers, storage_device = test_usb_start(u_boot_console)
+    output = u_boot_console.run_command('usb dev')
+
+    assert 'no usb devices available' not in output
+
+    output = u_boot_console.run_command('echo $?')
+    assert output.endswith('0')
+
+    devices = {}
+
+    for key in range(int(storage_device)):
+        devices[key] = {}
+
+    fail = 0
+    for x in range(0, storage_device):
+        devices[x]['detected'] = 'yes'
+        output = u_boot_console.run_command('usb dev %d' % x)
+
+        if 'Card did not respond to voltage select' in output:
+            fail = 1
+            devices[x]['detected'] = 'no'
+
+        if 'No USB device found' in output:
+            devices[x]['detected'] = 'no'
+
+        if 'unknown device' in output:
+            devices[x]['detected'] = 'no'
+
+        assert 'is now current device' in output
+        output = u_boot_console.run_command('echo $?')
+        assert output.endswith('0')
+
+    if fail:
+        pytest.fail('USB device not present')
+
+    return devices, controllers, storage_device
+
+@pytest.mark.buildconfigspec('cmd_usb')
+def test_usb_part(u_boot_console):
+    devices, controllers, storage_device = test_usb_dev(u_boot_console)
+    if not devices:
+        pytest.skip('No devices detected')
+
+    u_boot_console.run_command('usb part')
+
+    output = u_boot_console.run_command('echo $?')
+    assert output.endswith('0')
+
+    for i in range(0, storage_device):
+        if devices[i]['detected'] == 'yes':
+            u_boot_console.run_command('usb dev %d' % i)
+            output = u_boot_console.run_command('usb part')
+
+            lines = output.split('\n')
+            part_fat = []
+            part_ext = []
+            for line in lines:
+                obj = re.search(r'(\d)\s+\d+\s+\d+\s+\w+\d+\w+-\d+\s+(\d+\w+)', line)
+                if obj:
+                    part_id = int(obj.groups()[0])
+                    part_type = obj.groups()[1]
+                    print('part_id:%d, part_type:%s' % (part_id, part_type))
+
+                    if part_type == '0c' or part_type == '0b' or part_type == '0e':
+                        print('Fat detected')
+                        part_fat.append(part_id)
+                    elif part_type == '83':
+                        print('ext detected')
+                        part_ext.append(part_id)
+                    else:
+                        pytest.fail('Unsupported Filesystem on device %d' % i)
+            devices[i]['ext4'] = part_ext
+            devices[i]['ext2'] = part_ext
+            devices[i]['fat'] = part_fat
+
+            if not part_ext and not part_fat:
+                pytest.fail('No partition detected on device %d' % i)
+
+    return devices, controllers, storage_device
+
+@pytest.mark.buildconfigspec('cmd_usb')
+@pytest.mark.buildconfigspec('cmd_fat')
+def test_usb_fatls_fatinfo(u_boot_console):
+    devices, controllers, storage_device = test_usb_part(u_boot_console)
+    if not devices:
+        pytest.skip('No devices detected')
+
+    part_detect = 0
+    fs = 'fat'
+    for x in range(0, int(storage_device)):
+        if devices[x]['detected'] == 'yes':
+            u_boot_console.run_command('usb dev %d' % x)
+            try:
+                partitions = devices[x][fs]
+            except:
+                print('No %s table on this device' % fs.upper())
+                continue
+
+            for part in partitions:
+                output = u_boot_console.run_command('fatls usb %d:%s' % (x, part))
+                if 'Unrecognized filesystem type' in output:
+                    partitions.remove(part)
+                    pytest.fail('Unrecognized filesystem')
+
+                if not re.search(r'\d file\(s\), \d dir\(s\)', output):
+                    pytest.fail('%s read failed on device %d' % (fs.upper, x))
+
+                output = u_boot_console.run_command('fatinfo usb %d:%s' % (x, part))
+                string = 'Filesystem: %s' % fs.upper
+                if re.search(string, output):
+                    pytest.fail('%s FS failed on device %d' % (fs.upper(), x))
+                part_detect = 1
+
+    if not part_detect:
+        pytest.skip('No %s partition detected' % fs.upper())
+
+@pytest.mark.buildconfigspec('cmd_usb')
+@pytest.mark.buildconfigspec('cmd_fat')
+@pytest.mark.buildconfigspec('cmd_memory')
+def test_usb_fatload_fatwrite(u_boot_console):
+    devices, controllers, storage_device = test_usb_part(u_boot_console)
+    if not devices:
+        pytest.skip('No devices detected')
+
+    part_detect = 0
+    fs = 'fat'
+    for x in range(0, int(storage_device)):
+        if devices[x]['detected'] == 'yes':
+            u_boot_console.run_command('usb dev %d' % x)
+            try:
+                partitions = devices[x][fs]
+            except:
+                print('No %s table on this device' % fs.upper())
+                continue
+
+            for part in partitions:
+                part_detect = 1
+                addr = u_boot_utils.find_ram_base(u_boot_console)
+                size = random.randint(4, 1 * 1024 * 1024)
+                output = u_boot_console.run_command('crc32 %x %x' % (addr, size))
+                m = re.search('==> (.+?)', output)
+                if not m:
+                    pytest.fail('CRC32 failed')
+                expected_crc32 = m.group(1)
+
+                file = '%s_%d' % ('uboot_test', size)
+                output = u_boot_console.run_command(
+                    '%swrite usb %d:%s %x %s %x' % (fs, x, part, addr, file, size)
+                )
+                assert 'Unable to write' not in output
+                assert 'Error' not in output
+                assert 'overflow' not in output
+                expected_text = '%d bytes written' % size
+                assert expected_text in output
+
+                alignment = int(
+                    u_boot_console.config.buildconfig.get(
+                        'config_sys_cacheline_size', 128
+                    )
+                )
+                offset = random.randrange(alignment, 1024, alignment)
+                output = u_boot_console.run_command(
+                    '%sload usb %d:%s %x %s' % (fs, x, part, addr + offset, file)
+                )
+                assert 'Invalid FAT entry' not in output
+                assert 'Unable to read file' not in output
+                assert 'Misaligned buffer address' not in output
+                expected_text = '%d bytes read' % size
+                assert expected_text in output
+
+                output = u_boot_console.run_command(
+                    'crc32 %x $filesize' % (addr + offset)
+                )
+                assert expected_crc32 in output
+
+    if not part_detect:
+        pytest.skip('No %s partition detected' % fs.upper())
+
+    return file, size
+
+@pytest.mark.buildconfigspec('cmd_usb')
+@pytest.mark.buildconfigspec('cmd_ext4')
+def test_usb_ext4ls(u_boot_console):
+    devices, controllers, storage_device = test_usb_part(u_boot_console)
+    if not devices:
+        pytest.skip('No devices detected')
+
+    part_detect = 0
+    fs = 'ext4'
+    for x in range(0, int(storage_device)):
+        if devices[x]['detected'] == 'yes':
+            try:
+                partitions = devices[x][fs]
+            except:
+                print('No %s table on this device' % fs.upper())
+                continue
+
+            u_boot_console.run_command('usb dev %d' % x)
+            for part in partitions:
+                output = u_boot_console.run_command('%sls usb %d:%s' % (fs, x, part))
+                if 'Unrecognized filesystem type' in output:
+                    partitions.remove(part)
+                    pytest.fail('Unrecognized filesystem')
+                part_detect = 1
+
+    if not part_detect:
+        pytest.skip('No %s partition detected' % fs.upper())
+
+@pytest.mark.buildconfigspec('cmd_usb')
+@pytest.mark.buildconfigspec('cmd_ext4')
+@pytest.mark.buildconfigspec('ext4_write')
+@pytest.mark.buildconfigspec('cmd_memory')
+def test_usb_ext4load_ext4write(u_boot_console):
+    devices, controllers, storage_device = test_usb_part(u_boot_console)
+    if not devices:
+        pytest.skip('No devices detected')
+
+    part_detect = 0
+    fs = 'ext4'
+    for x in range(0, int(storage_device)):
+        if devices[x]['detected'] == 'yes':
+            u_boot_console.run_command('usb dev %d' % x)
+            try:
+                partitions = devices[x][fs]
+            except:
+                print('No %s table on this device' % fs.upper())
+                continue
+
+            for part in partitions:
+                part_detect = 1
+                addr = u_boot_utils.find_ram_base(u_boot_console)
+                size = random.randint(4, 1 * 1024 * 1024)
+                output = u_boot_console.run_command('crc32 %x %x' % (addr, size))
+                m = re.search('==> (.+?)', output)
+                if not m:
+                    pytest.fail('CRC32 failed')
+                expected_crc32 = m.group(1)
+                file = '%s_%d' % ('uboot_test', size)
+
+                output = u_boot_console.run_command(
+                    '%swrite usb %d:%s %x /%s %x' % (fs, x, part, addr, file, size)
+                )
+                assert 'Unable to write' not in output
+                assert 'Error' not in output
+                assert 'overflow' not in output
+                expected_text = '%d bytes written' % size
+                assert expected_text in output
+
+                offset = random.randrange(128, 1024, 128)
+                output = u_boot_console.run_command(
+                    '%sload usb %d:%s %x /%s' % (fs, x, part, addr + offset, file)
+                )
+                expected_text = '%d bytes read' % size
+                assert expected_text in output
+
+                output = u_boot_console.run_command(
+                    'crc32 %x $filesize' % (addr + offset)
+                )
+                assert expected_crc32 in output
+
+    if not part_detect:
+        pytest.skip('No %s partition detected' % fs.upper())
+
+    return file, size
+
+@pytest.mark.buildconfigspec('cmd_usb')
+@pytest.mark.buildconfigspec('cmd_ext2')
+def test_usb_ext2ls(u_boot_console):
+    devices, controllers, storage_device = test_usb_part(u_boot_console)
+    if not devices:
+        pytest.skip('No devices detected')
+
+    part_detect = 0
+    fs = 'ext2'
+    for x in range(0, int(storage_device)):
+        if devices[x]['detected'] == 'yes':
+            u_boot_console.run_command('usb dev %d' % x)
+            try:
+                partitions = devices[x][fs]
+            except:
+                print('No %s table on this device' % fs.upper())
+                continue
+
+            for part in partitions:
+                part_detect = 1
+                output = u_boot_console.run_command('%sls usb %d:%s' % (fs, x, part))
+                if 'Unrecognized filesystem type' in output:
+                    partitions.remove(part)
+                    pytest.fail('Unrecognized filesystem')
+                part_detect = 1
+
+    if not part_detect:
+        pytest.skip('No %s partition detected' % fs.upper())
+
+@pytest.mark.buildconfigspec('cmd_usb')
+@pytest.mark.buildconfigspec('cmd_ext2')
+@pytest.mark.buildconfigspec('cmd_ext4')
+@pytest.mark.buildconfigspec('ext4_write')
+@pytest.mark.buildconfigspec('cmd_memory')
+def test_usb_ext2load(u_boot_console):
+    devices, controllers, storage_device = test_usb_part(u_boot_console)
+    file, size = test_usb_ext4load_ext4write(u_boot_console)
+
+    if not devices:
+        pytest.skip('No devices detected')
+
+    part_detect = 0
+    fs = 'ext2'
+    for x in range(0, int(storage_device)):
+        if devices[x]['detected'] == 'yes':
+            u_boot_console.run_command('usb dev %d' % x)
+            try:
+                partitions = devices[x][fs]
+            except:
+                print('No %s table on this device' % fs.upper())
+                continue
+
+            for part in partitions:
+                part_detect = 1
+                addr = u_boot_utils.find_ram_base(u_boot_console)
+                output = u_boot_console.run_command('crc32 %x %x' % (addr, size))
+                m = re.search('==> (.+?)', output)
+                if not m:
+                    pytest.fail('CRC32 failed')
+                expected_crc32 = m.group(1)
+
+                offset = random.randrange(128, 1024, 128)
+                output = u_boot_console.run_command(
+                    '%sload usb %d:%s %x /%s' % (fs, x, part, addr + offset, file)
+                )
+                expected_text = '%d bytes read' % size
+                assert expected_text in output
+
+                output = u_boot_console.run_command(
+                    'crc32 %x $filesize' % (addr + offset)
+                )
+                assert expected_crc32 in output
+
+    if not part_detect:
+        pytest.skip('No %s partition detected' % fs.upper())
+
+@pytest.mark.buildconfigspec('cmd_usb')
+@pytest.mark.buildconfigspec('cmd_fs_generic')
+def test_usb_ls(u_boot_console):
+    devices, controllers, storage_device = test_usb_part(u_boot_console)
+    if not devices:
+        pytest.skip('No devices detected')
+
+    part_detect = 0
+    for x in range(0, int(storage_device)):
+        if devices[x]['detected'] == 'yes':
+            u_boot_console.run_command('usb dev %d' % x)
+            for fs in ['fat', 'ext4']:
+                try:
+                    partitions = devices[x][fs]
+                except:
+                    print('No %s table on this device' % fs.upper())
+                    continue
+
+                for part in partitions:
+                    part_detect = 1
+                    output = u_boot_console.run_command('ls usb %d:%s' % (x, part))
+                    if re.search(r'No \w+ table on this device', output):
+                        pytest.fail(
+                            '%s: Partition table not found %d' % (fs.upper(), x)
+                        )
+
+    if not part_detect:
+        pytest.skip('No partition detected')
+
+@pytest.mark.buildconfigspec('cmd_usb')
+@pytest.mark.buildconfigspec('cmd_fs_generic')
+def test_usb_load(u_boot_console):
+    devices, controllers, storage_device = test_usb_part(u_boot_console)
+    if not devices:
+        pytest.skip('No devices detected')
+
+    part_detect = 0
+    for x in range(0, int(storage_device)):
+        if devices[x]['detected'] == 'yes':
+            u_boot_console.run_command('usb dev %d' % x)
+            for fs in ['fat', 'ext4']:
+                try:
+                    partitions = devices[x][fs]
+                except:
+                    print('No %s table on this device' % fs.upper())
+                    continue
+
+                for part in partitions:
+                    part_detect = 1
+                    addr = u_boot_utils.find_ram_base(u_boot_console)
+
+                    if fs == 'fat':
+                        file, size = test_usb_fatload_fatwrite(u_boot_console)
+                    elif fs == 'ext4':
+                        file, size = test_usb_ext4load_ext4write(u_boot_console)
+
+                    output = u_boot_console.run_command('crc32 %x %x' % (addr, size))
+                    m = re.search('==> (.+?)', output)
+                    if not m:
+                        pytest.fail('CRC32 failed')
+                    expected_crc32 = m.group(1)
+
+                    offset = random.randrange(128, 1024, 128)
+                    output = u_boot_console.run_command(
+                        'load usb %d:%s %x /%s' % (x, part, addr + offset, file)
+                    )
+                    expected_text = '%d bytes read' % size
+                    assert expected_text in output
+
+                    output = u_boot_console.run_command(
+                        'crc32 %x $filesize' % (addr + offset)
+                    )
+                    assert expected_crc32 in output
+
+    if not part_detect:
+        pytest.skip('No partition detected')
+
+@pytest.mark.buildconfigspec('cmd_usb')
+@pytest.mark.buildconfigspec('cmd_fs_generic')
+def test_usb_save(u_boot_console):
+    devices, controllers, storage_device = test_usb_part(u_boot_console)
+    if not devices:
+        pytest.skip('No devices detected')
+
+    part_detect = 0
+    for x in range(0, int(storage_device)):
+        if devices[x]['detected'] == 'yes':
+            u_boot_console.run_command('usb dev %d' % x)
+            for fs in ['fat', 'ext4']:
+                try:
+                    partitions = devices[x][fs]
+                except:
+                    print('No %s table on this device' % fs.upper())
+                    continue
+
+                for part in partitions:
+                    part_detect = 1
+                    addr = u_boot_utils.find_ram_base(u_boot_console)
+                    size = random.randint(4, 1 * 1024 * 1024)
+                    file = '%s_%d' % ('uboot_test', size)
+
+                    offset = random.randrange(128, 1024, 128)
+                    output = u_boot_console.run_command(
+                        'save usb %d:%s %x /%s %x'
+                        % (x, part, addr + offset, file, size)
+                    )
+                    expected_text = '%d bytes written' % size
+                    assert expected_text in output
+
+    if not part_detect:
+        pytest.skip('No partition detected')
diff --git a/test/py/tests/test_zynq_secure.py b/test/py/tests/test_zynq_secure.py
new file mode 100644
index 0000000..0ee5aeb
--- /dev/null
+++ b/test/py/tests/test_zynq_secure.py
@@ -0,0 +1,190 @@
+# SPDX-License-Identifier: GPL-2.0
+# (C) Copyright 2023, Advanced Micro Devices, Inc.
+
+import pytest
+import re
+import u_boot_utils
+import test_net
+
+"""
+This test verifies different type of secure boot images to authentication and
+decryption using AES and RSA features for AMD's Zynq SoC.
+
+Note: This test relies on boardenv_* containing configuration values to define
+the network available and files to be used for testing. Without this, this test
+will be automatically skipped. It also relies on dhcp or setup_static net test
+to support tftp to load files from a TFTP server.
+
+For example:
+
+# Details regarding the files that may be read from a TFTP server and addresses
+# and size for aes and rsa cases respectively. This variable may be omitted or
+# set to None if zynqmp secure testing is not possible or desired.
+env__zynq_aes_readable_file = {
+    'fn': 'zynq_aes_image.bin',
+    'fnbit': 'zynq_aes_bit.bin',
+    'fnpbit': 'zynq_aes_par_bit.bin',
+    'srcaddr': 0x1000000,
+    'dstaddr': 0x2000000,
+    'dstlen': 0x1000000,
+}
+
+env__zynq_rsa_readable_file = {
+    'fn': 'zynq_rsa_image.bin',
+    'fninvalid': 'zynq_rsa_image_invalid.bin',
+    'srcaddr': 0x1000000,
+}
+"""
+
+def zynq_secure_pre_commands(u_boot_console):
+    output = u_boot_console.run_command('print modeboot')
+    if not 'modeboot=' in output:
+        pytest.skip('bootmode cannnot be determined')
+    m = re.search('modeboot=(.+?)boot', output)
+    if not m:
+        pytest.skip('bootmode cannnot be determined')
+    bootmode = m.group(1)
+    if bootmode == 'jtag':
+        pytest.skip('skipping due to jtag bootmode')
+
+@pytest.mark.buildconfigspec('cmd_zynq_aes')
+def test_zynq_aes_image(u_boot_console):
+    f = u_boot_console.config.env.get('env__zynq_aes_readable_file', None)
+    if not f:
+        pytest.skip('No TFTP readable file for zynq secure aes case to read')
+
+    dstaddr = f.get('dstaddr', None)
+    if not dstaddr:
+        pytest.skip('No dstaddr specified in env file to read')
+
+    dstsize = f.get('dstlen', None)
+    if not dstsize:
+        pytest.skip('No dstlen specified in env file to read')
+
+    zynq_secure_pre_commands(u_boot_console)
+    test_net.test_net_dhcp(u_boot_console)
+    if not test_net.net_set_up:
+        test_net.test_net_setup_static(u_boot_console)
+
+    srcaddr = f.get('srcaddr', None)
+    if not srcaddr:
+        addr = u_boot_utils.find_ram_base(u_boot_console)
+
+    expected_tftp = 'Bytes transferred = '
+    fn = f['fn']
+    output = u_boot_console.run_command('tftpboot %x %s' % (srcaddr, fn))
+    assert expected_tftp in output
+
+    expected_op = 'zynq aes [operation type] <srcaddr>'
+    output = u_boot_console.run_command(
+        'zynq aes %x $filesize %x %x' % (srcaddr, dstaddr, dstsize)
+    )
+    assert expected_op not in output
+    output = u_boot_console.run_command('echo $?')
+    assert output.endswith('0')
+
+@pytest.mark.buildconfigspec('cmd_zynq_aes')
+def test_zynq_aes_bitstream(u_boot_console):
+    f = u_boot_console.config.env.get('env__zynq_aes_readable_file', None)
+    if not f:
+        pytest.skip('No TFTP readable file for zynq secure aes case to read')
+
+    zynq_secure_pre_commands(u_boot_console)
+    test_net.test_net_dhcp(u_boot_console)
+    if not test_net.net_set_up:
+        test_net.test_net_setup_static(u_boot_console)
+
+    srcaddr = f.get('srcaddr', None)
+    if not srcaddr:
+        addr = u_boot_utils.find_ram_base(u_boot_console)
+
+    expected_tftp = 'Bytes transferred = '
+    fn = f['fnbit']
+    output = u_boot_console.run_command('tftpboot %x %s' % (srcaddr, fn))
+    assert expected_tftp in output
+
+    expected_op = 'zynq aes [operation type] <srcaddr>'
+    output = u_boot_console.run_command(
+        'zynq aes load %x $filesize' % (srcaddr)
+    )
+    assert expected_op not in output
+    output = u_boot_console.run_command('echo $?')
+    assert output.endswith('0')
+
+@pytest.mark.buildconfigspec('cmd_zynq_aes')
+def test_zynq_aes_partial_bitstream(u_boot_console):
+    f = u_boot_console.config.env.get('env__zynq_aes_readable_file', None)
+    if not f:
+        pytest.skip('No TFTP readable file for zynq secure aes case to read')
+
+    zynq_secure_pre_commands(u_boot_console)
+    test_net.test_net_dhcp(u_boot_console)
+    if not test_net.net_set_up:
+        test_net.test_net_setup_static(u_boot_console)
+
+    srcaddr = f.get('srcaddr', None)
+    if not srcaddr:
+        addr = u_boot_utils.find_ram_base(u_boot_console)
+
+    expected_tftp = 'Bytes transferred = '
+    fn = f['fnpbit']
+    output = u_boot_console.run_command('tftpboot %x %s' % (srcaddr, fn))
+    assert expected_tftp in output
+
+    expected_op = 'zynq aes [operation type] <srcaddr>'
+    output = u_boot_console.run_command('zynq aes loadp %x $filesize' % (srcaddr))
+    assert expected_op not in output
+    output = u_boot_console.run_command('echo $?')
+    assert output.endswith('0')
+
+@pytest.mark.buildconfigspec('cmd_zynq_rsa')
+def test_zynq_rsa_image(u_boot_console):
+    f = u_boot_console.config.env.get('env__zynq_rsa_readable_file', None)
+    if not f:
+        pytest.skip('No TFTP readable file for zynq secure rsa case to read')
+
+    zynq_secure_pre_commands(u_boot_console)
+    test_net.test_net_dhcp(u_boot_console)
+    if not test_net.net_set_up:
+        test_net.test_net_setup_static(u_boot_console)
+
+    srcaddr = f.get('srcaddr', None)
+    if not srcaddr:
+        addr = u_boot_utils.find_ram_base(u_boot_console)
+
+    expected_tftp = 'Bytes transferred = '
+    fn = f['fn']
+    output = u_boot_console.run_command('tftpboot %x %s' % (srcaddr, fn))
+    assert expected_tftp in output
+
+    expected_op = 'zynq rsa <baseaddr>'
+    output = u_boot_console.run_command('zynq rsa %x ' % (srcaddr))
+    assert expected_op not in output
+    output = u_boot_console.run_command('echo $?')
+    assert output.endswith('0')
+
+@pytest.mark.buildconfigspec('cmd_zynq_rsa')
+def test_zynq_rsa_image_invalid(u_boot_console):
+    f = u_boot_console.config.env.get('env__zynq_rsa_readable_file', None)
+    if not f:
+        pytest.skip('No TFTP readable file for zynq secure rsa case to read')
+
+    zynq_secure_pre_commands(u_boot_console)
+    test_net.test_net_dhcp(u_boot_console)
+    if not test_net.net_set_up:
+        test_net.test_net_setup_static(u_boot_console)
+
+    srcaddr = f.get('srcaddr', None)
+    if not srcaddr:
+        addr = u_boot_utils.find_ram_base(u_boot_console)
+
+    expected_tftp = 'Bytes transferred = '
+    fninvalid = f['fninvalid']
+    output = u_boot_console.run_command('tftpboot %x %s' % (srcaddr, fninvalid))
+    assert expected_tftp in output
+
+    expected_op = 'zynq rsa <baseaddr>'
+    output = u_boot_console.run_command('zynq rsa %x ' % (srcaddr))
+    assert expected_op in output
+    output = u_boot_console.run_command('echo $?')
+    assert not output.endswith('0')
diff --git a/test/py/tests/test_zynqmp_rpu.py b/test/py/tests/test_zynqmp_rpu.py
new file mode 100644
index 0000000..479a612
--- /dev/null
+++ b/test/py/tests/test_zynqmp_rpu.py
@@ -0,0 +1,208 @@
+# SPDX-License-Identifier: GPL-2.0
+# (C) Copyright 2023, Advanced Micro Devices, Inc.
+
+import pytest
+import random
+import string
+import test_net
+
+"""
+Note: This test relies on boardenv_* containing configuration values to define
+RPU applications information for AMD's ZynqMP SoC which contains, application
+names, processors, address where it is built, expected output and the tftp load
+addresses. This test will be automatically skipped without this.
+
+It also relies on dhcp or setup_static net test to support tftp to load
+application on DDR. All the environment parameters are stored sequentially.
+The length of all parameters values should be same. For example, if 2 app_names
+are defined in a list as a value of parameter 'app_name' then the other
+parameters value also should have a list with 2 items.
+It will run RPU cases for all the applications defined in boardenv_*
+configuration file.
+
+Example:
+env__zynqmp_rpu_apps = {
+    'app_name': ['hello_world_r5_0_ddr.elf', 'hello_world_r5_1_ddr.elf'],
+    'proc': ['rpu0', 'rpu1'],
+    'cpu_num': [4, 5],
+    'addr': [0xA00000, 0xB00000],
+    'output': ['Successfully ran Hello World application on DDR from RPU0',
+               'Successfully ran Hello World application on DDR from RPU1'],
+    'tftp_addr': [0x100000, 0x200000],
+}
+"""
+
+# Get rpu apps params from env
+def get_rpu_apps_env(u_boot_console):
+    rpu_apps = u_boot_console.config.env.get('env__zynqmp_rpu_apps', False)
+    if not rpu_apps:
+        pytest.skip('ZynqMP RPU application info not defined!')
+
+    apps = rpu_apps.get('app_name', None)
+    if not apps:
+        pytest.skip('No RPU application found!')
+
+    procs = rpu_apps.get('proc', None)
+    if not procs:
+        pytest.skip('No RPU application processor provided!')
+
+    cpu_nums = rpu_apps.get('cpu_num', None)
+    if not cpu_nums:
+        pytest.skip('No CPU number for respective processor provided!')
+
+    addrs = rpu_apps.get('addr', None)
+    if not addrs:
+        pytest.skip('No RPU application build address found!')
+
+    outputs = rpu_apps.get('output', None)
+    if not outputs:
+        pytest.skip('Expected output not found!')
+
+    tftp_addrs = rpu_apps.get('tftp_addr', None)
+    if not tftp_addrs:
+        pytest.skip('TFTP address to load application not found!')
+
+    return apps, procs, cpu_nums, addrs, outputs, tftp_addrs
+
+# Check return code
+def ret_code(u_boot_console):
+    return u_boot_console.run_command('echo $?')
+
+# Initialize tcm
+def tcminit(u_boot_console, rpu_mode):
+    output = u_boot_console.run_command('zynqmp tcminit %s' % rpu_mode)
+    assert 'Initializing TCM overwrites TCM content' in output
+    return ret_code(u_boot_console)
+
+# Load application in DDR
+def load_app_ddr(u_boot_console, tftp_addr, app):
+    output = u_boot_console.run_command('tftpboot %x %s' % (tftp_addr, app))
+    assert 'TIMEOUT' not in output
+    assert 'Bytes transferred = ' in output
+
+    # Load elf
+    u_boot_console.run_command('bootelf -p %x' % tftp_addr)
+    assert ret_code(u_boot_console).endswith('0')
+
+# Disable cpus
+def disable_cpus(u_boot_console, cpu_nums):
+    for num in cpu_nums:
+        u_boot_console.run_command(f'cpu {num} disable')
+
+# Load apps on RPU cores
+def rpu_apps_load(u_boot_console, rpu_mode):
+    apps, procs, cpu_nums, addrs, outputs, tftp_addrs = get_rpu_apps_env(
+        u_boot_console)
+    test_net.test_net_dhcp(u_boot_console)
+    if not test_net.net_set_up:
+        test_net.test_net_setup_static(u_boot_console)
+
+    try:
+        assert tcminit(u_boot_console, rpu_mode).endswith('0')
+
+        for i in range(len(apps)):
+            if rpu_mode == 'lockstep' and procs[i] != 'rpu0':
+                continue
+
+            load_app_ddr(u_boot_console, tftp_addrs[i], apps[i])
+            rel_addr = int(addrs[i] + 0x3C)
+
+            # Release cpu at app load address
+            cpu_num = cpu_nums[i]
+            cmd = 'cpu %d release %x %s' % (cpu_num, rel_addr, rpu_mode)
+            output = u_boot_console.run_command(cmd)
+            exp_op = f'Using TCM jump trampoline for address {hex(rel_addr)}'
+            assert exp_op in output
+            assert f'R5 {rpu_mode} mode' in output
+            u_boot_console.wait_for(outputs[i])
+            assert ret_code(u_boot_console).endswith('0')
+    finally:
+        disable_cpus(u_boot_console, cpu_nums)
+
+@pytest.mark.buildconfigspec('cmd_zynqmp')
+def test_zynqmp_rpu_app_load_split(u_boot_console):
+    rpu_apps_load(u_boot_console, 'split')
+
+@pytest.mark.buildconfigspec('cmd_zynqmp')
+def test_zynqmp_rpu_app_load_lockstep(u_boot_console):
+    rpu_apps_load(u_boot_console, 'lockstep')
+
+@pytest.mark.buildconfigspec('cmd_zynqmp')
+def test_zynqmp_rpu_app_load_negative(u_boot_console):
+    apps, procs, cpu_nums, addrs, outputs, tftp_addrs = get_rpu_apps_env(
+        u_boot_console)
+
+    # Invalid commands
+    u_boot_console.run_command('zynqmp tcminit mode')
+    assert ret_code(u_boot_console).endswith('1')
+
+    rand_str = ''.join(random.choices(string.ascii_lowercase, k=4))
+    u_boot_console.run_command('zynqmp tcminit %s' % rand_str)
+    assert ret_code(u_boot_console).endswith('1')
+
+    rand_num = random.randint(2, 100)
+    u_boot_console.run_command('zynqmp tcminit %d' % rand_num)
+    assert ret_code(u_boot_console).endswith('1')
+
+    test_net.test_net_dhcp(u_boot_console)
+    if not test_net.net_set_up:
+        test_net.test_net_setup_static(u_boot_console)
+
+    try:
+        rpu_mode = 'split'
+        assert tcminit(u_boot_console, rpu_mode).endswith('0')
+
+        for i in range(len(apps)):
+            load_app_ddr(u_boot_console, tftp_addrs[i], apps[i])
+
+            # Run in split mode at different load address
+            rel_addr = int(addrs[i]) + random.randint(200, 1000)
+            cpu_num = cpu_nums[i]
+            cmd = 'cpu %d release %x %s' % (cpu_num, rel_addr, rpu_mode)
+            output = u_boot_console.run_command(cmd)
+            exp_op = f'Using TCM jump trampoline for address {hex(rel_addr)}'
+            assert exp_op in output
+            assert f'R5 {rpu_mode} mode' in output
+            assert not outputs[i] in output
+
+            # Invalid rpu mode
+            rand_str = ''.join(random.choices(string.ascii_lowercase, k=4))
+            cmd = 'cpu %d release %x %s' % (cpu_num, rel_addr, rand_str)
+            output = u_boot_console.run_command(cmd)
+            assert exp_op in output
+            assert f'Unsupported mode' in output
+            assert not ret_code(u_boot_console).endswith('0')
+
+        # Switch to lockstep mode, without disabling CPUs
+        rpu_mode = 'lockstep'
+        u_boot_console.run_command('zynqmp tcminit %s' % rpu_mode)
+        assert not ret_code(u_boot_console).endswith('0')
+
+        # Disable cpus
+        disable_cpus(u_boot_console, cpu_nums)
+
+        # Switch to lockstep mode, after disabling CPUs
+        output = u_boot_console.run_command('zynqmp tcminit %s' % rpu_mode)
+        assert 'Initializing TCM overwrites TCM content' in output
+        assert ret_code(u_boot_console).endswith('0')
+
+        # Run lockstep mode for RPU1
+        for i in range(len(apps)):
+            if procs[i] == 'rpu0':
+                continue
+
+            load_app_ddr(u_boot_console, tftp_addrs[i], apps[i])
+            rel_addr = int(addrs[i] + 0x3C)
+            cpu_num = cpu_nums[i]
+            cmd = 'cpu %d release %x %s' % (cpu_num, rel_addr, rpu_mode)
+            output = u_boot_console.run_command(cmd)
+            exp_op = f'Using TCM jump trampoline for address {hex(rel_addr)}'
+            assert exp_op in output
+            assert f'R5 {rpu_mode} mode' in output
+            assert u_boot_console.p.expect([outputs[i]])
+    finally:
+        disable_cpus(u_boot_console, cpu_nums)
+        # This forces the console object to be shutdown, so any subsequent test
+        # will reset the board back into U-Boot.
+        u_boot_console.drain_console()
+        u_boot_console.cleanup_spawn()
diff --git a/test/py/tests/test_zynqmp_secure.py b/test/py/tests/test_zynqmp_secure.py
new file mode 100644
index 0000000..570bd24
--- /dev/null
+++ b/test/py/tests/test_zynqmp_secure.py
@@ -0,0 +1,104 @@
+# SPDX-License-Identifier: GPL-2.0
+# (C) Copyright 2023, Advanced Micro Devices, Inc.
+
+import pytest
+import re
+import u_boot_utils
+import test_net
+
+"""
+This test verifies different type of secure boot images loaded at the DDR for
+AMD's ZynqMP SoC.
+
+Note: This test relies on boardenv_* containing configuration values to define
+the files to be used for testing. Without this, this test will be automatically
+skipped. It also relies on dhcp or setup_static net test to support tftp to
+load files from a TFTP server.
+
+For example:
+
+# Details regarding the files that may be read from a TFTP server. This
+# variable may be omitted or set to None if zynqmp secure testing is not
+# possible or desired.
+env__zynqmp_secure_readable_file = {
+    'fn': 'auth_bhdr_ppk1.bin',
+    'enckupfn': 'auth_bhdr_enc_kup_load.bin',
+    'addr': 0x1000000,
+    'keyaddr': 0x100000,
+    'keyfn': 'aes.txt',
+}
+"""
+
+@pytest.mark.buildconfigspec('cmd_zynqmp')
+def test_zynqmp_secure_boot_image(u_boot_console):
+    """This test verifies secure boot image at the DDR address for
+    authentication only case.
+    """
+
+    f = u_boot_console.config.env.get('env__zynqmp_secure_readable_file', None)
+    if not f:
+        pytest.skip('No TFTP readable file for zynqmp secure cases to read')
+
+    test_net.test_net_dhcp(u_boot_console)
+    if not test_net.net_set_up:
+        test_net.test_net_setup_static(u_boot_console)
+
+    addr = f.get('addr', None)
+    if not addr:
+        addr = u_boot_utils.find_ram_base(u_boot_console)
+
+    expected_tftp = 'Bytes transferred = '
+    fn = f['fn']
+    output = u_boot_console.run_command('tftpboot %x %s' % (addr, fn))
+    assert expected_tftp in output
+
+    output = u_boot_console.run_command('zynqmp secure %x $filesize' % (addr))
+    assert 'Verified image at' in output
+    ver_addr = re.search(r'Verified image at 0x(.+)', output).group(1)
+    output = u_boot_console.run_command('echo $?')
+    assert output.endswith('0')
+    output = u_boot_console.run_command('print zynqmp_verified_img_addr')
+    assert f'zynqmp_verified_img_addr={ver_addr}' in output
+    assert 'Error' not in output
+
+
+@pytest.mark.buildconfigspec('cmd_zynqmp')
+def test_zynqmp_secure_boot_img_kup(u_boot_console):
+    """This test verifies secure boot image at the DDR address for encryption
+    with kup key case.
+    """
+
+    f = u_boot_console.config.env.get('env__zynqmp_secure_readable_file', None)
+    if not f:
+        pytest.skip('No TFTP readable file for zynqmp secure cases to read')
+
+    test_net.test_net_dhcp(u_boot_console)
+    if not test_net.net_set_up:
+        test_net.test_net_setup_static(u_boot_console)
+
+    keyaddr = f.get('keyaddr', None)
+    if not keyaddr:
+        addr = u_boot_utils.find_ram_base(u_boot_console)
+    expected_tftp = 'Bytes transferred = '
+    keyfn = f['keyfn']
+    output = u_boot_console.run_command('tftpboot %x %s' % (keyaddr, keyfn))
+    assert expected_tftp in output
+
+    addr = f.get('addr', None)
+    if not addr:
+        addr = u_boot_utils.find_ram_base(u_boot_console)
+    expected_tftp = 'Bytes transferred = '
+    fn = f['enckupfn']
+    output = u_boot_console.run_command('tftpboot %x %s' % (addr, fn))
+    assert expected_tftp in output
+
+    output = u_boot_console.run_command(
+        'zynqmp secure %x $filesize %x' % (addr, keyaddr)
+    )
+    assert 'Verified image at' in output
+    ver_addr = re.search(r'Verified image at 0x(.+)', output).group(1)
+    output = u_boot_console.run_command('echo $?')
+    assert output.endswith('0')
+    output = u_boot_console.run_command('print zynqmp_verified_img_addr')
+    assert f'zynqmp_verified_img_addr={ver_addr}' in output
+    assert 'Error' not in output
diff --git a/tools/buildman/bsettings.py b/tools/buildman/bsettings.py
index f7f8276..e225ac2 100644
--- a/tools/buildman/bsettings.py
+++ b/tools/buildman/bsettings.py
@@ -16,7 +16,7 @@
     global settings
     global config_fname
 
-    settings = configparser.SafeConfigParser()
+    settings = configparser.ConfigParser()
     if fname is not None:
         config_fname = fname
         if config_fname == '':