Merge tag 'u-boot-imx-20230713' of https://gitlab.denx.de/u-boot/custodians/u-boot-imx

u-boot-imx-20230713
-------------------

Merge for 2023.10.

CI: https://source.denx.de/u-boot/custodians/u-boot-imx/-/pipelines/16888
diff --git a/MAINTAINERS b/MAINTAINERS
index 2477923..87991cc 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1330,7 +1330,7 @@
 F:	doc/usage/sbi.rst
 F:	drivers/sysreset/sysreset_sbi.c
 F:	drivers/timer/andes_plmt_timer.c
-F:	drivers/timer/sifive_clint_timer.c
+F:	drivers/timer/riscv_aclint_timer.c
 F:	tools/prelink-riscv.c
 
 RISC-V CANAAN KENDRYTE K210
diff --git a/Makefile b/Makefile
index 8d9aef4..1d551da 100644
--- a/Makefile
+++ b/Makefile
@@ -3,7 +3,7 @@
 VERSION = 2023
 PATCHLEVEL = 07
 SUBLEVEL =
-EXTRAVERSION = -rc6
+EXTRAVERSION =
 NAME =
 
 # *DOCUMENTATION*
diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
index f6ed059..867cbcb 100644
--- a/arch/riscv/Kconfig
+++ b/arch/riscv/Kconfig
@@ -27,6 +27,10 @@
 config TARGET_STARFIVE_VISIONFIVE2
 	bool "Support StarFive VisionFive2 Board"
 
+config TARGET_TH1520_LPI4A
+	bool "Support Sipeed's TH1520 Lichee PI 4A Board"
+	select SYS_CACHE_SHIFT_6
+
 config TARGET_SIPEED_MAIX
 	bool "Support Sipeed Maix Board"
 	select SYS_CACHE_SHIFT_6
@@ -66,6 +70,7 @@
 source "board/microchip/mpfs_icicle/Kconfig"
 source "board/sifive/unleashed/Kconfig"
 source "board/sifive/unmatched/Kconfig"
+source "board/thead/th1520_lpi4a/Kconfig"
 source "board/openpiton/riscv64/Kconfig"
 source "board/sipeed/maix/Kconfig"
 source "board/starfive/visionfive2/Kconfig"
@@ -185,18 +190,22 @@
 	bool
 	default y if 64BIT
 
-config SIFIVE_CLINT
+config RISCV_ACLINT
 	bool
 	depends on RISCV_MMODE
+	select REGMAP
+	select SYSCON
 	help
-	  The SiFive CLINT block holds memory-mapped control and status registers
+	  The RISC-V ACLINT block holds memory-mapped control and status registers
 	  associated with software and timer interrupts.
 
-config SPL_SIFIVE_CLINT
+config SPL_RISCV_ACLINT
 	bool
 	depends on SPL_RISCV_MMODE
+	select SPL_REGMAP
+	select SPL_SYSCON
 	help
-	  The SiFive CLINT block holds memory-mapped control and status registers
+	  The RISC-V ACLINT block holds memory-mapped control and status registers
 	  associated with software and timer interrupts.
 
 config SIFIVE_CACHE
diff --git a/arch/riscv/cpu/fu540/Kconfig b/arch/riscv/cpu/fu540/Kconfig
index 1604b41..c68209d 100644
--- a/arch/riscv/cpu/fu540/Kconfig
+++ b/arch/riscv/cpu/fu540/Kconfig
@@ -11,7 +11,7 @@
 	imply CPU
 	imply CPU_RISCV
 	imply RISCV_TIMER if (RISCV_SMODE || SPL_RISCV_SMODE)
-	imply SPL_SIFIVE_CLINT
+	imply SPL_RISCV_ACLINT
 	imply CMD_CPU
 	imply SPL_CPU
 	imply SPL_OPENSBI
diff --git a/arch/riscv/cpu/fu740/Kconfig b/arch/riscv/cpu/fu740/Kconfig
index 3e0c1fd..d7ca968 100644
--- a/arch/riscv/cpu/fu740/Kconfig
+++ b/arch/riscv/cpu/fu740/Kconfig
@@ -11,7 +11,7 @@
 	imply CPU
 	imply CPU_RISCV
 	imply RISCV_TIMER if (RISCV_SMODE || SPL_RISCV_SMODE)
-	imply SPL_SIFIVE_CLINT
+	imply SPL_RISCV_ACLINT
 	imply CMD_CPU
 	imply SPL_CPU
 	imply SPL_OPENSBI
diff --git a/arch/riscv/cpu/generic/Kconfig b/arch/riscv/cpu/generic/Kconfig
index e025134..897765c 100644
--- a/arch/riscv/cpu/generic/Kconfig
+++ b/arch/riscv/cpu/generic/Kconfig
@@ -9,8 +9,8 @@
 	imply CPU
 	imply CPU_RISCV
 	imply RISCV_TIMER if (RISCV_SMODE || SPL_RISCV_SMODE)
-	imply SIFIVE_CLINT if RISCV_MMODE
-	imply SPL_SIFIVE_CLINT if SPL_RISCV_MMODE
+	imply RISCV_ACLINT if RISCV_MMODE
+	imply SPL_RISCV_ACLINT if SPL_RISCV_MMODE
 	imply CMD_CPU
 	imply SPL_CPU
 	imply SPL_OPENSBI
diff --git a/arch/riscv/cpu/jh7110/Kconfig b/arch/riscv/cpu/jh7110/Kconfig
index 3f14541..4d95811 100644
--- a/arch/riscv/cpu/jh7110/Kconfig
+++ b/arch/riscv/cpu/jh7110/Kconfig
@@ -25,4 +25,4 @@
 	imply SPL_CPU
 	imply SPL_LOAD_FIT
 	imply SPL_OPENSBI
-	imply SPL_SIFIVE_CLINT
+	imply SPL_RISCV_ACLINT
diff --git a/arch/riscv/cpu/jh7110/spl.c b/arch/riscv/cpu/jh7110/spl.c
index 104f0fe..72adcef 100644
--- a/arch/riscv/cpu/jh7110/spl.c
+++ b/arch/riscv/cpu/jh7110/spl.c
@@ -3,19 +3,49 @@
  * Copyright (C) 2022 StarFive Technology Co., Ltd.
  * Author: Yanhong Wang<yanhong.wang@starfivetech.com>
  */
-
+#include <common.h>
+#include <asm/arch/eeprom.h>
 #include <asm/csr.h>
 #include <asm/sections.h>
 #include <dm.h>
+#include <linux/sizes.h>
 #include <log.h>
+#include <init.h>
 
 #define CSR_U74_FEATURE_DISABLE	0x7c1
 #define L2_LIM_MEM_END	0x81FFFFFUL
 
+DECLARE_GLOBAL_DATA_PTR;
+
+static bool check_ddr_size(phys_size_t size)
+{
+	switch (size) {
+	case SZ_2:
+	case SZ_4:
+	case SZ_8:
+	case SZ_16:
+		return true;
+	default:
+		return false;
+	}
+}
+
 int spl_soc_init(void)
 {
 	int ret;
 	struct udevice *dev;
+	phys_size_t size;
+
+	ret = fdtdec_setup_mem_size_base();
+	if (ret)
+		return ret;
+
+	/* Read the definition of the DDR size from eeprom, and if not,
+	 * use the definition in DT
+	 */
+	size = (get_ddr_size_from_eeprom() >> 16) & 0xFF;
+	if (check_ddr_size(size))
+		gd->ram_size = size << 30;
 
 	/* DDR init */
 	ret = uclass_get_device(UCLASS_RAM, 0, &dev);
diff --git a/arch/riscv/dts/Makefile b/arch/riscv/dts/Makefile
index 1d61eb8..f1525cb 100644
--- a/arch/riscv/dts/Makefile
+++ b/arch/riscv/dts/Makefile
@@ -7,8 +7,8 @@
 dtb-$(CONFIG_TARGET_SIFIVE_UNLEASHED) += hifive-unleashed-a00.dtb
 dtb-$(CONFIG_TARGET_SIFIVE_UNMATCHED) += hifive-unmatched-a00.dtb
 dtb-$(CONFIG_TARGET_SIPEED_MAIX) += k210-maix-bit.dtb
-dtb-$(CONFIG_TARGET_STARFIVE_VISIONFIVE2) += jh7110-starfive-visionfive-2-v1.3b.dtb
-dtb-$(CONFIG_TARGET_STARFIVE_VISIONFIVE2) += jh7110-starfive-visionfive-2-v1.2a.dtb
+dtb-$(CONFIG_TARGET_STARFIVE_VISIONFIVE2) += jh7110-starfive-visionfive-2.dtb
+dtb-$(CONFIG_TARGET_TH1520_LPI4A) += th1520-lichee-pi-4a.dtb
 include $(srctree)/scripts/Makefile.dts
 
 targets += $(dtb-y)
diff --git a/arch/riscv/dts/jh7110-starfive-visionfive-2-u-boot.dtsi b/arch/riscv/dts/jh7110-starfive-visionfive-2-u-boot.dtsi
new file mode 100644
index 0000000..13f69da
--- /dev/null
+++ b/arch/riscv/dts/jh7110-starfive-visionfive-2-u-boot.dtsi
@@ -0,0 +1,106 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/*
+ * Copyright (C) 2023 StarFive Technology Co., Ltd.
+ */
+
+#include "binman.dtsi"
+#include "jh7110-u-boot.dtsi"
+/ {
+	chosen {
+		bootph-pre-ram;
+	};
+
+	firmware {
+		spi0 = &qspi;
+		bootph-pre-ram;
+	};
+
+	config {
+		bootph-pre-ram;
+		u-boot,spl-payload-offset = <0x100000>;
+	};
+
+	memory@40000000 {
+		bootph-pre-ram;
+	};
+};
+
+&uart0 {
+	bootph-pre-ram;
+};
+
+&mmc0 {
+	bootph-pre-ram;
+};
+
+&mmc1 {
+	bootph-pre-ram;
+};
+
+&qspi {
+	bootph-pre-ram;
+
+	nor-flash@0 {
+		bootph-pre-ram;
+	};
+};
+
+&sysgpio {
+	bootph-pre-ram;
+};
+
+&mmc0_pins {
+	bootph-pre-ram;
+	mmc0-pins-rest {
+		bootph-pre-ram;
+	};
+};
+
+&mmc1_pins {
+	bootph-pre-ram;
+	mmc1-pins0 {
+		bootph-pre-ram;
+	};
+
+	mmc1-pins1 {
+		bootph-pre-ram;
+	};
+};
+
+&i2c5_pins {
+	bootph-pre-ram;
+	i2c-pins {
+		bootph-pre-ram;
+	};
+};
+
+&i2c5 {
+	bootph-pre-ram;
+	eeprom@50 {
+		bootph-pre-ram;
+	};
+};
+
+&binman {
+	itb {
+		fit {
+			images {
+				fdt-1 {
+					description = "NAME";
+					load = <0x40400000>;
+					compression = "none";
+
+					uboot_fdt_blob: blob-ext {
+						filename = "u-boot.dtb";
+					};
+				};
+			};
+
+			configurations {
+				conf-1 {
+					fdt = "fdt-1";
+				};
+			};
+		};
+	};
+};
diff --git a/arch/riscv/dts/jh7110-starfive-visionfive-2-v1.2a-u-boot.dtsi b/arch/riscv/dts/jh7110-starfive-visionfive-2-v1.2a-u-boot.dtsi
deleted file mode 100644
index 3c322c5..0000000
--- a/arch/riscv/dts/jh7110-starfive-visionfive-2-v1.2a-u-boot.dtsi
+++ /dev/null
@@ -1,69 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0 OR MIT
-/*
- * Copyright (C) 2022 StarFive Technology Co., Ltd.
- */
-
-#include "binman.dtsi"
-#include "jh7110-u-boot.dtsi"
-/ {
-	chosen {
-		bootph-pre-ram;
-	};
-
-	firmware {
-		spi0 = &qspi;
-		bootph-pre-ram;
-	};
-
-	config {
-		bootph-pre-ram;
-		u-boot,spl-payload-offset = <0x100000>;
-	};
-
-	memory@40000000 {
-		bootph-pre-ram;
-	};
-};
-
-&uart0 {
-	bootph-pre-ram;
-};
-
-&mmc0 {
-	bootph-pre-ram;
-};
-
-&mmc1 {
-	bootph-pre-ram;
-};
-
-&qspi {
-	bootph-pre-ram;
-
-	nor-flash@0 {
-		bootph-pre-ram;
-	};
-};
-
-&sysgpio {
-	bootph-pre-ram;
-};
-
-&mmc0_pins {
-	bootph-pre-ram;
-	mmc0-pins-rest {
-		bootph-pre-ram;
-	};
-};
-
-&mmc1_pins {
-	bootph-pre-ram;
-	mmc1-pins0 {
-		bootph-pre-ram;
-	};
-
-	mmc1-pins1 {
-		bootph-pre-ram;
-	};
-};
-
diff --git a/arch/riscv/dts/jh7110-starfive-visionfive-2-v1.2a.dts b/arch/riscv/dts/jh7110-starfive-visionfive-2-v1.2a.dts
deleted file mode 100644
index b9d26d7..0000000
--- a/arch/riscv/dts/jh7110-starfive-visionfive-2-v1.2a.dts
+++ /dev/null
@@ -1,12 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0 OR MIT
-/*
- * Copyright (C) 2022 StarFive Technology Co., Ltd.
- */
-
-/dts-v1/;
-#include "jh7110-starfive-visionfive-2.dtsi"
-
-/ {
-	model = "StarFive VisionFive 2 v1.2A";
-	compatible = "starfive,visionfive-2-v1.2a", "starfive,jh7110";
-};
diff --git a/arch/riscv/dts/jh7110-starfive-visionfive-2-v1.3b-u-boot.dtsi b/arch/riscv/dts/jh7110-starfive-visionfive-2-v1.3b-u-boot.dtsi
deleted file mode 100644
index 3c322c5..0000000
--- a/arch/riscv/dts/jh7110-starfive-visionfive-2-v1.3b-u-boot.dtsi
+++ /dev/null
@@ -1,69 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0 OR MIT
-/*
- * Copyright (C) 2022 StarFive Technology Co., Ltd.
- */
-
-#include "binman.dtsi"
-#include "jh7110-u-boot.dtsi"
-/ {
-	chosen {
-		bootph-pre-ram;
-	};
-
-	firmware {
-		spi0 = &qspi;
-		bootph-pre-ram;
-	};
-
-	config {
-		bootph-pre-ram;
-		u-boot,spl-payload-offset = <0x100000>;
-	};
-
-	memory@40000000 {
-		bootph-pre-ram;
-	};
-};
-
-&uart0 {
-	bootph-pre-ram;
-};
-
-&mmc0 {
-	bootph-pre-ram;
-};
-
-&mmc1 {
-	bootph-pre-ram;
-};
-
-&qspi {
-	bootph-pre-ram;
-
-	nor-flash@0 {
-		bootph-pre-ram;
-	};
-};
-
-&sysgpio {
-	bootph-pre-ram;
-};
-
-&mmc0_pins {
-	bootph-pre-ram;
-	mmc0-pins-rest {
-		bootph-pre-ram;
-	};
-};
-
-&mmc1_pins {
-	bootph-pre-ram;
-	mmc1-pins0 {
-		bootph-pre-ram;
-	};
-
-	mmc1-pins1 {
-		bootph-pre-ram;
-	};
-};
-
diff --git a/arch/riscv/dts/jh7110-starfive-visionfive-2-v1.3b.dts b/arch/riscv/dts/jh7110-starfive-visionfive-2.dts
similarity index 65%
rename from arch/riscv/dts/jh7110-starfive-visionfive-2-v1.3b.dts
rename to arch/riscv/dts/jh7110-starfive-visionfive-2.dts
index 3b3b345..288ea39 100644
--- a/arch/riscv/dts/jh7110-starfive-visionfive-2-v1.3b.dts
+++ b/arch/riscv/dts/jh7110-starfive-visionfive-2.dts
@@ -1,12 +1,11 @@
 // SPDX-License-Identifier: GPL-2.0 OR MIT
 /*
- * Copyright (C) 2022 StarFive Technology Co., Ltd.
+ * Copyright (C) 2023 StarFive Technology Co., Ltd.
  */
 
 /dts-v1/;
 #include "jh7110-starfive-visionfive-2.dtsi"
 
 / {
-	model = "StarFive VisionFive 2 v1.3B";
 	compatible = "starfive,visionfive-2-v1.3b", "starfive,jh7110";
 };
diff --git a/arch/riscv/dts/jh7110-starfive-visionfive-2.dtsi b/arch/riscv/dts/jh7110-starfive-visionfive-2.dtsi
index c6b6dfa..710b082 100644
--- a/arch/riscv/dts/jh7110-starfive-visionfive-2.dtsi
+++ b/arch/riscv/dts/jh7110-starfive-visionfive-2.dtsi
@@ -17,6 +17,8 @@
 		i2c2 = &i2c2;
 		i2c5 = &i2c5;
 		i2c6 = &i2c6;
+		ethernet0 = &gmac0;
+		ethernet1 = &gmac1;
 	};
 
 	chosen {
@@ -118,6 +120,12 @@
 	pinctrl-names = "default";
 	pinctrl-0 = <&i2c5_pins>;
 	status = "okay";
+
+	eeprom@50 {
+		compatible = "atmel,24c04";
+		reg = <0x50>;
+		pagesize = <16>;
+	};
 };
 
 &i2c6 {
@@ -317,3 +325,35 @@
 	assigned-clock-parents = <&osc>;
 	assigned-clock-rates = <0>;
 };
+
+&gmac0 {
+	phy-handle = <&phy0>;
+	phy-mode = "rgmii-id";
+	status = "okay";
+
+	mdio {
+		#address-cells = <1>;
+		#size-cells = <0>;
+		compatible = "snps,dwmac-mdio";
+
+		phy0: ethernet-phy@0 {
+			reg = <0>;
+		};
+	};
+};
+
+&gmac1 {
+	phy-handle = <&phy1>;
+	phy-mode = "rgmii-id";
+	status = "okay";
+
+	mdio {
+		#address-cells = <1>;
+		#size-cells = <0>;
+		compatible = "snps,dwmac-mdio";
+
+		phy1: ethernet-phy@1 {
+			reg = <0>;
+		};
+	};
+};
diff --git a/arch/riscv/dts/jh7110.dtsi b/arch/riscv/dts/jh7110.dtsi
index bd60879..58e332e 100644
--- a/arch/riscv/dts/jh7110.dtsi
+++ b/arch/riscv/dts/jh7110.dtsi
@@ -235,6 +235,13 @@
 		#clock-cells = <0>;
 	};
 
+	stmmac_axi_setup: stmmac-axi-config {
+		snps,lpi_en;
+		snps,wr_osr_lmt = <4>;
+		snps,rd_osr_lmt = <4>;
+		snps,blen = <256 128 64 32 0 0 0>;
+	};
+
 	soc {
 		compatible = "simple-bus";
 		interrupt-parent = <&plic>;
@@ -539,6 +546,68 @@
 			status = "disabled";
 		};
 
+		gmac0: ethernet@16030000 {
+			compatible = "starfive,jh7110-dwmac", "snps,dwmac-5.20";
+			reg = <0x0 0x16030000 0x0 0x10000>;
+			clocks = <&aoncrg JH7110_AONCLK_GMAC0_AXI>,
+				 <&aoncrg JH7110_AONCLK_GMAC0_AHB>,
+				 <&syscrg JH7110_SYSCLK_GMAC0_PTP>,
+				 <&aoncrg JH7110_AONCLK_GMAC0_TX_INV>,
+				 <&syscrg JH7110_SYSCLK_GMAC0_GTXC>;
+			clock-names = "stmmaceth", "pclk", "ptp_ref",
+				      "tx", "gtx";
+			resets = <&aoncrg JH7110_AONRST_GMAC0_AXI>,
+				 <&aoncrg JH7110_AONRST_GMAC0_AHB>;
+			reset-names = "stmmaceth", "ahb";
+			interrupts = <7>, <6>, <5>;
+			interrupt-names = "macirq", "eth_wake_irq", "eth_lpi";
+			snps,multicast-filter-bins = <64>;
+			snps,perfect-filter-entries = <8>;
+			rx-fifo-depth = <2048>;
+			tx-fifo-depth = <2048>;
+			snps,fixed-burst;
+			snps,no-pbl-x8;
+			snps,force_thresh_dma_mode;
+			snps,axi-config = <&stmmac_axi_setup>;
+			snps,tso;
+			snps,en-tx-lpi-clockgating;
+			snps,txpbl = <16>;
+			snps,rxpbl = <16>;
+			starfive,syscon = <&aon_syscon 0xc 0x12>;
+			status = "disabled";
+		};
+
+		gmac1: ethernet@16040000 {
+			compatible = "starfive,jh7110-dwmac", "snps,dwmac-5.20";
+			reg = <0x0 0x16040000 0x0 0x10000>;
+			clocks = <&syscrg JH7110_SYSCLK_GMAC1_AXI>,
+				 <&syscrg JH7110_SYSCLK_GMAC1_AHB>,
+				 <&syscrg JH7110_SYSCLK_GMAC1_PTP>,
+				 <&syscrg JH7110_SYSCLK_GMAC1_TX_INV>,
+				 <&syscrg JH7110_SYSCLK_GMAC1_GTXC>;
+			clock-names = "stmmaceth", "pclk", "ptp_ref",
+				      "tx", "gtx";
+			resets = <&syscrg JH7110_SYSRST_GMAC1_AXI>,
+				 <&syscrg JH7110_SYSRST_GMAC1_AHB>;
+			reset-names = "stmmaceth", "ahb";
+			interrupts = <78>, <77>, <76>;
+			interrupt-names = "macirq", "eth_wake_irq", "eth_lpi";
+			snps,multicast-filter-bins = <64>;
+			snps,perfect-filter-entries = <8>;
+			rx-fifo-depth = <2048>;
+			tx-fifo-depth = <2048>;
+			snps,fixed-burst;
+			snps,no-pbl-x8;
+			snps,force_thresh_dma_mode;
+			snps,axi-config = <&stmmac_axi_setup>;
+			snps,tso;
+			snps,en-tx-lpi-clockgating;
+			snps,txpbl = <16>;
+			snps,rxpbl = <16>;
+			starfive,syscon = <&sys_syscon 0x90 0x2>;
+			status = "disabled";
+		};
+
 		aoncrg: clock-controller@17000000 {
 			compatible = "starfive,jh7110-aoncrg";
 			reg = <0x0 0x17000000 0x0 0x10000>;
diff --git a/arch/riscv/dts/th1520-lichee-module-4a.dtsi b/arch/riscv/dts/th1520-lichee-module-4a.dtsi
new file mode 100644
index 0000000..dc00e3d
--- /dev/null
+++ b/arch/riscv/dts/th1520-lichee-module-4a.dtsi
@@ -0,0 +1,34 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Jisheng Zhang <jszhang@kernel.org>
+ */
+
+/dts-v1/;
+
+#include "th1520.dtsi"
+
+/ {
+	model = "Sipeed Lichee Module 4A";
+	compatible = "sipeed,lichee-module-4a", "thead,th1520";
+
+	memory@0 {
+		device_type = "memory";
+		reg = <0x0 0x00000000 0x2 0x00000000>;
+	};
+};
+
+&osc {
+	clock-frequency = <24000000>;
+};
+
+&osc_32k {
+	clock-frequency = <32768>;
+};
+
+&apb_clk {
+	clock-frequency = <62500000>;
+};
+
+&uart_sclk {
+	clock-frequency = <100000000>;
+};
diff --git a/arch/riscv/dts/th1520-lichee-pi-4a.dts b/arch/riscv/dts/th1520-lichee-pi-4a.dts
new file mode 100644
index 0000000..a1248b2
--- /dev/null
+++ b/arch/riscv/dts/th1520-lichee-pi-4a.dts
@@ -0,0 +1,32 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Jisheng Zhang <jszhang@kernel.org>
+ */
+
+#include "th1520-lichee-module-4a.dtsi"
+
+/ {
+	model = "Sipeed Lichee Pi 4A";
+	compatible = "sipeed,lichee-pi-4a", "sipeed,lichee-module-4a", "thead,th1520";
+
+	aliases {
+		gpio0 = &gpio0;
+		gpio1 = &gpio1;
+		gpio2 = &gpio2;
+		gpio3 = &gpio3;
+		serial0 = &uart0;
+		serial1 = &uart1;
+		serial2 = &uart2;
+		serial3 = &uart3;
+		serial4 = &uart4;
+		serial5 = &uart5;
+	};
+
+	chosen {
+		stdout-path = "serial0:115200n8";
+	};
+};
+
+&uart0 {
+	status = "okay";
+};
diff --git a/arch/riscv/dts/th1520.dtsi b/arch/riscv/dts/th1520.dtsi
new file mode 100644
index 0000000..f7bfa42
--- /dev/null
+++ b/arch/riscv/dts/th1520.dtsi
@@ -0,0 +1,406 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2021 Alibaba Group Holding Limited.
+ * Copyright (C) 2023 Jisheng Zhang <jszhang@kernel.org>
+ */
+
+#include <dt-bindings/interrupt-controller/irq.h>
+
+/ {
+	compatible = "thead,th1520";
+	#address-cells = <2>;
+	#size-cells = <2>;
+
+	cpus: cpus {
+		#address-cells = <1>;
+		#size-cells = <0>;
+		timebase-frequency = <3000000>;
+
+		c910_0: cpu@0 {
+			compatible = "thead,c910", "riscv";
+			device_type = "cpu";
+			riscv,isa = "rv64imafdc";
+			reg = <0>;
+			i-cache-block-size = <64>;
+			i-cache-size = <65536>;
+			i-cache-sets = <512>;
+			d-cache-block-size = <64>;
+			d-cache-size = <65536>;
+			d-cache-sets = <512>;
+			next-level-cache = <&l2_cache>;
+			mmu-type = "riscv,sv39";
+
+			cpu0_intc: interrupt-controller {
+				compatible = "riscv,cpu-intc";
+				interrupt-controller;
+				#interrupt-cells = <1>;
+			};
+		};
+
+		c910_1: cpu@1 {
+			compatible = "thead,c910", "riscv";
+			device_type = "cpu";
+			riscv,isa = "rv64imafdc";
+			reg = <1>;
+			i-cache-block-size = <64>;
+			i-cache-size = <65536>;
+			i-cache-sets = <512>;
+			d-cache-block-size = <64>;
+			d-cache-size = <65536>;
+			d-cache-sets = <512>;
+			next-level-cache = <&l2_cache>;
+			mmu-type = "riscv,sv39";
+
+			cpu1_intc: interrupt-controller {
+				compatible = "riscv,cpu-intc";
+				interrupt-controller;
+				#interrupt-cells = <1>;
+			};
+		};
+
+		c910_2: cpu@2 {
+			compatible = "thead,c910", "riscv";
+			device_type = "cpu";
+			riscv,isa = "rv64imafdc";
+			reg = <2>;
+			i-cache-block-size = <64>;
+			i-cache-size = <65536>;
+			i-cache-sets = <512>;
+			d-cache-block-size = <64>;
+			d-cache-size = <65536>;
+			d-cache-sets = <512>;
+			next-level-cache = <&l2_cache>;
+			mmu-type = "riscv,sv39";
+
+			cpu2_intc: interrupt-controller {
+				compatible = "riscv,cpu-intc";
+				interrupt-controller;
+				#interrupt-cells = <1>;
+			};
+		};
+
+		c910_3: cpu@3 {
+			compatible = "thead,c910", "riscv";
+			device_type = "cpu";
+			riscv,isa = "rv64imafdc";
+			reg = <3>;
+			i-cache-block-size = <64>;
+			i-cache-size = <65536>;
+			i-cache-sets = <512>;
+			d-cache-block-size = <64>;
+			d-cache-size = <65536>;
+			d-cache-sets = <512>;
+			next-level-cache = <&l2_cache>;
+			mmu-type = "riscv,sv39";
+
+			cpu3_intc: interrupt-controller {
+				compatible = "riscv,cpu-intc";
+				interrupt-controller;
+				#interrupt-cells = <1>;
+			};
+		};
+
+		l2_cache: l2-cache {
+			compatible = "cache";
+			cache-block-size = <64>;
+			cache-level = <2>;
+			cache-size = <1048576>;
+			cache-sets = <1024>;
+			cache-unified;
+		};
+	};
+
+	osc: oscillator {
+		compatible = "fixed-clock";
+		clock-output-names = "osc_24m";
+		#clock-cells = <0>;
+	};
+
+	osc_32k: 32k-oscillator {
+		compatible = "fixed-clock";
+		clock-output-names = "osc_32k";
+		#clock-cells = <0>;
+	};
+
+	apb_clk: apb-clk-clock {
+		compatible = "fixed-clock";
+		clock-output-names = "apb_clk";
+		#clock-cells = <0>;
+	};
+
+	uart_sclk: uart-sclk-clock {
+		compatible = "fixed-clock";
+		clock-output-names = "uart_sclk";
+		#clock-cells = <0>;
+	};
+
+	soc {
+		compatible = "simple-bus";
+		interrupt-parent = <&plic>;
+		#address-cells = <2>;
+		#size-cells = <2>;
+		ranges;
+
+		plic: interrupt-controller@ffd8000000 {
+			compatible = "thead,th1520-plic", "thead,c900-plic";
+			reg = <0xff 0xd8000000 0x0 0x01000000>;
+			interrupts-extended = <&cpu0_intc 11>, <&cpu0_intc 9>,
+					      <&cpu1_intc 11>, <&cpu1_intc 9>,
+					      <&cpu2_intc 11>, <&cpu2_intc 9>,
+					      <&cpu3_intc 11>, <&cpu3_intc 9>;
+			interrupt-controller;
+			#address-cells = <0>;
+			#interrupt-cells = <2>;
+			riscv,ndev = <240>;
+		};
+
+		clint: timer@ffdc000000 {
+			compatible = "thead,th1520-clint", "thead,c900-clint";
+			reg = <0xff 0xdc000000 0x0 0x00010000>;
+			interrupts-extended = <&cpu0_intc 3>, <&cpu0_intc 7>,
+					      <&cpu1_intc 3>, <&cpu1_intc 7>,
+					      <&cpu2_intc 3>, <&cpu2_intc 7>,
+					      <&cpu3_intc 3>, <&cpu3_intc 7>;
+		};
+
+		uart0: serial@ffe7014000 {
+			compatible = "snps,dw-apb-uart";
+			reg = <0xff 0xe7014000 0x0 0x100>;
+			interrupts = <36 IRQ_TYPE_LEVEL_HIGH>;
+			clocks = <&uart_sclk>;
+			reg-shift = <2>;
+			reg-io-width = <4>;
+			status = "disabled";
+		};
+
+		uart1: serial@ffe7f00000 {
+			compatible = "snps,dw-apb-uart";
+			reg = <0xff 0xe7f00000 0x0 0x100>;
+			interrupts = <37 IRQ_TYPE_LEVEL_HIGH>;
+			clocks = <&uart_sclk>;
+			reg-shift = <2>;
+			reg-io-width = <4>;
+			status = "disabled";
+		};
+
+		uart3: serial@ffe7f04000 {
+			compatible = "snps,dw-apb-uart";
+			reg = <0xff 0xe7f04000 0x0 0x100>;
+			interrupts = <39 IRQ_TYPE_LEVEL_HIGH>;
+			clocks = <&uart_sclk>;
+			reg-shift = <2>;
+			reg-io-width = <4>;
+			status = "disabled";
+		};
+
+		gpio2: gpio@ffe7f34000 {
+			compatible = "snps,dw-apb-gpio";
+			reg = <0xff 0xe7f34000 0x0 0x1000>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			portc: gpio-controller@0 {
+				compatible = "snps,dw-apb-gpio-port";
+				gpio-controller;
+				#gpio-cells = <2>;
+				ngpios = <32>;
+				reg = <0>;
+				interrupt-controller;
+				#interrupt-cells = <2>;
+				interrupts = <58 IRQ_TYPE_LEVEL_HIGH>;
+			};
+		};
+
+		gpio3: gpio@ffe7f38000 {
+			compatible = "snps,dw-apb-gpio";
+			reg = <0xff 0xe7f38000 0x0 0x1000>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			portd: gpio-controller@0 {
+				compatible = "snps,dw-apb-gpio-port";
+				gpio-controller;
+				#gpio-cells = <2>;
+				ngpios = <32>;
+				reg = <0>;
+				interrupt-controller;
+				#interrupt-cells = <2>;
+				interrupts = <59 IRQ_TYPE_LEVEL_HIGH>;
+			};
+		};
+
+		gpio0: gpio@ffec005000 {
+			compatible = "snps,dw-apb-gpio";
+			reg = <0xff 0xec005000 0x0 0x1000>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			porta: gpio-controller@0 {
+				compatible = "snps,dw-apb-gpio-port";
+				gpio-controller;
+				#gpio-cells = <2>;
+				ngpios = <32>;
+				reg = <0>;
+				interrupt-controller;
+				#interrupt-cells = <2>;
+				interrupts = <56 IRQ_TYPE_LEVEL_HIGH>;
+			};
+		};
+
+		gpio1: gpio@ffec006000 {
+			compatible = "snps,dw-apb-gpio";
+			reg = <0xff 0xec006000 0x0 0x1000>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			portb: gpio-controller@0 {
+				compatible = "snps,dw-apb-gpio-port";
+				gpio-controller;
+				#gpio-cells = <2>;
+				ngpios = <32>;
+				reg = <0>;
+				interrupt-controller;
+				#interrupt-cells = <2>;
+				interrupts = <57 IRQ_TYPE_LEVEL_HIGH>;
+			};
+		};
+
+		uart2: serial@ffec010000 {
+			compatible = "snps,dw-apb-uart";
+			reg = <0xff 0xec010000 0x0 0x4000>;
+			interrupts = <38 IRQ_TYPE_LEVEL_HIGH>;
+			clocks = <&uart_sclk>;
+			reg-shift = <2>;
+			reg-io-width = <4>;
+			status = "disabled";
+		};
+
+		timer0: timer@ffefc32000 {
+			compatible = "snps,dw-apb-timer";
+			reg = <0xff 0xefc32000 0x0 0x14>;
+			clocks = <&apb_clk>;
+			clock-names = "timer";
+			interrupts = <16 IRQ_TYPE_LEVEL_HIGH>;
+			status = "disabled";
+		};
+
+		timer1: timer@ffefc32014 {
+			compatible = "snps,dw-apb-timer";
+			reg = <0xff 0xefc32014 0x0 0x14>;
+			clocks = <&apb_clk>;
+			clock-names = "timer";
+			interrupts = <17 IRQ_TYPE_LEVEL_HIGH>;
+			status = "disabled";
+		};
+
+		timer2: timer@ffefc32028 {
+			compatible = "snps,dw-apb-timer";
+			reg = <0xff 0xefc32028 0x0 0x14>;
+			clocks = <&apb_clk>;
+			clock-names = "timer";
+			interrupts = <18 IRQ_TYPE_LEVEL_HIGH>;
+			status = "disabled";
+		};
+
+		timer3: timer@ffefc3203c {
+			compatible = "snps,dw-apb-timer";
+			reg = <0xff 0xefc3203c 0x0 0x14>;
+			clocks = <&apb_clk>;
+			clock-names = "timer";
+			interrupts = <19 IRQ_TYPE_LEVEL_HIGH>;
+			status = "disabled";
+		};
+
+		uart4: serial@fff7f08000 {
+			compatible = "snps,dw-apb-uart";
+			reg = <0xff 0xf7f08000 0x0 0x4000>;
+			interrupts = <40 IRQ_TYPE_LEVEL_HIGH>;
+			clocks = <&uart_sclk>;
+			reg-shift = <2>;
+			reg-io-width = <4>;
+			status = "disabled";
+		};
+
+		uart5: serial@fff7f0c000 {
+			compatible = "snps,dw-apb-uart";
+			reg = <0xff 0xf7f0c000 0x0 0x4000>;
+			interrupts = <41 IRQ_TYPE_LEVEL_HIGH>;
+			clocks = <&uart_sclk>;
+			reg-shift = <2>;
+			reg-io-width = <4>;
+			status = "disabled";
+		};
+
+		timer4: timer@ffffc33000 {
+			compatible = "snps,dw-apb-timer";
+			reg = <0xff 0xffc33000 0x0 0x14>;
+			clocks = <&apb_clk>;
+			clock-names = "timer";
+			interrupts = <20 IRQ_TYPE_LEVEL_HIGH>;
+			status = "disabled";
+		};
+
+		timer5: timer@ffffc33014 {
+			compatible = "snps,dw-apb-timer";
+			reg = <0xff 0xffc33014 0x0 0x14>;
+			clocks = <&apb_clk>;
+			clock-names = "timer";
+			interrupts = <21 IRQ_TYPE_LEVEL_HIGH>;
+			status = "disabled";
+		};
+
+		timer6: timer@ffffc33028 {
+			compatible = "snps,dw-apb-timer";
+			reg = <0xff 0xffc33028 0x0 0x14>;
+			clocks = <&apb_clk>;
+			clock-names = "timer";
+			interrupts = <22 IRQ_TYPE_LEVEL_HIGH>;
+			status = "disabled";
+		};
+
+		timer7: timer@ffffc3303c {
+			compatible = "snps,dw-apb-timer";
+			reg = <0xff 0xffc3303c 0x0 0x14>;
+			clocks = <&apb_clk>;
+			clock-names = "timer";
+			interrupts = <23 IRQ_TYPE_LEVEL_HIGH>;
+			status = "disabled";
+		};
+
+		ao_gpio0: gpio@fffff41000 {
+			compatible = "snps,dw-apb-gpio";
+			reg = <0xff 0xfff41000 0x0 0x1000>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			porte: gpio-controller@0 {
+				compatible = "snps,dw-apb-gpio-port";
+				gpio-controller;
+				#gpio-cells = <2>;
+				ngpios = <32>;
+				reg = <0>;
+				interrupt-controller;
+				#interrupt-cells = <2>;
+				interrupts = <76 IRQ_TYPE_LEVEL_HIGH>;
+			};
+		};
+
+		ao_gpio1: gpio@fffff52000 {
+			compatible = "snps,dw-apb-gpio";
+			reg = <0xff 0xfff52000 0x0 0x1000>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			portf: gpio-controller@0 {
+				compatible = "snps,dw-apb-gpio-port";
+				gpio-controller;
+				#gpio-cells = <2>;
+				ngpios = <32>;
+				reg = <0>;
+				interrupt-controller;
+				#interrupt-cells = <2>;
+				interrupts = <55 IRQ_TYPE_LEVEL_HIGH>;
+			};
+		};
+	};
+};
diff --git a/arch/riscv/include/asm/arch-jh7110/eeprom.h b/arch/riscv/include/asm/arch-jh7110/eeprom.h
new file mode 100644
index 0000000..f354d5c
--- /dev/null
+++ b/arch/riscv/include/asm/arch-jh7110/eeprom.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2023 StarFive Technology Co., Ltd.
+ * Author: Yanhong Wang<yanhong.wang@starfivetech.com>
+ */
+
+#ifndef _ASM_RISCV_EEPROM_H
+#define _ASM_RISCV_EEPROM_H
+
+u8 get_pcb_revision_from_eeprom(void);
+u32 get_ddr_size_from_eeprom(void);
+
+#endif /* _ASM_RISCV_EEPROM_H */
diff --git a/arch/riscv/include/asm/global_data.h b/arch/riscv/include/asm/global_data.h
index 31ba726..9d97517 100644
--- a/arch/riscv/include/asm/global_data.h
+++ b/arch/riscv/include/asm/global_data.h
@@ -18,8 +18,8 @@
 struct arch_global_data {
 	long boot_hart;		/* boot hart id */
 	phys_addr_t firmware_fdt_addr;
-#if CONFIG_IS_ENABLED(SIFIVE_CLINT)
-	void __iomem *clint;	/* clint base address */
+#if CONFIG_IS_ENABLED(RISCV_ACLINT)
+	void __iomem *aclint;	/* aclint base address */
 #endif
 #ifdef CONFIG_ANDES_PLICSW
 	void __iomem *plicsw;	/* andes plicsw base address */
diff --git a/arch/riscv/include/asm/syscon.h b/arch/riscv/include/asm/syscon.h
index f2b3797..5787702 100644
--- a/arch/riscv/include/asm/syscon.h
+++ b/arch/riscv/include/asm/syscon.h
@@ -12,7 +12,7 @@
  */
 enum {
 	RISCV_NONE,
-	RISCV_SYSCON_CLINT,	/* Core Local Interruptor (CLINT) */
+	RISCV_SYSCON_ACLINT,	/* Advanced Core Local Interruptor (ACLINT) */
 	RISCV_SYSCON_PLICSW,	/* Andes PLICSW */
 };
 
diff --git a/arch/riscv/lib/Makefile b/arch/riscv/lib/Makefile
index e5a81ba..02c4d8f 100644
--- a/arch/riscv/lib/Makefile
+++ b/arch/riscv/lib/Makefile
@@ -12,7 +12,7 @@
 obj-y	+= cache.o
 obj-$(CONFIG_SIFIVE_CACHE) += sifive_cache.o
 ifeq ($(CONFIG_$(SPL_)RISCV_MMODE),y)
-obj-$(CONFIG_$(SPL_)SIFIVE_CLINT) += sifive_clint.o
+obj-$(CONFIG_$(SPL_)RISCV_ACLINT) += aclint_ipi.o
 obj-$(CONFIG_ANDES_PLICSW) += andes_plicsw.o
 else
 obj-$(CONFIG_SBI) += sbi.o
diff --git a/arch/riscv/lib/aclint_ipi.c b/arch/riscv/lib/aclint_ipi.c
new file mode 100644
index 0000000..90b8e12
--- /dev/null
+++ b/arch/riscv/lib/aclint_ipi.c
@@ -0,0 +1,78 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2020, Sean Anderson <seanga2@gmail.com>
+ * Copyright (C) 2018, Bin Meng <bmeng.cn@gmail.com>
+ *
+ * U-Boot syscon driver for SiFive's Core Local Interruptor (CLINT).
+ * The CLINT block holds memory-mapped control and status registers
+ * associated with software and timer interrupts.
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <regmap.h>
+#include <syscon.h>
+#include <asm/global_data.h>
+#include <asm/io.h>
+#include <asm/smp.h>
+#include <asm/syscon.h>
+#include <linux/err.h>
+
+/* MSIP registers */
+#define MSIP_REG(base, hart)		((ulong)(base) + (hart) * 4)
+
+DECLARE_GLOBAL_DATA_PTR;
+
+int riscv_init_ipi(void)
+{
+	int ret;
+	struct udevice *dev;
+
+	ret = uclass_get_device_by_driver(UCLASS_TIMER,
+					  DM_DRIVER_GET(riscv_aclint_timer), &dev);
+	if (ret)
+		return ret;
+
+	if (dev_get_driver_data(dev) != 0)
+		gd->arch.aclint = dev_read_addr_ptr(dev);
+	else
+		gd->arch.aclint = syscon_get_first_range(RISCV_SYSCON_ACLINT);
+
+	if (!gd->arch.aclint)
+		return -EINVAL;
+
+	return 0;
+}
+
+int riscv_send_ipi(int hart)
+{
+	writel(1, (void __iomem *)MSIP_REG(gd->arch.aclint, hart));
+
+	return 0;
+}
+
+int riscv_clear_ipi(int hart)
+{
+	writel(0, (void __iomem *)MSIP_REG(gd->arch.aclint, hart));
+
+	return 0;
+}
+
+int riscv_get_ipi(int hart, int *pending)
+{
+	*pending = readl((void __iomem *)MSIP_REG(gd->arch.aclint, hart));
+
+	return 0;
+}
+
+static const struct udevice_id riscv_aclint_swi_ids[] = {
+	{ .compatible = "riscv,aclint-mswi", .data = RISCV_SYSCON_ACLINT },
+	{ }
+};
+
+U_BOOT_DRIVER(riscv_aclint_swi) = {
+	.name		= "riscv_aclint_swi",
+	.id		= UCLASS_SYSCON,
+	.of_match	= riscv_aclint_swi_ids,
+	.flags		= DM_FLAG_PRE_RELOC,
+};
diff --git a/arch/riscv/lib/sifive_clint.c b/arch/riscv/lib/sifive_clint.c
deleted file mode 100644
index ab22395..0000000
--- a/arch/riscv/lib/sifive_clint.c
+++ /dev/null
@@ -1,59 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * Copyright (C) 2020, Sean Anderson <seanga2@gmail.com>
- * Copyright (C) 2018, Bin Meng <bmeng.cn@gmail.com>
- *
- * U-Boot syscon driver for SiFive's Core Local Interruptor (CLINT).
- * The CLINT block holds memory-mapped control and status registers
- * associated with software and timer interrupts.
- */
-
-#include <common.h>
-#include <dm.h>
-#include <asm/global_data.h>
-#include <asm/io.h>
-#include <asm/smp.h>
-#include <linux/err.h>
-
-/* MSIP registers */
-#define MSIP_REG(base, hart)		((ulong)(base) + (hart) * 4)
-
-DECLARE_GLOBAL_DATA_PTR;
-
-int riscv_init_ipi(void)
-{
-	int ret;
-	struct udevice *dev;
-
-	ret = uclass_get_device_by_driver(UCLASS_TIMER,
-					  DM_DRIVER_GET(sifive_clint), &dev);
-	if (ret)
-		return ret;
-
-	gd->arch.clint = dev_read_addr_ptr(dev);
-	if (!gd->arch.clint)
-		return -EINVAL;
-
-	return 0;
-}
-
-int riscv_send_ipi(int hart)
-{
-	writel(1, (void __iomem *)MSIP_REG(gd->arch.clint, hart));
-
-	return 0;
-}
-
-int riscv_clear_ipi(int hart)
-{
-	writel(0, (void __iomem *)MSIP_REG(gd->arch.clint, hart));
-
-	return 0;
-}
-
-int riscv_get_ipi(int hart, int *pending)
-{
-	*pending = readl((void __iomem *)MSIP_REG(gd->arch.clint, hart));
-
-	return 0;
-}
diff --git a/arch/x86/include/asm/bootparam.h b/arch/x86/include/asm/bootparam.h
index ea816ca..ac48653 100644
--- a/arch/x86/include/asm/bootparam.h
+++ b/arch/x86/include/asm/bootparam.h
@@ -62,7 +62,7 @@
 /**
  * struct setup_header - Information needed by Linux to boot
  *
- * See https://www.kernel.org/doc/html/latest/x86/boot.html
+ * See https://www.kernel.org/doc/html/latest/arch/x86/boot.html
  */
 struct setup_header {
 	__u8	setup_sects;
diff --git a/board/openpiton/riscv64/Kconfig b/board/openpiton/riscv64/Kconfig
index eb0db8a..21da1dc 100644
--- a/board/openpiton/riscv64/Kconfig
+++ b/board/openpiton/riscv64/Kconfig
@@ -29,7 +29,7 @@
 	select SUPPORT_SPL
 	imply CPU_RISCV
 	imply RISCV_TIMER
-	imply SPL_SIFIVE_CLINT
+	imply SPL_RISCV_ACLINT
 	imply CMD_CPU
 	imply SPL_CPU_SUPPORT
 	imply SPL_SMP
diff --git a/board/sipeed/maix/Kconfig b/board/sipeed/maix/Kconfig
index 2d212ec..d34ea4b 100644
--- a/board/sipeed/maix/Kconfig
+++ b/board/sipeed/maix/Kconfig
@@ -34,7 +34,7 @@
 	imply SMP
 	imply DM_SERIAL
 	imply SIFIVE_SERIAL
-	imply SIFIVE_CLINT
+	imply RISCV_ACLINT
 	imply POWER_DOMAIN
 	imply SIMPLE_PM_BUS
 	imply CLK_K210
diff --git a/board/starfive/visionfive2/Makefile b/board/starfive/visionfive2/Makefile
index 66c854d..c7ba4f7 100644
--- a/board/starfive/visionfive2/Makefile
+++ b/board/starfive/visionfive2/Makefile
@@ -5,3 +5,4 @@
 
 obj-y	:= starfive_visionfive2.o
 obj-$(CONFIG_SPL_BUILD) += spl.o
+obj-$(CONFIG_ID_EEPROM) += visionfive2-i2c-eeprom.o
diff --git a/board/starfive/visionfive2/spl.c b/board/starfive/visionfive2/spl.c
index db0b4cb..7acd399 100644
--- a/board/starfive/visionfive2/spl.c
+++ b/board/starfive/visionfive2/spl.c
@@ -5,16 +5,173 @@
  */
 
 #include <common.h>
+#include <asm/arch/eeprom.h>
 #include <asm/arch/regs.h>
 #include <asm/arch/spl.h>
 #include <asm/io.h>
+#include <dt-bindings/clock/starfive,jh7110-crg.h>
+#include <fdt_support.h>
+#include <linux/libfdt.h>
 #include <log.h>
 #include <spl.h>
 
+DECLARE_GLOBAL_DATA_PTR;
 #define JH7110_CLK_CPU_ROOT_OFFSET		0x0U
 #define JH7110_CLK_CPU_ROOT_SHIFT		24
 #define JH7110_CLK_CPU_ROOT_MASK		GENMASK(29, 24)
 
+struct starfive_vf2_pro {
+	const char *path;
+	const char *name;
+	const char *value;
+};
+
+static const struct starfive_vf2_pro starfive_vera[] = {
+	{"/soc/ethernet@16030000/mdio/ethernet-phy@0", "rx-internal-delay-ps",
+		"1900"},
+	{"/soc/ethernet@16030000/mdio/ethernet-phy@0", "tx-internal-delay-ps",
+		"1350"}
+};
+
+static const struct starfive_vf2_pro starfive_verb[] = {
+	{"/soc/ethernet@16030000", "starfive,tx-use-rgmii-clk", NULL},
+	{"/soc/ethernet@16040000", "starfive,tx-use-rgmii-clk", NULL},
+
+	{"/soc/ethernet@16030000/mdio/ethernet-phy@0",
+		"motorcomm,tx-clk-adj-enabled", NULL},
+	{"/soc/ethernet@16030000/mdio/ethernet-phy@0",
+		"motorcomm,tx-clk-100-inverted", NULL},
+	{"/soc/ethernet@16030000/mdio/ethernet-phy@0",
+		"motorcomm,tx-clk-1000-inverted", NULL},
+	{"/soc/ethernet@16030000/mdio/ethernet-phy@0",
+		"rx-internal-delay-ps", "1900"},
+	{"/soc/ethernet@16030000/mdio/ethernet-phy@0",
+		"tx-internal-delay-ps", "1500"},
+
+	{"/soc/ethernet@16040000/mdio/ethernet-phy@1",
+		"motorcomm,tx-clk-adj-enabled", NULL},
+	{ "/soc/ethernet@16040000/mdio/ethernet-phy@1",
+		"motorcomm,tx-clk-100-inverted", NULL},
+	{"/soc/ethernet@16040000/mdio/ethernet-phy@1",
+		"rx-internal-delay-ps", "0"},
+	{"/soc/ethernet@16040000/mdio/ethernet-phy@1",
+		"tx-internal-delay-ps", "0"},
+};
+
+void spl_fdt_fixup_version_a(void *fdt)
+{
+	u32 phandle;
+	u8 i;
+	int offset;
+	int ret;
+
+	fdt_setprop_string(fdt, fdt_path_offset(fdt, "/"), "model",
+			   "StarFive VisionFive 2 v1.2A");
+
+	offset = fdt_path_offset(fdt, "/soc/clock-controller@13020000");
+	phandle = fdt_get_phandle(fdt, offset);
+	offset = fdt_path_offset(fdt, "/soc/ethernet@16040000");
+
+	fdt_setprop_u32(fdt, offset, "assigned-clocks", phandle);
+	fdt_appendprop_u32(fdt, offset, "assigned-clocks", JH7110_SYSCLK_GMAC1_TX);
+	fdt_appendprop_u32(fdt, offset, "assigned-clocks", phandle);
+	fdt_appendprop_u32(fdt, offset, "assigned-clocks", JH7110_SYSCLK_GMAC1_RX);
+
+	fdt_setprop_u32(fdt, offset,  "assigned-clock-parents", phandle);
+	fdt_appendprop_u32(fdt, offset,  "assigned-clock-parents",
+			   JH7110_SYSCLK_GMAC1_RMII_RTX);
+	fdt_appendprop_u32(fdt, offset,  "assigned-clock-parents", phandle);
+	fdt_appendprop_u32(fdt, offset,  "assigned-clock-parents",
+			   JH7110_SYSCLK_GMAC1_RMII_RTX);
+
+	fdt_setprop_string(fdt, fdt_path_offset(fdt, "/soc/ethernet@16040000"),
+			   "phy-mode", "rmii");
+
+	for (i = 0; i < ARRAY_SIZE(starfive_vera); i++) {
+		offset = fdt_path_offset(fdt, starfive_vera[i].path);
+
+		if (starfive_vera[i].value)
+			ret = fdt_setprop_u32(fdt, offset,  starfive_vera[i].name,
+					      dectoul(starfive_vera[i].value, NULL));
+		else
+			ret = fdt_setprop_empty(fdt, offset, starfive_vera[i].name);
+
+		if (ret) {
+			pr_err("%s set prop %s fail.\n", __func__, starfive_vera[i].name);
+				break;
+		}
+	}
+}
+
+void spl_fdt_fixup_version_b(void *fdt)
+{
+	u32 phandle;
+	u8 i;
+	int offset;
+	int ret;
+
+	fdt_setprop_string(fdt, fdt_path_offset(fdt, "/"), "model",
+			   "StarFive VisionFive 2 v1.3B");
+
+	/* gmac0 */
+	offset = fdt_path_offset(fdt, "/soc/clock-controller@17000000");
+	phandle = fdt_get_phandle(fdt, offset);
+	offset = fdt_path_offset(fdt, "/soc/ethernet@16030000");
+
+	fdt_setprop_u32(fdt, offset, "assigned-clocks", phandle);
+	fdt_appendprop_u32(fdt, offset, "assigned-clocks", JH7110_AONCLK_GMAC0_TX);
+	fdt_setprop_u32(fdt, offset,  "assigned-clock-parents", phandle);
+	fdt_appendprop_u32(fdt, offset,  "assigned-clock-parents",
+			   JH7110_AONCLK_GMAC0_RMII_RTX);
+
+	/* gmac1 */
+	offset = fdt_path_offset(fdt, "/soc/clock-controller@13020000");
+	phandle = fdt_get_phandle(fdt, offset);
+	offset = fdt_path_offset(fdt, "/soc/ethernet@16040000");
+
+	fdt_setprop_u32(fdt, offset, "assigned-clocks", phandle);
+	fdt_appendprop_u32(fdt, offset, "assigned-clocks", JH7110_SYSCLK_GMAC1_TX);
+	fdt_setprop_u32(fdt, offset,  "assigned-clock-parents", phandle);
+	fdt_appendprop_u32(fdt, offset,  "assigned-clock-parents",
+			   JH7110_SYSCLK_GMAC1_RMII_RTX);
+
+	for (i = 0; i < ARRAY_SIZE(starfive_verb); i++) {
+		offset = fdt_path_offset(fdt, starfive_verb[i].path);
+
+		if (starfive_verb[i].value)
+			ret = fdt_setprop_u32(fdt, offset,  starfive_verb[i].name,
+					      dectoul(starfive_verb[i].value, NULL));
+		else
+			ret = fdt_setprop_empty(fdt, offset, starfive_verb[i].name);
+
+		if (ret) {
+			pr_err("%s set prop %s fail.\n", __func__, starfive_verb[i].name);
+				break;
+		}
+	}
+}
+
+void spl_perform_fixups(struct spl_image_info *spl_image)
+{
+	u8 version;
+
+	version = get_pcb_revision_from_eeprom();
+	switch (version) {
+	case 'a':
+	case 'A':
+		spl_fdt_fixup_version_a(spl_image->fdt_addr);
+		break;
+
+	case 'b':
+	case 'B':
+	default:
+		spl_fdt_fixup_version_b(spl_image->fdt_addr);
+		break;
+	};
+
+	/* Update the memory size which read form eeprom or DT */
+	fdt_fixup_memory(spl_image->fdt_addr, 0x40000000, gd->ram_size);
+}
 int spl_board_init_f(void)
 {
 	int ret;
diff --git a/board/starfive/visionfive2/starfive_visionfive2.c b/board/starfive/visionfive2/starfive_visionfive2.c
index 613fe79..07dcca2 100644
--- a/board/starfive/visionfive2/starfive_visionfive2.c
+++ b/board/starfive/visionfive2/starfive_visionfive2.c
@@ -6,7 +6,9 @@
 
 #include <common.h>
 #include <asm/io.h>
+#include <asm/sections.h>
 #include <cpu_func.h>
+#include <dm.h>
 #include <linux/bitops.h>
 
 #define JH7110_L2_PREFETCHER_BASE_ADDR		0x2030000
@@ -38,3 +40,14 @@
 
 	return 0;
 }
+
+void *board_fdt_blob_setup(int *err)
+{
+	*err = 0;
+	if (IS_ENABLED(CONFIG_OF_SEPARATE) || IS_ENABLED(CONFIG_OF_BOARD)) {
+		if (gd->arch.firmware_fdt_addr)
+			return (ulong *)(uintptr_t)gd->arch.firmware_fdt_addr;
+	}
+
+	return (ulong *)&_end;
+}
diff --git a/board/starfive/visionfive2/visionfive2-i2c-eeprom.c b/board/starfive/visionfive2/visionfive2-i2c-eeprom.c
new file mode 100644
index 0000000..befe788
--- /dev/null
+++ b/board/starfive/visionfive2/visionfive2-i2c-eeprom.c
@@ -0,0 +1,561 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2023 StarFive Technology Co., Ltd.
+ * Author: Yanhong Wang<yanhong.wang@starfivetech.com>
+ */
+
+#include <common.h>
+#include <command.h>
+#include <env.h>
+#include <i2c.h>
+#include <init.h>
+#include <u-boot/crc.h>
+#include <linux/delay.h>
+
+#define FORMAT_VERSION		0x2
+#define PCB_VERSION		0xB1
+#define BOM_VERSION		'A'
+/*
+ * BYTES_PER_EEPROM_PAGE: the 24FC04H datasheet says that data can
+ * only be written in page mode, which means 16 bytes at a time:
+ * 16-Byte Page Write Buffer
+ */
+#define BYTES_PER_EEPROM_PAGE		16
+
+/*
+ * EEPROM_WRITE_DELAY_MS: the 24FC04H datasheet says it takes up to
+ * 5ms to complete a given write:
+ * Write Cycle Time (byte or page) ro Page Write Time 5 ms, Maximum
+ */
+#define EEPROM_WRITE_DELAY_MS		5000
+/*
+ * StarFive OUI. Registration Date is 20xx-xx-xx
+ */
+#define STARFIVE_OUI_PREFIX		"6C:CF:39:"
+#define STARFIVE_DEFAULT_MAC0		"6C:CF:39:6C:DE:AD"
+#define STARFIVE_DEFAULT_MAC1		"6C:CF:39:6C:DE:AE"
+
+/* Magic number at the first four bytes of EEPROM HATs */
+#define STARFIVE_EEPROM_HATS_SIG	"SFVF" /* StarFive VisionFive */
+
+#define STARFIVE_EEPROM_HATS_SIZE_MAX	256 /* Header + Atom1&4(v1) */
+#define STARFIVE_EEPROM_WP_OFFSET	0 /* Read only field */
+#define STARFIVE_EEPROM_ATOM1_PSTR	"VF7110A1-2228-D008E000-00000001\0"
+#define STARFIVE_EEPROM_ATOM1_PSTR_SIZE	32
+#define STARFIVE_EEPROM_ATOM1_SN_OFFSET	23
+#define STARFIVE_EEPROM_ATOM1_VSTR	"StarFive Technology Co., Ltd.\0\0\0"
+#define STARFIVE_EEPROM_ATOM1_VSTR_SIZE	32
+
+#define MAGIC_NUMBER_BYTES	4
+#define MAC_ADDR_BYTES		6
+#define MAC_ADDR_STRLEN		17
+
+/*
+ * Atom Types
+ * 0x0000 = invalid
+ * 0x0001 = vendor info
+ * 0x0002 = GPIO map
+ * 0x0003 = Linux device tree blob
+ * 0x0004 = manufacturer custom data
+ * 0x0005-0xfffe = reserved for future use
+ * 0xffff = invalid
+ */
+
+#define HATS_ATOM_INVALID	0x0000
+#define HATS_ATOM_VENDOR	0x0001
+#define HATS_ATOM_GPIO		0x0002
+#define HATS_ATOM_DTB		0x0003
+#define HATS_ATOM_CUSTOM	0x0004
+#define HATS_ATOM_INVALID_END	0xffff
+
+struct eeprom_header {
+	char signature[MAGIC_NUMBER_BYTES];	/* ASCII table signature */
+	u8 version;		/* EEPROM data format version */
+				/* (0x00 reserved, 0x01 = first version) */
+	u8 reversed;		/* 0x00, Reserved field */
+	u16 numatoms;		/* total atoms in EEPROM */
+	u32 eeplen;		/* total length in bytes of all eeprom data */
+				/* (including this header) */
+};
+
+struct eeprom_atom_header {
+	u16 type;
+	u16 count;
+	u32 dlen;
+};
+
+struct eeprom_atom1_data {
+	u8 uuid[16];
+	u16 pid;
+	u16 pver;
+	u8 vslen;
+	u8 pslen;
+	uchar vstr[STARFIVE_EEPROM_ATOM1_VSTR_SIZE];
+	uchar pstr[STARFIVE_EEPROM_ATOM1_PSTR_SIZE]; /* product SN */
+};
+
+struct starfive_eeprom_atom1 {
+	struct eeprom_atom_header header;
+	struct eeprom_atom1_data data;
+	u16 crc;
+};
+
+struct eeprom_atom4_data {
+	u16 version;
+	u8 pcb_revision;		/* PCB version */
+	u8 bom_revision;		/* BOM version */
+	u8 mac0_addr[MAC_ADDR_BYTES];	/* Ethernet0 MAC */
+	u8 mac1_addr[MAC_ADDR_BYTES];	/* Ethernet1 MAC */
+	u8 reserved[2];
+};
+
+struct starfive_eeprom_atom4 {
+	struct eeprom_atom_header header;
+	struct eeprom_atom4_data data;
+	u16 crc;
+};
+
+struct starfive_eeprom {
+	struct eeprom_header header;
+	struct starfive_eeprom_atom1 atom1;
+	struct starfive_eeprom_atom4 atom4;
+};
+
+static union {
+	struct starfive_eeprom eeprom;
+	uchar buf[STARFIVE_EEPROM_HATS_SIZE_MAX];
+} pbuf __section(".data");
+
+/* Set to 1 if we've read EEPROM into memory */
+static int has_been_read __section(".data");
+
+static inline int is_match_magic(void)
+{
+	return strncmp(pbuf.eeprom.header.signature, STARFIVE_EEPROM_HATS_SIG,
+				MAGIC_NUMBER_BYTES);
+}
+
+/* Calculate the current CRC */
+static inline u32 calculate_crc16(struct eeprom_atom_header *head)
+{
+	uint len = sizeof(struct eeprom_atom_header) + head->dlen - sizeof(u16);
+
+	return crc16(0, (void *)head, len);
+}
+
+/* This function should be called after each update to the EEPROM structure */
+static inline void update_crc(void)
+{
+	pbuf.eeprom.atom1.crc = calculate_crc16(&pbuf.eeprom.atom1.header);
+	pbuf.eeprom.atom4.crc = calculate_crc16(&pbuf.eeprom.atom4.header);
+}
+
+static void dump_raw_eeprom(void)
+{
+	unsigned int i;
+	u32 len;
+
+	len = sizeof(struct starfive_eeprom);
+	for (i = 0; i < len; i++) {
+		if ((i % 16) == 0)
+			printf("%02X: ", i);
+		printf("%02X ", ((u8 *)pbuf.buf)[i]);
+		if (((i % 16) == 15) || (i == len - 1))
+			printf("\n");
+	}
+}
+
+/**
+ * show_eeprom - display the contents of the EEPROM
+ */
+static void show_eeprom(void)
+{
+	if (has_been_read != 1)
+		return;
+
+	printf("\n--------EEPROM INFO--------\n");
+	printf("Vendor : %s\n", pbuf.eeprom.atom1.data.vstr);
+	printf("Product full SN: %s\n", pbuf.eeprom.atom1.data.pstr);
+	printf("data version: 0x%x\n", pbuf.eeprom.atom4.data.version);
+	if (pbuf.eeprom.atom4.data.version == 2) {
+		printf("PCB revision: 0x%x\n", pbuf.eeprom.atom4.data.pcb_revision);
+		printf("BOM revision: %c\n", pbuf.eeprom.atom4.data.bom_revision);
+		printf("Ethernet MAC0 address: %02x:%02x:%02x:%02x:%02x:%02x\n",
+		       pbuf.eeprom.atom4.data.mac0_addr[0], pbuf.eeprom.atom4.data.mac0_addr[1],
+		       pbuf.eeprom.atom4.data.mac0_addr[2], pbuf.eeprom.atom4.data.mac0_addr[3],
+		       pbuf.eeprom.atom4.data.mac0_addr[4], pbuf.eeprom.atom4.data.mac0_addr[5]);
+		printf("Ethernet MAC1 address: %02x:%02x:%02x:%02x:%02x:%02x\n",
+		       pbuf.eeprom.atom4.data.mac1_addr[0], pbuf.eeprom.atom4.data.mac1_addr[1],
+		       pbuf.eeprom.atom4.data.mac1_addr[2], pbuf.eeprom.atom4.data.mac1_addr[3],
+		       pbuf.eeprom.atom4.data.mac1_addr[4], pbuf.eeprom.atom4.data.mac1_addr[5]);
+	} else {
+		printf("Custom data v%d is not Supported\n", pbuf.eeprom.atom4.data.version);
+	}
+	printf("--------EEPROM INFO--------\n\n");
+}
+
+/**
+ * set_mac_address() - stores a MAC address into the local EEPROM copy
+ *
+ * This function takes a pointer to MAC address string
+ * (i.e."XX:XX:XX:XX:XX:XX", where "XX" is a two-digit hex number),
+ * stores it in the MAC address field of the EEPROM local copy, and
+ * updates the local copy of the CRC.
+ */
+static void set_mac_address(char *string, int index)
+{
+	u8 i;
+	u8 *mac;
+
+	if (strncasecmp(STARFIVE_OUI_PREFIX, string,
+			strlen(STARFIVE_OUI_PREFIX))) {
+		printf("The MAC address doesn't match StarFive OUI %s\n",
+		       STARFIVE_OUI_PREFIX);
+		return;
+	}
+	mac = (index == 0) ? pbuf.eeprom.atom4.data.mac0_addr :
+			pbuf.eeprom.atom4.data.mac1_addr;
+
+	for (i = 0; *string && (i < MAC_ADDR_BYTES); i++) {
+		mac[i] = hextoul(string, &string);
+
+		if (*string == ':')
+			string++;
+	}
+
+	update_crc();
+}
+
+/**
+ * init_local_copy() - initialize the in-memory EEPROM copy
+ *
+ * Initialize the in-memory EEPROM copy with the magic number.  Must
+ * be done when preparing to initialize a blank EEPROM, or overwrite
+ * one with a corrupted magic number.
+ */
+static void init_local_copy(void)
+{
+	memset((void *)pbuf.buf, 0, sizeof(struct starfive_eeprom));
+	memcpy(pbuf.eeprom.header.signature, STARFIVE_EEPROM_HATS_SIG,
+	       strlen(STARFIVE_EEPROM_HATS_SIG));
+	pbuf.eeprom.header.version = FORMAT_VERSION;
+	pbuf.eeprom.header.numatoms = 2;
+	pbuf.eeprom.header.eeplen = sizeof(struct starfive_eeprom);
+
+	pbuf.eeprom.atom1.header.type = HATS_ATOM_VENDOR;
+	pbuf.eeprom.atom1.header.count = 1;
+	pbuf.eeprom.atom1.header.dlen = sizeof(struct eeprom_atom1_data) + sizeof(u16);
+	pbuf.eeprom.atom1.data.vslen = STARFIVE_EEPROM_ATOM1_VSTR_SIZE;
+	pbuf.eeprom.atom1.data.pslen = STARFIVE_EEPROM_ATOM1_PSTR_SIZE;
+	memcpy(pbuf.eeprom.atom1.data.vstr, STARFIVE_EEPROM_ATOM1_VSTR,
+	       strlen(STARFIVE_EEPROM_ATOM1_VSTR));
+	memcpy(pbuf.eeprom.atom1.data.pstr, STARFIVE_EEPROM_ATOM1_PSTR,
+	       strlen(STARFIVE_EEPROM_ATOM1_PSTR));
+
+	pbuf.eeprom.atom4.header.type = HATS_ATOM_CUSTOM;
+	pbuf.eeprom.atom4.header.count = 2;
+	pbuf.eeprom.atom4.header.dlen = sizeof(struct eeprom_atom4_data) + sizeof(u16);
+	pbuf.eeprom.atom4.data.version = FORMAT_VERSION;
+	pbuf.eeprom.atom4.data.pcb_revision = PCB_VERSION;
+	pbuf.eeprom.atom4.data.bom_revision = BOM_VERSION;
+	set_mac_address(STARFIVE_DEFAULT_MAC0, 0);
+	set_mac_address(STARFIVE_DEFAULT_MAC1, 1);
+}
+
+/**
+ * prog_eeprom() - write the EEPROM from memory
+ */
+static int prog_eeprom(unsigned int size)
+{
+	unsigned int i;
+	void *p;
+	uchar tmp_buff[STARFIVE_EEPROM_HATS_SIZE_MAX];
+	struct udevice *dev;
+	int ret;
+
+	if (is_match_magic()) {
+		printf("MAGIC ERROR, Please check the data@%p.\n", pbuf.buf);
+		return -1;
+	}
+
+	ret = i2c_get_chip_for_busnum(CONFIG_SYS_EEPROM_BUS_NUM,
+				      CONFIG_SYS_I2C_EEPROM_ADDR,
+				      CONFIG_SYS_I2C_EEPROM_ADDR_LEN,
+				      &dev);
+	if (ret) {
+		printf("Get i2c bus:%d addr:%d fail.\n", CONFIG_SYS_EEPROM_BUS_NUM,
+		       CONFIG_SYS_I2C_EEPROM_ADDR);
+		return ret;
+	}
+
+	for (i = 0, p = (u8 *)pbuf.buf; i < size; ) {
+		if (!ret)
+			ret = dm_i2c_write(dev, i, p, min((int)(size - i),
+							  BYTES_PER_EEPROM_PAGE));
+		if (ret)
+			break;
+
+		udelay(EEPROM_WRITE_DELAY_MS);
+		i += BYTES_PER_EEPROM_PAGE;
+		p += BYTES_PER_EEPROM_PAGE;
+	}
+
+	if (!ret) {
+		/* Verify the write by reading back the EEPROM and comparing */
+		ret = dm_i2c_read(dev,
+				  STARFIVE_EEPROM_WP_OFFSET,
+				  tmp_buff,
+				  STARFIVE_EEPROM_HATS_SIZE_MAX);
+		if (!ret && memcmp((void *)pbuf.buf, (void *)tmp_buff,
+				   STARFIVE_EEPROM_HATS_SIZE_MAX))
+			ret = -1;
+	}
+
+	if (ret) {
+		has_been_read = -1;
+		printf("Programming failed.\n");
+		return -1;
+	}
+
+	printf("Programming passed.\n");
+	return 0;
+}
+
+/**
+ * read_eeprom() - read the EEPROM into memory, if it hasn't been read already
+ */
+static int read_eeprom(void)
+{
+	int ret;
+	struct udevice *dev;
+
+	if (has_been_read == 1)
+		return 0;
+
+	ret = i2c_get_chip_for_busnum(CONFIG_SYS_EEPROM_BUS_NUM,
+				      CONFIG_SYS_I2C_EEPROM_ADDR, 1, &dev);
+	if (!ret)
+		ret = dm_i2c_read(dev, 0, (u8 *)pbuf.buf,
+				  STARFIVE_EEPROM_HATS_SIZE_MAX);
+
+	has_been_read = (ret == 0) ? 1 : 0;
+
+	return ret;
+}
+
+/**
+ * set_pcb_revision() - stores a StarFive PCB revision into the local EEPROM copy
+ *
+ * Takes a pointer to a string representing the numeric PCB revision in
+ * decimal ("0" - "255"), stores it in the pcb_revision field of the
+ * EEPROM local copy, and updates the CRC of the local copy.
+ */
+static void set_pcb_revision(char *string)
+{
+	u32 p;
+
+	p = simple_strtoul(string, &string, 16);
+	if (p > U8_MAX) {
+		printf("%s must not be greater than %d\n", "PCB revision",
+		       U8_MAX);
+		return;
+	}
+
+	pbuf.eeprom.atom4.data.pcb_revision = p;
+
+	update_crc();
+}
+
+/**
+ * set_bom_revision() - stores a StarFive BOM revision into the local EEPROM copy
+ *
+ * Takes a pointer to a uppercase ASCII character representing the BOM
+ * revision ("A" - "Z"), stores it in the bom_revision field of the
+ * EEPROM local copy, and updates the CRC of the local copy.
+ */
+static void set_bom_revision(char *string)
+{
+	if (string[0] < 'A' || string[0] > 'Z') {
+		printf("BOM revision must be an uppercase letter between A and Z\n");
+		return;
+	}
+
+	pbuf.eeprom.atom4.data.bom_revision = string[0];
+
+	update_crc();
+}
+
+/**
+ * set_product_id() - stores a StarFive product ID into the local EEPROM copy
+ *
+ * Takes a pointer to a string representing the numeric product ID  in
+ * string ("VF7100A1-2150-D008E000-00000001\0"), stores it in the product string
+ * field of the EEPROM local copy, and updates the CRC of the local copy.
+ */
+static void set_product_id(char *string)
+{
+	u32 len;
+
+	len = (strlen(string) > STARFIVE_EEPROM_ATOM1_PSTR_SIZE) ?
+		STARFIVE_EEPROM_ATOM1_PSTR_SIZE : strlen(string);
+
+	memcpy((void *)pbuf.eeprom.atom1.data.pstr, (void *)string, len);
+
+	update_crc();
+}
+
+static int print_usage(void)
+{
+	printf("display and program the system ID and MAC addresses in EEPROM\n"
+	"[read_eeprom|initialize|write_eeprom|mac_address|pcb_revision|bom_revision|product_id]\n"
+	"mac read_eeprom\n"
+	"    - read EEPROM content into memory data structure\n"
+	"mac write_eeprom\n"
+	"    - save memory data structure to the EEPROM\n"
+	"mac initialize\n"
+	"    - initialize the in-memory EEPROM copy with default data\n"
+	"mac mac0_address <xx:xx:xx:xx:xx:xx>\n"
+	"    - stores a MAC0 address into the local EEPROM copy\n"
+	"mac mac1_address <xx:xx:xx:xx:xx:xx>\n"
+	"    - stores a MAC1 address into the local EEPROM copy\n"
+	"mac pcb_revision <?>\n"
+	"    - stores a StarFive PCB revision into the local EEPROM copy\n"
+	"mac bom_revision <A>\n"
+	"    - stores a StarFive BOM revision into the local EEPROM copy\n"
+	"mac product_id <VF7110A1-2228-D008E000-xxxxxxxx>\n"
+	"    - stores a StarFive product ID into the local EEPROM copy\n");
+	return 0;
+}
+
+int do_mac(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+	char *cmd;
+
+	if (argc == 1) {
+		show_eeprom();
+		return 0;
+	}
+
+	if (argc > 3)
+		return print_usage();
+
+	cmd = argv[1];
+
+	/* Commands with no argument */
+	if (!strcmp(cmd, "read_eeprom")) {
+		has_been_read = 0;
+		return read_eeprom();
+	} else if (!strcmp(cmd, "initialize")) {
+		init_local_copy();
+		return 0;
+	} else if (!strcmp(cmd, "write_eeprom")) {
+		return prog_eeprom(STARFIVE_EEPROM_HATS_SIZE_MAX);
+	}
+
+	if (argc != 3)
+		return print_usage();
+
+	if (is_match_magic()) {
+		printf("Please read the EEPROM ('read_eeprom') and/or initialize the EEPROM ('initialize') first.\n");
+		return 0;
+	}
+
+	if (!strcmp(cmd, "mac0_address")) {
+		set_mac_address(argv[2], 0);
+		return 0;
+	} else if (!strcmp(cmd, "mac1_address")) {
+		set_mac_address(argv[2], 1);
+		return 0;
+	} else if (!strcmp(cmd, "pcb_revision")) {
+		set_pcb_revision(argv[2]);
+		return 0;
+	} else if (!strcmp(cmd, "bom_revision")) {
+		set_bom_revision(argv[2]);
+		return 0;
+	} else if (!strcmp(cmd, "product_id")) {
+		set_product_id(argv[2]);
+		return 0;
+	}
+
+	return print_usage();
+}
+
+/**
+ * mac_read_from_eeprom() - read the MAC address & the serial number in EEPROM
+ *
+ * This function reads the MAC address and the serial number from EEPROM and
+ * sets the appropriate environment variables for each one read.
+ *
+ * The environment variables are only set if they haven't been set already.
+ * This ensures that any user-saved variables are never overwritten.
+ *
+ * If CONFIG_ID_EEPROM is enabled, this function will be called in
+ * "static init_fnc_t init_sequence_r[]" of u-boot/common/board_r.c.
+ */
+int mac_read_from_eeprom(void)
+{
+	/**
+	 * try to fill the buff from EEPROM,
+	 * always return SUCCESS, even some error happens.
+	 */
+	if (read_eeprom()) {
+		dump_raw_eeprom();
+		return 0;
+	}
+
+	// 1, setup ethaddr env
+	eth_env_set_enetaddr("eth0addr", pbuf.eeprom.atom4.data.mac0_addr);
+	eth_env_set_enetaddr("eth1addr", pbuf.eeprom.atom4.data.mac1_addr);
+
+	/**
+	 * 2, setup serial# env, reference to hifive-platform-i2c-eeprom.c,
+	 * serial# can be a ASCII string, but not just a hex number, so we
+	 * setup serial# in the 32Byte format:
+	 * "VF7100A1-2201-D008E000-00000001;"
+	 * "<product>-<date>-<DDR&eMMC>-<serial_number>"
+	 * <date>: 4Byte, should be the output of `date +%y%W`
+	 * <DDR&eMMC>: 8Byte, "D008" means 8GB, "D01T" means 1TB;
+	 *     "E000" means no eMMC,"E032" means 32GB, "E01T" means 1TB.
+	 * <serial_number>: 8Byte, the Unique Identifier of board in hex.
+	 */
+	if (!env_get("serial#"))
+		env_set("serial#", pbuf.eeprom.atom1.data.pstr);
+
+	printf("StarFive EEPROM format v%u\n", pbuf.eeprom.header.version);
+	show_eeprom();
+	return 0;
+}
+
+/**
+ * get_pcb_revision_from_eeprom - get the PCB revision
+ *
+ * 1.2A return 'A'/'a', 1.3B return 'B'/'b',other values are illegal
+ */
+u8 get_pcb_revision_from_eeprom(void)
+{
+	u8 pv = 0xFF;
+
+	if (read_eeprom())
+		return pv;
+
+	return pbuf.eeprom.atom1.data.pstr[6];
+}
+
+/**
+ * get_ddr_size_from_eeprom - get the DDR size
+ * pstr:  VF7110A1-2228-D008E000-00000001
+ * VF7110A1/VF7110B1 : VisionFive JH7110A /VisionFive JH7110B
+ * D008: 8GB LPDDR4
+ * E000: No emmc device, ECxx: include emmc device, xx: Capacity size[GB]
+ * return: the field of 'D008E000'
+ */
+
+u32 get_ddr_size_from_eeprom(void)
+{
+	u32 pv = 0xFFFFFFFF;
+
+	if (read_eeprom())
+		return pv;
+
+	return hextoul(&pbuf.eeprom.atom1.data.pstr[14], NULL);
+}
diff --git a/board/thead/th1520_lpi4a/Kconfig b/board/thead/th1520_lpi4a/Kconfig
new file mode 100644
index 0000000..6222461
--- /dev/null
+++ b/board/thead/th1520_lpi4a/Kconfig
@@ -0,0 +1,42 @@
+if TARGET_TH1520_LPI4A
+
+config ARCH_THEAD
+	bool
+	default y
+
+config SYS_BOARD
+	default "th1520_lpi4a"
+
+config SYS_VENDOR
+	default "thead"
+
+config SYS_CPU
+	default "generic"
+
+config SYS_CONFIG_NAME
+	default "th1520_lpi4a"
+
+config TEXT_BASE
+	default 0x01b00000 if SPL
+	default 0x01c00000 if !RISCV_SMODE
+	default 0x01c00000 if RISCV_SMODE
+
+config SPL_TEXT_BASE
+	default 0x08000000
+
+config SPL_OPENSBI_LOAD_ADDR
+	default 0x80000000
+
+config BOARD_SPECIFIC_OPTIONS
+	def_bool y
+	select ARCH_EARLY_INIT_R
+	imply CPU
+	imply CPU_RISCV
+	imply RISCV_TIMER if RISCV_SMODE
+	imply CMD_CPU
+	imply SMP
+	imply SUPPORT_OF_CONTROL
+	imply OF_CONTROL
+	imply OF_REAL
+
+endif
diff --git a/board/thead/th1520_lpi4a/MAINTAINERS b/board/thead/th1520_lpi4a/MAINTAINERS
new file mode 100644
index 0000000..36c7ab7
--- /dev/null
+++ b/board/thead/th1520_lpi4a/MAINTAINERS
@@ -0,0 +1,7 @@
+Lichee PI 4A
+M:	Wei Fu <wefu@redhat.com>
+M:	Yixun Lan <dlan@gentoo.org>
+S:	Maintained
+F:	board/thead/th1520_lpi4a/
+F:	configs/th1520_lpi4a_defconfig
+F:	doc/board/thead/lpi4a.rst
diff --git a/board/thead/th1520_lpi4a/Makefile b/board/thead/th1520_lpi4a/Makefile
new file mode 100644
index 0000000..9671b3b
--- /dev/null
+++ b/board/thead/th1520_lpi4a/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Copyright (c) 2023, Yixun Lan <dlan@gentoo.org>
+
+obj-y += board.o
diff --git a/board/thead/th1520_lpi4a/board.c b/board/thead/th1520_lpi4a/board.c
new file mode 100644
index 0000000..16c3e45
--- /dev/null
+++ b/board/thead/th1520_lpi4a/board.c
@@ -0,0 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2023, Yixun Lan <dlan@gentoo.org>
+ *
+ */
+
+#include <common.h>
+#include <cpu_func.h>
+
+int board_init(void)
+{
+	enable_caches();
+
+	return 0;
+}
diff --git a/cmd/efidebug.c b/cmd/efidebug.c
index 9622430..0be3af3 100644
--- a/cmd/efidebug.c
+++ b/cmd/efidebug.c
@@ -486,6 +486,7 @@
 			if (guidcmp(guid[j], &efi_guid_device_path))
 				printf("  %pUs\n", guid[j]);
 		}
+		efi_free_pool(guid);
 	}
 
 	efi_free_pool(handles);
diff --git a/configs/starfive_visionfive2_defconfig b/configs/starfive_visionfive2_defconfig
index ffbc4b9..570a1f5 100644
--- a/configs/starfive_visionfive2_defconfig
+++ b/configs/starfive_visionfive2_defconfig
@@ -7,12 +7,13 @@
 CONFIG_CUSTOM_SYS_INIT_SP_ADDR=0x80000000
 CONFIG_SF_DEFAULT_SPEED=100000000
 CONFIG_SPL_DM_SPI=y
-CONFIG_DEFAULT_DEVICE_TREE="jh7110-starfive-visionfive-2-v1.3b"
+CONFIG_DEFAULT_DEVICE_TREE="jh7110-starfive-visionfive-2"
 CONFIG_SPL_TEXT_BASE=0x8000000
 CONFIG_SYS_PROMPT="StarFive #"
 CONFIG_OF_LIBFDT_OVERLAY=y
 CONFIG_DM_RESET=y
 CONFIG_SPL_MMC=y
+CONFIG_SPL_DRIVERS_MISC=y
 CONFIG_SPL_STACK=0x8180000
 CONFIG_SPL=y
 CONFIG_SPL_SPI_FLASH_SUPPORT=y
@@ -23,6 +24,7 @@
 CONFIG_ARCH_RV64I=y
 CONFIG_CMODEL_MEDANY=y
 CONFIG_RISCV_SMODE=y
+# CONFIG_OF_BOARD_FIXUP is not set
 CONFIG_FIT=y
 CONFIG_DISTRO_DEFAULTS=y
 CONFIG_QSPI_BOOT=y
@@ -31,9 +33,11 @@
 CONFIG_BOOTARGS="console=ttyS0,115200 debug rootwait earlycon=sbi"
 CONFIG_USE_PREBOOT=y
 CONFIG_PREBOOT="setenv fdt_addr ${fdtcontroladdr};fdt addr ${fdtcontroladdr};"
-CONFIG_DEFAULT_FDT_FILE="starfive/jh7110-starfive-visionfive-2-v1.3b.dtb"
+CONFIG_DEFAULT_FDT_FILE="starfive/jh7110-starfive-visionfive-2.dtb"
 CONFIG_DISPLAY_CPUINFO=y
 CONFIG_DISPLAY_BOARDINFO=y
+CONFIG_ID_EEPROM=y
+CONFIG_SYS_EEPROM_BUS_NUM=5
 CONFIG_SPL_MAX_SIZE=0x40000
 CONFIG_SPL_PAD_TO=0x0
 CONFIG_SPL_BSS_START_ADDR=0x8040000
@@ -45,19 +49,34 @@
 CONFIG_SYS_SPL_MALLOC_SIZE=0x400000
 CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_USE_PARTITION=y
 CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_PARTITION=0x2
+CONFIG_SPL_I2C=y
 CONFIG_SPL_DM_SPI_FLASH=y
 CONFIG_SPL_DM_RESET=y
 CONFIG_SPL_SPI_LOAD=y
 CONFIG_SYS_CBSIZE=256
 CONFIG_SYS_PBSIZE=276
 CONFIG_SYS_BOOTM_LEN=0x4000000
+CONFIG_CMD_EEPROM=y
+CONFIG_SYS_EEPROM_SIZE=512
+CONFIG_SYS_EEPROM_PAGE_WRITE_BITS=4
+CONFIG_SYS_EEPROM_PAGE_WRITE_DELAY_MS=5
 CONFIG_CMD_MEMINFO=y
+CONFIG_CMD_I2C=y
 CONFIG_CMD_TFTPPUT=y
+CONFIG_OF_BOARD=y
 CONFIG_SYS_RELOC_GD_ENV_ADDR=y
+CONFIG_SPL_DM_SEQ_ALIAS=y
+CONFIG_REGMAP=y
+CONFIG_SYSCON=y
 CONFIG_SPL_CLK_COMPOSITE_CCF=y
 CONFIG_CLK_COMPOSITE_CCF=y
 CONFIG_SPL_CLK_JH7110=y
-# CONFIG_I2C is not set
+CONFIG_DM_I2C=y
+CONFIG_SYS_I2C_DW=y
+CONFIG_MISC=y
+CONFIG_I2C_EEPROM=y
+CONFIG_SPL_I2C_EEPROM=y
+CONFIG_SYS_I2C_EEPROM_ADDR=0X50
 CONFIG_MMC_HS400_SUPPORT=y
 CONFIG_SPL_MMC_HS400_SUPPORT=y
 CONFIG_MMC_DW=y
@@ -66,6 +85,13 @@
 CONFIG_SPI_FLASH_GIGADEVICE=y
 CONFIG_SPI_FLASH_ISSI=y
 CONFIG_SPI_FLASH_MACRONIX=y
+CONFIG_PHY_MOTORCOMM=y
+CONFIG_DM_MDIO=y
+CONFIG_DM_ETH_PHY=y
+CONFIG_DWC_ETH_QOS=y
+CONFIG_DWC_ETH_QOS_STARFIVE=y
+CONFIG_RGMII=y
+CONFIG_RMII=y
 CONFIG_PINCTRL=y
 CONFIG_PINCONF=y
 CONFIG_SPL_PINCTRL=y
diff --git a/configs/th1520_lpi4a_defconfig b/configs/th1520_lpi4a_defconfig
new file mode 100644
index 0000000..710ec6a
--- /dev/null
+++ b/configs/th1520_lpi4a_defconfig
@@ -0,0 +1,82 @@
+CONFIG_RISCV=y
+CONFIG_SYS_MALLOC_LEN=0x800000
+CONFIG_SYS_MALLOC_F_LEN=0x3000
+CONFIG_NR_DRAM_BANKS=1
+CONFIG_HAS_CUSTOM_SYS_INIT_SP_ADDR=y
+CONFIG_CUSTOM_SYS_INIT_SP_ADDR=0x80200000
+CONFIG_DEFAULT_DEVICE_TREE="th1520-lichee-pi-4a"
+CONFIG_SYS_PROMPT="LPI4A=> "
+CONFIG_SYS_LOAD_ADDR=0x80200000
+# CONFIG_SMP is not set
+CONFIG_TARGET_TH1520_LPI4A=y
+CONFIG_ARCH_RV64I=y
+CONFIG_OF_BOARD_FIXUP=y
+CONFIG_SYS_BOOT_GET_CMDLINE=y
+CONFIG_SYS_BOOT_GET_KBD=y
+CONFIG_FIT=y
+# CONFIG_FIT_FULL_CHECK is not set
+# CONFIG_FIT_PRINT is not set
+# CONFIG_BOOTSTD is not set
+# CONFIG_LEGACY_IMAGE_FORMAT is not set
+CONFIG_DISTRO_DEFAULTS=y
+CONFIG_BOOTARGS_SUBST=y
+CONFIG_BOOTCOMMAND=""
+CONFIG_DEFAULT_FDT_FILE="thead/th1520-lichee-pi-4a.dtb"
+CONFIG_LOG=y
+CONFIG_DISPLAY_CPUINFO=y
+CONFIG_DISPLAY_BOARDINFO=y
+CONFIG_DISPLAY_BOARDINFO_LATE=y
+CONFIG_SYS_CBSIZE=256
+CONFIG_SYS_PBSIZE=276
+CONFIG_CMD_CONFIG=y
+CONFIG_CMD_LICENSE=y
+CONFIG_CMD_BOOTZ=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=0x4000000
+CONFIG_CMD_BOOTMENU=y
+# CONFIG_CMD_ELF is not set
+# CONFIG_CMD_IMI is not set
+# CONFIG_CMD_XIMG is not set
+# CONFIG_CMD_EXPORTENV is not set
+# CONFIG_CMD_IMPORTENV is not set
+# CONFIG_CMD_EDITENV is not set
+# CONFIG_CMD_SAVEENV is not set
+# CONFIG_CMD_CRC32 is not set
+# CONFIG_CMD_MEMORY is not set
+# CONFIG_CMD_LZMADEC is not set
+# CONFIG_CMD_UNLZ4 is not set
+# CONFIG_CMD_UNZIP is not set
+# CONFIG_CMD_LOADB is not set
+# CONFIG_CMD_LOADS is not set
+# CONFIG_CMD_ITEST is not set
+# CONFIG_CMD_SOURCE is not set
+# CONFIG_CMD_SETEXPR is not set
+# CONFIG_CMD_SLEEP is not set
+CONFIG_PARTITION_TYPE_GUID=y
+CONFIG_SYS_RELOC_GD_ENV_ADDR=y
+CONFIG_VERSION_VARIABLE=y
+# CONFIG_NET is not set
+# CONFIG_BLOCK_CACHE is not set
+# CONFIG_GPIO is not set
+# CONFIG_I2C is not set
+# CONFIG_INPUT is not set
+# CONFIG_DM_MMC is not set
+# CONFIG_MTD is not set
+# CONFIG_POWER is not set
+CONFIG_SYS_NS16550=y
+CONFIG_RISCV_TIMER=y
+CONFIG_AES=y
+CONFIG_BLAKE2=y
+CONFIG_SHA512=y
+CONFIG_LZ4=y
+CONFIG_LZMA=y
+CONFIG_LZO=y
+CONFIG_ZLIB_UNCOMPRESS=y
+CONFIG_BZIP2=y
+CONFIG_ZSTD=y
+CONFIG_LIB_RATIONAL=y
+# CONFIG_EFI_LOADER is not set
+# CONFIG_LMB_USE_MAX_REGIONS is not set
diff --git a/doc/board/index.rst b/doc/board/index.rst
index ce1fbe2..84aa8c1 100644
--- a/doc/board/index.rst
+++ b/doc/board/index.rst
@@ -46,6 +46,7 @@
    starfive/index
    ste/index
    tbs/index
+   thead/index
    ti/index
    toradex/index
    variscite/index
diff --git a/doc/board/starfive/visionfive2.rst b/doc/board/starfive/visionfive2.rst
index 4d43ac9..951e0d8 100644
--- a/doc/board/starfive/visionfive2.rst
+++ b/doc/board/starfive/visionfive2.rst
@@ -62,7 +62,7 @@
 .. code-block:: console
 
 	cd <U-Boot-dir>
-	make starfive_visionfive2_13b_defconfig
+	make starfive_visionfive2_defconfig
 	make OPENSBI=$(opensbi_dir)/opensbi/build/platform/generic/firmware/fw_dynamic.bin
 
 This will generate spl/u-boot-spl.bin and FIT image (u-boot.itb)
@@ -118,7 +118,7 @@
 	sudo cp u-boot.itb /mnt/
 	sudo cp Image.gz /mnt/
 	sudo cp initramfs.cpio.gz /mnt/
-	sudo cp jh7110-starfive-visionfive-2-v1.3b.dtb /mnt/
+	sudo cp jh7110-starfive-visionfive-2.dtb /mnt/
 	sudo umount /mnt
 
 Booting
@@ -264,7 +264,7 @@
 
 	StarFive #fatload mmc 1:3 ${kernel_addr_r} Image.gz
 	6429424 bytes read in 394 ms (15.6 MiB/s)
-	StarFive #fatload mmc 1:3 ${fdt_addr_r} jh7110-starfive-visionfive-2-v1.3b.dtb
+	StarFive #fatload mmc 1:3 ${fdt_addr_r} jh7110-starfive-visionfive-2.dtb
 	11285 bytes read in 5 ms (2.2 MiB/s)
 	StarFive #fatload mmc 1:3 ${ramdisk_addr_r} initramfs.cpio.gz
 	152848495 bytes read in 9271 ms (15.7 MiB/s)
diff --git a/doc/board/thead/index.rst b/doc/board/thead/index.rst
new file mode 100644
index 0000000..41566d3
--- /dev/null
+++ b/doc/board/thead/index.rst
@@ -0,0 +1,9 @@
+.. SPDX-License-Identifier: GPL-2.0+
+
+T-HEAD
+========
+
+.. toctree::
+   :maxdepth: 1
+
+   lpi4a
diff --git a/doc/board/thead/lpi4a.rst b/doc/board/thead/lpi4a.rst
new file mode 100644
index 0000000..e395c6a
--- /dev/null
+++ b/doc/board/thead/lpi4a.rst
@@ -0,0 +1,129 @@
+.. SPDX-License-Identifier: GPL-2.0+
+
+Sipeed's Lichee PI 4A based on T-HEAD TH1520 SoC
+================================================
+
+The LicheePi4A is a high-performance RISC-V SBC based on TH1520(4xC910@1.85GHz),
+comes with 4/8/16 GB RAM, and up to 128GB eMMC, and rich peripherals.
+
+ - SoC            T-HEAD TH1520 SoC
+ - System Memory  4GB, 8GB, or 16GB LPDDR4X
+ - Storage        eMMC flash with 8/32/128 GB
+ -                external microSD slot
+ - Networking     2x Gigabit Ethernet
+ -                WiFi+BT
+ - Display        HDMI2.0, 4-lane MIPI DSI
+ - Camera         4-lane MIPI CSI + 2x2-lane MIPI CSI
+ - Audio          Onboard Speaker, 2xMEMS MIC, 3.5mm headphone jack
+ - USB            4xUSB3.0 Type-A, 1xUSB2.0 Type-C
+ - GPIO           2x10Pin breakout, UART/IIC/SPI
+ - Power          DC 12V/2A, POE 5V/2.4A, USB Type-C 5V/2A
+
+TH1520 RISC-V SoC
+-----------------
+
+The TH1520 SoC consist of quad-core RISC-V Xuantie C910 (RV64GCV) processor,
+Xuantie C906 audio DSP, low power Xuantie E902 core, it also integrate
+Imagination GPU for graphics, and 4 TOPS NPU for AI acceleration.
+
+Mainline support
+----------------
+
+The support for following drivers are already enabled:
+
+1. ns16550 UART Driver.
+
+Building
+~~~~~~~~
+
+1. Add the RISC-V toolchain to your PATH.
+2. Setup ARCH & cross compilation environment variable:
+
+.. code-block:: none
+
+   export CROSS_COMPILE=<riscv64 toolchain prefix>
+
+The U-Boot is capable of running in M-Mode, so we can directly build it.
+
+.. code-block:: console
+
+	cd <U-Boot-dir>
+	make th1520_lpi4a_defconfig
+	make
+
+This will generate u-boot-dtb.bin
+
+Booting
+~~~~~~~
+
+Currently, we rely on vendor u-boot to initialize the clock, pinctrl subsystem,
+and chain load the mainline u-boot image either via tftp or emmc storage,
+then bootup from it.
+
+Sample boot log from Lichee PI 4A board via tftp
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. code-block:: none
+
+	brom_ver 8
+	[APP][E] protocol_connect failed, exit.
+
+	U-Boot SPL 2020.01-00016-g8c870a6be8 (May 20 2023 - 01:04:49 +0000)
+	FM[1] lpddr4x dualrank freq=3733 64bit dbi_off=n sdram init
+	ddr initialized, jump to uboot
+	image has no header
+
+
+	U-Boot 2020.01-00016-g8c870a6be8 (May 20 2023 - 01:04:49 +0000)
+
+	CPU:   rv64imafdcvsu
+	Model: T-HEAD c910 light
+	DRAM:  8 GiB
+	C910 CPU FREQ: 750MHz
+	AHB2_CPUSYS_HCLK FREQ: 250MHz
+	AHB3_CPUSYS_PCLK FREQ: 125MHz
+	PERISYS_AHB_HCLK FREQ: 250MHz
+	PERISYS_APB_PCLK FREQ: 62MHz
+	GMAC PLL POSTDIV FREQ: 1000MHZ
+	DPU0 PLL POSTDIV FREQ: 1188MHZ
+	DPU1 PLL POSTDIV FREQ: 1188MHZ
+	MMC:   sdhci@ffe7080000: 0, sd@ffe7090000: 1
+	Loading Environment from MMC... OK
+	Error reading output register
+	Warning: cannot get lcd-en GPIO
+	LCD panel cannot be found : -121
+	splash screen startup cost 16 ms
+	In:    serial
+	Out:   serial
+	Err:   serial
+	Net:
+	Warning: ethernet@ffe7070000 using MAC address from ROM
+	eth0: ethernet@ffe7070000ethernet@ffe7070000:0 is connected to ethernet@ffe7070000.  Reconnecting to ethernet@ffe7060000
+
+	Warning: ethernet@ffe7060000 (eth1) using random MAC address - 42:25:d4:16:5f:fc
+	, eth1: ethernet@ffe7060000
+	Hit any key to stop autoboot:  2
+	ethernet@ffe7060000 Waiting for PHY auto negotiation to complete.. done
+	Speed: 1000, full duplex
+	Using ethernet@ffe7070000 device
+	TFTP from server 192.168.8.50; our IP address is 192.168.8.45
+	Filename 'u-boot-dtb.bin'.
+	Load address: 0x1c00000
+	Loading: * #########################
+		 8 MiB/s
+	done
+	Bytes transferred = 376686 (5bf6e hex)
+	## Starting application at 0x01C00000 ...
+
+        U-Boot 2023.07-rc2-00004-g1befbe31c1 (May 23 2023 - 18:40:01 +0800)
+
+        CPU:   rv64imafdc
+        Model: Sipeed Lichee Pi 4A
+        DRAM:  8 GiB
+        Core:  13 devices, 6 uclasses, devicetree: separate
+        Loading Environment from <NULL>... OK
+        In:    serial@ffe7014000
+        Out:   serial@ffe7014000
+        Err:   serial@ffe7014000
+        Model: Sipeed Lichee Pi 4A
+        LPI4A=>
diff --git a/doc/develop/docstyle.rst b/doc/develop/docstyle.rst
index f9ba83a..50506d6 100644
--- a/doc/develop/docstyle.rst
+++ b/doc/develop/docstyle.rst
@@ -20,7 +20,7 @@
 * For documentation we use reStructured text conforming to the requirements
   of `Sphinx <https://www.sphinx-doc.org>`_.
 * For documentation within code we follow the Linux kernel guide
-  `Writing kernel-doc comments <https://docs.kernel.org/doc-guide/kernel-doc.html>`_.
+  `Writing kernel-doc comments <https://www.kernel.org/doc/html/latest/doc-guide/kernel-doc.html>`_.
 * We try to stick to 80 columns per line in documents.
 * For tables we prefer simple tables over grid tables. We avoid list tables
   as they make the reStructured text documents hard to read.
diff --git a/doc/usage/blkmap.rst b/doc/usage/blkmap.rst
index dbfa8e5..7337ea5 100644
--- a/doc/usage/blkmap.rst
+++ b/doc/usage/blkmap.rst
@@ -19,7 +19,7 @@
 The implementation is loosely modeled on Linux's "Device Mapper"
 subsystem, see `kernel documentation`_ for more information.
 
-.. _kernel documentation: https://docs.kernel.org/admin-guide/device-mapper/index.html
+.. _kernel documentation: https://www.kernel.org/doc/html/latest/admin-guide/device-mapper/index.html
 
 
 Example: Netbooting an Ext4 Image
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index d662dd3..0ed39a6 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -249,6 +249,13 @@
 	  The Synopsys Designware Ethernet QOS IP block with specific
 	  configuration used in Qcom QCS404 SoC.
 
+config DWC_ETH_QOS_STARFIVE
+	bool "Synopsys DWC Ethernet QOS device support for STARFIVE"
+	depends on DWC_ETH_QOS
+	help
+	  The Synopsys Designware Ethernet QOS IP block with specific
+	  configuration used in STARFIVE  JH7110 soc.
+
 config E1000
 	bool "Intel PRO/1000 Gigabit Ethernet support"
 	depends on PCI
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 46a40e2..d4af253 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -21,6 +21,7 @@
 obj-$(CONFIG_DWC_ETH_QOS) += dwc_eth_qos.o
 obj-$(CONFIG_DWC_ETH_QOS_IMX) += dwc_eth_qos_imx.o
 obj-$(CONFIG_DWC_ETH_QOS_QCOM) += dwc_eth_qos_qcom.o
+obj-$(CONFIG_DWC_ETH_QOS_STARFIVE) += dwc_eth_qos_starfive.o
 obj-$(CONFIG_E1000) += e1000.o
 obj-$(CONFIG_E1000_SPI) += e1000_spi.o
 obj-$(CONFIG_EEPRO100) += eepro100.o
diff --git a/drivers/net/dwc_eth_qos.c b/drivers/net/dwc_eth_qos.c
index 9bbba6e..1e92bd9 100644
--- a/drivers/net/dwc_eth_qos.c
+++ b/drivers/net/dwc_eth_qos.c
@@ -1725,6 +1725,12 @@
 		.data = (ulong)&eqos_qcom_config
 	},
 #endif
+#if IS_ENABLED(CONFIG_DWC_ETH_QOS_STARFIVE)
+	{
+		.compatible = "starfive,jh7110-dwmac",
+		.data = (ulong)&eqos_jh7110_config
+	},
+#endif
 
 	{ }
 };
diff --git a/drivers/net/dwc_eth_qos.h b/drivers/net/dwc_eth_qos.h
index fddbe93..a6b719a 100644
--- a/drivers/net/dwc_eth_qos.h
+++ b/drivers/net/dwc_eth_qos.h
@@ -289,3 +289,4 @@
 
 extern struct eqos_config eqos_imx_config;
 extern struct eqos_config eqos_qcom_config;
+extern struct eqos_config eqos_jh7110_config;
diff --git a/drivers/net/dwc_eth_qos_starfive.c b/drivers/net/dwc_eth_qos_starfive.c
new file mode 100644
index 0000000..5be8ac0
--- /dev/null
+++ b/drivers/net/dwc_eth_qos_starfive.c
@@ -0,0 +1,249 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2023 StarFive Technology Co., Ltd.
+ * Author: Yanhong Wang<yanhong.wang@starfivetech.com>
+ */
+
+#include <common.h>
+#include <asm/cache.h>
+#include <asm/gpio.h>
+#include <clk.h>
+#include <dm.h>
+#include <eth_phy.h>
+#include <net.h>
+#include <regmap.h>
+#include <reset.h>
+#include <syscon.h>
+
+#include "dwc_eth_qos.h"
+
+#define STARFIVE_DWMAC_PHY_INFT_RGMII	0x1
+#define STARFIVE_DWMAC_PHY_INFT_RMII	0x4
+#define STARFIVE_DWMAC_PHY_INFT_FIELD	0x7U
+
+struct starfive_platform_data {
+	struct regmap *regmap;
+	struct reset_ctl_bulk resets;
+	struct clk_bulk clks;
+	phy_interface_t interface;
+	u32 offset;
+	u32 shift;
+	bool tx_use_rgmii_clk;
+};
+
+static int eqos_interface_init_jh7110(struct udevice *dev)
+{
+	struct eth_pdata *pdata = dev_get_plat(dev);
+	struct starfive_platform_data *data = pdata->priv_pdata;
+	struct ofnode_phandle_args args;
+	unsigned int mode;
+	int ret;
+
+	switch (data->interface) {
+	case PHY_INTERFACE_MODE_RMII:
+		mode = STARFIVE_DWMAC_PHY_INFT_RMII;
+		break;
+
+	case PHY_INTERFACE_MODE_RGMII:
+	case PHY_INTERFACE_MODE_RGMII_ID:
+		mode = STARFIVE_DWMAC_PHY_INFT_RGMII;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	ret = dev_read_phandle_with_args(dev, "starfive,syscon", NULL,
+					 2, 0, &args);
+	if (ret)
+		return ret;
+
+	if (args.args_count != 2)
+		return -EINVAL;
+
+	data->offset = args.args[0];
+	data->shift = args.args[1];
+	data->regmap = syscon_regmap_lookup_by_phandle(dev, "starfive,syscon");
+	if (IS_ERR(data->regmap)) {
+		ret = PTR_ERR(data->regmap);
+		pr_err("Failed to get regmap: %d\n", ret);
+		return ret;
+	}
+
+	return regmap_update_bits(data->regmap, data->offset,
+				  STARFIVE_DWMAC_PHY_INFT_FIELD << data->shift,
+				  mode << data->shift);
+}
+
+static int eqos_set_tx_clk_speed_jh7110(struct udevice *dev)
+{
+	struct eqos_priv *eqos = dev_get_priv(dev);
+	struct eth_pdata *pdata = dev_get_plat(dev);
+	struct starfive_platform_data *data = pdata->priv_pdata;
+	struct clk *pclk, *c;
+	ulong rate;
+	int ret;
+
+	/* Generally, the rgmii_tx clock is provided by the internal clock,
+	 * which needs to match the corresponding clock frequency according
+	 * to different speeds. If the rgmii_tx clock is provided by the
+	 * external rgmii_rxin, there is no need to configure the clock
+	 * internally, because rgmii_rxin will be adaptively adjusted.
+	 */
+	if (data->tx_use_rgmii_clk)
+		return 0;
+
+	switch (eqos->phy->speed) {
+	case SPEED_1000:
+		rate = 125 * 1000 * 1000;
+		break;
+	case SPEED_100:
+		rate = 25 * 1000 * 1000;
+		break;
+	case SPEED_10:
+		rate = 2.5 * 1000 * 1000;
+		break;
+	default:
+		pr_err("invalid speed %d", eqos->phy->speed);
+		return -EINVAL;
+	}
+
+	/* eqos->clk_tx clock has no set rate operation, so just set the parent
+	 * clock rate directly
+	 */
+	ret = clk_get_by_id(eqos->clk_tx.id, &c);
+	if (ret)
+		return ret;
+
+	pclk = clk_get_parent(c);
+	if (pclk) {
+		ret = clk_set_rate(pclk, rate);
+		if (ret < 0) {
+			pr_err("jh7110 (clk_tx, %lu) failed: %d", rate, ret);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static ulong eqos_get_tick_clk_rate_jh7110(struct udevice *dev)
+{
+	struct eqos_priv *eqos = dev_get_priv(dev);
+
+	return clk_get_rate(&eqos->clk_tx);
+}
+
+static int eqos_start_clks_jh7110(struct udevice *dev)
+{
+	struct eth_pdata *pdata = dev_get_plat(dev);
+	struct starfive_platform_data *data = pdata->priv_pdata;
+
+	return clk_enable_bulk(&data->clks);
+}
+
+static int eqos_stop_clks_jh7110(struct udevice *dev)
+{
+	struct eth_pdata *pdata = dev_get_plat(dev);
+	struct starfive_platform_data *data = pdata->priv_pdata;
+
+	return clk_disable_bulk(&data->clks);
+}
+
+static int eqos_start_resets_jh7110(struct udevice *dev)
+{
+	struct eth_pdata *pdata = dev_get_plat(dev);
+	struct starfive_platform_data *data = pdata->priv_pdata;
+
+	return reset_deassert_bulk(&data->resets);
+}
+
+static int eqos_stop_resets_jh7110(struct udevice *dev)
+{
+	struct eth_pdata *pdata = dev_get_plat(dev);
+	struct starfive_platform_data *data = pdata->priv_pdata;
+
+	return reset_assert_bulk(&data->resets);
+}
+
+static int eqos_remove_resources_jh7110(struct udevice *dev)
+{
+	struct eth_pdata *pdata = dev_get_plat(dev);
+	struct starfive_platform_data *data = pdata->priv_pdata;
+
+	reset_assert_bulk(&data->resets);
+	clk_disable_bulk(&data->clks);
+
+	return 0;
+}
+
+static int eqos_probe_resources_jh7110(struct udevice *dev)
+{
+	struct eqos_priv *eqos = dev_get_priv(dev);
+	struct eth_pdata *pdata = dev_get_plat(dev);
+	struct starfive_platform_data *data;
+	int ret;
+
+	data = calloc(1, sizeof(struct starfive_platform_data));
+	if (!data)
+		return -ENOMEM;
+
+	pdata->priv_pdata = data;
+	data->interface = eqos->config->interface(dev);
+	if (data->interface == PHY_INTERFACE_MODE_NA) {
+		pr_err("Invalid PHY interface\n");
+		return -EINVAL;
+	}
+
+	ret = reset_get_bulk(dev, &data->resets);
+	if (ret < 0)
+		return ret;
+
+	ret = clk_get_bulk(dev, &data->clks);
+	if (ret < 0)
+		return ret;
+
+	ret = clk_get_by_name(dev, "gtx", &eqos->clk_tx);
+	if (ret)
+		return ret;
+
+	data->tx_use_rgmii_clk = dev_read_bool(dev, "starfive,tx-use-rgmii-clk");
+
+	return eqos_interface_init_jh7110(dev);
+}
+
+static struct eqos_ops eqos_jh7110_ops = {
+	.eqos_inval_desc = eqos_inval_desc_generic,
+	.eqos_flush_desc = eqos_flush_desc_generic,
+	.eqos_inval_buffer = eqos_inval_buffer_generic,
+	.eqos_flush_buffer = eqos_flush_buffer_generic,
+	.eqos_probe_resources = eqos_probe_resources_jh7110,
+	.eqos_remove_resources = eqos_remove_resources_jh7110,
+	.eqos_stop_resets = eqos_stop_resets_jh7110,
+	.eqos_start_resets = eqos_start_resets_jh7110,
+	.eqos_stop_clks = eqos_stop_clks_jh7110,
+	.eqos_start_clks = eqos_start_clks_jh7110,
+	.eqos_calibrate_pads = eqos_null_ops,
+	.eqos_disable_calibration = eqos_null_ops,
+	.eqos_set_tx_clk_speed = eqos_set_tx_clk_speed_jh7110,
+	.eqos_get_enetaddr = eqos_null_ops,
+	.eqos_get_tick_clk_rate = eqos_get_tick_clk_rate_jh7110
+};
+
+/* mdio_wait: There is no need to wait after setting the MAC_MDIO_Address register
+ * swr_wait: Software reset bit must be read at least 4 CSR clock cycles
+ *          after it is written to 1.
+ * config_mac: Enable rx queue to DCB mode.
+ * config_mac_mdio: CSR clock range is 250-300 Mhz.
+ * axi_bus_width: The width of the data bus is 64 bit.
+ */
+struct eqos_config __maybe_unused eqos_jh7110_config = {
+	.reg_access_always_ok = false,
+	.mdio_wait = 0,
+	.swr_wait = 4,
+	.config_mac = EQOS_MAC_RXQ_CTRL0_RXQ0EN_ENABLED_DCB,
+	.config_mac_mdio = EQOS_MAC_MDIO_ADDRESS_CR_250_300,
+	.axi_bus_width = EQOS_AXI_WIDTH_64,
+	.interface = dev_read_phy_mode,
+	.ops = &eqos_jh7110_ops
+};
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index 2415877..0c3c39a 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -220,6 +220,12 @@
 
 endif # PHY_MICREL
 
+config PHY_MOTORCOMM
+	tristate "Motorcomm PHYs"
+	help
+	  Enables support for Motorcomm network PHYs.
+	  Currently supports the YT8531 Gigabit Ethernet PHYs.
+
 config PHY_MSCC
 	bool "Microsemi Corp Ethernet PHYs support"
 
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index 85d17f1..2487f36 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -24,6 +24,7 @@
 obj-$(CONFIG_PHY_MICREL_KSZ8XXX) += micrel_ksz8xxx.o
 obj-$(CONFIG_PHY_MICREL_KSZ90X1) += micrel_ksz90x1.o
 obj-$(CONFIG_PHY_MESON_GXL) += meson-gxl.o
+obj-$(CONFIG_PHY_MOTORCOMM) += motorcomm.o
 obj-$(CONFIG_PHY_NATSEMI) += natsemi.o
 obj-$(CONFIG_PHY_NXP_C45_TJA11XX) += nxp-c45-tja11xx.o
 obj-$(CONFIG_PHY_NXP_TJA11XX) += nxp-tja11xx.o
diff --git a/drivers/net/phy/motorcomm.c b/drivers/net/phy/motorcomm.c
new file mode 100644
index 0000000..e822fd7
--- /dev/null
+++ b/drivers/net/phy/motorcomm.c
@@ -0,0 +1,437 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Motorcomm 8531 PHY driver.
+ *
+ * Copyright (C) 2023 StarFive Technology Co., Ltd.
+ */
+
+#include <config.h>
+#include <common.h>
+#include <malloc.h>
+#include <phy.h>
+#include <linux/bitfield.h>
+
+#define PHY_ID_YT8531				0x4f51e91b
+#define PHY_ID_MASK				GENMASK(31, 0)
+
+/* Extended Register's Address Offset Register */
+#define YTPHY_PAGE_SELECT			0x1E
+
+/* Extended Register's Data Register */
+#define YTPHY_PAGE_DATA			0x1F
+
+#define YTPHY_SYNCE_CFG_REG			0xA012
+
+#define YTPHY_DTS_OUTPUT_CLK_DIS		0
+#define YTPHY_DTS_OUTPUT_CLK_25M		25000000
+#define YTPHY_DTS_OUTPUT_CLK_125M		125000000
+
+#define YT8531_SCR_SYNCE_ENABLE		BIT(6)
+/* 1b0 output 25m clock   *default*
+ * 1b1 output 125m clock
+ */
+#define YT8531_SCR_CLK_FRE_SEL_125M		BIT(4)
+#define YT8531_SCR_CLK_SRC_MASK		GENMASK(3, 1)
+#define YT8531_SCR_CLK_SRC_PLL_125M		0
+#define YT8531_SCR_CLK_SRC_UTP_RX		1
+#define YT8531_SCR_CLK_SRC_SDS_RX		2
+#define YT8531_SCR_CLK_SRC_CLOCK_FROM_DIGITAL	3
+#define YT8531_SCR_CLK_SRC_REF_25M		4
+#define YT8531_SCR_CLK_SRC_SSC_25M		5
+
+/* 1b0 use original tx_clk_rgmii  *default*
+ * 1b1 use inverted tx_clk_rgmii.
+ */
+#define YT8531_RC1R_TX_CLK_SEL_INVERTED	BIT(14)
+#define YT8531_RC1R_RX_DELAY_MASK		GENMASK(13, 10)
+#define YT8531_RC1R_FE_TX_DELAY_MASK		GENMASK(7, 4)
+#define YT8531_RC1R_GE_TX_DELAY_MASK		GENMASK(3, 0)
+#define YT8531_RC1R_RGMII_0_000_NS		0
+#define YT8531_RC1R_RGMII_0_150_NS		1
+#define YT8531_RC1R_RGMII_0_300_NS		2
+#define YT8531_RC1R_RGMII_0_450_NS		3
+#define YT8531_RC1R_RGMII_0_600_NS		4
+#define YT8531_RC1R_RGMII_0_750_NS		5
+#define YT8531_RC1R_RGMII_0_900_NS		6
+#define YT8531_RC1R_RGMII_1_050_NS		7
+#define YT8531_RC1R_RGMII_1_200_NS		8
+#define YT8531_RC1R_RGMII_1_350_NS		9
+#define YT8531_RC1R_RGMII_1_500_NS		10
+#define YT8531_RC1R_RGMII_1_650_NS		11
+#define YT8531_RC1R_RGMII_1_800_NS		12
+#define YT8531_RC1R_RGMII_1_950_NS		13
+#define YT8531_RC1R_RGMII_2_100_NS		14
+#define YT8531_RC1R_RGMII_2_250_NS		15
+
+/* Phy gmii clock gating Register */
+#define YT8531_CLOCK_GATING_REG		0xC
+#define YT8531_CGR_RX_CLK_EN			BIT(12)
+
+/* Specific Status Register */
+#define YTPHY_SPECIFIC_STATUS_REG		0x11
+#define YTPHY_DUPLEX_MASK			BIT(13)
+#define YTPHY_DUPLEX_SHIFT			13
+#define YTPHY_SPEED_MODE_MASK			GENMASK(15, 14)
+#define YTPHY_SPEED_MODE_SHIFT			14
+
+#define YT8531_EXTREG_SLEEP_CONTROL1_REG	0x27
+#define YT8531_ESC1R_SLEEP_SW			BIT(15)
+#define YT8531_ESC1R_PLLON_SLP			BIT(14)
+
+#define YT8531_RGMII_CONFIG1_REG		0xA003
+
+#define YT8531_CHIP_CONFIG_REG			0xA001
+#define YT8531_CCR_SW_RST			BIT(15)
+/* 1b0 disable 1.9ns rxc clock delay  *default*
+ * 1b1 enable 1.9ns rxc clock delay
+ */
+#define YT8531_CCR_RXC_DLY_EN			BIT(8)
+#define YT8531_CCR_RXC_DLY_1_900_NS		1900
+
+/* bits in struct ytphy_plat_priv->flag */
+#define TX_CLK_ADJ_ENABLED			BIT(0)
+#define AUTO_SLEEP_DISABLED			BIT(1)
+#define KEEP_PLL_ENABLED			BIT(2)
+#define TX_CLK_10_INVERTED			BIT(3)
+#define TX_CLK_100_INVERTED			BIT(4)
+#define TX_CLK_1000_INVERTED			BIT(5)
+
+struct ytphy_plat_priv {
+	u32 rx_delay_ps;
+	u32 tx_delay_ps;
+	u32 clk_out_frequency;
+	u32 flag;
+};
+
+/**
+ * struct ytphy_cfg_reg_map - map a config value to a register value
+ * @cfg: value in device configuration
+ * @reg: value in the register
+ */
+struct ytphy_cfg_reg_map {
+	u32 cfg;
+	u32 reg;
+};
+
+static const struct ytphy_cfg_reg_map ytphy_rgmii_delays[] = {
+	/* for tx delay / rx delay with YT8531_CCR_RXC_DLY_EN is not set. */
+	{ 0,	YT8531_RC1R_RGMII_0_000_NS },
+	{ 150,	YT8531_RC1R_RGMII_0_150_NS },
+	{ 300,	YT8531_RC1R_RGMII_0_300_NS },
+	{ 450,	YT8531_RC1R_RGMII_0_450_NS },
+	{ 600,	YT8531_RC1R_RGMII_0_600_NS },
+	{ 750,	YT8531_RC1R_RGMII_0_750_NS },
+	{ 900,	YT8531_RC1R_RGMII_0_900_NS },
+	{ 1050,	YT8531_RC1R_RGMII_1_050_NS },
+	{ 1200,	YT8531_RC1R_RGMII_1_200_NS },
+	{ 1350,	YT8531_RC1R_RGMII_1_350_NS },
+	{ 1500,	YT8531_RC1R_RGMII_1_500_NS },
+	{ 1650,	YT8531_RC1R_RGMII_1_650_NS },
+	{ 1800,	YT8531_RC1R_RGMII_1_800_NS },
+	{ 1950,	YT8531_RC1R_RGMII_1_950_NS },	/* default tx/rx delay */
+	{ 2100,	YT8531_RC1R_RGMII_2_100_NS },
+	{ 2250,	YT8531_RC1R_RGMII_2_250_NS },
+
+	/* only for rx delay with YT8531_CCR_RXC_DLY_EN is set. */
+	{ 0    + YT8531_CCR_RXC_DLY_1_900_NS,	YT8531_RC1R_RGMII_0_000_NS },
+	{ 150  + YT8531_CCR_RXC_DLY_1_900_NS,	YT8531_RC1R_RGMII_0_150_NS },
+	{ 300  + YT8531_CCR_RXC_DLY_1_900_NS,	YT8531_RC1R_RGMII_0_300_NS },
+	{ 450  + YT8531_CCR_RXC_DLY_1_900_NS,	YT8531_RC1R_RGMII_0_450_NS },
+	{ 600  + YT8531_CCR_RXC_DLY_1_900_NS,	YT8531_RC1R_RGMII_0_600_NS },
+	{ 750  + YT8531_CCR_RXC_DLY_1_900_NS,	YT8531_RC1R_RGMII_0_750_NS },
+	{ 900  + YT8531_CCR_RXC_DLY_1_900_NS,	YT8531_RC1R_RGMII_0_900_NS },
+	{ 1050 + YT8531_CCR_RXC_DLY_1_900_NS,	YT8531_RC1R_RGMII_1_050_NS },
+	{ 1200 + YT8531_CCR_RXC_DLY_1_900_NS,	YT8531_RC1R_RGMII_1_200_NS },
+	{ 1350 + YT8531_CCR_RXC_DLY_1_900_NS,	YT8531_RC1R_RGMII_1_350_NS },
+	{ 1500 + YT8531_CCR_RXC_DLY_1_900_NS,	YT8531_RC1R_RGMII_1_500_NS },
+	{ 1650 + YT8531_CCR_RXC_DLY_1_900_NS,	YT8531_RC1R_RGMII_1_650_NS },
+	{ 1800 + YT8531_CCR_RXC_DLY_1_900_NS,	YT8531_RC1R_RGMII_1_800_NS },
+	{ 1950 + YT8531_CCR_RXC_DLY_1_900_NS,	YT8531_RC1R_RGMII_1_950_NS },
+	{ 2100 + YT8531_CCR_RXC_DLY_1_900_NS,	YT8531_RC1R_RGMII_2_100_NS },
+	{ 2250 + YT8531_CCR_RXC_DLY_1_900_NS,	YT8531_RC1R_RGMII_2_250_NS }
+};
+
+static u32 ytphy_get_delay_reg_value(struct phy_device *phydev,
+				     u32 val,
+				     u16 *rxc_dly_en)
+{
+	int tb_size = ARRAY_SIZE(ytphy_rgmii_delays);
+	int tb_size_half = tb_size / 2;
+	int i;
+
+	/* when rxc_dly_en is NULL, it is get the delay for tx, only half of
+	 * tb_size is valid.
+	 */
+	if (!rxc_dly_en)
+		tb_size = tb_size_half;
+
+	for (i = 0; i < tb_size; i++) {
+		if (ytphy_rgmii_delays[i].cfg == val) {
+			if (rxc_dly_en && i < tb_size_half)
+				*rxc_dly_en = 0;
+			return ytphy_rgmii_delays[i].reg;
+		}
+	}
+
+	pr_warn("Unsupported value %d, using default (%u)\n",
+		val, YT8531_RC1R_RGMII_1_950_NS);
+
+	/* when rxc_dly_en is not NULL, it is get the delay for rx.
+	 * The rx default in dts and ytphy_rgmii_clk_delay_config is 1950 ps,
+	 * so YT8531_CCR_RXC_DLY_EN should not be set.
+	 */
+	if (rxc_dly_en)
+		*rxc_dly_en = 0;
+
+	return YT8531_RC1R_RGMII_1_950_NS;
+}
+
+static int ytphy_modify_ext(struct phy_device *phydev, u16 regnum, u16 mask,
+			    u16 set)
+{
+	int ret;
+
+	ret = phy_write(phydev, MDIO_DEVAD_NONE, YTPHY_PAGE_SELECT, regnum);
+	if (ret < 0)
+		return ret;
+
+	return phy_modify(phydev, MDIO_DEVAD_NONE, YTPHY_PAGE_DATA, mask, set);
+}
+
+static int ytphy_rgmii_clk_delay_config(struct phy_device *phydev)
+{
+	struct ytphy_plat_priv	*priv = phydev->priv;
+	u16 rxc_dly_en = YT8531_CCR_RXC_DLY_EN;
+	u32 rx_reg, tx_reg;
+	u16 mask, val = 0;
+	int ret;
+
+	rx_reg = ytphy_get_delay_reg_value(phydev, priv->rx_delay_ps,
+					   &rxc_dly_en);
+	tx_reg = ytphy_get_delay_reg_value(phydev, priv->tx_delay_ps,
+					   NULL);
+
+	switch (phydev->interface) {
+	case PHY_INTERFACE_MODE_RGMII:
+		rxc_dly_en = 0;
+		break;
+	case PHY_INTERFACE_MODE_RGMII_RXID:
+		val |= FIELD_PREP(YT8531_RC1R_RX_DELAY_MASK, rx_reg);
+		break;
+	case PHY_INTERFACE_MODE_RGMII_TXID:
+		rxc_dly_en = 0;
+		val |= FIELD_PREP(YT8531_RC1R_GE_TX_DELAY_MASK, tx_reg);
+		break;
+	case PHY_INTERFACE_MODE_RGMII_ID:
+		val |= FIELD_PREP(YT8531_RC1R_RX_DELAY_MASK, rx_reg) |
+		       FIELD_PREP(YT8531_RC1R_GE_TX_DELAY_MASK, tx_reg);
+		break;
+	default: /* do not support other modes */
+		return -EOPNOTSUPP;
+	}
+
+	ret = ytphy_modify_ext(phydev, YT8531_CHIP_CONFIG_REG,
+			       YT8531_CCR_RXC_DLY_EN, rxc_dly_en);
+	if (ret < 0)
+		return ret;
+
+	/* Generally, it is not necessary to adjust YT8531_RC1R_FE_TX_DELAY */
+	mask = YT8531_RC1R_RX_DELAY_MASK | YT8531_RC1R_GE_TX_DELAY_MASK;
+	return ytphy_modify_ext(phydev, YT8531_RGMII_CONFIG1_REG, mask, val);
+}
+
+static int yt8531_parse_status(struct phy_device *phydev)
+{
+	int val;
+	int speed, speed_mode;
+
+	val = phy_read(phydev, MDIO_DEVAD_NONE, YTPHY_SPECIFIC_STATUS_REG);
+	if (val < 0)
+		return val;
+
+	speed_mode = (val & YTPHY_SPEED_MODE_MASK) >> YTPHY_SPEED_MODE_SHIFT;
+	switch (speed_mode) {
+	case 2:
+		speed = SPEED_1000;
+		break;
+	case 1:
+		speed = SPEED_100;
+		break;
+	default:
+		speed = SPEED_10;
+		break;
+	}
+
+	phydev->speed = speed;
+	phydev->duplex = (val & YTPHY_DUPLEX_MASK) >> YTPHY_DUPLEX_SHIFT;
+
+	return 0;
+}
+
+static int yt8531_startup(struct phy_device *phydev)
+{
+	struct ytphy_plat_priv	*priv = phydev->priv;
+	u16 val = 0;
+	int ret;
+
+	ret = genphy_update_link(phydev);
+	if (ret)
+		return ret;
+
+	ret = yt8531_parse_status(phydev);
+	if (ret)
+		return ret;
+
+	if (phydev->speed < 0)
+		return -EINVAL;
+
+	if (!(priv->flag & TX_CLK_ADJ_ENABLED))
+		return 0;
+
+	switch (phydev->speed) {
+	case SPEED_1000:
+		if (priv->flag & TX_CLK_1000_INVERTED)
+			val = YT8531_RC1R_TX_CLK_SEL_INVERTED;
+		break;
+	case SPEED_100:
+		if (priv->flag & TX_CLK_100_INVERTED)
+			val = YT8531_RC1R_TX_CLK_SEL_INVERTED;
+		break;
+	case SPEED_10:
+		if (priv->flag & TX_CLK_10_INVERTED)
+			val = YT8531_RC1R_TX_CLK_SEL_INVERTED;
+		break;
+	default:
+		printf("UNKNOWN SPEED\n");
+		return -EINVAL;
+	}
+
+	ret = ytphy_modify_ext(phydev, YT8531_RGMII_CONFIG1_REG,
+			       YT8531_RC1R_TX_CLK_SEL_INVERTED, val);
+	if (ret < 0)
+		pr_warn("Modify TX_CLK_SEL err:%d\n", ret);
+
+	return 0;
+}
+
+static void ytphy_dt_parse(struct phy_device *phydev)
+{
+	struct ytphy_plat_priv	*priv = phydev->priv;
+
+	priv->clk_out_frequency = ofnode_read_u32_default(phydev->node,
+							  "motorcomm,clk-out-frequency-hz",
+							  YTPHY_DTS_OUTPUT_CLK_DIS);
+	priv->rx_delay_ps = ofnode_read_u32_default(phydev->node,
+						    "rx-internal-delay-ps",
+						    YT8531_RC1R_RGMII_1_950_NS);
+	priv->tx_delay_ps = ofnode_read_u32_default(phydev->node,
+						    "tx-internal-delay-ps",
+						    YT8531_RC1R_RGMII_1_950_NS);
+
+	if (ofnode_read_bool(phydev->node, "motorcomm,auto-sleep-disabled"))
+		priv->flag |= AUTO_SLEEP_DISABLED;
+
+	if (ofnode_read_bool(phydev->node, "motorcomm,keep-pll-enabled"))
+		priv->flag |= KEEP_PLL_ENABLED;
+
+	if (ofnode_read_bool(phydev->node, "motorcomm,tx-clk-adj-enabled"))
+		priv->flag |= TX_CLK_ADJ_ENABLED;
+
+	if (ofnode_read_bool(phydev->node, "motorcomm,tx-clk-10-inverted"))
+		priv->flag |= TX_CLK_10_INVERTED;
+
+	if (ofnode_read_bool(phydev->node, "motorcomm,tx-clk-100-inverted"))
+		priv->flag |= TX_CLK_100_INVERTED;
+
+	if (ofnode_read_bool(phydev->node, "motorcomm,tx-clk-1000-inverted"))
+		priv->flag |= TX_CLK_1000_INVERTED;
+}
+
+static int yt8531_config(struct phy_device *phydev)
+{
+	struct ytphy_plat_priv	*priv = phydev->priv;
+	u16 mask, val;
+	int ret;
+
+	ret = genphy_config_aneg(phydev);
+	if (ret < 0)
+		return ret;
+
+	ytphy_dt_parse(phydev);
+	switch (priv->clk_out_frequency) {
+	case YTPHY_DTS_OUTPUT_CLK_DIS:
+		mask = YT8531_SCR_SYNCE_ENABLE;
+		val = 0;
+		break;
+	case YTPHY_DTS_OUTPUT_CLK_25M:
+		mask = YT8531_SCR_SYNCE_ENABLE | YT8531_SCR_CLK_SRC_MASK |
+			   YT8531_SCR_CLK_FRE_SEL_125M;
+		val = YT8531_SCR_SYNCE_ENABLE |
+			  FIELD_PREP(YT8531_SCR_CLK_SRC_MASK,
+				     YT8531_SCR_CLK_SRC_REF_25M);
+		break;
+	case YTPHY_DTS_OUTPUT_CLK_125M:
+		mask = YT8531_SCR_SYNCE_ENABLE | YT8531_SCR_CLK_SRC_MASK |
+			   YT8531_SCR_CLK_FRE_SEL_125M;
+		val = YT8531_SCR_SYNCE_ENABLE | YT8531_SCR_CLK_FRE_SEL_125M |
+			  FIELD_PREP(YT8531_SCR_CLK_SRC_MASK,
+				     YT8531_SCR_CLK_SRC_PLL_125M);
+		break;
+	default:
+		pr_warn("Freq err:%u\n", priv->clk_out_frequency);
+		return -EINVAL;
+	}
+
+	ret = ytphy_modify_ext(phydev, YTPHY_SYNCE_CFG_REG, mask,
+			       val);
+	if (ret < 0)
+		return ret;
+
+	ret = ytphy_rgmii_clk_delay_config(phydev);
+	if (ret < 0)
+		return ret;
+
+	if (priv->flag & AUTO_SLEEP_DISABLED) {
+		/* disable auto sleep */
+		ret = ytphy_modify_ext(phydev,
+				       YT8531_EXTREG_SLEEP_CONTROL1_REG,
+				       YT8531_ESC1R_SLEEP_SW, 0);
+		if (ret < 0)
+			return ret;
+	}
+
+	if (priv->flag & KEEP_PLL_ENABLED) {
+		/* enable RXC clock when no wire plug */
+		ret = ytphy_modify_ext(phydev,
+				       YT8531_CLOCK_GATING_REG,
+				       YT8531_CGR_RX_CLK_EN, 0);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int yt8531_probe(struct phy_device *phydev)
+{
+	struct ytphy_plat_priv	*priv;
+
+	priv = calloc(1, sizeof(struct ytphy_plat_priv));
+	if (!priv)
+		return -ENOMEM;
+
+	phydev->priv = priv;
+
+	return 0;
+}
+
+U_BOOT_PHY_DRIVER(motorcomm8531) = {
+	.name          = "YT8531 Gigabit Ethernet",
+	.uid           = PHY_ID_YT8531,
+	.mask          = PHY_ID_MASK,
+	.features      = PHY_GBIT_FEATURES,
+	.probe	       = &yt8531_probe,
+	.config        = &yt8531_config,
+	.startup       = &yt8531_startup,
+	.shutdown      = &genphy_shutdown,
+};
diff --git a/drivers/ram/starfive/starfive_ddr.c b/drivers/ram/starfive/starfive_ddr.c
index 553f2ce..a0a3d6b 100644
--- a/drivers/ram/starfive/starfive_ddr.c
+++ b/drivers/ram/starfive/starfive_ddr.c
@@ -72,8 +72,6 @@
 	u64 rate;
 	int ret;
 
-	/* Read memory base and size from DT */
-	fdtdec_setup_mem_size_base();
 	priv->info.base = gd->ram_base;
 	priv->info.size = gd->ram_size;
 
diff --git a/drivers/timer/Makefile b/drivers/timer/Makefile
index cdc20f5..1ca7480 100644
--- a/drivers/timer/Makefile
+++ b/drivers/timer/Makefile
@@ -25,7 +25,7 @@
 obj-$(CONFIG_ROCKCHIP_TIMER) += rockchip_timer.o
 obj-$(CONFIG_SANDBOX_TIMER)	+= sandbox_timer.o
 obj-$(CONFIG_SP804_TIMER)	+= sp804_timer.o
-obj-$(CONFIG_$(SPL_)SIFIVE_CLINT) += sifive_clint_timer.o
+obj-$(CONFIG_$(SPL_)RISCV_ACLINT) += riscv_aclint_timer.o
 obj-$(CONFIG_ARM_GLOBAL_TIMER)	+= arm_global_timer.o
 obj-$(CONFIG_STM32_TIMER)	+= stm32_timer.o
 obj-$(CONFIG_TEGRA_TIMER)	+= tegra-timer.o
diff --git a/drivers/timer/riscv_aclint_timer.c b/drivers/timer/riscv_aclint_timer.c
new file mode 100644
index 0000000..e29d527
--- /dev/null
+++ b/drivers/timer/riscv_aclint_timer.c
@@ -0,0 +1,74 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2020, Sean Anderson <seanga2@gmail.com>
+ * Copyright (C) 2018, Bin Meng <bmeng.cn@gmail.com>
+ */
+
+#include <common.h>
+#include <clk.h>
+#include <dm.h>
+#include <timer.h>
+#include <asm/io.h>
+#include <dm/device-internal.h>
+#include <linux/err.h>
+
+#define CLINT_MTIME_OFFSET		0xbff8
+#define ACLINT_MTIME_OFFSET		0
+
+/* mtime register */
+#define MTIME_REG(base, offset)		((ulong)(base) + (offset))
+
+static u64 notrace riscv_aclint_timer_get_count(struct udevice *dev)
+{
+	return readq((void __iomem *)MTIME_REG(dev_get_priv(dev),
+					       dev_get_driver_data(dev)));
+}
+
+#if CONFIG_IS_ENABLED(RISCV_MMODE) && IS_ENABLED(CONFIG_TIMER_EARLY)
+/**
+ * timer_early_get_rate() - Get the timer rate before driver model
+ */
+unsigned long notrace timer_early_get_rate(void)
+{
+	return RISCV_MMODE_TIMER_FREQ;
+}
+
+/**
+ * timer_early_get_count() - Get the timer count before driver model
+ *
+ */
+u64 notrace timer_early_get_count(void)
+{
+	return readq((void __iomem *)MTIME_REG(RISCV_MMODE_TIMERBASE,
+					       RISCV_MMODE_TIMEROFF));
+}
+#endif
+
+static const struct timer_ops riscv_aclint_timer_ops = {
+	.get_count = riscv_aclint_timer_get_count,
+};
+
+static int riscv_aclint_timer_probe(struct udevice *dev)
+{
+	dev_set_priv(dev, dev_read_addr_ptr(dev));
+	if (!dev_get_priv(dev))
+		return -EINVAL;
+
+	return timer_timebase_fallback(dev);
+}
+
+static const struct udevice_id riscv_aclint_timer_ids[] = {
+	{ .compatible = "riscv,clint0", .data = CLINT_MTIME_OFFSET },
+	{ .compatible = "sifive,clint0", .data = CLINT_MTIME_OFFSET },
+	{ .compatible = "riscv,aclint-mtimer", .data = ACLINT_MTIME_OFFSET },
+	{ }
+};
+
+U_BOOT_DRIVER(riscv_aclint_timer) = {
+	.name		= "riscv_aclint_timer",
+	.id		= UCLASS_TIMER,
+	.of_match	= riscv_aclint_timer_ids,
+	.probe		= riscv_aclint_timer_probe,
+	.ops		= &riscv_aclint_timer_ops,
+	.flags		= DM_FLAG_PRE_RELOC,
+};
diff --git a/drivers/timer/sifive_clint_timer.c b/drivers/timer/sifive_clint_timer.c
deleted file mode 100644
index 939b99d..0000000
--- a/drivers/timer/sifive_clint_timer.c
+++ /dev/null
@@ -1,68 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * Copyright (C) 2020, Sean Anderson <seanga2@gmail.com>
- * Copyright (C) 2018, Bin Meng <bmeng.cn@gmail.com>
- */
-
-#include <common.h>
-#include <clk.h>
-#include <dm.h>
-#include <timer.h>
-#include <asm/io.h>
-#include <dm/device-internal.h>
-#include <linux/err.h>
-
-/* mtime register */
-#define MTIME_REG(base)			((ulong)(base) + 0xbff8)
-
-static u64 notrace sifive_clint_get_count(struct udevice *dev)
-{
-	return readq((void __iomem *)MTIME_REG(dev_get_priv(dev)));
-}
-
-#if CONFIG_IS_ENABLED(RISCV_MMODE) && IS_ENABLED(CONFIG_TIMER_EARLY)
-/**
- * timer_early_get_rate() - Get the timer rate before driver model
- */
-unsigned long notrace timer_early_get_rate(void)
-{
-	return RISCV_MMODE_TIMER_FREQ;
-}
-
-/**
- * timer_early_get_count() - Get the timer count before driver model
- *
- */
-u64 notrace timer_early_get_count(void)
-{
-	return readq((void __iomem *)MTIME_REG(RISCV_MMODE_TIMERBASE));
-}
-#endif
-
-static const struct timer_ops sifive_clint_ops = {
-	.get_count = sifive_clint_get_count,
-};
-
-static int sifive_clint_probe(struct udevice *dev)
-{
-	dev_set_priv(dev, dev_read_addr_ptr(dev));
-	if (!dev_get_priv(dev))
-		return -EINVAL;
-
-	return timer_timebase_fallback(dev);
-}
-
-static const struct udevice_id sifive_clint_ids[] = {
-	{ .compatible = "riscv,clint0" },
-	{ .compatible = "sifive,clint0" },
-	{ }
-};
-
-U_BOOT_DRIVER(sifive_clint) = {
-	.name		= "sifive_clint",
-	.id		= UCLASS_TIMER,
-	.of_match	= sifive_clint_ids,
-	.probe		= sifive_clint_probe,
-	.ops		= &sifive_clint_ops,
-	.flags		= DM_FLAG_PRE_RELOC,
-};
diff --git a/include/configs/qemu-riscv.h b/include/configs/qemu-riscv.h
index 20135f5..f6d326b 100644
--- a/include/configs/qemu-riscv.h
+++ b/include/configs/qemu-riscv.h
@@ -11,8 +11,8 @@
 #define CFG_SYS_SDRAM_BASE		0x80000000
 
 #define RISCV_MMODE_TIMERBASE		0x2000000
+#define RISCV_MMODE_TIMEROFF		0xbff8
 #define RISCV_MMODE_TIMER_FREQ		1000000
-
 #define RISCV_SMODE_TIMER_FREQ		1000000
 
 /* Environment options */
diff --git a/include/configs/sifive-unleashed.h b/include/configs/sifive-unleashed.h
index de3a0dc..f208f5e 100644
--- a/include/configs/sifive-unleashed.h
+++ b/include/configs/sifive-unleashed.h
@@ -14,8 +14,8 @@
 #define CFG_SYS_SDRAM_BASE		0x80000000
 
 #define RISCV_MMODE_TIMERBASE		0x2000000
+#define RISCV_MMODE_TIMEROFF		0xbff8
 #define RISCV_MMODE_TIMER_FREQ		1000000
-
 #define RISCV_SMODE_TIMER_FREQ		1000000
 
 /* Environment options */
diff --git a/include/configs/starfive-visionfive2.h b/include/configs/starfive-visionfive2.h
index 93dcc22..4ee02b8 100644
--- a/include/configs/starfive-visionfive2.h
+++ b/include/configs/starfive-visionfive2.h
@@ -9,6 +9,7 @@
 #define _STARFIVE_VISIONFIVE2_H
 
 #define RISCV_MMODE_TIMERBASE		0x2000000
+#define RISCV_MMODE_TIMEROFF		0xbff8
 #define RISCV_MMODE_TIMER_FREQ		4000000
 #define RISCV_SMODE_TIMER_FREQ		4000000
 
diff --git a/include/configs/th1520_lpi4a.h b/include/configs/th1520_lpi4a.h
new file mode 100644
index 0000000..87496a5
--- /dev/null
+++ b/include/configs/th1520_lpi4a.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (c) 2023 Yixun Lan <dlan@gentoo.org>
+ *
+ */
+
+#ifndef __TH1520_LPI4A_H
+#define __TH1520_LPI4A_H
+
+#include <linux/sizes.h>
+
+#define CFG_SYS_SDRAM_BASE		0x00000000
+
+#define UART_BASE	0xffe7014000
+#define UART_REG_WIDTH  32
+
+/* Environment options */
+
+#define CFG_EXTRA_ENV_SETTINGS \
+	"PS1=[LPi4A]# \0"
+
+#endif /* __TH1520_LPI4A_H */
diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
index c5835e6..a22e476 100644
--- a/lib/efi_loader/Kconfig
+++ b/lib/efi_loader/Kconfig
@@ -96,7 +96,8 @@
 
 config EFI_VAR_BUF_SIZE
 	int "Memory size of the UEFI variable store"
-	default 16384
+	default 16384 if EFI_MM_COMM_TEE
+	default 65536
 	range 4096 2147483647
 	help
 	  This defines the size in bytes of the memory area reserved for keeping
@@ -106,7 +107,7 @@
 	  match the value of PcdFlashNvStorageVariableSize used to compile the
 	  StandAloneMM module.
 
-	  Minimum 4096, default 16384.
+	  Minimum 4096, default 65536, or 16384 when using StandAloneMM.
 
 config EFI_GET_TIME
 	bool "GetTime() runtime service"
diff --git a/lib/efi_loader/efi_var_mem.c b/lib/efi_loader/efi_var_mem.c
index d6b65ae..5fa7dcb 100644
--- a/lib/efi_loader/efi_var_mem.c
+++ b/lib/efi_loader/efi_var_mem.c
@@ -177,6 +177,10 @@
 
 u64 __efi_runtime efi_var_mem_free(void)
 {
+	if (efi_var_buf->length + sizeof(struct efi_var_entry) >=
+	    EFI_VAR_BUF_SIZE)
+		return 0;
+
 	return EFI_VAR_BUF_SIZE - efi_var_buf->length -
 	       sizeof(struct efi_var_entry);
 }
diff --git a/tools/eficapsule.h b/tools/eficapsule.h
index 753fb73..2099a2e 100644
--- a/tools/eficapsule.h
+++ b/tools/eficapsule.h
@@ -63,7 +63,7 @@
 	uint32_t version;
 	uint16_t embedded_driver_count;
 	uint16_t payload_item_count;
-	uint32_t item_offset_list[];
+	uint64_t item_offset_list[];
 } __packed;
 
 /* image_capsule_support */