[PATCH 1/1] board: mt5824: Add initial support for Metanoia MT5824 EVB

Jun Chang jun.chang at metanoia-comm.com
Tue Dec 30 04:03:28 CET 2025


- Add RISC-V target for MT5824
- Add board defconfig and DTS for MT5824 EVB
- Add SPL and U-Boot board support for MT5824 EVB
- Add DDR init for MT5824 EVB

Signed-off-by: Jun Chang <jun.chang at metanoia-comm.com>
---
 arch/riscv/Kconfig                    |   4 +
 arch/riscv/dts/mt5824-evb.dts         | 232 +++++++++++++++++
 board/metanoia/mt5824/Kconfig         |  49 ++++
 board/metanoia/mt5824/MAINTAINERS     |   7 +
 board/metanoia/mt5824/Makefile        |   7 +
 board/metanoia/mt5824/lpddr4.h        |  91 +++++++
 board/metanoia/mt5824/lpddr4_helper.c | 352 ++++++++++++++++++++++++++
 board/metanoia/mt5824/lpddr4_init.c   | 115 +++++++++
 board/metanoia/mt5824/mt5824.c        | 188 ++++++++++++++
 board/metanoia/mt5824/mt5824.h        |  21 ++
 board/metanoia/mt5824/mt5824_ddr.c    |  54 ++++
 board/metanoia/mt5824/mt5824_ddr.h    |  12 +
 board/metanoia/mt5824/mt5824_spl.c    | 141 +++++++++++
 configs/mt5824_evb_defconfig          |  61 +++++
 include/configs/mt5824.h              | 130 ++++++++++
 15 files changed, 1464 insertions(+)
 create mode 100644 arch/riscv/dts/mt5824-evb.dts
 create mode 100644 board/metanoia/mt5824/Kconfig
 create mode 100644 board/metanoia/mt5824/MAINTAINERS
 create mode 100644 board/metanoia/mt5824/Makefile
 create mode 100644 board/metanoia/mt5824/lpddr4.h
 create mode 100644 board/metanoia/mt5824/lpddr4_helper.c
 create mode 100644 board/metanoia/mt5824/lpddr4_init.c
 create mode 100644 board/metanoia/mt5824/mt5824.c
 create mode 100644 board/metanoia/mt5824/mt5824.h
 create mode 100644 board/metanoia/mt5824/mt5824_ddr.c
 create mode 100644 board/metanoia/mt5824/mt5824_ddr.h
 create mode 100644 board/metanoia/mt5824/mt5824_spl.c
 create mode 100644 configs/mt5824_evb_defconfig
 create mode 100644 include/configs/mt5824.h

diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
index 265b5320777..8ff60179f4f 100644
--- a/arch/riscv/Kconfig
+++ b/arch/riscv/Kconfig
@@ -23,6 +23,9 @@ config TARGET_K230_CANMV
 config TARGET_LICHEERV_NANO
 	bool "Support LicheeRV Nano Board"
 
+config TARGET_METANOIA_MT5824
+	bool "Support Metanoia MT5824 Chipset"
+
 config TARGET_MICROCHIP_GENERIC
 	bool "Support Microchip PolarFire-SoC Boards"
 
@@ -108,6 +111,7 @@ source "board/andestech/voyager/Kconfig"
 source "board/aspeed/ibex_ast2700/Kconfig"
 source "board/canaan/k230_canmv/Kconfig"
 source "board/emulation/qemu-riscv/Kconfig"
+source "board/metanoia/mt5824/Kconfig"
 source "board/microchip/mpfs_generic/Kconfig"
 source "board/openpiton/riscv64/Kconfig"
 source "board/sifive/unleashed/Kconfig"
diff --git a/arch/riscv/dts/mt5824-evb.dts b/arch/riscv/dts/mt5824-evb.dts
new file mode 100644
index 00000000000..fd58055c6e8
--- /dev/null
+++ b/arch/riscv/dts/mt5824-evb.dts
@@ -0,0 +1,232 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+
+/dts-v1/;
+
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/interrupt-controller/irq.h>
+#include "binman.dtsi"
+
+/ {
+	#address-cells = <0x02>;
+	#size-cells = <0x02>;
+	compatible = "metanoia,mt5824";
+	model = "metanoia,mt5824-cobra-evb";
+
+	aliases {
+		uart0 = "/soc/serial at 100e0000";
+	};
+
+	chosen {
+		bootargs = "console=ttyS0,115200n8 debug loglevel=7 earlycon=sbi";
+		stdout-path = "uart0:115200n8";
+	};
+
+	cpus: cpus {
+		#address-cells = <0x01>;
+		#size-cells = <0x00>;
+		timebase-frequency = <20000000>;
+
+		cpu0: cpu at 0 {
+			device_type = "cpu";
+			reg = <0x00>;
+			status = "okay";
+			compatible = "riscv", "andestech,ax45mp";
+			riscv,isa-base = "rv64i";
+			riscv,isa-extensions = "i", "m", "a", "f", "d", "c",
+					       "zicntr", "zicsr", "zifencei",
+					       "zihpm", "xandespmu";
+			mmu-type = "riscv,sv39";
+			clock-frequency = <900000000>;
+			i-cache-size = <0x8000>;
+			i-cache-sets = <0x100>;
+			i-cache-line-size = <0x40>;
+			i-cache-block-size = <0x40>;
+			d-cache-size = <0x8000>;
+			d-cache-sets = <0x80>;
+			d-cache-line-size = <0x40>;
+			d-cache-block-size = <0x40>;
+			next-level-cache = <&l2c>;
+
+			cpu0intc: interrupt-controller {
+				#interrupt-cells = <0x01>;
+				interrupt-controller;
+				compatible = "andestech,cpu-intc";
+			};
+		};
+
+		cpu1: cpu at 1 {
+			device_type = "cpu";
+			reg = <0x01>;
+			status = "okay";
+			compatible = "riscv", "andestech,ax45mp";
+			riscv,isa-base = "rv64i";
+			riscv,isa-extensions = "i", "m", "a", "f", "d", "c",
+					       "zicntr", "zicsr", "zifencei",
+					       "zihpm", "xandespmu";
+			mmu-type = "riscv,sv39";
+			clock-frequency = <900000000>;
+			i-cache-size = <0x8000>;
+			i-cache-sets = <0x100>;
+			i-cache-line-size = <0x40>;
+			i-cache-block-size = <0x40>;
+			d-cache-size = <0x8000>;
+			d-cache-sets = <0x80>;
+			d-cache-line-size = <0x40>;
+			d-cache-block-size = <0x40>;
+			next-level-cache = <&l2c>;
+
+			cpu1intc: interrupt-controller {
+				#interrupt-cells = <0x01>;
+				interrupt-controller;
+				compatible = "andestech,cpu-intc";
+			};
+		};
+	};
+
+	l2c: l2-cache at 0a000000 {
+		compatible = "cache", "andestech,ax45mp-cache";
+		cache-level = <0x02>;
+		cache-size = <0x80000>;
+		reg = <0x00 0x0a000000 0x00 0x40000>;
+		interrupts = <1 IRQ_TYPE_LEVEL_HIGH>;
+		interrupt-parent = <&plic>;
+		andes,inst-prefetch = <0x03>;
+		andes,data-prefetch = <0x03>;
+		andes,tag-ram-ctl = <0x00 0x00>;
+		andes,data-ram-ctl = <0x00 0x00>;
+	};
+
+	LPDDR4: memory at 0 {
+		device_type = "memory";
+		reg = <0x0 0x0000000080000000 0x00000000 0x80000000>, /* 2GB */
+			<0x1 0x0000000000000000 0x00000000 0x80000000>; /* 2GB */
+	};
+
+	sb_3v3: regulator-sb3v3 {
+		compatible = "regulator-fixed";
+		regulator-name = "3v3_vbus";
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+		regulator-boot-on;
+		regulator-always-on;
+	};
+
+	soc: soc {
+		#address-cells = <0x02>;
+		#size-cells = <0x02>;
+		compatible = "simple-bus";
+		ranges;
+
+		/* ArchDef Figure 53 - Clock Frequency inside SOC */
+		ext_clk: ext-clk {
+			compatible = "metanoia,mt28xx-ext-clk", "fixed-clock";
+			#clock-cells = <0>;
+			clock-frequency = <20000000>;
+			clock-output-names = "ext-clk";
+			/* mainly optional clock for wdt, plmt */
+		};
+
+		pclk: platform-clock {
+			compatible = "metanoia,mt28xx-pclk", "fixed-clock";
+			#clock-cells = <0>;
+			clock-frequency = <900000000>;
+			clock-output-names = "platform-clock";
+		};
+
+		pclk_div2: platform-clock-div2 {
+			compatible = "metanoia,mt28xx-pclk-div2", "fixed-clock";
+			#clock-cells = <0>;
+			clock-frequency = <450000000>;
+			clock-output-names = "platform-clock-div2";
+		};
+
+		pclk_div4: platform-clock-div4 {
+			compatible = "metanoia,mt28xx-pclk-div4", "fixed-clock";
+			#clock-cells = <0>;
+			clock-frequency = <225000000>;
+			clock-output-names = "platform-clock-div4";
+		};
+
+		pll_soc_out3: pll-soc-out3 {
+			compatible = "metanoia,mt28xx-out3-clk", "fixed-clock";
+			#clock-cells = <0>;
+			clock-frequency = <180000000>;
+			clock-output-names = "pll-soc-out3";
+		};
+
+		plic: interrupt-controller at c000000 {
+			compatible = "riscv,plic0", "andestech,nceplic100";
+			reg = <0x0 0x0c000000 0x0 0x400000>;
+			interrupts-extended =
+				<&cpu0intc 0x0b>,
+				<&cpu0intc 0x09>,
+				<&cpu1intc 0x0b>,
+				<&cpu1intc 0x09>;
+			interrupt-controller;
+			#address-cells = <0x02>;
+			#interrupt-cells = <0x02>;
+			riscv,ndev = <0x47>;
+		};
+
+		plicsw: interrupt-controller at c800000 {
+			compatible = "andestech,plicsw";
+			reg = <0x00 0x0c800000 0x00 0x400000>;
+			interrupts-extended =
+				<&cpu0intc IRQ_TYPE_EDGE_BOTH>,
+				<&cpu1intc IRQ_TYPE_EDGE_BOTH>;
+			interrupt-controller;
+			#address-cells = <0x02>;
+			#interrupt-cells = <0x02>;
+			riscv,ndev = <0x04>;
+		};
+
+		plmt0 at c400000 {
+			compatible = "andestech,plmt0";
+			reg = <0x0 0x0c400000 0x0 0x00400000>;
+			interrupts-extended =
+				<&cpu0intc 0x07>,
+				<&cpu1intc 0x07>;
+		};
+
+		sw_reset: hrst at 10000004 {
+			/* match swrstreq ctrl,
+			 * drived by opensbi's fdt_reset_metanoia.c.
+			 */
+			#reset-cells = <1>;
+			compatible = "metanoia,cobra-hrst";
+			reg = <0x0 0x10000004 0x0 0x08>;
+		};
+
+		riscv_console: serial at 100e0000 {
+			compatible = "metanoia,uart16550", "ns16550a";
+			reg = <0x00 0x100e0000 0x00 0x1000>;
+			interrupts = <17 IRQ_TYPE_LEVEL_HIGH>;
+			interrupt-parent = <&plic>;
+			clocks = <&pclk_div4>;
+			current-speed = <115200>;
+			reg-shift = <0x02>;
+			reg-offset = <0x20>;
+			reg-io-width = <0x04>;
+			no-loopback-test = <0x01>;
+		};
+
+		mmcsd: mmc at 4100000 {
+			compatible = "metanoia,dwc-mmc";
+			max-frequency = <200000000>; /* why not pll_soc_out3? */
+			clocks = <&pll_soc_out3>;
+			reg = <0x0 0x04100000 0x0 0x00100000>, /* SDHCI */
+			      <0x0 0x10010300 0x0 0x00000004>; /* DEV_DB_CFG_0 */
+			interrupts = <5 IRQ_TYPE_LEVEL_HIGH>;
+			interrupt-parent = <&plic>;
+			sdhci-caps-mask = <0x80000006 0x00000000>;
+			bus-width = <4>;
+		};
+
+		pmu {
+			compatible = "riscv,andes-pmu";
+			device_type = "pmu";
+		};
+
+	};
+
+};
diff --git a/board/metanoia/mt5824/Kconfig b/board/metanoia/mt5824/Kconfig
new file mode 100644
index 00000000000..3bd2f97beea
--- /dev/null
+++ b/board/metanoia/mt5824/Kconfig
@@ -0,0 +1,49 @@
+if TARGET_METANOIA_MT5824
+
+config SYS_CPU
+	default "andes"
+
+config SYS_BOARD
+	default "mt5824"
+
+config SYS_VENDOR
+	default "metanoia"
+
+config SYS_SOC
+	default "mt5824"
+
+config SYS_CONFIG_NAME
+	default "mt5824"
+
+config ENV_SIZE
+	default 0x2000 if ENV_IS_IN_SPI_FLASH
+
+config ENV_OFFSET
+	default 0x140000 if ENV_IS_IN_SPI_FLASH
+
+config SPL_TEXT_BASE
+	default 0x68000000
+
+config SPL_OPENSBI_LOAD_ADDR
+	default 0x80000000
+
+config SYS_FDT_BASE
+	hex
+	default 0x68010000 if OF_SEPARATE
+
+config NR_CPUS
+	default 2
+
+config BOARD_SPECIFIC_OPTIONS # dummy
+	def_bool y
+	select RISCV_ANDES
+	select SUPPORT_SPL
+	select BINMAN if SPL
+	imply SMP
+	imply SPL_RAM_SUPPORT
+	imply SPL_RAM_DEVICE
+	imply BOARD_EARLY_INIT_F
+	imply OF_HAS_PRIOR_STAGE
+	imply SPL_BOARD_INIT
+
+endif
diff --git a/board/metanoia/mt5824/MAINTAINERS b/board/metanoia/mt5824/MAINTAINERS
new file mode 100644
index 00000000000..a507da2d0af
--- /dev/null
+++ b/board/metanoia/mt5824/MAINTAINERS
@@ -0,0 +1,7 @@
+Metanoia MT5824 EVB
+M:	Jun Chang <jun.chang at metanoia-comm.com>
+S:	Maintained
+F:	board/metanoia/mt5824/
+F:	include/configs/mt5824.h
+F:	configs/mt5824_evb_defconfig
+F:	arch/riscv/dts/mt5824-evb.dts
diff --git a/board/metanoia/mt5824/Makefile b/board/metanoia/mt5824/Makefile
new file mode 100644
index 00000000000..e9bbc6157be
--- /dev/null
+++ b/board/metanoia/mt5824/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Copyright (C) 2025 Metanoia Communications Inc.,
+# Jun Chang, Metanoia Communications Inc. <jun.chang at metanoia-comm.com>
+
+obj-y	:= mt5824.o
+obj-$(CONFIG_SPL_BUILD)	+= mt5824_spl.o mt5824_ddr.o lpddr4_init.o lpddr4_helper.o
diff --git a/board/metanoia/mt5824/lpddr4.h b/board/metanoia/mt5824/lpddr4.h
new file mode 100644
index 00000000000..5de70c536ca
--- /dev/null
+++ b/board/metanoia/mt5824/lpddr4.h
@@ -0,0 +1,91 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2025 Metanoia Communications Inc.,
+ * Jun Chang, Metanoia Communications Inc. <jun.chang at metanoia-comm.com>
+ */
+
+#ifndef __COBRA_DDR_H
+#define __COBRA_DDR_H
+
+#include <asm/io.h>
+
+#define UMCTL2_REGS_BASE_ADDR 0x10060000
+
+#define reg8_write(addr, data) \
+	writeb((uint8_t)(data), (volatile void __iomem *)(addr))
+#define reg16_write(addr, data) \
+	writew((uint16_t)(data), (volatile void __iomem *)(addr))
+#define reg32_write(addr, data) \
+	writel((uint32_t)(data), (volatile void __iomem *)(addr))
+
+#define reg8_read(addr) readb((volatile void __iomem *)(addr))
+#define reg16_read(addr) readw((volatile void __iomem *)(addr))
+#define reg32_read(addr) readl((volatile void __iomem *)(addr))
+
+#define ddrphy_apb_wr(addr, data) reg16_write((2 * addr + 0x14000000), data)
+#define ddrphy_apb_rd(addr) reg16_read((2 * addr + 0x14000000))
+
+#define UctShadowRegs (0xd0000 + 0x4)
+#define DctWriteOnly (0xd0000 + 0x30)
+#define DctWriteProt (0xd0000 + 0x31)
+#define UctWriteOnlyShadow (0xd0000 + 0x32)
+#define UctDatWriteOnlyShadow (0xd0000 + 0x34)
+
+#define DDRC_STAT (UMCTL2_REGS_BASE_ADDR + 0x04)
+#define DDRC_PWRCTL (UMCTL2_REGS_BASE_ADDR + 0x30)
+#define DDRC_RFSHCTL3 (UMCTL2_REGS_BASE_ADDR + 0x60)
+#define DDRC_DFIMISC (UMCTL2_REGS_BASE_ADDR + 0x1b0)
+#define DDRC_DFISTAT (UMCTL2_REGS_BASE_ADDR + 0x1bc)
+#define DDRC_SWCTL (UMCTL2_REGS_BASE_ADDR + 0x320)
+#define DDRC_SWSTAT (UMCTL2_REGS_BASE_ADDR + 0x324)
+
+#define DDRFW_MAGIC 0x44445246 /* "DDRF" */
+
+struct ddrfw_comp{
+	u32 offset;
+	u32 size;
+	u32 crc32;
+	u32 reserved;
+};
+
+struct ddrfw_header{
+	u32 magic;
+	u32 version;
+	u32 header_size;
+	u32 total_size;
+	u32 flags;
+	u32 timestamp;
+	u32 reserved1;
+	u32 reserved2;
+	struct ddrfw_comp comps[7];
+	u32 header_crc32;
+	u32 reserved[27]; /* Padding to 256 bytes */
+};
+
+enum {
+	DDR_CTRL_CFG = 0,
+	DDR_PHY_CFG = 1,
+	DDR_PMU_1D_IMEM = 2,
+	DDR_PMU_1D_DMEM = 3,
+	DDR_PMU_2D_IMEM = 4,
+	DDR_PMU_2D_DMEM = 5,
+	DDR_PIE_IMAGE = 6,
+	DDR_COMP_MAX
+};
+
+int ddrphy_init(uintptr_t fw_base);
+
+/* DDR Firmware helper functions */
+int ddrfw_parse_fw_header(uintptr_t fw_base, struct ddrfw_header *header);
+void ddrfw_load_controller_config(uintptr_t fw_base, struct ddrfw_header *fw_header);
+void ddrfw_load_phy_cfg(uintptr_t fw_base, struct ddrfw_header *fw_header);
+int ddrfw_load_train_firmware(uintptr_t fw_base, struct ddrfw_header *fw_header);
+void ddrfw_load_pie_image(uintptr_t fw_base, struct ddrfw_header *fw_header);
+static inline int waitlp(unsigned int cnt)
+{
+	while (cnt--)
+		;
+	return cnt;
+}
+
+#endif /* __COBRA_DDR_H */
\ No newline at end of file
diff --git a/board/metanoia/mt5824/lpddr4_helper.c b/board/metanoia/mt5824/lpddr4_helper.c
new file mode 100644
index 00000000000..3bc447038dc
--- /dev/null
+++ b/board/metanoia/mt5824/lpddr4_helper.c
@@ -0,0 +1,352 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2025 Metanoia Communications Inc.,
+ * Jun Chang, Metanoia Communications Inc. <jun.chang at metanoia-comm.com>
+ */
+
+#include <config.h>
+#include <log.h>
+#include <asm/io.h>
+#include <u-boot/crc.h>
+
+#include "lpddr4.h"
+
+#define IMEM_OFFSET_ADDR 0x00050000
+#define DMEM_OFFSET_ADDR 0x00054000
+
+static u32 get_mail(int mode)
+{
+	u32 mail;
+
+	while ((ddrphy_apb_rd(UctShadowRegs) & 1)) {
+		if (mode == 0)
+			waitlp(100);
+	}
+
+	mail = ddrphy_apb_rd(UctWriteOnlyShadow);
+	if (mode == 1)
+		mail |= (ddrphy_apb_rd(UctDatWriteOnlyShadow) << 16);
+
+	ddrphy_apb_wr(DctWriteProt, 0);
+	while (ddrphy_apb_rd(UctShadowRegs) == 0) {
+		;
+	}
+	ddrphy_apb_wr(DctWriteProt, 1);
+
+	return mail;
+}
+
+static void decode_streaming_message(void)
+{
+	u32 string_index;
+	u32 i = 0;
+	u32 args[32];
+
+	string_index = get_mail(1);
+	while (i < (string_index & 0xffff)) {
+		args[i] = get_mail(1);
+		i++;
+	}
+
+	return;
+}
+
+static void decode_major_message(u32 mail)
+{
+	switch (mail) {
+	case 0x07:
+		printf("DDRPHY: Training has run successfully\n");
+		break;
+	case 0xff:
+		printf("DDRPHY: Training has failed\n");
+		break;
+	case 0x00:
+		debug("DDRPHY: End of initialization\n");
+		break;
+	case 0x01:
+		debug("DDRPHY: End of fine write leveling\n");
+		break;
+	case 0x02:
+		debug("DDRPHY: End of read enable training\n");
+		break;
+	case 0x03:
+		debug("DDRPHY: End of read delay center optimization\n");
+		break;
+	case 0x04:
+		debug("DDRPHY: End of write delay center optimization\n");
+		break;
+	case 0x05:
+		debug("DDRPHY: End of 2D read delay/voltage center optimization\n");
+		break;
+	case 0x06:
+		debug("DDRPHY: End of 2D write delay/voltage center optimization\n");
+		break;
+	case 0x09:
+		debug("DDRPHY: End of max read latency training\n");
+		break;
+	case 0x0a:
+		debug("DDRPHY: End of read dq deskew training\n");
+		break;
+	case 0x0b:
+		debug("DDRPHY: Reserved\n");
+		break;
+	case 0x0c:
+		debug("DDRPHY: End of all DB training (MREP/DWL/MRD/MWD complete)\n");
+		break;
+	case 0x0d:
+		debug("DDRPHY: End of CA training\n");
+		break;
+	case 0xfd:
+		debug("DDRPHY: End of MPR read delay center optimization\n");
+		break;
+	case 0xfe:
+		debug("DDRPHY: End of Write leveling coarse delay\n");
+		break;
+	case 0x08: /*printf("DDRPHY: Start streaming message mode\n");*/
+		decode_streaming_message();
+		break;
+	default:
+		printf("DDRPHY: Error, unknown major_message %x\n", mail);
+	}
+}
+
+static int ddrphy_phyinit_wait_done(void)
+{
+	u32 train_st = 0;
+
+	do {
+		train_st = get_mail(0);
+		decode_major_message(train_st);
+	} while (train_st != 0x7 && train_st != 0xff);
+
+	return (train_st == 0x7) ? 0 : -1;
+}
+
+static int verify_comp_crc(uintptr_t fw_base, u32 offset, u32 size,
+			   u32 expected_crc)
+{
+	u32 calculated_crc;
+	const void *data;
+
+	if (size == 0)
+		return 0; /* Empty component is valid */
+
+	data = (const void *)(fw_base + offset);
+	calculated_crc = crc32(0, data, size);
+
+	if (calculated_crc != expected_crc) {
+		printf("ERROR: CRC mismatch at offset 0x%x: calculated=0x%08x, expected=0x%08x\n",
+		       offset, calculated_crc, expected_crc);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int verify_comps(uintptr_t fw_base, struct ddrfw_header *header)
+{
+	int i;
+	int result = 0;
+
+	for (i = 0; i < DDR_COMP_MAX; i++) {
+		u32 offset = header->comps[i].offset;
+		u32 size = header->comps[i].size;
+		u32 expected_crc = header->comps[i].crc32;
+
+		if (size == 0) {
+			printf("Component #%d is empty\n", i);
+			result = -1;
+			continue;
+		}
+
+		if (verify_comp_crc(fw_base, offset, size, expected_crc) != 0) {
+			printf("ERROR: Component #%d CRC verification failed\n",
+			       i);
+			result = -1;
+		} else {
+			debug("Component #%d CRC verified successfully\n", i);
+		}
+	}
+
+	return result;
+}
+
+int ddrfw_parse_fw_header(uintptr_t fw_base, struct ddrfw_header *header)
+{
+	u32 calculated_crc;
+	const void *header_data = (const void *)fw_base;
+
+	memcpy(header, header_data, sizeof(struct ddrfw_header));
+
+	if (header->magic != DDRFW_MAGIC) {
+		printf("ERROR: Invalid firmware magic: 0x%08x (expected 0x%08x)\n",
+		       header->magic, DDRFW_MAGIC);
+		return -1;
+	}
+
+	calculated_crc = crc32(0, header_data, 144);
+	if (calculated_crc != header->header_crc32) {
+		printf("ERROR: Header CRC mismatch: calculated=0x%08x, expected=0x%08x\n",
+		       calculated_crc, header->header_crc32);
+		return -1;
+	}
+
+	if (verify_comps(fw_base, header) != 0) {
+		printf("ERROR: Component verification failed\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+void ddrfw_load_controller_config(uintptr_t fw_base, struct ddrfw_header *fw_header)
+{
+	u32 offset = fw_header->comps[DDR_CTRL_CFG].offset;
+	u32 size = fw_header->comps[DDR_CTRL_CFG].size;
+	u32 num_registers;
+	const u32 *cfg_data;
+	int i;
+
+	num_registers = size / 8; /* Each register is 8 bytes (addr + value) */
+	cfg_data = (const u32 *)(fw_base + offset);
+
+	/* Apply each register configuration */
+	for (i = 0; i < num_registers; i++) {
+		u64 reg_addr = (u64)cfg_data[i * 2];
+		u32 reg_value = cfg_data[i * 2 + 1];
+
+		reg32_write(reg_addr, reg_value);
+
+#if 0 /* Enable for debugging */
+		if (i < 10) {
+			printf("  [%02d] 0x%08x = 0x%08x\n", i, reg_addr, reg_value);
+		}
+#endif
+	}
+}
+
+static inline void load_img_to_apb(u64 file_addr, unsigned long len_bytes,
+				   u64 apb_base)
+{
+	u64 pr_from32 = file_addr;
+	u64 pr_to32 = apb_base;
+	u32 tmp32;
+	unsigned long i;
+
+	for (i = 0; i < len_bytes; i += 4) {
+		tmp32 = reg32_read(pr_from32);
+		ddrphy_apb_wr(pr_to32, (u16)(tmp32 & 0xFFFF));
+		pr_to32 += 1;
+		ddrphy_apb_wr(pr_to32, (u16)((tmp32 >> 16) & 0xFFFF));
+		pr_to32 += 1;
+		pr_from32 += 4;
+	}
+}
+
+void ddrfw_load_phy_cfg(uintptr_t fw_base, struct ddrfw_header *fw_header)
+{
+	int i = 0;
+	u32 comp_offset, comp_size;
+
+	u16 tmp16;
+	u32 from32;
+	u64 to32;
+	u32 to32_le, tmp32_le;
+
+	comp_offset = fw_header->comps[DDR_PHY_CFG].offset;
+	comp_size = fw_header->comps[DDR_PHY_CFG].size;
+
+	debug("Loading DDR PHY configuration (%u bytes)\n", comp_size);
+	from32 = fw_base + comp_offset;
+
+	for (i = 0; i < comp_size;) {
+		memcpy(&to32_le, (void *)(uintptr_t)from32, 4);
+		memcpy(&tmp32_le, (void *)(uintptr_t)(from32 + 4), 4);
+
+		to32 = to32_le;
+		tmp16 = (u16)(tmp32_le & 0xFFFF);
+
+		from32 += 8;
+		i += 8;
+		ddrphy_apb_wr(to32, tmp16);
+	}
+}
+
+int ddrfw_load_train_firmware(uintptr_t fw_base, struct ddrfw_header *fw_header)
+{
+	struct {
+		u64 imem_addr;
+		u64 dmem_addr;
+		u64 imem_len;
+		u64 dmem_len;
+	} fw_info[2] = {
+		{
+			fw_base + fw_header->comps[DDR_PMU_1D_IMEM].offset,
+			fw_base + fw_header->comps[DDR_PMU_1D_DMEM].offset,
+			fw_header->comps[DDR_PMU_1D_IMEM].size,
+			fw_header->comps[DDR_PMU_1D_DMEM].size,
+		},
+		{
+			fw_base + fw_header->comps[DDR_PMU_2D_IMEM].offset,
+			fw_base + fw_header->comps[DDR_PMU_2D_DMEM].offset,
+			fw_header->comps[DDR_PMU_2D_IMEM].size,
+			fw_header->comps[DDR_PMU_2D_DMEM].size,
+		}
+	};
+
+	for (unsigned stage = 0; stage < 2; ++stage) {
+		load_img_to_apb(fw_info[stage].imem_addr,
+				fw_info[stage].imem_len, IMEM_OFFSET_ADDR);
+
+		ddrphy_apb_wr(0xd0000, 0x1);
+		ddrphy_apb_wr(0xd0000, 0x0);
+
+		load_img_to_apb(fw_info[stage].dmem_addr,
+				fw_info[stage].dmem_len, DMEM_OFFSET_ADDR);
+
+		ddrphy_apb_wr(0xd0000, 0x1);
+		ddrphy_apb_wr(0xd0000, 0x1);
+		ddrphy_apb_wr(0xd0099, 0x9);
+		ddrphy_apb_wr(0xd0099, 0x1);
+		ddrphy_apb_wr(0xd0099, 0x0);
+
+		if (ddrphy_phyinit_wait_done() != 0)
+			return -1;
+
+		ddrphy_apb_wr(0xd0099, 0x1);
+		ddrphy_apb_wr(0xd0000, 0x0);
+		ddrphy_apb_wr(0xd0000, 0x1);
+		ddrphy_apb_wr(0xd0000, 0x0);
+	}
+
+	return 0;
+}
+
+void ddrfw_load_pie_image(uintptr_t fw_base, struct ddrfw_header *fw_header)
+{
+	int i = 0;
+	u32 comp_offset, comp_size;
+
+	u16 tmp16;
+	u32 from32;
+	u64 to32;
+	u32 to32_le, tmp32_le;
+
+	comp_offset = fw_header->comps[DDR_PIE_IMAGE].offset;
+	comp_size = fw_header->comps[DDR_PIE_IMAGE].size;
+
+	debug("Loading PIE image (%u bytes)\n", comp_size);
+	from32 = fw_base + comp_offset;
+
+	for (i = 0; i < comp_size;) {
+		memcpy(&to32_le, (void *)(uintptr_t)from32, 4);
+		memcpy(&tmp32_le, (void *)(uintptr_t)(from32 + 4), 4);
+
+		to32 = to32_le;
+		tmp16 = tmp32_le & 0xffff;
+
+		from32 += 8;
+		i += 8;
+		ddrphy_apb_wr(to32, tmp16);
+	}
+}
\ No newline at end of file
diff --git a/board/metanoia/mt5824/lpddr4_init.c b/board/metanoia/mt5824/lpddr4_init.c
new file mode 100644
index 00000000000..a65517793f2
--- /dev/null
+++ b/board/metanoia/mt5824/lpddr4_init.c
@@ -0,0 +1,115 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2025 Metanoia Communications Inc.,
+ * Jun Chang, Metanoia Communications Inc. <jun.chang at metanoia-comm.com>
+ */
+
+#include <config.h>
+#include <string.h>
+#include <asm/io.h>
+#include <log.h>
+
+#include "lpddr4.h"
+
+#define RST_DEVRSTREQ 0x1000000c
+#define RST_DEVRSTREQ_DDR_CNTR BIT(17)
+#define RST_DEVRSTREQ_DDR_PCLK BIT(16)
+
+static void ddrctrl_reset(int en)
+{
+	void __iomem *reg = phys_to_virt(RST_DEVRSTREQ);
+	u32 value;
+
+	value = readl(reg);
+
+	if (en)
+		value &= ~(RST_DEVRSTREQ_DDR_CNTR);
+	else
+		value |= (RST_DEVRSTREQ_DDR_CNTR);
+
+	writel(value, reg);
+	return;
+}
+
+static void ddrctrl_step1_s2(uintptr_t fw_base, struct ddrfw_header *fw_header)
+{
+	ddrfw_load_controller_config(fw_base, fw_header);
+}
+
+static int ddrctrl_step4_s7(void)
+{
+	reg32_write(DDRC_RFSHCTL3, reg32_read(DDRC_RFSHCTL3) | 1);
+	reg32_write(DDRC_PWRCTL, reg32_read(DDRC_PWRCTL) & (~(2 | 8)));
+	reg32_write(DDRC_SWCTL, 0);
+	reg32_write(DDRC_DFIMISC, (reg32_read(DDRC_DFIMISC) & 0xfffffffe));
+	reg32_write(DDRC_SWCTL, reg32_read(DDRC_SWCTL) | 0x1);
+	while (reg32_read(DDRC_SWSTAT) != 0x1)
+		;
+	return 0;
+}
+
+static int ddrctrl_step15_s25(void)
+{
+	reg32_write(DDRC_SWCTL, reg32_read(DDRC_SWCTL) & 0xfffffffe);
+	reg32_write(DDRC_DFIMISC, reg32_read(DDRC_DFIMISC) | 0x20);
+	reg32_write(DDRC_SWCTL, reg32_read(DDRC_SWCTL) | 0x1);
+	while (reg32_read(DDRC_SWSTAT) != 0x1)
+		; // poll swstat.sw_done_ack
+	while ((reg32_read(DDRC_DFISTAT) & 1) == 0x0)
+		; // 18. poll dfi_init_complete
+	reg32_write(DDRC_SWCTL, reg32_read(DDRC_SWCTL) & 0xfffffffe);
+	reg32_write(DDRC_DFIMISC, reg32_read(DDRC_DFIMISC) & (~(1 << 5)));
+	reg32_write(DDRC_DFIMISC, reg32_read(DDRC_DFIMISC) | ((1 << 0)));
+	reg32_write(DDRC_PWRCTL, reg32_read(DDRC_PWRCTL) & (~(1 << 5)));
+	reg32_write(DDRC_SWCTL, reg32_read(DDRC_SWCTL) | 0x1);
+	while (reg32_read(DDRC_SWSTAT) != 0x1)
+		;
+	while ((reg32_read(DDRC_STAT) & 0x7) != 1)
+		; // 25. wait operation mode to normal
+	reg32_write(DDRC_RFSHCTL3, reg32_read(DDRC_RFSHCTL3) & 0xffffffffe);
+
+	return 0;
+}
+
+static int ddrphy_training(uintptr_t fw_base, struct ddrfw_header *fw_header)
+{
+	ddrfw_load_phy_cfg(fw_base, fw_header);
+	debug("[PHY-TRAIN] DDR PHY configuration loaded successfully\n");
+
+	/* load the dram training firmware image */
+	if (ddrfw_load_train_firmware(fw_base, fw_header) != 0)
+		return -1;
+	debug("[PHY-TRAIN] PMU training firmware loaded successfully\n");
+
+	ddrfw_load_pie_image(fw_base, fw_header);
+	debug("[PHY-TRAIN] PIE image loaded successfully\n");
+
+	return 0;
+}
+
+int ddrphy_init(uintptr_t fw_base)
+{
+	struct ddrfw_header fw_header;
+
+	if (ddrfw_parse_fw_header(fw_base, &fw_header) != 0) {
+		printf("ERROR: Parsing DDRFW header failed\n");
+		return -1;
+	}
+
+	ddrctrl_reset(0);
+	/* 0. ~ 2. steps for control registers initiali*/
+	ddrctrl_step1_s2(fw_base, &fw_header);
+	/* 3. release soc_rst ddr reset */
+	ddrctrl_reset(1);
+	/* 4.~ 7. steps */
+	ddrctrl_step4_s7();
+	/* 8. ~ 14. steps for PHY training */
+	if (ddrphy_training(fw_base, &fw_header) != 0) {
+		printf("ERROR: DDR PHY training failed\n");
+		return -1;
+	}
+	/* 15. ~ 25. */
+	ddrctrl_step15_s25();
+
+	return 0;
+}
diff --git a/board/metanoia/mt5824/mt5824.c b/board/metanoia/mt5824/mt5824.c
new file mode 100644
index 00000000000..f385de1d897
--- /dev/null
+++ b/board/metanoia/mt5824/mt5824.c
@@ -0,0 +1,188 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2025 Metanoia Communications Inc.,
+ * Jun Chang, Metanoia Communications Inc. <jun.chang at metanoia-comm.com>
+ */
+
+#include <log.h>
+#include <asm/csr.h>
+#include <asm/sbi.h>
+#include <config.h>
+#include <cpu_func.h>
+#include <image.h>
+#include <init.h>
+#include <net.h>
+#include <asm/global_data.h>
+#include <linux/io.h>
+#include <fdtdec.h>
+#include <dm.h>
+#include <hang.h>
+#include <misc.h>
+#include <hexdump.h>
+#include <env_internal.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+
+#include "mt5824.h"
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/*
+ * Miscellaneous platform dependent initializations
+ */
+
+enum boot_source get_boot_src(void)
+{
+	void __iomem *reg = phys_to_virt(0x10000018);
+	u8 boot_src = (readl(reg) >> 6) & 0x3;
+
+	return boot_src;
+}
+
+static int bootsrc_env_set(void)
+{
+	char buff[8] = {};
+	int len;
+
+	len = snprintf(buff, sizeof(buff) - 1, "%d", get_boot_src());
+	if (len > 0) {
+		buff[len] = '\0';
+		debug("%s: bootsrc env %s from get_boot_src()\n", __func__,
+		      buff);
+		return env_set("bootsrc", buff);
+	} else {
+		pr_err(" bootsrc env len wrong\n");
+		return -1;
+	}
+}
+
+static int cpu_env_set(void)
+{
+	long csr_marchid = 0;
+	const long mask_64 = 0x8000;
+	const long mask_cpu = 0xff;
+	char cpu_name[10] = {};
+	int len;
+
+	if (IS_ENABLED(CONFIG_RISCV_SMODE))
+		sbi_get_marchid(&csr_marchid);
+	else if (IS_ENABLED(CONFIG_RISCV_MMODE))
+		csr_marchid = csr_read(CSR_MARCHID);
+
+	if (mask_64 & csr_marchid)
+		len = snprintf(cpu_name, sizeof(cpu_name), "ax%lx",
+			       (mask_cpu & csr_marchid));
+	else
+		len = snprintf(cpu_name, sizeof(cpu_name), "a%lx",
+			       (mask_cpu & csr_marchid));
+	if (len <= 0) {
+		pr_err(" cpu env len wrong\n");
+		return -1;
+	}
+	cpu_name[len] = '\0';
+
+	return env_set("cpu", cpu_name);
+}
+
+int misc_init_r(void)
+{
+	bootsrc_env_set();
+
+	return cpu_env_set();
+}
+
+int board_init(void)
+{
+	gd->bd->bi_boot_params = PHYS_SDRAM_0 + 0x400;
+
+	return 0;
+}
+
+int dram_init(void)
+{
+	return fdtdec_setup_mem_size_base();
+}
+
+int dram_init_banksize(void)
+{
+	return fdtdec_setup_memory_banksize();
+}
+
+/*
+ * The first priority of DTB is used build-in (u-boot-spl)
+ * The second priority of DTB is used individual partition (CONFIG_SYS_FDT_BASE) (default)
+ */
+#define MT5824_HW_DTB_ADDRESS 0x68010000
+int board_fdt_blob_setup(void **fdtp)
+{
+	if (IS_ENABLED(CONFIG_OF_SEPARATE) || IS_ENABLED(CONFIG_OF_BOARD)) {
+		if (fdt_magic((uintptr_t)gd->arch.firmware_fdt_addr) ==
+		    FDT_MAGIC) {
+			*fdtp = (void *)(ulong)gd->arch.firmware_fdt_addr;
+
+			return 0;
+		}
+	}
+
+	if (fdt_magic((uintptr_t)gd->fdt_blob) == FDT_MAGIC) {
+		printf("Using DTB from gd->fdt_blob: 0x%lX\n",
+		       (ulong)gd->fdt_blob);
+		*fdtp = (void *)(ulong)gd->fdt_blob;
+		return 0;
+	}
+
+	if (fdt_magic(CONFIG_SYS_FDT_BASE) == FDT_MAGIC) {
+		printf("Using DTB from CONFIG_SYS_FDT_BASE, 0x%X\n",
+		       CONFIG_SYS_FDT_BASE);
+		*fdtp = (void *)CONFIG_SYS_FDT_BASE;
+		return 0;
+	}
+
+	if (fdt_magic(MT5824_HW_DTB_ADDRESS) == FDT_MAGIC) {
+		printf("Using DTB from MT5824_HW_DTB_ADDRESS, 0x%X\n",
+		       MT5824_HW_DTB_ADDRESS);
+		*fdtp = (void *)MT5824_HW_DTB_ADDRESS;
+		return 0;
+	}
+
+	printf("No valid DTB found\n");
+
+	return -EINVAL;
+}
+
+#ifdef CONFIG_BOARD_EARLY_INIT_F
+int board_early_init_f(void)
+{
+	return 0;
+}
+#endif
+
+#ifdef CONFIG_BOARD_EARLY_INIT_R
+int board_early_init_r(void)
+{
+	if (!IS_ENABLED(CONFIG_SYS_DCACHE_OFF))
+		enable_caches();
+
+	return 0;
+}
+#endif
+
+enum env_location env_get_location(enum env_operation op, int prio)
+{
+	enum boot_source boot_src = get_boot_src();
+
+	if (prio)
+		return ENVL_UNKNOWN;
+
+	if ((boot_src == BOOT_SOURCE_SD || boot_src == BOOT_SOURCE_EMMC) &&
+	    IS_ENABLED(CONFIG_ENV_IS_IN_MMC))
+		return ENVL_MMC;
+	if (boot_src == BOOT_SOURCE_QSPI_NOR &&
+	    IS_ENABLED(CONFIG_ENV_IS_IN_SPI_FLASH))
+		return ENVL_SPI_FLASH;
+	if (boot_src == BOOT_SOURCE_RESERVED &&
+	    IS_ENABLED(CONFIG_ENV_IS_NOWHERE))
+		return ENVL_NOWHERE;
+
+	return ENVL_NOWHERE;
+}
diff --git a/board/metanoia/mt5824/mt5824.h b/board/metanoia/mt5824/mt5824.h
new file mode 100644
index 00000000000..67784c71c4a
--- /dev/null
+++ b/board/metanoia/mt5824/mt5824.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2025 Metanoia Communications Inc.,
+ * Jun Chang, Metanoia Communications Inc. <jun.chang at metanoia-comm.com>
+ */
+
+#ifndef __METANOIA_MT5824_H__
+#define __METANOIA_MT5824_H__
+
+#include <linux/types.h>
+
+enum boot_source {
+	BOOT_SOURCE_RESERVED = 0,
+	BOOT_SOURCE_QSPI_NOR,
+	BOOT_SOURCE_SD,
+	BOOT_SOURCE_EMMC,
+};
+
+enum boot_source get_boot_src(void);
+
+#endif /* __METANOIA_MT5824_H__ */
diff --git a/board/metanoia/mt5824/mt5824_ddr.c b/board/metanoia/mt5824/mt5824_ddr.c
new file mode 100644
index 00000000000..d2699f61cab
--- /dev/null
+++ b/board/metanoia/mt5824/mt5824_ddr.c
@@ -0,0 +1,54 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2025 Metanoia Communications Inc.,
+ * Jun Chang, Metanoia Communications Inc. <jun.chang at metanoia-comm.com>
+ */
+
+#include <config.h>
+#include <mmc.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+
+#include "lpddr4.h"
+#include "mt5824.h"
+#include "mt5824_ddr.h"
+
+static int ddrfw_load(enum boot_source boot_src)
+{
+	int cnt = 0;
+
+	if (boot_src == BOOT_SOURCE_RESERVED)
+		cnt = CFG_DDRFW_LOAD_BUF_SIZE;
+
+	if (cnt <= 0) {
+		printf("DDR/PHY training data not ready\n");
+		return -1;
+	}
+
+	return cnt;
+}
+
+int mt5824_ddr_init(void)
+{
+	enum boot_source boot_src = get_boot_src();
+	u32 tzseccfg0;
+	int ret;
+
+	tzseccfg0 = (readl((void __iomem *)0x10010280) & ~GENMASK(31, 29)) |
+		    (0x1 << 29);
+	writel(tzseccfg0, (void __iomem *)0x10010280);
+
+	ret = ddrfw_load(boot_src);
+	if (ret < 0) {
+		printf("DDRFW load failed\n");
+		return -1;
+	}
+
+	ret = ddrphy_init(CFG_DDRFW_LOAD_BUF_ADDR);
+	if (ret == 0)
+		printf("DDR/PHY initialized successfully\n");
+	else
+		printf("DDR/PHY initialization failed\n");
+
+	return ret;
+}
diff --git a/board/metanoia/mt5824/mt5824_ddr.h b/board/metanoia/mt5824/mt5824_ddr.h
new file mode 100644
index 00000000000..385d62ddd28
--- /dev/null
+++ b/board/metanoia/mt5824/mt5824_ddr.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2025 Metanoia Communications Inc.,
+ * Jun Chang, Metanoia Communications Inc. <jun.chang at metanoia-comm.com>
+ */
+
+#ifndef __METANOIA_MT5824_DDR_H__
+#define __METANOIA_MT5824_DDR_H__
+
+int mt5824_ddr_init(void);
+
+#endif /* __METANOIA_MT5824_DDR_H__ */
\ No newline at end of file
diff --git a/board/metanoia/mt5824/mt5824_spl.c b/board/metanoia/mt5824/mt5824_spl.c
new file mode 100644
index 00000000000..f816fee20dd
--- /dev/null
+++ b/board/metanoia/mt5824/mt5824_spl.c
@@ -0,0 +1,141 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2025 Metanoia Communications Inc.,
+ * Jun Chang, Metanoia Communications Inc. <jun.chang at metanoia-comm.com>
+ */
+
+#include <config.h>
+#include <hang.h>
+#include <spl.h>
+#include <cpu_func.h>
+#include <mapmem.h>
+#include <hexdump.h>
+#include <asm/cache.h>
+#include <asm/global_data.h>
+#include <asm/io.h>
+
+#include "mt5824.h"
+#include "mt5824_ddr.h"
+
+DECLARE_GLOBAL_DATA_PTR;
+
+void board_boot_order(u32 *spl_boot_list)
+{
+	enum boot_source boot_src = get_boot_src();
+
+	if ((boot_src == BOOT_SOURCE_SD || boot_src == BOOT_SOURCE_EMMC) &&
+	    IS_ENABLED(CONFIG_SPL_MMC)) {
+		spl_boot_list[0] = BOOT_DEVICE_MMC1;
+	} else if (boot_src == BOOT_SOURCE_QSPI_NOR &&
+		   IS_ENABLED(CONFIG_SPL_NOR_SUPPORT)) {
+		spl_boot_list[0] = BOOT_DEVICE_NOR;
+	} else {
+		spl_boot_list[0] = BOOT_DEVICE_RAM;
+	}
+}
+
+#ifdef CONFIG_SPL_LOAD_FIT
+int board_fit_config_name_match(const char *name)
+{
+	/* boot using first FIT config */
+	return 0;
+}
+#endif
+
+static void set_plic_mode(void)
+{
+	u32 reg_val = 0;
+
+	void __iomem *reg_address = phys_to_virt(0x0c000000);
+
+	reg_val = readl(reg_address + 0x0);
+
+#if BORAD_PLIC_NON_VECTOR
+	reg_val &= ~(0x2);
+#else
+	reg_val |= 0x2;
+#endif
+
+	writel(reg_val, reg_address + 0x0);
+}
+
+void spl_board_init(void)
+{
+	if (!IS_ENABLED(CONFIG_SYS_DCACHE_OFF))
+		enable_caches();
+
+	set_plic_mode();
+}
+
+int spl_board_init_f(void)
+{
+	int ret = 0;
+
+	ret = mt5824_ddr_init();
+
+	return ret;
+}
+
+/* rough 10us delay workaround before mtimer ready */
+static void delay_10us(void)
+{
+	volatile unsigned int i;
+
+	for (i = 0; i < 50; i++)
+		;
+}
+
+void shuttle_pll_fix(void)
+{
+	void __iomem *reg_pll_st1 = phys_to_virt(0x10040008);
+	void __iomem *reg_pll_cr0 = phys_to_virt(0x1004000c);
+	u8 step;
+
+	readl(reg_pll_st1);
+	delay_10us();
+	writel(0x040000C8, reg_pll_cr0);
+	delay_10us();
+	writel(0x440000C8, reg_pll_cr0);
+	delay_10us();
+	writel(0x040000C8, reg_pll_cr0);
+	delay_10us();
+	writel(0x840000C8, reg_pll_cr0);
+	delay_10us();
+	writel(0x840000C8, reg_pll_cr0);
+	delay_10us();
+
+	for (step = 0xc8; step >= 0xb4; step--) {
+		writel(0x84000000 | step, reg_pll_cr0);
+		delay_10us();
+		writel(0xc4000000 | step, reg_pll_cr0);
+		delay_10us();
+		writel(0x84000000 | step, reg_pll_cr0);
+		delay_10us();
+	}
+
+	readl(reg_pll_st1);
+	debug("[PLL/PCLK] change to 900MHz\n");
+
+	return;
+}
+
+void board_init_f(ulong dummy)
+{
+	int ret;
+	void __iomem *reg_deviceid = phys_to_virt(0x10010008);
+
+	ret = spl_early_init();
+	if (ret)
+		panic("spl_early_init() failed: %d\n", ret);
+
+	if (0x28240000 == readl(reg_deviceid))
+		shuttle_pll_fix();
+
+	riscv_cpu_setup();
+
+	preloader_console_init();
+
+	ret = spl_board_init_f();
+	if (ret)
+		panic("spl_board_init_f() failed: %d\n", ret);
+}
diff --git a/configs/mt5824_evb_defconfig b/configs/mt5824_evb_defconfig
new file mode 100644
index 00000000000..74f6ceb2284
--- /dev/null
+++ b/configs/mt5824_evb_defconfig
@@ -0,0 +1,61 @@
+CONFIG_RISCV=y
+CONFIG_RISCV_ANDES=y
+CONFIG_ARCH_RV64I=y
+CONFIG_RISCV_SMODE=y
+
+CONFIG_SPL_SIZE_LIMIT=0x200000
+CONFIG_SPL_PAD_TO=0x0010000
+CONFIG_SPL_MAX_SIZE=0x00010000
+
+CONFIG_SPL_STACK_R=y
+CONFIG_SPL_STACK_R_ADDR=0x68100000
+CONFIG_SPL_STACK_R_MALLOC_SIMPLE_LEN=0xC0000
+
+CONFIG_SYS_HAS_NONCACHED_MEMORY=y
+CONFIG_TEXT_BASE=0x84000000
+CONFIG_SYS_MALLOC_LEN=0x80000
+CONFIG_NR_DRAM_BANKS=2
+CONFIG_DEFAULT_DEVICE_TREE="mt5824-evb"
+CONFIG_SYS_PROMPT="RISC-V # "
+CONFIG_SPL_SYS_MALLOC_F_LEN=0x40000
+CONFIG_SYS_MALLOC_F_LEN=0x40000
+CONFIG_SPL=y
+CONFIG_SYS_LOAD_ADDR=0x100000
+CONFIG_TARGET_METANOIA_MT5824=y
+# CONFIG_BINMAN_FDT is not set
+CONFIG_DISTRO_DEFAULTS=y
+# CONFIG_AVAILABLE_HARTS is not set
+CONFIG_HAS_CUSTOM_SYS_INIT_SP_ADDR=y
+CONFIG_CUSTOM_SYS_INIT_SP_ADDR=0x68140000
+CONFIG_FIT=y
+CONFIG_SPL_LOAD_FIT_ADDRESS=0x90000000
+CONFIG_SYS_MONITOR_BASE=0x88000000
+CONFIG_BOOTDELAY=3
+CONFIG_MISC_INIT_R=y
+CONFIG_SPL_SYS_MALLOC_SIMPLE=y
+
+CONFIG_SPL_BSS_START_ADDR=0x68018000
+CONFIG_SPL_BSS_MAX_SIZE=0x01000
+CONFIG_SPL_CACHE=y
+CONFIG_SPL_OPENSBI_SCRATCH_OPTIONS=0x0
+CONFIG_SYS_PBSIZE=1050
+CONFIG_SYS_BOOTM_LEN=0x4000000
+
+# CONFIG_WATCHDOG_AUTOSTART is not set
+
+CONFIG_BAUDRATE=115200
+CONFIG_SYS_NS16550=y
+CONFIG_SPL_SYS_NS16550_SERIAL=y
+CONFIG_ENV_IS_NOWHERE=y
+
+CONFIG_SYS_FDT_BASE=0x68010000
+CONFIG_SYSRESET=y
+CONFIG_SYSRESET_SBI=y
+CONFIG_LMB_MAX_REGIONS=64
+
+CONFIG_MBEDTLS_LIB=y
+CONFIG_SPL_MBEDTLS_LIB=y
+
+CONFIG_DYNAMIC_CRC_TABLE=y
+CONFIG_SPL_CRC32=y
+
diff --git a/include/configs/mt5824.h b/include/configs/mt5824.h
new file mode 100644
index 00000000000..9809080b805
--- /dev/null
+++ b/include/configs/mt5824.h
@@ -0,0 +1,130 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2026 Metanoia Communication Inc.,
+ * Jun Chang, Metanoia Communication Inc. <jun.chang at metanoia-comm.com>
+ */
+
+#ifndef __CONFIG_H
+#define __CONFIG_H
+
+// #include <config.h>
+
+#define RISCV_MMODE_TIMERBASE	0x0c400000
+
+#define RISCV_MMODE_TIMER_FREQ	20000000
+#define RISCV_SMODE_TIMER_FREQ	20000000
+
+/*
+ * CPU and Board Configuration Options
+ */
+
+/*
+ * Miscellaneous configurable options
+ */
+/*
+ * PLIC configurable options
+ */
+#define BORAD_PLIC_NON_VECTOR    1    /* depends on CPU and kernel support */
+/*
+ * Physical Memory Map
+ */
+#define PHYS_SDRAM_0	0x80000000		/* SDRAM Bank #1 */
+#define PHYS_SDRAM_1	\
+	(PHYS_SDRAM_0 + PHYS_SDRAM_0_SIZE)	/* SDRAM Bank #2 */
+#define PHYS_SDRAM_0_SIZE	0x20000000	/* 512 MB */
+#define PHYS_SDRAM_1_SIZE	0x20000000	/* 512 MB */
+#define CFG_SYS_SDRAM_BASE	PHYS_SDRAM_0
+
+/* Init Heap Pointer */
+#define CFG_MALLOC_F_ADDR 0x68180000
+
+/*
+ * Serial console configuration
+ */
+#define CFG_SYS_NS16550_CLK	225000000
+
+
+/* Init Stack Pointer */
+
+/* support JEDEC */
+#define PHYS_FLASH_1			0x10120000	/* BANK 0 */
+#define CFG_SYS_FLASH_BASE		PHYS_FLASH_1
+#define CFG_SYS_FLASH_BANKS_LIST	{ PHYS_FLASH_1, }
+
+/* max number of memory banks */
+/*
+ * There are 4 banks supported for this Controller,
+ * but we have only 1 bank connected to flash on board
+*/
+#define CFG_SYS_FLASH_BANKS_SIZES {0x4000000}
+
+/* max number of sectors on one chip */
+#define CFG_FLASH_SECTOR_SIZE	(0x10000*2)
+
+/* environments */
+
+/* SPI FLASH */
+
+/*
+ * For booting Linux, the board info and command line data
+ * have to be in the first 16 MB of memory, since this is
+ * the maximum mapped by the Linux kernel during initialization.
+ */
+
+/* Initial Memory map for Linux*/
+#define CFG_SYS_BOOTMAPSZ	(64 << 20)
+/* Increase max gunzip size */
+
+/* Support autoboot from RAM (kernel image is loaded via debug port) */
+#define BOOTENV_DEV_NAME_RAM(devtypeu, devtypel, instance) \
+	"ram "
+
+#define BOOTENV_DEV_RAM(devtypeu, devtypel, instance) \
+	"bootcmd_ram=" \
+	"bootm ${fit_addr}\0"
+
+#define BOOTENV_DEV_LEGACY_MMC(devtypeu, devtypel, instance) \
+	"bootcmd_" #devtypel #instance "=" \
+	"setenv mmcdev " #instance"; "\
+	"setenv bootpart " #instance":2 ; "\
+	"run mmcboot\0"
+
+#define BOOTENV_DEV_NAME_LEGACY_MMC(devtypeu, devtypel, instance) \
+	#devtypel #instance " "
+
+#define BOOTENV_DEV_NAME_TFTP(devtypeu, devtypel, instance) \
+	#devtypel " "
+
+#define BOOTENV_DEV_TFTP(devtypeu, devtypel, instance) \
+	"bootcmd_" #devtypel "=" \
+	"tftpboot ${fit_addr} $serverip:fitImage-dev-image-initramfs; " \
+	"bootm ${fit_addr}\0"
+
+/* Enable distro boot */
+#define BOOT_TARGET_DEVICES(func) \
+	func(TFTP, tftp, na) \
+	func(RAM, ram, na)
+
+#include <config_distro_bootcmd.h>
+
+#define CFG_EXTRA_ENV_SETTINGS	\
+	"bootcmd=bootm 90000000\0" \
+	"kernel_addr_r=82000000\0" \
+	"pxefile_addr_r=0x83f00000\0" \
+	"scriptaddr=0x83f00000\0" \
+	"fdt_addr_r=0x68010000\0" \
+	"ramdisk_addr_r=0x83000000\0" \
+	"fit_addr=0x83000000\0" \
+	"initrd_high=0x100000000\0" \
+	BOOTENV
+
+#define COBRA_NOR_FLASH_BASE                         0x20000000
+#define CFG_SYS_UBOOT_BASE	\
+	(COBRA_NOR_FLASH_BASE + CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_SECTOR * 512)
+
+#define CFG_DDRFW_MMC_OFFSET			0x300000
+#define CFG_DDRFW_NOR_OFFSET			0x300000
+#define CFG_DDRFW_LOAD_BUF_ADDR			0x70080000
+#define CFG_DDRFW_LOAD_BUF_SIZE			0x80000
+
+#endif /* __CONFIG_H */
-- 
2.43.0



More information about the U-Boot mailing list