Merge https://source.denx.de/u-boot/custodians/u-boot-usb
diff --git a/MAINTAINERS b/MAINTAINERS
index 1baa038..b85629d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -827,6 +827,7 @@
 
 EFI PAYLOAD
 M:	Heinrich Schuchardt <xypron.glpk@gmx.de>
+M:	Ilias Apalodimas <ilias.apalodimas@linaro.org>
 S:	Maintained
 T:	git https://source.denx.de/u-boot/custodians/u-boot-efi.git
 F:	doc/api/efi.rst
@@ -1031,15 +1032,23 @@
 S:	Maintained
 F:	arch/mips/mach-mtmips/
 F:	arch/mips/dts/mt7620.dtsi
+F:	arch/mips/dts/mt7621.dtsi
 F:	arch/mips/dts/mt7620-u-boot.dtsi
+F:	arch/mips/dts/mt7621-u-boot.dtsi
 F:	include/configs/mt7620.h
+F:	include/configs/mt7621.h
 F:	include/dt-bindings/clock/mt7620-clk.h
+F:	include/dt-bindings/clock/mt7621-clk.h
 F:	include/dt-bindings/clock/mt7628-clk.h
 F:	include/dt-bindings/reset/mt7620-reset.h
+F:	include/dt-bindings/reset/mt7621-reset.h
 F:	include/dt-bindings/reset/mt7628-reset.h
 F:	drivers/clk/mtmips/
 F:	drivers/pinctrl/mtmips/
 F:	drivers/gpio/mt7620_gpio.c
+F:	drivers/mtd/nand/raw/mt7621_nand.c
+F:	drivers/mtd/nand/raw/mt7621_nand.h
+F:	drivers/mtd/nand/raw/mt7621_nand_spl.c
 F:	drivers/net/mt7620-eth.c
 F:	drivers/phy/mt7620-usb-phy.c
 F:	drivers/reset/reset-mtmips.c
diff --git a/arch/arm/dts/Makefile b/arch/arm/dts/Makefile
index c2435d8..66c7199 100644
--- a/arch/arm/dts/Makefile
+++ b/arch/arm/dts/Makefile
@@ -1174,13 +1174,17 @@
 
 dtb-$(CONFIG_STM32MP15x) += \
 	stm32mp157a-dk1.dtb \
+	stm32mp157a-dk1-scmi.dtb \
 	stm32mp157a-icore-stm32mp1-ctouch2.dtb \
 	stm32mp157a-icore-stm32mp1-edimm2.2.dtb \
 	stm32mp157a-microgea-stm32mp1-microdev2.0.dtb \
 	stm32mp157a-microgea-stm32mp1-microdev2.0-of7.dtb \
 	stm32mp157c-dk2.dtb \
+	stm32mp157c-dk2-scmi.dtb \
 	stm32mp157c-ed1.dtb \
+	stm32mp157c-ed1-scmi.dtb \
 	stm32mp157c-ev1.dtb \
+	stm32mp157c-ev1-scmi.dtb \
 	stm32mp157c-odyssey.dtb \
 	stm32mp15xx-dhcom-drc02.dtb \
 	stm32mp15xx-dhcom-pdk2.dtb \
diff --git a/arch/arm/dts/stm32mp13-u-boot.dtsi b/arch/arm/dts/stm32mp13-u-boot.dtsi
index 1b5b358..01552ad 100644
--- a/arch/arm/dts/stm32mp13-u-boot.dtsi
+++ b/arch/arm/dts/stm32mp13-u-boot.dtsi
@@ -82,10 +82,34 @@
 	u-boot,dm-pre-reloc;
 };
 
+&optee {
+	u-boot,dm-pre-reloc;
+};
+
 &pinctrl {
 	u-boot,dm-pre-reloc;
 };
 
+&scmi {
+	u-boot,dm-pre-reloc;
+};
+
+&scmi_clk {
+	u-boot,dm-pre-reloc;
+};
+
+&scmi_reset {
+	u-boot,dm-pre-reloc;
+};
+
+&scmi_shm {
+	u-boot,dm-pre-reloc;
+};
+
+&scmi_sram {
+	u-boot,dm-pre-reloc;
+};
+
 &syscfg {
 	u-boot,dm-pre-reloc;
 };
diff --git a/arch/arm/dts/stm32mp131.dtsi b/arch/arm/dts/stm32mp131.dtsi
index 950e172..84e16bb 100644
--- a/arch/arm/dts/stm32mp131.dtsi
+++ b/arch/arm/dts/stm32mp131.dtsi
@@ -27,6 +27,44 @@
 		interrupt-parent = <&intc>;
 	};
 
+	scmi_sram: sram@2ffff000 {
+		compatible = "mmio-sram";
+		reg = <0x2ffff000 0x1000>;
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges = <0 0x2ffff000 0x1000>;
+
+		scmi_shm: scmi_shm@0 {
+			compatible = "arm,scmi-shmem";
+			reg = <0 0x80>;
+		};
+	};
+
+	firmware {
+		optee: optee {
+			method = "smc";
+			compatible = "linaro,optee-tz";
+		};
+
+		scmi: scmi {
+			compatible = "linaro,scmi-optee";
+			#address-cells = <1>;
+			#size-cells = <0>;
+			linaro,optee-channel-id = <0>;
+			shmem = <&scmi_shm>;
+
+			scmi_clk: protocol@14 {
+				reg = <0x14>;
+				#clock-cells = <1>;
+			};
+
+			scmi_reset: protocol@16 {
+				reg = <0x16>;
+				#reset-cells = <1>;
+			};
+		};
+	};
+
 	clocks {
 		clk_axi: clk-axi {
 			#clock-cells = <0>;
@@ -75,6 +113,12 @@
 			compatible = "fixed-clock";
 			clock-frequency = <99000000>;
 		};
+
+		clk_rtc_k: clk-rtc-k {
+			#clock-cells = <0>;
+			compatible = "fixed-clock";
+			clock-frequency = <32768>;
+		};
 	};
 
 	intc: interrupt-controller@a0021000 {
@@ -218,6 +262,15 @@
 			status = "disabled";
 		};
 
+		rtc: rtc@5c004000 {
+			compatible = "st,stm32mp1-rtc";
+			reg = <0x5c004000 0x400>;
+			interrupts-extended = <&exti 19 IRQ_TYPE_LEVEL_HIGH>;
+			clocks = <&clk_pclk4>, <&clk_rtc_k>;
+			clock-names = "pclk", "rtc_ck";
+			status = "disabled";
+		};
+
 		bsec: efuse@5c005000 {
 			compatible = "st,stm32mp13-bsec";
 			reg = <0x5c005000 0x400>;
@@ -239,11 +292,13 @@
 		 * Break node order to solve dependency probe issue between
 		 * pinctrl and exti.
 		 */
-		pinctrl: pin-controller@50002000 {
+		pinctrl: pinctrl@50002000 {
 			#address-cells = <1>;
 			#size-cells = <1>;
 			compatible = "st,stm32mp135-pinctrl";
 			ranges = <0 0x50002000 0x8400>;
+			interrupt-parent = <&exti>;
+			st,syscfg = <&exti 0x60 0xff>;
 			pins-are-numbered;
 
 			gpioa: gpio@50002000 {
diff --git a/arch/arm/dts/stm32mp135f-dk-u-boot.dtsi b/arch/arm/dts/stm32mp135f-dk-u-boot.dtsi
index dfe5bbb..cbe4eb5 100644
--- a/arch/arm/dts/stm32mp135f-dk-u-boot.dtsi
+++ b/arch/arm/dts/stm32mp135f-dk-u-boot.dtsi
@@ -11,8 +11,18 @@
 	};
 
 	config {
+		u-boot,boot-led = "led-blue";
+		u-boot,error-led = "led-red";
 		u-boot,mmc-env-partition = "u-boot-env";
 	};
+
+	leds {
+		led-red {
+			color = <LED_COLOR_ID_RED>;
+			gpios = <&gpioa 13 GPIO_ACTIVE_LOW>;
+			default-state = "off";
+		};
+	};
 };
 
 &uart4 {
diff --git a/arch/arm/dts/stm32mp135f-dk.dts b/arch/arm/dts/stm32mp135f-dk.dts
index ee100d1..f436ffa 100644
--- a/arch/arm/dts/stm32mp135f-dk.dts
+++ b/arch/arm/dts/stm32mp135f-dk.dts
@@ -6,6 +6,9 @@
 
 /dts-v1/;
 
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/input/input.h>
+#include <dt-bindings/leds/common.h>
 #include "stm32mp135.dtsi"
 #include "stm32mp13xf.dtsi"
 #include "stm32mp13-pinctrl.dtsi"
@@ -23,6 +26,39 @@
 		reg = <0xc0000000 0x20000000>;
 	};
 
+	reserved-memory {
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges;
+
+		optee@de000000 {
+			reg = <0xde000000 0x2000000>;
+			no-map;
+		};
+	};
+
+	gpio-keys {
+		compatible = "gpio-keys";
+
+		user-pa13 {
+			label = "User-PA13";
+			linux,code = <BTN_1>;
+			gpios = <&gpioa 13 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>;
+		};
+	};
+
+	leds {
+		compatible = "gpio-leds";
+
+		led-blue {
+			function = LED_FUNCTION_HEARTBEAT;
+			color = <LED_COLOR_ID_BLUE>;
+			gpios = <&gpioa 14 GPIO_ACTIVE_LOW>;
+			linux,default-trigger = "heartbeat";
+			default-state = "off";
+		};
+	};
+
 	vdd_sd: vdd-sd {
 		compatible = "regulator-fixed";
 		regulator-name = "vdd_sd";
@@ -37,6 +73,10 @@
 	status = "okay";
 };
 
+&rtc {
+	status = "okay";
+};
+
 &sdmmc1 {
 	pinctrl-names = "default", "opendrain", "sleep";
 	pinctrl-0 = <&sdmmc1_b4_pins_a &sdmmc1_clk_pins_a>;
diff --git a/arch/arm/dts/stm32mp15-pinctrl.dtsi b/arch/arm/dts/stm32mp15-pinctrl.dtsi
index b92a149..d3ed103 100644
--- a/arch/arm/dts/stm32mp15-pinctrl.dtsi
+++ b/arch/arm/dts/stm32mp15-pinctrl.dtsi
@@ -379,6 +379,40 @@
 		};
 	};
 
+	ethernet0_rmii_pins_c: rmii-2 {
+		pins1 {
+			pinmux = <STM32_PINMUX('G', 13, AF11)>, /* ETH1_RMII_TXD0 */
+				 <STM32_PINMUX('G', 14, AF11)>, /* ETH1_RMII_TXD1 */
+				 <STM32_PINMUX('B', 11, AF11)>, /* ETH1_RMII_TX_EN */
+				 <STM32_PINMUX('A', 1, AF11)>,  /* ETH1_RMII_REF_CLK */
+				 <STM32_PINMUX('A', 2, AF11)>,  /* ETH1_MDIO */
+				 <STM32_PINMUX('C', 1, AF11)>;  /* ETH1_MDC */
+			bias-disable;
+			drive-push-pull;
+			slew-rate = <2>;
+		};
+		pins2 {
+			pinmux = <STM32_PINMUX('C', 4, AF11)>,  /* ETH1_RMII_RXD0 */
+				 <STM32_PINMUX('C', 5, AF11)>,  /* ETH1_RMII_RXD1 */
+				 <STM32_PINMUX('A', 7, AF11)>;  /* ETH1_RMII_CRS_DV */
+			bias-disable;
+		};
+	};
+
+	ethernet0_rmii_sleep_pins_c: rmii-sleep-2 {
+		pins1 {
+			pinmux = <STM32_PINMUX('G', 13, ANALOG)>, /* ETH1_RMII_TXD0 */
+				 <STM32_PINMUX('G', 14, ANALOG)>, /* ETH1_RMII_TXD1 */
+				 <STM32_PINMUX('B', 11, ANALOG)>, /* ETH1_RMII_TX_EN */
+				 <STM32_PINMUX('A', 2, ANALOG)>,  /* ETH1_MDIO */
+				 <STM32_PINMUX('C', 1, ANALOG)>,  /* ETH1_MDC */
+				 <STM32_PINMUX('C', 4, ANALOG)>,  /* ETH1_RMII_RXD0 */
+				 <STM32_PINMUX('C', 5, ANALOG)>,  /* ETH1_RMII_RXD1 */
+				 <STM32_PINMUX('A', 1, ANALOG)>,  /* ETH1_RMII_REF_CLK */
+				 <STM32_PINMUX('A', 7, ANALOG)>;  /* ETH1_RMII_CRS_DV */
+		};
+	};
+
 	fmc_pins_a: fmc-0 {
 		pins1 {
 			pinmux = <STM32_PINMUX('D', 4, AF12)>, /* FMC_NOE */
@@ -889,6 +923,21 @@
 		};
 	};
 
+	mco2_pins_a: mco2-0 {
+		pins {
+			pinmux = <STM32_PINMUX('G', 2, AF1)>; /* MCO2 */
+			bias-disable;
+			drive-push-pull;
+			slew-rate = <2>;
+		};
+	};
+
+	mco2_sleep_pins_a: mco2-sleep-0 {
+		pins {
+			pinmux = <STM32_PINMUX('G', 2, ANALOG)>; /* MCO2 */
+		};
+	};
+
 	m_can1_pins_a: m-can1-0 {
 		pins1 {
 			pinmux = <STM32_PINMUX('H', 13, AF9)>; /* CAN1_TX */
@@ -2331,4 +2380,19 @@
 			bias-disable;
 		};
 	};
+
+	spi1_pins_b: spi1-1 {
+		pins1 {
+			pinmux = <STM32_PINMUX('A', 5, AF5)>, /* SPI1_SCK */
+				 <STM32_PINMUX('B', 5, AF5)>; /* SPI1_MOSI */
+			bias-disable;
+			drive-push-pull;
+			slew-rate = <1>;
+		};
+
+		pins2 {
+			pinmux = <STM32_PINMUX('A', 6, AF5)>; /* SPI1_MISO */
+			bias-disable;
+		};
+	};
 };
diff --git a/arch/arm/dts/stm32mp15-scmi-u-boot.dtsi b/arch/arm/dts/stm32mp15-scmi-u-boot.dtsi
new file mode 100644
index 0000000..314fc39
--- /dev/null
+++ b/arch/arm/dts/stm32mp15-scmi-u-boot.dtsi
@@ -0,0 +1,167 @@
+// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-3-Clause)
+/*
+ * Copyright (C) STMicroelectronics 2022 - All Rights Reserved
+ */
+
+/ {
+	aliases {
+		gpio0 = &gpioa;
+		gpio1 = &gpiob;
+		gpio2 = &gpioc;
+		gpio3 = &gpiod;
+		gpio4 = &gpioe;
+		gpio5 = &gpiof;
+		gpio6 = &gpiog;
+		gpio7 = &gpioh;
+		gpio8 = &gpioi;
+		gpio9 = &gpioj;
+		gpio10 = &gpiok;
+		gpio25 = &gpioz;
+		pinctrl0 = &pinctrl;
+		pinctrl1 = &pinctrl_z;
+	};
+
+	binman: binman {
+		multiple-images;
+	};
+
+	soc {
+		u-boot,dm-pre-reloc;
+
+		ddr: ddr@5a003000 {
+			u-boot,dm-pre-reloc;
+
+			compatible = "st,stm32mp1-ddr";
+
+			reg = <0x5a003000 0x550
+			       0x5a004000 0x234>;
+
+			status = "okay";
+		};
+	};
+
+	/* need PSCI for sysreset during board_f */
+	psci {
+		u-boot,dm-pre-proper;
+	};
+};
+
+&bsec {
+	u-boot,dm-pre-reloc;
+};
+
+&gpioa {
+	u-boot,dm-pre-reloc;
+};
+
+&gpiob {
+	u-boot,dm-pre-reloc;
+};
+
+&gpioc {
+	u-boot,dm-pre-reloc;
+};
+
+&gpiod {
+	u-boot,dm-pre-reloc;
+};
+
+&gpioe {
+	u-boot,dm-pre-reloc;
+};
+
+&gpiof {
+	u-boot,dm-pre-reloc;
+};
+
+&gpiog {
+	u-boot,dm-pre-reloc;
+};
+
+&gpioh {
+	u-boot,dm-pre-reloc;
+};
+
+&gpioi {
+	u-boot,dm-pre-reloc;
+};
+
+&gpioj {
+	u-boot,dm-pre-reloc;
+};
+
+&gpiok {
+	u-boot,dm-pre-reloc;
+};
+
+&gpioz {
+	u-boot,dm-pre-reloc;
+};
+
+&optee {
+	u-boot,dm-pre-proper;
+};
+
+&iwdg2 {
+	u-boot,dm-pre-reloc;
+};
+
+/* pre-reloc probe = reserve video frame buffer in video_reserve() */
+&ltdc {
+	u-boot,dm-pre-proper;
+};
+
+/* temp = waiting kernel update */
+&m4_rproc {
+	resets = <&scmi_reset RST_SCMI_MCU>,
+		 <&scmi_reset RST_SCMI_MCU_HOLD_BOOT>;
+	reset-names = "mcu_rst", "hold_boot";
+};
+
+&pinctrl {
+	u-boot,dm-pre-reloc;
+};
+
+&pinctrl_z {
+	u-boot,dm-pre-reloc;
+};
+
+&rcc {
+	u-boot,dm-pre-reloc;
+};
+
+&scmi {
+	u-boot,dm-pre-proper;
+};
+
+&usart1 {
+	resets = <&rcc USART1_R>;
+};
+
+&usart2 {
+	resets = <&rcc USART2_R>;
+};
+
+&usart3 {
+	resets = <&rcc USART3_R>;
+};
+
+&uart4 {
+	resets = <&rcc UART4_R>;
+};
+
+&uart5 {
+	resets = <&rcc UART5_R>;
+};
+
+&usart6 {
+	resets = <&rcc USART6_R>;
+};
+
+&uart7 {
+	resets = <&rcc UART7_R>;
+};
+
+&uart8{
+	resets = <&rcc UART8_R>;
+};
diff --git a/arch/arm/dts/stm32mp15-scmi.dtsi b/arch/arm/dts/stm32mp15-scmi.dtsi
new file mode 100644
index 0000000..37d4547
--- /dev/null
+++ b/arch/arm/dts/stm32mp15-scmi.dtsi
@@ -0,0 +1,109 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
+/*
+ * Copyright (C) STMicroelectronics 2022 - All Rights Reserved
+ * Author: Alexandre Torgue <alexandre.torgue@foss.st.com> for STMicroelectronics.
+ */
+
+/ {
+	firmware {
+		optee: optee {
+			compatible = "linaro,optee-tz";
+			method = "smc";
+		};
+
+		scmi: scmi {
+			compatible = "linaro,scmi-optee";
+			#address-cells = <1>;
+			#size-cells = <0>;
+			linaro,optee-channel-id = <0>;
+			shmem = <&scmi_shm>;
+
+			scmi_clk: protocol@14 {
+				reg = <0x14>;
+				#clock-cells = <1>;
+			};
+
+			scmi_reset: protocol@16 {
+				reg = <0x16>;
+				#reset-cells = <1>;
+			};
+
+			scmi_voltd: protocol@17 {
+				reg = <0x17>;
+
+				scmi_reguls: regulators {
+					#address-cells = <1>;
+					#size-cells = <0>;
+
+					scmi_reg11: reg11@0 {
+						reg = <0>;
+						regulator-name = "reg11";
+						regulator-min-microvolt = <1100000>;
+						regulator-max-microvolt = <1100000>;
+					};
+
+					scmi_reg18: reg18@1 {
+						voltd-name = "reg18";
+						reg = <1>;
+						regulator-name = "reg18";
+						regulator-min-microvolt = <1800000>;
+						regulator-max-microvolt = <1800000>;
+					};
+
+					scmi_usb33: usb33@2 {
+						reg = <2>;
+						regulator-name = "usb33";
+						regulator-min-microvolt = <3300000>;
+						regulator-max-microvolt = <3300000>;
+					};
+				};
+			};
+		};
+	};
+
+	soc {
+		scmi_sram: sram@2ffff000 {
+			compatible = "mmio-sram";
+			reg = <0x2ffff000 0x1000>;
+			#address-cells = <1>;
+			#size-cells = <1>;
+			ranges = <0 0x2ffff000 0x1000>;
+
+			scmi_shm: scmi-sram@0 {
+				compatible = "arm,scmi-shmem";
+				reg = <0 0x80>;
+			};
+		};
+	};
+};
+
+&reg11 {
+	status = "disabled";
+};
+
+&reg18 {
+	status = "disabled";
+};
+
+&usb33 {
+	status = "disabled";
+};
+
+&usbotg_hs {
+	usb33d-supply = <&scmi_usb33>;
+};
+
+&usbphyc {
+	vdda1v1-supply = <&scmi_reg11>;
+	vdda1v8-supply = <&scmi_reg18>;
+};
+
+/delete-node/ &clk_hse;
+/delete-node/ &clk_hsi;
+/delete-node/ &clk_lse;
+/delete-node/ &clk_lsi;
+/delete-node/ &clk_csi;
+/delete-node/ &reg11;
+/delete-node/ &reg18;
+/delete-node/ &usb33;
+/delete-node/ &pwr_regulators;
diff --git a/arch/arm/dts/stm32mp151.dtsi b/arch/arm/dts/stm32mp151.dtsi
index e74a5fa..a5ac62c 100644
--- a/arch/arm/dts/stm32mp151.dtsi
+++ b/arch/arm/dts/stm32mp151.dtsi
@@ -583,7 +583,7 @@
 			compatible = "st,stm32-cec";
 			reg = <0x40016000 0x400>;
 			interrupts = <GIC_SPI 94 IRQ_TYPE_LEVEL_HIGH>;
-			clocks = <&rcc CEC_K>, <&clk_lse>;
+			clocks = <&rcc CEC_K>, <&rcc CEC>;
 			clock-names = "cec", "hdmi-cec";
 			status = "disabled";
 		};
@@ -1504,7 +1504,7 @@
 		usbh_ohci: usb@5800c000 {
 			compatible = "generic-ohci";
 			reg = <0x5800c000 0x1000>;
-			clocks = <&rcc USBH>, <&usbphyc>;
+			clocks = <&usbphyc>, <&rcc USBH>;
 			resets = <&rcc USBH_R>;
 			interrupts = <GIC_SPI 74 IRQ_TYPE_LEVEL_HIGH>;
 			status = "disabled";
@@ -1513,7 +1513,7 @@
 		usbh_ehci: usb@5800d000 {
 			compatible = "generic-ehci";
 			reg = <0x5800d000 0x1000>;
-			clocks = <&rcc USBH>;
+			clocks = <&usbphyc>, <&rcc USBH>;
 			resets = <&rcc USBH_R>;
 			interrupts = <GIC_SPI 75 IRQ_TYPE_LEVEL_HIGH>;
 			companion = <&usbh_ohci>;
@@ -1656,7 +1656,7 @@
 		 * Break node order to solve dependency probe issue between
 		 * pinctrl and exti.
 		 */
-		pinctrl: pin-controller@50002000 {
+		pinctrl: pinctrl@50002000 {
 			#address-cells = <1>;
 			#size-cells = <1>;
 			compatible = "st,stm32mp157-pinctrl";
@@ -1788,7 +1788,7 @@
 			};
 		};
 
-		pinctrl_z: pin-controller-z@54004000 {
+		pinctrl_z: pinctrl@54004000 {
 			#address-cells = <1>;
 			#size-cells = <1>;
 			compatible = "st,stm32mp157-z-pinctrl";
diff --git a/arch/arm/dts/stm32mp157a-dk1-scmi-u-boot.dtsi b/arch/arm/dts/stm32mp157a-dk1-scmi-u-boot.dtsi
new file mode 100644
index 0000000..2db045e
--- /dev/null
+++ b/arch/arm/dts/stm32mp157a-dk1-scmi-u-boot.dtsi
@@ -0,0 +1,57 @@
+// SPDX-License-Identifier: GPL-2.0-or-later OR BSD-3-Clause
+/*
+ * Copyright : STMicroelectronics 2022
+ */
+
+#include <dt-bindings/clock/stm32mp1-clksrc.h>
+#include "stm32mp15-scmi-u-boot.dtsi"
+#include "stm32mp15-ddr3-1x4Gb-1066-binG.dtsi"
+
+/ {
+	aliases {
+		i2c3 = &i2c4;
+		usb0 = &usbotg_hs;
+	};
+
+	config {
+		u-boot,boot-led = "heartbeat";
+		u-boot,error-led = "error";
+		u-boot,mmc-env-partition = "u-boot-env";
+		st,adc_usb_pd = <&adc1 18>, <&adc1 19>;
+		st,fastboot-gpios = <&gpioa 13 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>;
+		st,stm32prog-gpios = <&gpioa 14 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>;
+	};
+
+	led {
+		red {
+			label = "error";
+			gpios = <&gpioa 13 GPIO_ACTIVE_LOW>;
+			default-state = "off";
+			status = "okay";
+		};
+	};
+};
+
+&adc {
+	status = "okay";
+};
+
+&uart4 {
+	u-boot,dm-pre-reloc;
+};
+
+&uart4_pins_a {
+	u-boot,dm-pre-reloc;
+	pins1 {
+		u-boot,dm-pre-reloc;
+	};
+	pins2 {
+		u-boot,dm-pre-reloc;
+		/* pull-up on rx to avoid floating level */
+		bias-pull-up;
+	};
+};
+
+&usbotg_hs {
+	u-boot,force-b-session-valid;
+};
diff --git a/arch/arm/dts/stm32mp157a-dk1-scmi.dts b/arch/arm/dts/stm32mp157a-dk1-scmi.dts
new file mode 100644
index 0000000..e539cc8
--- /dev/null
+++ b/arch/arm/dts/stm32mp157a-dk1-scmi.dts
@@ -0,0 +1,79 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
+/*
+ * Copyright (C) STMicroelectronics 2022 - All Rights Reserved
+ * Author: Alexandre Torgue <alexandre.torgue@foss.st.com> for STMicroelectronics.
+ */
+
+/dts-v1/;
+
+#include "stm32mp157a-dk1.dts"
+#include "stm32mp15-scmi.dtsi"
+
+/ {
+	model = "STMicroelectronics STM32MP157A-DK1 SCMI Discovery Board";
+	compatible = "st,stm32mp157a-dk1-scmi", "st,stm32mp157a-dk1", "st,stm32mp157";
+
+	reserved-memory {
+		optee@de000000 {
+			reg = <0xde000000 0x2000000>;
+			no-map;
+		};
+	};
+};
+
+&cpu0 {
+	clocks = <&scmi_clk CK_SCMI_MPU>;
+};
+
+&cpu1 {
+	clocks = <&scmi_clk CK_SCMI_MPU>;
+};
+
+&dsi {
+	clocks = <&rcc DSI_K>, <&scmi_clk CK_SCMI_HSE>, <&rcc DSI_PX>;
+};
+
+&gpioz {
+	clocks = <&scmi_clk CK_SCMI_GPIOZ>;
+};
+
+&hash1 {
+	clocks = <&scmi_clk CK_SCMI_HASH1>;
+	resets = <&scmi_reset RST_SCMI_HASH1>;
+};
+
+&i2c4 {
+	clocks = <&scmi_clk CK_SCMI_I2C4>;
+	resets = <&scmi_reset RST_SCMI_I2C4>;
+};
+
+&iwdg2 {
+	clocks = <&rcc IWDG2>, <&scmi_clk CK_SCMI_LSI>;
+};
+
+&mdma1 {
+	resets = <&scmi_reset RST_SCMI_MDMA>;
+};
+
+&mlahb {
+	resets = <&scmi_reset RST_SCMI_MCU>;
+};
+
+&rcc {
+	compatible = "st,stm32mp1-rcc-secure", "syscon";
+	clock-names = "hse", "hsi", "csi", "lse", "lsi";
+	clocks = <&scmi_clk CK_SCMI_HSE>,
+		 <&scmi_clk CK_SCMI_HSI>,
+		 <&scmi_clk CK_SCMI_CSI>,
+		 <&scmi_clk CK_SCMI_LSE>,
+		 <&scmi_clk CK_SCMI_LSI>;
+};
+
+&rng1 {
+	clocks = <&scmi_clk CK_SCMI_RNG1>;
+	resets = <&scmi_reset RST_SCMI_RNG1>;
+};
+
+&rtc {
+	clocks = <&scmi_clk CK_SCMI_RTCAPB>, <&scmi_clk CK_SCMI_RTC>;
+};
diff --git a/arch/arm/dts/stm32mp157c-dk2-scmi-u-boot.dtsi b/arch/arm/dts/stm32mp157c-dk2-scmi-u-boot.dtsi
new file mode 100644
index 0000000..5a8fc15
--- /dev/null
+++ b/arch/arm/dts/stm32mp157c-dk2-scmi-u-boot.dtsi
@@ -0,0 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-or-later OR BSD-3-Clause
+/*
+ * Copyright : STMicroelectronics 2022
+ */
+
+#include "stm32mp157a-dk1-scmi-u-boot.dtsi"
diff --git a/arch/arm/dts/stm32mp157c-dk2-scmi.dts b/arch/arm/dts/stm32mp157c-dk2-scmi.dts
new file mode 100644
index 0000000..97e4f94
--- /dev/null
+++ b/arch/arm/dts/stm32mp157c-dk2-scmi.dts
@@ -0,0 +1,85 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
+/*
+ * Copyright (C) STMicroelectronics 2022 - All Rights Reserved
+ * Author: Alexandre Torgue <alexandre.torgue@foss.st.com> for STMicroelectronics.
+ */
+
+/dts-v1/;
+
+#include "stm32mp157c-dk2.dts"
+#include "stm32mp15-scmi.dtsi"
+
+/ {
+	model = "STMicroelectronics STM32MP157C-DK2 SCMI Discovery Board";
+	compatible = "st,stm32mp157c-dk2-scmi", "st,stm32mp157c-dk2", "st,stm32mp157";
+
+	reserved-memory {
+		optee@de000000 {
+			reg = <0xde000000 0x2000000>;
+			no-map;
+		};
+	};
+};
+
+&cpu0 {
+	clocks = <&scmi_clk CK_SCMI_MPU>;
+};
+
+&cpu1 {
+	clocks = <&scmi_clk CK_SCMI_MPU>;
+};
+
+&cryp1 {
+	clocks = <&scmi_clk CK_SCMI_CRYP1>;
+	resets = <&scmi_reset RST_SCMI_CRYP1>;
+};
+
+&dsi {
+	phy-dsi-supply = <&scmi_reg18>;
+	clocks = <&rcc DSI_K>, <&scmi_clk CK_SCMI_HSE>, <&rcc DSI_PX>;
+};
+
+&gpioz {
+	clocks = <&scmi_clk CK_SCMI_GPIOZ>;
+};
+
+&hash1 {
+	clocks = <&scmi_clk CK_SCMI_HASH1>;
+	resets = <&scmi_reset RST_SCMI_HASH1>;
+};
+
+&i2c4 {
+	clocks = <&scmi_clk CK_SCMI_I2C4>;
+	resets = <&scmi_reset RST_SCMI_I2C4>;
+};
+
+&iwdg2 {
+	clocks = <&rcc IWDG2>, <&scmi_clk CK_SCMI_LSI>;
+};
+
+&mdma1 {
+	resets = <&scmi_reset RST_SCMI_MDMA>;
+};
+
+&mlahb {
+	resets = <&scmi_reset RST_SCMI_MCU>;
+};
+
+&rcc {
+	compatible = "st,stm32mp1-rcc-secure", "syscon";
+	clock-names = "hse", "hsi", "csi", "lse", "lsi";
+	clocks = <&scmi_clk CK_SCMI_HSE>,
+		 <&scmi_clk CK_SCMI_HSI>,
+		 <&scmi_clk CK_SCMI_CSI>,
+		 <&scmi_clk CK_SCMI_LSE>,
+		 <&scmi_clk CK_SCMI_LSI>;
+};
+
+&rng1 {
+	clocks = <&scmi_clk CK_SCMI_RNG1>;
+	resets = <&scmi_reset RST_SCMI_RNG1>;
+};
+
+&rtc {
+	clocks = <&scmi_clk CK_SCMI_RTCAPB>, <&scmi_clk CK_SCMI_RTC>;
+};
diff --git a/arch/arm/dts/stm32mp157c-ed1-scmi-u-boot.dtsi b/arch/arm/dts/stm32mp157c-ed1-scmi-u-boot.dtsi
new file mode 100644
index 0000000..54662f7
--- /dev/null
+++ b/arch/arm/dts/stm32mp157c-ed1-scmi-u-boot.dtsi
@@ -0,0 +1,47 @@
+// SPDX-License-Identifier: GPL-2.0-or-later OR BSD-3-Clause
+/*
+ * Copyright : STMicroelectronics 2022
+ */
+
+#include <dt-bindings/clock/stm32mp1-clksrc.h>
+#include "stm32mp15-scmi-u-boot.dtsi"
+#include "stm32mp15-ddr3-2x4Gb-1066-binG.dtsi"
+
+/ {
+	aliases {
+		i2c3 = &i2c4;
+	};
+
+	config {
+		u-boot,boot-led = "heartbeat";
+		u-boot,error-led = "error";
+		u-boot,mmc-env-partition = "u-boot-env";
+		st,fastboot-gpios = <&gpioa 13 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>;
+		st,stm32prog-gpios = <&gpioa 14 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>;
+	};
+
+	led {
+		red {
+			label = "error";
+			gpios = <&gpioa 13 GPIO_ACTIVE_LOW>;
+			default-state = "off";
+			status = "okay";
+		};
+	};
+};
+
+&uart4 {
+	u-boot,dm-pre-reloc;
+};
+
+&uart4_pins_a {
+	u-boot,dm-pre-reloc;
+	pins1 {
+		u-boot,dm-pre-reloc;
+	};
+	pins2 {
+		u-boot,dm-pre-reloc;
+		/* pull-up on rx to avoid floating level */
+		bias-pull-up;
+	};
+};
diff --git a/arch/arm/dts/stm32mp157c-ed1-scmi.dts b/arch/arm/dts/stm32mp157c-ed1-scmi.dts
new file mode 100644
index 0000000..9cf0a44
--- /dev/null
+++ b/arch/arm/dts/stm32mp157c-ed1-scmi.dts
@@ -0,0 +1,84 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
+/*
+ * Copyright (C) STMicroelectronics 2022 - All Rights Reserved
+ * Author: Alexandre Torgue <alexandre.torgue@foss.st.com> for STMicroelectronics.
+ */
+
+/dts-v1/;
+
+#include "stm32mp157c-ed1.dts"
+#include "stm32mp15-scmi.dtsi"
+
+/ {
+	model = "STMicroelectronics STM32MP157C-ED1 SCMI eval daughter";
+	compatible = "st,stm32mp157c-ed1-scmi", "st,stm32mp157c-ed1", "st,stm32mp157";
+
+	reserved-memory {
+		optee@fe000000 {
+			reg = <0xfe000000 0x2000000>;
+			no-map;
+		};
+	};
+};
+
+&cpu0 {
+	clocks = <&scmi_clk CK_SCMI_MPU>;
+};
+
+&cpu1 {
+	clocks = <&scmi_clk CK_SCMI_MPU>;
+};
+
+&cryp1 {
+	clocks = <&scmi_clk CK_SCMI_CRYP1>;
+	resets = <&scmi_reset RST_SCMI_CRYP1>;
+};
+
+&dsi {
+	clocks = <&rcc DSI_K>, <&scmi_clk CK_SCMI_HSE>, <&rcc DSI_PX>;
+};
+
+&gpioz {
+	clocks = <&scmi_clk CK_SCMI_GPIOZ>;
+};
+
+&hash1 {
+	clocks = <&scmi_clk CK_SCMI_HASH1>;
+	resets = <&scmi_reset RST_SCMI_HASH1>;
+};
+
+&i2c4 {
+	clocks = <&scmi_clk CK_SCMI_I2C4>;
+	resets = <&scmi_reset RST_SCMI_I2C4>;
+};
+
+&iwdg2 {
+	clocks = <&rcc IWDG2>, <&scmi_clk CK_SCMI_LSI>;
+};
+
+&mdma1 {
+	resets = <&scmi_reset RST_SCMI_MDMA>;
+};
+
+&mlahb {
+	resets = <&scmi_reset RST_SCMI_MCU>;
+};
+
+&rcc {
+	compatible = "st,stm32mp1-rcc-secure", "syscon";
+	clock-names = "hse", "hsi", "csi", "lse", "lsi";
+	clocks = <&scmi_clk CK_SCMI_HSE>,
+		 <&scmi_clk CK_SCMI_HSI>,
+		 <&scmi_clk CK_SCMI_CSI>,
+		 <&scmi_clk CK_SCMI_LSE>,
+		 <&scmi_clk CK_SCMI_LSI>;
+};
+
+&rng1 {
+	clocks = <&scmi_clk CK_SCMI_RNG1>;
+	resets = <&scmi_reset RST_SCMI_RNG1>;
+};
+
+&rtc {
+	clocks = <&scmi_clk CK_SCMI_RTCAPB>, <&scmi_clk CK_SCMI_RTC>;
+};
diff --git a/arch/arm/dts/stm32mp157c-ev1-scmi-u-boot.dtsi b/arch/arm/dts/stm32mp157c-ev1-scmi-u-boot.dtsi
new file mode 100644
index 0000000..71a94f9
--- /dev/null
+++ b/arch/arm/dts/stm32mp157c-ev1-scmi-u-boot.dtsi
@@ -0,0 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0-or-later OR BSD-3-Clause
+/*
+ * Copyright : STMicroelectronics 2022
+ */
+
+#include "stm32mp157c-ed1-scmi-u-boot.dtsi"
+
+/ {
+	aliases {
+		gpio26 = &stmfx_pinctrl;
+		i2c1 = &i2c2;
+		i2c4 = &i2c5;
+		pinctrl2 = &stmfx_pinctrl;
+		spi0 = &qspi;
+		usb0 = &usbotg_hs;
+	};
+};
diff --git a/arch/arm/dts/stm32mp157c-ev1-scmi.dts b/arch/arm/dts/stm32mp157c-ev1-scmi.dts
new file mode 100644
index 0000000..3b9dd6f
--- /dev/null
+++ b/arch/arm/dts/stm32mp157c-ev1-scmi.dts
@@ -0,0 +1,90 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
+/*
+ * Copyright (C) STMicroelectronics 2022 - All Rights Reserved
+ * Author: Alexandre Torgue <alexandre.torgue@foss.st.com> for STMicroelectronics.
+ */
+
+/dts-v1/;
+
+#include "stm32mp157c-ev1.dts"
+#include "stm32mp15-scmi.dtsi"
+
+/ {
+	model = "STMicroelectronics STM32MP157C-EV1 SCMI eval daughter on eval mother";
+	compatible = "st,stm32mp157c-ev1-scmi", "st,stm32mp157c-ev1", "st,stm32mp157c-ed1",
+		     "st,stm32mp157";
+
+	reserved-memory {
+		optee@fe000000 {
+			reg = <0xfe000000 0x2000000>;
+			no-map;
+		};
+	};
+};
+
+&cpu0 {
+	clocks = <&scmi_clk CK_SCMI_MPU>;
+};
+
+&cpu1 {
+	clocks = <&scmi_clk CK_SCMI_MPU>;
+};
+
+&cryp1 {
+	clocks = <&scmi_clk CK_SCMI_CRYP1>;
+	resets = <&scmi_reset RST_SCMI_CRYP1>;
+};
+
+&dsi {
+	phy-dsi-supply = <&scmi_reg18>;
+	clocks = <&rcc DSI_K>, <&scmi_clk CK_SCMI_HSE>, <&rcc DSI_PX>;
+};
+
+&gpioz {
+	clocks = <&scmi_clk CK_SCMI_GPIOZ>;
+};
+
+&hash1 {
+	clocks = <&scmi_clk CK_SCMI_HASH1>;
+	resets = <&scmi_reset RST_SCMI_HASH1>;
+};
+
+&i2c4 {
+	clocks = <&scmi_clk CK_SCMI_I2C4>;
+	resets = <&scmi_reset RST_SCMI_I2C4>;
+};
+
+&iwdg2 {
+	clocks = <&rcc IWDG2>, <&scmi_clk CK_SCMI_LSI>;
+};
+
+&m_can1 {
+	clocks = <&scmi_clk CK_SCMI_HSE>, <&rcc FDCAN_K>;
+};
+
+&mdma1 {
+	resets = <&scmi_reset RST_SCMI_MDMA>;
+};
+
+&mlahb {
+	resets = <&scmi_reset RST_SCMI_MCU>;
+};
+
+&rcc {
+	compatible = "st,stm32mp1-rcc-secure", "syscon";
+	clock-names = "hse", "hsi", "csi", "lse", "lsi";
+	clocks = <&scmi_clk CK_SCMI_HSE>,
+		 <&scmi_clk CK_SCMI_HSI>,
+		 <&scmi_clk CK_SCMI_CSI>,
+		 <&scmi_clk CK_SCMI_LSE>,
+		 <&scmi_clk CK_SCMI_LSI>;
+};
+
+&rng1 {
+	clocks = <&scmi_clk CK_SCMI_RNG1>;
+	resets = <&scmi_reset RST_SCMI_RNG1>;
+};
+
+&rtc {
+	clocks = <&scmi_clk CK_SCMI_RTCAPB>, <&scmi_clk CK_SCMI_RTC>;
+};
diff --git a/arch/arm/dts/stm32mp157c-ev1.dts b/arch/arm/dts/stm32mp157c-ev1.dts
index e222d2d..d142dd3 100644
--- a/arch/arm/dts/stm32mp157c-ev1.dts
+++ b/arch/arm/dts/stm32mp157c-ev1.dts
@@ -262,7 +262,7 @@
 	#size-cells = <0>;
 	status = "okay";
 
-	flash0: mx66l51235l@0 {
+	flash0: flash@0 {
 		compatible = "jedec,spi-nor";
 		reg = <0>;
 		spi-rx-bus-width = <4>;
@@ -271,7 +271,7 @@
 		#size-cells = <1>;
 	};
 
-	flash1: mx66l51235l@1 {
+	flash1: flash@1 {
 		compatible = "jedec,spi-nor";
 		reg = <1>;
 		spi-rx-bus-width = <4>;
diff --git a/arch/arm/dts/stm32mp15xx-dhcom-u-boot.dtsi b/arch/arm/dts/stm32mp15xx-dhcom-u-boot.dtsi
index d73967a..ee747a5 100644
--- a/arch/arm/dts/stm32mp15xx-dhcom-u-boot.dtsi
+++ b/arch/arm/dts/stm32mp15xx-dhcom-u-boot.dtsi
@@ -27,8 +27,6 @@
 	config {
 		u-boot,boot-led = "heartbeat";
 		u-boot,error-led = "error";
-		st,fastboot-gpios = <&gpioa 13 GPIO_ACTIVE_LOW>;
-		st,stm32prog-gpios = <&gpioa 14 GPIO_ACTIVE_LOW>;
 		dh,som-coding-gpios = <&gpiof 12 0>, <&gpiof 13 0>, <&gpiof 15 0>;
 		dh,ddr3-coding-gpios = <&gpioz 6 0>, <&gpioz 7 0>;
 	};
diff --git a/arch/arm/mach-stm32mp/include/mach/sys_proto.h b/arch/arm/mach-stm32mp/include/mach/sys_proto.h
index 4b564e8..f19a70e 100644
--- a/arch/arm/mach-stm32mp/include/mach/sys_proto.h
+++ b/arch/arm/mach-stm32mp/include/mach/sys_proto.h
@@ -41,6 +41,7 @@
 
 #define CPU_REV1	0x1000
 #define CPU_REV1_1	0x1001
+#define CPU_REV1_2	0x1003
 #define CPU_REV2	0x2000
 #define CPU_REV2_1	0x2001
 
diff --git a/arch/arm/mach-stm32mp/stm32mp13x.c b/arch/arm/mach-stm32mp/stm32mp13x.c
index bd3f24c..845d973 100644
--- a/arch/arm/mach-stm32mp/stm32mp13x.c
+++ b/arch/arm/mach-stm32mp/stm32mp13x.c
@@ -126,6 +126,9 @@
 	case CPU_REV1_1:
 		cpu_r = "Z";
 		break;
+	case CPU_REV1_2:
+		cpu_r = "Y";
+		break;
 	default:
 		cpu_r = "?";
 		break;
diff --git a/arch/mips/Makefile b/arch/mips/Makefile
index 32c436f..a007914 100644
--- a/arch/mips/Makefile
+++ b/arch/mips/Makefile
@@ -4,6 +4,11 @@
 
 ifeq ($(CONFIG_SPL_BUILD),y)
 head-$(CONFIG_ARCH_JZ47XX) := arch/mips/mach-jz47xx/start.o
+head-$(CONFIG_SOC_MT7621) := arch/mips/mach-mtmips/mt7621/spl/start.o
+endif
+
+ifeq ($(CONFIG_TPL_BUILD),y)
+head-$(CONFIG_SOC_MT7621) := arch/mips/mach-mtmips/mt7621/tpl/start.o
 endif
 
 libs-y += arch/mips/cpu/
diff --git a/arch/mips/cpu/u-boot-spl.lds b/arch/mips/cpu/u-boot-spl.lds
index 194398b..310a5c5 100644
--- a/arch/mips/cpu/u-boot-spl.lds
+++ b/arch/mips/cpu/u-boot-spl.lds
@@ -13,7 +13,9 @@
 
 	. = ALIGN(4);
 	.text : {
+		__text_start = .;
 		*(.text*)
+		__text_end = .;
 	} > .spl_mem
 
 	. = ALIGN(4);
@@ -36,6 +38,7 @@
 
 	. = ALIGN(4);
 	__image_copy_end = .;
+	__image_copy_len = __image_copy_end - __text_start;
 
 	_image_binary_end = .;
 
diff --git a/arch/mips/dts/Makefile b/arch/mips/dts/Makefile
index 95144b2..1b17911 100644
--- a/arch/mips/dts/Makefile
+++ b/arch/mips/dts/Makefile
@@ -16,6 +16,8 @@
 dtb-$(CONFIG_BOARD_HUAWEI_HG556A) += huawei,hg556a.dtb
 dtb-$(CONFIG_BOARD_MT7620_RFB) += mediatek,mt7620-rfb.dtb
 dtb-$(CONFIG_BOARD_MT7620_MT7530_RFB) += mediatek,mt7620-mt7530-rfb.dtb
+dtb-$(CONFIG_BOARD_MT7621_RFB) += mediatek,mt7621-rfb.dtb
+dtb-$(CONFIG_BOARD_MT7621_NAND_RFB) += mediatek,mt7621-nand-rfb.dtb
 dtb-$(CONFIG_BOARD_MT7628_RFB) += mediatek,mt7628-rfb.dtb
 dtb-$(CONFIG_BOARD_GARDENA_SMART_GATEWAY_MT7688) += gardena-smart-gateway-mt7688.dtb
 dtb-$(CONFIG_BOARD_LINKIT_SMART_7688) += linkit-smart-7688.dtb
diff --git a/arch/mips/dts/mediatek,mt7621-nand-rfb.dts b/arch/mips/dts/mediatek,mt7621-nand-rfb.dts
new file mode 100644
index 0000000..67ba298
--- /dev/null
+++ b/arch/mips/dts/mediatek,mt7621-nand-rfb.dts
@@ -0,0 +1,67 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2022 MediaTek Inc. All rights reserved.
+ *
+ * Author: Weijie Gao <weijie.gao@mediatek.com>
+ */
+
+/dts-v1/;
+
+#include "mt7621.dtsi"
+
+/ {
+	compatible = "mediatek,mt7621-nand-rfb", "mediatek,mt7621-soc";
+	model = "MediaTek MT7621 RFB (NAND)";
+
+	aliases {
+		serial0 = &uart0;
+	};
+
+	chosen {
+		stdout-path = &uart0;
+	};
+};
+
+&pinctrl {
+	state_default: pin_state {
+		nand {
+			groups = "spi", "sdxc";
+			function = "nand";
+		};
+
+		gpios {
+			groups = "i2c", "uart3", "pcie reset";
+			function = "gpio";
+		};
+
+		wdt {
+			groups = "wdt";
+			function = "wdt rst";
+		};
+
+		jtag {
+			groups = "jtag";
+			function = "jtag";
+		};
+	};
+};
+
+&uart0 {
+	status = "okay";
+};
+
+&gpio {
+	status = "okay";
+};
+
+&eth {
+	status = "okay";
+};
+
+&ssusb {
+	status = "okay";
+};
+
+&u3phy {
+	status = "okay";
+};
diff --git a/arch/mips/dts/mediatek,mt7621-rfb.dts b/arch/mips/dts/mediatek,mt7621-rfb.dts
new file mode 100644
index 0000000..ff7eaf0
--- /dev/null
+++ b/arch/mips/dts/mediatek,mt7621-rfb.dts
@@ -0,0 +1,82 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2022 MediaTek Inc. All rights reserved.
+ *
+ * Author: Weijie Gao <weijie.gao@mediatek.com>
+ */
+
+/dts-v1/;
+
+#include "mt7621.dtsi"
+
+/ {
+	compatible = "mediatek,mt7621-rfb", "mediatek,mt7621-soc";
+	model = "MediaTek MT7621 RFB (SPI-NOR)";
+
+	aliases {
+		serial0 = &uart0;
+		spi0 = &spi;
+	};
+
+	chosen {
+		stdout-path = &uart0;
+	};
+};
+
+&pinctrl {
+	state_default: pin_state {
+		gpios {
+			groups = "i2c", "uart3", "pcie reset";
+			function = "gpio";
+		};
+
+		wdt {
+			groups = "wdt";
+			function = "wdt rst";
+		};
+
+		jtag {
+			groups = "jtag";
+			function = "jtag";
+		};
+	};
+};
+
+&uart0 {
+	status = "okay";
+};
+
+&gpio {
+	status = "okay";
+};
+
+&spi {
+	status = "okay";
+	num-cs = <2>;
+
+	spi-flash@0 {
+		#address-cells = <1>;
+		#size-cells = <1>;
+		compatible = "jedec,spi-nor";
+		spi-max-frequency = <25000000>;
+		reg = <0>;
+	};
+};
+
+&eth {
+	status = "okay";
+};
+
+&mmc {
+	cap-sd-highspeed;
+
+	status = "okay";
+};
+
+&ssusb {
+	status = "okay";
+};
+
+&u3phy {
+	status = "okay";
+};
diff --git a/arch/mips/dts/mt7621-u-boot.dtsi b/arch/mips/dts/mt7621-u-boot.dtsi
new file mode 100644
index 0000000..c5a8aa3
--- /dev/null
+++ b/arch/mips/dts/mt7621-u-boot.dtsi
@@ -0,0 +1,111 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2022 MediaTek Inc. All rights reserved.
+ *
+ * Author: Weijie Gao <weijie.gao@mediatek.com>
+ */
+
+#include <linux/stringify.h>
+
+/ {
+	binman: binman {
+		multiple-images;
+	};
+};
+
+&sysc {
+	u-boot,dm-pre-reloc;
+};
+
+&reboot {
+	u-boot,dm-pre-reloc;
+};
+
+&clkctrl {
+	u-boot,dm-pre-reloc;
+};
+
+&rstctrl {
+	u-boot,dm-pre-reloc;
+};
+
+&pinctrl {
+	u-boot,dm-pre-reloc;
+};
+
+&uart0 {
+	u-boot,dm-pre-reloc;
+};
+
+&uart1 {
+	u-boot,dm-pre-reloc;
+};
+
+&uart2 {
+	u-boot,dm-pre-reloc;
+};
+
+&binman {
+	u-boot-spl-ddr {
+		align = <4>;
+		align-size = <4>;
+		filename = "u-boot-spl-ddr.bin";
+		pad-byte = <0xff>;
+
+		u-boot-spl {
+			align-end = <4>;
+			filename = "u-boot-spl.bin";
+		};
+
+		stage_bin {
+			filename = "mt7621_stage_sram.bin";
+			type = "blob-ext";
+		};
+	};
+
+	spl-img {
+		filename = "u-boot-spl-ddr.img";
+
+		mkimage {
+#ifdef CONFIG_MT7621_BOOT_FROM_NAND
+			args = "-T", "mtk_image", "-n", "mt7621=1",
+			       "-a", __stringify(CONFIG_SPL_TEXT_BASE),
+			       "-e", __stringify(CONFIG_SPL_TEXT_BASE);
+#else
+			args = "-A", "mips", "-T", "standalone", "-O", "u-boot",
+			       "-C", "none", "-n", "MT7621 U-Boot SPL",
+			       "-a", __stringify(CONFIG_SPL_TEXT_BASE),
+			       "-e", __stringify(CONFIG_SPL_TEXT_BASE);
+#endif
+
+			blob {
+				filename = "u-boot-spl-ddr.bin";
+			};
+		};
+	};
+
+	mt7621-uboot {
+		filename = "u-boot-mt7621.bin";
+		pad-byte = <0xff>;
+
+#ifndef CONFIG_MT7621_BOOT_FROM_NAND
+		u-boot-tpl {
+			align-end = <4>;
+			filename = "u-boot-tpl.bin";
+		};
+#endif
+
+		spl {
+#ifdef CONFIG_MT7621_BOOT_FROM_NAND
+			align-end = <0x1000>;
+#endif
+			filename = "u-boot-spl-ddr.img";
+			type = "blob";
+		};
+
+		u-boot {
+			filename = "u-boot-lzma.img";
+			type = "blob";
+		};
+	};
+};
diff --git a/arch/mips/dts/mt7621.dtsi b/arch/mips/dts/mt7621.dtsi
new file mode 100644
index 0000000..c32b609
--- /dev/null
+++ b/arch/mips/dts/mt7621.dtsi
@@ -0,0 +1,349 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2022 MediaTek Inc. All rights reserved.
+ *
+ * Author: Weijie Gao <weijie.gao@mediatek.com>
+ */
+
+#include <dt-bindings/clock/mt7621-clk.h>
+#include <dt-bindings/reset/mt7621-reset.h>
+#include <dt-bindings/phy/phy.h>
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+	compatible = "mediatek,mt7621-soc";
+
+	cpus {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		cpu@0 {
+			device_type = "cpu";
+			compatible = "mips,mips1004Kc";
+			reg = <0>;
+		};
+
+		cpu@1 {
+			device_type = "cpu";
+			compatible = "mips,mips1004Kc";
+			reg = <1>;
+		};
+	};
+
+	clk48m: clk48m {
+		compatible = "fixed-clock";
+
+		clock-frequency = <48000000>;
+
+		#clock-cells = <0>;
+	};
+
+	clk50m: clk50m {
+		compatible = "fixed-clock";
+
+		clock-frequency = <50000000>;
+
+		#clock-cells = <0>;
+	};
+
+	sysc: sysctrl@1e000000 {
+		compatible = "mediatek,mt7621-sysc", "syscon";
+		reg = <0x1e000000 0x100>;
+
+		clkctrl: clock-controller@1e000030 {
+			compatible = "mediatek,mt7621-clk";
+			mediatek,memc = <&memc>;
+
+			#clock-cells = <1>;
+		};
+	};
+
+	rstctrl: reset-controller@1e000034 {
+		compatible = "mediatek,mtmips-reset";
+		reg = <0x1e000034 0x4>;
+		#reset-cells = <1>;
+	};
+
+	reboot: resetctl-reboot {
+		compatible = "resetctl-reboot";
+
+		resets = <&rstctrl RST_SYS>;
+		reset-names = "sysreset";
+	};
+
+	memc: memctrl@1e005000 {
+		compatible = "mediatek,mt7621-memc", "syscon";
+		reg = <0x1e005000 0x1000>;
+	};
+
+	pinctrl: pinctrl@1e000060 {
+		compatible = "mediatek,mt7621-pinctrl";
+		reg = <0x1e000048 0x30>;
+
+		pinctrl-names = "default";
+		pinctrl-0 = <&state_default>;
+
+		state_default: pin_state {
+		};
+
+		uart1_pins: uart1_pins {
+			groups = "uart1";
+			function = "uart";
+		};
+
+		uart2_pins: uart2_pins {
+			groups = "uart2";
+			function = "uart";
+		};
+
+		uart3_pins: uart3_pins {
+			groups = "uart3";
+			function = "uart";
+		};
+
+		sdxc_pins: sdxc_pins {
+			groups = "sdxc";
+			function = "sdxc";
+		};
+
+		spi_pins: spi_pins {
+			groups = "spi";
+			function = "spi";
+		};
+
+		eth_pins: eth_pins {
+			mdio_pins {
+				groups = "mdio";
+				function = "mdio";
+			};
+
+			rgmii1_pins {
+				groups = "rgmii1";
+				function = "rgmii";
+			};
+
+			esw_pins {
+				groups = "esw int";
+				function = "esw int";
+			};
+
+			mdio_pconf {
+				groups = "mdio";
+				drive-strength = <2>;
+			};
+		};
+	};
+
+	watchdog: watchdog@1e000100 {
+		compatible = "mediatek,mt7621-wdt";
+		reg = <0x1e000100 0x40>;
+
+		resets = <&rstctrl RST_TIMER>;
+		reset-names = "wdt";
+
+		status = "disabled";
+	};
+
+	gpio: gpio@1e000600 {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		compatible = "mtk,mt7621-gpio";
+		reg = <0x1e000600 0x100>;
+
+		resets = <&rstctrl RST_PIO>;
+		reset-names = "pio";
+
+		gpio0: bank@0 {
+			reg = <0>;
+			compatible = "mtk,mt7621-gpio-bank";
+			gpio-controller;
+			#gpio-cells = <2>;
+		};
+
+		gpio1: bank@1 {
+			reg = <1>;
+			compatible = "mtk,mt7621-gpio-bank";
+			gpio-controller;
+			#gpio-cells = <2>;
+		};
+
+		gpio2: bank@2 {
+			reg = <2>;
+			compatible = "mtk,mt7621-gpio-bank";
+			gpio-controller;
+			#gpio-cells = <2>;
+		};
+	};
+
+	spi: spi@1e000b00 {
+		compatible = "ralink,mt7621-spi";
+		reg = <0x1e000b00 0x40>;
+
+		status = "disabled";
+
+		pinctrl-names = "default";
+		pinctrl-0 = <&spi_pins>;
+
+		resets = <&rstctrl RST_SPI>;
+		reset-names = "spi";
+
+		clocks = <&clkctrl MT7621_CLK_SPI>;
+
+		#address-cells = <1>;
+		#size-cells = <0>;
+	};
+
+	uart0: uart1@1e000c00 {
+		compatible = "mediatek,hsuart", "ns16550a";
+		reg = <0x1e000c00 0x100>;
+
+		pinctrl-names = "default";
+		pinctrl-0 = <&uart1_pins>;
+
+		clocks = <&clkctrl MT7621_CLK_UART1>;
+
+		resets = <&rstctrl RST_UART1>;
+
+		reg-shift = <2>;
+	};
+
+	uart1: uart2@1e000d00 {
+		compatible = "mediatek,hsuart", "ns16550a";
+		reg = <0x1e000d00 0x100>;
+
+		pinctrl-names = "default";
+		pinctrl-0 = <&uart2_pins>;
+
+		clocks = <&clkctrl MT7621_CLK_UART2>;
+
+		resets = <&rstctrl RST_UART2>;
+
+		reg-shift = <2>;
+
+		status = "disabled";
+	};
+
+	uart2: uart3@1e000e00 {
+		compatible = "mediatek,hsuart", "ns16550a";
+		reg = <0x1e000e00 0x100>;
+
+		pinctrl-names = "default";
+		pinctrl-0 = <&uart3_pins>;
+
+		clocks = <&clkctrl MT7621_CLK_UART3>;
+
+		resets = <&rstctrl RST_UART3>;
+
+		reg-shift = <2>;
+
+		status = "disabled";
+	};
+
+	eth: eth@1e100000 {
+		compatible = "mediatek,mt7621-eth";
+		reg = <0x1e100000 0x20000>;
+		mediatek,ethsys = <&sysc>;
+
+		pinctrl-names = "default";
+		pinctrl-0 = <&eth_pins>;
+
+		resets = <&rstctrl RST_FE>, <&rstctrl RST_GMAC>, <&rstctrl RST_MCM>;
+		reset-names = "fe", "gmac", "mcm";
+
+		clocks = <&clkctrl MT7621_CLK_GDMA>,
+			 <&clkctrl MT7621_CLK_ETH>;
+		clock-names = "gmac", "fe";
+
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		mediatek,gmac-id = <0>;
+		phy-mode = "rgmii";
+		mediatek,switch = "mt7530";
+		mediatek,mcm;
+
+		fixed-link {
+			speed = <1000>;
+			full-duplex;
+		};
+	};
+
+	mmc: mmc@1e130000 {
+		compatible = "mediatek,mt7621-mmc";
+		reg = <0x1e130000 0x4000>;
+
+		status = "disabled";
+
+		bus-width = <4>;
+		builtin-cd = <1>;
+		r_smpl = <1>;
+
+		pinctrl-names = "default";
+		pinctrl-0 = <&sdxc_pins>;
+
+		clocks = <&clk50m>, <&clkctrl MT7621_CLK_SHXC>;
+		clock-names = "source", "hclk";
+
+		resets = <&rstctrl RST_SDXC>;
+	};
+
+	ssusb: usb@1e1c0000 {
+		compatible = "mediatek,mt7621-xhci", "mediatek,mtk-xhci";
+		reg = <0x1e1c0000 0x1000>, <0x1e1d0700 0x100>;
+		reg-names = "mac", "ippc";
+
+		clocks = <&clk48m>, <&clk48m>;
+		clock-names = "sys_ck", "ref_ck";
+
+		phys = <&u2port0 PHY_TYPE_USB2>,
+		       <&u3port0 PHY_TYPE_USB3>,
+		       <&u2port1 PHY_TYPE_USB2>;
+
+		status = "disabled";
+	};
+
+	u3phy: usb-phy@1e1d0000 {
+		compatible = "mediatek,mt7621-u3phy",
+			     "mediatek,generic-tphy-v1";
+		reg = <0x1e1d0000 0x700>;
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges;
+		status = "disabled";
+
+		u2port0: usb-phy@1e1d0800 {
+			reg = <0x1e1d0800 0x0100>;
+			#phy-cells = <1>;
+			clocks = <&clk48m>;
+			clock-names = "ref";
+		};
+
+		u3port0: usb-phy@1e1d0900 {
+			reg = <0x1e1d0900 0x0100>;
+			#phy-cells = <1>;
+		};
+
+		u2port1: usb-phy@1e1d1000 {
+			reg = <0x1e1d1000 0x0100>;
+			#phy-cells = <1>;
+			clocks = <&clk48m>;
+			clock-names = "ref";
+		};
+	};
+
+	i2c: i2c@1e000900 {
+		compatible = "i2c-gpio";
+
+		status = "disabled";
+
+		i2c-gpio,delay-us = <3>;
+
+		gpios = <&gpio0 3 1>, /* PIN3 as SDA */
+			<&gpio0 4 1>; /* PIN4 as CLK */
+
+		#address-cells = <1>;
+		#size-cells = <0>;
+	};
+};
diff --git a/arch/mips/include/asm/cm.h b/arch/mips/include/asm/cm.h
index 99ddbcc..5cc8c09 100644
--- a/arch/mips/include/asm/cm.h
+++ b/arch/mips/include/asm/cm.h
@@ -8,9 +8,23 @@
 #define __MIPS_ASM_CM_H__
 
 /* Global Control Register (GCR) offsets */
+#define GCR_CONFIG			0x0000
 #define GCR_BASE			0x0008
 #define GCR_BASE_UPPER			0x000c
+#define GCR_CONTROL			0x0010
+#define GCR_ACCESS			0x0020
 #define GCR_REV				0x0030
+#define GCR_GIC_BASE			0x0080
+#define GCR_CPC_BASE			0x0088
+#define GCR_REG0_BASE			0x0090
+#define GCR_REG0_MASK			0x0098
+#define GCR_REG1_BASE			0x00a0
+#define GCR_REG1_MASK			0x00a8
+#define GCR_REG2_BASE			0x00b0
+#define GCR_REG2_MASK			0x00b8
+#define GCR_REG3_BASE			0x00c0
+#define GCR_REG3_MASK			0x00c8
+#define GCR_CPC_STATUS			0x00f0
 #define GCR_L2_CONFIG			0x0130
 #define GCR_L2_TAG_ADDR			0x0600
 #define GCR_L2_TAG_ADDR_UPPER		0x0604
@@ -19,10 +33,59 @@
 #define GCR_L2_DATA			0x0610
 #define GCR_L2_DATA_UPPER		0x0614
 #define GCR_Cx_COHERENCE		0x2008
+#define GCR_Cx_OTHER			0x2018
+#define GCR_Cx_ID			0x2028
+#define GCR_CO_COHERENCE		0x4008
+
+/* GCR_CONFIG fields */
+#define GCR_CONFIG_NUM_CLUSTERS_SHIFT	23
+#define GCR_CONFIG_NUM_CLUSTERS		(0x7f << 23)
+#define GCR_CONFIG_NUMIOCU_SHIFT	8
+#define GCR_CONFIG_NUMIOCU		(0xff << 8)
+#define GCR_CONFIG_PCORES_SHIFT		0
+#define GCR_CONFIG_PCORES		(0xff << 0)
+
+/* GCR_BASE fields */
+#define GCR_BASE_SHIFT			15
+#define CCA_DEFAULT_OVR_SHIFT		5
+#define CCA_DEFAULT_OVR_MASK		(0x7 << 5)
+#define CCA_DEFAULT_OVREN		(0x1 << 4)
+#define CM_DEFAULT_TARGET_SHIFT		0
+#define CM_DEFAULT_TARGET_MASK		(0x3 << 0)
+
+/* GCR_CONTROL fields */
+#define GCR_CONTROL_SYNCCTL		(0x1 << 16)
 
 /* GCR_REV CM versions */
 #define GCR_REV_CM3			0x0800
 
+/* GCR_GIC_BASE fields */
+#define GCR_GIC_BASE_ADDRMASK_SHIFT	7
+#define GCR_GIC_BASE_ADDRMASK		(0x1ffffff << 7)
+#define GCR_GIC_EN			(0x1 << 0)
+
+/* GCR_CPC_BASE fields */
+#define GCR_CPC_BASE_ADDRMASK_SHIFT	15
+#define GCR_CPC_BASE_ADDRMASK		(0x1ffff << 15)
+#define GCR_CPC_EN			(0x1 << 0)
+
+/* GCR_REGn_MASK fields */
+#define GCR_REGn_MASK_ADDRMASK_SHIFT	16
+#define GCR_REGn_MASK_ADDRMASK		(0xffff << 16)
+#define GCR_REGn_MASK_CCAOVR_SHIFT	5
+#define GCR_REGn_MASK_CCAOVR		(0x7 << 5)
+#define GCR_REGn_MASK_CCAOVREN		(1 << 4)
+#define GCR_REGn_MASK_DROPL2		(1 << 2)
+#define GCR_REGn_MASK_CMTGT_SHIFT	0
+#define GCR_REGn_MASK_CMTGT		(0x3 << 0)
+#define  GCR_REGn_MASK_CMTGT_DISABLED	0x0
+#define  GCR_REGn_MASK_CMTGT_MEM	0x1
+#define  GCR_REGn_MASK_CMTGT_IOCU0	0x2
+#define  GCR_REGn_MASK_CMTGT_IOCU1	0x3
+
+/* GCR_CPC_STATUS fields */
+#define GCR_CPC_EX			(0x1 << 0)
+
 /* GCR_L2_CONFIG fields */
 #define GCR_L2_CONFIG_ASSOC_SHIFT	0
 #define GCR_L2_CONFIG_ASSOC_BITS	8
@@ -36,6 +99,10 @@
 #define GCR_Cx_COHERENCE_DOM_EN		(0xff << 0)
 #define GCR_Cx_COHERENCE_EN		(0x1 << 0)
 
+/* GCR_Cx_OTHER fields */
+#define GCR_Cx_OTHER_CORENUM_SHIFT	16
+#define GCR_Cx_OTHER_CORENUM		(0xffff << 16)
+
 #ifndef __ASSEMBLY__
 
 #include <asm/io.h>
diff --git a/arch/mips/include/asm/mipsmtregs.h b/arch/mips/include/asm/mipsmtregs.h
new file mode 100644
index 0000000..ba82e2b
--- /dev/null
+++ b/arch/mips/include/asm/mipsmtregs.h
@@ -0,0 +1,142 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * MT regs definitions, follows on from mipsregs.h
+ * Copyright (C) 2004 - 2005 MIPS Technologies, Inc.  All rights reserved.
+ * Elizabeth Clarke et. al.
+ *
+ */
+#ifndef _ASM_MIPSMTREGS_H
+#define _ASM_MIPSMTREGS_H
+
+#include <asm/mipsregs.h>
+
+/*
+ * Macros for use in assembly language code
+ */
+
+#define CP0_MVPCONTROL		$0, 1
+#define CP0_MVPCONF0		$0, 2
+#define CP0_MVPCONF1		$0, 3
+#define CP0_VPECONTROL		$1, 1
+#define CP0_VPECONF0		$1, 2
+#define CP0_VPECONF1		$1, 3
+#define CP0_YQMASK		$1, 4
+#define CP0_VPESCHEDULE		$1, 5
+#define CP0_VPESCHEFBK		$1, 6
+#define CP0_TCSTATUS		$2, 1
+#define CP0_TCBIND		$2, 2
+#define CP0_TCRESTART		$2, 3
+#define CP0_TCHALT		$2, 4
+#define CP0_TCCONTEXT		$2, 5
+#define CP0_TCSCHEDULE		$2, 6
+#define CP0_TCSCHEFBK		$2, 7
+#define CP0_SRSCONF0		$6, 1
+#define CP0_SRSCONF1		$6, 2
+#define CP0_SRSCONF2		$6, 3
+#define CP0_SRSCONF3		$6, 4
+#define CP0_SRSCONF4		$6, 5
+
+/* MVPControl fields */
+#define MVPCONTROL_EVP		(_ULCAST_(1))
+
+#define MVPCONTROL_VPC_SHIFT	1
+#define MVPCONTROL_VPC		(_ULCAST_(1) << MVPCONTROL_VPC_SHIFT)
+
+#define MVPCONTROL_STLB_SHIFT	2
+#define MVPCONTROL_STLB		(_ULCAST_(1) << MVPCONTROL_STLB_SHIFT)
+
+/* MVPConf0 fields */
+#define MVPCONF0_PTC_SHIFT	0
+#define MVPCONF0_PTC		(_ULCAST_(0xff))
+#define MVPCONF0_PVPE_SHIFT	10
+#define MVPCONF0_PVPE		(_ULCAST_(0xf) << MVPCONF0_PVPE_SHIFT)
+#define MVPCONF0_TCA_SHIFT	15
+#define MVPCONF0_TCA		(_ULCAST_(1) << MVPCONF0_TCA_SHIFT)
+#define MVPCONF0_PTLBE_SHIFT	16
+#define MVPCONF0_PTLBE		(_ULCAST_(0x3ff) << MVPCONF0_PTLBE_SHIFT)
+#define MVPCONF0_TLBS_SHIFT	29
+#define MVPCONF0_TLBS		(_ULCAST_(1) << MVPCONF0_TLBS_SHIFT)
+#define MVPCONF0_M_SHIFT	31
+#define MVPCONF0_M		(_ULCAST_(0x1) << MVPCONF0_M_SHIFT)
+
+/* config3 fields */
+#define CONFIG3_MT_SHIFT	2
+#define CONFIG3_MT		(_ULCAST_(1) << CONFIG3_MT_SHIFT)
+
+/* VPEControl fields (per VPE) */
+#define VPECONTROL_TARGTC	(_ULCAST_(0xff))
+
+#define VPECONTROL_TE_SHIFT	15
+#define VPECONTROL_TE		(_ULCAST_(1) << VPECONTROL_TE_SHIFT)
+#define VPECONTROL_EXCPT_SHIFT	16
+#define VPECONTROL_EXCPT	(_ULCAST_(0x7) << VPECONTROL_EXCPT_SHIFT)
+
+/* Thread Exception Codes for EXCPT field */
+#define THREX_TU		0
+#define THREX_TO		1
+#define THREX_IYQ		2
+#define THREX_GSX		3
+#define THREX_YSCH		4
+#define THREX_GSSCH		5
+
+#define VPECONTROL_GSI_SHIFT	20
+#define VPECONTROL_GSI		(_ULCAST_(1) << VPECONTROL_GSI_SHIFT)
+#define VPECONTROL_YSI_SHIFT	21
+#define VPECONTROL_YSI		(_ULCAST_(1) << VPECONTROL_YSI_SHIFT)
+
+/* VPEConf0 fields (per VPE) */
+#define VPECONF0_VPA_SHIFT	0
+#define VPECONF0_VPA		(_ULCAST_(1) << VPECONF0_VPA_SHIFT)
+#define VPECONF0_MVP_SHIFT	1
+#define VPECONF0_MVP		(_ULCAST_(1) << VPECONF0_MVP_SHIFT)
+#define VPECONF0_XTC_SHIFT	21
+#define VPECONF0_XTC		(_ULCAST_(0xff) << VPECONF0_XTC_SHIFT)
+
+/* VPEConf1 fields (per VPE) */
+#define VPECONF1_NCP1_SHIFT	0
+#define VPECONF1_NCP1		(_ULCAST_(0xff) << VPECONF1_NCP1_SHIFT)
+#define VPECONF1_NCP2_SHIFT	10
+#define VPECONF1_NCP2		(_ULCAST_(0xff) << VPECONF1_NCP2_SHIFT)
+#define VPECONF1_NCX_SHIFT	20
+#define VPECONF1_NCX		(_ULCAST_(0xff) << VPECONF1_NCX_SHIFT)
+
+/* TCStatus fields (per TC) */
+#define TCSTATUS_TASID		(_ULCAST_(0xff))
+#define TCSTATUS_IXMT_SHIFT	10
+#define TCSTATUS_IXMT		(_ULCAST_(1) << TCSTATUS_IXMT_SHIFT)
+#define TCSTATUS_TKSU_SHIFT	11
+#define TCSTATUS_TKSU		(_ULCAST_(3) << TCSTATUS_TKSU_SHIFT)
+#define TCSTATUS_A_SHIFT	13
+#define TCSTATUS_A		(_ULCAST_(1) << TCSTATUS_A_SHIFT)
+#define TCSTATUS_DA_SHIFT	15
+#define TCSTATUS_DA		(_ULCAST_(1) << TCSTATUS_DA_SHIFT)
+#define TCSTATUS_DT_SHIFT	20
+#define TCSTATUS_DT		(_ULCAST_(1) << TCSTATUS_DT_SHIFT)
+#define TCSTATUS_TDS_SHIFT	21
+#define TCSTATUS_TDS		(_ULCAST_(1) << TCSTATUS_TDS_SHIFT)
+#define TCSTATUS_TSST_SHIFT	22
+#define TCSTATUS_TSST		(_ULCAST_(1) << TCSTATUS_TSST_SHIFT)
+#define TCSTATUS_RNST_SHIFT	23
+#define TCSTATUS_RNST		(_ULCAST_(3) << TCSTATUS_RNST_SHIFT)
+/* Codes for RNST */
+#define TC_RUNNING		0
+#define TC_WAITING		1
+#define TC_YIELDING		2
+#define TC_GATED		3
+
+#define TCSTATUS_TMX_SHIFT	27
+#define TCSTATUS_TMX		(_ULCAST_(1) << TCSTATUS_TMX_SHIFT)
+/* TCStatus TCU bits can use same definitions/offsets as CU bits in Status */
+
+/* TCBind */
+#define TCBIND_CURVPE_SHIFT	0
+#define TCBIND_CURVPE		(_ULCAST_(0xf))
+
+#define TCBIND_CURTC_SHIFT	21
+
+#define TCBIND_CURTC		(_ULCAST_(0xff) << TCBIND_CURTC_SHIFT)
+
+/* TCHalt */
+#define TCHALT_H		(_ULCAST_(1))
+
+#endif
diff --git a/arch/mips/include/asm/system.h b/arch/mips/include/asm/system.h
index 79e6388..89a2ac2 100644
--- a/arch/mips/include/asm/system.h
+++ b/arch/mips/include/asm/system.h
@@ -282,4 +282,24 @@
 	: "=&r"(tmp));
 }
 
+#ifdef CONFIG_SYS_NONCACHED_MEMORY
+/* 1MB granularity */
+#define MMU_SECTION_SHIFT	20
+#define MMU_SECTION_SIZE	(1 << MMU_SECTION_SHIFT)
+
+/**
+ * noncached_init() - Initialize non-cached memory region
+ *
+ * Initialize non-cached memory area. This memory region will be typically
+ * located right below the malloc() area and be accessed from KSEG1.
+ *
+ * It is called during the generic post-relocation init sequence.
+ *
+ * Return: 0 if OK
+ */
+int noncached_init(void);
+
+phys_addr_t noncached_alloc(size_t size, size_t align);
+#endif /* CONFIG_SYS_NONCACHED_MEMORY */
+
 #endif /* _ASM_SYSTEM_H */
diff --git a/arch/mips/lib/cache.c b/arch/mips/lib/cache.c
index ec652f0..d23b38d 100644
--- a/arch/mips/lib/cache.c
+++ b/arch/mips/lib/cache.c
@@ -6,6 +6,7 @@
 
 #include <common.h>
 #include <cpu_func.h>
+#include <malloc.h>
 #include <asm/cache.h>
 #include <asm/cacheops.h>
 #include <asm/cm.h>
@@ -197,3 +198,45 @@
 	/* ensure the pipeline doesn't contain now-invalid instructions */
 	instruction_hazard_barrier();
 }
+
+#ifdef CONFIG_SYS_NONCACHED_MEMORY
+static unsigned long noncached_start;
+static unsigned long noncached_end;
+static unsigned long noncached_next;
+
+void noncached_set_region(void)
+{
+}
+
+int noncached_init(void)
+{
+	phys_addr_t start, end;
+	size_t size;
+
+	/* If this calculation changes, update board_f.c:reserve_noncached() */
+	end = ALIGN(mem_malloc_start, MMU_SECTION_SIZE) - MMU_SECTION_SIZE;
+	size = ALIGN(CONFIG_SYS_NONCACHED_MEMORY, MMU_SECTION_SIZE);
+	start = end - size;
+
+	debug("mapping memory %pa-%pa non-cached\n", &start, &end);
+
+	noncached_start = start;
+	noncached_end = end;
+	noncached_next = start;
+
+	return 0;
+}
+
+phys_addr_t noncached_alloc(size_t size, size_t align)
+{
+	phys_addr_t next = ALIGN(noncached_next, align);
+
+	if (next >= noncached_end || (noncached_end - next) < size)
+		return 0;
+
+	debug("allocated %zu bytes of uncached memory @%pa\n", size, &next);
+	noncached_next = next + size;
+
+	return CKSEG1ADDR(next);
+}
+#endif /* CONFIG_SYS_NONCACHED_MEMORY */
diff --git a/arch/mips/mach-mtmips/Kconfig b/arch/mips/mach-mtmips/Kconfig
index 151b004..d46be50 100644
--- a/arch/mips/mach-mtmips/Kconfig
+++ b/arch/mips/mach-mtmips/Kconfig
@@ -9,6 +9,7 @@
 
 config SYS_SOC
 	default "mt7620" if SOC_MT7620
+	default "mt7621" if SOC_MT7621
 	default "mt7628" if SOC_MT7628
 
 config SYS_DCACHE_SIZE
@@ -18,25 +19,45 @@
 	default 32
 
 config SYS_ICACHE_SIZE
-	default 65536
+	default 65536 if SOC_MT7620 || SOC_MT7628
+	default 32768 if SOC_MT7621
 
 config SYS_ICACHE_LINE_SIZE
 	default 32
 
+config SYS_SCACHE_LINE_SIZE
+	default 32 if SOC_MT7621
+
 config SYS_TEXT_BASE
-	default 0x9c000000 if !SPL
-	default 0x80200000 if SPL
+	default 0x9c000000 if !SPL && !SOC_MT7621
+	default 0x80200000 if SPL || SOC_MT7621
 
 config SPL_TEXT_BASE
-	default 0x9c000000
+	default 0x9c000000 if !SOC_MT7621
+	default 0x80100000 if SOC_MT7621
+
+config SPL_SIZE_LIMIT
+	default 0x30000 if SOC_MT7621
+
+config TPL_TEXT_BASE
+	default 0xbfc00000 if SOC_MT7621
+
+config TPL_MAX_SIZE
+	default 4096 if SOC_MT7621
 
 config SPL_PAYLOAD
 	default "u-boot-lzma.img" if SPL_LZMA
 
 config BUILD_TARGET
-	default "u-boot-with-spl.bin" if SPL
+	default "u-boot-with-spl.bin" if SPL && !SOC_MT7621
+	default "u-boot-lzma.img" if SOC_MT7621
 	default "u-boot.bin"
 
+config MAX_MEM_SIZE
+	int
+	default 256 if SOC_MT7620 || SOC_MT7628
+	default 512 if SOC_MT7621
+
 choice
 	prompt "MediaTek MIPS SoC select"
 
@@ -55,6 +76,23 @@
 	help
 	  This supports MediaTek MT7620.
 
+config SOC_MT7621
+	bool "MT7621"
+	select MIPS_CM
+	select MIPS_L2_CACHE
+	select SYS_CACHE_SHIFT_5
+	select SYS_MIPS_CACHE_INIT_RAM_LOAD
+	select PINCTRL_MT7621
+	select MTK_SERIAL
+	select REGMAP
+	select SYSCON
+	select BINMAN
+	select SUPPORT_TPL
+	select SPL_LOADER_SUPPORT if SPL
+	select SPL_INIT_STACK_WITHOUT_MALLOC_F if SPL
+	help
+	  This supports MediaTek MT7621.
+
 config SOC_MT7628
 	bool "MT7628"
 	select SYS_CACHE_SHIFT_5
@@ -80,6 +118,7 @@
 endchoice
 
 source "arch/mips/mach-mtmips/mt7620/Kconfig"
+source "arch/mips/mach-mtmips/mt7621/Kconfig"
 source "arch/mips/mach-mtmips/mt7628/Kconfig"
 
 endmenu
diff --git a/arch/mips/mach-mtmips/Makefile b/arch/mips/mach-mtmips/Makefile
index 4909b47..19f1e07 100644
--- a/arch/mips/mach-mtmips/Makefile
+++ b/arch/mips/mach-mtmips/Makefile
@@ -1,9 +1,13 @@
 # SPDX-License-Identifier: GPL-2.0+
 
 obj-y += cpu.o
+
+ifneq ($(CONFIG_SOC_MT7621),y)
 obj-y += ddr_init.o
 obj-y += ddr_cal.o
 obj-$(CONFIG_SPL_BUILD) += spl.o
+endif
 
 obj-$(CONFIG_SOC_MT7620) += mt7620/
+obj-$(CONFIG_SOC_MT7621) += mt7621/
 obj-$(CONFIG_SOC_MT7628) += mt7628/
diff --git a/arch/mips/mach-mtmips/cpu.c b/arch/mips/mach-mtmips/cpu.c
index a4b5cff..f1e9022 100644
--- a/arch/mips/mach-mtmips/cpu.c
+++ b/arch/mips/mach-mtmips/cpu.c
@@ -16,7 +16,7 @@
 
 int dram_init(void)
 {
-	gd->ram_size = get_ram_size((void *)KSEG1, SZ_256M);
+	gd->ram_size = get_ram_size((void *)KSEG1, CONFIG_MAX_MEM_SIZE << 20);
 
 	return 0;
 }
diff --git a/arch/mips/mach-mtmips/mt7621/Kconfig b/arch/mips/mach-mtmips/mt7621/Kconfig
new file mode 100644
index 0000000..008a28f
--- /dev/null
+++ b/arch/mips/mach-mtmips/mt7621/Kconfig
@@ -0,0 +1,115 @@
+
+if SOC_MT7621
+
+menu "CPU & DDR configuration"
+
+config MT7621_CPU_FREQ
+	int "CPU Frequency (MHz)"
+	range 400 1200
+	default 880
+
+choice
+	prompt "DRAM Frequency"
+	default MT7621_DRAM_FREQ_1200
+
+config MT7621_DRAM_FREQ_400
+	bool "400MHz"
+
+config MT7621_DRAM_FREQ_800
+	bool "800MHz"
+
+config MT7621_DRAM_FREQ_1066
+	bool "1066MHz"
+
+config MT7621_DRAM_FREQ_1200
+	bool "1200MHz"
+
+endchoice
+
+choice
+	prompt "DDR2 timing parameters"
+	default MT7621_DRAM_DDR2_1024M
+
+config MT7621_DRAM_DDR2_512M
+	bool "64MB"
+
+config MT7621_DRAM_DDR2_1024M
+	bool "128MB"
+
+config MT7621_DRAM_DDR2_512M_W9751G6KB_A02_1066MHZ
+	bool "W9751G6KB_A02 @ 1066MHz (64MB)"
+
+config MT7621_DRAM_DDR2_1024M_W971GG6KB25_800MHZ
+	bool "W971GG6KB25 @ 800MHz (128MB)"
+
+config MT7621_DRAM_DDR2_1024M_W971GG6KB18_1066MHZ
+	bool "W971GG6KB18 @ 1066MHz (128MB)"
+
+endchoice
+
+choice
+	prompt "DDR3 timing parameters"
+	default MT7621_DRAM_DDR3_2048M
+
+config MT7621_DRAM_DDR3_1024M
+	bool "128MB"
+
+config MT7621_DRAM_DDR3_1024M_KGD
+	bool "128MB KGD (MT7621DA)"
+
+config MT7621_DRAM_DDR3_2048M
+	bool "256MB"
+
+config MT7621_DRAM_DDR3_4096M
+	bool "512MB"
+
+endchoice
+
+endmenu
+
+config DEBUG_UART_BOARD_INIT
+	default y
+
+config MT7621_BOOT_FROM_NAND
+	bool "Boot from NAND"
+	help
+	  Select this if u-boot will boot from NAND flash. When booting from
+	  NAND, SPL will be loaded by bootrom directly and no TPL is needed.
+
+choice
+	prompt "Board select"
+
+config BOARD_MT7621_RFB
+	bool "MediaTek MT7621 RFB (SPI-NOR)"
+	help
+	  The reference design of MT7621A (WS3010) booting from SPI-NOR flash.
+	  The board can be configured with DDR2 (64MiB~256MiB) or DDR3
+	  (128MiB~512MiB). The board has 16 MiB SPI-NOR flash, built-in MT7530
+	  GbE switch, 1 UART, 1 USB 2.0 host, 1 USB 3.0 host, 1 SDXC, 3 PCIe
+	  sockets, 1 RGMII to external GbE PHY, 2 audio jacks (in/out),
+	  JTAG pins and expansion GPIO pins.
+
+config BOARD_MT7621_NAND_RFB
+	bool "MediaTek MT7621 RFB (NAND)"
+	help
+	  The reference design of MT7621A (WS3010) booting from NAND flash.
+	  The board can be configured with DDR2 (64MiB~256MiB) or DDR3
+	  (128MiB~512MiB). The board has 128 MiB parallel NAND flash, built-in
+	  MT7530 GbE switch, 1 UART, 1 USB 2.0 host, 1 USB 3.0 host, 3 PCIe
+	  sockets, 1 RGMII to external GbE PHY, 2 audio jacks (in/out),
+	  JTAG pins and expansion GPIO pins.
+
+endchoice
+
+config SYS_CONFIG_NAME
+	string "Board configuration name"
+	default "mt7621" if  BOARD_MT7621_RFB || BOARD_MT7621_NAND_RFB
+
+config SYS_BOARD
+	string "Board name"
+	default "mt7621" if  BOARD_MT7621_RFB || BOARD_MT7621_NAND_RFB
+
+config SYS_VENDOR
+	default "mediatek" if BOARD_MT7621_RFB || BOARD_MT7621_NAND_RFB
+
+endif
diff --git a/arch/mips/mach-mtmips/mt7621/Makefile b/arch/mips/mach-mtmips/mt7621/Makefile
new file mode 100644
index 0000000..bf1b0bb
--- /dev/null
+++ b/arch/mips/mach-mtmips/mt7621/Makefile
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-y += init.o
+obj-y += serial.o
+
+ifeq ($(CONFIG_SPL_BUILD),y)
+ifeq ($(CONFIG_TPL_BUILD),y)
+obj-y += tpl/
+else
+obj-y += spl/
+endif
+
+obj-y += sram_init.o
+endif
diff --git a/arch/mips/mach-mtmips/mt7621/init.c b/arch/mips/mach-mtmips/mt7621/init.c
new file mode 100644
index 0000000..d21848a
--- /dev/null
+++ b/arch/mips/mach-mtmips/mt7621/init.c
@@ -0,0 +1,246 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2022 MediaTek Inc. All rights reserved.
+ *
+ * Author: Weijie Gao <weijie.gao@mediatek.com>
+ */
+
+#include <clk.h>
+#include <dm.h>
+#include <dm/uclass.h>
+#include <dt-bindings/clock/mt7621-clk.h>
+#include <asm/global_data.h>
+#include <linux/io.h>
+#include <linux/bitfield.h>
+#include "mt7621.h"
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static const char *const boot_mode[(CHIP_MODE_M >> CHIP_MODE_S) + 1] = {
+	[1] = "NAND 2K+64",
+	[2] = "SPI-NOR 3-Byte Addr",
+	[3] = "SPI-NOR 4-Byte Addr",
+	[10] = "NAND 2K+128",
+	[11] = "NAND 4K+128",
+	[12] = "NAND 4K+256",
+};
+
+int print_cpuinfo(void)
+{
+	void __iomem *sysc = ioremap_nocache(SYSCTL_BASE, SYSCTL_SIZE);
+	u32 val, ver, eco, pkg, core, dram, chipmode;
+	u32 cpu_clk, ddr_clk, bus_clk, xtal_clk;
+	struct udevice *clkdev;
+	const char *bootdev;
+	struct clk clk;
+	int ret;
+
+	val = readl(sysc + SYSCTL_CHIP_REV_ID_REG);
+	ver = FIELD_GET(VER_ID_M, val);
+	eco = FIELD_GET(ECO_ID_M, val);
+	pkg = FIELD_GET(PKG_ID, val);
+	core = FIELD_GET(CPU_ID, val);
+
+	val = readl(sysc + SYSCTL_SYSCFG0_REG);
+	dram = FIELD_GET(DRAM_TYPE, val);
+	chipmode = FIELD_GET(CHIP_MODE_M, val);
+
+	bootdev = boot_mode[chipmode];
+	if (!bootdev)
+		bootdev = "Unsupported boot mode";
+
+	printf("CPU:   MediaTek MT7621%c ver %u, eco %u\n",
+	       core ? (pkg ? 'A' : 'N') : 'S', ver, eco);
+
+	printf("Boot:  DDR%u, %s\n", dram ? 2 : 3, bootdev);
+
+	ret = uclass_get_device_by_driver(UCLASS_CLK, DM_DRIVER_GET(mt7621_clk),
+					  &clkdev);
+	if (ret)
+		return ret;
+
+	clk.dev = clkdev;
+
+	clk.id = MT7621_CLK_CPU;
+	cpu_clk = clk_get_rate(&clk);
+
+	clk.id = MT7621_CLK_BUS;
+	bus_clk = clk_get_rate(&clk);
+
+	clk.id = MT7621_CLK_DDR;
+	ddr_clk = clk_get_rate(&clk);
+
+	clk.id = MT7621_CLK_XTAL;
+	xtal_clk = clk_get_rate(&clk);
+
+	/* Set final timer frequency */
+	if (cpu_clk)
+		gd->arch.timer_freq = cpu_clk / 2;
+
+	printf("Clock: CPU: %uMHz, DDR: %uMT/s, Bus: %uMHz, XTAL: %uMHz\n",
+	       cpu_clk / 1000000, ddr_clk / 500000, bus_clk / 1000000,
+	       xtal_clk / 1000000);
+
+	return 0;
+}
+
+unsigned long get_xtal_mhz(void)
+{
+	void __iomem *sysc = ioremap_nocache(SYSCTL_BASE, SYSCTL_SIZE);
+	u32 bs, xtal_sel;
+
+	bs = readl(sysc + SYSCTL_SYSCFG0_REG);
+	xtal_sel = FIELD_GET(XTAL_MODE_SEL_M, bs);
+
+	if (xtal_sel <= 2)
+		return 20;
+	else if (xtal_sel <= 5)
+		return 40;
+	else
+		return 25;
+}
+
+static void xhci_config_40mhz(void __iomem *usbh)
+{
+	writel(FIELD_PREP(SSUSB_MAC3_SYS_CK_GATE_MASK_TIME_M, 0x20) |
+	       FIELD_PREP(SSUSB_MAC2_SYS_CK_GATE_MASK_TIME_M, 0x20) |
+	       FIELD_PREP(SSUSB_MAC3_SYS_CK_GATE_MODE_M, 2) |
+	       FIELD_PREP(SSUSB_MAC2_SYS_CK_GATE_MODE_M, 2) | 0x10,
+	       usbh + SSUSB_MAC_CK_CTRL_REG);
+
+	writel(FIELD_PREP(SSUSB_PLL_PREDIV_PE1D_M, 2) |
+	       FIELD_PREP(SSUSB_PLL_PREDIV_U3_M, 1) |
+	       FIELD_PREP(SSUSB_PLL_FBKDI_M, 4),
+	       usbh + DA_SSUSB_U3PHYA_10_REG);
+
+	writel(FIELD_PREP(SSUSB_PLL_FBKDIV_PE2H_M, 0x18) |
+	       FIELD_PREP(SSUSB_PLL_FBKDIV_PE1D_M, 0x18) |
+	       FIELD_PREP(SSUSB_PLL_FBKDIV_PE1H_M, 0x18) |
+	       FIELD_PREP(SSUSB_PLL_FBKDIV_U3_M, 0x1e),
+	       usbh + DA_SSUSB_PLL_FBKDIV_REG);
+
+	writel(FIELD_PREP(SSUSB_PLL_PCW_NCPO_U3_M, 0x1e400000),
+	       usbh + DA_SSUSB_PLL_PCW_NCPO_REG);
+
+	writel(FIELD_PREP(SSUSB_PLL_SSC_DELTA1_PE1H_M, 0x25) |
+	       FIELD_PREP(SSUSB_PLL_SSC_DELTA1_U3_M, 0x73),
+	       usbh + DA_SSUSB_PLL_SSC_DELTA1_REG);
+
+	writel(FIELD_PREP(SSUSB_PLL_SSC_DELTA_U3_M, 0x71) |
+	       FIELD_PREP(SSUSB_PLL_SSC_DELTA1_PE2D_M, 0x4a),
+	       usbh + DA_SSUSB_U3PHYA_21_REG);
+
+	writel(FIELD_PREP(SSUSB_PLL_SSC_PRD_M, 0x140),
+	       usbh + SSUSB_U3PHYA_9_REG);
+
+	writel(FIELD_PREP(SSUSB_SYSPLL_PCW_NCPO_M, 0x11c00000),
+	       usbh + SSUSB_U3PHYA_3_REG);
+
+	writel(FIELD_PREP(SSUSB_PCIE_CLKDRV_AMP_M, 4) |
+	       FIELD_PREP(SSUSB_SYSPLL_FBSEL_M, 1) |
+	       FIELD_PREP(SSUSB_SYSPLL_PREDIV_M, 1),
+	       usbh + SSUSB_U3PHYA_1_REG);
+
+	writel(FIELD_PREP(SSUSB_SYSPLL_FBDIV_M, 0x12) |
+	       SSUSB_SYSPLL_VCO_DIV_SEL | SSUSB_SYSPLL_FPEN |
+	       SSUSB_SYSPLL_MONCK_EN | SSUSB_SYSPLL_VOD_EN,
+	       usbh + SSUSB_U3PHYA_2_REG);
+
+	writel(SSUSB_EQ_CURSEL | FIELD_PREP(SSUSB_RX_DAC_MUX_M, 8) |
+	       FIELD_PREP(SSUSB_PCIE_SIGDET_VTH_M, 1) |
+	       FIELD_PREP(SSUSB_PCIE_SIGDET_LPF_M, 1),
+	       usbh + SSUSB_U3PHYA_11_REG);
+
+	writel(FIELD_PREP(SSUSB_RING_OSC_CNTEND_M, 0x1ff) |
+	       FIELD_PREP(SSUSB_XTAL_OSC_CNTEND_M, 0x7f) |
+	       SSUSB_RING_BYPASS_DET,
+	       usbh + SSUSB_B2_ROSC_0_REG);
+
+	writel(FIELD_PREP(SSUSB_RING_OSC_FRC_RECAL_M, 3) |
+	       SSUSB_RING_OSC_FRC_SEL,
+	       usbh + SSUSB_B2_ROSC_1_REG);
+}
+
+static void xhci_config_25mhz(void __iomem *usbh)
+{
+	writel(FIELD_PREP(SSUSB_MAC3_SYS_CK_GATE_MASK_TIME_M, 0x20) |
+	       FIELD_PREP(SSUSB_MAC2_SYS_CK_GATE_MASK_TIME_M, 0x20) |
+	       FIELD_PREP(SSUSB_MAC3_SYS_CK_GATE_MODE_M, 2) |
+	       FIELD_PREP(SSUSB_MAC2_SYS_CK_GATE_MODE_M, 2) | 0x10,
+	       usbh + SSUSB_MAC_CK_CTRL_REG);
+
+	writel(FIELD_PREP(SSUSB_PLL_PREDIV_PE1D_M, 2) |
+	       FIELD_PREP(SSUSB_PLL_FBKDI_M, 4),
+	       usbh + DA_SSUSB_U3PHYA_10_REG);
+
+	writel(FIELD_PREP(SSUSB_PLL_FBKDIV_PE2H_M, 0x18) |
+	       FIELD_PREP(SSUSB_PLL_FBKDIV_PE1D_M, 0x18) |
+	       FIELD_PREP(SSUSB_PLL_FBKDIV_PE1H_M, 0x18) |
+	       FIELD_PREP(SSUSB_PLL_FBKDIV_U3_M, 0x19),
+	       usbh + DA_SSUSB_PLL_FBKDIV_REG);
+
+	writel(FIELD_PREP(SSUSB_PLL_PCW_NCPO_U3_M, 0x18000000),
+	       usbh + DA_SSUSB_PLL_PCW_NCPO_REG);
+
+	writel(FIELD_PREP(SSUSB_PLL_SSC_DELTA1_PE1H_M, 0x25) |
+	       FIELD_PREP(SSUSB_PLL_SSC_DELTA1_U3_M, 0x4a),
+	       usbh + DA_SSUSB_PLL_SSC_DELTA1_REG);
+
+	writel(FIELD_PREP(SSUSB_PLL_SSC_DELTA_U3_M, 0x48) |
+	       FIELD_PREP(SSUSB_PLL_SSC_DELTA1_PE2D_M, 0x4a),
+	       usbh + DA_SSUSB_U3PHYA_21_REG);
+
+	writel(FIELD_PREP(SSUSB_PLL_SSC_PRD_M, 0x190),
+	       usbh + SSUSB_U3PHYA_9_REG);
+
+	writel(FIELD_PREP(SSUSB_SYSPLL_PCW_NCPO_M, 0xe000000),
+	       usbh + SSUSB_U3PHYA_3_REG);
+
+	writel(FIELD_PREP(SSUSB_PCIE_CLKDRV_AMP_M, 4) |
+	       FIELD_PREP(SSUSB_SYSPLL_FBSEL_M, 1),
+	       usbh + SSUSB_U3PHYA_1_REG);
+
+	writel(FIELD_PREP(SSUSB_SYSPLL_FBDIV_M, 0xf) |
+	       SSUSB_SYSPLL_VCO_DIV_SEL | SSUSB_SYSPLL_FPEN |
+	       SSUSB_SYSPLL_MONCK_EN | SSUSB_SYSPLL_VOD_EN,
+	       usbh + SSUSB_U3PHYA_2_REG);
+
+	writel(SSUSB_EQ_CURSEL | FIELD_PREP(SSUSB_RX_DAC_MUX_M, 8) |
+	       FIELD_PREP(SSUSB_PCIE_SIGDET_VTH_M, 1) |
+	       FIELD_PREP(SSUSB_PCIE_SIGDET_LPF_M, 1),
+	       usbh + SSUSB_U3PHYA_11_REG);
+
+	writel(FIELD_PREP(SSUSB_RING_OSC_CNTEND_M, 0x1ff) |
+	       FIELD_PREP(SSUSB_XTAL_OSC_CNTEND_M, 0x7f) |
+	       SSUSB_RING_BYPASS_DET,
+	       usbh + SSUSB_B2_ROSC_0_REG);
+
+	writel(FIELD_PREP(SSUSB_RING_OSC_FRC_RECAL_M, 3) |
+	       SSUSB_RING_OSC_FRC_SEL,
+	       usbh + SSUSB_B2_ROSC_1_REG);
+}
+
+void lowlevel_init(void)
+{
+	void __iomem *usbh = ioremap_nocache(SSUSB_BASE, SSUSB_SIZE);
+	u32 xtal = get_xtal_mhz();
+
+	/* Setup USB xHCI */
+	if (xtal == 40)
+		xhci_config_40mhz(usbh);
+	else if (xtal == 25)
+		xhci_config_25mhz(usbh);
+}
+
+ulong notrace get_tbclk(void)
+{
+	return gd->arch.timer_freq;
+}
+
+void _machine_restart(void)
+{
+	void __iomem *sysc = ioremap_nocache(SYSCTL_BASE, SYSCTL_SIZE);
+
+	while (1)
+		writel(SYS_RST, sysc + SYSCTL_RSTCTL_REG);
+}
diff --git a/arch/mips/mach-mtmips/mt7621/mt7621.h b/arch/mips/mach-mtmips/mt7621/mt7621.h
new file mode 100644
index 0000000..916cc99
--- /dev/null
+++ b/arch/mips/mach-mtmips/mt7621/mt7621.h
@@ -0,0 +1,229 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2022 MediaTek Inc. All rights reserved.
+ *
+ * Author: Weijie Gao <weijie.gao@mediatek.com>
+ */
+
+#ifndef _MT7621_H_
+#define _MT7621_H_
+
+#define SYSCTL_BASE				0x1e000000
+#define SYSCTL_SIZE				0x100
+#define TIMER_BASE				0x1e000100
+#define TIMER_SIZE				0x100
+#define RBUS_BASE				0x1e000400
+#define RBUS_SIZE				0x100
+#define GPIO_BASE				0x1e000600
+#define GPIO_SIZE				0x100
+#define DMA_CFG_ARB_BASE			0x1e000800
+#define DMA_CFG_ARB_SIZE			0x100
+#define SPI_BASE				0x1e000b00
+#define SPI_SIZE				0x100
+#define UART1_BASE				0x1e000c00
+#define UART1_SIZE				0x100
+#define UART2_BASE				0x1e000d00
+#define UART2_SIZE				0x100
+#define UART3_BASE				0x1e000e00
+#define UART3_SIZE				0x100
+#define NFI_BASE				0x1e003000
+#define NFI_SIZE				0x800
+#define NFI_ECC_BASE				0x1e003800
+#define NFI_ECC_SIZE				0x800
+#define DRAMC_BASE				0x1e005000
+#define DRAMC_SIZE				0x1000
+#define FE_BASE					0x1e100000
+#define FE_SIZE					0xe000
+#define GMAC_BASE				0x1e110000
+#define GMAC_SIZE				0x8000
+#define SSUSB_BASE				0x1e1c0000
+#define SSUSB_SIZE				0x40000
+
+ /* GIC Base Address */
+#define MIPS_GIC_BASE				0x1fbc0000
+
+ /* CPC Base Address */
+#define MIPS_CPC_BASE				0x1fbf0000
+
+ /* Flash Memory-mapped Base Address */
+#define FLASH_MMAP_BASE				0x1fc00000
+#define TPL_INFO_OFFSET				0x40
+#define TPL_INFO_MAGIC				0x31323637 /* Magic "7621" */
+
+/* SRAM */
+#define FE_SRAM_BASE1				0x8000
+#define FE_SRAM_BASE2				0xa000
+
+/* SYSCTL_BASE */
+#define SYSCTL_CHIP_REV_ID_REG			0x0c
+#define CPU_ID					0x20000
+#define PKG_ID					0x10000
+#define VER_ID_S				8
+#define VER_ID_M				0xf00
+#define ECO_ID_S				0
+#define ECO_ID_M				0x0f
+
+#define SYSCTL_SYSCFG0_REG			0x10
+#define XTAL_MODE_SEL_S				6
+#define XTAL_MODE_SEL_M				0x1c0
+#define DRAM_TYPE				0x10
+#define CHIP_MODE_S				0
+#define CHIP_MODE_M				0x0f
+
+#define BOOT_SRAM_BASE_REG			0x20
+
+#define SYSCTL_CLKCFG0_REG			0x2c
+#define CPU_CLK_SEL_S				30
+#define CPU_CLK_SEL_M				0xc0000000
+#define MPLL_CFG_SEL_S				23
+#define MPLL_CFG_SEL_M				0x800000
+
+#define SYSCTL_RSTCTL_REG			0x34
+#define MCM_RST					0x04
+#define SYS_RST					0x01
+
+#define SYSCTL_CUR_CLK_STS_REG			0x44
+#define CUR_CPU_FDIV_S				8
+#define CUR_CPU_FDIV_M				0x1f00
+#define CUR_CPU_FFRAC_S				0
+#define CUR_CPU_FFRAC_M				0x1f
+
+#define SYSCTL_GPIOMODE_REG			0x60
+#define UART2_MODE_S				5
+#define UART2_MODE_M				0x60
+#define UART3_MODE_S				3
+#define UART3_MODE_M				0x18
+#define UART1_MODE				0x02
+
+/* RBUS_BASE */
+#define RBUS_DYN_CFG0_REG			0x0010
+#define CPU_FDIV_S				8
+#define CPU_FDIV_M				0x1f00
+#define CPU_FFRAC_S				0
+#define CPU_FFRAC_M				0x1f
+
+/* DMA_CFG_ARB_BASE */
+#define DMA_ROUTE_REG				0x000c
+
+/* SPI_BASE */
+#define SPI_SPACE_REG				0x003c
+#define FS_SLAVE_SEL_S				12
+#define FS_SLAVE_SEL_M				0x70000
+#define FS_CLK_SEL_S				0
+#define FS_CLK_SEL_M				0xfff
+
+/* FE_BASE */
+#define FE_RST_GLO_REG				0x0004
+#define FE_PSE_RAM				0x04
+#define FE_PSE_MEM_EN				0x02
+#define FE_PSE_RESET				0x01
+
+/* SSUSB_BASE */
+#define SSUSB_MAC_CK_CTRL_REG			0x10784
+#define SSUSB_MAC3_SYS_CK_GATE_MASK_TIME_S	16
+#define SSUSB_MAC3_SYS_CK_GATE_MASK_TIME_M	0xff0000
+#define SSUSB_MAC2_SYS_CK_GATE_MASK_TIME_S	8
+#define SSUSB_MAC2_SYS_CK_GATE_MASK_TIME_M	0xff00
+#define SSUSB_MAC3_SYS_CK_GATE_MODE_S		2
+#define SSUSB_MAC3_SYS_CK_GATE_MODE_M		0x0c
+#define SSUSB_MAC2_SYS_CK_GATE_MODE_S		0
+#define SSUSB_MAC2_SYS_CK_GATE_MODE_M		0x03
+
+#define SSUSB_B2_ROSC_0_REG			0x10a40
+#define SSUSB_RING_OSC_CNTEND_S			23
+#define SSUSB_RING_OSC_CNTEND_M			0xff800000
+#define SSUSB_XTAL_OSC_CNTEND_S			16
+#define SSUSB_XTAL_OSC_CNTEND_M			0x7f0000
+#define SSUSB_RING_BYPASS_DET			0x01
+
+#define SSUSB_B2_ROSC_1_REG			0x10a44
+#define SSUSB_RING_OSC_FRC_RECAL_S		17
+#define SSUSB_RING_OSC_FRC_RECAL_M		0x60000
+#define SSUSB_RING_OSC_FRC_SEL			0x01
+
+#define SSUSB_U3PHYA_1_REG			0x10b04
+#define SSUSB_PCIE_CLKDRV_AMP_S			27
+#define SSUSB_PCIE_CLKDRV_AMP_M			0x38000000
+#define SSUSB_SYSPLL_FBSEL_S			2
+#define SSUSB_SYSPLL_FBSEL_M			0x0c
+#define SSUSB_SYSPLL_PREDIV_S			0
+#define SSUSB_SYSPLL_PREDIV_M			0x03
+
+#define SSUSB_U3PHYA_2_REG			0x10b08
+#define SSUSB_SYSPLL_FBDIV_S			24
+#define SSUSB_SYSPLL_FBDIV_M			0x7f000000
+#define SSUSB_SYSPLL_VCO_DIV_SEL		0x200000
+#define SSUSB_SYSPLL_FPEN			0x2000
+#define SSUSB_SYSPLL_MONCK_EN			0x1000
+#define SSUSB_SYSPLL_VOD_EN			0x200
+
+#define SSUSB_U3PHYA_3_REG			0x10b10
+#define SSUSB_SYSPLL_PCW_NCPO_S			1
+#define SSUSB_SYSPLL_PCW_NCPO_M			0xfffffffe
+
+#define SSUSB_U3PHYA_9_REG			0x10b24
+#define SSUSB_PLL_SSC_PRD_S			0
+#define SSUSB_PLL_SSC_PRD_M			0xffff
+
+#define SSUSB_U3PHYA_11_REG			0x10b2c
+#define SSUSB_EQ_CURSEL				0x1000000
+#define SSUSB_RX_DAC_MUX_S			19
+#define SSUSB_RX_DAC_MUX_M			0xf80000
+#define SSUSB_PCIE_SIGDET_VTH_S			5
+#define SSUSB_PCIE_SIGDET_VTH_M			0x60
+#define SSUSB_PCIE_SIGDET_LPF_S			3
+#define SSUSB_PCIE_SIGDET_LPF_M			0x18
+
+#define DA_SSUSB_PLL_FBKDIV_REG			0x10c1c
+#define SSUSB_PLL_FBKDIV_PE2H_S			24
+#define SSUSB_PLL_FBKDIV_PE2H_M			0x7f000000
+#define SSUSB_PLL_FBKDIV_PE1D_S			16
+#define SSUSB_PLL_FBKDIV_PE1D_M			0x7f0000
+#define SSUSB_PLL_FBKDIV_PE1H_S			8
+#define SSUSB_PLL_FBKDIV_PE1H_M			0x7f00
+#define SSUSB_PLL_FBKDIV_U3_S			0
+#define SSUSB_PLL_FBKDIV_U3_M			0x7f
+
+#define DA_SSUSB_U3PHYA_10_REG			0x10c20
+#define SSUSB_PLL_PREDIV_PE1D_S			18
+#define SSUSB_PLL_PREDIV_PE1D_M			0xc0000
+#define SSUSB_PLL_PREDIV_U3_S			8
+#define SSUSB_PLL_PREDIV_U3_M			0x300
+#define SSUSB_PLL_FBKDI_S			0
+#define SSUSB_PLL_FBKDI_M			0x07
+
+#define DA_SSUSB_PLL_PCW_NCPO_REG		0x10c24
+#define SSUSB_PLL_PCW_NCPO_U3_S			0
+#define SSUSB_PLL_PCW_NCPO_U3_M			0x7fffffff
+
+#define DA_SSUSB_PLL_SSC_DELTA1_REG		0x10c38
+#define SSUSB_PLL_SSC_DELTA1_PE1H_S		16
+#define SSUSB_PLL_SSC_DELTA1_PE1H_M		0xffff0000
+#define SSUSB_PLL_SSC_DELTA1_U3_S		0
+#define SSUSB_PLL_SSC_DELTA1_U3_M		0xffff
+
+#define DA_SSUSB_U3PHYA_21_REG			0x10c40
+#define SSUSB_PLL_SSC_DELTA_U3_S		16
+#define SSUSB_PLL_SSC_DELTA_U3_M		0xffff0000
+#define SSUSB_PLL_SSC_DELTA1_PE2D_S		0
+#define SSUSB_PLL_SSC_DELTA1_PE2D_M		0xffff
+
+/* MT7621 specific CM values */
+
+/* GCR_REGx_BASE */
+#define GCR_REG0_BASE_VALUE			0x1c000000
+#define GCR_REG1_BASE_VALUE			0x60000000
+#define GCR_REG2_BASE_VALUE			0x1c000000
+#define GCR_REG3_BASE_VALUE			0x1c000000
+
+/* GCR_REGx_MASK */
+#define GCR_REG0_MASK_VALUE			0x0000fc00 /* 64M Bus */
+#define GCR_REG1_MASK_VALUE			0x0000f000 /* 256M PCI Mem */
+#define GCR_REG2_MASK_VALUE			0x0000fc00 /* unused */
+#define GCR_REG3_MASK_VALUE			0x0000fc00 /* unused */
+
+#ifndef __ASSEMBLY__
+unsigned long get_xtal_mhz(void);
+#endif
+
+#endif /* _MT7621_H_ */
diff --git a/arch/mips/mach-mtmips/mt7621/serial.c b/arch/mips/mach-mtmips/mt7621/serial.c
new file mode 100644
index 0000000..0ccc71d
--- /dev/null
+++ b/arch/mips/mach-mtmips/mt7621/serial.c
@@ -0,0 +1,23 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2022 MediaTek Inc. All rights reserved.
+ *
+ * Author: Weijie Gao <weijie.gao@mediatek.com>
+ */
+
+#include <asm/io.h>
+#include <asm/addrspace.h>
+#include "mt7621.h"
+
+void board_debug_uart_init(void)
+{
+	void __iomem *base = ioremap_nocache(SYSCTL_BASE, SYSCTL_SIZE);
+
+#if CONFIG_DEBUG_UART_BASE == 0xbe000c00 /* KSEG1ADDR(UART1_BASE) */
+	clrbits_32(base + SYSCTL_GPIOMODE_REG, UART1_MODE);
+#elif CONFIG_DEBUG_UART_BASE == 0xbe000d00 /* KSEG1ADDR(UART2_BASE) */
+	clrbits_32(base + SYSCTL_GPIOMODE_REG, UART2_MODE_M);
+#elif CONFIG_DEBUG_UART_BASE == 0xbe000e00 /* KSEG1ADDR(UART3_BASE) */
+	clrbits_32(base + SYSCTL_GPIOMODE_REG, UART3_MODE_M);
+#endif
+}
diff --git a/arch/mips/mach-mtmips/mt7621/spl/Makefile b/arch/mips/mach-mtmips/mt7621/spl/Makefile
new file mode 100644
index 0000000..ebe54e7
--- /dev/null
+++ b/arch/mips/mach-mtmips/mt7621/spl/Makefile
@@ -0,0 +1,9 @@
+
+extra-y += start.o
+
+obj-y += spl.o
+obj-y += cps.o
+obj-y += dram.o
+obj-y += serial.o
+obj-y += launch.o
+obj-y += launch_ll.o
diff --git a/arch/mips/mach-mtmips/mt7621/spl/cps.c b/arch/mips/mach-mtmips/mt7621/spl/cps.c
new file mode 100644
index 0000000..779e646
--- /dev/null
+++ b/arch/mips/mach-mtmips/mt7621/spl/cps.c
@@ -0,0 +1,153 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2022 MediaTek Inc. All rights reserved.
+ *
+ * Author: Weijie Gao <weijie.gao@mediatek.com>
+ */
+
+#include <asm/io.h>
+#include <asm/addrspace.h>
+#include <asm/mipsregs.h>
+#include <asm/cm.h>
+#include <linux/bitfield.h>
+#include "../mt7621.h"
+
+/* GIC Shared Register Bases */
+#define GIC_SH_POL_BASE		0x100
+#define GIC_SH_TRIG_BASE	0x180
+#define GIC_SH_RMASK_BASE	0x300
+#define GIC_SH_SMASK_BASE	0x380
+#define GIC_SH_MASK_BASE	0x400
+#define GIC_SH_PEND_BASE	0x480
+#define GIC_SH_MAP_PIN_BASE	0x500
+#define GIC_SH_MAP_VPE_BASE	0x2000
+
+/* GIC Registers */
+#define GIC_SH_POL31_0		(GIC_SH_POL_BASE + 0x00)
+#define GIC_SH_POL63_32		(GIC_SH_POL_BASE + 0x04)
+
+#define GIC_SH_TRIG31_0		(GIC_SH_TRIG_BASE + 0x00)
+#define GIC_SH_TRIG63_32	(GIC_SH_TRIG_BASE + 0x04)
+
+#define GIC_SH_RMASK31_0	(GIC_SH_RMASK_BASE + 0x00)
+#define GIC_SH_RMASK63_32	(GIC_SH_RMASK_BASE + 0x04)
+
+#define GIC_SH_SMASK31_0	(GIC_SH_SMASK_BASE + 0x00)
+#define GIC_SH_SMASK63_32	(GIC_SH_SMASK_BASE + 0x04)
+
+#define GIC_SH_MAP_PIN(n)	(GIC_SH_MAP_PIN_BASE + (n) * 4)
+
+#define GIC_SH_MAP_VPE(n, v)	(GIC_SH_MAP_VPE_BASE + (n) * 0x20 + ((v) / 32) * 4)
+#define GIC_SH_MAP_VPE31_0(n)	GIC_SH_MAP_VPE(n, 0)
+
+/* GIC_SH_MAP_PIN fields */
+#define GIC_MAP_TO_PIN		BIT(31)
+#define GIC_MAP_TO_NMI		BIT(30)
+#define GIC_MAP			GENMASK(5, 0)
+#define GIC_MAP_SHIFT		0
+
+static void cm_init(void __iomem *cm_base)
+{
+	u32 gcrcfg, num_cores;
+
+	gcrcfg = readl(cm_base + GCR_CONFIG);
+	num_cores = FIELD_GET(GCR_CONFIG_PCORES, gcrcfg) + 1;
+
+	writel((1 << num_cores) - 1, cm_base + GCR_ACCESS);
+
+	writel(GCR_REG0_BASE_VALUE, cm_base + GCR_REG0_BASE);
+	writel(GCR_REG1_BASE_VALUE, cm_base + GCR_REG1_BASE);
+	writel(GCR_REG2_BASE_VALUE, cm_base + GCR_REG2_BASE);
+	writel(GCR_REG3_BASE_VALUE, cm_base + GCR_REG3_BASE);
+
+	clrsetbits_32(cm_base + GCR_REG0_MASK,
+		      GCR_REGn_MASK_ADDRMASK | GCR_REGn_MASK_CMTGT,
+		      FIELD_PREP(GCR_REGn_MASK_ADDRMASK, GCR_REG0_MASK_VALUE) |
+		      GCR_REGn_MASK_CMTGT_IOCU0);
+
+	clrsetbits_32(cm_base + GCR_REG1_MASK,
+		      GCR_REGn_MASK_ADDRMASK | GCR_REGn_MASK_CMTGT,
+		      FIELD_PREP(GCR_REGn_MASK_ADDRMASK, GCR_REG1_MASK_VALUE) |
+		      GCR_REGn_MASK_CMTGT_IOCU0);
+
+	clrsetbits_32(cm_base + GCR_REG2_MASK,
+		      GCR_REGn_MASK_ADDRMASK | GCR_REGn_MASK_CMTGT,
+		      FIELD_PREP(GCR_REGn_MASK_ADDRMASK, GCR_REG2_MASK_VALUE) |
+		      GCR_REGn_MASK_CMTGT_IOCU0);
+
+	clrsetbits_32(cm_base + GCR_REG3_MASK,
+		      GCR_REGn_MASK_ADDRMASK | GCR_REGn_MASK_CMTGT,
+		      FIELD_PREP(GCR_REGn_MASK_ADDRMASK, GCR_REG3_MASK_VALUE) |
+		      GCR_REGn_MASK_CMTGT_IOCU0);
+
+	clrbits_32(cm_base + GCR_BASE, CM_DEFAULT_TARGET_MASK);
+	setbits_32(cm_base + GCR_CONTROL, GCR_CONTROL_SYNCCTL);
+}
+
+static void gic_init(void)
+{
+	void __iomem *gic_base = (void *)KSEG1ADDR(MIPS_GIC_BASE);
+	int i;
+
+	/* Interrupt 0..5: Level Trigger, Active High */
+	writel(0, gic_base + GIC_SH_TRIG31_0);
+	writel(0x3f, gic_base + GIC_SH_RMASK31_0);
+	writel(0x3f, gic_base + GIC_SH_POL31_0);
+	writel(0x3f, gic_base + GIC_SH_SMASK31_0);
+
+	/* Interrupt 56..63: Edge Trigger, Rising Edge */
+	/* Hardcoded to set up the last 8 external interrupts for IPI. */
+	writel(0xff000000, gic_base + GIC_SH_TRIG63_32);
+	writel(0xff000000, gic_base + GIC_SH_RMASK63_32);
+	writel(0xff000000, gic_base + GIC_SH_POL63_32);
+	writel(0xff000000, gic_base + GIC_SH_SMASK63_32);
+
+	/* Map interrupt source to particular hardware interrupt pin */
+	/* source {0,1,2,3,4,5} -> pin {0,0,4,3,0,5} */
+	writel(GIC_MAP_TO_PIN | 0, gic_base + GIC_SH_MAP_PIN(0));
+	writel(GIC_MAP_TO_PIN | 0, gic_base + GIC_SH_MAP_PIN(1));
+	writel(GIC_MAP_TO_PIN | 4, gic_base + GIC_SH_MAP_PIN(2));
+	writel(GIC_MAP_TO_PIN | 3, gic_base + GIC_SH_MAP_PIN(3));
+	writel(GIC_MAP_TO_PIN | 0, gic_base + GIC_SH_MAP_PIN(4));
+	writel(GIC_MAP_TO_PIN | 5, gic_base + GIC_SH_MAP_PIN(5));
+
+	/* source 56~59 -> pin 1, 60~63 -> pin 2 */
+	writel(GIC_MAP_TO_PIN | 1, gic_base + GIC_SH_MAP_PIN(56));
+	writel(GIC_MAP_TO_PIN | 1, gic_base + GIC_SH_MAP_PIN(57));
+	writel(GIC_MAP_TO_PIN | 1, gic_base + GIC_SH_MAP_PIN(58));
+	writel(GIC_MAP_TO_PIN | 1, gic_base + GIC_SH_MAP_PIN(59));
+	writel(GIC_MAP_TO_PIN | 2, gic_base + GIC_SH_MAP_PIN(60));
+	writel(GIC_MAP_TO_PIN | 2, gic_base + GIC_SH_MAP_PIN(61));
+	writel(GIC_MAP_TO_PIN | 2, gic_base + GIC_SH_MAP_PIN(62));
+	writel(GIC_MAP_TO_PIN | 2, gic_base + GIC_SH_MAP_PIN(63));
+
+	/* Interrupt map to VPE (bit mask) */
+	for (i = 0; i < 32; i++)
+		writel(BIT(0), gic_base + GIC_SH_MAP_VPE31_0(i));
+
+	/*
+	 * Direct GIC_int 56..63 to vpe 0..3
+	 * MIPS Linux convention that last 16 interrupts implemented be set
+	 * aside for IPI signaling.
+	 * The actual interrupts are tied low and software sends interrupts
+	 * via GIC_SH_WEDGE writes.
+	 */
+	for (i = 0; i < 4; i++) {
+		writel(BIT(i), gic_base + GIC_SH_MAP_VPE31_0(i + 56));
+		writel(BIT(i), gic_base + GIC_SH_MAP_VPE31_0(i + 60));
+	}
+}
+
+void mt7621_cps_init(void)
+{
+	void __iomem *cm_base = (void *)KSEG1ADDR(CONFIG_MIPS_CM_BASE);
+
+	/* Enable GIC */
+	writel(MIPS_GIC_BASE | GCR_GIC_EN, cm_base + GCR_GIC_BASE);
+
+	/* Enable CPC */
+	writel(MIPS_CPC_BASE | GCR_CPC_EN, cm_base + GCR_CPC_BASE);
+
+	gic_init();
+	cm_init(cm_base);
+}
diff --git a/arch/mips/mach-mtmips/mt7621/spl/dram.c b/arch/mips/mach-mtmips/mt7621/spl/dram.c
new file mode 100644
index 0000000..100adfb
--- /dev/null
+++ b/arch/mips/mach-mtmips/mt7621/spl/dram.c
@@ -0,0 +1,153 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2022 MediaTek Inc. All rights reserved.
+ *
+ * Author: Weijie Gao <weijie.gao@mediatek.com>
+ */
+
+#include <vsprintf.h>
+#include <asm/io.h>
+#include <asm/sections.h>
+#include <asm/byteorder.h>
+#include <asm/addrspace.h>
+#include <linux/string.h>
+#include "../mt7621.h"
+#include "dram.h"
+
+static const u32 ddr2_act[DDR_PARAM_SIZE] = {
+#if defined(CONFIG_MT7621_DRAM_DDR2_512M)
+	0xAA00AA00, 0xAA00AA00, 0x00000007, 0x22174441,
+	0x00000000, 0xF0748661, 0x40001273, 0x9F0A0481,
+	0x0304692F, 0x15602842, 0x00008888, 0x88888888,
+	0x00000000, 0x00000000, 0x00000000, 0x07100000,
+	0x00001B63, 0x00002000, 0x00004000, 0x00006000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000,
+#elif defined(CONFIG_MT7621_DRAM_DDR2_512M_W9751G6KB_A02_1066MHZ)
+	0xAA00AA00, 0xAA00AA00, 0x00000007, 0x33484584,
+	0x00000000, 0xF07486A1, 0x50001273, 0x9F010481,
+	0x0304693F, 0x15602842, 0x00008888, 0x88888888,
+	0x00000000, 0x00000000, 0x00000010, 0x07100000,
+	0x00001F73, 0x00002000, 0x00004000, 0x00006000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000,
+#elif defined(CONFIG_MT7621_DRAM_DDR2_1024M_W971GG6KB25_800MHZ)
+	0xAA00AA00, 0xAA00AA00, 0x00000007, 0x22174430,
+	0x01000000, 0xF0748661, 0x40001273, 0x9F0F0481,
+	0x0304692F, 0x15602842, 0x00008888, 0x88888888,
+	0x00000000, 0x00000000, 0x00000000, 0x07100000,
+	0x00001B63, 0x00002000, 0x00004000, 0x00006000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000,
+#elif defined(CONFIG_MT7621_DRAM_DDR2_1024M_W971GG6KB18_1066MHZ)
+	0xAA00AA00, 0xAA00AA00, 0x00000007, 0x33484584,
+	0x01000000, 0xF07486A1, 0x50001273, 0x9F070481,
+	0x0304693F, 0x15602842, 0x00008888, 0x88888888,
+	0x00000000, 0x00000000, 0x00000010, 0x07100000,
+	0x00001F73, 0x00002000, 0x00004000, 0x00006000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000,
+#else /* CONFIG_MT7621_DRAM_DDR2_1024M */
+	0xAA00AA00, 0xAA00AA00, 0x00000007, 0x22174441,
+	0x01000000, 0xF0748661, 0x40001273, 0x9F0F0481,
+	0x0304692F, 0x15602842, 0x00008888, 0x88888888,
+	0x00000000, 0x00000000, 0x00000000, 0x07100000,
+	0x00001B63, 0x00002000, 0x00004000, 0x00006000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000,
+#endif
+};
+
+static const u32 ddr3_act[DDR_PARAM_SIZE] = {
+#if defined(CONFIG_MT7621_DRAM_DDR3_1024M)
+	0xAA00AA00, 0xAA00AA00, 0x00000007, 0x44694683,
+	0x01000000, 0xF07486A1, 0xC287221D, 0x9F060481,
+	0x03046948, 0x15602842, 0x00008888, 0x88888888,
+	0x00000000, 0x00000000, 0x00000210, 0x07100000,
+	0x00001B61, 0x00002040, 0x00004010, 0x00006000,
+	0x0C000000, 0x07070000, 0x00000000, 0x00000000,
+#elif defined(CONFIG_MT7621_DRAM_DDR3_4096M)
+	0xAA00AA00, 0xAA00AA00, 0x00000007, 0x44694683,
+	0x01000000, 0xF07486A1, 0xC287221D, 0x9F0F0481,
+	0x03046948, 0x15602842, 0x00008888, 0x88888888,
+	0x00000000, 0x00000000, 0x00000240, 0x07100000,
+	0x00001B61, 0x00002040, 0x00004010, 0x00006000,
+	0x0C000000, 0x07070000, 0x00000000, 0x00000000,
+#elif defined(CONFIG_MT7621_DRAM_DDR3_1024M_KGD)
+	0xFF00FF00, 0xFF00FF00, 0x00000007, 0x44694683,
+	0x01000000, 0xF07406A1, 0xC287221D, 0x9F060481,
+	0x03046923, 0x152f2842, 0x00008888, 0x88888888,
+	0x00000000, 0x00000000, 0x00000210, 0x07100000,
+	0x00001B61, 0x00002040, 0x00004010, 0x00006000,
+	0x0C000000, 0x07070000, 0x000C0000, 0x00000000,
+#else /* CONFIG_MT7621_DRAM_DDR3_2048M */
+	0xAA00AA00, 0xAA00AA00, 0x00000007, 0x44694673,
+	0x01000000, 0xF07486A1, 0xC287221D, 0x9F050481,
+	0x03046948, 0x15602842, 0x00008888, 0x88888888,
+	0x00000000, 0x00000000, 0x00000220, 0x07100000,
+	0x00001B61, 0x00002040, 0x00004010, 0x00006000,
+	0x0C000000, 0x07070000, 0x00000000, 0x00000000,
+#endif
+};
+
+#if defined(CONFIG_MT7621_DRAM_FREQ_400)
+#define DDR_FREQ_PARAM		0x41000000
+#elif defined(CONFIG_MT7621_DRAM_FREQ_1066)
+#define DDR_FREQ_PARAM		0x21000000
+#elif defined(CONFIG_MT7621_DRAM_FREQ_1200)
+#define DDR_FREQ_PARAM		0x11000000
+#else /* CONFIG_MT7621_DRAM_FREQ_800 */
+#define DDR_FREQ_PARAM		0x31000000
+#endif
+
+#define RG_MEPL_FBDIV_S		4
+#define RG_MEPL_FBDIV_M		0x7f
+
+static inline void word_copy(u32 *dest, const u32 *src, u32 count)
+{
+	u32 i;
+
+	for (i = 0; i < count; i++)
+		dest[i] = src[i];
+}
+
+static u32 calc_cpu_pll_val(void)
+{
+	u32 div, baseval, fb;
+
+	div = get_xtal_mhz();
+
+	if (div == 40) {
+		div /= 2;
+		baseval = 0xc0005802;
+	} else {
+		baseval = 0xc0004802;
+	}
+
+	fb = CONFIG_MT7621_CPU_FREQ / div - 1;
+	if (fb > RG_MEPL_FBDIV_M)
+		fb = RG_MEPL_FBDIV_M;
+
+	return baseval | (fb << RG_MEPL_FBDIV_S);
+}
+
+void prepare_stage_bin(void)
+{
+	u32 stage_size;
+
+	const struct stage_header *stock_stage_bin =
+		(const struct stage_header *)__image_copy_end;
+
+	struct stage_header *new_stage_bin =
+		(struct stage_header *)STAGE_LOAD_ADDR;
+
+	if (be32_to_cpu(stock_stage_bin->ep) != STAGE_LOAD_ADDR)
+		panic("Invalid DDR stage binary blob\n");
+
+	stage_size = be32_to_cpu(stock_stage_bin->stage_size);
+
+	word_copy((u32 *)new_stage_bin, (const u32 *)stock_stage_bin,
+		  (stage_size + sizeof(u32) - 1) / sizeof(u32));
+
+	word_copy(new_stage_bin->ddr2_act, ddr2_act, DDR_PARAM_SIZE);
+	word_copy(new_stage_bin->ddr3_act, ddr3_act, DDR_PARAM_SIZE);
+
+	new_stage_bin->cpu_pll_cfg = calc_cpu_pll_val();
+	new_stage_bin->ddr_pll_cfg = DDR_FREQ_PARAM;
+	new_stage_bin->baudrate = CONFIG_BAUDRATE;
+}
diff --git a/arch/mips/mach-mtmips/mt7621/spl/dram.h b/arch/mips/mach-mtmips/mt7621/spl/dram.h
new file mode 100644
index 0000000..7322c58
--- /dev/null
+++ b/arch/mips/mach-mtmips/mt7621/spl/dram.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2022 MediaTek Inc. All rights reserved.
+ *
+ * Author: Weijie Gao <weijie.gao@mediatek.com>
+ */
+
+#ifndef _MT7621_DRAM_H_
+#define _MT7621_DRAM_H_
+
+#define STAGE_LOAD_ADDR			0xBE108800
+
+#ifndef __ASSEMBLY__
+#include <linux/types.h>
+
+#define DDR_PARAM_SIZE			24
+
+struct stage_header {
+	u32 jump_insn[2];
+	u32 ep;
+	u32 stage_size;
+	u32 has_stage2;
+	u32 next_ep;
+	u32 next_size;
+	u32 next_offset;
+	u32 cpu_pll_cfg;
+	u32 ddr_pll_cfg;
+	u32 reserved2[6];
+	char build_tag[32];
+	u32 ddr3_act[DDR_PARAM_SIZE];
+	u32 padding1[2];
+	u32 ddr2_act[DDR_PARAM_SIZE];
+	u32 padding2[2];
+	u32 baudrate;
+	u32 padding3;
+};
+#endif
+
+#endif /* _MT7621_DRAM_H_ */
diff --git a/arch/mips/mach-mtmips/mt7621/spl/launch.c b/arch/mips/mach-mtmips/mt7621/spl/launch.c
new file mode 100644
index 0000000..37c20a5
--- /dev/null
+++ b/arch/mips/mach-mtmips/mt7621/spl/launch.c
@@ -0,0 +1,100 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2022 MediaTek Inc. All rights reserved.
+ *
+ * Author: Weijie Gao <weijie.gao@mediatek.com>
+ */
+
+#include <asm/io.h>
+#include <asm/cm.h>
+#include <asm/sections.h>
+#include <asm/addrspace.h>
+#include <asm/mipsmtregs.h>
+#include <linux/sizes.h>
+#include <time.h>
+#include <cpu_func.h>
+#include "launch.h"
+#include "../mt7621.h"
+
+/* Cluster Power Controller (CPC) offsets */
+#define CPC_CL_OTHER			0x2010
+#define CPC_CO_CMD			0x4000
+
+/* CPC_CL_OTHER fields */
+#define CPC_CL_OTHER_CORENUM_SHIFT	16
+#define CPC_CL_OTHER_CORENUM		GENMASK(23, 16)
+
+/* CPC_CO_CMD */
+#define PWR_UP				3
+
+#define NUM_CORES			2
+#define NUM_CPUS			4
+#define WAIT_CPUS_TIMEOUT		4000
+
+static void copy_launch_wait_code(void)
+{
+	memset((void *)KSEG1, 0, SZ_4K);
+
+	memcpy((void *)KSEG1ADDR(LAUNCH_WAITCODE),
+	       &launch_wait_code_start,
+	       &launch_wait_code_end - &launch_wait_code_start);
+
+	invalidate_dcache_range(KSEG0, SZ_4K);
+}
+
+static void bootup_secondary_core(void)
+{
+	void __iomem *cpcbase = (void __iomem *)KSEG1ADDR(MIPS_CPC_BASE);
+	int i;
+
+	for (i = 1; i < NUM_CORES; i++) {
+		writel(i << CPC_CL_OTHER_CORENUM_SHIFT, cpcbase + CPC_CL_OTHER);
+		writel(PWR_UP, cpcbase + CPC_CO_CMD);
+	}
+}
+
+void secondary_cpu_init(void)
+{
+	void __iomem *sysc = (void __iomem *)KSEG1ADDR(SYSCTL_BASE);
+	u32 i, dual_core = 0, cpuready = 1, cpumask = 0x03;
+	ulong wait_tick;
+	struct cpulaunch_t *c;
+
+	/* Copy LAUNCH wait code used by other VPEs */
+	copy_launch_wait_code();
+
+	dual_core = readl(sysc + SYSCTL_CHIP_REV_ID_REG) & CPU_ID;
+
+	if (dual_core) {
+		/* Bootup secondary core for MT7621A */
+		cpumask = 0x0f;
+
+		/* Make BootROM/TPL redirect Core1's bootup flow to our entry point */
+		writel((uintptr_t)&_start, sysc + BOOT_SRAM_BASE_REG);
+
+		bootup_secondary_core();
+	}
+
+	/* Join the coherent domain */
+	join_coherent_domain(dual_core ? 2 : 1);
+
+	/* Bootup Core0/VPE1 */
+	boot_vpe1();
+
+	/* Wait for all CPU ready */
+	wait_tick = get_timer(0) + WAIT_CPUS_TIMEOUT;
+
+	while (time_before(get_timer(0), wait_tick)) {
+		/* CPU0 is obviously ready */
+		for (i = 1; i < NUM_CPUS; i++) {
+			c = (struct cpulaunch_t *)(KSEG0ADDR(CPULAUNCH) +
+						   (i << LOG2CPULAUNCH));
+
+			if (c->flags & LAUNCH_FREADY)
+				cpuready |= BIT(i);
+		}
+
+		if ((cpuready & cpumask) == cpumask)
+			break;
+	}
+}
diff --git a/arch/mips/mach-mtmips/mt7621/spl/launch.h b/arch/mips/mach-mtmips/mt7621/spl/launch.h
new file mode 100644
index 0000000..f34250d
--- /dev/null
+++ b/arch/mips/mach-mtmips/mt7621/spl/launch.h
@@ -0,0 +1,52 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2022 MediaTek Inc. All rights reserved.
+ *
+ * Author: Weijie Gao <weijie.gao@mediatek.com>
+ */
+
+#ifndef _LAUNCH_H_
+#define _LAUNCH_H_
+
+#ifndef __ASSEMBLY__
+
+struct cpulaunch_t {
+	unsigned long pc;
+	unsigned long gp;
+	unsigned long sp;
+	unsigned long a0;
+	unsigned long _pad[3]; /* pad to cache line size to avoid thrashing */
+	unsigned long flags;
+};
+
+extern char launch_wait_code_start;
+extern char launch_wait_code_end;
+
+void join_coherent_domain(int ncores);
+void boot_vpe1(void);
+
+#else
+
+#define	LAUNCH_PC		0
+#define	LAUNCH_GP		4
+#define	LAUNCH_SP		8
+#define	LAUNCH_A0		12
+#define	LAUNCH_FLAGS		28
+
+#endif
+
+#define LOG2CPULAUNCH		5
+
+#define LAUNCH_FREADY		1
+#define LAUNCH_FGO		2
+#define LAUNCH_FGONE		4
+
+#define LAUNCH_WAITCODE		0x00000d00
+#define SCRLAUNCH		0x00000e00
+#define CPULAUNCH		0x00000f00
+#define NCPULAUNCH		8
+
+/* Polling period in count cycles for secondary CPU's */
+#define LAUNCHPERIOD		10000
+
+#endif /* _LAUNCH_H_ */
diff --git a/arch/mips/mach-mtmips/mt7621/spl/launch_ll.S b/arch/mips/mach-mtmips/mt7621/spl/launch_ll.S
new file mode 100644
index 0000000..32d28c7
--- /dev/null
+++ b/arch/mips/mach-mtmips/mt7621/spl/launch_ll.S
@@ -0,0 +1,339 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2022 MediaTek Inc. All rights reserved.
+ *
+ * Author: Weijie Gao <weijie.gao@mediatek.com>
+ */
+
+#include <asm/cm.h>
+#include <asm/asm.h>
+#include <asm/regdef.h>
+#include <asm/cacheops.h>
+#include <asm/mipsregs.h>
+#include <asm/addrspace.h>
+#include <asm/mipsmtregs.h>
+#include "launch.h"
+
+	.macro cache_loop	curr, end, line_sz, op
+10:	cache		\op, 0(\curr)
+	PTR_ADDU	\curr, \curr, \line_sz
+	bne		\curr, \end, 10b
+	.endm
+
+	.set	mt
+
+/*
+ * Join the coherent domain
+ * a0 = number of cores
+ */
+LEAF(join_coherent_domain)
+	/*
+	 * Enable coherence and allow interventions from all other cores.
+	 * (Write access enabled via GCR_ACCESS by core 0.)
+	 */
+	li	t1, 1
+	sll	t1, a0
+	addiu	t1, -1
+
+	li	t0, KSEG1ADDR(CONFIG_MIPS_CM_BASE)
+	sw	t1, GCR_Cx_COHERENCE(t0)
+	ehb
+
+	move	t2, zero
+
+_next_coherent_core:
+	sll	t1, t2, GCR_Cx_OTHER_CORENUM_SHIFT
+	sw	t1, GCR_Cx_OTHER(t0)
+
+_busy_wait_coherent_core:
+	lw	t1, GCR_CO_COHERENCE(t0)
+	beqz	t1, _busy_wait_coherent_core
+
+	addiu	t2, 1
+	bne	t2, a0, _next_coherent_core
+
+	jr	ra
+	END(join_coherent_domain)
+
+/*
+ * All VPEs other than VPE0 will go here.
+ */
+LEAF(launch_vpe_entry)
+	mfc0	t0, CP0_EBASE
+	and	t0, t0, MIPS_EBASE_CPUNUM
+
+	/* per-VPE cpulaunch_t */
+	li	a0, KSEG0ADDR(CPULAUNCH)
+	sll	t1, t0, LOG2CPULAUNCH
+	addu	a0, t1
+
+	/* Set CPU online flag */
+	li	t0, LAUNCH_FREADY
+	sw	t0, LAUNCH_FLAGS(a0)
+
+	/* Enable count interrupt in mask, but do not enable interrupts */
+	mfc0	t0, CP0_STATUS
+	ori	t0, STATUSF_IP7
+	mtc0	t0, CP0_STATUS
+
+	/* VPEs executing in wait code do not need a stack */
+	li	t9, KSEG0ADDR(LAUNCH_WAITCODE)
+	jr	t9
+	END(launch_vpe_entry)
+
+/*
+ * This function will not be executed in place.
+ * It will be copied into memory, and VPEs other than VPE0 will be
+ * started to run into this in-memory function.
+ */
+LEAF(launch_wait_code)
+	.globl	launch_wait_code_start
+launch_wait_code_start:
+
+	move	t0, a0
+
+start_poll:
+	/* Poll CPU go flag */
+	mtc0	zero, CP0_COUNT
+	li	t1, LAUNCHPERIOD
+	mtc0	t1, CP0_COMPARE
+
+time_wait:
+	/* Software wait */
+	mfc0	t2, CP0_COUNT
+	subu	t2, t1
+	bltz	t2, time_wait
+
+	/* Check the launch flag */
+	lw	t3, LAUNCH_FLAGS(t0)
+	and	t3, LAUNCH_FGO
+	beqz	t3, start_poll
+
+	/* Reset the counter and interrupts to give naive clients a chance */
+	mfc0	t1, CP0_STATUS
+	ins	t1, zero, STATUSB_IP7, 1
+	mtc0	t1, CP0_STATUS
+
+	mfc0	t1, CP0_COUNT
+	subu	t1, 1
+	mtc0	t1, CP0_COMPARE
+
+	/* Jump to kernel */
+	lw	t9, LAUNCH_PC(t0)
+	lw	gp, LAUNCH_GP(t0)
+	lw	sp, LAUNCH_SP(t0)
+	lw	a0, LAUNCH_A0(t0)
+	move	a1, zero
+	move	a2, zero
+	move	a3, zero
+	ori	t3, LAUNCH_FGONE
+	sw	t3, LAUNCH_FLAGS(t0)
+
+	jr	t9
+
+	.globl	launch_wait_code_end
+launch_wait_code_end:
+	END(launch_wait_code)
+
+/*
+ * Core1 will go here.
+ */
+LEAF(launch_core_entry)
+	/* Disable caches */
+	bal	mips_cache_disable
+
+	/* Initialize L1 cache only */
+	li	a0, CONFIG_SYS_ICACHE_SIZE
+	li	a1, CONFIG_SYS_ICACHE_LINE_SIZE
+	li	a2, CONFIG_SYS_DCACHE_SIZE
+	li	a3, CONFIG_SYS_DCACHE_LINE_SIZE
+
+	mtc0	zero, CP0_TAGLO
+	mtc0	zero, CP0_TAGLO, 2
+	ehb
+
+	/*
+	 * Initialize the I-cache first,
+	 */
+	li		t0, KSEG0
+	addu		t1, t0, a0
+	/* clear tag to invalidate */
+	cache_loop	t0, t1, a1, INDEX_STORE_TAG_I
+#ifdef CONFIG_SYS_MIPS_CACHE_INIT_RAM_LOAD
+	/* fill once, so data field parity is correct */
+	PTR_LI		t0, KSEG0
+	cache_loop	t0, t1, a1, FILL
+	/* invalidate again - prudent but not strictly necessary */
+	PTR_LI		t0, KSEG0
+	cache_loop	t0, t1, a1, INDEX_STORE_TAG_I
+#endif
+
+	/*
+	 * then initialize D-cache.
+	 */
+	PTR_LI		t0, KSEG0
+	PTR_ADDU	t1, t0, a2
+	/* clear all tags */
+	cache_loop	t0, t1, a3, INDEX_STORE_TAG_D
+#ifdef CONFIG_SYS_MIPS_CACHE_INIT_RAM_LOAD
+	/* load from each line (in cached space) */
+	PTR_LI		t0, KSEG0
+2:	LONG_L		zero, 0(t0)
+	PTR_ADDU	t0, a3
+	 bne		t0, t1, 2b
+	/* clear all tags */
+	PTR_LI		t0, KSEG0
+	cache_loop	t0, t1, a3, INDEX_STORE_TAG_D
+#endif
+
+	/* Set Cache Mode */
+	mfc0	t0, CP0_CONFIG
+	li	t1, CONF_CM_CACHABLE_COW
+	ins	t0, t1, 0, 3
+	mtc0	t0, CP0_CONFIG
+
+	/* Join the coherent domain */
+	li	a0, 2
+	bal	join_coherent_domain
+
+	/* Bootup Core0/VPE1 */
+	bal	boot_vpe1
+
+	b	launch_vpe_entry
+	END(launch_core_entry)
+
+/*
+ * Bootup VPE1.
+ * This subroutine must be executed from VPE0 with VPECONF0[MVP] already set.
+ */
+LEAF(boot_vpe1)
+	mfc0	t0, CP0_MVPCONF0
+
+	/* a0 = number of TCs - 1 */
+	ext	a0, t0, MVPCONF0_PTC_SHIFT, 8
+	beqz	a0, _vpe1_init_done
+
+	/* a1 = number of VPEs - 1 */
+	ext	a1, t0, MVPCONF0_PVPE_SHIFT, 4
+	beqz	a1, _vpe1_init_done
+
+	/* a2 = current TC No. */
+	move	a2, zero
+
+	/* Enter VPE Configuration State */
+	mfc0	t0, CP0_MVPCONTROL
+	or	t0, MVPCONTROL_VPC
+	mtc0	t0, CP0_MVPCONTROL
+	ehb
+
+_next_tc:
+	/* Set the TC number to be used on MTTR and MFTR instructions */
+	mfc0	t0, CP0_VPECONTROL
+	ins	t0, a2, 0, 8
+	mtc0	t0, CP0_VPECONTROL
+	ehb
+
+	/* TC0 is already bound */
+	beqz	a2, _next_vpe
+
+	/* Halt current TC */
+	li	t0, TCHALT_H
+	mttc0	t0, CP0_TCHALT
+	ehb
+
+	/* If there is spare TC, bind it to the last VPE (VPE[a1]) */
+	slt	t1, a1, a2
+	bnez	t1, _vpe_bind_tc
+	 move	t1, a1
+
+	/* Set Exclusive TC for active TC */
+	mftc0	t0, CP0_VPECONF0
+	ins	t0, a2, VPECONF0_XTC_SHIFT, 8
+	mttc0	t0, CP0_VPECONF0
+
+	move	t1, a2
+_vpe_bind_tc:
+	/* Bind TC to a VPE */
+	mftc0	t0, CP0_TCBIND
+	ins	t0, t1, TCBIND_CURVPE_SHIFT, 4
+	mttc0	t0, CP0_TCBIND
+
+	/*
+	 * Set up CP0_TCSTATUS register:
+	 * Disable Coprocessor Usable bits
+	 * Disable MDMX/DSP ASE
+	 * Clear Dirty TC
+	 * not dynamically allocatable
+	 * not allocated
+	 * Kernel mode
+	 * interrupt exempt
+	 * ASID 0
+	 */
+	li	t0, TCSTATUS_IXMT
+	mttc0	t0, CP0_TCSTATUS
+
+_next_vpe:
+	slt	t1, a1, a2
+	bnez	t1, _done_vpe	# No more VPEs
+
+	/* Disable TC multi-threading */
+	mftc0	t0, CP0_VPECONTROL
+	ins	t0, zero, VPECONTROL_TE_SHIFT, 1
+	mttc0	t0, CP0_VPECONTROL
+
+	/* Skip following configuration for TC0 */
+	beqz	a2, _done_vpe
+
+	/* Deactivate VPE, set Master VPE */
+	mftc0	t0, CP0_VPECONF0
+	ins	t0, zero, VPECONF0_VPA_SHIFT, 1
+	or	t0, VPECONF0_MVP
+	mttc0	t0, CP0_VPECONF0
+
+	mfc0	t0, CP0_STATUS
+	mttc0	t0, CP0_STATUS
+
+	mttc0	zero, CP0_EPC
+	mttc0	zero, CP0_CAUSE
+
+	mfc0	t0, CP0_CONFIG
+	mttc0	t0, CP0_CONFIG
+
+	/*
+	 * VPE1 of each core can execute cached as its L1 I$ has already
+	 * been initialized.
+	 * and the L2$ has been initialized or "disabled" via CCA override.
+	 */
+	PTR_LA	t0, _start
+	mttc0	t0, CP0_TCRESTART
+
+	/* Unset Interrupt Exempt, set Activate Thread */
+	mftc0	t0, CP0_TCSTATUS
+	ins	t0, zero, TCSTATUS_IXMT_SHIFT, 1
+	ori	t0, TCSTATUS_A
+	mttc0	t0, CP0_TCSTATUS
+
+	/* Resume TC */
+	mttc0	zero, CP0_TCHALT
+
+	/* Activate VPE */
+	mftc0	t0, CP0_VPECONF0
+	ori	t0, VPECONF0_VPA
+	mttc0	t0, CP0_VPECONF0
+
+_done_vpe:
+	addu	a2, 1
+	sltu	t0, a0, a2
+	beqz	t0, _next_tc
+
+	mfc0	t0, CP0_MVPCONTROL
+	/* Enable all activated VPE to execute */
+	ori	t0, MVPCONTROL_EVP
+	/* Exit VPE Configuration State */
+	ins	t0, zero, MVPCONTROL_VPC_SHIFT, 1
+	mtc0	t0, CP0_MVPCONTROL
+	ehb
+
+_vpe1_init_done:
+	jr	ra
+	END(boot_vpe1)
diff --git a/arch/mips/mach-mtmips/mt7621/spl/serial.c b/arch/mips/mach-mtmips/mt7621/spl/serial.c
new file mode 100644
index 0000000..5cf093a
--- /dev/null
+++ b/arch/mips/mach-mtmips/mt7621/spl/serial.c
@@ -0,0 +1,24 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2022 MediaTek Inc. All rights reserved.
+ *
+ * Author: Weijie Gao <weijie.gao@mediatek.com>
+ */
+
+#include <asm/io.h>
+#include "../mt7621.h"
+
+void mtmips_spl_serial_init(void)
+{
+#ifdef CONFIG_SPL_SERIAL
+	void __iomem *base = ioremap_nocache(SYSCTL_BASE, SYSCTL_SIZE);
+
+#if CONFIG_CONS_INDEX == 1
+	clrbits_32(base + SYSCTL_GPIOMODE_REG, UART1_MODE);
+#elif CONFIG_CONS_INDEX == 2
+	clrbits_32(base + SYSCTL_GPIOMODE_REG, UART2_MODE_M);
+#elif CONFIG_CONS_INDEX == 3
+	clrbits_32(base + SYSCTL_GPIOMODE_REG, UART3_MODE_M);
+#endif /* CONFIG_CONS_INDEX */
+#endif /* CONFIG_SPL_SERIAL */
+}
diff --git a/arch/mips/mach-mtmips/mt7621/spl/spl.c b/arch/mips/mach-mtmips/mt7621/spl/spl.c
new file mode 100644
index 0000000..91eebc6
--- /dev/null
+++ b/arch/mips/mach-mtmips/mt7621/spl/spl.c
@@ -0,0 +1,96 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2022 MediaTek Inc. All rights reserved.
+ *
+ * Author: Weijie Gao <weijie.gao@mediatek.com>
+ */
+
+#include <init.h>
+#include <image.h>
+#include <vsprintf.h>
+#include <malloc.h>
+#include <asm/io.h>
+#include <asm/sections.h>
+#include <asm/addrspace.h>
+#include <asm/byteorder.h>
+#include <asm/global_data.h>
+#include <linux/sizes.h>
+#include <linux/types.h>
+#include <mach/serial.h>
+#include "../mt7621.h"
+#include "dram.h"
+#include <spl.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct tpl_info {
+	u32 magic;
+	u32 size;
+};
+
+void set_timer_freq_simple(void)
+{
+	u32 div = get_xtal_mhz();
+
+	/* Round down cpu freq */
+	gd->arch.timer_freq = rounddown(CONFIG_MT7621_CPU_FREQ, div) * 500000;
+}
+
+void __noreturn board_init_f(ulong dummy)
+{
+	spl_init();
+
+#ifdef CONFIG_SPL_SERIAL
+	/*
+	 * mtmips_spl_serial_init() is useful if debug uart is enabled,
+	 * or DM based serial is not enabled.
+	 */
+	mtmips_spl_serial_init();
+	preloader_console_init();
+#endif
+
+	board_init_r(NULL, 0);
+}
+
+void board_boot_order(u32 *spl_boot_list)
+{
+#ifdef CONFIG_MT7621_BOOT_FROM_NAND
+	spl_boot_list[0] = BOOT_DEVICE_NAND;
+#else
+	spl_boot_list[0] = BOOT_DEVICE_NOR;
+#endif
+}
+
+unsigned long spl_nor_get_uboot_base(void)
+{
+	const struct tpl_info *tpli;
+	const image_header_t *hdr;
+	u32 addr;
+
+	addr = FLASH_MMAP_BASE + TPL_INFO_OFFSET;
+	tpli = (const struct tpl_info *)KSEG1ADDR(addr);
+
+	if (tpli->magic == TPL_INFO_MAGIC) {
+		addr = FLASH_MMAP_BASE + tpli->size;
+		hdr = (const image_header_t *)KSEG1ADDR(addr);
+
+		if (image_get_magic(hdr) == IH_MAGIC) {
+			addr += sizeof(*hdr) + image_get_size(hdr);
+			return KSEG1ADDR(addr);
+		}
+	}
+
+	panic("Unable to locate SPL payload\n");
+	return 0;
+}
+
+uint32_t spl_nand_get_uboot_raw_page(void)
+{
+	const struct stage_header *sh = (const struct stage_header *)&_start;
+	u32 addr;
+
+	addr = image_get_header_size() + be32_to_cpu(sh->stage_size);
+	addr = ALIGN(addr, SZ_4K);
+
+	return addr;
+}
diff --git a/arch/mips/mach-mtmips/mt7621/spl/start.S b/arch/mips/mach-mtmips/mt7621/spl/start.S
new file mode 100644
index 0000000..3cad356
--- /dev/null
+++ b/arch/mips/mach-mtmips/mt7621/spl/start.S
@@ -0,0 +1,226 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2022 MediaTek Inc. All rights reserved.
+ *
+ * Author: Weijie Gao <weijie.gao@mediatek.com>
+ */
+
+#include <asm-offsets.h>
+#include <config.h>
+#include <asm/asm.h>
+#include <asm/regdef.h>
+#include <asm/mipsregs.h>
+#include <asm/cacheops.h>
+#include <asm/addrspace.h>
+#include <asm/mipsmtregs.h>
+#include <asm/cm.h>
+#include "../mt7621.h"
+#include "dram.h"
+
+#ifndef CONFIG_SYS_INIT_SP_ADDR
+#define CONFIG_SYS_INIT_SP_ADDR	(CONFIG_SYS_SDRAM_BASE + \
+				CONFIG_SYS_INIT_SP_OFFSET)
+#endif
+
+#define SP_ADDR_TEMP		0xbe10dff0
+
+	.macro init_wr sel
+	MTC0	zero, CP0_WATCHLO,\sel
+	mtc0	t1, CP0_WATCHHI,\sel
+	.endm
+
+	.macro setup_stack_gd
+	li	t0, -16
+	PTR_LI	t1, CONFIG_SYS_INIT_SP_ADDR
+	and	sp, t1, t0		# force 16 byte alignment
+	PTR_SUBU \
+		sp, sp, GD_SIZE		# reserve space for gd
+	and	sp, sp, t0		# force 16 byte alignment
+	move	k0, sp			# save gd pointer
+#if CONFIG_VAL(SYS_MALLOC_F_LEN) && \
+    !CONFIG_IS_ENABLED(INIT_STACK_WITHOUT_MALLOC_F)
+	li	t2, CONFIG_VAL(SYS_MALLOC_F_LEN)
+	PTR_SUBU \
+		sp, sp, t2		# reserve space for early malloc
+	and	sp, sp, t0		# force 16 byte alignment
+#endif
+	move	fp, sp
+
+	/* Clear gd */
+	move	t0, k0
+1:
+	PTR_S	zero, 0(t0)
+	PTR_ADDIU t0, PTRSIZE
+	blt	t0, t1, 1b
+	 nop
+
+#if CONFIG_VAL(SYS_MALLOC_F_LEN) && \
+    !CONFIG_IS_ENABLED(INIT_STACK_WITHOUT_MALLOC_F)
+	PTR_S	sp, GD_MALLOC_BASE(k0)	# gd->malloc_base offset
+#endif
+	.endm
+
+	.set	noreorder
+
+ENTRY(_start)
+	b	1f
+	 mtc0	zero, CP0_COUNT
+
+	/* Stage header required by BootROM */
+	.org	0x8
+	.word	0		# ep, filled by mkimage
+	.word	0		# stage_size, filled by mkimage
+	.word	0		# has_stage2
+	.word	0		# next_ep
+	.word	0		# next_size
+	.word	0		# next_offset
+
+1:
+	/* Init CP0 Status */
+	mfc0	t0, CP0_STATUS
+	and	t0, ST0_IMPL
+	or	t0, ST0_BEV | ST0_ERL
+	mtc0	t0, CP0_STATUS
+	ehb
+
+	/* Clear Watch Status bits and disable watch exceptions */
+	li	t1, 0x7		# Clear I, R and W conditions
+	init_wr	0
+	init_wr	1
+	init_wr	2
+	init_wr	3
+
+	/* Clear WP, IV and SW interrupts */
+	mtc0	zero, CP0_CAUSE
+
+	/* Clear timer interrupt (CP0_COUNT cleared on branch to 'reset') */
+	mtc0	zero, CP0_COMPARE
+
+	/* VPE1 goes to wait code directly */
+	mfc0	t0, CP0_TCBIND
+	andi	t0, TCBIND_CURVPE
+	bnez	t0, launch_vpe_entry
+	 nop
+
+	/* Core1 goes to specific launch entry */
+	PTR_LI	t0, KSEG1ADDR(CONFIG_MIPS_CM_BASE)
+	lw	t1, GCR_Cx_ID(t0)
+	bnez	t1, launch_core_entry
+	 nop
+
+	/* MT7530 reset */
+	li	t0, KSEG1ADDR(SYSCTL_BASE)
+	lw	t1, SYSCTL_RSTCTL_REG(t0)
+	ori	t1, MCM_RST
+	sw	t1, SYSCTL_RSTCTL_REG(t0)
+
+	/* Disable DMA route for PSE SRAM set by BootROM */
+	PTR_LI	t0, KSEG1ADDR(DMA_CFG_ARB_BASE)
+	sw	zero, DMA_ROUTE_REG(t0)
+
+	/* Set CPU clock to 500MHz (Required if boot from NAND) */
+	li	t0, KSEG1ADDR(SYSCTL_BASE)
+	lw	t1, SYSCTL_CLKCFG0_REG(t0)
+	ins	t1, zero, 30, 2		# CPU_CLK_SEL
+	sw	t1, SYSCTL_CLKCFG0_REG(t0)
+
+	/* Set CPU clock divider to 1/1 */
+	li	t0, KSEG1ADDR(RBUS_BASE)
+	li	t1, 0x101
+	sw	t1, RBUS_DYN_CFG0_REG(t0)
+
+	/* (Re-)initialize the SRAM */
+	bal	mips_sram_init
+	 nop
+
+	/* Set up temporary stack */
+	li	sp, SP_ADDR_TEMP
+
+	/* Setup full CPS */
+	bal	mips_cm_map
+	 nop
+
+	bal	mt7621_cps_init
+	 nop
+
+	/* Prepare for CPU/DDR initialization binary blob */
+	bal	prepare_stage_bin
+	 nop
+
+	/* Call CPU/DDR initialization binary blob */
+	li	t9, STAGE_LOAD_ADDR
+	jalr	t9
+	 nop
+
+	/* Switch CPU PLL source */
+	li	t0, KSEG1ADDR(SYSCTL_BASE)
+	lw	t1, SYSCTL_CLKCFG0_REG(t0)
+	li	t2, 1
+	ins	t1, t2, CPU_CLK_SEL_S, 2
+	sw	t1, SYSCTL_CLKCFG0_REG(t0)
+
+	/*
+	 * Currently SPL is running on locked L2 cache (on KSEG0).
+	 * To reset the entire cache, we have to writeback SPL to DRAM first.
+	 * Cache flush won't work here. Use memcpy instead.
+	 */
+
+	la	a0, __text_start
+	move	a1, a0
+	la	a2, __image_copy_end
+	sub	a2, a2, a1
+	li	a3, 5
+	ins	a0, a3, 29, 3	# convert to KSEG1
+
+	bal	memcpy
+	 nop
+
+	/* Disable caches */
+	bal	mips_cache_disable
+	 nop
+
+	/* Reset caches */
+	bal	mips_cache_reset
+	 nop
+
+	/* Disable SRAM */
+	li	t0, KSEG1ADDR(FE_BASE)
+	li	t1, FE_PSE_RESET
+	sw	t1, FE_RST_GLO_REG(t0)
+
+	/* Clear the .bss section */
+	la	a0, __bss_start
+	la	a1, __bss_end
+1:	sw	zero, 0(a0)
+	addiu	a0, 4
+	ble	a0, a1, 1b
+	 nop
+
+	/* Set up initial stack and global data */
+	setup_stack_gd
+
+#if CONFIG_IS_ENABLED(INIT_STACK_WITHOUT_MALLOC_F)
+	/* Set malloc base */
+	li	t0, (CONFIG_SYS_INIT_SP_ADDR + 15) & (~15)
+	PTR_S	t0, GD_MALLOC_BASE(k0)	# gd->malloc_base offset
+#endif
+
+#if defined(CONFIG_DEBUG_UART) && defined(CONFIG_SPL_SERIAL)
+	/* Earliest point to set up debug uart */
+	bal	debug_uart_init
+	 nop
+#endif
+
+	/* Setup timer */
+	bal	set_timer_freq_simple
+	 nop
+
+	/* Bootup secondary CPUs */
+	bal	secondary_cpu_init
+	 nop
+
+	move	a0, zero		# a0 <-- boot_flags = 0
+	bal board_init_f
+	 move	ra, zero
+
+	END(_start)
diff --git a/arch/mips/mach-mtmips/mt7621/sram_init.S b/arch/mips/mach-mtmips/mt7621/sram_init.S
new file mode 100644
index 0000000..03b9eab
--- /dev/null
+++ b/arch/mips/mach-mtmips/mt7621/sram_init.S
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2022 MediaTek Inc. All rights reserved.
+ *
+ * Author: Weijie Gao <weijie.gao@mediatek.com>
+ */
+
+#include <asm/addrspace.h>
+#include <asm/asm.h>
+#include <asm/regdef.h>
+#include "mt7621.h"
+
+LEAF(mips_sram_init)
+	li	t0, KSEG1ADDR(FE_BASE)
+	li	t1, FE_PSE_RESET
+	sw	t1, FE_RST_GLO_REG(t0)
+
+	li	t1, (FE_PSE_RAM | FE_PSE_MEM_EN)
+	sw	t1, FE_RST_GLO_REG(t0)
+
+	jr	ra
+	END(mips_sram_init)
diff --git a/arch/mips/mach-mtmips/mt7621/tpl/Makefile b/arch/mips/mach-mtmips/mt7621/tpl/Makefile
new file mode 100644
index 0000000..471ad74
--- /dev/null
+++ b/arch/mips/mach-mtmips/mt7621/tpl/Makefile
@@ -0,0 +1,4 @@
+
+extra-y += start.o
+
+obj-y += tpl.o
diff --git a/arch/mips/mach-mtmips/mt7621/tpl/start.S b/arch/mips/mach-mtmips/mt7621/tpl/start.S
new file mode 100644
index 0000000..19b09f7
--- /dev/null
+++ b/arch/mips/mach-mtmips/mt7621/tpl/start.S
@@ -0,0 +1,161 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2022 MediaTek Inc. All rights reserved.
+ *
+ * Author: Weijie Gao <weijie.gao@mediatek.com>
+ */
+
+#include <asm-offsets.h>
+#include <config.h>
+#include <asm/asm.h>
+#include <asm/regdef.h>
+#include <asm/addrspace.h>
+#include <asm/mipsregs.h>
+#include <asm/cm.h>
+#include "../mt7621.h"
+
+#define SP_ADDR_TEMP		0xbe10dff0
+
+	.set noreorder
+
+	.macro init_wr sel
+	MTC0	zero, CP0_WATCHLO,\sel
+	mtc0	t1, CP0_WATCHHI,\sel
+	.endm
+
+	.macro uhi_mips_exception
+	move	k0, t9		# preserve t9 in k0
+	move	k1, a0		# preserve a0 in k1
+	li	t9, 15		# UHI exception operation
+	li	a0, 0		# Use hard register context
+	sdbbp	1		# Invoke UHI operation
+	.endm
+
+ENTRY(_start)
+	b	reset
+	 mtc0	zero, CP0_COUNT
+
+	/*
+	 * Store TPL size here.
+	 * This will be used by SPL to locate u-boot payload.
+	 */
+	.org	TPL_INFO_OFFSET
+	.word	TPL_INFO_MAGIC
+	.word	__image_copy_len
+
+	/* Exception vector */
+	.org 0x200
+	/* TLB refill, 32 bit task */
+	uhi_mips_exception
+
+	.org 0x280
+	/* XTLB refill, 64 bit task */
+	uhi_mips_exception
+
+	.org 0x300
+	/* Cache error exception */
+	uhi_mips_exception
+
+	.org 0x380
+	/* General exception */
+	uhi_mips_exception
+
+	.org 0x400
+	/* Catch interrupt exceptions */
+	uhi_mips_exception
+
+	.org 0x480
+	/* EJTAG debug exception */
+1:	b	1b
+	 nop
+
+	.org 0x500
+
+reset:
+	/* Set KSEG0 to Uncached */
+	mfc0	t0, CP0_CONFIG
+	ins	t0, zero, 0, 3
+	ori	t0, t0, CONF_CM_UNCACHED
+	mtc0	t0, CP0_CONFIG
+	ehb
+
+	/* Check for CPU number */
+	mfc0	t0, CP0_EBASE
+	and	t0, t0, MIPS_EBASE_CPUNUM
+	beqz	t0, 1f
+	 nop
+
+	/* Secondary core goes to specified SPL entry address */
+	li	t0, KSEG1ADDR(SYSCTL_BASE)
+	lw	t0, BOOT_SRAM_BASE_REG(t0)
+	jr	t0
+	 nop
+
+	/* Init CP0 Status */
+1:	mfc0	t0, CP0_STATUS
+	and	t0, ST0_IMPL
+	or	t0, ST0_BEV | ST0_ERL
+	mtc0	t0, CP0_STATUS
+	 nop
+
+	/* Clear Watch Status bits and disable watch exceptions */
+	li	t1, 0x7		# Clear I, R and W conditions
+	init_wr	0
+	init_wr	1
+	init_wr	2
+	init_wr	3
+
+	/* Clear WP, IV and SW interrupts */
+	mtc0	zero, CP0_CAUSE
+
+	/* Clear timer interrupt (CP0_COUNT cleared on branch to 'reset') */
+	mtc0	zero, CP0_COMPARE
+
+	/* Setup basic CPS */
+	bal	mips_cm_map
+	 nop
+
+	li	t0, KSEG1ADDR(CONFIG_MIPS_CM_BASE)
+	li	t1, GCR_REG0_BASE_VALUE
+	sw	t1, GCR_REG0_BASE(t0)
+
+	li	t1, ((GCR_REG0_MASK_VALUE << GCR_REGn_MASK_ADDRMASK_SHIFT) | \
+		    GCR_REGn_MASK_CMTGT_IOCU0)
+	sw	t1, GCR_REG0_MASK(t0)
+
+	lw	t1, GCR_BASE(t0)
+	ins	t1, zero, 0, 2		# CM_DEFAULT_TARGET
+	sw	t1, GCR_BASE(t0)
+
+	lw	t1, GCR_CONTROL(t0)
+	li	t2, GCR_CONTROL_SYNCCTL
+	or	t1, t1, t2
+	sw	t1, GCR_CONTROL(t0)
+
+	/* Increase SPI frequency */
+	li	t0, KSEG1ADDR(SPI_BASE)
+	li	t1, 5
+	sw	t1, SPI_SPACE_REG(t0)
+
+	/* Set CPU clock to 500MHz */
+	li	t0, KSEG1ADDR(SYSCTL_BASE)
+	lw	t1, SYSCTL_CLKCFG0_REG(t0)
+	ins	t1, zero, 30, 2		# CPU_CLK_SEL
+	sw	t1, SYSCTL_CLKCFG0_REG(t0)
+
+	/* Set CPU clock divider to 1/1 */
+	li	t0, KSEG1ADDR(RBUS_BASE)
+	li	t1, 0x101
+	sw	t1, RBUS_DYN_CFG0_REG(t0)
+
+	/* Initialize the SRAM */
+	bal	mips_sram_init
+	 nop
+
+	/* Set up initial stack */
+	li	sp, SP_ADDR_TEMP
+
+	bal	tpl_main
+	 nop
+
+	END(_start)
diff --git a/arch/mips/mach-mtmips/mt7621/tpl/tpl.c b/arch/mips/mach-mtmips/mt7621/tpl/tpl.c
new file mode 100644
index 0000000..2a82890
--- /dev/null
+++ b/arch/mips/mach-mtmips/mt7621/tpl/tpl.c
@@ -0,0 +1,144 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2022 MediaTek Inc. All rights reserved.
+ *
+ * Author: Weijie Gao <weijie.gao@mediatek.com>
+ */
+
+#include <image.h>
+#include <asm/system.h>
+#include <asm/sections.h>
+#include <asm/cacheops.h>
+#include <asm/mipsregs.h>
+#include <asm/cm.h>
+
+#define INDEX_STORE_DATA_SD	0x0f
+
+typedef void __noreturn (*image_entry_noargs_t)(void);
+
+/*
+ * Lock L2 cache and fill data
+ * Assume that data is 4-byte aligned and start_addr/size is 32-byte aligned
+ */
+static void fill_lock_l2cache(uintptr_t dataptr, ulong start_addr, ulong size)
+{
+	ulong slsize = CONFIG_SYS_DCACHE_LINE_SIZE;
+	ulong end_addr = start_addr + size;
+	const u32 *data = (u32 *)dataptr;
+	ulong i, addr;
+	u32 val;
+
+	/* Clear WSC & SPR bit in ErrCtl */
+	val = read_c0_ecc();
+	val &= 0xcfffffff;
+	write_c0_ecc(val);
+	execution_hazard_barrier();
+
+	for (addr = start_addr; addr < end_addr; addr += slsize) {
+		/* Set STagLo to lock cache line */
+		write_c0_staglo((addr & 0x1ffff800) | 0xa0);
+		mips_cache(INDEX_STORE_TAG_SD, (void *)addr);
+
+		/* Fill data */
+		for (i = 0; i < slsize; i += 8) {
+			val = *data++;
+			__write_32bit_c0_register($28, 5, val); /* sdtaglo */
+			val = *data++;
+			__write_32bit_c0_register($29, 5, val); /* sdtaghi */
+			mips_cache(INDEX_STORE_DATA_SD, (void *)(addr + i));
+		}
+	}
+
+	sync();
+}
+
+/* A simple function to initialize MT7621's cache */
+static void mt7621_cache_init(void)
+{
+	void __iomem *cm_base = (void *)KSEG1ADDR(CONFIG_MIPS_CM_BASE);
+	ulong lsize = CONFIG_SYS_DCACHE_LINE_SIZE;
+	ulong addr;
+	u32 val;
+
+	/* Enable CCA override. Set to uncached */
+	val = readl(cm_base + GCR_BASE);
+	val &= ~CCA_DEFAULT_OVR_MASK;
+	val |= CCA_DEFAULT_OVREN | (2 << CCA_DEFAULT_OVR_SHIFT);
+	writel(val, cm_base + GCR_BASE);
+
+	/* Initialize L1 I-Cache */
+	write_c0_taglo(0);
+	write_c0_taghi(0);
+
+	for (addr = 0; addr < CONFIG_SYS_ICACHE_SIZE; addr += lsize)
+		mips_cache(INDEX_STORE_TAG_I, (void *)addr);
+
+	/* Initialize L1 D-Cache */
+	write_c0_dtaglo(0);
+	__write_32bit_c0_register($29, 2, 0); /* dtaghi */
+
+	for (addr = 0; addr < CONFIG_SYS_DCACHE_SIZE; addr += lsize)
+		mips_cache(INDEX_STORE_TAG_D, (void *)addr);
+
+	/* Initialize L2 Cache */
+	write_c0_staglo(0);
+	__write_32bit_c0_register($29, 4, 0); /* staghi */
+
+	for (addr = 0; addr < (256 << 10); addr += lsize)
+		mips_cache(INDEX_STORE_TAG_SD, (void *)addr);
+
+	/* Dsiable CCA override */
+	val = readl(cm_base + GCR_BASE);
+	val &= ~(CCA_DEFAULT_OVR_MASK | CCA_DEFAULT_OVREN);
+	writel(val, cm_base + GCR_BASE);
+
+	/* Set KSEG0 to non-coherent cached (important!) */
+	val = read_c0_config();
+	val &= ~CONF_CM_CMASK;
+	val |= CONF_CM_CACHABLE_NONCOHERENT;
+	write_c0_config(val);
+	execution_hazard_barrier();
+
+	/* Again, invalidate L1 D-Cache */
+	for (addr = 0; addr < CONFIG_SYS_DCACHE_SIZE; addr += lsize)
+		mips_cache(INDEX_WRITEBACK_INV_D, (void *)addr);
+
+	/* Invalidate L1 I-Cache */
+	for (addr = 0; addr < CONFIG_SYS_ICACHE_SIZE; addr += lsize)
+		mips_cache(INDEX_INVALIDATE_I, (void *)addr);
+
+	/* Disable L2 cache bypass */
+	val = read_c0_config2();
+	val &= ~MIPS_CONF_IMPL;
+	write_c0_config2(val);
+	execution_hazard_barrier();
+}
+
+void __noreturn tpl_main(void)
+{
+	const image_header_t *hdr = (const image_header_t *)__image_copy_end;
+	image_entry_noargs_t image_entry;
+	u32 loadaddr, size;
+	uintptr_t data;
+
+	/* Initialize the cache first */
+	mt7621_cache_init();
+
+	if (image_get_magic(hdr) != IH_MAGIC)
+		goto failed;
+
+	loadaddr = image_get_load(hdr);
+	size = image_get_size(hdr);
+	image_entry = (image_entry_noargs_t)image_get_ep(hdr);
+
+	/* Load TPL image to L2 cache */
+	data = (uintptr_t)__image_copy_end + sizeof(struct image_header);
+	fill_lock_l2cache(data, loadaddr, size);
+
+	/* Jump to SPL */
+	image_entry();
+
+failed:
+	for (;;)
+		;
+}
diff --git a/board/mediatek/mt7621/MAINTAINERS b/board/mediatek/mt7621/MAINTAINERS
new file mode 100644
index 0000000..f83141c
--- /dev/null
+++ b/board/mediatek/mt7621/MAINTAINERS
@@ -0,0 +1,8 @@
+MT7621_RFB BOARD
+M:	Weijie Gao <weijie.gao@mediatek.com>
+S:	Maintained
+F:	board/mediatek/mt7621
+F:	configs/mt7621_rfb_defconfig
+F:	configs/mt7621_nand_rfb_defconfig
+F:	arch/mips/dts/mediatek,mt7621-rfb.dts
+F:	arch/mips/dts/mediatek,mt7621-nand-rfb.dts
diff --git a/board/mediatek/mt7621/Makefile b/board/mediatek/mt7621/Makefile
new file mode 100644
index 0000000..db129c5
--- /dev/null
+++ b/board/mediatek/mt7621/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-y += board.o
diff --git a/board/mediatek/mt7621/board.c b/board/mediatek/mt7621/board.c
new file mode 100644
index 0000000..0496f3f
--- /dev/null
+++ b/board/mediatek/mt7621/board.c
@@ -0,0 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2022 MediaTek Inc. All rights reserved.
+ *
+ * Author: Weijie Gao <weijie.gao@mediatek.com>
+ */
diff --git a/cmd/bootefi.c b/cmd/bootefi.c
index 37ce659..8ab0ff5 100644
--- a/cmd/bootefi.c
+++ b/cmd/bootefi.c
@@ -119,9 +119,9 @@
 			efi_free_pool(image_tmp);
 		}
 		bootefi_image_path = image;
-		log_debug("- recorded device %ls\n", efi_dp_str(device));
+		log_debug("- boot device %pD\n", device);
 		if (image)
-			log_debug("- and image %ls\n", efi_dp_str(image));
+			log_debug("- image %pD\n", image);
 	} else {
 		log_debug("- efi_dp_from_name() failed, err=%lx\n", ret);
 		efi_clear_bootdev();
diff --git a/common/spl/Kconfig b/common/spl/Kconfig
index 931619c..908d7d1 100644
--- a/common/spl/Kconfig
+++ b/common/spl/Kconfig
@@ -884,7 +884,7 @@
 	  expose the cmd_ctrl() interface.
 
 config SPL_NAND_BASE
-	depends on SPL_NAND_DRIVERS
+	depends on SPL_NAND_SUPPORT
 	bool "Use Base NAND Driver"
 	help
 	  Include nand_base.c in the SPL.
diff --git a/common/spl/spl_legacy.c b/common/spl/spl_legacy.c
index 2ec7154..ae8731c 100644
--- a/common/spl/spl_legacy.c
+++ b/common/spl/spl_legacy.c
@@ -88,15 +88,29 @@
 	/* Read header into local struct */
 	load->read(load, header, sizeof(hdr), &hdr);
 
+	/*
+	 * If the payload is compressed, the decompressed data should be
+	 * directly write to its load address.
+	 */
+	if (spl_image_get_comp(&hdr) != IH_COMP_NONE)
+		spl_image->flags |= SPL_COPY_PAYLOAD_ONLY;
+
 	ret = spl_parse_image_header(spl_image, bootdev, &hdr);
 	if (ret)
 		return ret;
 
-	dataptr = header + sizeof(hdr);
-
 	/* Read image */
 	switch (spl_image_get_comp(&hdr)) {
 	case IH_COMP_NONE:
+		dataptr = header;
+
+		/*
+		 * Image header will be skipped only if SPL_COPY_PAYLOAD_ONLY
+		 * is set
+		 */
+		if (spl_image->flags & SPL_COPY_PAYLOAD_ONLY)
+			dataptr += sizeof(hdr);
+
 		load->read(load, dataptr, spl_image->size,
 			   (void *)(unsigned long)spl_image->load_addr);
 		break;
@@ -104,6 +118,9 @@
 	case IH_COMP_LZMA:
 		lzma_len = LZMA_LEN;
 
+		/* dataptr points to compressed payload  */
+		dataptr = header + sizeof(hdr);
+
 		debug("LZMA: Decompressing %08lx to %08lx\n",
 		      dataptr, spl_image->load_addr);
 		src = malloc(spl_image->size);
diff --git a/common/spl/spl_nand.c b/common/spl/spl_nand.c
index 82a10ff..7b7579a 100644
--- a/common/spl/spl_nand.c
+++ b/common/spl/spl_nand.c
@@ -56,6 +56,21 @@
 	return size / load->bl_len;
 }
 
+static ulong spl_nand_legacy_read(struct spl_load_info *load, ulong offs,
+				  ulong size, void *dst)
+{
+	int err;
+
+	debug("%s: offs %lx, size %lx, dst %p\n",
+	      __func__, offs, size, dst);
+
+	err = nand_spl_load_image(offs, size, dst);
+	if (err)
+		return 0;
+
+	return size;
+}
+
 struct mtd_info * __weak nand_get_mtd(void)
 {
 	return NULL;
@@ -93,6 +108,18 @@
 		load.bl_len = bl_len;
 		load.read = spl_nand_fit_read;
 		return spl_load_imx_container(spl_image, &load, offset / bl_len);
+	} else if (IS_ENABLED(CONFIG_SPL_LEGACY_IMAGE_FORMAT) &&
+		   image_get_magic(header) == IH_MAGIC) {
+		struct spl_load_info load;
+
+		debug("Found legacy image\n");
+		load.dev = NULL;
+		load.priv = NULL;
+		load.filename = NULL;
+		load.bl_len = 1;
+		load.read = spl_nand_legacy_read;
+
+		return spl_load_legacy_img(spl_image, bootdev, &load, offset);
 	} else {
 		err = spl_parse_image_header(spl_image, bootdev, header);
 		if (err)
diff --git a/configs/mt7621_nand_rfb_defconfig b/configs/mt7621_nand_rfb_defconfig
new file mode 100644
index 0000000..0ad4849
--- /dev/null
+++ b/configs/mt7621_nand_rfb_defconfig
@@ -0,0 +1,89 @@
+CONFIG_MIPS=y
+CONFIG_SYS_MALLOC_LEN=0x100000
+CONFIG_SPL_LIBCOMMON_SUPPORT=y
+CONFIG_SPL_LIBGENERIC_SUPPORT=y
+CONFIG_NR_DRAM_BANKS=1
+CONFIG_ENV_SIZE=0x1000
+CONFIG_DEFAULT_DEVICE_TREE="mediatek,mt7621-nand-rfb"
+CONFIG_SPL_SERIAL=y
+CONFIG_SPL_SYS_MALLOC_F_LEN=0x100000
+CONFIG_SPL=y
+CONFIG_DEBUG_UART_BASE=0xbe000c00
+CONFIG_DEBUG_UART_CLOCK=50000000
+CONFIG_SYS_LOAD_ADDR=0x83000000
+CONFIG_ARCH_MTMIPS=y
+CONFIG_SOC_MT7621=y
+CONFIG_MT7621_BOOT_FROM_NAND=y
+CONFIG_BOARD_MT7621_NAND_RFB=y
+# CONFIG_MIPS_CACHE_SETUP is not set
+# CONFIG_MIPS_CACHE_DISABLE is not set
+CONFIG_RESTORE_EXCEPTION_VECTOR_BASE=y
+CONFIG_MIPS_BOOT_FDT=y
+CONFIG_DEBUG_UART=y
+CONFIG_FIT=y
+# CONFIG_ARCH_FIXUP_FDT_MEMORY is not set
+CONFIG_SYS_CONSOLE_INFO_QUIET=y
+CONFIG_SPL_MAX_SIZE=0x30000
+CONFIG_SPL_BSS_START_ADDR=0x80140000
+CONFIG_SPL_BSS_MAX_SIZE=0x80000
+CONFIG_SPL_SYS_MALLOC_SIMPLE=y
+CONFIG_SPL_NAND_SUPPORT=y
+CONFIG_SPL_NAND_BASE=y
+CONFIG_SPL_NAND_IDENT=y
+# CONFIG_BOOTM_NETBSD is not set
+# CONFIG_BOOTM_PLAN9 is not set
+# CONFIG_BOOTM_RTEMS is not set
+# CONFIG_BOOTM_VXWORKS is not set
+CONFIG_SYS_BOOTM_LEN=0x2000000
+# CONFIG_CMD_ELF is not set
+# CONFIG_CMD_XIMG is not set
+# CONFIG_CMD_CRC32 is not set
+# CONFIG_CMD_DM is not set
+# CONFIG_CMD_FLASH is not set
+CONFIG_CMD_GPIO=y
+# CONFIG_CMD_LOADS is not set
+CONFIG_CMD_MMC=y
+CONFIG_CMD_MTD=y
+CONFIG_CMD_PART=y
+# CONFIG_CMD_PINMUX is not set
+CONFIG_CMD_USB=y
+# CONFIG_CMD_NFS is not set
+CONFIG_CMD_FAT=y
+CONFIG_CMD_FS_GENERIC=y
+# CONFIG_SPL_DOS_PARTITION is not set
+# CONFIG_ISO_PARTITION is not set
+CONFIG_EFI_PARTITION=y
+# CONFIG_SPL_EFI_PARTITION is not set
+CONFIG_PARTITION_TYPE_GUID=y
+CONFIG_SYS_RELOC_GD_ENV_ADDR=y
+CONFIG_NET_RANDOM_ETHADDR=y
+# CONFIG_I2C is not set
+# CONFIG_INPUT is not set
+CONFIG_MMC=y
+# CONFIG_MMC_QUIRKS is not set
+# CONFIG_MMC_HW_PARTITIONING is not set
+CONFIG_MMC_MTK=y
+CONFIG_MTD=y
+CONFIG_DM_MTD=y
+CONFIG_MTD_RAW_NAND=y
+CONFIG_NAND_MT7621=y
+CONFIG_SYS_NAND_ONFI_DETECTION=y
+CONFIG_SYS_NAND_U_BOOT_LOCATIONS=y
+CONFIG_SYS_NAND_U_BOOT_OFFS=0x0
+CONFIG_MEDIATEK_ETH=y
+CONFIG_PHY=y
+CONFIG_PHY_MTK_TPHY=y
+CONFIG_DEBUG_UART_SHIFT=2
+CONFIG_SYSRESET=y
+CONFIG_SYSRESET_RESETCTL=y
+CONFIG_USB=y
+CONFIG_USB_XHCI_HCD=y
+CONFIG_USB_XHCI_MTK=y
+CONFIG_USB_STORAGE=y
+CONFIG_WDT=y
+CONFIG_WDT_MT7621=y
+CONFIG_FAT_WRITE=y
+# CONFIG_BINMAN_FDT is not set
+CONFIG_LZMA=y
+# CONFIG_GZIP is not set
+CONFIG_SPL_LZMA=y
diff --git a/configs/mt7621_rfb_defconfig b/configs/mt7621_rfb_defconfig
new file mode 100644
index 0000000..fa66364
--- /dev/null
+++ b/configs/mt7621_rfb_defconfig
@@ -0,0 +1,86 @@
+CONFIG_MIPS=y
+CONFIG_SYS_MALLOC_LEN=0x100000
+CONFIG_SPL_LIBCOMMON_SUPPORT=y
+CONFIG_SPL_LIBGENERIC_SUPPORT=y
+CONFIG_NR_DRAM_BANKS=1
+CONFIG_ENV_SIZE=0x1000
+CONFIG_DEFAULT_DEVICE_TREE="mediatek,mt7621-rfb"
+CONFIG_SPL_SERIAL=y
+CONFIG_SPL_SYS_MALLOC_F_LEN=0x40000
+CONFIG_SPL=y
+CONFIG_DEBUG_UART_BASE=0xbe000c00
+CONFIG_DEBUG_UART_CLOCK=50000000
+CONFIG_SYS_LOAD_ADDR=0x83000000
+CONFIG_ARCH_MTMIPS=y
+CONFIG_SOC_MT7621=y
+# CONFIG_MIPS_CACHE_SETUP is not set
+# CONFIG_MIPS_CACHE_DISABLE is not set
+CONFIG_RESTORE_EXCEPTION_VECTOR_BASE=y
+CONFIG_MIPS_BOOT_FDT=y
+CONFIG_DEBUG_UART=y
+CONFIG_TPL_SYS_MALLOC_F_LEN=0x1000
+CONFIG_FIT=y
+# CONFIG_ARCH_FIXUP_FDT_MEMORY is not set
+CONFIG_SYS_CONSOLE_INFO_QUIET=y
+CONFIG_SPL_MAX_SIZE=0x30000
+CONFIG_SPL_BSS_START_ADDR=0x80140000
+CONFIG_SPL_BSS_MAX_SIZE=0x80000
+CONFIG_SPL_SYS_MALLOC_SIMPLE=y
+CONFIG_SPL_NOR_SUPPORT=y
+CONFIG_TPL=y
+# CONFIG_TPL_FRAMEWORK is not set
+# CONFIG_BOOTM_NETBSD is not set
+# CONFIG_BOOTM_PLAN9 is not set
+# CONFIG_BOOTM_RTEMS is not set
+# CONFIG_BOOTM_VXWORKS is not set
+CONFIG_SYS_BOOTM_LEN=0x2000000
+# CONFIG_CMD_ELF is not set
+# CONFIG_CMD_XIMG is not set
+# CONFIG_CMD_CRC32 is not set
+# CONFIG_CMD_DM is not set
+CONFIG_CMD_GPIO=y
+# CONFIG_CMD_LOADS is not set
+CONFIG_CMD_MMC=y
+CONFIG_CMD_PART=y
+# CONFIG_CMD_PINMUX is not set
+CONFIG_CMD_SPI=y
+# CONFIG_CMD_NFS is not set
+CONFIG_DOS_PARTITION=y
+# CONFIG_SPL_DOS_PARTITION is not set
+# CONFIG_ISO_PARTITION is not set
+CONFIG_EFI_PARTITION=y
+# CONFIG_SPL_EFI_PARTITION is not set
+CONFIG_PARTITION_TYPE_GUID=y
+CONFIG_SYS_RELOC_GD_ENV_ADDR=y
+CONFIG_NET_RANDOM_ETHADDR=y
+# CONFIG_I2C is not set
+# CONFIG_INPUT is not set
+CONFIG_MMC=y
+# CONFIG_MMC_QUIRKS is not set
+# CONFIG_MMC_HW_PARTITIONING is not set
+CONFIG_MMC_MTK=y
+CONFIG_SF_DEFAULT_SPEED=20000000
+CONFIG_SPI_FLASH_BAR=y
+CONFIG_SPI_FLASH_EON=y
+CONFIG_SPI_FLASH_GIGADEVICE=y
+CONFIG_SPI_FLASH_ISSI=y
+CONFIG_SPI_FLASH_MACRONIX=y
+CONFIG_SPI_FLASH_SPANSION=y
+CONFIG_SPI_FLASH_STMICRO=y
+CONFIG_SPI_FLASH_WINBOND=y
+CONFIG_SPI_FLASH_XMC=y
+CONFIG_SPI_FLASH_XTX=y
+CONFIG_MEDIATEK_ETH=y
+CONFIG_PHY=y
+CONFIG_PHY_MTK_TPHY=y
+CONFIG_DEBUG_UART_SHIFT=2
+CONFIG_SPI=y
+CONFIG_MT7621_SPI=y
+CONFIG_SYSRESET=y
+CONFIG_SYSRESET_RESETCTL=y
+CONFIG_WDT=y
+CONFIG_WDT_MT7621=y
+# CONFIG_BINMAN_FDT is not set
+CONFIG_LZMA=y
+# CONFIG_GZIP is not set
+CONFIG_SPL_LZMA=y
diff --git a/configs/stm32mp13_defconfig b/configs/stm32mp13_defconfig
index f5b6262..673b468 100644
--- a/configs/stm32mp13_defconfig
+++ b/configs/stm32mp13_defconfig
@@ -21,15 +21,25 @@
 CONFIG_SYS_BOOTM_LEN=0x2000000
 CONFIG_CMD_ADTIMG=y
 CONFIG_CMD_ERASEENV=y
+CONFIG_CMD_NVEDIT_EFI=y
 CONFIG_CMD_MEMINFO=y
 CONFIG_CMD_MEMTEST=y
+CONFIG_CMD_UNZIP=y
 CONFIG_CMD_CLK=y
 CONFIG_CMD_GPIO=y
+CONFIG_CMD_I2C=y
+CONFIG_CMD_LSBLK=y
 CONFIG_CMD_MMC=y
 CONFIG_CMD_CACHE=y
+CONFIG_CMD_EFIDEBUG=y
 CONFIG_CMD_TIME=y
+CONFIG_CMD_RNG=y
 CONFIG_CMD_TIMER=y
+CONFIG_CMD_REGULATOR=y
+CONFIG_CMD_EXT4_WRITE=y
+CONFIG_CMD_MTDPARTS=y
 CONFIG_CMD_LOG=y
+CONFIG_CMD_UBI=y
 CONFIG_OF_LIVE=y
 CONFIG_ENV_IS_NOWHERE=y
 CONFIG_ENV_IS_IN_MMC=y
@@ -37,7 +47,16 @@
 CONFIG_SYS_RELOC_GD_ENV_ADDR=y
 CONFIG_SYS_MMC_ENV_DEV=-1
 CONFIG_CLK_SCMI=y
+CONFIG_GPIO_HOG=y
+CONFIG_DM_I2C=y
+CONFIG_SYS_I2C_STM32F7=y
+CONFIG_LED=y
+CONFIG_LED_GPIO=y
+CONFIG_SUPPORT_EMMC_BOOT=y
 CONFIG_STM32_SDMMC2=y
+CONFIG_MTD=y
+CONFIG_DM_MTD=y
+CONFIG_SYS_MTDPARTS_RUNTIME=y
 CONFIG_DM_ETH=y
 CONFIG_PINCONF=y
 CONFIG_DM_REGULATOR=y
@@ -45,12 +64,17 @@
 CONFIG_DM_REGULATOR_GPIO=y
 CONFIG_DM_REGULATOR_SCMI=y
 CONFIG_RESET_SCMI=y
+CONFIG_DM_RNG=y
+CONFIG_RNG_OPTEE=y
+CONFIG_DM_RTC=y
+CONFIG_RTC_STM32=y
 CONFIG_SERIAL_RX_BUFFER=y
 CONFIG_SYSRESET_PSCI=y
 CONFIG_TEE=y
 CONFIG_OPTEE=y
 # CONFIG_OPTEE_TA_AVB is not set
 CONFIG_ERRNO_STR=y
+CONFIG_FDT_FIXUP_PARTITIONS=y
 # CONFIG_LMB_USE_MAX_REGIONS is not set
 CONFIG_LMB_MEMORY_REGIONS=2
 CONFIG_LMB_RESERVED_REGIONS=16
diff --git a/configs/stm32mp15_defconfig b/configs/stm32mp15_defconfig
index 3452403..e5a2996 100644
--- a/configs/stm32mp15_defconfig
+++ b/configs/stm32mp15_defconfig
@@ -1,7 +1,7 @@
 CONFIG_ARM=y
 CONFIG_ARCH_STM32MP=y
 CONFIG_TFABOOT=y
-CONFIG_SYS_MALLOC_F_LEN=0x3000
+CONFIG_SYS_MALLOC_F_LEN=0x20000
 CONFIG_ENV_OFFSET=0x480000
 CONFIG_ENV_SECT_SIZE=0x40000
 CONFIG_DEFAULT_DEVICE_TREE="stm32mp157c-ev1"
diff --git a/doc/board/index.rst b/doc/board/index.rst
index f90a9ca..01b99f9 100644
--- a/doc/board/index.rst
+++ b/doc/board/index.rst
@@ -23,6 +23,7 @@
    highbank/index
    intel/index
    kontron/index
+   mediatek/index
    microchip/index
    nokia/index
    nxp/index
diff --git a/doc/board/mediatek/index.rst b/doc/board/mediatek/index.rst
new file mode 100644
index 0000000..38cd8cb
--- /dev/null
+++ b/doc/board/mediatek/index.rst
@@ -0,0 +1,9 @@
+.. SPDX-License-Identifier: GPL-2.0+
+
+Mediatek
+=========
+
+.. toctree::
+   :maxdepth: 2
+
+   mt7621
diff --git a/doc/board/mediatek/mt7621.rst b/doc/board/mediatek/mt7621.rst
new file mode 100644
index 0000000..1662255
--- /dev/null
+++ b/doc/board/mediatek/mt7621.rst
@@ -0,0 +1,48 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+mt7621_rfb/mt7621_nand_rfb
+==========================
+
+U-Boot for the MediaTek MT7621 boards
+
+Quick Start
+-----------
+
+- Get the DDR initialization binary blob
+- Configure CPU and DDR parameters
+- Build U-Boot
+
+Get the DDR initialization binary blob
+--------------------------------------
+
+Download one from:
+ - https://raw.githubusercontent.com/mtk-openwrt/mt7621-lowlevel-preloader/master/mt7621_stage_sram.bin
+ - https://raw.githubusercontent.com/mtk-openwrt/mt7621-lowlevel-preloader/master/mt7621_stage_sram_noprint.bin
+
+mt7621_stage_sram_noprint.bin has removed all output logs. To use this one,
+download and rename it to mt7621_stage_sram.bin
+
+Put the binary blob to the u-boot build directory.
+
+Configure CPU and DDR parameters
+--------------------------------
+
+menuconfig > MIPS architecture > MediaTek MIPS platforms > CPU & DDR configuration
+
+Select the correct DDR timing parameters for your board. The size shown here
+must match the DDR size of you board.
+
+The frequency of CPU and DDR can also be adjusted.
+
+Build U-Boot
+------------
+
+.. code-block:: bash
+
+   $ export CROSS_COMPILE=mipsel-linux-
+   $ make O=build mt7621_rfb_defconfig # or mt7621_nand_rfb_defconfig
+   $ cp mt7621_stage_sram.bin ./build/mt7621_stage_sram.bin
+   $ # or cp mt7621_stage_sram_noprint.bin ./build/mt7621_stage_sram.bin
+   $ make O=build
+
+Burn the u-boot-mt7621.bin to the SPI-NOR or NAND flash.
diff --git a/doc/board/st/stm32mp1.rst b/doc/board/st/stm32mp1.rst
index 00f9b45..3759df3 100644
--- a/doc/board/st/stm32mp1.rst
+++ b/doc/board/st/stm32mp1.rst
@@ -77,6 +77,16 @@
  + stm32mp157c-ev1.dts
  + stm32mp15xx-dhcor-avenger96.dts
 
+The SCMI variant of each board is supported by a specific "scmi" device tree:
+ + stm32mp157a-dk1-scmi.dts
+ + stm32mp157c-dk2-scmi.dts
+ + stm32mp157c-ed1-scmi.dts
+ + stm32mp157c-ev1-scmi.dts
+
+SCMI variant is used only with stm32mp15_defconfig, when the resources are
+secured with RCC_TZCR.TZEN=1 in OP-TEE. The access to these reset and clock
+resources are provided by OP-TEE and the associated SCMI services.
+
 STM32MP13x
 ``````````
 
@@ -136,6 +146,9 @@
      the secure monitor to access to secure resources.
    + HW_CONFIG: The hardware configuration file = the U-Boot device tree
 
+The scmi variant of each device tree is only support with OP-TEE as secure
+monitor, with stm32mp15_defconfig.
+
 The **Basic** boot chain with SPL (for STM32MP15x)
 ``````````````````````````````````````````````````
 
@@ -250,6 +263,12 @@
 
      # export KBUILD_OUTPUT=stm32mp15
      # make stm32mp15_defconfig
+     # make DEVICE_TREE=stm32mp157c-ev1-scmi all
+
+    or without SCMI support
+
+     # export KBUILD_OUTPUT=stm32mp15
+     # make stm32mp15_defconfig
      # make DEVICE_TREE=stm32mp157c-ev1 all
 
   b) trusted boot on STM32MP13x discovery board::
diff --git a/doc/develop/statistics/u-boot-stats-v2022.07.rst b/doc/develop/statistics/u-boot-stats-v2022.07.rst
new file mode 100644
index 0000000..c1b627c
--- /dev/null
+++ b/doc/develop/statistics/u-boot-stats-v2022.07.rst
@@ -0,0 +1,805 @@
+:orphan:
+
+Release Statistics for U-Boot v2022.07
+======================================
+
+* Processed 1696 csets from 183 developers
+
+* 27 employers found
+
+* A total of 148026 lines added, 56440 removed (delta 91586)
+
+.. table:: Developers with the most changesets
+   :widths: auto
+
+   ================================  =====
+   Name                              Count
+   ================================  =====
+   Tom Rini                          164 (9.7%)
+   Simon Glass                       141 (8.3%)
+   Pali Rohár                        114 (6.7%)
+   Marek Vasut                       94 (5.5%)
+   Peng Fan                          61 (3.6%)
+   Sean Anderson                     59 (3.5%)
+   Heinrich Schuchardt               57 (3.4%)
+   Samuel Holland                    49 (2.9%)
+   Michal Simek                      49 (2.9%)
+   Johan Jonker                      45 (2.7%)
+   Aaron Williams                    40 (2.4%)
+   AKASHI Takahiro                   38 (2.2%)
+   Marek Behún                       36 (2.1%)
+   Tim Harvey                        33 (1.9%)
+   Philippe Reynes                   29 (1.7%)
+   Andre Przywara                    28 (1.7%)
+   Andrew Scull                      27 (1.6%)
+   Patrice Chotard                   26 (1.5%)
+   Patrick Delaunay                  25 (1.5%)
+   Fabio Estevam                     24 (1.4%)
+   Gaurav Jain                       20 (1.2%)
+   Ye Li                             20 (1.2%)
+   Ashok Reddy Soma                  16 (0.9%)
+   Stefan Roese                      13 (0.8%)
+   Sughosh Ganu                      12 (0.7%)
+   Masahisa Kojima                   12 (0.7%)
+   Michael Trimarchi                 11 (0.6%)
+   Paweł Anikiel                     11 (0.6%)
+   Adam Ford                         11 (0.6%)
+   Marcel Ziswiler                   11 (0.6%)
+   Dave Gerlach                      11 (0.6%)
+   Philip Oberfichtner               11 (0.6%)
+   T Karthik Reddy                   11 (0.6%)
+   Bin Meng                          9 (0.5%)
+   Tommaso Merciai                   9 (0.5%)
+   Heiko Thiery                      8 (0.5%)
+   Alper Nebi Yasak                  8 (0.5%)
+   Philippe Schenker                 8 (0.5%)
+   Vyacheslav Bocharov               8 (0.5%)
+   Michael Walle                     7 (0.4%)
+   Peter Robinson                    6 (0.4%)
+   Francesco Dolcini                 6 (0.4%)
+   Mark Kettenis                     6 (0.4%)
+   Neal Frager                       6 (0.4%)
+   Robert Marko                      6 (0.4%)
+   Jim Liu                           6 (0.4%)
+   Tudor Ambarus                     6 (0.4%)
+   Paweł Jarosz                      5 (0.3%)
+   Dinesh Maniyam                    5 (0.3%)
+   Ariel D'Alessandro                5 (0.3%)
+   Eugen Hristev                     5 (0.3%)
+   Sergiu Moga                       5 (0.3%)
+   Lukasz Majewski                   5 (0.3%)
+   Clément Léger                     5 (0.3%)
+   Huang Jianan                      5 (0.3%)
+   Etienne Carriere                  5 (0.3%)
+   Tien Fong Chee                    4 (0.2%)
+   Chris Morgan                      4 (0.2%)
+   Dario Binacchi                    4 (0.2%)
+   Frieder Schrempf                  4 (0.2%)
+   Janne Grunau                      4 (0.2%)
+   Josua Mayer                       4 (0.2%)
+   Vincent Stehlé                    4 (0.2%)
+   Chris Packham                     4 (0.2%)
+   Sai Pavan Boddu                   4 (0.2%)
+   Neil Armstrong                    4 (0.2%)
+   Jan Kiszka                        4 (0.2%)
+   Andrew Davis                      4 (0.2%)
+   Aswath Govindraju                 4 (0.2%)
+   Masami Hiramatsu                  4 (0.2%)
+   Durai Manickam KR                 4 (0.2%)
+   Billy Tsai                        4 (0.2%)
+   Angus Ainslie                     4 (0.2%)
+   Jesse Taube                       4 (0.2%)
+   Andrew Jeffery                    4 (0.2%)
+   Hari Nagalla                      4 (0.2%)
+   Icenowy Zheng                     4 (0.2%)
+   Romain Naour                      4 (0.2%)
+   Joel Stanley                      3 (0.2%)
+   Andrey Zhizhikin                  3 (0.2%)
+   Loic Poulain                      3 (0.2%)
+   Rasmus Villemoes                  3 (0.2%)
+   Vagrant Cascadian                 3 (0.2%)
+   Ilias Apalodimas                  3 (0.2%)
+   Eddie James                       3 (0.2%)
+   Gireesh Hiremath                  3 (0.2%)
+   Sam Shih                          3 (0.2%)
+   Luca Ellero                       3 (0.2%)
+   Tony Dinh                         3 (0.2%)
+   Paul Barker                       3 (0.2%)
+   Oleksandr Suvorov                 3 (0.2%)
+   Igor Opaniuk                      3 (0.2%)
+   Denys Drozdov                     3 (0.2%)
+   SESA644425                        3 (0.2%)
+   Vignesh Raghavendra               3 (0.2%)
+   Dzmitry Sankouski                 3 (0.2%)
+   Jernej Skrabec                    3 (0.2%)
+   Miquel Raynal                     2 (0.1%)
+   Christian Gmeiner                 2 (0.1%)
+   Nate Drude                        2 (0.1%)
+   Rick Chen                         2 (0.1%)
+   Yannick Fertre                    2 (0.1%)
+   Peter Hoyes                       2 (0.1%)
+   Dominic Rath                      2 (0.1%)
+   Du Huanpeng                       2 (0.1%)
+   Haolin Li                         2 (0.1%)
+   Clement Faure                     2 (0.1%)
+   Vladimir Oltean                   2 (0.1%)
+   Christoph Niedermaier             2 (0.1%)
+   Paul Barbieri                     1 (0.1%)
+   Hannes Schmelzer                  1 (0.1%)
+   Teik Heng Chong                   1 (0.1%)
+   Andrea Scian                      1 (0.1%)
+   Jerome Forissier                  1 (0.1%)
+   Emmanuel Vadot                    1 (0.1%)
+   Nicolas Iooss                     1 (0.1%)
+   Andrejs Cainikovs                 1 (0.1%)
+   qianfan Zhao                      1 (0.1%)
+   Yau Wai Gan                       1 (0.1%)
+   Corentin LABBE                    1 (0.1%)
+   Christophe Leroy                  1 (0.1%)
+   Chunfeng Yun                      1 (0.1%)
+   Camelia Groza                     1 (0.1%)
+   Wasim Khan                        1 (0.1%)
+   Georgi Vlaev                      1 (0.1%)
+   Andrea zi0Black Cappa             1 (0.1%)
+   Leo Yu-Chi Liang                  1 (0.1%)
+   Humberto Naves                    1 (0.1%)
+   Sean Nyekjaer                     1 (0.1%)
+   Christoph Fritz                   1 (0.1%)
+   Sergei Antonov                    1 (0.1%)
+   George Hilliard                   1 (0.1%)
+   Ying-Chun Liu (PaulLiu)           1 (0.1%)
+   Vishal Patel                      1 (0.1%)
+   Piyush Mehta                      1 (0.1%)
+   Amit Kumar Mahapatra              1 (0.1%)
+   Jorge Ramirez-Ortiz               1 (0.1%)
+   Ralph Siemsen                     1 (0.1%)
+   Ovidiu Panait                     1 (0.1%)
+   John Keeping                      1 (0.1%)
+   Oliver Graute                     1 (0.1%)
+   Nicolas Heemeryck                 1 (0.1%)
+   Stanley Chu                       1 (0.1%)
+   Josef Schlehofer                  1 (0.1%)
+   Stephen Carlson                   1 (0.1%)
+   Yuantian Tang                     1 (0.1%)
+   Hou Zhiqiang                      1 (0.1%)
+   Kyle Evans                        1 (0.1%)
+   Lyle Franklin                     1 (0.1%)
+   Andrew Abbott                     1 (0.1%)
+   Oleksii Titov                     1 (0.1%)
+   Kshitiz Varshney                  1 (0.1%)
+   Ian Ray                           1 (0.1%)
+   Rogier Stam                       1 (0.1%)
+   Diego Rondini                     1 (0.1%)
+   Daniel Golle                      1 (0.1%)
+   Peter Cai                         1 (0.1%)
+   Alexandre Besnard                 1 (0.1%)
+   Elmar Albert                      1 (0.1%)
+   Arjan Minzinga Zijlstra           1 (0.1%)
+   Oleksii Bidnichenko               1 (0.1%)
+   Ji Luo                            1 (0.1%)
+   Clark Wang                        1 (0.1%)
+   Clément Péron                     1 (0.1%)
+   Andy Shevchenko                   1 (0.1%)
+   Hajo Noerenberg                   1 (0.1%)
+   Ville Baillie                     1 (0.1%)
+   Paul HENRYS                       1 (0.1%)
+   Jérôme Carretero                  1 (0.1%)
+   Dhananjay Phadke                  1 (0.1%)
+   Ivan Vozvakhov                    1 (0.1%)
+   Tom Saeger                        1 (0.1%)
+   Adarsh Babu Kalepalli             1 (0.1%)
+   He Yong                           1 (0.1%)
+   Samuel Dionne-Riel                1 (0.1%)
+   Felix Vietmeyer                   1 (0.1%)
+   Yi Liu                            1 (0.1%)
+   Angelo Dureghello                 1 (0.1%)
+   Baltazár Radics                   1 (0.1%)
+   Alexander Graf                    1 (0.1%)
+   Mihai Sain                        1 (0.1%)
+   Sai Krishna Potthuri              1 (0.1%)
+   weichangzheng                     1 (0.1%)
+   ================================  =====
+
+
+.. table:: Developers with the most changed lines
+   :widths: auto
+
+   ================================  =====
+   Name                              Count
+   ================================  =====
+   Aaron Williams                    31473 (16.9%)
+   Tom Rini                          25599 (13.7%)
+   Simon Glass                       17972 (9.7%)
+   Marek Vasut                       14707 (7.9%)
+   Tim Harvey                        11858 (6.4%)
+   Sean Anderson                     10323 (5.5%)
+   Samuel Holland                    8705 (4.7%)
+   Stefan Roese                      4394 (2.4%)
+   Johan Jonker                      3712 (2.0%)
+   Jim Liu                           3650 (2.0%)
+   Ariel D'Alessandro                3358 (1.8%)
+   Huang Jianan                      3272 (1.8%)
+   Philippe Reynes                   2587 (1.4%)
+   Philip Oberfichtner               2400 (1.3%)
+   Peng Fan                          2365 (1.3%)
+   Pali Rohár                        2277 (1.2%)
+   Paweł Jarosz                      2092 (1.1%)
+   AKASHI Takahiro                   1700 (0.9%)
+   Eugen Hristev                     1532 (0.8%)
+   Gaurav Jain                       1522 (0.8%)
+   Humberto Naves                    1498 (0.8%)
+   Marek Behún                       1353 (0.7%)
+   Andre Przywara                    1318 (0.7%)
+   Paul Barker                       1308 (0.7%)
+   Patrick Delaunay                  1200 (0.6%)
+   Paweł Anikiel                     1162 (0.6%)
+   Michal Simek                      1065 (0.6%)
+   Patrice Chotard                   1038 (0.6%)
+   Ashok Reddy Soma                  1000 (0.5%)
+   Sughosh Ganu                      954 (0.5%)
+   Denys Drozdov                     942 (0.5%)
+   Neal Frager                       894 (0.5%)
+   Angus Ainslie                     804 (0.4%)
+   Heinrich Schuchardt               692 (0.4%)
+   weichangzheng                     686 (0.4%)
+   Etienne Carriere                  652 (0.4%)
+   Janne Grunau                      648 (0.3%)
+   Marcel Ziswiler                   636 (0.3%)
+   Andrew Scull                      606 (0.3%)
+   Neil Armstrong                    598 (0.3%)
+   Masahisa Kojima                   567 (0.3%)
+   Adam Ford                         524 (0.3%)
+   Billy Tsai                        497 (0.3%)
+   Alper Nebi Yasak                  440 (0.2%)
+   Fabio Estevam                     420 (0.2%)
+   Michael Trimarchi                 393 (0.2%)
+   Durai Manickam KR                 378 (0.2%)
+   Vincent Stehlé                    369 (0.2%)
+   Andrew Jeffery                    359 (0.2%)
+   T Karthik Reddy                   322 (0.2%)
+   Francesco Dolcini                 306 (0.2%)
+   Ye Li                             303 (0.2%)
+   Nate Drude                        275 (0.1%)
+   Dave Gerlach                      263 (0.1%)
+   Sergiu Moga                       260 (0.1%)
+   Tony Dinh                         256 (0.1%)
+   Sam Shih                          252 (0.1%)
+   Ivan Vozvakhov                    252 (0.1%)
+   Dzmitry Sankouski                 244 (0.1%)
+   Tommaso Merciai                   242 (0.1%)
+   Yannick Fertre                    217 (0.1%)
+   Clément Léger                     208 (0.1%)
+   Eddie James                       202 (0.1%)
+   Ilias Apalodimas                  190 (0.1%)
+   Philippe Schenker                 189 (0.1%)
+   Adarsh Babu Kalepalli             174 (0.1%)
+   Igor Opaniuk                      160 (0.1%)
+   Michael Walle                     159 (0.1%)
+   Lukasz Majewski                   157 (0.1%)
+   Josua Mayer                       137 (0.1%)
+   Vyacheslav Bocharov               131 (0.1%)
+   Stanley Chu                       131 (0.1%)
+   Gireesh Hiremath                  127 (0.1%)
+   Hari Nagalla                      126 (0.1%)
+   Leo Yu-Chi Liang                  116 (0.1%)
+   Luca Ellero                       107 (0.1%)
+   Chris Morgan                      100 (0.1%)
+   Peter Hoyes                       100 (0.1%)
+   Ji Luo                            100 (0.1%)
+   George Hilliard                   82 (0.0%)
+   Mark Kettenis                     79 (0.0%)
+   Frieder Schrempf                  78 (0.0%)
+   Heiko Thiery                      72 (0.0%)
+   Icenowy Zheng                     69 (0.0%)
+   Joel Stanley                      66 (0.0%)
+   Diego Rondini                     64 (0.0%)
+   Robert Marko                      63 (0.0%)
+   Dinesh Maniyam                    59 (0.0%)
+   Andrejs Cainikovs                 49 (0.0%)
+   Samuel Dionne-Riel                48 (0.0%)
+   Mihai Sain                        45 (0.0%)
+   Bin Meng                          41 (0.0%)
+   Loic Poulain                      40 (0.0%)
+   SESA644425                        40 (0.0%)
+   Masami Hiramatsu                  39 (0.0%)
+   Clement Faure                     39 (0.0%)
+   He Yong                           39 (0.0%)
+   Rasmus Villemoes                  38 (0.0%)
+   Jernej Skrabec                    37 (0.0%)
+   Jerome Forissier                  35 (0.0%)
+   Paul HENRYS                       31 (0.0%)
+   Jérôme Carretero                  30 (0.0%)
+   Paul Barbieri                     28 (0.0%)
+   Aswath Govindraju                 27 (0.0%)
+   Jan Kiszka                        26 (0.0%)
+   Jesse Taube                       26 (0.0%)
+   Nicolas Heemeryck                 23 (0.0%)
+   Ville Baillie                     23 (0.0%)
+   Andrew Davis                      22 (0.0%)
+   Kyle Evans                        22 (0.0%)
+   Peter Robinson                    21 (0.0%)
+   Romain Naour                      21 (0.0%)
+   Peter Cai                         21 (0.0%)
+   Hou Zhiqiang                      20 (0.0%)
+   Clark Wang                        20 (0.0%)
+   Sai Pavan Boddu                   18 (0.0%)
+   Amit Kumar Mahapatra              18 (0.0%)
+   Elmar Albert                      18 (0.0%)
+   Tudor Ambarus                     16 (0.0%)
+   Oleksandr Suvorov                 16 (0.0%)
+   Chris Packham                     15 (0.0%)
+   Vagrant Cascadian                 15 (0.0%)
+   Dominic Rath                      13 (0.0%)
+   Andrea Scian                      13 (0.0%)
+   Christoph Fritz                   13 (0.0%)
+   Nicolas Iooss                     12 (0.0%)
+   Miquel Raynal                     11 (0.0%)
+   Vishal Patel                      11 (0.0%)
+   Georgi Vlaev                      9 (0.0%)
+   Dario Binacchi                    8 (0.0%)
+   Teik Heng Chong                   8 (0.0%)
+   Piyush Mehta                      8 (0.0%)
+   Ovidiu Panait                     8 (0.0%)
+   John Keeping                      8 (0.0%)
+   Dhananjay Phadke                  8 (0.0%)
+   Tom Saeger                        8 (0.0%)
+   Tien Fong Chee                    7 (0.0%)
+   Stephen Carlson                   7 (0.0%)
+   Lyle Franklin                     7 (0.0%)
+   Felix Vietmeyer                   7 (0.0%)
+   Alexander Graf                    7 (0.0%)
+   Andrey Zhizhikin                  6 (0.0%)
+   Vignesh Raghavendra               6 (0.0%)
+   Christian Gmeiner                 6 (0.0%)
+   Haolin Li                         6 (0.0%)
+   Daniel Golle                      6 (0.0%)
+   Rick Chen                         5 (0.0%)
+   Ying-Chun Liu (PaulLiu)           5 (0.0%)
+   Rogier Stam                       5 (0.0%)
+   Alexandre Besnard                 5 (0.0%)
+   Angelo Dureghello                 5 (0.0%)
+   Christoph Niedermaier             4 (0.0%)
+   Hannes Schmelzer                  4 (0.0%)
+   Chunfeng Yun                      4 (0.0%)
+   Kshitiz Varshney                  4 (0.0%)
+   Yi Liu                            4 (0.0%)
+   Baltazár Radics                   4 (0.0%)
+   Christophe Leroy                  3 (0.0%)
+   Wasim Khan                        3 (0.0%)
+   Andrea zi0Black Cappa             3 (0.0%)
+   Sean Nyekjaer                     3 (0.0%)
+   Andy Shevchenko                   3 (0.0%)
+   Du Huanpeng                       2 (0.0%)
+   Vladimir Oltean                   2 (0.0%)
+   Camelia Groza                     2 (0.0%)
+   Sergei Antonov                    2 (0.0%)
+   Oliver Graute                     2 (0.0%)
+   Yuantian Tang                     2 (0.0%)
+   Oleksii Titov                     2 (0.0%)
+   Arjan Minzinga Zijlstra           2 (0.0%)
+   Oleksii Bidnichenko               2 (0.0%)
+   Clément Péron                     2 (0.0%)
+   Hajo Noerenberg                   2 (0.0%)
+   Sai Krishna Potthuri              2 (0.0%)
+   Emmanuel Vadot                    1 (0.0%)
+   qianfan Zhao                      1 (0.0%)
+   Yau Wai Gan                       1 (0.0%)
+   Corentin LABBE                    1 (0.0%)
+   Jorge Ramirez-Ortiz               1 (0.0%)
+   Ralph Siemsen                     1 (0.0%)
+   Josef Schlehofer                  1 (0.0%)
+   Andrew Abbott                     1 (0.0%)
+   Ian Ray                           1 (0.0%)
+   ================================  =====
+
+
+.. table:: Developers with the most lines removed
+   :widths: auto
+
+   ================================  =====
+   Name                              Count
+   ================================  =====
+   Tom Rini                          19724 (34.9%)
+   Denys Drozdov                     941 (1.7%)
+   Marcel Ziswiler                   430 (0.8%)
+   Stefan Roese                      331 (0.6%)
+   Marek Behún                       309 (0.5%)
+   Adam Ford                         247 (0.4%)
+   Tony Dinh                         187 (0.3%)
+   Igor Opaniuk                      134 (0.2%)
+   Leo Yu-Chi Liang                  116 (0.2%)
+   Etienne Carriere                  97 (0.2%)
+   Francesco Dolcini                 30 (0.1%)
+   Rasmus Villemoes                  19 (0.0%)
+   Hou Zhiqiang                      16 (0.0%)
+   Frieder Schrempf                  14 (0.0%)
+   John Keeping                      8 (0.0%)
+   Romain Naour                      5 (0.0%)
+   Ying-Chun Liu (PaulLiu)           5 (0.0%)
+   Ovidiu Panait                     3 (0.0%)
+   Vignesh Raghavendra               3 (0.0%)
+   Andrea zi0Black Cappa             2 (0.0%)
+   Andy Shevchenko                   2 (0.0%)
+   Peter Robinson                    1 (0.0%)
+   Josef Schlehofer                  1 (0.0%)
+   Ian Ray                           1 (0.0%)
+   ================================  =====
+
+
+.. table:: Developers with the most signoffs (total 266)
+   :widths: auto
+
+   ================================  =====
+   Name                              Count
+   ================================  =====
+   Andre Przywara                    50 (18.8%)
+   Michal Simek                      43 (16.2%)
+   Stefan Roese                      41 (15.4%)
+   Patrice Chotard                   23 (8.6%)
+   Peng Fan                          17 (6.4%)
+   Marcel Ziswiler                   16 (6.0%)
+   Neil Armstrong                    9 (3.4%)
+   Priyanka Jain                     7 (2.6%)
+   Michael Trimarchi                 6 (2.3%)
+   Johan Jonker                      5 (1.9%)
+   Tom Rini                          4 (1.5%)
+   Tom Warren                        4 (1.5%)
+   T Karthik Reddy                   4 (1.5%)
+   Stanley Chu                       4 (1.5%)
+   Samuel Holland                    4 (1.5%)
+   Yau Wai Gan                       2 (0.8%)
+   Jagan Teki                        2 (0.8%)
+   Biwen Li                          2 (0.8%)
+   Ye Li                             2 (0.8%)
+   Ilias Apalodimas                  2 (0.8%)
+   Heinrich Schuchardt               2 (0.8%)
+   Ariel D'Alessandro                2 (0.8%)
+   Amit Kumar Mahapatra              1 (0.4%)
+   Wolfgang Wallner                  1 (0.4%)
+   Alexandru M Stan                  1 (0.4%)
+   Kever Yang                        1 (0.4%)
+   Chee Hong Ang                     1 (0.4%)
+   Yifan Gu                          1 (0.4%)
+   Horia Geantă                      1 (0.4%)
+   Richard Weinberger                1 (0.4%)
+   Anatolij Gustschin                1 (0.4%)
+   Gabriel Fernandez                 1 (0.4%)
+   Christian Gmeiner                 1 (0.4%)
+   Sai Krishna Potthuri              1 (0.4%)
+   Josua Mayer                       1 (0.4%)
+   Masahisa Kojima                   1 (0.4%)
+   Ashok Reddy Soma                  1 (0.4%)
+   ================================  =====
+
+
+.. table:: Developers with the most reviews (total 871)
+   :widths: auto
+
+   ================================  =====
+   Name                              Count
+   ================================  =====
+   Simon Glass                       100 (11.5%)
+   Fabio Estevam                     69 (7.9%)
+   Stefan Roese                      67 (7.7%)
+   Ramon Fried                       65 (7.5%)
+   Kever Yang                        63 (7.2%)
+   Priyanka Jain                     53 (6.1%)
+   Patrice Chotard                   38 (4.4%)
+   Patrick Delaunay                  38 (4.4%)
+   Heinrich Schuchardt               35 (4.0%)
+   Andre Przywara                    31 (3.6%)
+   Peng Fan                          26 (3.0%)
+   Bin Meng                          25 (2.9%)
+   Alper Nebi Yasak                  23 (2.6%)
+   Tom Rini                          21 (2.4%)
+   Marek Behún                       21 (2.4%)
+   Jaehoon Chung                     19 (2.2%)
+   Ye Li                             13 (1.5%)
+   Marek Vasut                       12 (1.4%)
+   Heiko Schocher                    9 (1.0%)
+   Tien Fong Chee                    9 (1.0%)
+   Sean Anderson                     9 (1.0%)
+   Vladimir Oltean                   8 (0.9%)
+   Ilias Apalodimas                  7 (0.8%)
+   Leo Yu-Chi Liang                  6 (0.7%)
+   Mark Kettenis                     6 (0.7%)
+   Neil Armstrong                    5 (0.6%)
+   Samuel Holland                    5 (0.6%)
+   Jagan Teki                        5 (0.6%)
+   Masami Hiramatsu                  5 (0.6%)
+   Gaurav Jain                       5 (0.6%)
+   Christian Gmeiner                 4 (0.5%)
+   Chia-Wei Wang                     4 (0.5%)
+   Claudiu Beznea                    4 (0.5%)
+   Pali Rohár                        4 (0.5%)
+   Ashok Reddy Soma                  3 (0.3%)
+   Francesco Dolcini                 3 (0.3%)
+   Nishanth Menon                    3 (0.3%)
+   Stefano Babic                     3 (0.3%)
+   Holger Brunck                     3 (0.3%)
+   Miquel Raynal                     3 (0.3%)
+   Michael Walle                     3 (0.3%)
+   Michal Simek                      2 (0.2%)
+   Michael Trimarchi                 2 (0.2%)
+   Tony Dinh                         2 (0.2%)
+   Frieder Schrempf                  2 (0.2%)
+   Minkyu Kang                       2 (0.2%)
+   Rick Chen                         2 (0.2%)
+   Tudor Ambarus                     2 (0.2%)
+   Joel Stanley                      2 (0.2%)
+   Tim Harvey                        2 (0.2%)
+   Marcel Ziswiler                   1 (0.1%)
+   John Keeping                      1 (0.1%)
+   Peter Robinson                    1 (0.1%)
+   Peter Collingbourne               1 (0.1%)
+   Pankaj Gupta                      1 (0.1%)
+   Han Xu                            1 (0.1%)
+   Radhey Shyam Pandey               1 (0.1%)
+   Philipp Tomsich                   1 (0.1%)
+   Thomas Chou                       1 (0.1%)
+   Kristian Amlie                    1 (0.1%)
+   Qu Wenruo                         1 (0.1%)
+   Jacky Bai                         1 (0.1%)
+   Ley Foon Tan                      1 (0.1%)
+   Dylan Hung                        1 (0.1%)
+   Andrey Zhizhikin                  1 (0.1%)
+   Kshitiz Varshney                  1 (0.1%)
+   Andrew Scull                      1 (0.1%)
+   Janne Grunau                      1 (0.1%)
+   ================================  =====
+
+
+.. table:: Developers with the most test credits (total 66)
+   :widths: auto
+
+   ================================  =====
+   Name                              Count
+   ================================  =====
+   Tim Harvey                        15 (22.7%)
+   Patrice Chotard                   5 (7.6%)
+   Mark Kettenis                     4 (6.1%)
+   Adam Ford                         4 (6.1%)
+   Fabio Estevam                     3 (4.5%)
+   Marek Vasut                       3 (4.5%)
+   Marcel Ziswiler                   3 (4.5%)
+   Derald D. Woods                   3 (4.5%)
+   Pali Rohár                        2 (3.0%)
+   Ariel D'Alessandro                2 (3.0%)
+   Heiko Thiery                      2 (3.0%)
+   Andre Przywara                    1 (1.5%)
+   Chia-Wei Wang                     1 (1.5%)
+   Stefano Babic                     1 (1.5%)
+   Frieder Schrempf                  1 (1.5%)
+   John Keeping                      1 (1.5%)
+   Peter Robinson                    1 (1.5%)
+   Peter Collingbourne               1 (1.5%)
+   Kshitiz Varshney                  1 (1.5%)
+   Janne Grunau                      1 (1.5%)
+   Josua Mayer                       1 (1.5%)
+   Ying-Chun Liu (PaulLiu)           1 (1.5%)
+   Peter Griffin                     1 (1.5%)
+   Tatsuhiko Yasumatsu               1 (1.5%)
+   Jincheng Wang                     1 (1.5%)
+   Gabriel Hojda                     1 (1.5%)
+   Arti Zirk                         1 (1.5%)
+   Merlijn Wajer                     1 (1.5%)
+   Soeren Moch                       1 (1.5%)
+   Raffaele RECALCATI                1 (1.5%)
+   Angus Ainslie                     1 (1.5%)
+   ================================  =====
+
+
+.. table:: Developers who gave the most tested-by credits (total 67)
+   :widths: auto
+
+   ================================  =====
+   Name                              Count
+   ================================  =====
+   Marek Vasut                       14 (20.9%)
+   Fabio Estevam                     6 (9.0%)
+   Michael Trimarchi                 6 (9.0%)
+   Heiko Thiery                      4 (6.0%)
+   Adam Ford                         3 (4.5%)
+   Heinrich Schuchardt               3 (4.5%)
+   Tom Rini                          3 (4.5%)
+   Pali Rohár                        2 (3.0%)
+   Andre Przywara                    2 (3.0%)
+   Frieder Schrempf                  2 (3.0%)
+   Janne Grunau                      2 (3.0%)
+   Miquel Raynal                     2 (3.0%)
+   Masahisa Kojima                   2 (3.0%)
+   Tim Harvey                        1 (1.5%)
+   Mark Kettenis                     1 (1.5%)
+   Angus Ainslie                     1 (1.5%)
+   Simon Glass                       1 (1.5%)
+   Peng Fan                          1 (1.5%)
+   Marek Behún                       1 (1.5%)
+   Ye Li                             1 (1.5%)
+   Gaurav Jain                       1 (1.5%)
+   Joel Stanley                      1 (1.5%)
+   Rasmus Villemoes                  1 (1.5%)
+   Christoph Niedermaier             1 (1.5%)
+   Peter Cai                         1 (1.5%)
+   Jerome Forissier                  1 (1.5%)
+   Andrejs Cainikovs                 1 (1.5%)
+   Nate Drude                        1 (1.5%)
+   AKASHI Takahiro                   1 (1.5%)
+   ================================  =====
+
+
+.. table:: Developers with the most report credits (total 19)
+   :widths: auto
+
+   ================================  =====
+   Name                              Count
+   ================================  =====
+   Tom Rini                          2 (10.5%)
+   Patrick Delaunay                  2 (10.5%)
+   Fabio Estevam                     1 (5.3%)
+   Tim Harvey                        1 (5.3%)
+   Tatsuhiko Yasumatsu               1 (5.3%)
+   Jincheng Wang                     1 (5.3%)
+   Gabriel Hojda                     1 (5.3%)
+   Nishanth Menon                    1 (5.3%)
+   Milan P. Stanić                   1 (5.3%)
+   Yun-Chien Yu                      1 (5.3%)
+   Nicolas Bidron                    1 (5.3%)
+   Balaji Anandapadmanaban           1 (5.3%)
+   David Mosberger-Tang              1 (5.3%)
+   Jesse Villarreal                  1 (5.3%)
+   Suman Anna                        1 (5.3%)
+   Vagrant Cascadian                 1 (5.3%)
+   Chris Morgan                      1 (5.3%)
+   ================================  =====
+
+
+.. table:: Developers who gave the most report credits (total 19)
+   :widths: auto
+
+   ================================  =====
+   Name                              Count
+   ================================  =====
+   Simon Glass                       5 (26.3%)
+   Andre Przywara                    3 (15.8%)
+   Heinrich Schuchardt               2 (10.5%)
+   Miquel Raynal                     2 (10.5%)
+   Aswath Govindraju                 2 (10.5%)
+   Tom Rini                          1 (5.3%)
+   Fabio Estevam                     1 (5.3%)
+   Samuel Holland                    1 (5.3%)
+   Tudor Ambarus                     1 (5.3%)
+   Chunfeng Yun                      1 (5.3%)
+   ================================  =====
+
+
+.. table:: Top changeset contributors by employer
+   :widths: auto
+
+   ================================  =====
+   Name                              Count
+   ================================  =====
+   (Unknown)                         669 (39.4%)
+   Google, Inc.                      168 (9.9%)
+   Konsulko Group                    164 (9.7%)
+   DENX Software Engineering         147 (8.7%)
+   NXP                               112 (6.6%)
+   Linaro                            80 (4.7%)
+   ST Microelectronics               53 (3.1%)
+   AMD                               51 (3.0%)
+   Marvell                           40 (2.4%)
+   Xilinx                            39 (2.3%)
+   ARM                               34 (2.0%)
+   Toradex                           33 (1.9%)
+   Texas Instruments                 27 (1.6%)
+   Amarula Solutions                 24 (1.4%)
+   Intel                             12 (0.7%)
+   Semihalf Embedded Systems         11 (0.6%)
+   Bootlin                           7 (0.4%)
+   BayLibre SAS                      5 (0.3%)
+   Collabora Ltd.                    5 (0.3%)
+   Siemens                           4 (0.2%)
+   Debian.org                        3 (0.2%)
+   IBM                               3 (0.2%)
+   Wind River                        1 (0.1%)
+   Dave S.r.l.                       1 (0.1%)
+   General Electric                  1 (0.1%)
+   Oracle                            1 (0.1%)
+   Rockchip                          1 (0.1%)
+   ================================  =====
+
+
+.. table:: Top lines changed by employer
+   :widths: auto
+
+   ================================  =====
+   Name                              Count
+   ================================  =====
+   (Unknown)                         63402 (34.0%)
+   Marvell                           31473 (16.9%)
+   Konsulko Group                    25599 (13.7%)
+   DENX Software Engineering         22078 (11.9%)
+   Google, Inc.                      18578 (10.0%)
+   NXP                               4382 (2.4%)
+   Linaro                            4183 (2.2%)
+   Collabora Ltd.                    3358 (1.8%)
+   ST Microelectronics               2455 (1.3%)
+   Xilinx                            2277 (1.2%)
+   Toradex                           2140 (1.1%)
+   ARM                               1787 (1.0%)
+   Semihalf Embedded Systems         1162 (0.6%)
+   AMD                               1061 (0.6%)
+   Amarula Solutions                 643 (0.3%)
+   BayLibre SAS                      599 (0.3%)
+   Texas Instruments                 453 (0.2%)
+   Bootlin                           219 (0.1%)
+   IBM                               202 (0.1%)
+   Intel                             78 (0.0%)
+   Siemens                           26 (0.0%)
+   Debian.org                        15 (0.0%)
+   Dave S.r.l.                       13 (0.0%)
+   Wind River                        8 (0.0%)
+   Oracle                            8 (0.0%)
+   Rockchip                          4 (0.0%)
+   General Electric                  1 (0.0%)
+   ================================  =====
+
+
+.. table:: Employers with the most signoffs (total 266)
+   :widths: auto
+
+   ================================  =====
+   Name                              Count
+   ================================  =====
+   ARM                               50 (18.8%)
+   DENX Software Engineering         42 (15.8%)
+   Xilinx                            39 (14.7%)
+   NXP                               29 (10.9%)
+   ST Microelectronics               24 (9.0%)
+   (Unknown)                         21 (7.9%)
+   Toradex                           16 (6.0%)
+   AMD                               11 (4.1%)
+   BayLibre SAS                      9 (3.4%)
+   Amarula Solutions                 8 (3.0%)
+   Konsulko Group                    4 (1.5%)
+   NVidia                            4 (1.5%)
+   Linaro                            3 (1.1%)
+   Intel                             3 (1.1%)
+   Collabora Ltd.                    2 (0.8%)
+   Rockchip                          1 (0.4%)
+   ================================  =====
+
+
+.. table:: Employers with the most hackers (total 185)
+   :widths: auto
+
+   ================================  =====
+   Name                              Count
+   ================================  =====
+   (Unknown)                         102 (55.1%)
+   NXP                               12 (6.5%)
+   Linaro                            10 (5.4%)
+   Xilinx                            9 (4.9%)
+   Toradex                           7 (3.8%)
+   Texas Instruments                 6 (3.2%)
+   DENX Software Engineering         5 (2.7%)
+   Intel                             5 (2.7%)
+   ARM                               3 (1.6%)
+   ST Microelectronics               3 (1.6%)
+   Amarula Solutions                 3 (1.6%)
+   AMD                               2 (1.1%)
+   BayLibre SAS                      2 (1.1%)
+   Google, Inc.                      2 (1.1%)
+   Bootlin                           2 (1.1%)
+   Konsulko Group                    1 (0.5%)
+   Collabora Ltd.                    1 (0.5%)
+   Rockchip                          1 (0.5%)
+   Marvell                           1 (0.5%)
+   Semihalf Embedded Systems         1 (0.5%)
+   IBM                               1 (0.5%)
+   Siemens                           1 (0.5%)
+   Debian.org                        1 (0.5%)
+   Dave S.r.l.                       1 (0.5%)
+   Wind River                        1 (0.5%)
+   Oracle                            1 (0.5%)
+   General Electric                  1 (0.5%)
+   ================================  =====
diff --git a/doc/dumpimage.1 b/doc/dumpimage.1
new file mode 100644
index 0000000..52a45a3
--- /dev/null
+++ b/doc/dumpimage.1
@@ -0,0 +1,103 @@
+.\" SPDX-License-Identifier: GPL-2.0
+.\" Copyright (C) 2022 Sean Anderson <seanga2@gmail.com>
+.TH DUMPIMAGE 1 2022-06-11 U-Boot
+.SH NAME
+dumpimage \- extract data from U-Boot images
+.
+.SH SYNOPSIS
+.SY dumpimage
+.OP \-T type
+.BI \-l\~ image
+.YS
+.SY dumpimage
+.OP \-T type
+.OP \-p position
+.BI \-o\~ outfile
+.I image
+.YS
+.SY dumpimage
+.B \-h
+.YS
+.SY dumpimage
+.B \-V
+.YS
+.
+.SH DESCRIPTION
+.B dumpimage
+lists and extracts data from U-Boot images. If
+.B \-l
+is specified,
+.B dumpimage
+lists the components in
+.IR image .
+Otherwise,
+.B dumpimage
+extracts the component at
+.IR position " to " outfile .
+.
+.SH OPTIONS
+.TP
+.B \-h
+Print usage information and exit.
+.
+.TP
+.B \-l
+Print the header information for
+.IR image ,
+including a list of components.
+.
+.TP
+.BI \-o " outfile"
+The file to write the dumped component to.
+.TP
+.BI \-p " position"
+Specify the
+.I position
+of the component to dump. This should be a numeric index, starting at 0. If not
+specified, the default
+.I position
+is 0.
+.
+.TP
+.BI \-T " type"
+Specify the
+.I type
+of the image. If not specified, the image type will be automatically detected. A
+list of supported image types may be printed by running
+.BR "mkimage\~\-T\~list" .
+.
+.TP
+.B \-V
+Print version information and exit.
+.
+.SH BUGS
+Please report bugs to the
+.UR https://\:source\:.denx\:.de/\:u-boot/\:u-boot/\:issues
+U-Boot bug tracker
+.UE .
+.SH EXAMPLES
+Create a multi-file image and then extract part of that image
+.PP
+.EX
+.in +4
+$ \c
+.B mkimage \-A x86 \-O linux \-T multi \-n x86 \\\\\&
+.in +4
+.B \-d vmlinuz:initrd.img:System.map multi.img
+.in
+Image Name:   x86
+Created:      Thu Jul 25 10:29:13 2013
+Image Type:   Intel x86 Linux Multi-File Image (gzip compressed)
+Data Size:    13722956 Bytes = 13401.32 kB = 13.09 MB
+Load Address: 00000000
+Entry Point:  00000000
+Contents:
+   Image 0: 4040128 Bytes = 3945.44 kB = 3.85 MB
+   Image 1: 7991719 Bytes = 7804.41 kB = 7.62 MB
+   Image 2: 1691092 Bytes = 1651.46 kB = 1.61 MB
+$ \c
+.B dumpimage -p 2 -o System.map multi.img
+.EE
+.in
+.SH SEE ALSO
+.BR mkimage (1)
diff --git a/doc/mkimage.1 b/doc/mkimage.1
index 759dc2d..353ea8b 100644
--- a/doc/mkimage.1
+++ b/doc/mkimage.1
@@ -1,340 +1,779 @@
-.TH MKIMAGE 1 "2022-02-07"
-
+.\" SPDX-License-Identifier: GPL-2.0
+.\" Copyright (C) 2022 Sean Anderson <seanga2@gmail.com>
+.\" Copyright (C) 2013-20 Simon Glass <sjg@chromium.org>
+.\" Copyright (C) 2010 Nobuhiro Iwamatsu <iwamatsu@nigauri.org>
+.\" Copyright (C) 2010 Wolfgang Denk <wd@denx.de>
+.TH MKIMAGE 1 2022-06-11 U-Boot
+.
 .SH NAME
-mkimage \- Generate image for U-Boot
+mkimage \- generate images for U-Boot
 .SH SYNOPSIS
-.B mkimage
-.RB [ \-T " \fItype\fP] " \-l " [\fIuimage file name\fP]"
-
-.B mkimage
-.RB [\fIoptions\fP] " \-f [" "image tree source file" "]" " [" "uimage file name" "]"
-
-.B mkimage
-.RB [\fIoptions\fP] " \-F [" "uimage file name" "]"
-
-.B mkimage
-.RB [\fIoptions\fP] " (legacy mode)"
-
-.SH "DESCRIPTION"
+.SY mkimage
+.OP \-T type
+.BI \-l\~ image-file-name
+.YS
+.
+.SY mkimage
+.RI [ option\~ .\|.\|.\&]
+.OP \-T type
+.I image-file-name
+.YS
+.
+.SY mkimage
+.RI [ option\~ .\|.\|.\&]
+.BI \-f\~ image-tree-source-file\c
+.RB | auto
+.I image-file-name
+.YS
+.
+.SY mkimage
+.RI [ option\~ .\|.\|.\&]
+.BI \-F\~ image-file-name
+.YS
+.
+.SH DESCRIPTION
 The
 .B mkimage
-command is used to create images for use with the U-Boot boot loader.
-These images can contain the linux kernel, device tree blob, root file
-system image, firmware images etc., either separate or combined.
-
+command is used to create images for use with the U-Boot boot loader.  These
+images can contain the Linux kernel, device tree blob, root file system image,
+firmware images etc., either separate or combined.
+.P
 .B mkimage
-supports two different formats:
-
-The old
-.I legacy image
-format concatenates the individual parts (for example, kernel image,
-device tree blob and ramdisk image) and adds a 64 bytes header
-containing information about target architecture, operating system,
-image type, compression method, entry points, time stamp, checksums,
-etc.
-
+supports many image formats. Some of these formats may be used by embedded boot
+firmware to load U-Boot. Others may be used by U-Boot to load Linux (or some
+other kernel):
+.P
+The legacy image format concatenates the individual parts (for example, kernel
+image, device tree blob and ramdisk image) and adds a 64 byte header containing
+information about the target architecture, operating system, image type,
+compression method, entry points, time stamp, checksums, etc.
+.P
 The new
-.I FIT (Flattened Image Tree) format
-allows for more flexibility in handling images of various types and also
-enhances integrity protection of images with stronger checksums. It also
-supports verified boot.
-
-.SH "OPTIONS"
-
-.B List image information:
-
+.I FIT
+(Flattened Image Tree) format allows for more flexibility in handling images of
+various types and also enhances integrity protection of images with stronger
+checksums. It also supports verified boot.
+.
+.SH OPTIONS
+.
+.SS General options
+.
 .TP
-.BI "\-l [" "uimage file name" "]"
-mkimage lists the information contained in the header of an existing U-Boot image.
-
+.B \-h
+.TQ
+.B \-\-help
+Print a help message and exit.
+.
 .TP
-.BI "\-T [" "image type" "]"
-Parse image file as type.
-Pass \-h as the image to see the list of supported image type.
-Without this option image type is autodetected.
-
+.B \-l
+.TQ
+.B \-\-list
+.B mkimage
+lists the information contained in the header of an existing U-Boot image.
+.
 .TP
-.BI "\-q"
-Quiet. Don't print the image header on successful verification.
-
-.P
-.B Create old legacy image:
-
+.B \-s
+.TQ
+.B \-\-no\-copy
+Don't copy in the image data. Depending on the image type, this may create
+just the header, everything but the image data, or nothing at all.
+.
 .TP
-.BI "\-A [" "architecture" "]"
-Set architecture. Pass \-h as the architecture to see the list of supported architectures.
-
+.BI \-T " image-type"
+.TQ
+.BI \-\-type " image-type"
+Parse image file as
+.IR image-type .
+Pass
+.B list
+as
+.I image-type
+to see the list of supported image types. If this option is absent, then it
+defaults to
+.B kernel
+(legacy image). If this option is absent when
+.B \-l
+is passed, then
+.B mkimage
+will attempt to automatically detect the image type. Not all image types support
+automatic detection, so it may be necessary to pass
+.B \-T
+explicitly.
+.IP
+When creating a FIT image with
+.BR \-f ,
+the image type is always set to
+.BR flat_dt .
+In this case,
+.B \-T
+specifies the image node's \(oqtype\(cq property. If
+.B \-T
+is absent, then the \(oqtype\(cq property will default to
+.BR kernel .
+.
 .TP
-.BI "\-O [" "os" "]"
-Set operating system. bootm command of u-boot changes boot method by os type.
-Pass \-h as the OS to see the list of supported OS.
-
+.B \-q
+.TQ
+.B \-\-quiet
+Quiet. Don't print the image header.
+.
 .TP
-.BI "\-T [" "image type" "]"
-Set image type.
-Pass \-h as the image to see the list of supported image type.
-
+.B \-v
+.TQ
+.B \-\-verbose
+Verbose. Print file names as they are added to the image.
+.
 .TP
-.BI "\-C [" "compression type" "]"
-Set compression type.
-Pass \-h as the compression to see the list of supported compression type.
-
+.B \-V
+.TQ
+.B \-\-version
+Print version information and exit.
+.
+.SS General image-creation options
+.
 .TP
-.BI "\-a [" "load address" "]"
-Set load address with a hex number.
-
+.BI \-A " architecture"
+.TQ
+.BI \-\-architecture " architecture"
+Set the architecture. Pass
+.B \-h
+as the architecture to see the list of supported architectures. If
+.B \-A
+is absent, it defaults to
+.BR ppc .
+.
 .TP
-.BI "\-e [" "entry point" "]"
-Set entry point with a hex number.
-
+.BI \-O " os"
+.TQ
+.BI \-\-os " os"
+Set the operating system. The U-Boot
+.I bootm
+command changes boot method based on the OS type.
+Pass
+.B \-h
+as the
+.I os
+to see the list of supported OSs. If
+.B \-O
+is absent, it defaults to
+.BR linux .
+.
 .TP
-.BI "\-l"
-List the contents of an image.
-
+.BI \-C " compression-type"
+.TQ
+.BI \-\-compression " compression-type"
+Set the compression type. The image data should have already been compressed
+using this compression type.
+.B mkimage
+will not automatically compress image data.
+Pass
+.B \-h
+as the
+.I compression-type
+to see the list of supported compression types. If
+.B \-C
+is absent, it defaults to
+.BR gzip .
+.
 .TP
-.BI "\-n [" "image name" "]"
-Set image name to 'image name'.
-
+.BI \-a " load-address"
+.TQ
+.BI \-\-load\-address " load-address"
+Set the absolute address to load the image data to.
+.I load-address
+will be interpreted as a hexadecimal number.
+.
 .TP
-.BI "\-R [" "secondary image name" "]"
-Some image types support a second image for additional data. For these types,
-use \-R to specify this second image.
-.TS
-allbox;
-lb lbx
-l l.
-Image Type	Secondary Image Description
-pblimage	Additional RCW-style header, typically used for PBI commands.
-zynqimage, zynqmpimage	T{
-Initialization parameters, one per line. Each parameter has the form
-.sp
-.ti 4
-.I address data
-.sp
-where
-.I address
-and
-.I data
-are hexadecimal integers. The boot ROM will write each
-.I data
-to
-.I address
-when loading the image. At most 256 parameters may be specified in this
-manner.
-T}
-.TE
-
+.BI \-e " entry-point"
+.TQ
+.BI \-\-entry\-point " entry-point"
+Set the absolute address of the image entry point. The U-Boot
+.I bootm
+command will jump to this address after loading the image.
+.I entry-point
+will be interpreted as a hexadecimal number.
+.
 .TP
-.BI "\-d [" "image data file" "]"
-Use image data from 'image data file'.
-
+.BI \-n " primary-configuration"
+.TQ
+.BI \-\-config " primary-configuration"
+Images may require additional configuration not specified with other options,
+often in a image-type-specific format. The image types which support this
+option and the format of their configuration are listed in
+.BR CONFIGURATION .
+.
 .TP
-.BI "\-x"
-Set XIP (execute in place) flag.
-
+.BI \-R " secondary-configuration"
+.TQ
+.BI \-\-secondary\-config " secondary-configuration"
+Some image types support a second set of configuration data. The image types
+which support secondary configuration and the formap of their configuration are
+listed in
+.BR CONFIGURATION .
+.
 .TP
-.BI "\-s"
-Don't copy in the image data. Depending on the image type, this may create
-just the header, everything but the image data, or nothing at all.
-
+.BI \-d " image-data-file"
+.TQ
+.BI \-\-image " image-data-file"
+Use image data from
+.IR image-data-file .
+If the
+.I image-type
+is
+.BR multi ,
+then multiple images may be specified, separated by colons:
+.RS
+.IP
+.IR image-data-file [\fB:\fP image-data-file .\|.\|.]
+.RE
+.
 .TP
-.BI "\-v"
-Verbose. Print file names as they are added to the image.
-
-.P
-.B Create FIT image:
-
+.B \-x
+.TQ
+.B \-\-xip
+Set the
+.I XIP
+(execute in place) flag. The U-Boot
+.I bootm
+command will not load the image data, and instead will assume it is already
+accessible at the load address (such as via memory-mapped flash).
+.
+.SS Options for creating FIT images
+.
 .TP
-.BI "\-b [" "device tree file" "]
+.BI \-b " device-tree-file"
+.TQ
+.BI \-\-device\-tree " device-tree-file"
 Appends the device tree binary file (.dtb) to the FIT.
-
+.
 .TP
-.BI "\-c [" "comment" "]"
-Specifies a comment to be added when signing. This is typically a useful
-message which describes how the image was signed or some other useful
-information.
-
+.BI \-c " comment"
+.TQ
+.BI \-\-comment " comment"
+Specifies a comment to be added when signing. This is typically a message which
+describes how the image was signed or some other useful information.
+.
 .TP
-.BI "\-D [" "dtc options" "]"
-Provide special options to the device tree compiler that is used to
-create the image.
-
+.BI \-D " dtc-options"
+.TQ
+.BI \-\-dtcopts " dtc-options"
+Provide additional options to the device tree compiler when creating the image.
+See
+.BR dtc (1)
+for documentation of possible options. If
+.B \-D
+is absent, it defaults to
+.BR "\-I dts \-O dtb \-p 500" .
+.
 .TP
-.BI "\-E
+.BI \-E
+.TQ
+.BI \-\-external
 After processing, move the image data outside the FIT and store a data offset
-in the FIT. Images will be placed one after the other immediately after the
-FIT, with each one aligned to a 4-byte boundary. The existing 'data' property
-in each image will be replaced with 'data-offset' and 'data-size' properties.
-A 'data-offset' of 0 indicates that it starts in the first (4-byte aligned)
-byte after the FIT.
-
+in the FIT. Images will be placed one after the other immediately after the FIT,
+with each one aligned to a 4-byte boundary. The existing \(oqdata\(cq property
+in each image will be replaced with \(oqdata-offset\(cq and \(oqdata-size\(cq
+properties.  A \(oqdata-offset\(cq of 0 indicates that it starts in the first
+(4-byte-aligned) byte after the FIT.
+.
 .TP
-.BI "\-B [" "alignment" "]"
+.BI \-B " alignment"
+.TQ
+.BI \-\-alignment " alignment"
 The alignment, in hexadecimal, that external data will be aligned to. This
 option only has an effect when \-E is specified.
-
+.
 .TP
-.BI "\-f [" "image tree source file" " | " "auto" "]"
+.BI \-p " external-position"
+.TQ
+.BI \-\-position " external-position"
+Place external data at a static external position. Instead of writing a
+\(oqdata-offset\(cq property defining the offset from the end of the FIT,
+.B \-p
+will use \(oqdata-position\(cq as the absolute position from the base of the
+FIT. See
+.B \-E
+for details on using external data.
+.
+.TP
+\fB\-f \fIimage-tree-source-file\fR | \fBauto
+.TQ
+\fB\-\-fit \fIimage-tree-source-file\fR | \fBauto
 Image tree source file that describes the structure and contents of the
 FIT image.
-
-This can be automatically generated for some simple cases.
-Use "-f auto" for this. In that case the arguments -d, -A, -O, -T, -C, -a
-and -e are used to specify the image to include in the FIT and its attributes.
-No .its file is required.
-
+.IP
+In some simple cases, the image tree source can be generated automatically. To
+use this feature, pass
+.BR "\-f auto" .
+The
+.BR \-d ,
+.BR \-A ,
+.BR \-O ,
+.BR \-T ,
+.BR \-C ,
+.BR \-a ,
+and
+.B \-e
+options may be used to specify the image to include in the FIT and its
+attributes. No
+.I image-tree-source-file
+is required.
+.
 .TP
-.BI "\-F"
-Indicates that an existing FIT image should be modified. No dtc
-compilation is performed and the \-f flag should not be given.
-This can be used to sign images with additional keys after initial image
-creation.
-
+.B \-F
+.TQ
+.B \-\-update
+Indicates that an existing FIT image should be modified. No dtc compilation will
+be performed and
+.B \-f
+should not be passed. This can be used to sign images with additional keys
+after initial image creation.
+.
 .TP
-.BI "\-i [" "ramdisk_file" "]"
-Appends the ramdisk file to the FIT.
-
+.BI \-i " ramdisk-file"
+.TQ
+.BI \-\-initramfs " ramdisk-file"
+Append a ramdisk or initramfs file to the image.
+.
 .TP
-.BI "\-k [" "key_directory" "]"
+.BI \-k " key-directory"
+.TQ
+.BI \-\-key\-dir " key-directory"
 Specifies the directory containing keys to use for signing. This directory
-should contain a private key file <name>.key for use with signing and a
-certificate <name>.crt (containing the public key) for use with verification.
-
+should contain a private key file
+.IR name .key
+for use with signing, and a certificate
+.IR name .crt
+(containing the public key) for use with verification. The public key is only
+necessary when embedding it into another device tree using
+.BR \-K .
+.I name
+defaults to the value of the signature node's \(oqkey-name-hint\(cq property,
+but may be overridden using
+.BR \-g .
+.
 .TP
-.BI "\-G [" "key_file" "]"
+.BI \-G " key-file"
+.TQ
+.BI \-\-key\-file " key-file"
 Specifies the private key file to use when signing. This option may be used
 instead of \-k.
-
+.
 .TP
-.BI "\-K [" "key_destination" "]"
+.BI \-K " key-destination"
+.TQ
+.BI \-\-key\-dest " key-destination"
 Specifies a compiled device tree binary file (typically .dtb) to write
 public key information into. When a private key is used to sign an image,
 the corresponding public key is written into this file for for run-time
 verification. Typically the file here is the device tree binary used by
 CONFIG_OF_CONTROL in U-Boot.
-
+.
 .TP
-.BI "\-G [" "key_file" "]"
-Specifies the private key file to use when signing. This option may be used
-instead of \-k.
-
-.TP
-.BI "\-g [" "key_name_hint" "]"
-Sets the key-name-hint property when used with \-f auto. This is the <name>
-part of the key. The directory part is set by \-k. This option also indicates
-that the images included in the FIT should be signed. If this option is
-specified, \-o must be specified as well.
-
+.BI \-g " key-name-hint"
+.TQ
+.BI \-\-key\-name\-hint " key-name-hint"
+Overrides the signature node's \(oqkey-name-hint\(cq property. This is
+especially useful when signing an image with
+.BR "\-f auto" .
+This is the
+.I name
+part of the key. The directory part is set by
+.BR \-k .
+This option also indicates that the images included in the FIT should be signed.
+If this option is specified, then
+.B \-o
+must be specified as well.
+.
 .TP
-.BI "\-o [" "signing algorithm" "]"
+.BI \-o " crypto" , checksum
+.TQ
+.BI \-\-algo " crypto" , checksum
 Specifies the algorithm to be used for signing a FIT image. The default is
-taken from the signature node's 'algo' property.
-
-.TP
-.BI "\-p [" "external position" "]"
-Place external data at a static external position. See \-E. Instead of writing
-a 'data-offset' property defining the offset from the end of the FIT, \-p will
-use 'data-position' as the absolute position from the base of the FIT.
-
+taken from the signature node's \(oqalgo\(cq property.
+The valid values for
+.I crypto
+are:
+.RS
+.IP
+.TS
+lb.
+rsa2048
+rsa3072
+rsa4096
+ecdsa256
+.TE
+.RE
+.IP
+The valid values for
+.I checksum
+are
+.RS
+.IP
+.TS
+lb.
+sha1
+sha256
+sha384
+sha512
+.TE
+.RE
+.
 .TP
-.BI "\-r"
+.B \-r
+.TQ
+.B \-\-key\-required
 Specifies that keys used to sign the FIT are required. This means that they
 must be verified for the image to boot. Without this option, the verification
 will be optional (useful for testing but not for release).
-
+.
 .TP
-.BI "\-N [" "engine" "]"
-The openssl engine to use when signing and verifying the image. For a complete list of
-available engines, refer to
+.BI \-N " engine"
+.TQ
+.BI \-\-engine " engine"
+The openssl engine to use when signing and verifying the image. For a complete
+list of available engines, refer to
 .BR engine (1).
-
+.
 .TP
-.BI "\-t
+.B \-t
+.TQ
+.B \-\-touch
 Update the timestamp in the FIT.
-
-Normally the FIT timestamp is created the first time mkimage is run on a FIT,
+.IP
+Normally the FIT timestamp is created the first time mkimage runs,
 when converting the source .its to the binary .fit file. This corresponds to
-using the -f flag. But if the original input to mkimage is a binary file
-(already compiled) then the timestamp is assumed to have been set previously.
-
+using
+.BR -f .
+But if the original input to mkimage is a binary file (already compiled), then
+the timestamp is assumed to have been set previously.
+.
+.SH CONFIGURATION
+This section documents the formats of the primary and secondary configuration
+options for each image type which supports them.
+.
+.SS aisimage
+The primary configuration is a file containing a series of
+.I AIS
+(Application Image Script) commands, one per line. Each command has the form
+.RS
+.P
+.IR "command argument " .\|.\|.
+.RE
+.P
+See
+.UR https://\:www\:.ti\:.com/\:lit/\:pdf/\:spraag0
+TI application report SPRAAG0E
+.UE
+for details.
+.
+.SS atmelimage
+The primary configuration is a comma-separated list of NAND Flash parameters of
+the form
+.RS
+.P
+\fIparameter\fB=\fIvalue\fR[\fB,\fIparameter\fB=\fIvalue\fR.\|.\|.\&]
+.RE
+.P
+Valid
+.IR parameter s
+are
+.RS
+.P
+.TS
+lb.
+usePmecc
+nbSectorPerPage
+spareSize
+eccBitReq
+sectorSize
+eccOffset
+.TE
+.RE
+.P
+and valid
+.IR value s
+are decimal numbers. See section 11.4.4.1 of the SAMA5D3 Series Data Sheet for
+valid values for each parameter.
+.
+.SS imximage
+The primary configuration is a file containing configuration commands, as
+documented in doc/\:imx/\:mkimage/\:imximage.txt of the U-Boot source.
+.
+.SS imx8image and imx8mimage
+The primary configuration is a file containing configuration commands, as
+documented in doc/\:imx/\:mkimage/\:imx8image.txt of the U-Boot source.
+.
+.SS kwbimage
+The primary configuration is a file containing configuration commands, as
+documented in doc/\:imx/\:mkimage/\:kwbimage.txt of the U-Boot source.
+.
+.SS mtk_image
+The primary configuration is a semicolon-separated list of header options of the
+form
+.RS
+.P
+\fIkey\fB=\fIvalue\fR[\fB;\fIkey\fB=\fIvalue\fR.\|.\|.\&]
+.RE
+.P
+where the valid keys are:
+.RS
+.P
+.TS
+lb lbx
+lb l.
+Key	Description
+_
+lk	T{
+If \fB1\fP, then an \fILK\fP (legacy) image header is used. Otherwise, a
+\fIBootROM\fP image header is used.
+T}
+lkname	T{
+The name of the LK image header. The maximum length is 32 ASCII characters. If
+not specified, the default value is \fBU-Boot\fP.
+T}
+media	The boot device. See below for valid values.
+nandinfo	The desired NAND device type. See below for valid values.
+arm64	If \fB1\fP, then this denotes an AArch64 image.
+hdroffset	Increase the reported size of the BRLYT header by this amount.
+.TE
+.RE
+.P
+Valid values for
+.B media
+are:
+.RS
+.P
+.TS
+lb lb
+lb l.
+Value	Description
+_
+nand	Parallel NAND flash
+snand	Serial NAND flash
+nor	Serial NOR flash
+emmc	\fIeMMC\fP (Embedded Multi-Media Card)
+sdmmc	\fISD\fP (Secure Digital) card
+.TE
+.RE
+.P
+Valid values for
+.B nandinfo
+are:
+.RS
+.P
+.TS
+lb lb lb	lb	lb
+lb l	l	l	l.
+Value	NAND type	Page size	OOB size	Total size
+_
+2k+64	Serial	2KiB	64B
+2k+120	Serial	2KiB	120B
+2k+128	Serial	2KiB	128B
+4k+256	Serial	4KiB	256B
+1g:2k+64	Parallel	2KiB	64B	1Gbit
+2g:2k+64	Parallel	2KiB	64B	2Gbit
+4g:2k+64	Parallel	2KiB	64B	4Gbit
+2g:2k+128	Parallel	2KiB	128B	2Gbit
+4g:2k+128	Parallel	2KiB	128B	4Gbit
+.TE
+.RE
+.
+.SS mxsimage
+The primary configuration is a file containing configuration commands, as
+documented in doc/\:imx/\:mkimage/\:mxsimage.txt of the U-Boot source.
+.
+.SS omapimage
+The primary configuration is the optional value
+.BR byteswap .
+If present, each 32-bit word of the image will have its bytes swapped
+(converting from little-endian to big-endian, or vice versa).
+.
+.SS pblimage
+The primary configuration is a file containing the
+.I PBI
+(Pre-Boot Image) header. Each line of the configuration has the format
+.RS
+.P
+.IR value "[ " value .\|.\|.\&]
+.RE
+.P
+Where
+.I value
+is a 32-bit hexadecimal integer. Each
+.I value
+will, after being converted to raw bytes, be literally prepended to the PBI.
+.P
+The secondary configuration is a file with the same format as the primary
+configuration file. It will be inserted into the image after the primary
+configuration data and before the image data.
+.P
+It is traditional to use the primary configuration file for the
+.I RCW
+(Reset Configuration Word), and the secondary configuration file for any
+additional PBI commands. However, it is also possible to convert an existing PBI
+to the above format and \(lqchain\(rq additional data onto the end of the
+image. This may be especially useful for creating secure boot images.
+.
+.SS rkimage
+The primary configuration is the name of the processor to generate the image
+for. Valid values are:
+.RS
+.P
+.TS
+lb.
+px30
+rk3036
+rk3066
+rk3128
+rk3188
+rk322x
+rk3288
+rk3308
+rk3328
+rk3368
+rk3399
+rv1108
+rk3568
+.TE
+.RE
+.
+.SS sunxi_egon
+The primary configuration is the name to use for the device tree.
+.
+.SS ublimage
+The primary configuration is a file containing configuration commands, as
+documented in doc/\:README.ublimage of the U-Boot source.
+.
+.SS zynqimage and zynqmpimage
+For
+.BR zynqmpimage ,
+the primary configuration is a file containing the
+.I PMUFW
+(Power Management Unit Firmware).
+.B zynqimage
+does not use the primary configuration.
+.P
+For both image types, the secondary configuration is a file containinig
+initialization parameters, one per line. Each parameter has the form
+.RS
+.P
+.I address data
+.RE
+.P
+where
+.I address
+and
+.I data
+are hexadecimal integers. The boot ROM will write each
+.I data
+to
+.I address
+when loading the image. At most 256 parameters may be specified in this
+manner.
+.
+.SH BUGS
+Please report bugs to the
+.UR https://\:source\:.denx\:.de/\:u-boot/\:u-boot/\:issues
+U-Boot bug tracker
+.UE .
 .SH EXAMPLES
-
+.\" Reduce the width of the tab stops to something reasonable
+.ta T 1i
 List image information:
-.nf
-.B mkimage -l uImage
-.fi
+.RS
+.P
+.EX
+\fBmkimage \-l uImage
+.EE
+.RE
 .P
 Create legacy image with compressed PowerPC Linux kernel:
-.nf
-.B mkimage -A powerpc -O linux -T kernel -C gzip \\\\
-.br
-.B -a 0 -e 0 -n Linux -d vmlinux.gz uImage
-.fi
+.RS
+.P
+.EX
+\fBmkimage \-A powerpc \-O linux \-T kernel \-C gzip \\
+	\-a 0 \-e 0 \-n Linux \-d vmlinux.gz uImage
+.EE
+.RE
 .P
 Create FIT image with compressed PowerPC Linux kernel:
-.nf
-.B mkimage -f kernel.its kernel.itb
-.fi
+.RS
+.P
+.EX
+\fBmkimage \-f kernel.its kernel.itb
+.EE
+.RE
 .P
 Create FIT image with compressed kernel and sign it with keys in the
-/public/signing-keys directory. Add corresponding public keys into u-boot.dtb,
+/public/signing\-keys directory. Add corresponding public keys into u\-boot.dtb,
 skipping those for which keys cannot be found. Also add a comment.
-.nf
-.B mkimage -f kernel.its -k /public/signing-keys -K u-boot.dtb \\\\
-.br
-.B -c """Kernel 3.8 image for production devices""" kernel.itb
-.fi
-
+.RS
 .P
-Add public keys to u-boot.dtb without needing a FIT to sign. This will also
+.EX
+\fBmkimage \-f kernel.its \-k /public/signing\-keys \-K u\-boot.dtb \\
+	\-c \(dqKernel 3.8 image for production devices\(dq kernel.itb
+.EE
+.RE
+.P
+Add public keys to u\-boot.dtb without needing a FIT to sign. This will also
 create a FIT containing an images node with no data named unused.itb.
-.nf
-.B mkimage -f auto -d /dev/null -k /public/signing-keys -g dev \\\\
-.br
-.B -o sha256,rsa2048 -K u-boot.dtb unused.itb
-.fi
-
+.RS
+.P
+.EX
+\fBmkimage \-f auto \-d /dev/null \-k /public/signing\-keys \-g dev \\
+	\-o sha256,rsa2048 \-K u\-boot.dtb unused.itb
+.EE
+.RE
 .P
 Update an existing FIT image, signing it with additional keys.
-Add corresponding public keys into u-boot.dtb. This will resign all images
+Add corresponding public keys into u\-boot.dtb. This will resign all images
 with keys that are available in the new directory. Images that request signing
 with unavailable keys are skipped.
-.nf
-.B mkimage -F -k /secret/signing-keys -K u-boot.dtb \\\\
-.br
-.B -c """Kernel 3.8 image for production devices""" kernel.itb
-.fi
-
+.RS
+.P
+.EX
+\fBmkimage \-F \-k /secret/signing\-keys \-K u\-boot.dtb \\
+	\-c \(dqKernel 3.8 image for production devices\(dq kernel.itb
+.EE
+.RE
 .P
 Create a FIT image containing a kernel, using automatic mode. No .its file
 is required.
-.nf
-.B mkimage -f auto -A arm -O linux -T kernel -C none -a 43e00000 -e 0 \\\\
-.br
-.B -c """Kernel 4.4 image for production devices""" -d vmlinuz kernel.itb
-.fi
+.RS
+.P
+.EX
+\fBmkimage \-f auto \-A arm \-O linux \-T kernel \-C none \-a 43e00000 \-e 0 \\
+	\-c \(dqKernel 4.4 image for production devices\(dq \-d vmlinuz kernel.itb
+.EE
+.RE
 .P
 Create a FIT image containing a kernel and some device tree files, using
 automatic mode. No .its file is required.
-.nf
-.B mkimage -f auto -A arm -O linux -T kernel -C none -a 43e00000 -e 0 \\\\
-.br
-.B -c """Kernel 4.4 image for production devices""" -d vmlinuz \\\\
-.B -b /path/to/rk3288-firefly.dtb -b /path/to/rk3288-jerry.dtb kernel.itb
-.fi
+.RS
+.P
+.EX
+\fBmkimage \-f auto \-A arm \-O linux \-T kernel \-C none \-a 43e00000 \-e 0 \\
+	\-c \(dqKernel 4.4 image for production devices\(dq \-d vmlinuz \\
+	\-b /path/to/rk3288\-firefly.dtb \-b /path/to/rk3288\-jerry.dtb kernel.itb
+.EE
+.RE
 .P
 Create a FIT image containing a signed kernel, using automatic mode. No .its
 file is required.
-.nf
-.B mkimage -f auto -A arm -O linux -T kernel -C none -a 43e00000 -e 0 \\\\
-.br
-.B -d vmlinuz -k /secret/signing-keys -g dev -o sha256,rsa2048 kernel.itb
-.fi
-
-.SH HOMEPAGE
-http://www.denx.de/wiki/U-Boot/WebHome
-.PP
-.SH AUTHOR
-This manual page was written by Nobuhiro Iwamatsu <iwamatsu@nigauri.org>
-and Wolfgang Denk <wd@denx.de>. It was updated for image signing by
-Simon Glass <sjg@chromium.org>.
+.RS
+.P
+.EX
+\fBmkimage \-f auto \-A arm \-O linux \-T kernel \-C none \-a 43e00000 \-e 0 \\
+	\-d vmlinuz \-k /secret/signing\-keys \-g dev \-o sha256,rsa2048 kernel.itb
+.EE
+.RE
+.
+.SH SEE ALSO
+.BR dtc (1),
+.BR dumpimage (1),
+.BR openssl (1),
+the\~
+.UR https://\:u-boot\:.readthedocs\:.io/\:en/\:latest/\:index.html
+U-Boot documentation
+.UE
diff --git a/doc/usage/environment.rst b/doc/usage/environment.rst
index 28a8952..83543f6 100644
--- a/doc/usage/environment.rst
+++ b/doc/usage/environment.rst
@@ -204,7 +204,9 @@
     to work it must reside in writable memory, have
     sufficient padding on the end of it for u-boot to
     add the information it needs into it, and the memory
-    must be accessible by the kernel.
+    must be accessible by the kernel. This usage is strongly discouraged
+    however as it also stops U-Boot from ensuring the device tree starting
+    address is properly aligned and a misaligned tree will cause OS failures.
 
 fdtcontroladdr
     if set this is the address of the control flattened
@@ -240,14 +242,21 @@
     memory. In this case U-Boot will NOT COPY the
     ramdisk at all. This may be useful to reduce the
     boot time on your system, but requires that this
-    feature is supported by your Linux kernel.
+    feature is supported by your Linux kernel. This usage however requires
+    that the user ensure that there will be no overlap with other parts of the
+    image such as the Linux kernel BSS. It should not be enabled by default
+    and only done as part of optimizing a deployment.
 
 ipaddr
     IP address; needed for tftpboot command
 
 loadaddr
     Default load address for commands like "bootp",
-    "rarpboot", "tftpboot", "loadb" or "diskboot"
+    "rarpboot", "tftpboot", "loadb" or "diskboot".  Note that the optimal
+    default values here will vary between architectures.  On 32bit ARM for
+    example, some offset from start of memory is used as the Linux kernel
+    zImage has a self decompressor and it's best if we stay out of where that
+    will be working.
 
 loads_echo
     see CONFIG_LOADS_ECHO
@@ -390,7 +399,6 @@
 ================= ============== ================ ==============
 Image             File Name      RAM Address      Flash Location
 ================= ============== ================ ==============
-u-boot            u-boot         u-boot_addr_r    u-boot_addr
 Linux kernel      bootfile       kernel_addr_r    kernel_addr
 device tree blob  fdtfile        fdt_addr_r       fdt_addr
 ramdisk           ramdiskfile    ramdisk_addr_r   ramdisk_addr
diff --git a/drivers/clk/mtmips/Makefile b/drivers/clk/mtmips/Makefile
index 732e7f2..ee8b5af 100644
--- a/drivers/clk/mtmips/Makefile
+++ b/drivers/clk/mtmips/Makefile
@@ -1,4 +1,5 @@
 # SPDX-License-Identifier: GPL-2.0
 
 obj-$(CONFIG_SOC_MT7620) += clk-mt7620.o
+obj-$(CONFIG_SOC_MT7621) += clk-mt7621.o
 obj-$(CONFIG_SOC_MT7628) += clk-mt7628.o
diff --git a/drivers/clk/mtmips/clk-mt7621.c b/drivers/clk/mtmips/clk-mt7621.c
new file mode 100644
index 0000000..03363b7
--- /dev/null
+++ b/drivers/clk/mtmips/clk-mt7621.c
@@ -0,0 +1,288 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2022 MediaTek Inc. All rights reserved.
+ *
+ * Author: Weijie Gao <weijie.gao@mediatek.com>
+ */
+
+#include <clk-uclass.h>
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <regmap.h>
+#include <syscon.h>
+#include <dt-bindings/clock/mt7621-clk.h>
+#include <linux/io.h>
+#include <linux/bitops.h>
+#include <linux/bitfield.h>
+
+#define SYSC_MAP_SIZE			0x100
+#define MEMC_MAP_SIZE			0x1000
+
+/* SYSC */
+#define SYSCFG0_REG			0x10
+#define XTAL_MODE_SEL			GENMASK(8, 6)
+
+#define CLKCFG0_REG			0x2c
+#define CPU_CLK_SEL			GENMASK(31, 30)
+#define PERI_CLK_SEL			BIT(4)
+
+#define CLKCFG1_REG			0x30
+
+#define CUR_CLK_STS_REG			0x44
+#define CUR_CPU_FDIV			GENMASK(12, 8)
+#define CUR_CPU_FFRAC			GENMASK(4, 0)
+
+/* MEMC */
+#define MEMPLL1_REG			0x0604
+#define RG_MEPL_DIV2_SEL		GENMASK(2, 1)
+
+#define MEMPLL6_REG			0x0618
+#define MEMPLL18_REG			0x0648
+#define RG_MEPL_PREDIV			GENMASK(13, 12)
+#define RG_MEPL_FBDIV			GENMASK(10, 4)
+
+/* Fixed 500M clock */
+#define GMPLL_CLK			500000000
+
+struct mt7621_clk_priv {
+	void __iomem *sysc_base;
+	int cpu_clk;
+	int ddr_clk;
+	int sys_clk;
+	int xtal_clk;
+};
+
+enum mt7621_clk_src {
+	CLK_SRC_CPU,
+	CLK_SRC_DDR,
+	CLK_SRC_SYS,
+	CLK_SRC_XTAL,
+	CLK_SRC_PERI,
+	CLK_SRC_125M,
+	CLK_SRC_150M,
+	CLK_SRC_250M,
+	CLK_SRC_270M,
+
+	__CLK_SRC_MAX
+};
+
+struct mt7621_clk_map {
+	u32 cgbit;
+	enum mt7621_clk_src clksrc;
+};
+
+#define CLK_MAP(_id, _cg, _src) \
+	[_id] = { .cgbit = (_cg), .clksrc = (_src) }
+
+#define CLK_MAP_SRC(_id, _src) \
+	[_id] = { .cgbit = UINT32_MAX, .clksrc = (_src) }
+
+static const struct mt7621_clk_map mt7621_clk_mappings[] = {
+	CLK_MAP_SRC(MT7621_CLK_XTAL, CLK_SRC_XTAL),
+	CLK_MAP_SRC(MT7621_CLK_CPU, CLK_SRC_CPU),
+	CLK_MAP_SRC(MT7621_CLK_BUS, CLK_SRC_SYS),
+	CLK_MAP_SRC(MT7621_CLK_50M, CLK_SRC_PERI),
+	CLK_MAP_SRC(MT7621_CLK_125M, CLK_SRC_125M),
+	CLK_MAP_SRC(MT7621_CLK_150M, CLK_SRC_150M),
+	CLK_MAP_SRC(MT7621_CLK_250M, CLK_SRC_250M),
+	CLK_MAP_SRC(MT7621_CLK_270M, CLK_SRC_270M),
+
+	CLK_MAP(MT7621_CLK_HSDMA, 5, CLK_SRC_150M),
+	CLK_MAP(MT7621_CLK_FE, 6, CLK_SRC_250M),
+	CLK_MAP(MT7621_CLK_SP_DIVTX, 7, CLK_SRC_270M),
+	CLK_MAP(MT7621_CLK_TIMER, 8, CLK_SRC_PERI),
+	CLK_MAP(MT7621_CLK_PCM, 11, CLK_SRC_270M),
+	CLK_MAP(MT7621_CLK_PIO, 13, CLK_SRC_PERI),
+	CLK_MAP(MT7621_CLK_GDMA, 14, CLK_SRC_SYS),
+	CLK_MAP(MT7621_CLK_NAND, 15, CLK_SRC_125M),
+	CLK_MAP(MT7621_CLK_I2C, 16, CLK_SRC_PERI),
+	CLK_MAP(MT7621_CLK_I2S, 17, CLK_SRC_270M),
+	CLK_MAP(MT7621_CLK_SPI, 18, CLK_SRC_SYS),
+	CLK_MAP(MT7621_CLK_UART1, 19, CLK_SRC_PERI),
+	CLK_MAP(MT7621_CLK_UART2, 20, CLK_SRC_PERI),
+	CLK_MAP(MT7621_CLK_UART3, 21, CLK_SRC_PERI),
+	CLK_MAP(MT7621_CLK_ETH, 23, CLK_SRC_PERI),
+	CLK_MAP(MT7621_CLK_PCIE0, 24, CLK_SRC_125M),
+	CLK_MAP(MT7621_CLK_PCIE1, 25, CLK_SRC_125M),
+	CLK_MAP(MT7621_CLK_PCIE2, 26, CLK_SRC_125M),
+	CLK_MAP(MT7621_CLK_CRYPTO, 29, CLK_SRC_250M),
+	CLK_MAP(MT7621_CLK_SHXC, 30, CLK_SRC_PERI),
+
+	CLK_MAP_SRC(MT7621_CLK_MAX, __CLK_SRC_MAX),
+
+	CLK_MAP_SRC(MT7621_CLK_DDR, CLK_SRC_DDR),
+};
+
+static ulong mt7621_clk_get_rate(struct clk *clk)
+{
+	struct mt7621_clk_priv *priv = dev_get_priv(clk->dev);
+	u32 val;
+
+	switch (mt7621_clk_mappings[clk->id].clksrc) {
+	case CLK_SRC_CPU:
+		return priv->cpu_clk;
+	case CLK_SRC_DDR:
+		return priv->ddr_clk;
+	case CLK_SRC_SYS:
+		return priv->sys_clk;
+	case CLK_SRC_XTAL:
+		return priv->xtal_clk;
+	case CLK_SRC_PERI:
+		val = readl(priv->sysc_base + CLKCFG0_REG);
+		if (val & PERI_CLK_SEL)
+			return priv->xtal_clk;
+		else
+			return GMPLL_CLK / 10;
+	case CLK_SRC_125M:
+		return 125000000;
+	case CLK_SRC_150M:
+		return 150000000;
+	case CLK_SRC_250M:
+		return 250000000;
+	case CLK_SRC_270M:
+		return 270000000;
+	default:
+		return 0;
+	}
+}
+
+static int mt7621_clk_enable(struct clk *clk)
+{
+	struct mt7621_clk_priv *priv = dev_get_priv(clk->dev);
+	u32 cgbit;
+
+	cgbit = mt7621_clk_mappings[clk->id].cgbit;
+	if (cgbit == UINT32_MAX)
+		return -ENOSYS;
+
+	setbits_32(priv->sysc_base + CLKCFG1_REG, BIT(cgbit));
+
+	return 0;
+}
+
+static int mt7621_clk_disable(struct clk *clk)
+{
+	struct mt7621_clk_priv *priv = dev_get_priv(clk->dev);
+	u32 cgbit;
+
+	cgbit = mt7621_clk_mappings[clk->id].cgbit;
+	if (cgbit == UINT32_MAX)
+		return -ENOSYS;
+
+	clrbits_32(priv->sysc_base + CLKCFG1_REG, BIT(cgbit));
+
+	return 0;
+}
+
+static int mt7621_clk_request(struct clk *clk)
+{
+	if (clk->id >= ARRAY_SIZE(mt7621_clk_mappings))
+		return -EINVAL;
+	return 0;
+}
+
+const struct clk_ops mt7621_clk_ops = {
+	.request = mt7621_clk_request,
+	.enable = mt7621_clk_enable,
+	.disable = mt7621_clk_disable,
+	.get_rate = mt7621_clk_get_rate,
+};
+
+static void mt7621_get_clocks(struct mt7621_clk_priv *priv, struct regmap *memc)
+{
+	u32 bs, xtal_sel, clkcfg0, cur_clk, mempll, dividx, fb;
+	u32 xtal_clk, xtal_div, ffiv, ffrac, cpu_clk, ddr_clk;
+	static const u32 xtal_div_tbl[] = {0, 1, 2, 2};
+
+	bs = readl(priv->sysc_base + SYSCFG0_REG);
+	clkcfg0 = readl(priv->sysc_base + CLKCFG0_REG);
+	cur_clk = readl(priv->sysc_base + CUR_CLK_STS_REG);
+
+	xtal_sel = FIELD_GET(XTAL_MODE_SEL, bs);
+
+	if (xtal_sel <= 2)
+		xtal_clk = 20 * 1000 * 1000;
+	else if (xtal_sel <= 5)
+		xtal_clk = 40 * 1000 * 1000;
+	else
+		xtal_clk = 25 * 1000 * 1000;
+
+	switch (FIELD_GET(CPU_CLK_SEL, clkcfg0)) {
+	case 0:
+		cpu_clk = GMPLL_CLK;
+		break;
+	case 1:
+		regmap_read(memc, MEMPLL18_REG, &mempll);
+		dividx = FIELD_GET(RG_MEPL_PREDIV, mempll);
+		fb = FIELD_GET(RG_MEPL_FBDIV, mempll);
+		xtal_div = 1 << xtal_div_tbl[dividx];
+		cpu_clk = (fb + 1) * xtal_clk / xtal_div;
+		break;
+	default:
+		cpu_clk = xtal_clk;
+	}
+
+	ffiv = FIELD_GET(CUR_CPU_FDIV, cur_clk);
+	ffrac = FIELD_GET(CUR_CPU_FFRAC, cur_clk);
+	cpu_clk = cpu_clk / ffiv * ffrac;
+
+	regmap_read(memc, MEMPLL6_REG, &mempll);
+	dividx = FIELD_GET(RG_MEPL_PREDIV, mempll);
+	fb = FIELD_GET(RG_MEPL_FBDIV, mempll);
+	xtal_div = 1 << xtal_div_tbl[dividx];
+	ddr_clk = fb * xtal_clk / xtal_div;
+
+	regmap_read(memc, MEMPLL1_REG, &bs);
+	if (!FIELD_GET(RG_MEPL_DIV2_SEL, bs))
+		ddr_clk *= 2;
+
+	priv->cpu_clk = cpu_clk;
+	priv->sys_clk = cpu_clk / 4;
+	priv->ddr_clk = ddr_clk;
+	priv->xtal_clk = xtal_clk;
+}
+
+static int mt7621_clk_probe(struct udevice *dev)
+{
+	struct mt7621_clk_priv *priv = dev_get_priv(dev);
+	struct ofnode_phandle_args args;
+	struct udevice *pdev;
+	struct regmap *memc;
+	int ret;
+
+	pdev = dev_get_parent(dev);
+	if (!pdev)
+		return -ENODEV;
+
+	priv->sysc_base = dev_remap_addr(pdev);
+	if (!priv->sysc_base)
+		return -EINVAL;
+
+	/* get corresponding memc phandle */
+	ret = dev_read_phandle_with_args(dev, "mediatek,memc", NULL, 0, 0,
+					 &args);
+	if (ret)
+		return ret;
+
+	memc = syscon_node_to_regmap(args.node);
+	if (IS_ERR(memc))
+		return PTR_ERR(memc);
+
+	mt7621_get_clocks(priv, memc);
+
+	return 0;
+}
+
+static const struct udevice_id mt7621_clk_ids[] = {
+	{ .compatible = "mediatek,mt7621-clk" },
+	{ }
+};
+
+U_BOOT_DRIVER(mt7621_clk) = {
+	.name = "mt7621-clk",
+	.id = UCLASS_CLK,
+	.of_match = mt7621_clk_ids,
+	.probe = mt7621_clk_probe,
+	.priv_auto = sizeof(struct mt7621_clk_priv),
+	.ops = &mt7621_clk_ops,
+};
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 3c73a7f..aaa152f 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -553,7 +553,7 @@
 
 config MT7621_GPIO
 	bool "MediaTek MT7621 GPIO driver"
-	depends on DM_GPIO && SOC_MT7628
+	depends on DM_GPIO && (SOC_MT7621 || SOC_MT7628)
 	default y
 	help
 	  Say yes here to support MediaTek MT7621 compatible GPIOs.
diff --git a/drivers/i2c/stm32f7_i2c.c b/drivers/i2c/stm32f7_i2c.c
index c6ae65b..bf2a6c9 100644
--- a/drivers/i2c/stm32f7_i2c.c
+++ b/drivers/i2c/stm32f7_i2c.c
@@ -267,6 +267,10 @@
 	.fmp_clr_offset = 0x40,
 };
 
+static const struct stm32_i2c_data stm32mp13_data = {
+	.fmp_clr_offset = 0x4,
+};
+
 static int stm32_i2c_check_device_busy(struct stm32_i2c_priv *i2c_priv)
 {
 	struct stm32_i2c_regs *regs = i2c_priv->regs;
@@ -957,6 +961,7 @@
 static const struct udevice_id stm32_i2c_of_match[] = {
 	{ .compatible = "st,stm32f7-i2c", .data = (ulong)&stm32f7_data },
 	{ .compatible = "st,stm32mp15-i2c", .data = (ulong)&stm32mp15_data },
+	{ .compatible = "st,stm32mp13-i2c", .data = (ulong)&stm32mp13_data },
 	{}
 };
 
diff --git a/drivers/misc/stm32_rcc.c b/drivers/misc/stm32_rcc.c
index b816503..c1e5428 100644
--- a/drivers/misc/stm32_rcc.c
+++ b/drivers/misc/stm32_rcc.c
@@ -84,6 +84,7 @@
 	{.compatible = "st,stm32f746-rcc", .data = (ulong)&stm32_rcc_clk_f7 },
 	{.compatible = "st,stm32h743-rcc", .data = (ulong)&stm32_rcc_clk_h7 },
 	{.compatible = "st,stm32mp1-rcc", .data = (ulong)&stm32_rcc_clk_mp1 },
+	{.compatible = "st,stm32mp1-rcc-secure", .data = (ulong)&stm32_rcc_clk_mp1 },
 	{.compatible = "st,stm32mp13-rcc", .data = (ulong)&stm32_rcc_clk_mp13 },
 	{ }
 };
diff --git a/drivers/mmc/mtk-sd.c b/drivers/mmc/mtk-sd.c
index 97182ff..e61e8cf 100644
--- a/drivers/mmc/mtk-sd.c
+++ b/drivers/mmc/mtk-sd.c
@@ -1761,6 +1761,18 @@
 	.default_pad_dly = true,
 };
 
+static const struct msdc_compatible mt7621_compat = {
+	.clk_div_bits = 8,
+	.pad_tune0 = false,
+	.async_fifo = true,
+	.data_tune = true,
+	.busy_check = false,
+	.stop_clk_fix = false,
+	.enhance_rx = false,
+	.builtin_pad_ctrl = true,
+	.default_pad_dly = true,
+};
+
 static const struct msdc_compatible mt7622_compat = {
 	.clk_div_bits = 12,
 	.pad_tune0 = true,
@@ -1809,6 +1821,7 @@
 
 static const struct udevice_id msdc_ids[] = {
 	{ .compatible = "mediatek,mt7620-mmc", .data = (ulong)&mt7620_compat },
+	{ .compatible = "mediatek,mt7621-mmc", .data = (ulong)&mt7621_compat },
 	{ .compatible = "mediatek,mt7622-mmc", .data = (ulong)&mt7622_compat },
 	{ .compatible = "mediatek,mt7623-mmc", .data = (ulong)&mt7623_compat },
 	{ .compatible = "mediatek,mt8512-mmc", .data = (ulong)&mt8512_compat },
diff --git a/drivers/mmc/stm32_sdmmc2.c b/drivers/mmc/stm32_sdmmc2.c
index 81b0760..bfce8a2 100644
--- a/drivers/mmc/stm32_sdmmc2.c
+++ b/drivers/mmc/stm32_sdmmc2.c
@@ -30,9 +30,6 @@
 struct stm32_sdmmc2_plat {
 	struct mmc_config cfg;
 	struct mmc mmc;
-};
-
-struct stm32_sdmmc2_priv {
 	fdt_addr_t base;
 	struct clk clk;
 	struct reset_ctl reset_ctl;
@@ -208,7 +205,7 @@
 				    struct mmc_data *data,
 				    struct stm32_sdmmc2_ctx *ctx)
 {
-	struct stm32_sdmmc2_priv *priv = dev_get_priv(dev);
+	struct stm32_sdmmc2_plat *plat = dev_get_plat(dev);
 	u32 data_ctrl, idmabase0;
 
 	/* Configure the SDMMC DPSM (Data Path State Machine) */
@@ -224,10 +221,10 @@
 	}
 
 	/* Set the SDMMC DataLength value */
-	writel(ctx->data_length, priv->base + SDMMC_DLEN);
+	writel(ctx->data_length, plat->base + SDMMC_DLEN);
 
 	/* Write to SDMMC DCTRL */
-	writel(data_ctrl, priv->base + SDMMC_DCTRL);
+	writel(data_ctrl, plat->base + SDMMC_DCTRL);
 
 	/* Cache align */
 	ctx->cache_start = rounddown(idmabase0, ARCH_DMA_MINALIGN);
@@ -242,19 +239,19 @@
 	flush_dcache_range(ctx->cache_start, ctx->cache_end);
 
 	/* Enable internal DMA */
-	writel(idmabase0, priv->base + SDMMC_IDMABASE0);
-	writel(SDMMC_IDMACTRL_IDMAEN, priv->base + SDMMC_IDMACTRL);
+	writel(idmabase0, plat->base + SDMMC_IDMABASE0);
+	writel(SDMMC_IDMACTRL_IDMAEN, plat->base + SDMMC_IDMACTRL);
 }
 
 static void stm32_sdmmc2_start_cmd(struct udevice *dev,
 				   struct mmc_cmd *cmd, u32 cmd_param,
 				   struct stm32_sdmmc2_ctx *ctx)
 {
-	struct stm32_sdmmc2_priv *priv = dev_get_priv(dev);
+	struct stm32_sdmmc2_plat *plat = dev_get_plat(dev);
 	u32 timeout = 0;
 
-	if (readl(priv->base + SDMMC_CMD) & SDMMC_CMD_CPSMEN)
-		writel(0, priv->base + SDMMC_CMD);
+	if (readl(plat->base + SDMMC_CMD) & SDMMC_CMD_CPSMEN)
+		writel(0, plat->base + SDMMC_CMD);
 
 	cmd_param |= cmd->cmdidx | SDMMC_CMD_CPSMEN;
 	if (cmd->resp_type & MMC_RSP_PRESENT) {
@@ -277,30 +274,30 @@
 	if (ctx->data_length) {
 		timeout = SDMMC_CMD_TIMEOUT;
 	} else {
-		writel(0, priv->base + SDMMC_DCTRL);
+		writel(0, plat->base + SDMMC_DCTRL);
 
 		if (cmd->resp_type & MMC_RSP_BUSY)
 			timeout = SDMMC_CMD_TIMEOUT;
 	}
 
 	/* Set the SDMMC Data TimeOut value */
-	writel(timeout, priv->base + SDMMC_DTIMER);
+	writel(timeout, plat->base + SDMMC_DTIMER);
 
 	/* Clear flags */
-	writel(SDMMC_ICR_STATIC_FLAGS, priv->base + SDMMC_ICR);
+	writel(SDMMC_ICR_STATIC_FLAGS, plat->base + SDMMC_ICR);
 
 	/* Set SDMMC argument value */
-	writel(cmd->cmdarg, priv->base + SDMMC_ARG);
+	writel(cmd->cmdarg, plat->base + SDMMC_ARG);
 
 	/* Set SDMMC command parameters */
-	writel(cmd_param, priv->base + SDMMC_CMD);
+	writel(cmd_param, plat->base + SDMMC_CMD);
 }
 
 static int stm32_sdmmc2_end_cmd(struct udevice *dev,
 				struct mmc_cmd *cmd,
 				struct stm32_sdmmc2_ctx *ctx)
 {
-	struct stm32_sdmmc2_priv *priv = dev_get_priv(dev);
+	struct stm32_sdmmc2_plat *plat = dev_get_plat(dev);
 	u32 mask = SDMMC_STA_CTIMEOUT;
 	u32 status;
 	int ret;
@@ -314,7 +311,7 @@
 	}
 
 	/* Polling status register */
-	ret = readl_poll_timeout(priv->base + SDMMC_STA, status, status & mask,
+	ret = readl_poll_timeout(plat->base + SDMMC_STA, status, status & mask,
 				 10000);
 
 	if (ret < 0) {
@@ -339,11 +336,11 @@
 	}
 
 	if (status & SDMMC_STA_CMDREND && cmd->resp_type & MMC_RSP_PRESENT) {
-		cmd->response[0] = readl(priv->base + SDMMC_RESP1);
+		cmd->response[0] = readl(plat->base + SDMMC_RESP1);
 		if (cmd->resp_type & MMC_RSP_136) {
-			cmd->response[1] = readl(priv->base + SDMMC_RESP2);
-			cmd->response[2] = readl(priv->base + SDMMC_RESP3);
-			cmd->response[3] = readl(priv->base + SDMMC_RESP4);
+			cmd->response[1] = readl(plat->base + SDMMC_RESP2);
+			cmd->response[2] = readl(plat->base + SDMMC_RESP3);
+			cmd->response[3] = readl(plat->base + SDMMC_RESP4);
 		}
 
 		/* Wait for BUSYD0END flag if busy status is detected */
@@ -352,7 +349,7 @@
 			mask = SDMMC_STA_DTIMEOUT | SDMMC_STA_BUSYD0END;
 
 			/* Polling status register */
-			ret = readl_poll_timeout(priv->base + SDMMC_STA,
+			ret = readl_poll_timeout(plat->base + SDMMC_STA,
 						 status, status & mask,
 						 SDMMC_BUSYD0END_TIMEOUT_US);
 
@@ -380,7 +377,7 @@
 				 struct mmc_data *data,
 				 struct stm32_sdmmc2_ctx *ctx)
 {
-	struct stm32_sdmmc2_priv *priv = dev_get_priv(dev);
+	struct stm32_sdmmc2_plat *plat = dev_get_plat(dev);
 	u32 mask = SDMMC_STA_DCRCFAIL | SDMMC_STA_DTIMEOUT |
 		   SDMMC_STA_IDMATE | SDMMC_STA_DATAEND;
 	u32 status;
@@ -390,9 +387,9 @@
 	else
 		mask |= SDMMC_STA_TXUNDERR;
 
-	status = readl(priv->base + SDMMC_STA);
+	status = readl(plat->base + SDMMC_STA);
 	while (!(status & mask))
-		status = readl(priv->base + SDMMC_STA);
+		status = readl(plat->base + SDMMC_STA);
 
 	/*
 	 * Need invalidate the dcache again to avoid any
@@ -404,7 +401,7 @@
 	if (status & SDMMC_STA_DCRCFAIL) {
 		dev_dbg(dev, "error SDMMC_STA_DCRCFAIL (0x%x) for cmd %d\n",
 			status, cmd->cmdidx);
-		if (readl(priv->base + SDMMC_DCOUNT))
+		if (readl(plat->base + SDMMC_DCOUNT))
 			ctx->dpsm_abort = true;
 		return -EILSEQ;
 	}
@@ -443,7 +440,7 @@
 static int stm32_sdmmc2_send_cmd(struct udevice *dev, struct mmc_cmd *cmd,
 				 struct mmc_data *data)
 {
-	struct stm32_sdmmc2_priv *priv = dev_get_priv(dev);
+	struct stm32_sdmmc2_plat *plat = dev_get_plat(dev);
 	struct stm32_sdmmc2_ctx ctx;
 	u32 cmdat = data ? SDMMC_CMD_CMDTRANS : 0;
 	int ret, retry = 3;
@@ -470,9 +467,9 @@
 		ret = stm32_sdmmc2_end_data(dev, cmd, data, &ctx);
 
 	/* Clear flags */
-	writel(SDMMC_ICR_STATIC_FLAGS, priv->base + SDMMC_ICR);
+	writel(SDMMC_ICR_STATIC_FLAGS, plat->base + SDMMC_ICR);
 	if (data)
-		writel(0x0, priv->base + SDMMC_IDMACTRL);
+		writel(0x0, plat->base + SDMMC_IDMACTRL);
 
 	/*
 	 * To stop Data Path State Machine, a stop_transmission command
@@ -493,7 +490,7 @@
 				       SDMMC_CMD_CMDSTOP, &ctx);
 		stm32_sdmmc2_end_cmd(dev, &stop_cmd, &ctx);
 
-		writel(SDMMC_ICR_STATIC_FLAGS, priv->base + SDMMC_ICR);
+		writel(SDMMC_ICR_STATIC_FLAGS, plat->base + SDMMC_ICR);
 	}
 
 	if ((ret != -ETIMEDOUT) && (ret != 0) && retry) {
@@ -512,17 +509,17 @@
  * This will reset the SDMMC to the reset state and the CPSM and DPSM
  * to the Idle state. SDMMC is disabled, Signals Hiz.
  */
-static void stm32_sdmmc2_reset(struct stm32_sdmmc2_priv *priv)
+static void stm32_sdmmc2_reset(struct stm32_sdmmc2_plat *plat)
 {
-	if (reset_valid(&priv->reset_ctl)) {
+	if (reset_valid(&plat->reset_ctl)) {
 		/* Reset */
-		reset_assert(&priv->reset_ctl);
+		reset_assert(&plat->reset_ctl);
 		udelay(2);
-		reset_deassert(&priv->reset_ctl);
+		reset_deassert(&plat->reset_ctl);
 	}
 
 	/* init the needed SDMMC register after reset */
-	writel(priv->pwr_reg_msk, priv->base + SDMMC_POWER);
+	writel(plat->pwr_reg_msk, plat->base + SDMMC_POWER);
 }
 
 /*
@@ -531,13 +528,13 @@
  * SDMMC_CMD and SDMMC_CK are driven low, to prevent the card from being
  * supplied through the signal lines.
  */
-static void stm32_sdmmc2_pwrcycle(struct stm32_sdmmc2_priv *priv)
+static void stm32_sdmmc2_pwrcycle(struct stm32_sdmmc2_plat *plat)
 {
-	if ((readl(priv->base + SDMMC_POWER) & SDMMC_POWER_PWRCTRL_MASK) ==
+	if ((readl(plat->base + SDMMC_POWER) & SDMMC_POWER_PWRCTRL_MASK) ==
 	    SDMMC_POWER_PWRCTRL_CYCLE)
 		return;
 
-	stm32_sdmmc2_reset(priv);
+	stm32_sdmmc2_reset(plat);
 }
 
 /*
@@ -546,10 +543,10 @@
  * Reset => Power-Cycle => Power-Off => Power
  *    PWRCTRL=10     PWCTRL=00    PWCTRL=11
  */
-static void stm32_sdmmc2_pwron(struct stm32_sdmmc2_priv *priv)
+static void stm32_sdmmc2_pwron(struct stm32_sdmmc2_plat *plat)
 {
 	u32 pwrctrl =
-		readl(priv->base + SDMMC_POWER) &  SDMMC_POWER_PWRCTRL_MASK;
+		readl(plat->base + SDMMC_POWER) &  SDMMC_POWER_PWRCTRL_MASK;
 
 	if (pwrctrl == SDMMC_POWER_PWRCTRL_ON)
 		return;
@@ -558,21 +555,21 @@
 	 * it is the reset state here = the only managed by the driver
 	 */
 	if (pwrctrl == SDMMC_POWER_PWRCTRL_OFF) {
-		writel(SDMMC_POWER_PWRCTRL_CYCLE | priv->pwr_reg_msk,
-		       priv->base + SDMMC_POWER);
+		writel(SDMMC_POWER_PWRCTRL_CYCLE | plat->pwr_reg_msk,
+		       plat->base + SDMMC_POWER);
 	}
 
 	/*
 	 * the remaining case is SDMMC_POWER_PWRCTRL_CYCLE
 	 * switch to Power-Off state: SDMCC disable, signals drive 1
 	 */
-	writel(SDMMC_POWER_PWRCTRL_OFF | priv->pwr_reg_msk,
-	       priv->base + SDMMC_POWER);
+	writel(SDMMC_POWER_PWRCTRL_OFF | plat->pwr_reg_msk,
+	       plat->base + SDMMC_POWER);
 
 	/* After the 1ms delay set the SDMMC to power-on */
 	mdelay(1);
-	writel(SDMMC_POWER_PWRCTRL_ON | priv->pwr_reg_msk,
-	       priv->base + SDMMC_POWER);
+	writel(SDMMC_POWER_PWRCTRL_ON | plat->pwr_reg_msk,
+	       plat->base + SDMMC_POWER);
 
 	/* during the first 74 SDMMC_CK cycles the SDMMC is still disabled. */
 }
@@ -581,18 +578,18 @@
 static int stm32_sdmmc2_set_ios(struct udevice *dev)
 {
 	struct mmc *mmc = mmc_get_mmc_dev(dev);
-	struct stm32_sdmmc2_priv *priv = dev_get_priv(dev);
+	struct stm32_sdmmc2_plat *plat = dev_get_plat(dev);
 	u32 desired = mmc->clock;
-	u32 sys_clock = clk_get_rate(&priv->clk);
+	u32 sys_clock = clk_get_rate(&plat->clk);
 	u32 clk = 0;
 
 	dev_dbg(dev, "bus_with = %d, clock = %d\n",
 		mmc->bus_width, mmc->clock);
 
 	if (mmc->clk_disable)
-		stm32_sdmmc2_pwrcycle(priv);
+		stm32_sdmmc2_pwrcycle(plat);
 	else
-		stm32_sdmmc2_pwron(priv);
+		stm32_sdmmc2_pwron(plat);
 
 	/*
 	 * clk_div = 0 => command and data generated on SDMMCCLK falling edge
@@ -602,7 +599,7 @@
 	 * SDMMCCLK falling edge
 	 */
 	if (desired && ((sys_clock > desired) ||
-			IS_RISING_EDGE(priv->clk_reg_msk))) {
+			IS_RISING_EDGE(plat->clk_reg_msk))) {
 		clk = DIV_ROUND_UP(sys_clock, 2 * desired);
 		if (clk > SDMMC_CLKCR_CLKDIV_MAX)
 			clk = SDMMC_CLKCR_CLKDIV_MAX;
@@ -613,30 +610,30 @@
 	if (mmc->bus_width == 8)
 		clk |= SDMMC_CLKCR_WIDBUS_8;
 
-	writel(clk | priv->clk_reg_msk | SDMMC_CLKCR_HWFC_EN,
-	       priv->base + SDMMC_CLKCR);
+	writel(clk | plat->clk_reg_msk | SDMMC_CLKCR_HWFC_EN,
+	       plat->base + SDMMC_CLKCR);
 
 	return 0;
 }
 
 static int stm32_sdmmc2_getcd(struct udevice *dev)
 {
-	struct stm32_sdmmc2_priv *priv = dev_get_priv(dev);
+	struct stm32_sdmmc2_plat *plat = dev_get_plat(dev);
 
 	dev_dbg(dev, "%s called\n", __func__);
 
-	if (dm_gpio_is_valid(&priv->cd_gpio))
-		return dm_gpio_get_value(&priv->cd_gpio);
+	if (dm_gpio_is_valid(&plat->cd_gpio))
+		return dm_gpio_get_value(&plat->cd_gpio);
 
 	return 1;
 }
 
 static int stm32_sdmmc2_host_power_cycle(struct udevice *dev)
 {
-	struct stm32_sdmmc2_priv *priv = dev_get_priv(dev);
+	struct stm32_sdmmc2_plat *plat = dev_get_plat(dev);
 
-	writel(SDMMC_POWER_PWRCTRL_CYCLE | priv->pwr_reg_msk,
-	       priv->base + SDMMC_POWER);
+	writel(SDMMC_POWER_PWRCTRL_CYCLE | plat->pwr_reg_msk,
+	       plat->base + SDMMC_POWER);
 
 	return 0;
 }
@@ -648,20 +645,55 @@
 	.host_power_cycle = stm32_sdmmc2_host_power_cycle,
 };
 
+static int stm32_sdmmc2_of_to_plat(struct udevice *dev)
+{
+	struct stm32_sdmmc2_plat *plat = dev_get_plat(dev);
+	struct mmc_config *cfg = &plat->cfg;
+	int ret;
+
+	plat->base = dev_read_addr(dev);
+	if (plat->base == FDT_ADDR_T_NONE)
+		return -EINVAL;
+
+	if (dev_read_bool(dev, "st,neg-edge"))
+		plat->clk_reg_msk |= SDMMC_CLKCR_NEGEDGE;
+	if (dev_read_bool(dev, "st,sig-dir"))
+		plat->pwr_reg_msk |= SDMMC_POWER_DIRPOL;
+	if (dev_read_bool(dev, "st,use-ckin"))
+		plat->clk_reg_msk |= SDMMC_CLKCR_SELCLKRX_CKIN;
+
+	cfg->f_min = 400000;
+	cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195;
+	cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT;
+	cfg->name = "STM32 SD/MMC";
+	cfg->host_caps = 0;
+	cfg->f_max = 52000000;
+	ret = mmc_of_parse(dev, cfg);
+	if (ret)
+		return ret;
+
+	ret = clk_get_by_index(dev, 0, &plat->clk);
+	if (ret)
+		return ret;
+
+	ret = reset_get_by_index(dev, 0, &plat->reset_ctl);
+	if (ret)
+		dev_dbg(dev, "No reset provided\n");
+
+	gpio_request_by_name(dev, "cd-gpios", 0, &plat->cd_gpio,
+			     GPIOD_IS_IN);
+
+	return 0;
+}
+
 static int stm32_sdmmc2_probe_level_translator(struct udevice *dev)
 {
-	struct stm32_sdmmc2_priv *priv = dev_get_priv(dev);
+	struct stm32_sdmmc2_plat *plat = dev_get_plat(dev);
 	struct gpio_desc cmd_gpio;
 	struct gpio_desc ck_gpio;
 	struct gpio_desc ckin_gpio;
 	int clk_hi, clk_lo, ret;
 
-	/*
-	 * Assume the level translator is present if st,use-ckin is set.
-	 * This is to cater for DTs which do not implement this test.
-	 */
-	priv->clk_reg_msk |= SDMMC_CLKCR_SELCLKRX_CKIN;
-
 	ret = gpio_request_by_name(dev, "st,cmd-gpios", 0, &cmd_gpio,
 				   GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE);
 	if (ret)
@@ -694,7 +726,7 @@
 
 	/* Level translator is present if CK signal is propagated to CKIN */
 	if (!clk_hi || clk_lo)
-		priv->clk_reg_msk &= ~SDMMC_CLKCR_SELCLKRX_CKIN;
+		plat->clk_reg_msk &= ~SDMMC_CLKCR_SELCLKRX_CKIN;
 
 	dm_gpio_free(dev, &ckin_gpio);
 
@@ -712,58 +744,26 @@
 {
 	struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
 	struct stm32_sdmmc2_plat *plat = dev_get_plat(dev);
-	struct stm32_sdmmc2_priv *priv = dev_get_priv(dev);
-	struct mmc_config *cfg = &plat->cfg;
 	int ret;
 
-	priv->base = dev_read_addr(dev);
-	if (priv->base == FDT_ADDR_T_NONE)
-		return -EINVAL;
-
-	if (dev_read_bool(dev, "st,neg-edge"))
-		priv->clk_reg_msk |= SDMMC_CLKCR_NEGEDGE;
-	if (dev_read_bool(dev, "st,sig-dir"))
-		priv->pwr_reg_msk |= SDMMC_POWER_DIRPOL;
-	if (dev_read_bool(dev, "st,use-ckin"))
-		stm32_sdmmc2_probe_level_translator(dev);
-
-	ret = clk_get_by_index(dev, 0, &priv->clk);
-	if (ret)
+	ret = clk_enable(&plat->clk);
+	if (ret) {
+		clk_free(&plat->clk);
 		return ret;
-
-	ret = clk_enable(&priv->clk);
-	if (ret)
-		goto clk_free;
-
-	ret = reset_get_by_index(dev, 0, &priv->reset_ctl);
-	if (ret)
-		dev_dbg(dev, "No reset provided\n");
-
-	gpio_request_by_name(dev, "cd-gpios", 0, &priv->cd_gpio,
-			     GPIOD_IS_IN);
-
-	cfg->f_min = 400000;
-	cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195;
-	cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT;
-	cfg->name = "STM32 SD/MMC";
-
-	cfg->host_caps = 0;
-	cfg->f_max = 52000000;
-	mmc_of_parse(dev, cfg);
+	}
 
 	upriv->mmc = &plat->mmc;
 
-	/* SDMMC init */
-	stm32_sdmmc2_reset(priv);
-	return 0;
+	if (plat->clk_reg_msk & SDMMC_CLKCR_SELCLKRX_CKIN)
+		stm32_sdmmc2_probe_level_translator(dev);
 
-clk_free:
-	clk_free(&priv->clk);
+	/* SDMMC init */
+	stm32_sdmmc2_reset(plat);
 
-	return ret;
+	return 0;
 }
 
-static int stm32_sdmmc_bind(struct udevice *dev)
+static int stm32_sdmmc2_bind(struct udevice *dev)
 {
 	struct stm32_sdmmc2_plat *plat = dev_get_plat(dev);
 
@@ -781,7 +781,7 @@
 	.of_match = stm32_sdmmc2_ids,
 	.ops = &stm32_sdmmc2_ops,
 	.probe = stm32_sdmmc2_probe,
-	.bind = stm32_sdmmc_bind,
-	.priv_auto	= sizeof(struct stm32_sdmmc2_priv),
+	.bind = stm32_sdmmc2_bind,
+	.of_to_plat = stm32_sdmmc2_of_to_plat,
 	.plat_auto	= sizeof(struct stm32_sdmmc2_plat),
 };
diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig
index 190300f..ce67d1a 100644
--- a/drivers/mtd/nand/raw/Kconfig
+++ b/drivers/mtd/nand/raw/Kconfig
@@ -526,12 +526,25 @@
 	help
 	  Enables support for NAND Flash chips on Tegra SoCs platforms.
 
+config NAND_MT7621
+	bool "Support for MediaTek MT7621 NAND flash controller"
+	depends on SOC_MT7621
+	select SYS_NAND_SELF_INIT
+	select SPL_SYS_NAND_SELF_INIT
+	imply CMD_NAND
+	help
+	  This enables NAND driver for the NAND flash controller on MediaTek
+	  MT7621 platform.
+	  The controller supports 4~12 bits correction per 512 bytes with a
+	  maximum 4KB page size.
+
 comment "Generic NAND options"
 
 config SYS_NAND_BLOCK_SIZE
 	hex "NAND chip eraseblock size"
 	depends on ARCH_SUNXI || SPL_NAND_SUPPORT || TPL_NAND_SUPPORT
-	depends on !NAND_MXS && !NAND_DENALI_DT && !NAND_LPC32XX_MLC && !NAND_FSL_IFC
+	depends on !NAND_MXS && !NAND_DENALI_DT && !NAND_LPC32XX_MLC && \
+		!NAND_FSL_IFC && !NAND_MT7621
 	help
 	  Number of data bytes in one eraseblock for the NAND chip on the
 	  board. This is the multiple of NAND_PAGE_SIZE and the number of
@@ -556,7 +569,7 @@
 	depends on ARCH_SUNXI || NAND_OMAP_GPMC || NAND_LPC32XX_SLC || \
 		SPL_NAND_SIMPLE || (NAND_MXC && SPL_NAND_SUPPORT) || \
 		(NAND_ATMEL && SPL_NAND_SUPPORT) || SPL_GENERATE_ATMEL_PMECC_HEADER
-	depends on !NAND_MXS && !NAND_DENALI_DT && !NAND_LPC32XX_MLC
+	depends on !NAND_MXS && !NAND_DENALI_DT && !NAND_LPC32XX_MLC && !NAND_MT7621
 	help
 	  Number of data bytes in one page for the NAND chip on the
 	  board, not including the OOB area.
diff --git a/drivers/mtd/nand/raw/Makefile b/drivers/mtd/nand/raw/Makefile
index e3f6b90..f278f31 100644
--- a/drivers/mtd/nand/raw/Makefile
+++ b/drivers/mtd/nand/raw/Makefile
@@ -72,6 +72,7 @@
 obj-$(CONFIG_NAND_STM32_FMC2) += stm32_fmc2_nand.o
 obj-$(CONFIG_CORTINA_NAND) += cortina_nand.o
 obj-$(CONFIG_ROCKCHIP_NAND) += rockchip_nfc.o
+obj-$(CONFIG_NAND_MT7621) += mt7621_nand.o
 
 else  # minimal SPL drivers
 
@@ -80,5 +81,6 @@
 obj-$(CONFIG_NAND_MXC) += mxc_nand_spl.o
 obj-$(CONFIG_NAND_MXS) += mxs_nand_spl.o mxs_nand.o
 obj-$(CONFIG_NAND_SUNXI) += sunxi_nand_spl.o
+obj-$(CONFIG_NAND_MT7621) += mt7621_nand_spl.o mt7621_nand.o
 
 endif # drivers
diff --git a/drivers/mtd/nand/raw/mt7621_nand.c b/drivers/mtd/nand/raw/mt7621_nand.c
new file mode 100644
index 0000000..2fd8934
--- /dev/null
+++ b/drivers/mtd/nand/raw/mt7621_nand.c
@@ -0,0 +1,1205 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2022 MediaTek Inc. All rights reserved.
+ *
+ * Author: Weijie Gao <weijie.gao@mediatek.com>
+ */
+
+#include <log.h>
+#include <nand.h>
+#include <malloc.h>
+#include <asm/addrspace.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/sizes.h>
+#include <linux/bitops.h>
+#include <linux/bitfield.h>
+#include "mt7621_nand.h"
+
+/* NFI core registers */
+#define NFI_CNFG			0x000
+#define   CNFG_OP_MODE			GENMASK(14, 12)
+#define     CNFG_OP_CUSTOM		6
+#define   CNFG_AUTO_FMT_EN		BIT(9)
+#define   CNFG_HW_ECC_EN		BIT(8)
+#define   CNFG_BYTE_RW			BIT(6)
+#define   CNFG_READ_MODE		BIT(1)
+
+#define NFI_PAGEFMT			0x004
+#define   PAGEFMT_FDM_ECC		GENMASK(15, 12)
+#define   PAGEFMT_FDM			GENMASK(11, 8)
+#define   PAGEFMT_SPARE			GENMASK(5, 4)
+#define   PAGEFMT_PAGE			GENMASK(1, 0)
+
+#define NFI_CON				0x008
+#define   CON_NFI_SEC			GENMASK(15, 12)
+#define   CON_NFI_BWR			BIT(9)
+#define   CON_NFI_BRD			BIT(8)
+#define   CON_NFI_RST			BIT(1)
+#define   CON_FIFO_FLUSH		BIT(0)
+
+#define NFI_ACCCON			0x00c
+#define   ACCCON_POECS			GENMASK(31, 28)
+#define   ACCCON_POECS_DEF		3
+#define   ACCCON_PRECS			GENMASK(27, 22)
+#define   ACCCON_PRECS_DEF		3
+#define   ACCCON_C2R			GENMASK(21, 16)
+#define   ACCCON_C2R_DEF		7
+#define   ACCCON_W2R			GENMASK(15, 12)
+#define   ACCCON_W2R_DEF		7
+#define   ACCCON_WH			GENMASK(11, 8)
+#define   ACCCON_WH_DEF			15
+#define   ACCCON_WST			GENMASK(7, 4)
+#define   ACCCON_WST_DEF		15
+#define   ACCCON_WST_MIN		3
+#define   ACCCON_RLT			GENMASK(3, 0)
+#define   ACCCON_RLT_DEF		15
+#define   ACCCON_RLT_MIN		3
+
+#define NFI_CMD				0x020
+
+#define NFI_ADDRNOB			0x030
+#define   ADDR_ROW_NOB			GENMASK(6, 4)
+#define   ADDR_COL_NOB			GENMASK(2, 0)
+
+#define NFI_COLADDR			0x034
+#define NFI_ROWADDR			0x038
+
+#define NFI_STRDATA			0x040
+#define   STR_DATA			BIT(0)
+
+#define NFI_CNRNB			0x044
+#define   CB2R_TIME			GENMASK(7, 4)
+#define   STR_CNRNB			BIT(0)
+
+#define NFI_DATAW			0x050
+#define NFI_DATAR			0x054
+
+#define NFI_PIO_DIRDY			0x058
+#define   PIO_DIRDY			BIT(0)
+
+#define NFI_STA				0x060
+#define   STA_NFI_FSM			GENMASK(19, 16)
+#define     STA_FSM_CUSTOM_DATA		14
+#define   STA_BUSY			BIT(8)
+#define   STA_ADDR			BIT(1)
+#define   STA_CMD			BIT(0)
+
+#define NFI_ADDRCNTR			0x070
+#define   SEC_CNTR			GENMASK(15, 12)
+#define   SEC_ADDR			GENMASK(9, 0)
+
+#define NFI_CSEL			0x090
+#define   CSEL				GENMASK(1, 0)
+
+#define NFI_FDM0L			0x0a0
+#define NFI_FDML(n)			(0x0a0 + ((n) << 3))
+
+#define NFI_FDM0M			0x0a4
+#define NFI_FDMM(n)			(0x0a4 + ((n) << 3))
+
+#define NFI_MASTER_STA			0x210
+#define   MAS_ADDR			GENMASK(11, 9)
+#define   MAS_RD			GENMASK(8, 6)
+#define   MAS_WR			GENMASK(5, 3)
+#define   MAS_RDDLY			GENMASK(2, 0)
+
+/* ECC engine registers */
+#define ECC_ENCCON			0x000
+#define   ENC_EN			BIT(0)
+
+#define ECC_ENCCNFG			0x004
+#define   ENC_CNFG_MSG			GENMASK(28, 16)
+#define   ENC_MODE			GENMASK(5, 4)
+#define     ENC_MODE_NFI		1
+#define   ENC_TNUM			GENMASK(2, 0)
+
+#define ECC_ENCIDLE			0x00c
+#define   ENC_IDLE			BIT(0)
+
+#define ECC_DECCON			0x100
+#define   DEC_EN			BIT(0)
+
+#define ECC_DECCNFG			0x104
+#define   DEC_EMPTY_EN			BIT(31)
+#define   DEC_CS			GENMASK(28, 16)
+#define   DEC_CON			GENMASK(13, 12)
+#define     DEC_CON_EL			2
+#define   DEC_MODE			GENMASK(5, 4)
+#define     DEC_MODE_NFI		1
+#define   DEC_TNUM			GENMASK(2, 0)
+
+#define ECC_DECIDLE			0x10c
+#define   DEC_IDLE			BIT(1)
+
+#define ECC_DECENUM			0x114
+#define   ERRNUM_S			2
+#define   ERRNUM_M			GENMASK(3, 0)
+
+#define ECC_DECDONE			0x118
+#define   DEC_DONE7			BIT(7)
+#define   DEC_DONE6			BIT(6)
+#define   DEC_DONE5			BIT(5)
+#define   DEC_DONE4			BIT(4)
+#define   DEC_DONE3			BIT(3)
+#define   DEC_DONE2			BIT(2)
+#define   DEC_DONE1			BIT(1)
+#define   DEC_DONE0			BIT(0)
+
+#define ECC_DECEL(n)			(0x11c + (n) * 4)
+#define   DEC_EL_ODD_S			16
+#define   DEC_EL_M			0x1fff
+#define   DEC_EL_BYTE_POS_S		3
+#define   DEC_EL_BIT_POS_M		GENMASK(2, 0)
+
+#define ECC_FDMADDR			0x13c
+
+/* ENCIDLE and DECIDLE */
+#define   ECC_IDLE			BIT(0)
+
+#define ACCTIMING(tpoecs, tprecs, tc2r, tw2r, twh, twst, trlt) \
+	(FIELD_PREP(ACCCON_POECS, tpoecs) | \
+	 FIELD_PREP(ACCCON_PRECS, tprecs) | \
+	 FIELD_PREP(ACCCON_C2R, tc2r) | \
+	 FIELD_PREP(ACCCON_W2R, tw2r) | \
+	 FIELD_PREP(ACCCON_WH, twh) | \
+	 FIELD_PREP(ACCCON_WST, twst) | \
+	 FIELD_PREP(ACCCON_RLT, trlt))
+
+#define MASTER_STA_MASK			(MAS_ADDR | MAS_RD | MAS_WR | \
+					 MAS_RDDLY)
+#define NFI_RESET_TIMEOUT		1000000
+#define NFI_CORE_TIMEOUT		500000
+#define ECC_ENGINE_TIMEOUT		500000
+
+#define ECC_SECTOR_SIZE			512
+#define ECC_PARITY_BITS			13
+
+#define NFI_FDM_SIZE			8
+
+/* Register base */
+#define NFI_BASE			0x1e003000
+#define NFI_ECC_BASE			0x1e003800
+
+static struct mt7621_nfc nfc_dev;
+
+static const u16 mt7621_nfi_page_size[] = { SZ_512, SZ_2K, SZ_4K };
+static const u8 mt7621_nfi_spare_size[] = { 16, 26, 27, 28 };
+static const u8 mt7621_ecc_strength[] = { 4, 6, 8, 10, 12 };
+
+static inline u32 nfi_read32(struct mt7621_nfc *nfc, u32 reg)
+{
+	return readl(nfc->nfi_regs + reg);
+}
+
+static inline void nfi_write32(struct mt7621_nfc *nfc, u32 reg, u32 val)
+{
+	writel(val, nfc->nfi_regs + reg);
+}
+
+static inline u16 nfi_read16(struct mt7621_nfc *nfc, u32 reg)
+{
+	return readw(nfc->nfi_regs + reg);
+}
+
+static inline void nfi_write16(struct mt7621_nfc *nfc, u32 reg, u16 val)
+{
+	writew(val, nfc->nfi_regs + reg);
+}
+
+static inline void ecc_write16(struct mt7621_nfc *nfc, u32 reg, u16 val)
+{
+	writew(val, nfc->ecc_regs + reg);
+}
+
+static inline u32 ecc_read32(struct mt7621_nfc *nfc, u32 reg)
+{
+	return readl(nfc->ecc_regs + reg);
+}
+
+static inline void ecc_write32(struct mt7621_nfc *nfc, u32 reg, u32 val)
+{
+	return writel(val, nfc->ecc_regs + reg);
+}
+
+static inline u8 *oob_fdm_ptr(struct nand_chip *nand, int sect)
+{
+	return nand->oob_poi + sect * NFI_FDM_SIZE;
+}
+
+static inline u8 *oob_ecc_ptr(struct mt7621_nfc *nfc, int sect)
+{
+	struct nand_chip *nand = &nfc->nand;
+
+	return nand->oob_poi + nand->ecc.steps * NFI_FDM_SIZE +
+		sect * (nfc->spare_per_sector - NFI_FDM_SIZE);
+}
+
+static inline u8 *page_data_ptr(struct nand_chip *nand, const u8 *buf,
+				int sect)
+{
+	return (u8 *)buf + sect * nand->ecc.size;
+}
+
+static int mt7621_ecc_wait_idle(struct mt7621_nfc *nfc, u32 reg)
+{
+	u32 val;
+	int ret;
+
+	ret = readw_poll_timeout(nfc->ecc_regs + reg, val, val & ECC_IDLE,
+				 ECC_ENGINE_TIMEOUT);
+	if (ret) {
+		pr_warn("ECC engine timed out entering idle mode\n");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int mt7621_ecc_decoder_wait_done(struct mt7621_nfc *nfc, u32 sect)
+{
+	u32 val;
+	int ret;
+
+	ret = readw_poll_timeout(nfc->ecc_regs + ECC_DECDONE, val,
+				 val & (1 << sect), ECC_ENGINE_TIMEOUT);
+	if (ret) {
+		pr_warn("ECC decoder for sector %d timed out\n", sect);
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+static void mt7621_ecc_encoder_op(struct mt7621_nfc *nfc, bool enable)
+{
+	mt7621_ecc_wait_idle(nfc, ECC_ENCIDLE);
+	ecc_write16(nfc, ECC_ENCCON, enable ? ENC_EN : 0);
+}
+
+static void mt7621_ecc_decoder_op(struct mt7621_nfc *nfc, bool enable)
+{
+	mt7621_ecc_wait_idle(nfc, ECC_DECIDLE);
+	ecc_write16(nfc, ECC_DECCON, enable ? DEC_EN : 0);
+}
+
+static int mt7621_ecc_correct_check(struct mt7621_nfc *nfc, u8 *sector_buf,
+				    u8 *fdm_buf, u32 sect)
+{
+	struct nand_chip *nand = &nfc->nand;
+	u32 decnum, num_error_bits, fdm_end_bits;
+	u32 error_locations, error_bit_loc;
+	u32 error_byte_pos, error_bit_pos;
+	int bitflips = 0;
+	u32 i;
+
+	decnum = ecc_read32(nfc, ECC_DECENUM);
+	num_error_bits = (decnum >> (sect << ERRNUM_S)) & ERRNUM_M;
+	fdm_end_bits = (nand->ecc.size + NFI_FDM_SIZE) << 3;
+
+	if (!num_error_bits)
+		return 0;
+
+	if (num_error_bits == ERRNUM_M)
+		return -1;
+
+	for (i = 0; i < num_error_bits; i++) {
+		error_locations = ecc_read32(nfc, ECC_DECEL(i / 2));
+		error_bit_loc = (error_locations >> ((i % 2) * DEC_EL_ODD_S)) &
+				DEC_EL_M;
+		error_byte_pos = error_bit_loc >> DEC_EL_BYTE_POS_S;
+		error_bit_pos = error_bit_loc & DEC_EL_BIT_POS_M;
+
+		if (error_bit_loc < (nand->ecc.size << 3)) {
+			if (sector_buf) {
+				sector_buf[error_byte_pos] ^=
+					(1 << error_bit_pos);
+			}
+		} else if (error_bit_loc < fdm_end_bits) {
+			if (fdm_buf) {
+				fdm_buf[error_byte_pos - nand->ecc.size] ^=
+					(1 << error_bit_pos);
+			}
+		}
+
+		bitflips++;
+	}
+
+	return bitflips;
+}
+
+static int mt7621_nfc_wait_write_completion(struct mt7621_nfc *nfc,
+					    struct nand_chip *nand)
+{
+	u16 val;
+	int ret;
+
+	ret = readw_poll_timeout(nfc->nfi_regs + NFI_ADDRCNTR, val,
+				 FIELD_GET(SEC_CNTR, val) >= nand->ecc.steps,
+				 NFI_CORE_TIMEOUT);
+
+	if (ret) {
+		pr_warn("NFI core write operation timed out\n");
+		return -ETIMEDOUT;
+	}
+
+	return ret;
+}
+
+static void mt7621_nfc_hw_reset(struct mt7621_nfc *nfc)
+{
+	u32 val;
+	int ret;
+
+	/* reset all registers and force the NFI master to terminate */
+	nfi_write16(nfc, NFI_CON, CON_FIFO_FLUSH | CON_NFI_RST);
+
+	/* wait for the master to finish the last transaction */
+	ret = readw_poll_timeout(nfc->nfi_regs + NFI_MASTER_STA, val,
+				 !(val & MASTER_STA_MASK), NFI_RESET_TIMEOUT);
+	if (ret) {
+		pr_warn("Failed to reset NFI master in %dms\n",
+			NFI_RESET_TIMEOUT);
+	}
+
+	/* ensure any status register affected by the NFI master is reset */
+	nfi_write16(nfc, NFI_CON, CON_FIFO_FLUSH | CON_NFI_RST);
+	nfi_write16(nfc, NFI_STRDATA, 0);
+}
+
+static inline void mt7621_nfc_hw_init(struct mt7621_nfc *nfc)
+{
+	u32 acccon;
+
+	/*
+	 * CNRNB: nand ready/busy register
+	 * -------------------------------
+	 * 7:4: timeout register for polling the NAND busy/ready signal
+	 * 0  : poll the status of the busy/ready signal after [7:4]*16 cycles.
+	 */
+	nfi_write16(nfc, NFI_CNRNB, CB2R_TIME | STR_CNRNB);
+
+	mt7621_nfc_hw_reset(nfc);
+
+	/* Apply default access timing */
+	acccon = ACCTIMING(ACCCON_POECS_DEF, ACCCON_PRECS_DEF, ACCCON_C2R_DEF,
+			   ACCCON_W2R_DEF, ACCCON_WH_DEF, ACCCON_WST_DEF,
+			   ACCCON_RLT_DEF);
+
+	nfi_write32(nfc, NFI_ACCCON, acccon);
+}
+
+static int mt7621_nfc_send_command(struct mt7621_nfc *nfc, u8 command)
+{
+	u32 val;
+	int ret;
+
+	nfi_write32(nfc, NFI_CMD, command);
+
+	ret = readl_poll_timeout(nfc->nfi_regs + NFI_STA, val, !(val & STA_CMD),
+				 NFI_CORE_TIMEOUT);
+	if (ret) {
+		pr_warn("NFI core timed out entering command mode\n");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int mt7621_nfc_send_address_byte(struct mt7621_nfc *nfc, int addr)
+{
+	u32 val;
+	int ret;
+
+	nfi_write32(nfc, NFI_COLADDR, addr);
+	nfi_write32(nfc, NFI_ROWADDR, 0);
+	nfi_write16(nfc, NFI_ADDRNOB, 1);
+
+	ret = readl_poll_timeout(nfc->nfi_regs + NFI_STA, val,
+				 !(val & STA_ADDR), NFI_CORE_TIMEOUT);
+	if (ret) {
+		pr_warn("NFI core timed out entering address mode\n");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static void mt7621_nfc_cmd_ctrl(struct mtd_info *mtd, int dat,
+				unsigned int ctrl)
+{
+	struct mt7621_nfc *nfc = nand_get_controller_data(mtd_to_nand(mtd));
+
+	if (ctrl & NAND_ALE) {
+		mt7621_nfc_send_address_byte(nfc, dat & 0xff);
+	} else if (ctrl & NAND_CLE) {
+		mt7621_nfc_hw_reset(nfc);
+		nfi_write16(nfc, NFI_CNFG,
+			    FIELD_PREP(CNFG_OP_MODE, CNFG_OP_CUSTOM));
+		mt7621_nfc_send_command(nfc, dat);
+	}
+}
+
+static int mt7621_nfc_dev_ready(struct mtd_info *mtd)
+{
+	struct mt7621_nfc *nfc = nand_get_controller_data(mtd_to_nand(mtd));
+
+	if (nfi_read32(nfc, NFI_STA) & STA_BUSY)
+		return 0;
+
+	return 1;
+}
+
+static void mt7621_nfc_select_chip(struct mtd_info *mtd, int chipnr)
+{
+	struct mt7621_nfc *nfc = nand_get_controller_data(mtd_to_nand(mtd));
+
+	nfi_write16(nfc, NFI_CSEL, 0);
+}
+
+static void mt7621_nfc_wait_pio_ready(struct mt7621_nfc *nfc)
+{
+	int ret;
+	u16 val;
+
+	ret = readw_poll_timeout(nfc->nfi_regs + NFI_PIO_DIRDY, val,
+				 val & PIO_DIRDY, NFI_CORE_TIMEOUT);
+	if (ret < 0)
+		pr_err("NFI core PIO mode not ready\n");
+}
+
+static u32 mt7621_nfc_pio_read(struct mt7621_nfc *nfc, bool br)
+{
+	u32 reg, fsm;
+
+	/* after each byte read, the NFI_STA reg is reset by the hardware */
+	reg = nfi_read32(nfc, NFI_STA);
+	fsm = FIELD_GET(STA_NFI_FSM, reg);
+
+	if (fsm != STA_FSM_CUSTOM_DATA) {
+		reg = nfi_read16(nfc, NFI_CNFG);
+		reg |= CNFG_READ_MODE | CNFG_BYTE_RW;
+		if (!br)
+			reg &= ~CNFG_BYTE_RW;
+		nfi_write16(nfc, NFI_CNFG, reg);
+
+		/*
+		 * set to max sector to allow the HW to continue reading over
+		 * unaligned accesses
+		 */
+		nfi_write16(nfc, NFI_CON, CON_NFI_SEC | CON_NFI_BRD);
+
+		/* trigger to fetch data */
+		nfi_write16(nfc, NFI_STRDATA, STR_DATA);
+	}
+
+	mt7621_nfc_wait_pio_ready(nfc);
+
+	return nfi_read32(nfc, NFI_DATAR);
+}
+
+static void mt7621_nfc_read_data(struct mt7621_nfc *nfc, u8 *buf, u32 len)
+{
+	while (((uintptr_t)buf & 3) && len) {
+		*buf = mt7621_nfc_pio_read(nfc, true);
+		buf++;
+		len--;
+	}
+
+	while (len >= 4) {
+		*(u32 *)buf = mt7621_nfc_pio_read(nfc, false);
+		buf += 4;
+		len -= 4;
+	}
+
+	while (len) {
+		*buf = mt7621_nfc_pio_read(nfc, true);
+		buf++;
+		len--;
+	}
+}
+
+static void mt7621_nfc_read_data_discard(struct mt7621_nfc *nfc, u32 len)
+{
+	while (len >= 4) {
+		mt7621_nfc_pio_read(nfc, false);
+		len -= 4;
+	}
+
+	while (len) {
+		mt7621_nfc_pio_read(nfc, true);
+		len--;
+	}
+}
+
+static void mt7621_nfc_pio_write(struct mt7621_nfc *nfc, u32 val, bool bw)
+{
+	u32 reg, fsm;
+
+	reg = nfi_read32(nfc, NFI_STA);
+	fsm = FIELD_GET(STA_NFI_FSM, reg);
+
+	if (fsm != STA_FSM_CUSTOM_DATA) {
+		reg = nfi_read16(nfc, NFI_CNFG);
+		reg &= ~(CNFG_READ_MODE | CNFG_BYTE_RW);
+		if (bw)
+			reg |= CNFG_BYTE_RW;
+		nfi_write16(nfc, NFI_CNFG, reg);
+
+		nfi_write16(nfc, NFI_CON, CON_NFI_SEC | CON_NFI_BWR);
+		nfi_write16(nfc, NFI_STRDATA, STR_DATA);
+	}
+
+	mt7621_nfc_wait_pio_ready(nfc);
+	nfi_write32(nfc, NFI_DATAW, val);
+}
+
+static void mt7621_nfc_write_data(struct mt7621_nfc *nfc, const u8 *buf,
+				  u32 len)
+{
+	while (((uintptr_t)buf & 3) && len) {
+		mt7621_nfc_pio_write(nfc, *buf, true);
+		buf++;
+		len--;
+	}
+
+	while (len >= 4) {
+		mt7621_nfc_pio_write(nfc, *(const u32 *)buf, false);
+		buf += 4;
+		len -= 4;
+	}
+
+	while (len) {
+		mt7621_nfc_pio_write(nfc, *buf, true);
+		buf++;
+		len--;
+	}
+}
+
+static void mt7621_nfc_write_data_empty(struct mt7621_nfc *nfc, u32 len)
+{
+	while (len >= 4) {
+		mt7621_nfc_pio_write(nfc, 0xffffffff, false);
+		len -= 4;
+	}
+
+	while (len) {
+		mt7621_nfc_pio_write(nfc, 0xff, true);
+		len--;
+	}
+}
+
+static void mt7621_nfc_write_byte(struct mtd_info *mtd, u8 byte)
+{
+	struct mt7621_nfc *nfc = nand_get_controller_data(mtd_to_nand(mtd));
+
+	mt7621_nfc_pio_write(nfc, byte, true);
+}
+
+static void mt7621_nfc_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
+{
+	struct mt7621_nfc *nfc = nand_get_controller_data(mtd_to_nand(mtd));
+
+	return mt7621_nfc_write_data(nfc, buf, len);
+}
+
+static u8 mt7621_nfc_read_byte(struct mtd_info *mtd)
+{
+	struct mt7621_nfc *nfc = nand_get_controller_data(mtd_to_nand(mtd));
+
+	return mt7621_nfc_pio_read(nfc, true);
+}
+
+static void mt7621_nfc_read_buf(struct mtd_info *mtd, u8 *buf, int len)
+{
+	struct mt7621_nfc *nfc = nand_get_controller_data(mtd_to_nand(mtd));
+
+	mt7621_nfc_read_data(nfc, buf, len);
+}
+
+static int mt7621_nfc_calc_ecc_strength(struct mt7621_nfc *nfc,
+					u32 avail_ecc_bytes)
+{
+	struct nand_chip *nand = &nfc->nand;
+	struct mtd_info *mtd = nand_to_mtd(nand);
+	u32 strength;
+	int i;
+
+	strength = avail_ecc_bytes * 8 / ECC_PARITY_BITS;
+
+	/* Find the closest supported ecc strength */
+	for (i = ARRAY_SIZE(mt7621_ecc_strength) - 1; i >= 0; i--) {
+		if (mt7621_ecc_strength[i] <= strength)
+			break;
+	}
+
+	if (unlikely(i < 0)) {
+		pr_err("OOB size (%u) is not supported\n", mtd->oobsize);
+		return -EINVAL;
+	}
+
+	nand->ecc.strength = mt7621_ecc_strength[i];
+	nand->ecc.bytes = DIV_ROUND_UP(nand->ecc.strength * ECC_PARITY_BITS, 8);
+
+	pr_debug("ECC strength adjusted to %u bits\n", nand->ecc.strength);
+
+	return i;
+}
+
+static int mt7621_nfc_set_spare_per_sector(struct mt7621_nfc *nfc)
+{
+	struct nand_chip *nand = &nfc->nand;
+	struct mtd_info *mtd = nand_to_mtd(nand);
+	u32 size;
+	int i;
+
+	size = nand->ecc.bytes + NFI_FDM_SIZE;
+
+	/* Find the closest supported spare size */
+	for (i = 0; i < ARRAY_SIZE(mt7621_nfi_spare_size); i++) {
+		if (mt7621_nfi_spare_size[i] >= size)
+			break;
+	}
+
+	if (unlikely(i >= ARRAY_SIZE(mt7621_nfi_spare_size))) {
+		pr_err("OOB size (%u) is not supported\n", mtd->oobsize);
+		return -EINVAL;
+	}
+
+	nfc->spare_per_sector = mt7621_nfi_spare_size[i];
+
+	return i;
+}
+
+static int mt7621_nfc_ecc_init(struct mt7621_nfc *nfc)
+{
+	struct nand_chip *nand = &nfc->nand;
+	struct mtd_info *mtd = nand_to_mtd(nand);
+	u32 avail_ecc_bytes, encode_block_size, decode_block_size;
+	u32 ecc_enccfg, ecc_deccfg;
+	int ecc_cap;
+
+	nand->ecc.options |= NAND_ECC_CUSTOM_PAGE_ACCESS;
+
+	nand->ecc.size = ECC_SECTOR_SIZE;
+	nand->ecc.steps = mtd->writesize / nand->ecc.size;
+
+	avail_ecc_bytes = mtd->oobsize / nand->ecc.steps - NFI_FDM_SIZE;
+
+	ecc_cap = mt7621_nfc_calc_ecc_strength(nfc, avail_ecc_bytes);
+	if (ecc_cap < 0)
+		return ecc_cap;
+
+	/* Sector + FDM */
+	encode_block_size = (nand->ecc.size + NFI_FDM_SIZE) * 8;
+	ecc_enccfg = ecc_cap | FIELD_PREP(ENC_MODE, ENC_MODE_NFI) |
+		     FIELD_PREP(ENC_CNFG_MSG, encode_block_size);
+
+	/* Sector + FDM + ECC parity bits */
+	decode_block_size = ((nand->ecc.size + NFI_FDM_SIZE) * 8) +
+			    nand->ecc.strength * ECC_PARITY_BITS;
+	ecc_deccfg = ecc_cap | FIELD_PREP(DEC_MODE, DEC_MODE_NFI) |
+		     FIELD_PREP(DEC_CS, decode_block_size) |
+		     FIELD_PREP(DEC_CON, DEC_CON_EL) | DEC_EMPTY_EN;
+
+	mt7621_ecc_encoder_op(nfc, false);
+	ecc_write32(nfc, ECC_ENCCNFG, ecc_enccfg);
+
+	mt7621_ecc_decoder_op(nfc, false);
+	ecc_write32(nfc, ECC_DECCNFG, ecc_deccfg);
+
+	return 0;
+}
+
+static int mt7621_nfc_set_page_format(struct mt7621_nfc *nfc)
+{
+	struct nand_chip *nand = &nfc->nand;
+	struct mtd_info *mtd = nand_to_mtd(nand);
+	int i, spare_size;
+	u32 pagefmt;
+
+	spare_size = mt7621_nfc_set_spare_per_sector(nfc);
+	if (spare_size < 0)
+		return spare_size;
+
+	for (i = 0; i < ARRAY_SIZE(mt7621_nfi_page_size); i++) {
+		if (mt7621_nfi_page_size[i] == mtd->writesize)
+			break;
+	}
+
+	if (unlikely(i >= ARRAY_SIZE(mt7621_nfi_page_size))) {
+		pr_err("Page size (%u) is not supported\n", mtd->writesize);
+		return -EINVAL;
+	}
+
+	pagefmt = FIELD_PREP(PAGEFMT_PAGE, i) |
+		  FIELD_PREP(PAGEFMT_SPARE, spare_size) |
+		  FIELD_PREP(PAGEFMT_FDM, NFI_FDM_SIZE) |
+		  FIELD_PREP(PAGEFMT_FDM_ECC, NFI_FDM_SIZE);
+
+	nfi_write16(nfc, NFI_PAGEFMT, pagefmt);
+
+	return 0;
+}
+
+static int mt7621_nfc_attach_chip(struct nand_chip *nand)
+{
+	struct mt7621_nfc *nfc = nand_get_controller_data(nand);
+	int ret;
+
+	if (nand->options & NAND_BUSWIDTH_16) {
+		pr_err("16-bit buswidth is not supported");
+		return -EINVAL;
+	}
+
+	ret = mt7621_nfc_ecc_init(nfc);
+	if (ret)
+		return ret;
+
+	return mt7621_nfc_set_page_format(nfc);
+}
+
+static void mt7621_nfc_write_fdm(struct mt7621_nfc *nfc)
+{
+	struct nand_chip *nand = &nfc->nand;
+	u32 vall, valm;
+	u8 *oobptr;
+	int i, j;
+
+	for (i = 0; i < nand->ecc.steps; i++) {
+		vall = 0;
+		valm = 0;
+		oobptr = oob_fdm_ptr(nand, i);
+
+		for (j = 0; j < 4; j++)
+			vall |= (u32)oobptr[j] << (j * 8);
+
+		for (j = 0; j < 4; j++)
+			valm |= (u32)oobptr[j + 4] << (j * 8);
+
+		nfi_write32(nfc, NFI_FDML(i), vall);
+		nfi_write32(nfc, NFI_FDMM(i), valm);
+	}
+}
+
+static void mt7621_nfc_read_sector_fdm(struct mt7621_nfc *nfc, u32 sect)
+{
+	struct nand_chip *nand = &nfc->nand;
+	u32 vall, valm;
+	u8 *oobptr;
+	int i;
+
+	vall = nfi_read32(nfc, NFI_FDML(sect));
+	valm = nfi_read32(nfc, NFI_FDMM(sect));
+	oobptr = oob_fdm_ptr(nand, sect);
+
+	for (i = 0; i < 4; i++)
+		oobptr[i] = (vall >> (i * 8)) & 0xff;
+
+	for (i = 0; i < 4; i++)
+		oobptr[i + 4] = (valm >> (i * 8)) & 0xff;
+}
+
+static int mt7621_nfc_read_page_hwecc(struct mtd_info *mtd,
+				      struct nand_chip *nand, uint8_t *buf,
+				      int oob_required, int page)
+{
+	struct mt7621_nfc *nfc = nand_get_controller_data(nand);
+	int bitflips = 0, ret = 0;
+	int rc, i;
+
+	nand_read_page_op(nand, page, 0, NULL, 0);
+
+	nfi_write16(nfc, NFI_CNFG, FIELD_PREP(CNFG_OP_MODE, CNFG_OP_CUSTOM) |
+		    CNFG_READ_MODE | CNFG_AUTO_FMT_EN | CNFG_HW_ECC_EN);
+
+	mt7621_ecc_decoder_op(nfc, true);
+
+	nfi_write16(nfc, NFI_CON, FIELD_PREP(CON_NFI_SEC, nand->ecc.steps) |
+		    CON_NFI_BRD);
+
+	for (i = 0; i < nand->ecc.steps; i++) {
+		if (buf)
+			mt7621_nfc_read_data(nfc, page_data_ptr(nand, buf, i),
+					     nand->ecc.size);
+		else
+			mt7621_nfc_read_data_discard(nfc, nand->ecc.size);
+
+		rc = mt7621_ecc_decoder_wait_done(nfc, i);
+
+		mt7621_nfc_read_sector_fdm(nfc, i);
+
+		if (rc < 0) {
+			ret = -EIO;
+			continue;
+		}
+
+		rc = mt7621_ecc_correct_check(nfc,
+			buf ? page_data_ptr(nand, buf, i) : NULL,
+			oob_fdm_ptr(nand, i), i);
+
+		if (rc < 0) {
+			pr_warn("Uncorrectable ECC error at page %d step %d\n",
+				page, i);
+			bitflips = nand->ecc.strength + 1;
+			mtd->ecc_stats.failed++;
+		} else {
+			if (rc > bitflips)
+				bitflips = rc;
+			mtd->ecc_stats.corrected += rc;
+		}
+	}
+
+	mt7621_ecc_decoder_op(nfc, false);
+
+	nfi_write16(nfc, NFI_CON, 0);
+
+	if (ret < 0)
+		return ret;
+
+	return bitflips;
+}
+
+static int mt7621_nfc_read_page_raw(struct mtd_info *mtd,
+				    struct nand_chip *nand, uint8_t *buf,
+				    int oob_required, int page)
+{
+	struct mt7621_nfc *nfc = nand_get_controller_data(nand);
+	int i;
+
+	nand_read_page_op(nand, page, 0, NULL, 0);
+
+	nfi_write16(nfc, NFI_CNFG, FIELD_PREP(CNFG_OP_MODE, CNFG_OP_CUSTOM) |
+		    CNFG_READ_MODE);
+
+	nfi_write16(nfc, NFI_CON, FIELD_PREP(CON_NFI_SEC, nand->ecc.steps) |
+		    CON_NFI_BRD);
+
+	for (i = 0; i < nand->ecc.steps; i++) {
+		/* Read data */
+		if (buf)
+			mt7621_nfc_read_data(nfc, page_data_ptr(nand, buf, i),
+					     nand->ecc.size);
+		else
+			mt7621_nfc_read_data_discard(nfc, nand->ecc.size);
+
+		/* Read FDM */
+		mt7621_nfc_read_data(nfc, oob_fdm_ptr(nand, i), NFI_FDM_SIZE);
+
+		/* Read ECC parity data */
+		mt7621_nfc_read_data(nfc, oob_ecc_ptr(nfc, i),
+				     nfc->spare_per_sector - NFI_FDM_SIZE);
+	}
+
+	nfi_write16(nfc, NFI_CON, 0);
+
+	return 0;
+}
+
+static int mt7621_nfc_read_oob_hwecc(struct mtd_info *mtd,
+				     struct nand_chip *nand, int page)
+{
+	return mt7621_nfc_read_page_hwecc(mtd, nand, NULL, 1, page);
+}
+
+static int mt7621_nfc_read_oob_raw(struct mtd_info *mtd,
+				   struct nand_chip *nand, int page)
+{
+	return mt7621_nfc_read_page_raw(mtd, nand, NULL, 1, page);
+}
+
+static int mt7621_nfc_check_empty_page(struct nand_chip *nand, const u8 *buf)
+{
+	struct mtd_info *mtd = nand_to_mtd(nand);
+	u8 *oobptr;
+	u32 i, j;
+
+	if (buf) {
+		for (i = 0; i < mtd->writesize; i++)
+			if (buf[i] != 0xff)
+				return 0;
+	}
+
+	for (i = 0; i < nand->ecc.steps; i++) {
+		oobptr = oob_fdm_ptr(nand, i);
+		for (j = 0; j < NFI_FDM_SIZE; j++)
+			if (oobptr[j] != 0xff)
+				return 0;
+	}
+
+	return 1;
+}
+
+static int mt7621_nfc_write_page_hwecc(struct mtd_info *mtd,
+				       struct nand_chip *nand,
+				       const u8 *buf, int oob_required,
+				       int page)
+{
+	struct mt7621_nfc *nfc = nand_get_controller_data(nand);
+
+	if (mt7621_nfc_check_empty_page(nand, buf)) {
+		/*
+		 * MT7621 ECC engine always generates parity code for input
+		 * pages, even for empty pages. Doing so will write back ECC
+		 * parity code to the oob region, which means such pages will
+		 * no longer be empty pages.
+		 *
+		 * To avoid this, stop write operation if current page is an
+		 * empty page.
+		 */
+		return 0;
+	}
+
+	nand_prog_page_begin_op(nand, page, 0, NULL, 0);
+
+	nfi_write16(nfc, NFI_CNFG, FIELD_PREP(CNFG_OP_MODE, CNFG_OP_CUSTOM) |
+		    CNFG_AUTO_FMT_EN | CNFG_HW_ECC_EN);
+
+	mt7621_ecc_encoder_op(nfc, true);
+
+	mt7621_nfc_write_fdm(nfc);
+
+	nfi_write16(nfc, NFI_CON, FIELD_PREP(CON_NFI_SEC, nand->ecc.steps) |
+		    CON_NFI_BWR);
+
+	if (buf)
+		mt7621_nfc_write_data(nfc, buf, mtd->writesize);
+	else
+		mt7621_nfc_write_data_empty(nfc, mtd->writesize);
+
+	mt7621_nfc_wait_write_completion(nfc, nand);
+
+	mt7621_ecc_encoder_op(nfc, false);
+
+	nfi_write16(nfc, NFI_CON, 0);
+
+	return nand_prog_page_end_op(nand);
+}
+
+static int mt7621_nfc_write_page_raw(struct mtd_info *mtd,
+				     struct nand_chip *nand,
+				     const u8 *buf, int oob_required,
+				     int page)
+{
+	struct mt7621_nfc *nfc = nand_get_controller_data(nand);
+	int i;
+
+	nand_prog_page_begin_op(nand, page, 0, NULL, 0);
+
+	nfi_write16(nfc, NFI_CNFG, FIELD_PREP(CNFG_OP_MODE, CNFG_OP_CUSTOM));
+
+	nfi_write16(nfc, NFI_CON, FIELD_PREP(CON_NFI_SEC, nand->ecc.steps) |
+		    CON_NFI_BWR);
+
+	for (i = 0; i < nand->ecc.steps; i++) {
+		/* Write data */
+		if (buf)
+			mt7621_nfc_write_data(nfc, page_data_ptr(nand, buf, i),
+					      nand->ecc.size);
+		else
+			mt7621_nfc_write_data_empty(nfc, nand->ecc.size);
+
+		/* Write FDM */
+		mt7621_nfc_write_data(nfc, oob_fdm_ptr(nand, i),
+				      NFI_FDM_SIZE);
+
+		/* Write dummy ECC parity data */
+		mt7621_nfc_write_data_empty(nfc, nfc->spare_per_sector -
+					    NFI_FDM_SIZE);
+	}
+
+	mt7621_nfc_wait_write_completion(nfc, nand);
+
+	nfi_write16(nfc, NFI_CON, 0);
+
+	return nand_prog_page_end_op(nand);
+}
+
+static int mt7621_nfc_write_oob_hwecc(struct mtd_info *mtd,
+				      struct nand_chip *nand, int page)
+{
+	return mt7621_nfc_write_page_hwecc(mtd, nand, NULL, 1, page);
+}
+
+static int mt7621_nfc_write_oob_raw(struct mtd_info *mtd,
+				    struct nand_chip *nand, int page)
+{
+	return mt7621_nfc_write_page_raw(mtd, nand, NULL, 1, page);
+}
+
+static int mt7621_nfc_ooblayout_free(struct mtd_info *mtd, int section,
+				     struct mtd_oob_region *oob_region)
+{
+	struct nand_chip *nand = mtd_to_nand(mtd);
+
+	if (section >= nand->ecc.steps)
+		return -ERANGE;
+
+	oob_region->length = NFI_FDM_SIZE - 1;
+	oob_region->offset = section * NFI_FDM_SIZE + 1;
+
+	return 0;
+}
+
+static int mt7621_nfc_ooblayout_ecc(struct mtd_info *mtd, int section,
+				    struct mtd_oob_region *oob_region)
+{
+	struct nand_chip *nand = mtd_to_nand(mtd);
+
+	if (section)
+		return -ERANGE;
+
+	oob_region->offset = NFI_FDM_SIZE * nand->ecc.steps;
+	oob_region->length = mtd->oobsize - oob_region->offset;
+
+	return 0;
+}
+
+static const struct mtd_ooblayout_ops mt7621_nfc_ooblayout_ops = {
+	.rfree = mt7621_nfc_ooblayout_free,
+	.ecc = mt7621_nfc_ooblayout_ecc,
+};
+
+/*
+ * This function will override the default one which is not supposed to be
+ * used for ECC syndrome based pages.
+ */
+static int mt7621_nfc_block_bad(struct mtd_info *mtd, loff_t ofs)
+{
+	struct nand_chip *nand = mtd_to_nand(mtd);
+	struct mtd_oob_ops ops;
+	int ret, i = 0;
+	u16 bad;
+
+	memset(&ops, 0, sizeof(ops));
+	ops.oobbuf = (uint8_t *)&bad;
+	ops.ooboffs = nand->badblockpos;
+	if (nand->options & NAND_BUSWIDTH_16) {
+		ops.ooboffs &= ~0x01;
+		ops.ooblen = 2;
+	} else {
+		ops.ooblen = 1;
+	}
+	ops.mode = MTD_OPS_RAW;
+
+	/* Read from first/last page(s) if necessary */
+	if (nand->bbt_options & NAND_BBT_SCANLASTPAGE)
+		ofs += mtd->erasesize - mtd->writesize;
+
+	do {
+		ret = mtd_read_oob(mtd, ofs, &ops);
+		if (ret)
+			return ret;
+
+		if (likely(nand->badblockbits == 8))
+			ret = bad != 0xFF;
+		else
+			ret = hweight8(bad) < nand->badblockbits;
+
+		i++;
+		ofs += mtd->writesize;
+	} while (!ret && (nand->bbt_options & NAND_BBT_SCAN2NDPAGE) && i < 2);
+
+	return ret;
+}
+
+static void mt7621_nfc_init_chip(struct mt7621_nfc *nfc)
+{
+	struct nand_chip *nand = &nfc->nand;
+	struct mtd_info *mtd;
+	int ret;
+
+	nand_set_controller_data(nand, nfc);
+
+	nand->options |= NAND_NO_SUBPAGE_WRITE;
+
+	nand->ecc.mode = NAND_ECC_HW_SYNDROME;
+	nand->ecc.read_page = mt7621_nfc_read_page_hwecc;
+	nand->ecc.read_page_raw = mt7621_nfc_read_page_raw;
+	nand->ecc.write_page = mt7621_nfc_write_page_hwecc;
+	nand->ecc.write_page_raw = mt7621_nfc_write_page_raw;
+	nand->ecc.read_oob = mt7621_nfc_read_oob_hwecc;
+	nand->ecc.read_oob_raw = mt7621_nfc_read_oob_raw;
+	nand->ecc.write_oob = mt7621_nfc_write_oob_hwecc;
+	nand->ecc.write_oob_raw = mt7621_nfc_write_oob_raw;
+
+	nand->dev_ready = mt7621_nfc_dev_ready;
+	nand->select_chip = mt7621_nfc_select_chip;
+	nand->write_byte = mt7621_nfc_write_byte;
+	nand->write_buf = mt7621_nfc_write_buf;
+	nand->read_byte = mt7621_nfc_read_byte;
+	nand->read_buf = mt7621_nfc_read_buf;
+	nand->cmd_ctrl = mt7621_nfc_cmd_ctrl;
+	nand->block_bad = mt7621_nfc_block_bad;
+
+	mtd = nand_to_mtd(nand);
+	mtd_set_ooblayout(mtd, &mt7621_nfc_ooblayout_ops);
+
+	/* Reset NFI master */
+	mt7621_nfc_hw_init(nfc);
+
+	ret = nand_scan_ident(mtd, 1, NULL);
+	if (ret)
+		return;
+
+	mt7621_nfc_attach_chip(nand);
+
+	ret = nand_scan_tail(mtd);
+	if (ret)
+		return;
+
+	nand_register(0, mtd);
+}
+
+static void mt7621_nfc_set_regs(struct mt7621_nfc *nfc)
+{
+	nfc->nfi_regs = (void __iomem *)CKSEG1ADDR(NFI_BASE);
+	nfc->ecc_regs = (void __iomem *)CKSEG1ADDR(NFI_ECC_BASE);
+}
+
+void mt7621_nfc_spl_init(struct mt7621_nfc *nfc)
+{
+	struct nand_chip *nand = &nfc->nand;
+
+	mt7621_nfc_set_regs(nfc);
+
+	nand_set_controller_data(nand, nfc);
+
+	nand->options |= NAND_NO_SUBPAGE_WRITE;
+
+	nand->ecc.mode = NAND_ECC_HW_SYNDROME;
+	nand->ecc.read_page = mt7621_nfc_read_page_hwecc;
+
+	nand->dev_ready = mt7621_nfc_dev_ready;
+	nand->select_chip = mt7621_nfc_select_chip;
+	nand->read_byte = mt7621_nfc_read_byte;
+	nand->read_buf = mt7621_nfc_read_buf;
+	nand->cmd_ctrl = mt7621_nfc_cmd_ctrl;
+
+	/* Reset NFI master */
+	mt7621_nfc_hw_init(nfc);
+}
+
+int mt7621_nfc_spl_post_init(struct mt7621_nfc *nfc)
+{
+	struct nand_chip *nand = &nfc->nand;
+	int nand_maf_id, nand_dev_id;
+	struct nand_flash_dev *type;
+
+	type = nand_get_flash_type(&nand->mtd, nand, &nand_maf_id,
+				   &nand_dev_id, NULL);
+
+	if (IS_ERR(type))
+		return PTR_ERR(type);
+
+	nand->numchips = 1;
+	nand->mtd.size = nand->chipsize;
+
+	return mt7621_nfc_attach_chip(nand);
+}
+
+void board_nand_init(void)
+{
+	mt7621_nfc_set_regs(&nfc_dev);
+	mt7621_nfc_init_chip(&nfc_dev);
+}
diff --git a/drivers/mtd/nand/raw/mt7621_nand.h b/drivers/mtd/nand/raw/mt7621_nand.h
new file mode 100644
index 0000000..af4bc55
--- /dev/null
+++ b/drivers/mtd/nand/raw/mt7621_nand.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2022 MediaTek Inc. All rights reserved.
+ *
+ * Author: Weijie Gao <weijie.gao@mediatek.com>
+ */
+
+#ifndef _MT7621_NAND_H_
+#define _MT7621_NAND_H_
+
+#include <linux/types.h>
+#include <linux/mtd/mtd.h>
+#include <linux/compiler.h>
+#include <linux/mtd/rawnand.h>
+
+struct mt7621_nfc {
+	struct nand_chip nand;
+
+	void __iomem *nfi_regs;
+	void __iomem *ecc_regs;
+
+	u32 spare_per_sector;
+};
+
+/* for SPL */
+void mt7621_nfc_spl_init(struct mt7621_nfc *nfc);
+int mt7621_nfc_spl_post_init(struct mt7621_nfc *nfc);
+
+#endif /* _MT7621_NAND_H_ */
diff --git a/drivers/mtd/nand/raw/mt7621_nand_spl.c b/drivers/mtd/nand/raw/mt7621_nand_spl.c
new file mode 100644
index 0000000..114fc8b
--- /dev/null
+++ b/drivers/mtd/nand/raw/mt7621_nand_spl.c
@@ -0,0 +1,237 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2022 MediaTek Inc. All rights reserved.
+ *
+ * Author: Weijie Gao <weijie.gao@mediatek.com>
+ */
+
+#include <image.h>
+#include <malloc.h>
+#include <linux/sizes.h>
+#include <linux/delay.h>
+#include <linux/mtd/rawnand.h>
+#include "mt7621_nand.h"
+
+static struct mt7621_nfc nfc_dev;
+static u8 *buffer;
+static int nand_valid;
+
+static void nand_command_lp(struct mtd_info *mtd, unsigned int command,
+			    int column, int page_addr)
+{
+	register struct nand_chip *chip = mtd_to_nand(mtd);
+
+	/* Command latch cycle */
+	chip->cmd_ctrl(mtd, command, NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
+
+	if (column != -1 || page_addr != -1) {
+		int ctrl = NAND_CTRL_CHANGE | NAND_NCE | NAND_ALE;
+
+		/* Serially input address */
+		if (column != -1) {
+			chip->cmd_ctrl(mtd, column, ctrl);
+			ctrl &= ~NAND_CTRL_CHANGE;
+			if (command != NAND_CMD_READID)
+				chip->cmd_ctrl(mtd, column >> 8, ctrl);
+		}
+		if (page_addr != -1) {
+			chip->cmd_ctrl(mtd, page_addr, ctrl);
+			chip->cmd_ctrl(mtd, page_addr >> 8,
+				       NAND_NCE | NAND_ALE);
+			if (chip->options & NAND_ROW_ADDR_3)
+				chip->cmd_ctrl(mtd, page_addr >> 16,
+					       NAND_NCE | NAND_ALE);
+		}
+	}
+	chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
+
+	/*
+	 * Program and erase have their own busy handlers status, sequential
+	 * in and status need no delay.
+	 */
+	switch (command) {
+	case NAND_CMD_STATUS:
+	case NAND_CMD_READID:
+	case NAND_CMD_SET_FEATURES:
+		return;
+
+	case NAND_CMD_READ0:
+		chip->cmd_ctrl(mtd, NAND_CMD_READSTART,
+			       NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
+		chip->cmd_ctrl(mtd, NAND_CMD_NONE,
+			       NAND_NCE | NAND_CTRL_CHANGE);
+	}
+
+	/*
+	 * Apply this short delay always to ensure that we do wait tWB in
+	 * any case on any machine.
+	 */
+	ndelay(100);
+
+	nand_wait_ready(mtd);
+}
+
+static int nfc_read_page_hwecc(struct mtd_info *mtd, void *buf,
+			       unsigned int page)
+{
+	struct nand_chip *chip = mtd_to_nand(mtd);
+	int ret;
+
+	chip->cmdfunc(mtd, NAND_CMD_READ0, 0x0, page);
+
+	ret = chip->ecc.read_page(mtd, chip, buf, 1, page);
+	if (ret < 0 || ret > chip->ecc.strength)
+		return -1;
+
+	return 0;
+}
+
+static int nfc_read_oob_hwecc(struct mtd_info *mtd, void *buf, u32 len,
+			      unsigned int page)
+{
+	struct nand_chip *chip = mtd_to_nand(mtd);
+	int ret;
+
+	chip->cmdfunc(mtd, NAND_CMD_READ0, 0x0, page);
+
+	ret = chip->ecc.read_page(mtd, chip, NULL, 1, page);
+	if (ret < 0)
+		return -1;
+
+	if (len > mtd->oobsize)
+		len = mtd->oobsize;
+
+	memcpy(buf, chip->oob_poi, len);
+
+	return 0;
+}
+
+static int nfc_check_bad_block(struct mtd_info *mtd, unsigned int page)
+{
+	struct nand_chip *chip = mtd_to_nand(mtd);
+	u32 pages_per_block, i = 0;
+	int ret;
+	u8 bad;
+
+	pages_per_block = 1 << (mtd->erasesize_shift - mtd->writesize_shift);
+
+	/* Read from first/last page(s) if necessary */
+	if (chip->bbt_options & NAND_BBT_SCANLASTPAGE) {
+		page += pages_per_block - 1;
+		if (chip->bbt_options & NAND_BBT_SCAN2NDPAGE)
+			page--;
+	}
+
+	do {
+		ret = nfc_read_oob_hwecc(mtd, &bad, 1, page);
+		if (ret)
+			return ret;
+
+		ret = bad != 0xFF;
+
+		i++;
+		page++;
+	} while (!ret && (chip->bbt_options & NAND_BBT_SCAN2NDPAGE) && i < 2);
+
+	return ret;
+}
+
+int nand_spl_load_image(uint32_t offs, unsigned int size, void *dest)
+{
+	struct mt7621_nfc *nfc = &nfc_dev;
+	struct nand_chip *chip = &nfc->nand;
+	struct mtd_info *mtd = &chip->mtd;
+	u32 addr, col, page, chksz;
+	bool check_bad = true;
+
+	if (!nand_valid)
+		return -ENODEV;
+
+	while (size) {
+		if (check_bad || !(offs & mtd->erasesize_mask)) {
+			addr = offs & (~mtd->erasesize_mask);
+			page = addr >> mtd->writesize_shift;
+			if (nfc_check_bad_block(mtd, page)) {
+				/* Skip bad block */
+				if (addr >= mtd->size - mtd->erasesize)
+					return -1;
+
+				offs += mtd->erasesize;
+				continue;
+			}
+
+			check_bad = false;
+		}
+
+		col = offs & mtd->writesize_mask;
+		page = offs >> mtd->writesize_shift;
+		chksz = min(mtd->writesize - col, (uint32_t)size);
+
+		if (unlikely(chksz < mtd->writesize)) {
+			/* Not reading a full page */
+			if (nfc_read_page_hwecc(mtd, buffer, page))
+				return -1;
+
+			memcpy(dest, buffer + col, chksz);
+		} else {
+			if (nfc_read_page_hwecc(mtd, dest, page))
+				return -1;
+		}
+
+		dest += chksz;
+		offs += chksz;
+		size -= chksz;
+	}
+
+	return 0;
+}
+
+int nand_default_bbt(struct mtd_info *mtd)
+{
+	return 0;
+}
+
+unsigned long nand_size(void)
+{
+	if (!nand_valid)
+		return 0;
+
+	/* Unlikely that NAND size > 2GBytes */
+	if (nfc_dev.nand.chipsize <= SZ_2G)
+		return nfc_dev.nand.chipsize;
+
+	return SZ_2G;
+}
+
+void nand_deselect(void)
+{
+}
+
+void nand_init(void)
+{
+	struct mtd_info *mtd;
+	struct nand_chip *chip;
+
+	if (nand_valid)
+		return;
+
+	mt7621_nfc_spl_init(&nfc_dev);
+
+	chip = &nfc_dev.nand;
+	mtd = &chip->mtd;
+	chip->cmdfunc = nand_command_lp;
+
+	if (mt7621_nfc_spl_post_init(&nfc_dev))
+		return;
+
+	mtd->erasesize_shift = ffs(mtd->erasesize) - 1;
+	mtd->writesize_shift = ffs(mtd->writesize) - 1;
+	mtd->erasesize_mask = (1 << mtd->erasesize_shift) - 1;
+	mtd->writesize_mask = (1 << mtd->writesize_shift) - 1;
+
+	buffer = malloc(mtd->writesize);
+	if (!buffer)
+		return;
+
+	nand_valid = 1;
+}
diff --git a/drivers/net/mtk_eth.c b/drivers/net/mtk_eth.c
index 666ddeb..4fe7ee0 100644
--- a/drivers/net/mtk_eth.c
+++ b/drivers/net/mtk_eth.c
@@ -145,7 +145,8 @@
 enum mtk_soc {
 	SOC_MT7623,
 	SOC_MT7629,
-	SOC_MT7622
+	SOC_MT7622,
+	SOC_MT7621
 };
 
 struct mtk_eth_priv {
@@ -159,9 +160,10 @@
 
 	void __iomem *fe_base;
 	void __iomem *gmac_base;
-	void __iomem *ethsys_base;
 	void __iomem *sgmii_base;
 
+	struct regmap *ethsys_regmap;
+
 	struct mii_dev *mdio_bus;
 	int (*mii_read)(struct mtk_eth_priv *priv, u8 phy, u8 reg);
 	int (*mii_write)(struct mtk_eth_priv *priv, u8 phy, u8 reg, u16 val);
@@ -233,7 +235,12 @@
 static void mtk_ethsys_rmw(struct mtk_eth_priv *priv, u32 reg, u32 clr,
 			   u32 set)
 {
-	clrsetbits_le32(priv->ethsys_base + reg, clr, set);
+	uint val;
+
+	regmap_read(priv->ethsys_regmap, reg, &val);
+	val &= ~clr;
+	val |= set;
+	regmap_write(priv->ethsys_regmap, reg, val);
 }
 
 /* Direct MDIO clause 22/45 access via SoC */
@@ -669,12 +676,18 @@
 static int mt7530_setup(struct mtk_eth_priv *priv)
 {
 	u16 phy_addr, phy_val;
-	u32 val;
+	u32 val, txdrv;
 	int i;
 
-	/* Select 250MHz clk for RGMII mode */
-	mtk_ethsys_rmw(priv, ETHSYS_CLKCFG0_REG,
-		       ETHSYS_TRGMII_CLK_SEL362_5, 0);
+	if (priv->soc != SOC_MT7621) {
+		/* Select 250MHz clk for RGMII mode */
+		mtk_ethsys_rmw(priv, ETHSYS_CLKCFG0_REG,
+			       ETHSYS_TRGMII_CLK_SEL362_5, 0);
+
+		txdrv = 8;
+	} else {
+		txdrv = 4;
+	}
 
 	/* Modify HWTRAP first to allow direct access to internal PHYs */
 	mt753x_reg_read(priv, HWTRAP_REG, &val);
@@ -732,7 +745,8 @@
 	/* Lower Tx Driving for TRGMII path */
 	for (i = 0 ; i < NUM_TRGMII_CTRL ; i++)
 		mt753x_reg_write(priv, MT7530_TRGMII_TD_ODT(i),
-				 (8 << TD_DM_DRVP_S) | (8 << TD_DM_DRVN_S));
+				 (txdrv << TD_DM_DRVP_S) |
+				 (txdrv << TD_DM_DRVN_S));
 
 	for (i = 0 ; i < NUM_TRGMII_CTRL; i++)
 		mt753x_reg_rmw(priv, MT7530_TRGMII_RD(i), RD_TAP_M, 16);
@@ -1419,7 +1433,7 @@
 
 	priv->soc = dev_get_driver_data(dev);
 
-	pdata->iobase = dev_read_addr(dev);
+	pdata->iobase = (phys_addr_t)dev_remap_addr(dev);
 
 	/* get corresponding ethsys phandle */
 	ret = dev_read_phandle_with_args(dev, "mediatek,ethsys", NULL, 0, 0,
@@ -1427,15 +1441,9 @@
 	if (ret)
 		return ret;
 
-	regmap = syscon_node_to_regmap(args.node);
-	if (IS_ERR(regmap))
-		return PTR_ERR(regmap);
-
-	priv->ethsys_base = regmap_get_range(regmap, 0);
-	if (!priv->ethsys_base) {
-		dev_err(dev, "Unable to find ethsys\n");
-		return -ENODEV;
-	}
+	priv->ethsys_regmap = syscon_node_to_regmap(args.node);
+	if (IS_ERR(priv->ethsys_regmap))
+		return PTR_ERR(priv->ethsys_regmap);
 
 	/* Reset controllers */
 	ret = reset_get_by_name(dev, "fe", &priv->rst_fe);
@@ -1540,6 +1548,7 @@
 	{ .compatible = "mediatek,mt7629-eth", .data = SOC_MT7629 },
 	{ .compatible = "mediatek,mt7623-eth", .data = SOC_MT7623 },
 	{ .compatible = "mediatek,mt7622-eth", .data = SOC_MT7622 },
+	{ .compatible = "mediatek,mt7621-eth", .data = SOC_MT7621 },
 	{}
 };
 
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index 4a3856d..c25b42c 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -266,7 +266,7 @@
 config PHY_MTK_TPHY
 	bool "MediaTek T-PHY Driver"
 	depends on PHY
-	depends on ARCH_MEDIATEK
+	depends on ARCH_MEDIATEK || SOC_MT7621
 	help
 	  MediaTek T-PHY driver supports usb2.0, usb3.0 ports, PCIe and
 	  SATA, and meanwhile supports two version T-PHY which have
diff --git a/drivers/pinctrl/mtmips/Kconfig b/drivers/pinctrl/mtmips/Kconfig
index 844d5b7..456f3ea 100644
--- a/drivers/pinctrl/mtmips/Kconfig
+++ b/drivers/pinctrl/mtmips/Kconfig
@@ -12,6 +12,15 @@
 	  The driver is controlled by a device tree node which contains
 	  the pin mux functions for each available pin groups.
 
+config PINCTRL_MT7621
+	bool "MediaTek MT7621 pin control driver"
+	select PINCTRL_MTMIPS
+	depends on SOC_MT7621 && PINCTRL_GENERIC
+	help
+	  Support pin multiplexing control on MediaTek MT7621.
+	  The driver is controlled by a device tree node which contains
+	  the pin mux functions for each available pin groups.
+
 config PINCTRL_MT7628
 	bool "MediaTek MT7628 pin control driver"
 	select PINCTRL_MTMIPS
diff --git a/drivers/pinctrl/mtmips/Makefile b/drivers/pinctrl/mtmips/Makefile
index ba945a8..8fece4f 100644
--- a/drivers/pinctrl/mtmips/Makefile
+++ b/drivers/pinctrl/mtmips/Makefile
@@ -5,4 +5,5 @@
 
 # SoC Drivers
 obj-$(CONFIG_PINCTRL_MT7620) += pinctrl-mt7620.o
+obj-$(CONFIG_PINCTRL_MT7621) += pinctrl-mt7621.o
 obj-$(CONFIG_PINCTRL_MT7628) += pinctrl-mt7628.o
diff --git a/drivers/pinctrl/mtmips/pinctrl-mt7621.c b/drivers/pinctrl/mtmips/pinctrl-mt7621.c
new file mode 100644
index 0000000..3e98a01
--- /dev/null
+++ b/drivers/pinctrl/mtmips/pinctrl-mt7621.c
@@ -0,0 +1,306 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2022 MediaTek Inc. All rights reserved.
+ *
+ * Author: Weijie Gao <weijie.gao@mediatek.com>
+ */
+
+#include <dm.h>
+#include <dm/pinctrl.h>
+#include <dm/device_compat.h>
+#include <linux/bitops.h>
+#include <linux/io.h>
+
+#include "pinctrl-mtmips-common.h"
+
+#define SYSC_MAP_SIZE			0x100
+
+#define PAD_UART1_GPIO0_OFS		0x00
+#define PAD_UART3_I2C_OFS		0x04
+#define PAD_UART2_JTAG_OFS		0x08
+#define PAD_PERST_WDT_OFS		0x0c
+#define PAD_RGMII2_MDIO_OFS		0x10
+#define PAD_SDXC_SPI_OFS		0x14
+#define GPIOMODE_OFS			0x18
+#define PAD_BOPT_ESWINT_OFS		0x28
+
+#define ESWINT_SHIFT			20
+#define SDXC_SHIFT			18
+#define SPI_SHIFT			16
+#define RGMII2_SHIFT			15
+#define RGMII1_SHIFT			14
+#define MDIO_SHIFT			12
+#define PERST_SHIFT			10
+#define WDT_SHIFT			8
+#define JTAG_SHIFT			7
+#define UART2_SHIFT			5
+#define UART3_SHIFT			3
+#define I2C_SHIFT			2
+#define UART1_SHIFT			1
+#define GPIO0_SHIFT			0 /* Dummy */
+
+#define GM4_MASK			3
+
+#define E4_E2_M				0x03
+#define E4_E2_S				4
+#define PULL_UP				BIT(3)
+#define PULL_DOWN			BIT(2)
+#define SMT				BIT(1)
+#define SR				BIT(0)
+
+struct mt7621_pinctrl_priv {
+	struct mtmips_pinctrl_priv mp;
+};
+
+#if CONFIG_IS_ENABLED(PINMUX)
+static const struct mtmips_pmx_func esw_int_grp[] = {
+	FUNC("gpio", 1),
+	FUNC("esw int", 0),
+};
+
+static const struct mtmips_pmx_func sdxc_grp[] = {
+	FUNC("nand", 2),
+	FUNC("gpio", 1),
+	FUNC("sdxc", 0),
+};
+
+static const struct mtmips_pmx_func spi_grp[] = {
+	FUNC("nand", 2),
+	FUNC("gpio", 1),
+	FUNC("spi", 0),
+};
+
+static const struct mtmips_pmx_func rgmii2_grp[] = {
+	FUNC("gpio", 1),
+	FUNC("rgmii", 0),
+};
+
+static const struct mtmips_pmx_func rgmii1_grp[] = {
+	FUNC("gpio", 1),
+	FUNC("rgmii", 0),
+};
+
+static const struct mtmips_pmx_func mdio_grp[] = {
+	FUNC("gpio", 1),
+	FUNC("mdio", 0),
+};
+
+static const struct mtmips_pmx_func perst_grp[] = {
+	FUNC("refclk", 2),
+	FUNC("gpio", 1),
+	FUNC("pcie reset", 0),
+};
+
+static const struct mtmips_pmx_func wdt_grp[] = {
+	FUNC("refclk", 2),
+	FUNC("gpio", 1),
+	FUNC("wdt rst", 0),
+};
+
+static const struct mtmips_pmx_func jtag_grp[] = {
+	FUNC("gpio", 1),
+	FUNC("jtag", 0),
+};
+
+static const struct mtmips_pmx_func uart2_grp[] = {
+	FUNC("spdif", 3),
+	FUNC("pcm", 2),
+	FUNC("gpio", 1),
+	FUNC("uart", 0),
+};
+
+static const struct mtmips_pmx_func uart3_grp[] = {
+	FUNC("spdif", 3),
+	FUNC("i2s", 2),
+	FUNC("gpio", 1),
+	FUNC("uart", 0),
+};
+
+static const struct mtmips_pmx_func i2c_grp[] = {
+	FUNC("gpio", 1),
+	FUNC("i2c", 0),
+};
+
+static const struct mtmips_pmx_func uart1_grp[] = {
+	FUNC("gpio", 1),
+	FUNC("uart", 0),
+};
+
+static const struct mtmips_pmx_func gpio0_grp[] = {
+	FUNC("gpio", 0),
+};
+
+static const struct mtmips_pmx_group mt7621_pmx_data[] = {
+	GRP_PCONF("esw int", esw_int_grp, GPIOMODE_OFS, ESWINT_SHIFT, 1,
+		  PAD_BOPT_ESWINT_OFS, 0),
+	GRP_PCONF("sdxc", sdxc_grp, GPIOMODE_OFS, SDXC_SHIFT, GM4_MASK,
+		  PAD_SDXC_SPI_OFS, 16),
+	GRP_PCONF("spi", spi_grp, GPIOMODE_OFS, SPI_SHIFT, GM4_MASK,
+		  PAD_SDXC_SPI_OFS, 0),
+	GRP_PCONF("rgmii2", rgmii2_grp, GPIOMODE_OFS, RGMII2_SHIFT, 1,
+		  PAD_RGMII2_MDIO_OFS, 16),
+	GRP("rgmii1", rgmii1_grp, GPIOMODE_OFS, RGMII1_SHIFT, 1),
+	GRP_PCONF("mdio", mdio_grp, GPIOMODE_OFS, MDIO_SHIFT, GM4_MASK,
+		  PAD_RGMII2_MDIO_OFS, 0),
+	GRP_PCONF("pcie reset", perst_grp, GPIOMODE_OFS, PERST_SHIFT, GM4_MASK,
+		  PAD_PERST_WDT_OFS, 16),
+	GRP_PCONF("wdt", wdt_grp, GPIOMODE_OFS, WDT_SHIFT, GM4_MASK,
+		  PAD_PERST_WDT_OFS, 0),
+	GRP_PCONF("jtag", jtag_grp, GPIOMODE_OFS, JTAG_SHIFT, 1,
+		  PAD_UART2_JTAG_OFS, 16),
+	GRP_PCONF("uart2", uart2_grp, GPIOMODE_OFS, UART2_SHIFT, GM4_MASK,
+		  PAD_UART2_JTAG_OFS, 0),
+	GRP_PCONF("uart3", uart3_grp, GPIOMODE_OFS, UART3_SHIFT, GM4_MASK,
+		  PAD_UART3_I2C_OFS, 16),
+	GRP_PCONF("i2c", i2c_grp, GPIOMODE_OFS, I2C_SHIFT, 1,
+		  PAD_UART3_I2C_OFS, 0),
+	GRP_PCONF("uart1", uart1_grp, GPIOMODE_OFS, UART1_SHIFT, 1,
+		  PAD_UART1_GPIO0_OFS, 16),
+	GRP_PCONF("gpio0", gpio0_grp, GPIOMODE_OFS, GPIO0_SHIFT, 1,
+		  PAD_UART1_GPIO0_OFS, 0),
+};
+
+static int mt7621_get_groups_count(struct udevice *dev)
+{
+	return ARRAY_SIZE(mt7621_pmx_data);
+}
+
+static const char *mt7621_get_group_name(struct udevice *dev,
+					 unsigned int selector)
+{
+	return mt7621_pmx_data[selector].name;
+}
+#endif /* CONFIG_IS_ENABLED(PINMUX) */
+
+#if CONFIG_IS_ENABLED(PINCONF)
+static const struct pinconf_param mt7621_conf_params[] = {
+	{ "bias-disable", PIN_CONFIG_BIAS_DISABLE, 0 },
+	{ "bias-pull-up", PIN_CONFIG_BIAS_PULL_UP, 1 },
+	{ "bias-pull-down", PIN_CONFIG_BIAS_PULL_DOWN, 1 },
+	{ "input-schmitt-enable", PIN_CONFIG_INPUT_SCHMITT_ENABLE, 1 },
+	{ "input-schmitt-disable", PIN_CONFIG_INPUT_SCHMITT_ENABLE, 0 },
+	{ "drive-strength", PIN_CONFIG_DRIVE_STRENGTH, 0 },
+	{ "slew-rate", PIN_CONFIG_SLEW_RATE, 0 },
+};
+
+static const u32 mt7621_pconf_drv_strength_tbl[] = {2, 4, 6, 8};
+
+static int mt7621_pinconf_group_set(struct udevice *dev,
+				    unsigned int group_selector,
+				    unsigned int param, unsigned int arg)
+{
+	struct mt7621_pinctrl_priv *priv = dev_get_priv(dev);
+	const struct mtmips_pmx_group *grp = &mt7621_pmx_data[group_selector];
+	u32 clr = 0, set = 0;
+	int i;
+
+	if (!grp->pconf_avail)
+		return 0;
+
+	switch (param) {
+	case PIN_CONFIG_BIAS_DISABLE:
+		clr = PULL_UP | PULL_DOWN;
+		break;
+
+	case PIN_CONFIG_BIAS_PULL_UP:
+		clr = PULL_DOWN;
+		set = PULL_UP;
+		break;
+
+	case PIN_CONFIG_BIAS_PULL_DOWN:
+		clr = PULL_UP;
+		set = PULL_DOWN;
+		break;
+
+	case PIN_CONFIG_INPUT_SCHMITT_ENABLE:
+		if (arg)
+			set = SMT;
+		else
+			clr = SMT;
+		break;
+
+	case PIN_CONFIG_DRIVE_STRENGTH:
+		for (i = 0; i < ARRAY_SIZE(mt7621_pconf_drv_strength_tbl); i++)
+			if (mt7621_pconf_drv_strength_tbl[i] == arg)
+				break;
+
+		if (i >= ARRAY_SIZE(mt7621_pconf_drv_strength_tbl))
+			return -EINVAL;
+
+		clr = E4_E2_M << E4_E2_S;
+		set = i << E4_E2_S;
+		break;
+
+	case PIN_CONFIG_SLEW_RATE:
+		if (arg)
+			set = SR;
+		else
+			clr = SR;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	mtmips_pinctrl_reg_set(&priv->mp, grp->pconf_reg, grp->pconf_shift,
+			       clr, set);
+
+	return 0;
+}
+#endif
+
+static int mt7621_pinctrl_probe(struct udevice *dev)
+{
+	struct mt7621_pinctrl_priv *priv = dev_get_priv(dev);
+	int ret = 0;
+
+#if CONFIG_IS_ENABLED(PINMUX)
+	ret = mtmips_pinctrl_probe(&priv->mp, ARRAY_SIZE(mt7621_pmx_data),
+				   mt7621_pmx_data);
+#endif /* CONFIG_IS_ENABLED(PINMUX) */
+
+	return ret;
+}
+
+static int mt7621_pinctrl_of_to_plat(struct udevice *dev)
+{
+	struct mt7621_pinctrl_priv *priv = dev_get_priv(dev);
+
+	priv->mp.base = (void __iomem *)dev_remap_addr_index(dev, 0);
+
+	if (!priv->mp.base)
+		return -EINVAL;
+
+	return 0;
+}
+
+static const struct pinctrl_ops mt7621_pinctrl_ops = {
+#if CONFIG_IS_ENABLED(PINMUX)
+	.get_groups_count = mt7621_get_groups_count,
+	.get_group_name = mt7621_get_group_name,
+	.get_functions_count = mtmips_get_functions_count,
+	.get_function_name = mtmips_get_function_name,
+	.pinmux_group_set = mtmips_pinmux_group_set,
+#endif /* CONFIG_IS_ENABLED(PINMUX) */
+#if CONFIG_IS_ENABLED(PINCONF)
+	.pinconf_num_params = ARRAY_SIZE(mt7621_conf_params),
+	.pinconf_params = mt7621_conf_params,
+	.pinconf_group_set = mt7621_pinconf_group_set,
+#endif /* CONFIG_IS_ENABLED(PINCONF) */
+	.set_state = pinctrl_generic_set_state,
+};
+
+static const struct udevice_id mt7621_pinctrl_ids[] = {
+	{ .compatible = "mediatek,mt7621-pinctrl" },
+	{ }
+};
+
+U_BOOT_DRIVER(mt7621_pinctrl) = {
+	.name = "mt7621-pinctrl",
+	.id = UCLASS_PINCTRL,
+	.of_match = mt7621_pinctrl_ids,
+	.of_to_plat = mt7621_pinctrl_of_to_plat,
+	.ops = &mt7621_pinctrl_ops,
+	.probe = mt7621_pinctrl_probe,
+	.priv_auto = sizeof(struct mt7621_pinctrl_priv),
+};
diff --git a/drivers/pinctrl/mtmips/pinctrl-mtmips-common.c b/drivers/pinctrl/mtmips/pinctrl-mtmips-common.c
index e361916..869b781 100644
--- a/drivers/pinctrl/mtmips/pinctrl-mtmips-common.c
+++ b/drivers/pinctrl/mtmips/pinctrl-mtmips-common.c
@@ -13,8 +13,8 @@
 
 #include "pinctrl-mtmips-common.h"
 
-static void mtmips_pinctrl_reg_set(struct mtmips_pinctrl_priv *priv,
-				   u32 reg, u32 shift, u32 mask, u32 value)
+void mtmips_pinctrl_reg_set(struct mtmips_pinctrl_priv *priv,
+			    u32 reg, u32 shift, u32 mask, u32 value)
 {
 	u32 val;
 
diff --git a/drivers/pinctrl/mtmips/pinctrl-mtmips-common.h b/drivers/pinctrl/mtmips/pinctrl-mtmips-common.h
index b51d8f0..1f1023e 100644
--- a/drivers/pinctrl/mtmips/pinctrl-mtmips-common.h
+++ b/drivers/pinctrl/mtmips/pinctrl-mtmips-common.h
@@ -22,6 +22,10 @@
 	u32 shift;
 	char mask;
 
+	int pconf_avail;
+	u32 pconf_reg;
+	u32 pconf_shift;
+
 	int nfuncs;
 	const struct mtmips_pmx_func *funcs;
 };
@@ -42,6 +46,14 @@
 	{ .name = (_name), .reg = (_reg), .shift = (_shift), .mask = (_mask), \
 	  .funcs = (_funcs), .nfuncs = ARRAY_SIZE(_funcs) }
 
+#define GRP_PCONF(_name, _funcs, _reg, _shift, _mask, _pconf_reg, _pconf_shift) \
+	{ .name = (_name), .reg = (_reg), .shift = (_shift), .mask = (_mask), \
+	  .funcs = (_funcs), .nfuncs = ARRAY_SIZE(_funcs), .pconf_avail = 1, \
+	  .pconf_reg = (_pconf_reg), .pconf_shift = (_pconf_shift) }
+
+void mtmips_pinctrl_reg_set(struct mtmips_pinctrl_priv *priv,
+			    u32 reg, u32 shift, u32 mask, u32 value);
+
 int mtmips_get_functions_count(struct udevice *dev);
 const char *mtmips_get_function_name(struct udevice *dev,
 				     unsigned int selector);
diff --git a/drivers/rng/stm32mp1_rng.c b/drivers/rng/stm32mp1_rng.c
index 8ea00e3..89da78c 100644
--- a/drivers/rng/stm32mp1_rng.c
+++ b/drivers/rng/stm32mp1_rng.c
@@ -18,22 +18,28 @@
 #include <linux/iopoll.h>
 #include <linux/kernel.h>
 
-#define RNG_CR 0x00
-#define RNG_CR_RNGEN BIT(2)
-#define RNG_CR_CED BIT(5)
+#define RNG_CR		0x00
+#define RNG_CR_RNGEN	BIT(2)
+#define RNG_CR_CED	BIT(5)
+#define RNG_CR_CONDRST	BIT(30)
 
-#define RNG_SR 0x04
-#define RNG_SR_SEIS BIT(6)
-#define RNG_SR_CEIS BIT(5)
-#define RNG_SR_SECS BIT(2)
-#define RNG_SR_DRDY BIT(0)
+#define RNG_SR		0x04
+#define RNG_SR_SEIS	BIT(6)
+#define RNG_SR_CEIS	BIT(5)
+#define RNG_SR_SECS	BIT(2)
+#define RNG_SR_DRDY	BIT(0)
 
-#define RNG_DR 0x08
+#define RNG_DR		0x08
+
+struct stm32_rng_data {
+	bool has_cond_reset;
+};
 
 struct stm32_rng_plat {
 	fdt_addr_t base;
 	struct clk clk;
 	struct reset_ctl rst;
+	const struct stm32_rng_data *data;
 };
 
 static int stm32_rng_read(struct udevice *dev, void *data, size_t len)
@@ -83,18 +89,36 @@
 static int stm32_rng_init(struct stm32_rng_plat *pdata)
 {
 	int err;
+	u32 cr, sr;
 
 	err = clk_enable(&pdata->clk);
 	if (err)
 		return err;
 
+	cr = readl(pdata->base + RNG_CR);
+
 	/* Disable CED */
-	writel(RNG_CR_RNGEN | RNG_CR_CED, pdata->base + RNG_CR);
+	cr |= RNG_CR_CED;
+	if (pdata->data->has_cond_reset) {
+		cr |= RNG_CR_CONDRST;
+		writel(cr, pdata->base + RNG_CR);
+		cr &= ~RNG_CR_CONDRST;
+		writel(cr, pdata->base + RNG_CR);
+		err = readl_poll_timeout(pdata->base + RNG_CR, cr,
+					 (!(cr & RNG_CR_CONDRST)), 10000);
+		if (err)
+			return err;
+	}
 
 	/* clear error indicators */
 	writel(0, pdata->base + RNG_SR);
 
-	return 0;
+	cr |= RNG_CR_RNGEN;
+	writel(cr, pdata->base + RNG_CR);
+
+	err = readl_poll_timeout(pdata->base + RNG_SR, sr,
+				 sr & RNG_SR_DRDY, 10000);
+	return err;
 }
 
 static int stm32_rng_cleanup(struct stm32_rng_plat *pdata)
@@ -108,6 +132,8 @@
 {
 	struct stm32_rng_plat *pdata = dev_get_plat(dev);
 
+	pdata->data = (struct stm32_rng_data *)dev_get_driver_data(dev);
+
 	reset_assert(&pdata->rst);
 	udelay(20);
 	reset_deassert(&pdata->rst);
@@ -146,10 +172,17 @@
 	.read = stm32_rng_read,
 };
 
+static const struct stm32_rng_data stm32mp13_rng_data = {
+	.has_cond_reset = true,
+};
+
+static const struct stm32_rng_data stm32_rng_data = {
+	.has_cond_reset = false,
+};
+
 static const struct udevice_id stm32_rng_match[] = {
-	{
-		.compatible = "st,stm32-rng",
-	},
+	{.compatible = "st,stm32mp13-rng", .data = (ulong)&stm32mp13_rng_data},
+	{.compatible = "st,stm32-rng", .data = (ulong)&stm32_rng_data},
 	{},
 };
 
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 766d563..75b7945 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -254,7 +254,7 @@
 
 config MT7621_SPI
 	bool "MediaTek MT7621 SPI driver"
-	depends on SOC_MT7628
+	depends on SOC_MT7621 || SOC_MT7628
 	help
 	  Enable the MT7621 SPI driver. This driver can be used to access
 	  the SPI NOR flash on platforms embedding this Ralink / MediaTek
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index 31ae9f7..c3b97f4 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -42,7 +42,7 @@
 
 config USB_XHCI_MTK
 	bool "Support for MediaTek on-chip xHCI USB controller"
-	depends on ARCH_MEDIATEK
+	depends on ARCH_MEDIATEK || SOC_MT7621
 	help
 	  Enables support for the on-chip xHCI controller on MediaTek SoCs.
 
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 532ada8..6043fe7 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -191,7 +191,7 @@
 
 config WDT_MT7621
 	bool "MediaTek MT7621 watchdog timer support"
-	depends on WDT && SOC_MT7628
+	depends on WDT && (SOC_MT7621 || SOC_MT7628)
 	help
 	  Select this to enable Ralink / Mediatek watchdog timer,
 	  which can be found on some MediaTek chips.
diff --git a/include/configs/mt7621.h b/include/configs/mt7621.h
new file mode 100644
index 0000000..1f68997
--- /dev/null
+++ b/include/configs/mt7621.h
@@ -0,0 +1,43 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2022 MediaTek Inc. All rights reserved.
+ *
+ * Author: Weijie Gao <weijie.gao@mediatek.com>
+ */
+
+#ifndef __CONFIG_MT7621_H
+#define __CONFIG_MT7621_H
+
+#define CONFIG_SYS_MIPS_TIMER_FREQ	440000000
+
+#define CONFIG_SYS_SDRAM_BASE		0x80000000
+
+#define CONFIG_VERY_BIG_RAM
+#define CONFIG_MAX_MEM_MAPPED		0x1c000000
+
+#define CONFIG_SYS_INIT_SP_OFFSET	0x800000
+
+#define CONFIG_SYS_NONCACHED_MEMORY	0x100000
+
+/* MMC */
+#define MMC_SUPPORTS_TUNING
+
+/* NAND */
+#define CONFIG_SYS_MAX_NAND_DEVICE	1
+
+/* Serial SPL */
+#if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_SERIAL)
+#define CONFIG_SYS_NS16550_MEM32
+#define CONFIG_SYS_NS16550_CLK		50000000
+#define CONFIG_SYS_NS16550_REG_SIZE	-4
+#define CONFIG_SYS_NS16550_COM1		0xbe000c00
+#endif
+
+/* Serial common */
+#define CONFIG_SYS_BAUDRATE_TABLE	{ 9600, 19200, 38400, 57600, 115200, \
+					  230400, 460800, 921600 }
+
+/* Dummy value */
+#define CONFIG_SYS_UBOOT_BASE		0
+
+#endif /* __CONFIG_MT7621_H */
diff --git a/include/configs/stm32mp13_st_common.h b/include/configs/stm32mp13_st_common.h
index ec64b12..c51022b 100644
--- a/include/configs/stm32mp13_st_common.h
+++ b/include/configs/stm32mp13_st_common.h
@@ -14,4 +14,9 @@
 
 #include <configs/stm32mp13_common.h>
 
+/* uart with on-board st-link */
+#define CONFIG_SYS_BAUDRATE_TABLE      { 9600, 19200, 38400, 57600, 115200, \
+					 230400, 460800, 921600, \
+					 1000000, 2000000, 4000000}
+
 #endif
diff --git a/include/configs/stm32mp15_st_common.h b/include/configs/stm32mp15_st_common.h
index 37b216e..6bdc286 100644
--- a/include/configs/stm32mp15_st_common.h
+++ b/include/configs/stm32mp15_st_common.h
@@ -48,7 +48,8 @@
 	ST_STM32MP1_BOOTCMD \
 	STM32MP_PARTS_DEFAULT \
 	BOOTENV \
-	STM32MP_EXTRA
+	STM32MP_EXTRA \
+	STM32MP_BOARD_EXTRA_ENV
 
 #endif
 #endif
diff --git a/include/dt-bindings/clock/mt7621-clk.h b/include/dt-bindings/clock/mt7621-clk.h
new file mode 100644
index 0000000..978c679
--- /dev/null
+++ b/include/dt-bindings/clock/mt7621-clk.h
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2022 MediaTek Inc. All rights reserved.
+ *
+ * Author: Weijie Gao <weijie.gao@mediatek.com>
+ */
+
+#ifndef _DT_BINDINGS_MT7621_CLK_H_
+#define _DT_BINDINGS_MT7621_CLK_H_
+
+#define MT7621_CLK_XTAL		0
+#define MT7621_CLK_CPU		1
+#define MT7621_CLK_BUS		2
+#define MT7621_CLK_50M		3
+#define MT7621_CLK_125M		4
+#define MT7621_CLK_150M		5
+#define MT7621_CLK_250M		6
+#define MT7621_CLK_270M		7
+
+#define MT7621_CLK_HSDMA	8
+#define MT7621_CLK_FE		9
+#define MT7621_CLK_SP_DIVTX	10
+#define MT7621_CLK_TIMER	11
+#define MT7621_CLK_PCM		12
+#define MT7621_CLK_PIO		13
+#define MT7621_CLK_GDMA		14
+#define MT7621_CLK_NAND		15
+#define MT7621_CLK_I2C		16
+#define MT7621_CLK_I2S		17
+#define MT7621_CLK_SPI		18
+#define MT7621_CLK_UART1	19
+#define MT7621_CLK_UART2	20
+#define MT7621_CLK_UART3	21
+#define MT7621_CLK_ETH		22
+#define MT7621_CLK_PCIE0	23
+#define MT7621_CLK_PCIE1	24
+#define MT7621_CLK_PCIE2	25
+#define MT7621_CLK_CRYPTO	26
+#define MT7621_CLK_SHXC		27
+
+#define MT7621_CLK_MAX		28
+
+/* for u-boot only */
+#define MT7621_CLK_DDR		29
+
+#endif /* _DT_BINDINGS_MT7621_CLK_H_ */
diff --git a/include/dt-bindings/clock/stm32fx-clock.h b/include/dt-bindings/clock/stm32fx-clock.h
index 1cc89c5..e5dad05 100644
--- a/include/dt-bindings/clock/stm32fx-clock.h
+++ b/include/dt-bindings/clock/stm32fx-clock.h
@@ -7,10 +7,10 @@
  */
 
 /*
- * List of clocks wich are not derived from system clock (SYSCLOCK)
+ * List of clocks which are not derived from system clock (SYSCLOCK)
  *
  * The index of these clocks is the secondary index of DT bindings
- * (see Documentatoin/devicetree/bindings/clock/st,stm32-rcc.txt)
+ * (see Documentation/devicetree/bindings/clock/st,stm32-rcc.txt)
  *
  * e.g:
 	<assigned-clocks = <&rcc 1 CLK_LSE>;
diff --git a/include/dt-bindings/clock/stm32mp1-clks.h b/include/dt-bindings/clock/stm32mp1-clks.h
index 4cdaf13..25e8cfd 100644
--- a/include/dt-bindings/clock/stm32mp1-clks.h
+++ b/include/dt-bindings/clock/stm32mp1-clks.h
@@ -248,4 +248,27 @@
 
 #define STM32MP1_LAST_CLK 232
 
+/* SCMI clock identifiers */
+#define CK_SCMI_HSE		0
+#define CK_SCMI_HSI		1
+#define CK_SCMI_CSI		2
+#define CK_SCMI_LSE		3
+#define CK_SCMI_LSI		4
+#define CK_SCMI_PLL2_Q		5
+#define CK_SCMI_PLL2_R		6
+#define CK_SCMI_MPU		7
+#define CK_SCMI_AXI		8
+#define CK_SCMI_BSEC		9
+#define CK_SCMI_CRYP1		10
+#define CK_SCMI_GPIOZ		11
+#define CK_SCMI_HASH1		12
+#define CK_SCMI_I2C4		13
+#define CK_SCMI_I2C6		14
+#define CK_SCMI_IWDG1		15
+#define CK_SCMI_RNG1		16
+#define CK_SCMI_RTC		17
+#define CK_SCMI_RTCAPB		18
+#define CK_SCMI_SPI6		19
+#define CK_SCMI_USART1		20
+
 #endif /* _DT_BINDINGS_STM32MP1_CLKS_H_ */
diff --git a/include/dt-bindings/pinctrl/stm32-pinfunc.h b/include/dt-bindings/pinctrl/stm32-pinfunc.h
index e6e0780..e6fb8ad 100644
--- a/include/dt-bindings/pinctrl/stm32-pinfunc.h
+++ b/include/dt-bindings/pinctrl/stm32-pinfunc.h
@@ -39,3 +39,4 @@
 #define STM32MP_PKG_AD	0x8
 
 #endif /* _DT_BINDINGS_STM32_PINFUNC_H */
+
diff --git a/include/dt-bindings/reset/mt7621-reset.h b/include/dt-bindings/reset/mt7621-reset.h
new file mode 100644
index 0000000..8e4341f
--- /dev/null
+++ b/include/dt-bindings/reset/mt7621-reset.h
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2022 MediaTek Inc. All rights reserved.
+ *
+ * Author: Weijie Gao <weijie.gao@mediatek.com>
+ */
+
+#ifndef _DT_BINDINGS_MT7621_RESET_H_
+#define _DT_BINDINGS_MT7621_RESET_H_
+
+#define RST_PPE			31
+#define RST_SDXC		30
+#define RST_CRYPTO		29
+#define RST_AUX_STCK		28
+#define RST_PCIE2		26
+#define RST_PCIE1		25
+#define RST_PCIE0		24
+#define RST_GMAC		23
+#define RST_UART3		21
+#define RST_UART2		20
+#define RST_UART1		19
+#define RST_SPI			18
+#define RST_I2S			17
+#define RST_I2C			16
+#define RST_NFI			15
+#define RST_GDMA		14
+#define RST_PIO			13
+#define RST_PCM			11
+#define RST_MC			10
+#define RST_INTC		9
+#define RST_TIMER		8
+#define RST_SPDIFTX		7
+#define RST_FE			6
+#define RST_HSDMA		5
+#define RST_MCM			2
+#define RST_SYS			0
+
+#endif /* _DT_BINDINGS_MT7621_RESET_H_ */
diff --git a/include/dt-bindings/reset/stm32mp1-resets.h b/include/dt-bindings/reset/stm32mp1-resets.h
index 702da37..4ffa7c3 100644
--- a/include/dt-bindings/reset/stm32mp1-resets.h
+++ b/include/dt-bindings/reset/stm32mp1-resets.h
@@ -106,4 +106,18 @@
 #define GPIOJ_R		19785
 #define GPIOK_R		19786
 
+/* SCMI reset domain identifiers */
+#define RST_SCMI_SPI6		0
+#define RST_SCMI_I2C4		1
+#define RST_SCMI_I2C6		2
+#define RST_SCMI_USART1	3
+#define RST_SCMI_STGEN		4
+#define RST_SCMI_GPIOZ		5
+#define RST_SCMI_CRYP1		6
+#define RST_SCMI_HASH1		7
+#define RST_SCMI_RNG1		8
+#define RST_SCMI_MDMA		9
+#define RST_SCMI_MCU		10
+#define RST_SCMI_MCU_HOLD_BOOT	11
+
 #endif /* _DT_BINDINGS_STM32MP1_RESET_H_ */
diff --git a/include/efi_loader.h b/include/efi_loader.h
index 5b41985..3a63a1f 100644
--- a/include/efi_loader.h
+++ b/include/efi_loader.h
@@ -802,6 +802,9 @@
 	(((_dp)->type == DEVICE_PATH_TYPE_##_type) && \
 	 ((_dp)->sub_type == DEVICE_PATH_SUB_TYPE_##_subtype))
 
+/* template END node: */
+extern const struct efi_device_path END;
+
 /* Indicate supported runtime services */
 efi_status_t efi_init_runtime_supported(void);
 
diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c
index 2493d74..ebffb77 100644
--- a/lib/efi_loader/efi_device_path.c
+++ b/lib/efi_loader/efi_device_path.c
@@ -30,7 +30,7 @@
 #endif
 
 /* template END node: */
-static const struct efi_device_path END = {
+const struct efi_device_path END = {
 	.type     = DEVICE_PATH_TYPE_END,
 	.sub_type = DEVICE_PATH_SUB_TYPE_END,
 	.length   = sizeof(END),
diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib
index c0a5bb9..8e13bf2 100644
--- a/scripts/Makefile.lib
+++ b/scripts/Makefile.lib
@@ -426,8 +426,8 @@
 	$(call cmd,efi_objcopy)
 
 quiet_cmd_efi_ld = LD      $@
-cmd_efi_ld = $(LD) -nostdlib -znocombreloc -T $(EFI_LDS_PATH) -shared \
-		-Bsymbolic -znorelro -s $^ -o $@
+cmd_efi_ld = $(LD) -nostdlib -zexecstack -znocombreloc -T $(EFI_LDS_PATH) \
+		-shared -Bsymbolic -znorelro -s $^ -o $@
 
 EFI_LDS_PATH = $(srctree)/arch/$(ARCH)/lib/$(EFI_LDS)
 
diff --git a/tools/mkimage.c b/tools/mkimage.c
index 0e1198b..597cb3a 100644
--- a/tools/mkimage.c
+++ b/tools/mkimage.c
@@ -11,6 +11,7 @@
 #include "mkimage.h"
 #include "imximage.h"
 #include <fit_common.h>
+#include <getopt.h>
 #include <image.h>
 #include <version.h>
 #ifdef __linux__
@@ -134,6 +135,7 @@
 	fprintf(stderr, "       %s -V ==> print version information and exit\n",
 		params.cmdname);
 	fprintf(stderr, "Use '-T list' to see a list of available image types\n");
+	fprintf(stderr, "Long options are available; read the man page for details\n");
 
 	exit(EXIT_FAILURE);
 }
@@ -156,6 +158,45 @@
 	return 0;
 }
 
+static const char optstring[] =
+	"a:A:b:B:c:C:d:D:e:Ef:Fg:G:i:k:K:ln:N:o:O:p:qrR:stT:vVx";
+
+static const struct option longopts[] = {
+	{ "load-address", required_argument, NULL, 'a' },
+	{ "architecture", required_argument, NULL, 'A' },
+	{ "device-tree", required_argument, NULL, 'b' },
+	{ "alignment", required_argument, NULL, 'B' },
+	{ "comment", required_argument, NULL, 'c' },
+	{ "compression", required_argument, NULL, 'C' },
+	{ "image", required_argument, NULL, 'd' },
+	{ "dtcopts", required_argument, NULL, 'D' },
+	{ "entry-point", required_argument, NULL, 'e' },
+	{ "external", no_argument, NULL, 'E' },
+	{ "fit", required_argument, NULL, 'f' },
+	{ "update", no_argument, NULL, 'F' },
+	{ "key-name-hint", required_argument, NULL, 'g' },
+	{ "key-file", required_argument, NULL, 'G' },
+	{ "help", no_argument, NULL, 'h' },
+	{ "initramfs", required_argument, NULL, 'i' },
+	{ "key-dir", required_argument, NULL, 'k' },
+	{ "key-dest", required_argument, NULL, 'K' },
+	{ "list", no_argument, NULL, 'l' },
+	{ "config", required_argument, NULL, 'n' },
+	{ "engine", required_argument, NULL, 'N' },
+	{ "algo", required_argument, NULL, 'o' },
+	{ "os", required_argument, NULL, 'O' },
+	{ "position", required_argument, NULL, 'p' },
+	{ "quiet", no_argument, NULL, 'q' },
+	{ "key-required", no_argument, NULL, 'r' },
+	{ "secondary-config", required_argument, NULL, 'R' },
+	{ "no-copy", no_argument, NULL, 's' },
+	{ "touch", no_argument, NULL, 't' },
+	{ "type", required_argument, NULL, 'T' },
+	{ "verbose", no_argument, NULL, 'v' },
+	{ "version", no_argument, NULL, 'V' },
+	{ "xip", no_argument, NULL, 'x' },
+};
+
 static void process_args(int argc, char **argv)
 {
 	char *ptr;
@@ -163,8 +204,8 @@
 	char *datafile = NULL;
 	int opt;
 
-	while ((opt = getopt(argc, argv,
-		   "a:A:b:B:c:C:d:D:e:Ef:Fg:G:k:i:K:ln:N:p:o:O:rR:qstT:vVx")) != -1) {
+	while ((opt = getopt_long(argc, argv, optstring,
+				  longopts, NULL)) != -1) {
 		switch (opt) {
 		case 'a':
 			params.addr = strtoull(optarg, &ptr, 16);
diff --git a/tools/mtk_image.c b/tools/mtk_image.c
index 418c5fd..de5ce4d 100644
--- a/tools/mtk_image.c
+++ b/tools/mtk_image.c
@@ -6,7 +6,9 @@
  * Author: Weijie Gao <weijie.gao@mediatek.com>
  */
 
+#include <time.h>
 #include <image.h>
+#include <u-boot/crc.h>
 #include <u-boot/sha256.h>
 #include "imagetool.h"
 #include "mtk_image.h"
@@ -251,17 +253,45 @@
 static enum brlyt_img_type hdr_media;
 static uint32_t hdr_offset;
 static int use_lk_hdr;
+static int use_mt7621_hdr;
 static bool is_arm64_image;
 
 /* LK image name */
 static char lk_name[32] = "U-Boot";
 
+/* CRC32 normal table required by MT7621 image */
+static uint32_t crc32tbl[256];
+
 /* NAND header selected by user */
 static const union nand_boot_header *hdr_nand;
 
 /* GFH header + 2 * 4KB pages of NAND */
 static char hdr_tmp[sizeof(struct gfh_header) + 0x2000];
 
+static uint32_t crc32_normal_cal(uint32_t crc, const void *data, size_t length,
+				 const uint32_t *crc32c_table)
+{
+	const uint8_t *p = data;
+
+	while (length--)
+		crc = crc32c_table[(uint8_t)((crc >> 24) ^ *p++)] ^ (crc << 8);
+
+	return crc;
+}
+
+static void crc32_normal_init(uint32_t *crc32c_table, uint32_t poly)
+{
+	uint32_t v, i, j;
+
+	for (i = 0; i < 256; i++) {
+		v = i << 24;
+		for (j = 0; j < 8; j++)
+			v = (v << 1) ^ ((v & (1 << 31)) ? poly : 0);
+
+		crc32c_table[i] = v;
+	}
+}
+
 static int mtk_image_check_image_types(uint8_t type)
 {
 	if (type == IH_TYPE_MTKIMAGE)
@@ -283,6 +313,7 @@
 	static const char *hdr_offs = "";
 	static const char *nandinfo = "";
 	static const char *lk = "";
+	static const char *mt7621 = "";
 	static const char *arm64_param = "";
 
 	key = buf;
@@ -332,6 +363,9 @@
 			if (!strcmp(key, "lk"))
 				lk = val;
 
+			if (!strcmp(key, "mt7621"))
+				mt7621 = val;
+
 			if (!strcmp(key, "lkname"))
 				snprintf(lk_name, sizeof(lk_name), "%s", val);
 
@@ -352,6 +386,13 @@
 		return 0;
 	}
 
+	/* if user specified MT7621 image header, skip following checks */
+	if (mt7621 && mt7621[0] == '1') {
+		use_mt7621_hdr = 1;
+		free(buf);
+		return 0;
+	}
+
 	/* parse media type */
 	for (i = 0; i < ARRAY_SIZE(brom_images); i++) {
 		if (!strcmp(brom_images[i].name, media)) {
@@ -419,6 +460,13 @@
 		return 0;
 	}
 
+	if (use_mt7621_hdr) {
+		tparams->header_size = image_get_header_size();
+		tparams->hdr = &hdr_tmp;
+		memset(&hdr_tmp, 0, tparams->header_size);
+		return 0;
+	}
+
 	if (hdr_media == BRLYT_TYPE_NAND || hdr_media == BRLYT_TYPE_SNAND)
 		tparams->header_size = 2 * le16_to_cpu(hdr_nand->pagesize);
 	else
@@ -579,9 +627,90 @@
 	return 0;
 }
 
+static uint32_t crc32be_cal(const void *data, size_t length)
+{
+	uint32_t crc = 0;
+	uint8_t c;
+
+	if (crc32tbl[1] != MT7621_IH_CRC_POLYNOMIAL)
+		crc32_normal_init(crc32tbl, MT7621_IH_CRC_POLYNOMIAL);
+
+	crc = crc32_normal_cal(crc, data, length, crc32tbl);
+
+	for (; length; length >>= 8) {
+		c = length & 0xff;
+		crc = crc32_normal_cal(crc, &c, 1, crc32tbl);
+	}
+
+	return ~crc;
+}
+
+static int mtk_image_verify_mt7621_header(const uint8_t *ptr, int print)
+{
+	const image_header_t *hdr = (const image_header_t *)ptr;
+	struct mt7621_nand_header *nhdr;
+	uint32_t spl_size, crcval;
+	image_header_t header;
+	int ret;
+
+	spl_size = image_get_size(hdr);
+
+	if (spl_size > img_size) {
+		if (print)
+			printf("Incomplete SPL image\n");
+		return -1;
+	}
+
+	ret = image_check_hcrc(hdr);
+	if (!ret) {
+		if (print)
+			printf("Bad header CRC\n");
+		return -1;
+	}
+
+	ret = image_check_dcrc(hdr);
+	if (!ret) {
+		if (print)
+			printf("Bad data CRC\n");
+		return -1;
+	}
+
+	/* Copy header so we can blank CRC field for re-calculation */
+	memmove(&header, hdr, image_get_header_size());
+	image_set_hcrc(&header, 0);
+
+	nhdr = (struct mt7621_nand_header *)header.ih_name;
+	crcval = be32_to_cpu(nhdr->crc);
+	nhdr->crc = 0;
+
+	if (crcval != crc32be_cal(&header, image_get_header_size())) {
+		if (print)
+			printf("Bad NAND header CRC\n");
+		return -1;
+	}
+
+	if (print) {
+		printf("Load Address: %08x\n", image_get_load(hdr));
+
+		printf("Image Name:   %.*s\n", MT7621_IH_NMLEN,
+		       image_get_name(hdr));
+
+		if (IMAGE_ENABLE_TIMESTAMP) {
+			printf("Created:      ");
+			genimg_print_time((time_t)image_get_time(hdr));
+		}
+
+		printf("Data Size:    ");
+		genimg_print_size(image_get_data_size(hdr));
+	}
+
+	return 0;
+}
+
 static int mtk_image_verify_header(unsigned char *ptr, int image_size,
 				   struct image_tool_params *params)
 {
+	image_header_t *hdr = (image_header_t *)ptr;
 	union lk_hdr *lk = (union lk_hdr *)ptr;
 
 	/* nothing to verify for LK image header */
@@ -590,6 +719,9 @@
 
 	img_size = image_size;
 
+	if (image_get_magic(hdr) == IH_MAGIC)
+		return mtk_image_verify_mt7621_header(ptr, 0);
+
 	if (!strcmp((char *)ptr, NAND_BOOT_NAME))
 		return mtk_image_verify_nand_header(ptr, 0);
 	else
@@ -600,6 +732,7 @@
 
 static void mtk_image_print_header(const void *ptr)
 {
+	image_header_t *hdr = (image_header_t *)ptr;
 	union lk_hdr *lk = (union lk_hdr *)ptr;
 
 	if (le32_to_cpu(lk->magic) == LK_PART_MAGIC) {
@@ -610,6 +743,11 @@
 
 	printf("Image Type:   MediaTek BootROM Loadable Image\n");
 
+	if (image_get_magic(hdr) == IH_MAGIC) {
+		mtk_image_verify_mt7621_header(ptr, 1);
+		return;
+	}
+
 	if (!strcmp((char *)ptr, NAND_BOOT_NAME))
 		mtk_image_verify_nand_header(ptr, 1);
 	else
@@ -773,6 +911,45 @@
 		 filesize - 2 * le16_to_cpu(hdr_nand->pagesize) - SHA256_SUM_LEN);
 }
 
+static void mtk_image_set_mt7621_header(void *ptr, off_t filesize,
+					uint32_t loadaddr)
+{
+	image_header_t *hdr = (image_header_t *)ptr;
+	struct mt7621_stage1_header *shdr;
+	struct mt7621_nand_header *nhdr;
+	uint32_t datasize, crcval;
+
+	datasize = filesize - image_get_header_size();
+	nhdr = (struct mt7621_nand_header *)hdr->ih_name;
+	shdr = (struct mt7621_stage1_header *)(ptr + image_get_header_size());
+
+	shdr->ep = cpu_to_be32(loadaddr);
+	shdr->stage_size = cpu_to_be32(datasize);
+
+	image_set_magic(hdr, IH_MAGIC);
+	image_set_time(hdr, time(NULL));
+	image_set_size(hdr, datasize);
+	image_set_load(hdr, loadaddr);
+	image_set_ep(hdr, loadaddr);
+	image_set_os(hdr, IH_OS_U_BOOT);
+	image_set_arch(hdr, IH_ARCH_MIPS);
+	image_set_type(hdr, IH_TYPE_STANDALONE);
+	image_set_comp(hdr, IH_COMP_NONE);
+
+	crcval = crc32(0, (uint8_t *)shdr, datasize);
+	image_set_dcrc(hdr, crcval);
+
+	strncpy(nhdr->ih_name, "MT7621 NAND", MT7621_IH_NMLEN);
+
+	nhdr->ih_stage_offset = cpu_to_be32(image_get_header_size());
+
+	crcval = crc32be_cal(hdr, image_get_header_size());
+	nhdr->crc = cpu_to_be32(crcval);
+
+	crcval = crc32(0, (uint8_t *)hdr, image_get_header_size());
+	image_set_hcrc(hdr, crcval);
+}
+
 static void mtk_image_set_header(void *ptr, struct stat *sbuf, int ifd,
 				 struct image_tool_params *params)
 {
@@ -791,6 +968,11 @@
 	img_gen = true;
 	img_size = sbuf->st_size;
 
+	if (use_mt7621_hdr) {
+		mtk_image_set_mt7621_header(ptr, sbuf->st_size, params->addr);
+		return;
+	}
+
 	if (hdr_media == BRLYT_TYPE_NAND || hdr_media == BRLYT_TYPE_SNAND)
 		mtk_image_set_nand_header(ptr, sbuf->st_size, params->addr);
 	else
diff --git a/tools/mtk_image.h b/tools/mtk_image.h
index 7dda71c..d868545 100644
--- a/tools/mtk_image.h
+++ b/tools/mtk_image.h
@@ -200,4 +200,28 @@
 
 #define LK_PART_MAGIC		0x58881688
 
+/* MT7621 NAND SPL image header */
+
+#define MT7621_IH_NMLEN			12
+#define MT7621_IH_CRC_POLYNOMIAL	0x04c11db7
+
+struct mt7621_nand_header {
+	char ih_name[MT7621_IH_NMLEN];
+	uint32_t nand_ac_timing;
+	uint32_t ih_stage_offset;
+	uint32_t ih_bootloader_offset;
+	uint32_t nand_info_1_data;
+	uint32_t crc;
+};
+
+struct mt7621_stage1_header {
+	uint32_t jump_insn[2];
+	uint32_t ep;
+	uint32_t stage_size;
+	uint32_t has_stage2;
+	uint32_t next_ep;
+	uint32_t next_size;
+	uint32_t next_offset;
+};
+
 #endif /* _MTK_IMAGE_H */