mips: mtmips: add support for MediaTek MT7620 SoC

This patch adds support for MediaTek MT7620 SoC.
All files are dedicated for u-boot.

Reviewed-by: Stefan Roese <sr@denx.de>
diff --git a/arch/mips/mach-mtmips/mt7620/Kconfig b/arch/mips/mach-mtmips/mt7620/Kconfig
new file mode 100644
index 0000000..aa7cf1d
--- /dev/null
+++ b/arch/mips/mach-mtmips/mt7620/Kconfig
@@ -0,0 +1,54 @@
+
+if SOC_MT7620
+
+config DEBUG_UART_BOARD_INIT
+	default y
+
+choice
+	prompt "Board select"
+
+endchoice
+
+choice
+	prompt "CPU frequency select"
+	default CPU_FREQ_580MHZ
+
+config CPU_FREQ_480MHZ
+	bool "480MHz"
+
+config CPU_FREQ_500MHZ
+	bool "500MHz"
+
+config CPU_FREQ_520MHZ
+	bool "520MHz"
+
+config CPU_FREQ_540MHZ
+	bool "540MHz"
+
+config CPU_FREQ_560MHZ
+	bool "560MHz"
+
+config CPU_FREQ_580MHZ
+	bool "580MHz"
+
+config CPU_FREQ_600MHZ
+	bool "600MHz"
+
+config CPU_FREQ_620MHZ
+	bool "620MHz"
+
+endchoice
+
+config CPU_FREQ_MULTI
+	int
+	range 0 7
+	default 0 if CPU_FREQ_480MHZ
+	default 1 if CPU_FREQ_500MHZ
+	default 2 if CPU_FREQ_520MHZ
+	default 3 if CPU_FREQ_540MHZ
+	default 4 if CPU_FREQ_560MHZ
+	default 5 if CPU_FREQ_580MHZ
+	default 6 if CPU_FREQ_600MHZ
+	default 7 if CPU_FREQ_620MHZ
+
+endif
diff --git a/arch/mips/mach-mtmips/mt7620/Makefile b/arch/mips/mach-mtmips/mt7620/Makefile
new file mode 100644
index 0000000..649f6c3
--- /dev/null
+++ b/arch/mips/mach-mtmips/mt7620/Makefile
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-y += lowlevel_init.o
+obj-y += init.o
+obj-y += dram.o
+obj-y += serial.o
+
+ifndef CONFIG_SPL_BUILD
+obj-y += sysc.o
+endif
diff --git a/arch/mips/mach-mtmips/mt7620/dram.c b/arch/mips/mach-mtmips/mt7620/dram.c
new file mode 100644
index 0000000..0f0e64b
--- /dev/null
+++ b/arch/mips/mach-mtmips/mt7620/dram.c
@@ -0,0 +1,113 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 MediaTek Inc. All Rights Reserved.
+ *
+ * Author:  Weijie Gao <weijie.gao@mediatek.com>
+ */
+
+#include <asm/addrspace.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/sizes.h>
+#include <linux/io.h>
+#include <mach/ddr.h>
+#include <mach/mc.h>
+#include "mt7620.h"
+
+/* SDR parameters */
+#define SDR_CFG0_VAL		0x51B283B3
+#define SDR_CFG1_VAL		0xC00003A9
+
+/* DDR2 DQ_DLY */
+#define DDR2_DQ_DLY		0x88888888
+
+/* DDR2 DQS_DLY */
+#define DDR2_DQS_DLY		0x88888888
+
+static const struct mc_ddr_cfg ddr1_cfgs_200mhz[] = {
+	[DRAM_8MB]   = { 0x34A1EB94, 0x20262324, 0x28000033, 0x00000002, 0x00000000 },
+	[DRAM_16MB]  = { 0x34A1EB94, 0x202A2324, 0x28000033, 0x00000002, 0x00000000 },
+	[DRAM_32MB]  = { 0x34A1E5CA, 0x202E2324, 0x28000033, 0x00000002, 0x00000000 },
+	[DRAM_64MB]  = { 0x3421E5CA, 0x20322324, 0x28000033, 0x00000002, 0x00000000 },
+	[DRAM_128MB] = { 0x241B05CA, 0x20362334, 0x28000033, 0x00000002, 0x00000000 },
+};
+
+static const struct mc_ddr_cfg ddr1_cfgs_160mhz[] = {
+	[DRAM_8MB]   = { 0x239964A1, 0x20262323, 0x00000033, 0x00000002, 0x00000000 },
+	[DRAM_16MB]  = { 0x239964A1, 0x202A2323, 0x00000033, 0x00000002, 0x00000000 },
+	[DRAM_32MB]  = { 0x239964A1, 0x202E2323, 0x00000033, 0x00000002, 0x00000000 },
+	[DRAM_64MB]  = { 0x239984A1, 0x20322323, 0x00000033, 0x00000002, 0x00000000 },
+	[DRAM_128MB] = { 0x239AB4A1, 0x20362333, 0x00000033, 0x00000002, 0x00000000 },
+};
+
+static const struct mc_ddr_cfg ddr2_cfgs_200mhz[] = {
+	[DRAM_32MB]  = { 0x2519E2E5, 0x222E2323, 0x68000C43, 0x00000416, 0x0000000A },
+	[DRAM_64MB]  = { 0x249AA2E5, 0x22322323, 0x68000C43, 0x00000416, 0x0000000A },
+	[DRAM_128MB] = { 0x249B42E5, 0x22362323, 0x68000C43, 0x00000416, 0x0000000A },
+	[DRAM_256MB] = { 0x249CE2E5, 0x223A2323, 0x68000C43, 0x00000416, 0x0000000A },
+};
+
+static const struct mc_ddr_cfg ddr2_cfgs_160mhz[] = {
+	[DRAM_32MB]  = { 0x23918250, 0x222E2322, 0x40000A43, 0x00000416, 0x00000006 },
+	[DRAM_64MB]  = { 0x239A2250, 0x22322322, 0x40000A43, 0x00000416, 0x00000008 },
+	[DRAM_128MB] = { 0x2392A250, 0x22362322, 0x40000A43, 0x00000416, 0x00000008 },
+	[DRAM_256MB] = { 0x24140250, 0x223A2322, 0x40000A43, 0x00000416, 0x00000008 },
+};
+
+static void mt7620_memc_reset(int assert)
+{
+	void __iomem *sysc = ioremap_nocache(SYSCTL_BASE, SYSCTL_SIZE);
+
+	if (assert)
+		setbits_32(sysc + SYSCTL_RSTCTL_REG, MC_RST);
+	else
+		clrbits_32(sysc + SYSCTL_RSTCTL_REG, MC_RST);
+}
+
+void mt7620_dram_init(void)
+{
+	void __iomem *sysc;
+	bool lspd = false;
+	int ddr_type, aux;
+	struct mc_ddr_init_param param;
+
+	sysc = ioremap_nocache(SYSCTL_BASE, SYSCTL_SIZE);
+	ddr_type = (readl(sysc + SYSCTL_SYSCFG0_REG) & DRAM_TYPE_M)
+		   >> DRAM_TYPE_S;
+	aux = readl(sysc + SYSCTL_CPLL_CFG1_REG) &
+	      (CPU_CLK_AUX1 | CPU_CLK_AUX0);
+
+	if (aux == CPU_CLK_AUX1 || aux == CPU_CLK_AUX0)
+		lspd = true;
+
+	mt7620_memc_reset(1);
+	__udelay(200);
+
+	param.memc = ioremap_nocache(MEMCTL_BASE, MEMCTL_SIZE);
+	param.dq_dly = DDR2_DQ_DLY;
+	param.dqs_dly = DDR2_DQS_DLY;
+	param.mc_reset = mt7620_memc_reset;
+	param.memsize = 0;
+	param.bus_width = 0;
+
+	if (ddr_type == DRAM_DDR1) {
+		if (lspd)
+			param.cfgs = ddr1_cfgs_160mhz;
+		else
+			param.cfgs = ddr1_cfgs_200mhz;
+
+		ddr1_init(&param);
+	} else if (ddr_type == DRAM_DDR2) {
+		if (lspd)
+			param.cfgs = ddr2_cfgs_160mhz;
+		else
+			param.cfgs = ddr2_cfgs_200mhz;
+
+		ddr2_init(&param);
+	} else {
+		param.sdr_cfg0 = SDR_CFG0_VAL;
+		param.sdr_cfg1 = SDR_CFG1_VAL;
+
+		sdr_init(&param);
+	}
+}
diff --git a/arch/mips/mach-mtmips/mt7620/init.c b/arch/mips/mach-mtmips/mt7620/init.c
new file mode 100644
index 0000000..93abf92
--- /dev/null
+++ b/arch/mips/mach-mtmips/mt7620/init.c
@@ -0,0 +1,193 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 MediaTek Inc. All Rights Reserved.
+ *
+ * Author:  Weijie Gao <weijie.gao@mediatek.com>
+ */
+
+#include <config.h>
+#include <asm/global_data.h>
+#include <linux/io.h>
+#include "mt7620.h"
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static const char * const dram_type[] = {
+	"SDRAM", "DDR", "DDR2", "SDRAM"
+};
+
+static const char * const boot_mode[(CHIP_MODE_M >> CHIP_MODE_S) + 1] = {
+	[1] = "NAND 4-cycles 2KB-page",
+	[2] = "SPI-NOR 3-Byte Addr",
+	[3] = "SPI-NOR 4-Byte Addr",
+	[10] = "NAND 4-cycles 512B-page",
+	[11] = "NAND 5-cycles 2KB-page",
+	[12] = "NAND 3-cycles 512B-page",
+};
+
+static void cpu_pll_init(void)
+{
+	void __iomem *sysc = ioremap_nocache(SYSCTL_BASE, SYSCTL_SIZE);
+	u32 pllmul = CONFIG_CPU_FREQ_MULTI;
+
+	/* Make sure the pll multiplier is valid */
+	if (pllmul > 7)
+		pllmul = 7;
+
+	/* Set init CPU clock to 480MHz */
+	clrsetbits_32(sysc + SYSCTL_CPLL_CFG1_REG, CPU_CLK_AUX1, CPU_CLK_AUX0);
+
+	/* Enable software control of CPU PLL */
+	setbits_32(sysc + SYSCTL_CPLL_CFG0_REG, CPLL_SW_CFG);
+
+	/* CPU PLL power down */
+	setbits_32(sysc + SYSCTL_CPLL_CFG1_REG, CPLL_PD);
+
+	/* PLL configuration */
+	clrsetbits_32(sysc + SYSCTL_CPLL_CFG0_REG, PLL_MULT_RATIO_M |
+		      PLL_DIV_RATIO_M | SSC_UP_BOUND_M | SSC_EN,
+		      (pllmul << PLL_MULT_RATIO_S) | SSC_SWING_M);
+
+	/* CPU PLL power up */
+	clrbits_32(sysc + SYSCTL_CPLL_CFG1_REG, CPLL_PD);
+
+	/* Wait for CPU PLL locked */
+	while (!(readl(sysc + SYSCTL_CPLL_CFG1_REG) & CPLL_LD))
+		;
+
+	/* Set final CPU clock source */
+	clrbits_32(sysc + SYSCTL_CPLL_CFG1_REG, CPU_CLK_AUX1 | CPU_CLK_AUX0);
+
+	/* Adjust CPU clock */
+	clrsetbits_32(sysc + SYSCTL_CPU_SYS_CLKCFG_REG,
+		      CPU_FDIV_M | CPU_FFRAC_M,
+		      (1 << CPU_FDIV_S) | (1 << CPU_FFRAC_S));
+}
+
+void mt7620_init(void)
+{
+	u32 cpu_clk;
+
+	cpu_pll_init();
+
+	/*
+	 * Set timer freq, which will be used during DRAM initialization
+	 * Note that this function is using a temporary gd which will be
+	 * destroyed after leaving this function.
+	 */
+	mt7620_get_clks(&cpu_clk, NULL, NULL);
+	gd->arch.timer_freq = cpu_clk / 2;
+
+	mt7620_dram_init();
+}
+
+void mt7620_get_clks(u32 *cpu_clk, u32 *sys_clk, u32 *xtal_clk)
+{
+	void __iomem *sysc = ioremap_nocache(SYSCTL_BASE, SYSCTL_SIZE);
+	u32 val, multi, div, fdiv, ffrac, dram_type, sys_div;
+	u32 cpu_freq, xtal_freq;
+
+	static const u32 div_ratio_table[] = {2, 3, 4, 8};
+
+	val = readl(sysc + SYSCTL_SYSCFG0_REG);
+
+	dram_type = (val & DRAM_TYPE_M) >> DRAM_TYPE_S;
+
+	if (val & XTAL_FREQ_SEL)
+		xtal_freq = 40000000;
+	else
+		xtal_freq = 20000000;
+
+	val = readl(sysc + SYSCTL_CPLL_CFG1_REG);
+	if (val & CPU_CLK_AUX1) {
+		cpu_freq = xtal_freq;
+	} else if (val & CPU_CLK_AUX0) {
+		cpu_freq = 480000000;
+	} else {
+		val = readl(sysc + SYSCTL_CPLL_CFG0_REG);
+		if (val & CPLL_SW_CFG) {
+			multi = (val & PLL_MULT_RATIO_M) >> PLL_MULT_RATIO_S;
+			div = (val & PLL_DIV_RATIO_M) >> PLL_DIV_RATIO_S;
+			cpu_freq = (multi + 24) * 40000000 /
+					div_ratio_table[div];
+		} else {
+			cpu_freq = 600000000;
+		}
+	}
+
+	val = readl(sysc + SYSCTL_CUR_CLK_STS_REG);
+	ffrac = (val & CUR_CPU_FFRAC_M) >> CUR_CPU_FFRAC_S;
+	fdiv = (val & CUR_CPU_FDIV_M) >> CUR_CPU_FDIV_S;
+	cpu_freq = (cpu_freq * ffrac) / fdiv;
+
+	switch (dram_type) {
+	case DRAM_SDRAM_E1:
+		sys_div = 4;
+		break;
+	case DRAM_DDR1:
+	case DRAM_DDR2:
+		sys_div = 3;
+		break;
+	case DRAM_SDRAM:
+		sys_div = 5;
+		break;
+	}
+
+	if (cpu_clk)
+		*cpu_clk = cpu_freq;
+
+	if (sys_clk)
+		*sys_clk = cpu_freq / sys_div;
+
+	if (xtal_clk)
+		*xtal_clk = xtal_freq;
+}
+
+int print_cpuinfo(void)
+{
+	void __iomem *sysc = ioremap_nocache(SYSCTL_BASE, SYSCTL_SIZE);
+	u32 cpu_clk, bus_clk, xtal_clk;
+	u32 val, ver, eco, pkg, dram, chipmode;
+	const char *bootdev;
+
+	val = readl(sysc + SYSCTL_CHIP_REV_ID_REG);
+	ver = (val & VER_M) >> VER_S;
+	eco = (val & ECO_M) >> ECO_S;
+	pkg = !!(val & PKG_ID);
+
+	val = readl(sysc + SYSCTL_SYSCFG0_REG);
+	dram = (val & DRAM_TYPE_M) >> DRAM_TYPE_S;
+	chipmode = (val & CHIP_MODE_M) >> CHIP_MODE_S;
+
+	bootdev = boot_mode[chipmode];
+	if (!bootdev)
+		bootdev = "Unsupported boot mode";
+
+	printf("CPU:   MediaTek MT7620%c ver:%u eco:%u\n",
+	       pkg ? 'A' : 'N', ver, eco);
+
+	printf("Boot:  %s, %s\n", dram_type[dram], bootdev);
+
+	mt7620_get_clks(&cpu_clk, &bus_clk, &xtal_clk);
+
+	/* Set final timer frequency */
+	gd->arch.timer_freq = cpu_clk / 2;
+
+	printf("Clock: CPU: %uMHz, Bus: %uMHz, XTAL: %uMHz\n",
+	       cpu_clk / 1000000, bus_clk / 1000000, xtal_clk / 1000000);
+
+	return 0;
+}
+
+ulong notrace get_tbclk(void)
+{
+	return gd->arch.timer_freq;
+}
+
+void _machine_restart(void)
+{
+	void __iomem *sysc = ioremap_nocache(SYSCTL_BASE, SYSCTL_SIZE);
+
+	while (1)
+		writel(SYS_RST, sysc + SYSCTL_RSTCTL_REG);
+}
diff --git a/arch/mips/mach-mtmips/mt7620/lowlevel_init.S b/arch/mips/mach-mtmips/mt7620/lowlevel_init.S
new file mode 100644
index 0000000..3991746
--- /dev/null
+++ b/arch/mips/mach-mtmips/mt7620/lowlevel_init.S
@@ -0,0 +1,53 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 MediaTek Inc.
+ *
+ * Author:  Weijie Gao <weijie.gao@mediatek.com>
+ */
+
+#include <config.h>
+#include <asm-offsets.h>
+#include <asm/regdef.h>
+#include <asm/asm.h>
+
+	.set noreorder
+
+NESTED(lowlevel_init, 0, ra)
+	/* Save ra and do real lowlevel initialization */
+	move	s0, ra
+
+	/*
+	* Use SRAM from 802.11n MAC/BBP, 16KiB (0x10184000 ~ 0x10187fff)
+	* NOTE: non-word operations may fail in this SRAM.
+	* Use it as stack only for CPU/DRAM init which only has word operations.
+	*/
+	PTR_LI	sp, 0xb0187f00
+
+	/* We still need a temporary gd for udelay */
+	PTR_SUBU \
+		sp, sp, GD_SIZE		# reserve space for gd
+	li	t0, -16
+	and	sp, sp, t0		# force 16 byte alignment
+	move	k0, sp			# save gd pointer
+
+	move	fp, sp
+
+	/* Clear gd */
+	move	t0, k0
+1:
+	PTR_S	zero, 0(t0)
+	PTR_ADDIU t0, PTRSIZE
+	blt	t0, t1, 1b
+	 nop
+
+	/* Do CPU & DRAM initialization */
+	PTR_LA	t9, mt7620_init
+	jalr	t9
+	 nop
+
+	/* Restore ra */
+	move	ra, s0
+
+	jr	ra
+	 nop
+	END(lowlevel_init)
diff --git a/arch/mips/mach-mtmips/mt7620/mt7620.h b/arch/mips/mach-mtmips/mt7620/mt7620.h
new file mode 100644
index 0000000..dd5e6d0
--- /dev/null
+++ b/arch/mips/mach-mtmips/mt7620/mt7620.h
@@ -0,0 +1,103 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 MediaTek Inc. All Rights Reserved.
+ *
+ * Author:  Weijie Gao <weijie.gao@mediatek.com>
+ */
+
+#ifndef _MT7620_H_
+#define _MT7620_H_
+
+#include <linux/bitops.h>
+
+#define SYSCTL_BASE			0x10000000
+#define SYSCTL_SIZE			0x100
+#define MEMCTL_BASE			0x10000300
+#define MEMCTL_SIZE			0x100
+#define UARTFULL_BASE			0x10000500
+#define UARTFULL_SIZE			0x100
+#define UARTLITE_BASE			0x10000c00
+#define UARTLITE_SIZE			0x100
+
+#define SYSCTL_CHIP_REV_ID_REG		0x0c
+#define PKG_ID				BIT(16)
+#define   PKG_ID_A			1
+#define   PKG_ID_N			0
+#define VER_S				8
+#define VER_M				GENMASK(11, 8)
+#define ECO_S				0
+#define ECO_M				GENMASK(3, 0)
+
+#define SYSCTL_SYSCFG0_REG		0x10
+#define XTAL_FREQ_SEL			BIT(6)
+#define   XTAL_40MHZ			1
+#define   XTAL_20MHZ			0
+#define DRAM_TYPE_S			4
+#define DRAM_TYPE_M			GENMASK(5, 4)
+#define   DRAM_SDRAM			3
+#define   DRAM_DDR2			2
+#define   DRAM_DDR1			1
+#define   DRAM_SDRAM_E1			0
+#define CHIP_MODE_S			0
+#define CHIP_MODE_M			GENMASK(3, 0)
+
+#define SYSCTL_SYSCFG1_REG		0x14
+#define GE2_MODE_S			14
+#define GE2_MODE_M			GENMASK(15, 14)
+#define GE1_MODE_S			12
+#define GE1_MODE_M			GENMASK(13, 12)
+#define USB0_HOST_MODE			BIT(10)
+#define PCIE_RC_MODE			BIT(8)
+#define GE_MODE_M			GENMASK(1, 0)
+
+#define SYSCTL_RSTCTL_REG		0x34
+#define MC_RST				BIT(10)
+#define SYS_RST				BIT(0)
+
+#define SYSCTL_CLKCFG0_REG		0x2c
+#define PERI_CLK_SEL			BIT(4)
+
+#define SYSCTL_CPU_SYS_CLKCFG_REG	0x3c
+#define CPU_OCP_RATIO_S			16
+#define CPU_OCP_RATIO_M			GENMASK(19, 16)
+#define CPU_FDIV_S			8
+#define CPU_FDIV_M			GENMASK(12, 8)
+#define CPU_FFRAC_S			0
+#define CPU_FFRAC_M			GENMASK(4, 0)
+
+#define SYSCTL_CUR_CLK_STS_REG		0x44
+#define CUR_CPU_OCP_RATIO_S		16
+#define CUR_CPU_OCP_RATIO_M		GENMASK(19, 16)
+#define CUR_CPU_FDIV_S			8
+#define CUR_CPU_FDIV_M			GENMASK(12, 8)
+#define CUR_CPU_FFRAC_S			0
+#define CUR_CPU_FFRAC_M			GENMASK(4, 0)
+
+#define SYSCTL_CPLL_CFG0_REG		0x54
+#define CPLL_SW_CFG			BIT(31)
+#define PLL_MULT_RATIO_S		16
+#define PLL_MULT_RATIO_M		GENMASK(18, 16)
+#define PLL_DIV_RATIO_S			10
+#define PLL_DIV_RATIO_M			GENMASK(11, 10)
+#define SSC_UP_BOUND_S			8
+#define SSC_UP_BOUND_M			GENMASK(9, 8)
+#define SSC_EN				BIT(7)
+#define SSC_SWING_S			4
+#define SSC_SWING_M			GENMASK(6, 4)
+
+#define SYSCTL_CPLL_CFG1_REG		0x58
+#define CPLL_PD				BIT(26)
+#define CPU_CLK_AUX1			BIT(25)
+#define CPU_CLK_AUX0			BIT(24)
+#define CPLL_LD				BIT(23)
+
+#define SYSCTL_GPIOMODE_REG		0x60
+#define UARTL_GPIO_MODE			BIT(5)
+#define UARTF_SHARE_MODE_S		2
+#define UARTF_SHARE_MODE_M		GENMASK(4, 2)
+#define   UARTF_MODE_UARTF_GPIO		5
+
+void mt7620_dram_init(void);
+void mt7620_get_clks(u32 *cpu_clk, u32 *sys_clk, u32 *xtal_clk);
+
+#endif /* _MT7620_H_ */
diff --git a/arch/mips/mach-mtmips/mt7620/serial.c b/arch/mips/mach-mtmips/mt7620/serial.c
new file mode 100644
index 0000000..44f061c
--- /dev/null
+++ b/arch/mips/mach-mtmips/mt7620/serial.c
@@ -0,0 +1,36 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 MediaTek Inc.
+ *
+ * Author:  Weijie Gao <weijie.gao@mediatek.com>
+ */
+
+#include <asm/io.h>
+#include <asm/addrspace.h>
+#include "mt7620.h"
+
+void board_debug_uart_init(void)
+{
+	void __iomem *base = ioremap_nocache(SYSCTL_BASE, SYSCTL_SIZE);
+
+#if CONFIG_DEBUG_UART_BASE == 0xb0000500 /* KSEG1ADDR(UARTFULL_BASE) */
+	clrsetbits_32(base + SYSCTL_GPIOMODE_REG, UARTF_SHARE_MODE_M,
+		      UARTF_MODE_UARTF_GPIO << UARTF_SHARE_MODE_S);
+#else
+	clrbits_32(base + SYSCTL_GPIOMODE_REG, UARTL_GPIO_MODE);
+#endif
+}
+
+void mtmips_spl_serial_init(void)
+{
+#ifdef CONFIG_SPL_SERIAL_SUPPORT
+	void __iomem *base = ioremap_nocache(SYSCTL_BASE, SYSCTL_SIZE);
+
+#if CONFIG_CONS_INDEX == 1
+	clrbits_32(base + SYSCTL_GPIOMODE_REG, UARTL_GPIO_MODE);
+#elif CONFIG_CONS_INDEX == 2
+	clrsetbits_32(base + SYSCTL_GPIOMODE_REG, UARTF_SHARE_MODE_M,
+		      UARTF_MODE_UARTF_GPIO << UARTF_SHARE_MODE_S);
+#endif
+#endif /* CONFIG_SPL_SERIAL_SUPPORT */
+}
diff --git a/arch/mips/mach-mtmips/mt7620/sysc.c b/arch/mips/mach-mtmips/mt7620/sysc.c
new file mode 100644
index 0000000..296a9be
--- /dev/null
+++ b/arch/mips/mach-mtmips/mt7620/sysc.c
@@ -0,0 +1,172 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 MediaTek Inc. All Rights Reserved.
+ *
+ * Author:  Weijie Gao <weijie.gao@mediatek.com>
+ *
+ * Misc driver for manipulating System control registers
+ */
+
+#include <dm.h>
+#include <misc.h>
+#include <asm/io.h>
+#include <asm/addrspace.h>
+#include <dm/device_compat.h>
+#include <mach/mt7620-sysc.h>
+#include "mt7620.h"
+
+struct mt7620_sysc_priv {
+	void __iomem *base;
+};
+
+static int mt7620_sysc_read(struct udevice *dev, int offset, void *buf,
+			    int size)
+{
+	struct mt7620_sysc_priv *priv = dev_get_priv(dev);
+	u32 val;
+
+	if (offset % sizeof(u32) || size != sizeof(u32) ||
+	    offset >= SYSCTL_SIZE)
+		return -EINVAL;
+
+	val = readl(priv->base + offset);
+
+	if (buf)
+		*(u32 *)buf = val;
+
+	return 0;
+}
+
+static int mt7620_sysc_write(struct udevice *dev, int offset, const void *buf,
+			     int size)
+{
+	struct mt7620_sysc_priv *priv = dev_get_priv(dev);
+	u32 val;
+
+	if (offset % sizeof(u32) || size != sizeof(u32) ||
+	    offset >= SYSCTL_SIZE || !buf)
+		return -EINVAL;
+
+	val = *(u32 *)buf;
+	writel(val, priv->base + offset);
+
+	return 0;
+}
+
+static int mt7620_sysc_ioctl(struct udevice *dev, unsigned long request,
+			     void *buf)
+{
+	struct mt7620_sysc_priv *priv = dev_get_priv(dev);
+	struct mt7620_sysc_chip_rev *chip_rev;
+	struct mt7620_sysc_clks *clks;
+	u32 val, shift;
+
+	if (!buf)
+		return -EINVAL;
+
+	switch (request) {
+	case MT7620_SYSC_IOCTL_GET_CLK:
+		clks = buf;
+		mt7620_get_clks(&clks->cpu_clk, &clks->sys_clk,
+				&clks->xtal_clk);
+
+		val = readl(priv->base + SYSCTL_CLKCFG0_REG);
+		if (val & PERI_CLK_SEL)
+			clks->peri_clk = clks->xtal_clk;
+		else
+			clks->peri_clk = 40000000;
+
+		return 0;
+
+	case MT7620_SYSC_IOCTL_GET_CHIP_REV:
+		chip_rev = buf;
+
+		val = readl(priv->base + SYSCTL_CHIP_REV_ID_REG);
+
+		chip_rev->bga = !!(val & PKG_ID);
+		chip_rev->ver = (val & VER_M) >> VER_S;
+		chip_rev->eco = (val & ECO_M) >> ECO_S;
+
+		return 0;
+
+	case MT7620_SYSC_IOCTL_SET_GE1_MODE:
+	case MT7620_SYSC_IOCTL_SET_GE2_MODE:
+		val = *(u32 *)buf;
+
+		if (val > MT7620_SYSC_GE_ESW_PHY)
+			return -EINVAL;
+
+		if (request == MT7620_SYSC_IOCTL_SET_GE1_MODE)
+			shift = GE1_MODE_S;
+		else
+			shift = GE2_MODE_S;
+
+		clrsetbits_32(priv->base + SYSCTL_SYSCFG1_REG,
+			      GE_MODE_M << shift, val << shift);
+
+		return 0;
+
+	case MT7620_SYSC_IOCTL_SET_USB_MODE:
+		val = *(u32 *)buf;
+
+		if (val == MT7620_SYSC_USB_DEVICE_MODE)
+			val = 0;
+		else if (val == MT7620_SYSC_USB_HOST_MODE)
+			val = USB0_HOST_MODE;
+
+		clrsetbits_32(priv->base + SYSCTL_SYSCFG1_REG,
+			      USB0_HOST_MODE, val);
+
+		return 0;
+
+	case MT7620_SYSC_IOCTL_SET_PCIE_MODE:
+		val = *(u32 *)buf;
+
+		if (val == MT7620_SYSC_PCIE_EP_MODE)
+			val = 0;
+		else if (val == MT7620_SYSC_PCIE_RC_MODE)
+			val = PCIE_RC_MODE;
+
+		clrsetbits_32(priv->base + SYSCTL_SYSCFG1_REG,
+			      PCIE_RC_MODE, val);
+
+		return 0;
+
+	default:
+		return -EINVAL;
+	}
+}
+
+static int mt7620_sysc_probe(struct udevice *dev)
+{
+	struct mt7620_sysc_priv *priv = dev_get_priv(dev);
+
+	priv->base = dev_remap_addr_index(dev, 0);
+	if (!priv->base) {
+		dev_err(dev, "failed to map sysc registers\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static struct misc_ops mt7620_sysc_ops = {
+	.read = mt7620_sysc_read,
+	.write = mt7620_sysc_write,
+	.ioctl = mt7620_sysc_ioctl,
+};
+
+static const struct udevice_id mt7620_sysc_ids[] = {
+	{ .compatible = "mediatek,mt7620-sysc" },
+	{ }
+};
+
+U_BOOT_DRIVER(mt7620_sysc) = {
+	.name		= "mt7620_sysc",
+	.id		= UCLASS_MISC,
+	.of_match	= mt7620_sysc_ids,
+	.probe		= mt7620_sysc_probe,
+	.ops		= &mt7620_sysc_ops,
+	.priv_auto	= sizeof(struct mt7620_sysc_priv),
+	.flags = DM_FLAG_PRE_RELOC,
+};