[][openwrt][mt7988][integration][Add basic Filogic 880 SoC support]

[Description]
Add basic filogic 880 SoC support to openwrt 21.02

[Release-log]

Change-Id: I57791df2e9f9f4729cb2d32f734090de52c370f2
Reviewed-on: https://gerrit.mediatek.inc/c/openwrt/feeds/mtk_openwrt_feeds/+/6592143
Build: srv_hbgsm110
diff --git a/target/linux/mediatek/patches-5.4/0001-cpufreq-add-the-missing-platform-driver-unregister.patch b/target/linux/mediatek/patches-5.4/0001-cpufreq-add-the-missing-platform-driver-unregister.patch
new file mode 100644
index 0000000..fdf953d
--- /dev/null
+++ b/target/linux/mediatek/patches-5.4/0001-cpufreq-add-the-missing-platform-driver-unregister.patch
@@ -0,0 +1,12 @@
+diff --git a/drivers/cpufreq/mediatek-cpufreq.c b/drivers/cpufreq/mediatek-cpufreq.c
+index 927ebc5..03bb7b5 100644
+--- a/drivers/cpufreq/mediatek-cpufreq.c
++++ b/drivers/cpufreq/mediatek-cpufreq.c
+@@ -573,6 +573,7 @@ static int __init mtk_cpufreq_driver_init(void)
+ 	pdev = platform_device_register_simple("mtk-cpufreq", -1, NULL, 0);
+ 	if (IS_ERR(pdev)) {
+ 		pr_err("failed to register mtk-cpufreq platform device\n");
++		platform_driver_unregister(&mtk_cpufreq_platdrv);
+ 		return PTR_ERR(pdev);
+ 	}
+ 
diff --git a/target/linux/mediatek/patches-5.4/0002-cpufreq-Enable-clocks-and-regulators.patch b/target/linux/mediatek/patches-5.4/0002-cpufreq-Enable-clocks-and-regulators.patch
new file mode 100644
index 0000000..2fa9359
--- /dev/null
+++ b/target/linux/mediatek/patches-5.4/0002-cpufreq-Enable-clocks-and-regulators.patch
@@ -0,0 +1,88 @@
+diff --git a/drivers/cpufreq/mediatek-cpufreq.c b/drivers/cpufreq/mediatek-cpufreq.c
+index 03bb7b5..010a947 100644
+--- a/drivers/cpufreq/mediatek-cpufreq.c
++++ b/drivers/cpufreq/mediatek-cpufreq.c
+@@ -351,6 +351,12 @@ static int mtk_cpu_dvfs_info_init(struct mtk_cpu_dvfs_info *info, int cpu)
+ 		goto out_free_resources;
+ 	}
+ 
++	ret = regulator_enable(proc_reg);
++	if (ret) {
++		dev_warn(cpu_dev, "cpu%d: failed to enable vproc\n", cpu);
++		goto out_free_resources;
++	}
++
+ 	/* Both presence and absence of sram regulator are valid cases. */
+ 	sram_reg = regulator_get_exclusive(cpu_dev, "sram");
+ 
+@@ -368,13 +374,21 @@ static int mtk_cpu_dvfs_info_init(struct mtk_cpu_dvfs_info *info, int cpu)
+ 		goto out_free_resources;
+ 	}
+ 
++	ret = clk_prepare_enable(cpu_clk);
++	if (ret)
++		goto out_free_opp_table;
++
++	ret = clk_prepare_enable(inter_clk);
++	if (ret)
++		goto out_disable_mux_clock;
++
+ 	/* Search a safe voltage for intermediate frequency. */
+ 	rate = clk_get_rate(inter_clk);
+ 	opp = dev_pm_opp_find_freq_ceil(cpu_dev, &rate);
+ 	if (IS_ERR(opp)) {
+ 		pr_err("failed to get intermediate opp for cpu%d\n", cpu);
+ 		ret = PTR_ERR(opp);
+-		goto out_free_opp_table;
++		goto out_disable_inter_clock;
+ 	}
+ 	info->intermediate_voltage = dev_pm_opp_get_voltage(opp);
+ 	dev_pm_opp_put(opp);
+@@ -393,10 +407,23 @@ static int mtk_cpu_dvfs_info_init(struct mtk_cpu_dvfs_info *info, int cpu)
+ 
+ 	return 0;
+ 
++out_disable_inter_clock:
++	if(!IS_ERR(inter_clk))
++		clk_disable_unprepare(inter_clk);
++
++out_disable_mux_clock:
++	if(!IS_ERR(cpu_clk))
++		clk_disable_unprepare(cpu_clk);
++
+ out_free_opp_table:
+ 	dev_pm_opp_of_cpumask_remove_table(&info->cpus);
+ 
+ out_free_resources:
++	if (!IS_ERR(proc_reg)) {
++		if (regulator_is_enabled(proc_reg))
++			regulator_disable(proc_reg);
++	}
++
+ 	if (!IS_ERR(proc_reg))
+ 		regulator_put(proc_reg);
+ 	if (!IS_ERR(sram_reg))
+@@ -411,14 +438,20 @@ out_free_resources:
+ 
+ static void mtk_cpu_dvfs_info_release(struct mtk_cpu_dvfs_info *info)
+ {
+-	if (!IS_ERR(info->proc_reg))
++	if (!IS_ERR(info->proc_reg)){
++		regulator_disable(info->proc_reg);
+ 		regulator_put(info->proc_reg);
++	}
+ 	if (!IS_ERR(info->sram_reg))
+ 		regulator_put(info->sram_reg);
+-	if (!IS_ERR(info->cpu_clk))
++	if (!IS_ERR(info->cpu_clk)){
++		clk_disable_unprepare(info->cpu_clk);
+ 		clk_put(info->cpu_clk);
+-	if (!IS_ERR(info->inter_clk))
++	}
++	if (!IS_ERR(info->inter_clk)){
++		clk_disable_unprepare(info->inter_clk);
+ 		clk_put(info->inter_clk);
++	}
+ 
+ 	dev_pm_opp_of_cpumask_remove_table(&info->cpus);
+ }
diff --git a/target/linux/mediatek/patches-5.4/0003-clk-mtk-add-mt7988-support.patch b/target/linux/mediatek/patches-5.4/0003-clk-mtk-add-mt7988-support.patch
new file mode 100644
index 0000000..e673148
--- /dev/null
+++ b/target/linux/mediatek/patches-5.4/0003-clk-mtk-add-mt7988-support.patch
@@ -0,0 +1,38 @@
+diff --git a/drivers/clk/mediatek/Kconfig b/drivers/clk/mediatek/Kconfig
+index 23393d5..cf3a53e 100644
+--- a/drivers/clk/mediatek/Kconfig
++++ b/drivers/clk/mediatek/Kconfig
+@@ -275,6 +275,14 @@ config COMMON_CLK_MT7981
+ 	  This driver supports MediaTek MT7981 basic clocks and clocks
+ 	  required for various periperals found on MediaTek.
+ 
++config COMMON_CLK_MT7988
++	bool "Clock driver for MediaTek MT7988"
++	depends on ARCH_MEDIATEK || COMPILE_TEST
++	select COMMON_CLK_MEDIATEK
++	---help---
++	  This driver supports MediaTek MT7988 basic clocks and clocks
++	  required for various periperals found on MediaTek.
++
+ config COMMON_CLK_MT8135
+ 	bool "Clock driver for MediaTek MT8135"
+ 	depends on (ARCH_MEDIATEK && ARM) || COMPILE_TEST
+diff --git a/drivers/clk/mediatek/Makefile b/drivers/clk/mediatek/Makefile
+index ffe0850..43ca85d 100644
+--- a/drivers/clk/mediatek/Makefile
++++ b/drivers/clk/mediatek/Makefile
+@@ -41,6 +41,7 @@ obj-$(CONFIG_COMMON_CLK_MT7629_ETHSYS) += clk-mt7629-eth.o
+ obj-$(CONFIG_COMMON_CLK_MT7629_HIFSYS) += clk-mt7629-hif.o
+ obj-$(CONFIG_COMMON_CLK_MT7986) += clk-mt7986.o
+ obj-$(CONFIG_COMMON_CLK_MT7981) += clk-mt7981.o
++obj-$(CONFIG_COMMON_CLK_MT7988) += clk-mt7988.o
+ obj-$(CONFIG_COMMON_CLK_MT8135) += clk-mt8135.o
+ obj-$(CONFIG_COMMON_CLK_MT8173) += clk-mt8173.o
+ obj-$(CONFIG_COMMON_CLK_MT8183) += clk-mt8183.o
+@@ -57,4 +58,4 @@ obj-$(CONFIG_COMMON_CLK_MT8183_VDECSYS) += clk-mt8183-vdec.o
+ obj-$(CONFIG_COMMON_CLK_MT8183_VENCSYS) += clk-mt8183-venc.o
+ obj-$(CONFIG_COMMON_CLK_MT8516) += clk-mt8516.o
+ obj-$(CONFIG_COMMON_CLK_MT8516_AUDSYS) += clk-mt8516-aud.o
+-obj-y += clk-bringup.o
+\ No newline at end of file
++obj-y += clk-bringup.o
diff --git a/target/linux/mediatek/patches-5.4/0003-cpufreq-add-mt7988a-spim-snand-support.patch b/target/linux/mediatek/patches-5.4/0003-cpufreq-add-mt7988a-spim-snand-support.patch
new file mode 100644
index 0000000..ee87f4e
--- /dev/null
+++ b/target/linux/mediatek/patches-5.4/0003-cpufreq-add-mt7988a-spim-snand-support.patch
@@ -0,0 +1,212 @@
+diff --git a/drivers/cpufreq/mediatek-cpufreq.c b/drivers/cpufreq/mediatek-cpufreq.c
+index 010a947..291f629 100644
+--- a/drivers/cpufreq/mediatek-cpufreq.c
++++ b/drivers/cpufreq/mediatek-cpufreq.c
+@@ -38,6 +38,7 @@ struct mtk_cpu_dvfs_info {
+ 	struct regulator *proc_reg;
+ 	struct regulator *sram_reg;
+ 	struct clk *cpu_clk;
++	struct clk *cci_clk;
+ 	struct clk *inter_clk;
+ 	struct list_head list_head;
+ 	int intermediate_voltage;
+@@ -205,15 +206,24 @@ static int mtk_cpufreq_set_target(struct cpufreq_policy *policy,
+ 	struct cpufreq_frequency_table *freq_table = policy->freq_table;
+ 	struct clk *cpu_clk = policy->clk;
+ 	struct clk *armpll = clk_get_parent(cpu_clk);
++	struct clk *cci_clk = ERR_PTR(-ENODEV);
++	struct clk *ccipll;
+ 	struct mtk_cpu_dvfs_info *info = policy->driver_data;
+ 	struct device *cpu_dev = info->cpu_dev;
+ 	struct dev_pm_opp *opp;
+-	long freq_hz, old_freq_hz;
++	long freq_hz, old_freq_hz, cci_freq_hz, cci_old_freq_hz;
+ 	int vproc, old_vproc, inter_vproc, target_vproc, ret;
+ 
+ 	inter_vproc = info->intermediate_voltage;
+ 
+ 	old_freq_hz = clk_get_rate(cpu_clk);
++
++	if (!IS_ERR(info->cci_clk)) {
++		cci_clk = info->cci_clk;
++		ccipll = clk_get_parent(cci_clk);
++		cci_old_freq_hz = clk_get_rate(cci_clk);
++	}
++
+ 	old_vproc = regulator_get_voltage(info->proc_reg);
+ 	if (old_vproc < 0) {
+ 		pr_err("%s: invalid Vproc value: %d\n", __func__, old_vproc);
+@@ -221,6 +231,7 @@ static int mtk_cpufreq_set_target(struct cpufreq_policy *policy,
+ 	}
+ 
+ 	freq_hz = freq_table[index].frequency * 1000;
++	cci_freq_hz = freq_table[index].frequency * 600;
+ 
+ 	opp = dev_pm_opp_find_freq_ceil(cpu_dev, &freq_hz);
+ 	if (IS_ERR(opp)) {
+@@ -246,6 +257,18 @@ static int mtk_cpufreq_set_target(struct cpufreq_policy *policy,
+ 		}
+ 	}
+ 
++	/* Reparent the CCI clock to intermediate clock. */
++	if (!IS_ERR(cci_clk)) {
++		ret = clk_set_parent(cci_clk, info->inter_clk);
++		if (ret) {
++			pr_err("cpu%d: failed to re-parent cci clock!\n",
++			       policy->cpu);
++			mtk_cpufreq_set_voltage(info, old_vproc);
++			WARN_ON(1);
++			return ret;
++		}
++	}
++
+ 	/* Reparent the CPU clock to intermediate clock. */
+ 	ret = clk_set_parent(cpu_clk, info->inter_clk);
+ 	if (ret) {
+@@ -266,6 +289,18 @@ static int mtk_cpufreq_set_target(struct cpufreq_policy *policy,
+ 		return ret;
+ 	}
+ 
++	/* Set the original PLL to target rate. */
++	if (!IS_ERR(cci_clk)) {
++		ret = clk_set_rate(ccipll, cci_freq_hz);
++		if (ret) {
++			pr_err("cpu%d: failed to scale cci clock rate!\n",
++			       policy->cpu);
++			clk_set_parent(cci_clk, ccipll);
++			mtk_cpufreq_set_voltage(info, old_vproc);
++			return ret;
++		}
++	}
++
+ 	/* Set parent of CPU clock back to the original PLL. */
+ 	ret = clk_set_parent(cpu_clk, armpll);
+ 	if (ret) {
+@@ -276,6 +311,17 @@ static int mtk_cpufreq_set_target(struct cpufreq_policy *policy,
+ 		return ret;
+ 	}
+ 
++	/* Set parent of CCI clock back to the original PLL. */
++	if (!IS_ERR(cci_clk)) {
++		ret = clk_set_parent(cci_clk, ccipll);
++		if (ret) {
++			pr_err("cpu%d: failed to re-parent cci clock!\n",
++			       policy->cpu);
++			mtk_cpufreq_set_voltage(info, inter_vproc);
++			WARN_ON(1);
++			return ret;
++		}
++	}
+ 	/*
+ 	 * If the new voltage is lower than the intermediate voltage or the
+ 	 * original voltage, scale down to the new voltage.
+@@ -285,9 +331,20 @@ static int mtk_cpufreq_set_target(struct cpufreq_policy *policy,
+ 		if (ret) {
+ 			pr_err("cpu%d: failed to scale down voltage!\n",
+ 			       policy->cpu);
++			if (!IS_ERR(cci_clk))
++				clk_set_parent(cci_clk, info->inter_clk);
++
+ 			clk_set_parent(cpu_clk, info->inter_clk);
+ 			clk_set_rate(armpll, old_freq_hz);
++
++			if (!IS_ERR(cci_clk))
++				clk_set_rate(ccipll, cci_old_freq_hz);
++
+ 			clk_set_parent(cpu_clk, armpll);
++
++			if (!IS_ERR(cci_clk))
++				clk_set_parent(cci_clk, ccipll);
++
+ 			return ret;
+ 		}
+ 	}
+@@ -303,6 +360,7 @@ static int mtk_cpu_dvfs_info_init(struct mtk_cpu_dvfs_info *info, int cpu)
+ 	struct regulator *proc_reg = ERR_PTR(-ENODEV);
+ 	struct regulator *sram_reg = ERR_PTR(-ENODEV);
+ 	struct clk *cpu_clk = ERR_PTR(-ENODEV);
++	struct clk *cci_clk = ERR_PTR(-ENODEV);
+ 	struct clk *inter_clk = ERR_PTR(-ENODEV);
+ 	struct dev_pm_opp *opp;
+ 	unsigned long rate;
+@@ -338,6 +396,8 @@ static int mtk_cpu_dvfs_info_init(struct mtk_cpu_dvfs_info *info, int cpu)
+ 		goto out_free_resources;
+ 	}
+ 
++	cci_clk = clk_get(cpu_dev, "cci");
++
+ 	proc_reg = regulator_get_optional(cpu_dev, "proc");
+ 	if (IS_ERR(proc_reg)) {
+ 		if (PTR_ERR(proc_reg) == -EPROBE_DEFER)
+@@ -379,16 +439,23 @@ static int mtk_cpu_dvfs_info_init(struct mtk_cpu_dvfs_info *info, int cpu)
+ 		goto out_free_opp_table;
+ 
+ 	ret = clk_prepare_enable(inter_clk);
++
+ 	if (ret)
+ 		goto out_disable_mux_clock;
+ 
++	if(!(IS_ERR(cci_clk))) {
++		ret = clk_prepare_enable(cci_clk);
++		if(ret)
++			goto out_disable_inter_clock;
++	}
++
+ 	/* Search a safe voltage for intermediate frequency. */
+ 	rate = clk_get_rate(inter_clk);
+ 	opp = dev_pm_opp_find_freq_ceil(cpu_dev, &rate);
+ 	if (IS_ERR(opp)) {
+ 		pr_err("failed to get intermediate opp for cpu%d\n", cpu);
+ 		ret = PTR_ERR(opp);
+-		goto out_disable_inter_clock;
++		goto out_disable_cci_clock;
+ 	}
+ 	info->intermediate_voltage = dev_pm_opp_get_voltage(opp);
+ 	dev_pm_opp_put(opp);
+@@ -397,6 +464,7 @@ static int mtk_cpu_dvfs_info_init(struct mtk_cpu_dvfs_info *info, int cpu)
+ 	info->proc_reg = proc_reg;
+ 	info->sram_reg = IS_ERR(sram_reg) ? NULL : sram_reg;
+ 	info->cpu_clk = cpu_clk;
++	info->cci_clk = cci_clk;
+ 	info->inter_clk = inter_clk;
+ 
+ 	/*
+@@ -407,6 +475,10 @@ static int mtk_cpu_dvfs_info_init(struct mtk_cpu_dvfs_info *info, int cpu)
+ 
+ 	return 0;
+ 
++out_disable_cci_clock:
++	if(!IS_ERR(cci_clk))
++		clk_disable_unprepare(cci_clk);
++
+ out_disable_inter_clock:
+ 	if(!IS_ERR(inter_clk))
+ 		clk_disable_unprepare(inter_clk);
+@@ -432,6 +504,8 @@ out_free_resources:
+ 		clk_put(cpu_clk);
+ 	if (!IS_ERR(inter_clk))
+ 		clk_put(inter_clk);
++	if (!IS_ERR(cci_clk))
++		clk_put(cci_clk);
+ 
+ 	return ret;
+ }
+@@ -452,6 +526,10 @@ static void mtk_cpu_dvfs_info_release(struct mtk_cpu_dvfs_info *info)
+ 		clk_disable_unprepare(info->inter_clk);
+ 		clk_put(info->inter_clk);
+ 	}
++	if (!IS_ERR(info->cci_clk)){
++		clk_disable_unprepare(info->cci_clk);
++		clk_put(info->cci_clk);
++	}
+ 
+ 	dev_pm_opp_of_cpumask_remove_table(&info->cpus);
+ }
+@@ -570,6 +648,7 @@ static const struct of_device_id mtk_cpufreq_machines[] __initconst = {
+ 	{ .compatible = "mediatek,mt8176", },
+ 	{ .compatible = "mediatek,mt8183", },
+ 	{ .compatible = "mediatek,mt8516", },
++	{ .compatible = "mediatek,mt7988", },
+ 
+ 	{ }
+ };
diff --git a/target/linux/mediatek/patches-5.4/0005-clk-mtk-add-chg-shift-control.patch b/target/linux/mediatek/patches-5.4/0005-clk-mtk-add-chg-shift-control.patch
new file mode 100644
index 0000000..4a9ff6f
--- /dev/null
+++ b/target/linux/mediatek/patches-5.4/0005-clk-mtk-add-chg-shift-control.patch
@@ -0,0 +1,28 @@
+diff --git a/drivers/clk/mediatek/clk-mtk.h b/drivers/clk/mediatek/clk-mtk.h
+index c3d6756..d84c45d 100644
+--- a/drivers/clk/mediatek/clk-mtk.h
++++ b/drivers/clk/mediatek/clk-mtk.h
+@@ -231,6 +231,7 @@ struct mtk_pll_data {
+ 	uint32_t pcw_reg;
+ 	int pcw_shift;
+ 	uint32_t pcw_chg_reg;
++	int pcw_chg_shift;
+ 	const struct mtk_pll_div_table *div_table;
+ 	const char *parent_name;
+ };
+diff --git a/drivers/clk/mediatek/clk-pll.c b/drivers/clk/mediatek/clk-pll.c
+index f440f2c..db318fe 100644
+--- a/drivers/clk/mediatek/clk-pll.c
++++ b/drivers/clk/mediatek/clk-pll.c
+@@ -136,7 +136,10 @@ static void mtk_pll_set_rate_regs(struct mtk_clk_pll *pll, u32 pcw,
+ 			pll->data->pcw_shift);
+ 	val |= pcw << pll->data->pcw_shift;
+ 	writel(val, pll->pcw_addr);
+-	chg = readl(pll->pcw_chg_addr) | PCW_CHG_MASK;
++	if (pll->data->pcw_chg_shift)
++		chg = readl(pll->pcw_chg_addr) | BIT(pll->data->pcw_chg_shift);
++	else
++		chg = readl(pll->pcw_chg_addr) | PCW_CHG_MASK;
+ 	writel(chg, pll->pcw_chg_addr);
+ 	if (pll->tuner_addr)
+ 		writel(val + 1, pll->tuner_addr);
diff --git a/target/linux/mediatek/patches-5.4/0006-powerdomain-add-mt7988-support.patch b/target/linux/mediatek/patches-5.4/0006-powerdomain-add-mt7988-support.patch
new file mode 100644
index 0000000..7fc4f1d
--- /dev/null
+++ b/target/linux/mediatek/patches-5.4/0006-powerdomain-add-mt7988-support.patch
@@ -0,0 +1,9 @@
+diff --git a/drivers/soc/mediatek/Makefile b/drivers/soc/mediatek/Makefile
+index b017330..1c485e3 100644
+--- a/drivers/soc/mediatek/Makefile
++++ b/drivers/soc/mediatek/Makefile
+@@ -3,3 +3,4 @@ obj-$(CONFIG_MTK_CMDQ) += mtk-cmdq-helper.o
+ obj-$(CONFIG_MTK_INFRACFG) += mtk-infracfg.o
+ obj-$(CONFIG_MTK_PMIC_WRAP) += mtk-pmic-wrap.o
+ obj-$(CONFIG_MTK_SCPSYS) += mtk-scpsys.o
++obj-$(CONFIG_MTK_SCPSYS) += mtk-pm-domains.o
diff --git a/target/linux/mediatek/patches-5.4/0007-cpufreq-mtk-vbining-add-mt7988-support.patch b/target/linux/mediatek/patches-5.4/0007-cpufreq-mtk-vbining-add-mt7988-support.patch
new file mode 100644
index 0000000..8e99118
--- /dev/null
+++ b/target/linux/mediatek/patches-5.4/0007-cpufreq-mtk-vbining-add-mt7988-support.patch
@@ -0,0 +1,47 @@
+diff --git a/drivers/cpufreq/mediatek-cpufreq.c b/drivers/cpufreq/mediatek-cpufreq.c
+index b23b6d2..147a224 100644
+--- a/drivers/cpufreq/mediatek-cpufreq.c
++++ b/drivers/cpufreq/mediatek-cpufreq.c
+@@ -15,6 +15,7 @@
+ #include <linux/regulator/consumer.h>
+ #include <linux/slab.h>
+ #include <linux/thermal.h>
++#include <linux/nvmem-consumer.h>
+ 
+ #define MIN_VOLT_SHIFT		(100000)
+ #define MAX_VOLT_SHIFT		(200000)
+@@ -539,6 +540,11 @@ static int mtk_cpufreq_init(struct cpufreq_policy *policy)
+ 	struct mtk_cpu_dvfs_info *info;
+ 	struct cpufreq_frequency_table *freq_table;
+ 	int ret;
++	int target_vproc;
++	u32 reg_val;
++	struct nvmem_cell *cell;
++	size_t len;
++	u32 *buf;
+ 
+ 	info = mtk_cpu_dvfs_info_lookup(policy->cpu);
+ 	if (!info) {
+@@ -547,6 +553,22 @@ static int mtk_cpufreq_init(struct cpufreq_policy *policy)
+ 		return -EINVAL;
+ 	}
+ 
++	cell = nvmem_cell_get(info->cpu_dev, "calibration-data");
++	if (!IS_ERR(cell)) {
++		buf = (u32 *)nvmem_cell_read(cell, &len);
++		nvmem_cell_put(cell);
++		if (!IS_ERR(buf)) {
++			reg_val = buf[0] & 0x1f;
++			pr_debug("%s: read vbinning value: %d\n", __func__, reg_val);
++			if (reg_val > 0) {
++				target_vproc = 850000 + reg_val * 10000;
++				dev_pm_opp_remove(info->cpu_dev, 1800000000);
++				dev_pm_opp_add(info->cpu_dev, 1800000000, target_vproc);
++			}
++			kfree(buf);
++		}
++	}
++
+ 	ret = dev_pm_opp_init_cpufreq_table(info->cpu_dev, &freq_table);
+ 	if (ret) {
+ 		pr_err("failed to init cpufreq table for cpu%d: %d\n",
diff --git a/target/linux/mediatek/patches-5.4/0932-add-pwm-feature-in-mt7988-project.patch b/target/linux/mediatek/patches-5.4/0932-add-pwm-feature-in-mt7988-project.patch
new file mode 100644
index 0000000..8268e7d
--- /dev/null
+++ b/target/linux/mediatek/patches-5.4/0932-add-pwm-feature-in-mt7988-project.patch
@@ -0,0 +1,25 @@
+diff --git a/drivers/pwm/pwm-mediatek.c b/drivers/pwm/pwm-mediatek.c
+index 3a5a456..6d6206e 100644
+--- a/drivers/pwm/pwm-mediatek.c
++++ b/drivers/pwm/pwm-mediatek.c
+@@ -350,6 +350,12 @@ static const struct pwm_mediatek_of_data mt7986_pwm_data = {
+ 	.reg_ver = REG_V2,
+ };
+ 
++static const struct pwm_mediatek_of_data mt7988_pwm_data = {
++	.num_pwms = 8,
++	.pwm45_fixup = false,
++	.reg_ver = REG_V2,
++};
++
+ static const struct pwm_mediatek_of_data mt8516_pwm_data = {
+ 	.num_pwms = 5,
+ 	.pwm45_fixup = false,
+@@ -364,6 +370,7 @@ static const struct of_device_id pwm_mediatek_of_match[] = {
+ 	{ .compatible = "mediatek,mt7629-pwm", .data = &mt7629_pwm_data },
+ 	{ .compatible = "mediatek,mt7981-pwm", .data = &mt7981_pwm_data },
+ 	{ .compatible = "mediatek,mt7986-pwm", .data = &mt7986_pwm_data },
++	{ .compatible = "mediatek,mt7988-pwm", .data = &mt7988_pwm_data },
+ 	{ .compatible = "mediatek,mt8516-pwm", .data = &mt8516_pwm_data },
+ 	{ },
+ };
diff --git a/target/linux/mediatek/patches-5.4/0950-add-pmic-config.patch b/target/linux/mediatek/patches-5.4/0950-add-pmic-config.patch
new file mode 100644
index 0000000..f5384f3
--- /dev/null
+++ b/target/linux/mediatek/patches-5.4/0950-add-pmic-config.patch
@@ -0,0 +1,36 @@
+diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
+index 3ee6353..2e393a7 100644
+--- a/drivers/regulator/Kconfig
++++ b/drivers/regulator/Kconfig
+@@ -798,6 +798,16 @@ config REGULATOR_RT5033
+ 	  RT5033 PMIC. The device supports multiple regulators like
+ 	  current source, LDO and Buck.
+ 
++config REGULATOR_RT5190A
++	tristate "Richtek RT5190A PMIC"
++	depends on I2C
++       select REGMAP_I2C
++	help
++	  This add support for voltage regulator in Ritchtek RT5190A PMIC.
++	  It integrates 1 channel buck controller, 3 channels high efficiency
++	  buck converters, 1 LDO, mute AC OFF depop function, with the general
++	  I2C control interface.
++
+ config REGULATOR_S2MPA01
+ 	tristate "Samsung S2MPA01 voltage regulator"
+ 	depends on MFD_SEC_CORE
+diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
+index 2210ba5..bf75b77 100644
+--- a/drivers/regulator/Makefile
++++ b/drivers/regulator/Makefile
+@@ -100,6 +100,7 @@ obj-$(CONFIG_REGULATOR_RC5T583)  += rc5t583-regulator.o
+ obj-$(CONFIG_REGULATOR_RK808)   += rk808-regulator.o
+ obj-$(CONFIG_REGULATOR_RN5T618) += rn5t618-regulator.o
+ obj-$(CONFIG_REGULATOR_RT5033)	+= rt5033-regulator.o
++obj-$(CONFIG_REGULATOR_RT5190A) += rt5190a-regulator.o
+ obj-$(CONFIG_REGULATOR_S2MPA01) += s2mpa01.o
+ obj-$(CONFIG_REGULATOR_S2MPS11) += s2mps11.o
+ obj-$(CONFIG_REGULATOR_S5M8767) += s5m8767.o
+-- 
+2.18.0
+
diff --git a/target/linux/mediatek/patches-5.4/1662-trng-Add-trng-support-for-mt7988.patch b/target/linux/mediatek/patches-5.4/1662-trng-Add-trng-support-for-mt7988.patch
new file mode 100644
index 0000000..a8f1dfe
--- /dev/null
+++ b/target/linux/mediatek/patches-5.4/1662-trng-Add-trng-support-for-mt7988.patch
@@ -0,0 +1,38 @@
+From cbd37bfc8221c1a81d235ddfb1898536a821c650 Mon Sep 17 00:00:00 2001
+From: "mingming.su" <Mingming.Su@mediatek.com>
+Date: Wed, 7 Sep 2022 15:44:46 +0800
+Subject: [PATCH] trng: Add trng support for mt7988
+
+Add trng support for mt7988.
+
+Signed-off-by: mingming.su <Mingming.Su@mediatek.com>
+---
+ drivers/char/hw_random/mtk-rng.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/drivers/char/hw_random/mtk-rng.c b/drivers/char/hw_random/mtk-rng.c
+index 75fca4cef..878170c15 100644
+--- a/drivers/char/hw_random/mtk-rng.c
++++ b/drivers/char/hw_random/mtk-rng.c
+@@ -231,6 +231,10 @@ static const struct mtk_rng_of_data mt7986_rng_data = {
+ 	.rng_version = 1,
+ };
+ 
++static const struct mtk_rng_of_data mt7988_rng_data = {
++	.rng_version = 2,
++};
++
+ static const struct mtk_rng_of_data mt7623_rng_data = {
+ 	.rng_version = 1,
+ };
+@@ -238,6 +242,7 @@ static const struct mtk_rng_of_data mt7623_rng_data = {
+ static const struct of_device_id mtk_rng_match[] = {
+ 	{ .compatible = "mediatek,mt7981-rng", .data = &mt7981_rng_data },
+ 	{ .compatible = "mediatek,mt7986-rng", .data = &mt7986_rng_data },
++	{ .compatible = "mediatek,mt7988-rng", .data = &mt7988_rng_data },
+ 	{ .compatible = "mediatek,mt7623-rng", .data = &mt7623_rng_data },
+ 	{},
+ };
+-- 
+2.18.0
+
diff --git a/target/linux/mediatek/patches-5.4/401-pinctrl-enable-mt7988-pinctrl-config.patch b/target/linux/mediatek/patches-5.4/401-pinctrl-enable-mt7988-pinctrl-config.patch
new file mode 100644
index 0000000..01f01f6
--- /dev/null
+++ b/target/linux/mediatek/patches-5.4/401-pinctrl-enable-mt7988-pinctrl-config.patch
@@ -0,0 +1,30 @@
+diff --git a/drivers/pinctrl/mediatek/Kconfig b/drivers/pinctrl/mediatek/Kconfig
+index e7ec276..b6341dd 100644
+--- a/drivers/pinctrl/mediatek/Kconfig
++++ b/drivers/pinctrl/mediatek/Kconfig
+@@ -112,6 +112,13 @@ config PINCTRL_MT7986
+ 	default ARM64 && ARCH_MEDIATEK
+ 	select PINCTRL_MTK_MOORE
+ 
++config PINCTRL_MT7988
++        bool "Mediatek MT7988 pin control"
++        depends on OF
++        depends on ARM64 || COMPILE_TEST
++        default ARM64 && ARCH_MEDIATEK
++        select PINCTRL_MTK_MOORE
++
+ config PINCTRL_MT8173
+ 	bool "Mediatek MT8173 pin control"
+ 	depends on OF
+diff --git a/drivers/pinctrl/mediatek/Makefile b/drivers/pinctrl/mediatek/Makefile
+index e6813cf..6e28df9 100644
+--- a/drivers/pinctrl/mediatek/Makefile
++++ b/drivers/pinctrl/mediatek/Makefile
+@@ -17,6 +17,7 @@ obj-$(CONFIG_PINCTRL_MT7623)	+= pinctrl-mt7623.o
+ obj-$(CONFIG_PINCTRL_MT7629)	+= pinctrl-mt7629.o
+ obj-$(CONFIG_PINCTRL_MT7981)	+= pinctrl-mt7981.o
+ obj-$(CONFIG_PINCTRL_MT7986)	+= pinctrl-mt7986.o
++obj-$(CONFIG_PINCTRL_MT7988)    += pinctrl-mt7988.o
+ obj-$(CONFIG_PINCTRL_MT8173)	+= pinctrl-mt8173.o
+ obj-$(CONFIG_PINCTRL_MT8183)	+= pinctrl-mt8183.o
+ obj-$(CONFIG_PINCTRL_MT8516)	+= pinctrl-mt8516.o
diff --git a/target/linux/mediatek/patches-5.4/6001-mtk-thermal-add-lvts-support.patch b/target/linux/mediatek/patches-5.4/6001-mtk-thermal-add-lvts-support.patch
new file mode 100644
index 0000000..1591144
--- /dev/null
+++ b/target/linux/mediatek/patches-5.4/6001-mtk-thermal-add-lvts-support.patch
@@ -0,0 +1,28 @@
+diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
+index 001a21ab..67d3da48 100644
+--- a/drivers/thermal/Kconfig
++++ b/drivers/thermal/Kconfig
+@@ -348,6 +348,11 @@ config MTK_THERMAL
+ 	  Enable this option if you want to have support for thermal management
+ 	  controller present in Mediatek SoCs
+ 
++menu "Mediatek thermal drivers"
++depends on ARCH_MEDIATEK || COMPILE_TEST
++source "drivers/thermal/mediatek/Kconfig"
++endmenu
++
+ menu "Intel thermal drivers"
+ depends on X86 || X86_INTEL_QUARK || COMPILE_TEST
+ source "drivers/thermal/intel/Kconfig"
+diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
+index 74a37c7f..6be9ff19 100644
+--- a/drivers/thermal/Makefile
++++ b/drivers/thermal/Makefile
+@@ -51,6 +51,7 @@ obj-$(CONFIG_QCOM_TSENS)	+= qcom/
+ obj-y				+= tegra/
+ obj-$(CONFIG_HISI_THERMAL)     += hisi_thermal.o
+ obj-$(CONFIG_MTK_THERMAL)	+= mtk_thermal.o
++obj-y				+= mediatek/
+ obj-$(CONFIG_GENERIC_ADC_THERMAL)	+= thermal-generic-adc.o
+ obj-$(CONFIG_ZX2967_THERMAL)	+= zx2967_thermal.o
+ obj-$(CONFIG_UNIPHIER_THERMAL)	+= uniphier_thermal.o
diff --git a/target/linux/mediatek/patches-5.4/746-add-mediatek-2p5ge-phy-support.patch b/target/linux/mediatek/patches-5.4/746-add-mediatek-2p5ge-phy-support.patch
new file mode 100644
index 0000000..a102660
--- /dev/null
+++ b/target/linux/mediatek/patches-5.4/746-add-mediatek-2p5ge-phy-support.patch
@@ -0,0 +1,24 @@
+--- a/drivers/net/phy/Kconfig
++++ b/drivers/net/phy/Kconfig
+@@ -522,6 +522,11 @@ config MEDIATEK_GE_PHY
+ 	help
+ 	  Supports the MediaTek Gigabit Ethernet PHYs.
+ 
++config MEDIATEK_2P5GE_PHY
++	tristate "MediaTek 2.5Gb Ethernet PHYs"
++	---help---
++	  Supports MediaTek internal 2.5Gb Ethernet PHYs.
++
+ config MICREL_PHY
+ 	tristate "Micrel PHYs"
+ 	---help---
+--- a/drivers/net/phy/Makefile
++++ b/drivers/net/phy/Makefile
+@@ -95,6 +95,7 @@ obj-$(CONFIG_LXT_PHY)		+= lxt.o
+ obj-$(CONFIG_MARVELL_PHY)	+= marvell.o
+ obj-$(CONFIG_MARVELL_10G_PHY)	+= marvell10g.o
+ obj-$(CONFIG_MEDIATEK_GE_PHY)	+= mediatek-ge.o
++obj-$(CONFIG_MEDIATEK_2P5GE_PHY)+= mediatek-2p5ge.o
+ obj-$(CONFIG_MESON_GXL_PHY)	+= meson-gxl.o
+ obj-$(CONFIG_MICREL_KS8995MA)	+= spi_ks8995.o
+ obj-$(CONFIG_MICREL_PHY)	+= micrel.o
diff --git a/target/linux/mediatek/patches-5.4/748-add-netlink-support-for-dsa.patch b/target/linux/mediatek/patches-5.4/748-add-netlink-support-for-dsa.patch
new file mode 100644
index 0000000..8853324
--- /dev/null
+++ b/target/linux/mediatek/patches-5.4/748-add-netlink-support-for-dsa.patch
@@ -0,0 +1,498 @@
+Index: linux-5.4.203/drivers/net/dsa/Makefile
+===================================================================
+--- linux-5.4.203.orig/drivers/net/dsa/Makefile
++++ linux-5.4.203/drivers/net/dsa/Makefile
+@@ -7,7 +7,7 @@ obj-$(CONFIG_FIXED_PHY)		+= dsa_loop_bdi
+ endif
+ obj-$(CONFIG_NET_DSA_LANTIQ_GSWIP) += lantiq_gswip.o
+ obj-$(CONFIG_NET_DSA_MT7530)	+= mt7530-dsa.o
+-mt7530-dsa-objs			:= mt7530.o mt7531_phy.o
++mt7530-dsa-objs			:= mt7530.o mt7530_nl.o mt7531_phy.o
+ obj-$(CONFIG_NET_DSA_MV88E6060) += mv88e6060.o
+ obj-$(CONFIG_NET_DSA_QCA8K)	+= qca8k.o
+ obj-$(CONFIG_NET_DSA_REALTEK_SMI) += realtek-smi.o
+Index: linux-5.4.203/drivers/net/dsa/mt7530.c
+===================================================================
+--- linux-5.4.203.orig/drivers/net/dsa/mt7530.c
++++ linux-5.4.203/drivers/net/dsa/mt7530.c
+@@ -21,6 +21,7 @@
+ #include <net/dsa.h>
+
+ #include "mt7530.h"
++#include "mt7530_nl.h"
+
+ /* String, offset, and register size in bytes if different from 4 bytes */
+ static const struct mt7530_mib_desc mt7530_mib[] = {
+@@ -222,7 +223,7 @@ mt7530_mii_read(struct mt7530_priv *priv
+ 	return (hi << 16) | (lo & 0xffff);
+ }
+
+-static void
++void
+ mt7530_write(struct mt7530_priv *priv, u32 reg, u32 val)
+ {
+ 	struct mii_bus *bus = priv->bus;
+@@ -255,7 +256,7 @@ _mt7530_read(struct mt7530_dummy_poll *p
+ 	return val;
+ }
+
+-static u32
++u32
+ mt7530_read(struct mt7530_priv *priv, u32 reg)
+ {
+ 	struct mt7530_dummy_poll p;
+@@ -614,7 +615,7 @@ static int mt7530_phy_write(struct dsa_s
+ 	return mdiobus_write_nested(priv->bus, port, regnum, val);
+ }
+
+-static int
++int
+ mt7531_ind_c45_phy_read(struct mt7530_priv *priv, int port, int devad,
+ 			int regnum)
+ {
+@@ -663,7 +664,7 @@ out:
+ 	return ret;
+ }
+
+-static int
++int
+ mt7531_ind_c45_phy_write(struct mt7530_priv *priv, int port, int devad,
+ 			 int regnum, u32 data)
+ {
+@@ -711,7 +712,7 @@ out:
+ 	return ret;
+ }
+
+-static int
++int
+ mt7531_ind_c22_phy_read(struct mt7530_priv *priv, int port, int regnum)
+ {
+ 	struct mii_bus *bus = priv->bus;
+@@ -749,7 +750,7 @@ out:
+ 	return ret;
+ }
+
+-static int
++int
+ mt7531_ind_c22_phy_write(struct mt7530_priv *priv, int port, int regnum,
+ 			 u16 data)
+ {
+@@ -2690,6 +2691,7 @@ mt7530_probe(struct mdio_device *mdiodev
+ {
+ 	struct mt7530_priv *priv;
+ 	struct device_node *dn;
++	int ret;
+
+ 	dn = mdiodev->dev.of_node;
+
+@@ -2765,7 +2767,13 @@ mt7530_probe(struct mdio_device *mdiodev
+ 	mutex_init(&priv->reg_mutex);
+ 	dev_set_drvdata(&mdiodev->dev, priv);
+
+-	return dsa_register_switch(priv->ds);
++	ret = dsa_register_switch(priv->ds);
++	if (ret)
++		return ret;
++
++	mt7530_nl_init(&priv);
++
++	return 0;
+ }
+
+ static void
+@@ -2786,6 +2794,8 @@ mt7530_remove(struct mdio_device *mdiode
+
+ 	dsa_unregister_switch(priv->ds);
+ 	mutex_destroy(&priv->reg_mutex);
++
++	mt7530_nl_exit();
+ }
+
+ static struct mdio_driver mt7530_mdio_driver = {
+Index: linux-5.4.203/drivers/net/dsa/mt7530.h
+===================================================================
+--- linux-5.4.203.orig/drivers/net/dsa/mt7530.h
++++ linux-5.4.203/drivers/net/dsa/mt7530.h
+@@ -783,4 +783,12 @@ static inline void INIT_MT7530_DUMMY_POL
+ }
+
+ int mt7531_phy_setup(struct dsa_switch *ds);
++u32 mt7530_read(struct mt7530_priv *priv, u32 reg);
++void mt7530_write(struct mt7530_priv *priv, u32 reg, u32 val);
++int mt7531_ind_c45_phy_read(struct mt7530_priv *priv, int port, int devad, int regnum);
++int mt7531_ind_c45_phy_write(struct mt7530_priv *priv, int port, int devad, int regnum, u32 data);
++int mt7531_ind_c22_phy_read(struct mt7530_priv *priv, int port, int regnum);
++int mt7531_ind_c22_phy_write(struct mt7530_priv *priv, int port, int regnum, u16 data);
++
++
+ #endif /* __MT7530_H */
+Index: linux-5.4.203/drivers/net/dsa/mt7530_nl.c
+===================================================================
+--- /dev/null
++++ linux-5.4.203/drivers/net/dsa/mt7530_nl.c
+@@ -0,0 +1,311 @@
++// SPDX-License-Identifier: GPL-2.0

++/*

++ * Copyright (c) 2018 MediaTek Inc.

++ * Author: Sirui Zhao <Sirui.Zhao@mediatek.com>

++ */

++

++#include <linux/types.h>

++#include <linux/kernel.h>

++#include <linux/module.h>

++#include <linux/init.h>

++#include <net/genetlink.h>

++#include <linux/of_mdio.h>

++#include <linux/phylink.h>

++#include <net/dsa.h>

++

++#include "mt7530.h"

++#include "mt7530_nl.h"

++

++struct mt7530_nl_cmd_item {

++	enum mt7530_cmd cmd;

++	bool require_dev;

++	int (*process)(struct genl_info *info);

++	u32 nr_required_attrs;

++	const enum mt7530_attr *required_attrs;

++};

++

++struct mt7530_priv *sw_priv;

++

++static DEFINE_MUTEX(mt7530_devs_lock);

++

++void mt7530_put(void)

++{

++	mutex_unlock(&mt7530_devs_lock);

++}

++

++void mt7530_lock(void)

++{

++	mutex_lock(&mt7530_devs_lock);

++}

++

++static int mt7530_nl_response(struct sk_buff *skb, struct genl_info *info);

++

++static const struct nla_policy mt7530_nl_cmd_policy[] = {

++	[MT7530_ATTR_TYPE_MESG] = { .type = NLA_STRING },

++	[MT7530_ATTR_TYPE_PHY] = { .type = NLA_S32 },

++	[MT7530_ATTR_TYPE_REG] = { .type = NLA_S32 },

++	[MT7530_ATTR_TYPE_VAL] = { .type = NLA_S32 },

++	[MT7530_ATTR_TYPE_DEV_NAME] = { .type = NLA_S32 },

++	[MT7530_ATTR_TYPE_DEV_ID] = { .type = NLA_S32 },

++	[MT7530_ATTR_TYPE_DEVAD] = { .type = NLA_S32 },

++};

++

++static const struct genl_ops mt7530_nl_ops[] = {

++	{

++		.cmd = MT7530_CMD_REQUEST,

++		.doit = mt7530_nl_response,

++		.flags = GENL_ADMIN_PERM,

++	}, {

++		.cmd = MT7530_CMD_READ,

++		.doit = mt7530_nl_response,

++		.flags = GENL_ADMIN_PERM,

++	}, {

++		.cmd = MT7530_CMD_WRITE,

++		.doit = mt7530_nl_response,

++		.flags = GENL_ADMIN_PERM,

++	},

++};

++

++static struct genl_family mt7530_nl_family = {

++	.name =		MT7530_DSA_GENL_NAME,

++	.version =	MT7530_GENL_VERSION,

++	.maxattr =	MT7530_NR_ATTR_TYPE,

++	.ops =		mt7530_nl_ops,

++	.n_ops =	ARRAY_SIZE(mt7530_nl_ops),

++	.policy =	mt7530_nl_cmd_policy,

++};

++

++static int mt7530_nl_prepare_reply(struct genl_info *info, u8 cmd,

++				   struct sk_buff **skbp)

++{

++	struct sk_buff *msg;

++	void *reply;

++

++	if (!info)

++		return -EINVAL;

++

++	msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);

++	if (!msg)

++		return -ENOMEM;

++

++	/* Construct send-back message header */

++	reply = genlmsg_put(msg, info->snd_portid, info->snd_seq,

++			    &mt7530_nl_family, 0, cmd);

++	if (!reply) {

++		nlmsg_free(msg);

++		return -EINVAL;

++	}

++

++	*skbp = msg;

++	return 0;

++}

++

++static int mt7530_nl_send_reply(struct sk_buff *skb, struct genl_info *info)

++{

++	struct genlmsghdr *genlhdr = nlmsg_data(nlmsg_hdr(skb));

++	void *reply = genlmsg_data(genlhdr);

++

++	/* Finalize a generic netlink message (update message header) */

++	genlmsg_end(skb, reply);

++

++	/* reply to a request */

++	return genlmsg_reply(skb, info);

++}

++

++static s32 mt7530_nl_get_s32(struct genl_info *info, enum mt7530_attr attr,

++			     s32 defval)

++{

++	struct nlattr *na;

++

++	na = info->attrs[attr];

++	if (na)

++		return nla_get_s32(na);

++

++	return defval;

++}

++

++static int mt7530_nl_get_u32(struct genl_info *info, enum mt7530_attr attr,

++			     u32 *val)

++{

++	struct nlattr *na;

++

++	na = info->attrs[attr];

++	if (na) {

++		*val = nla_get_u32(na);

++		return 0;

++	}

++

++	return -1;

++}

++

++static int mt7530_nl_reply_read(struct genl_info *info)

++{

++	struct sk_buff *rep_skb = NULL;

++	s32 phy, devad, reg;

++	int value;

++	int ret = 0;

++

++	phy = mt7530_nl_get_s32(info, MT7530_ATTR_TYPE_PHY, -1);

++	devad = mt7530_nl_get_s32(info, MT7530_ATTR_TYPE_DEVAD, -1);

++	reg = mt7530_nl_get_s32(info, MT7530_ATTR_TYPE_REG, -1);

++

++	if (reg < 0)

++		goto err;

++

++	ret = mt7530_nl_prepare_reply(info, MT7530_CMD_READ, &rep_skb);

++	if (ret < 0)

++		goto err;

++

++	if (phy >= 0) {

++		if (devad < 0)

++			value = mt7531_ind_c22_phy_read(sw_priv, phy, reg);

++		else

++			value = mt7531_ind_c45_phy_read(sw_priv, phy, devad, reg);

++	} else

++		value = mt7530_read(sw_priv, reg);

++

++	ret = nla_put_s32(rep_skb, MT7530_ATTR_TYPE_REG, reg);

++	if (ret < 0)

++		goto err;

++

++	ret = nla_put_s32(rep_skb, MT7530_ATTR_TYPE_VAL, value);

++	if (ret < 0)

++		goto err;

++

++	return mt7530_nl_send_reply(rep_skb, info);

++

++err:

++	if (rep_skb)

++		nlmsg_free(rep_skb);

++

++	return ret;

++}

++

++static int mt7530_nl_reply_write(struct genl_info *info)

++{

++	struct sk_buff *rep_skb = NULL;

++	s32 phy, devad, reg;

++	u32 value;

++	int ret = 0;

++

++	phy = mt7530_nl_get_s32(info, MT7530_ATTR_TYPE_PHY, -1);

++	devad = mt7530_nl_get_s32(info, MT7530_ATTR_TYPE_DEVAD, -1);

++	reg = mt7530_nl_get_s32(info, MT7530_ATTR_TYPE_REG, -1);

++

++	if (mt7530_nl_get_u32(info, MT7530_ATTR_TYPE_VAL, &value))

++		goto err;

++

++	if (reg < 0)

++		goto err;

++

++	ret = mt7530_nl_prepare_reply(info, MT7530_CMD_WRITE, &rep_skb);

++	if (ret < 0)

++		goto err;

++

++	if (phy >= 0) {

++		if (devad < 0)

++			mt7531_ind_c22_phy_write(sw_priv, phy, reg, value);

++		else

++			mt7531_ind_c45_phy_write(sw_priv, phy, devad, reg, value);

++	} else

++		mt7530_write(sw_priv, reg, value);

++

++	ret = nla_put_s32(rep_skb, MT7530_ATTR_TYPE_REG, reg);

++	if (ret < 0)

++		goto err;

++

++	ret = nla_put_s32(rep_skb, MT7530_ATTR_TYPE_VAL, value);

++	if (ret < 0)

++		goto err;

++

++	return mt7530_nl_send_reply(rep_skb, info);

++

++err:

++	if (rep_skb)

++		nlmsg_free(rep_skb);

++

++	return ret;

++}

++

++static const enum mt7530_attr mt7530_nl_cmd_read_attrs[] = {

++	MT7530_ATTR_TYPE_REG

++};

++

++static const enum mt7530_attr mt7530_nl_cmd_write_attrs[] = {

++	MT7530_ATTR_TYPE_REG,

++	MT7530_ATTR_TYPE_VAL

++};

++

++static const struct mt7530_nl_cmd_item mt7530_nl_cmds[] = {

++	{

++		.cmd = MT7530_CMD_READ,

++		.require_dev = true,

++		.process = mt7530_nl_reply_read,

++		.required_attrs = mt7530_nl_cmd_read_attrs,

++		.nr_required_attrs = ARRAY_SIZE(mt7530_nl_cmd_read_attrs),

++	}, {

++		.cmd = MT7530_CMD_WRITE,

++		.require_dev = true,

++		.process = mt7530_nl_reply_write,

++		.required_attrs = mt7530_nl_cmd_write_attrs,

++		.nr_required_attrs = ARRAY_SIZE(mt7530_nl_cmd_write_attrs),

++	}

++};

++

++static int mt7530_nl_response(struct sk_buff *skb, struct genl_info *info)

++{

++	struct genlmsghdr *hdr = nlmsg_data(info->nlhdr);

++	const struct mt7530_nl_cmd_item *cmditem = NULL;

++	u32 sat_req_attrs = 0;

++	int i, ret;

++

++	for (i = 0; i < ARRAY_SIZE(mt7530_nl_cmds); i++) {

++		if (hdr->cmd == mt7530_nl_cmds[i].cmd) {

++			cmditem = &mt7530_nl_cmds[i];

++			break;

++		}

++	}

++

++	if (!cmditem) {

++		pr_info("mt7530-nl: unknown cmd %u\n", hdr->cmd);

++		return -EINVAL;

++	}

++

++	for (i = 0; i < cmditem->nr_required_attrs; i++) {

++		if (info->attrs[cmditem->required_attrs[i]])

++			sat_req_attrs++;

++	}

++

++	if (sat_req_attrs != cmditem->nr_required_attrs) {

++		pr_info("mt7530-nl: missing required attr(s) for cmd %u\n",

++			hdr->cmd);

++		return -EINVAL;

++	}

++

++	ret = cmditem->process(info);

++

++	mt7530_put();

++

++	return ret;

++}

++

++int mt7530_nl_init(struct mt7530_priv **priv)

++{

++	int ret;

++

++	pr_info("mt7530-nl: genl_register_family_with_ops \n");

++

++	sw_priv = *priv;

++	ret = genl_register_family(&mt7530_nl_family);

++	if (ret) {

++		return ret;

++	}

++

++	return 0;

++}

++

++void mt7530_nl_exit()

++{

++	sw_priv = NULL;

++	genl_unregister_family(&mt7530_nl_family);

++}

+Index: linux-5.4.203/drivers/net/dsa/mt7530_nl.h
+===================================================================
+--- /dev/null
++++ linux-5.4.203/drivers/net/dsa/mt7530_nl.h
+@@ -0,0 +1,49 @@
++/* SPDX-License-Identifier: GPL-2.0-only */

++/*

++ * Copyright (c) 2018 MediaTek Inc.

++ * Author: Sirui Zhao <Sirui.Zhao@mediatek.com>

++ */

++

++#ifndef _MT753x_NL_H_

++#define _MT753x_NL_H_

++

++#define MT7530_DSA_GENL_NAME "mt753x_dsa"

++#define MT7530_GENL_VERSION		0x1

++

++enum mt7530_cmd {

++	MT7530_CMD_UNSPEC = 0,

++	MT7530_CMD_REQUEST,

++	MT7530_CMD_REPLY,

++	MT7530_CMD_READ,

++	MT7530_CMD_WRITE,

++

++	__MT7530_CMD_MAX,

++};

++

++enum mt7530_attr {

++	MT7530_ATTR_TYPE_UNSPEC = 0,

++	MT7530_ATTR_TYPE_MESG,

++	MT7530_ATTR_TYPE_PHY,

++	MT7530_ATTR_TYPE_DEVAD,

++	MT7530_ATTR_TYPE_REG,

++	MT7530_ATTR_TYPE_VAL,

++	MT7530_ATTR_TYPE_DEV_NAME,

++	MT7530_ATTR_TYPE_DEV_ID,

++

++	__MT7530_ATTR_TYPE_MAX,

++};

++

++#define MT7530_NR_ATTR_TYPE		(__MT7530_ATTR_TYPE_MAX - 1)

++

++struct mt7530_info {

++	struct mii_bus	*bus;

++	void __iomem *base;

++	int direct_access;

++};

++

++#ifdef __KERNEL__

++int  mt7530_nl_init(struct mt7530_priv **priv);

++void mt7530_nl_exit(void);

++#endif /* __KERNEL__ */

++

++#endif /* _MT7530_NL_H_ */

diff --git a/target/linux/mediatek/patches-5.4/749-net-dsa-support-mt7988.patch b/target/linux/mediatek/patches-5.4/749-net-dsa-support-mt7988.patch
new file mode 100644
index 0000000..7c468d8
--- /dev/null
+++ b/target/linux/mediatek/patches-5.4/749-net-dsa-support-mt7988.patch
@@ -0,0 +1,360 @@
+Index: linux-5.4.203/drivers/net/dsa/mt7530.c
+===================================================================
+--- linux-5.4.203.orig/drivers/net/dsa/mt7530.c
++++ linux-5.4.203/drivers/net/dsa/mt7530.c
+@@ -19,6 +19,7 @@
+ #include <linux/reset.h>
+ #include <linux/gpio/consumer.h>
+ #include <net/dsa.h>
++#include <linux/of_address.h>
+
+ #include "mt7530.h"
+ #include "mt7530_nl.h"
+@@ -170,28 +171,44 @@ core_clear(struct mt7530_priv *priv, u32
+ 	core_rmw(priv, reg, val, 0);
+ }
+
++static void
++mtk_w32(struct mt7530_priv *priv, u32 val, unsigned reg)
++{
++	__raw_writel(val, priv->base + reg);
++}
++
++static u32
++mtk_r32(struct mt7530_priv *priv, unsigned reg)
++{
++	return __raw_readl(priv->base + reg);
++}
++
+ static int
+ mt7530_mii_write(struct mt7530_priv *priv, u32 reg, u32 val)
+ {
+ 	struct mii_bus *bus = priv->bus;
+ 	u16 page, r, lo, hi;
+-	int ret;
+-
+-	page = (reg >> 6) & 0x3ff;
+-	r  = (reg >> 2) & 0xf;
+-	lo = val & 0xffff;
+-	hi = val >> 16;
+-
+-	/* MT7530 uses 31 as the pseudo port */
+-	ret = bus->write(bus, 0x1f, 0x1f, page);
+-	if (ret < 0)
+-		goto err;
++	int ret = 0;
+
+-	ret = bus->write(bus, 0x1f, r,  lo);
+-	if (ret < 0)
+-		goto err;
++	if (priv->direct_access){
++		mtk_w32(priv, val, reg);
++	} else {
++		page = (reg >> 6) & 0x3ff;
++		r  = (reg >> 2) & 0xf;
++		lo = val & 0xffff;
++		hi = val >> 16;
++
++		/* MT7530 uses 31 as the pseudo port */
++		ret = bus->write(bus, 0x1f, 0x1f, page);
++		if (ret < 0)
++			goto err;
++
++		ret = bus->write(bus, 0x1f, r,  lo);
++		if (ret < 0)
++			goto err;
+
+-	ret = bus->write(bus, 0x1f, 0x10, hi);
++		ret = bus->write(bus, 0x1f, 0x10, hi);
++	}
+ err:
+ 	if (ret < 0)
+ 		dev_err(&bus->dev,
+@@ -206,21 +223,25 @@ mt7530_mii_read(struct mt7530_priv *priv
+ 	u16 page, r, lo, hi;
+ 	int ret;
+
+-	page = (reg >> 6) & 0x3ff;
+-	r = (reg >> 2) & 0xf;
++	if (priv->direct_access){
++		return mtk_r32(priv, reg);
++	} else {
++		page = (reg >> 6) & 0x3ff;
++		r = (reg >> 2) & 0xf;
+
+-	/* MT7530 uses 31 as the pseudo port */
+-	ret = bus->write(bus, 0x1f, 0x1f, page);
+-	if (ret < 0) {
+-		dev_err(&bus->dev,
+-			"failed to read mt7530 register\n");
+-		return ret;
+-	}
++		/* MT7530 uses 31 as the pseudo port */
++		ret = bus->write(bus, 0x1f, 0x1f, page);
++		if (ret < 0) {
++			dev_err(&bus->dev,
++				"failed to read mt7530 register\n");
++			return ret;
++		}
+
+-	lo = bus->read(bus, 0x1f, r);
+-	hi = bus->read(bus, 0x1f, 0x10);
++		lo = bus->read(bus, 0x1f, r);
++		hi = bus->read(bus, 0x1f, 0x10);
+
+-	return (hi << 16) | (lo & 0xffff);
++		return (hi << 16) | (lo & 0xffff);
++	}
+ }
+
+ void
+@@ -1906,9 +1927,9 @@ mt7531_phy_supported(struct dsa_switch *
+ 		if (mt7531_is_rgmii_port(priv, port))
+ 			return phy_interface_mode_is_rgmii(state->interface);
+ 		fallthrough;
+-	case 6: /* 1st cpu port supports sgmii/8023z only */
+-		if (state->interface != PHY_INTERFACE_MODE_SGMII &&
+-		    !phy_interface_mode_is_8023z(state->interface))
++	case 6: /* 1st cpu port supports sgmii/8023z/usxgmii/10gkr */
++		if (state->interface != PHY_INTERFACE_MODE_SGMII && state->interface != PHY_INTERFACE_MODE_USXGMII &&
++		    state->interface != PHY_INTERFACE_MODE_10GKR && !phy_interface_mode_is_8023z(state->interface))
+ 			goto unsupported;
+ 		break;
+ 	default:
+@@ -2017,6 +2038,13 @@ static void mt7531_sgmii_validate(struct
+ 		phylink_set(supported, 1000baseX_Full);
+ 		phylink_set(supported, 2500baseX_Full);
+ 		phylink_set(supported, 2500baseT_Full);
++		phylink_set(supported, 10000baseKR_Full);
++		phylink_set(supported, 10000baseT_Full);
++		phylink_set(supported, 10000baseCR_Full);
++		phylink_set(supported, 10000baseSR_Full);
++		phylink_set(supported, 10000baseLR_Full);
++		phylink_set(supported, 10000baseLRM_Full);
++		phylink_set(supported, 10000baseER_Full);
+ 	}
+ }
+
+@@ -2165,6 +2193,8 @@ mt7531_mac_config(struct dsa_switch *ds,
+ 	case PHY_INTERFACE_MODE_NA:
+ 	case PHY_INTERFACE_MODE_1000BASEX:
+ 	case PHY_INTERFACE_MODE_2500BASEX:
++	case PHY_INTERFACE_MODE_USXGMII:
++	case PHY_INTERFACE_MODE_10GKR:
+ 		if (phylink_autoneg_inband(mode))
+ 			return -EINVAL;
+
+@@ -2302,8 +2332,8 @@ static void mt753x_phylink_mac_link_up(s
+ 	/* MT753x MAC works in 1G full duplex mode for all up-clocked
+ 	 * variants.
+ 	 */
+-	if (interface == PHY_INTERFACE_MODE_TRGMII ||
+-	    (phy_interface_mode_is_8023z(interface))) {
++	if (interface == PHY_INTERFACE_MODE_TRGMII || interface == PHY_INTERFACE_MODE_USXGMII ||
++	    interface == PHY_INTERFACE_MODE_10GKR || (phy_interface_mode_is_8023z(interface))) {
+ 		speed = SPEED_1000;
+ 		duplex = DUPLEX_FULL;
+ 	}
+@@ -2402,8 +2432,8 @@ mt753x_phylink_validate(struct dsa_switc
+
+ 	phylink_set_port_modes(mask);
+
+-	if (state->interface != PHY_INTERFACE_MODE_TRGMII ||
+-	    !phy_interface_mode_is_8023z(state->interface)) {
++	if (state->interface != PHY_INTERFACE_MODE_TRGMII || state->interface != PHY_INTERFACE_MODE_USXGMII ||
++	    state->interface != PHY_INTERFACE_MODE_10GKR || !phy_interface_mode_is_8023z(state->interface)) {
+ 		phylink_set(mask, 10baseT_Half);
+ 		phylink_set(mask, 10baseT_Full);
+ 		phylink_set(mask, 100baseT_Half);
+@@ -2607,6 +2637,66 @@ mt753x_phy_write(struct dsa_switch *ds,
+ 	return priv->info->phy_write(ds, port, regnum, val);
+ }
+
++static int
++mt7988_pad_setup(struct dsa_switch *ds, phy_interface_t interface)
++{
++	return 0;
++}
++
++static int
++mt7988_setup(struct dsa_switch *ds)
++{
++	struct mt7530_priv *priv = ds->priv;
++	u32 unused_pm = 0;
++	int ret, i;
++
++	/* Reset the switch through internal reset */
++	mt7530_write(priv, MT7530_SYS_CTRL,
++		     SYS_CTRL_PHY_RST | SYS_CTRL_SW_RST);
++
++	/* BPDU to CPU port */
++	mt7530_rmw(priv, MT7531_CFC, MT7531_CPU_PMAP_MASK,
++		   BIT(MT7530_CPU_PORT));
++	mt7530_rmw(priv, MT753X_BPC, MT753X_BPDU_PORT_FW_MASK,
++		   MT753X_BPDU_CPU_ONLY);
++
++	/* Enable and reset MIB counters */
++	mt7530_mib_reset(ds);
++
++	for (i = 0; i < MT7530_NUM_PORTS; i++) {
++		/* Disable forwarding by default on all ports */
++		mt7530_rmw(priv, MT7530_PCR_P(i), PCR_MATRIX_MASK,
++			   PCR_MATRIX_CLR);
++
++		mt7530_set(priv, MT7531_DBG_CNT(i), MT7531_DIS_CLR);
++
++		if (dsa_is_unused_port(ds, i))
++			unused_pm |= BIT(i);
++		else if (dsa_is_cpu_port(ds, i))
++			mt753x_cpu_port_enable(ds, i);
++		else
++			mt7530_port_disable(ds, i);
++
++		/* Enable consistent egress tag */
++		mt7530_rmw(priv, MT7530_PVC_P(i), PVC_EG_TAG_MASK,
++			   PVC_EG_TAG(MT7530_VLAN_EG_CONSISTENT));
++	}
++
++	mt7531_phy_setup(ds);
++
++	/* Group and enable unused ports as a standalone dumb switch. */
++	setup_unused_ports(ds, unused_pm);
++
++	ds->configure_vlan_while_not_filtering = true;
++
++	/* Flush the FDB table */
++	ret = mt7530_fdb_cmd(priv, MT7530_FDB_FLUSH, NULL);
++	if (ret < 0)
++		return ret;
++
++	return 0;
++}
++
+ static const struct dsa_switch_ops mt7530_switch_ops = {
+ 	.get_tag_protocol	= mtk_get_tag_protocol,
+ 	.setup			= mt753x_setup,
+@@ -2676,12 +2766,28 @@ static const struct mt753x_info mt753x_t
+ 		.mac_pcs_an_restart = mt7531_sgmii_restart_an,
+ 		.mac_pcs_link_up = mt7531_sgmii_link_up_force,
+ 	},
++	[ID_MT7988] = {
++		.id = ID_MT7988,
++		.sw_setup = mt7988_setup,
++		.phy_read = mt7531_ind_phy_read,
++		.phy_write = mt7531_ind_phy_write,
++		.pad_setup = mt7988_pad_setup,
++		.cpu_port_config = mt7531_cpu_port_config,
++		.phy_mode_supported = mt7531_phy_supported,
++		.mac_port_validate = mt7531_mac_port_validate,
++		.mac_port_get_state = mt7531_phylink_mac_link_state,
++		.mac_port_config = mt7531_mac_config,
++		.mac_pcs_an_restart = mt7531_sgmii_restart_an,
++		.mac_pcs_link_up = mt7531_sgmii_link_up_force,
++	},
++
+ };
+
+ static const struct of_device_id mt7530_of_match[] = {
+ 	{ .compatible = "mediatek,mt7621", .data = &mt753x_table[ID_MT7621], },
+ 	{ .compatible = "mediatek,mt7530", .data = &mt753x_table[ID_MT7530], },
+ 	{ .compatible = "mediatek,mt7531", .data = &mt753x_table[ID_MT7531], },
++	{ .compatible = "mediatek,mt7988", .data = &mt753x_table[ID_MT7988], },
+ 	{ /* sentinel */ },
+ };
+ MODULE_DEVICE_TABLE(of, mt7530_of_match);
+@@ -2691,6 +2797,7 @@ mt7530_probe(struct mdio_device *mdiodev
+ {
+ 	struct mt7530_priv *priv;
+ 	struct device_node *dn;
++	struct device_node *switch_node = NULL;
+ 	int ret;
+
+ 	dn = mdiodev->dev.of_node;
+@@ -2760,6 +2867,16 @@ mt7530_probe(struct mdio_device *mdiodev
+ 		}
+ 	}
+
++	switch_node = of_find_node_by_name(NULL, "switch0");
++	if(switch_node) {
++		priv->base = of_iomap(switch_node, 0);
++		if(priv->base == NULL){
++			dev_err(&mdiodev->dev, "of_iomap failed\n");
++			return -ENOMEM;
++		}
++		priv->direct_access = 1;
++	}
++
+ 	priv->bus = mdiodev->bus;
+ 	priv->dev = &mdiodev->dev;
+ 	priv->ds->priv = priv;
+@@ -2768,9 +2885,12 @@ mt7530_probe(struct mdio_device *mdiodev
+ 	dev_set_drvdata(&mdiodev->dev, priv);
+
+ 	ret = dsa_register_switch(priv->ds);
+-	if (ret)
+-		return ret;
+-
++	if (ret) {
++		if(priv->base)
++			iounmap(priv->base);
++
++		return ret;
++	}
+ 	mt7530_nl_init(&priv);
+
+ 	return 0;
+@@ -2795,6 +2915,9 @@ mt7530_remove(struct mdio_device *mdiode
+ 	dsa_unregister_switch(priv->ds);
+ 	mutex_destroy(&priv->reg_mutex);
+
++	if(priv->base)
++		iounmap(priv->base);
++
+ 	mt7530_nl_exit();
+ }
+
+Index: linux-5.4.203/drivers/net/dsa/mt7530.h
+===================================================================
+--- linux-5.4.203.orig/drivers/net/dsa/mt7530.h
++++ linux-5.4.203/drivers/net/dsa/mt7530.h
+@@ -16,6 +16,7 @@ enum mt753x_id {
+ 	ID_MT7530 = 0,
+ 	ID_MT7621 = 1,
+ 	ID_MT7531 = 2,
++	ID_MT7988 = 3,
+ };
+
+ #define	NUM_TRGMII_CTRL			5
+@@ -51,11 +52,11 @@ enum mt753x_id {
+ #define  MT7531_MIRROR_PORT_SET(x)	(((x) & MIRROR_MASK) << 16)
+ #define  MT7531_CPU_PMAP_MASK		GENMASK(7, 0)
+
+-#define MT753X_MIRROR_REG(id)		(((id) == ID_MT7531) ? \
++#define MT753X_MIRROR_REG(id)		((((id) == ID_MT7531) || ((id) == ID_MT7988)) ? \
+ 					 MT7531_CFC : MT7530_MFC)
+-#define MT753X_MIRROR_EN(id)		(((id) == ID_MT7531) ? \
++#define MT753X_MIRROR_EN(id)		((((id) == ID_MT7531) || ((id) == ID_MT7988)) ? \
+ 					 MT7531_MIRROR_EN : MIRROR_EN)
+-#define MT753X_MIRROR_MASK(id)		(((id) == ID_MT7531) ? \
++#define MT753X_MIRROR_MASK(id)		((((id) == ID_MT7531) || ((id) == ID_MT7988)) ? \
+ 					 MT7531_MIRROR_MASK : MIRROR_MASK)
+
+ /* Registers for BPDU and PAE frame control*/
+@@ -261,7 +262,7 @@ enum mt7530_vlan_port_attr {
+ 					 MT7531_FORCE_DPX | \
+ 					 MT7531_FORCE_RX_FC | \
+ 					 MT7531_FORCE_TX_FC)
+-#define  PMCR_FORCE_MODE_ID(id)		(((id) == ID_MT7531) ? \
++#define  PMCR_FORCE_MODE_ID(id)		((((id) == ID_MT7531) || ((id) == ID_MT7988)) ? \
+ 					 MT7531_FORCE_MODE : \
+ 					 PMCR_FORCE_MODE)
+ #define  PMCR_LINK_SETTINGS_MASK	(PMCR_TX_EN | PMCR_FORCE_SPEED_1000 | \
+@@ -733,6 +734,8 @@ struct mt7530_priv {
+ 	struct regulator	*core_pwr;
+ 	struct regulator	*io_pwr;
+ 	struct gpio_desc	*reset;
++	void  __iomem *base;
++	int   direct_access;
+ 	const struct mt753x_info *info;
+ 	unsigned int		id;
+ 	bool			mcm;
diff --git a/target/linux/mediatek/patches-5.4/750-add-mdio-bus-for-phy-node.patch b/target/linux/mediatek/patches-5.4/750-add-mdio-bus-for-phy-node.patch
new file mode 100644
index 0000000..5a130b1
--- /dev/null
+++ b/target/linux/mediatek/patches-5.4/750-add-mdio-bus-for-phy-node.patch
@@ -0,0 +1,165 @@
+Index: linux-5.4.203/drivers/net/dsa/mt7530.c
+===================================================================
+--- linux-5.4.203.orig/drivers/net/dsa/mt7530.c
++++ linux-5.4.203/drivers/net/dsa/mt7530.c
+@@ -847,6 +847,132 @@ mt7531_ind_phy_write(struct dsa_switch *
+ 	return ret;
+ }
+
++static int mt753x_mdio_read(struct mii_bus *bus, int addr, int regnum)
++{
++	struct mt7530_priv *priv = bus->priv;
++	struct mt7530_dummy_poll p;
++	int ret;
++	u32 val;
++
++	INIT_MT7530_DUMMY_POLL(&p, priv, MT7531_PHY_IAC);
++
++	mutex_lock_nested(&priv->bus->mdio_lock, MDIO_MUTEX_NESTED);
++
++	ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val,
++				 !(val & MT7531_PHY_ACS_ST), 20, 100000);
++	if (ret < 0) {
++		dev_err(priv->dev, "poll timeout\n");
++		goto out;
++	}
++
++	val = MT7531_MDIO_CL22_READ | MT7531_MDIO_PHY_ADDR(addr) |
++		  MT7531_MDIO_REG_ADDR(regnum);
++
++	mt7530_mii_write(priv, MT7531_PHY_IAC, val | MT7531_PHY_ACS_ST);
++
++	ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val,
++				 !(val & MT7531_PHY_ACS_ST), 20, 100000);
++	if (ret < 0) {
++		dev_err(priv->dev, "poll timeout\n");
++		goto out;
++	}
++
++	ret = val & MT7531_MDIO_RW_DATA_MASK;
++out:
++	mutex_unlock(&priv->bus->mdio_lock);
++
++	return ret;
++}
++
++static int mt753x_mdio_write(struct mii_bus *bus, int addr, int regnum, u16 val)
++{
++	struct mt7530_priv *priv = bus->priv;
++	struct mt7530_dummy_poll p;
++	int ret;
++	u32 reg;
++
++	INIT_MT7530_DUMMY_POLL(&p, priv, MT7531_PHY_IAC);
++
++	mutex_lock_nested(&priv->bus->mdio_lock, MDIO_MUTEX_NESTED);
++
++	ret = readx_poll_timeout(_mt7530_unlocked_read, &p, reg,
++				 !(reg & MT7531_PHY_ACS_ST), 20, 100000);
++	if (ret < 0) {
++		dev_err(priv->dev, "poll timeout\n");
++		goto out;
++	}
++
++	reg = MT7531_MDIO_CL22_WRITE | MT7531_MDIO_PHY_ADDR(addr) |
++		  MT7531_MDIO_REG_ADDR(regnum) | val;
++
++	mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST);
++
++	ret = readx_poll_timeout(_mt7530_unlocked_read, &p, reg,
++				 !(reg & MT7531_PHY_ACS_ST), 20, 100000);
++	if (ret < 0) {
++		dev_err(priv->dev, "poll timeout\n");
++		goto out;
++	}
++
++out:
++	mutex_unlock(&priv->bus->mdio_lock);
++
++	return ret;
++}
++
++static int mt753x_mdio_init(struct mt7530_priv *priv)
++{
++	struct device_node *dn;
++	struct device_node *mii_np;
++	int ret;
++
++	dn = priv->dev->of_node;
++
++	mii_np = of_get_child_by_name(dn, "mdio-bus");
++	if (!mii_np) {
++		ret = -ENODEV;
++		goto err_put_node;
++	}
++
++	if (!of_device_is_available(mii_np)) {
++		ret = -ENODEV;
++		goto err_put_node;
++	}
++
++	priv->gbus = devm_mdiobus_alloc(priv->dev);
++	if (!priv->gbus) {
++		ret = -ENOMEM;
++		goto err_put_node;
++	}
++	priv->gbus->name = "mt753x_mdio";
++	priv->gbus->read = mt753x_mdio_read;
++	priv->gbus->write = mt753x_mdio_write;
++	priv->gbus->priv = priv;
++	priv->gbus->parent = priv->dev;
++
++	if(snprintf(priv->gbus->id, MII_BUS_ID_SIZE, "%s@%s", mii_np->name, dn->name) < 0) {
++		ret = -ENOMEM;
++		goto err_put_node;
++	}
++
++	ret = of_mdiobus_register(priv->gbus, mii_np);
++	if (ret)
++		priv->gbus = NULL;
++
++err_put_node:
++	of_node_put(mii_np);
++
++	return ret;
++}
++
++static void mt753x_mdio_exit(struct mt7530_priv *priv)
++{
++	if (!priv->gbus)
++		return;
++
++	mdiobus_unregister(priv->gbus);
++}
++
+ static void
+ mt7530_get_strings(struct dsa_switch *ds, int port, u32 stringset,
+ 		   uint8_t *data)
+@@ -2892,6 +3018,7 @@ mt7530_probe(struct mdio_device *mdiodev
+ 		return ret;
+ 	}
+ 	mt7530_nl_init(&priv);
++	mt753x_mdio_init(priv);
+
+ 	return 0;
+ }
+@@ -2919,6 +3046,7 @@ mt7530_remove(struct mdio_device *mdiode
+ 		iounmap(priv->base);
+
+ 	mt7530_nl_exit();
++	mt753x_mdio_exit(priv);
+ }
+
+ static struct mdio_driver mt7530_mdio_driver = {
+Index: linux-5.4.203/drivers/net/dsa/mt7530.h
+===================================================================
+--- linux-5.4.203.orig/drivers/net/dsa/mt7530.h
++++ linux-5.4.203/drivers/net/dsa/mt7530.h
+@@ -730,6 +730,7 @@ struct mt7530_priv {
+ 	struct device		*dev;
+ 	struct dsa_switch	*ds;
+ 	struct mii_bus		*bus;
++	struct mii_bus		*gbus;
+ 	struct reset_control	*rstc;
+ 	struct regulator	*core_pwr;
+ 	struct regulator	*io_pwr;
diff --git a/target/linux/mediatek/patches-5.4/8010-phy-phy-mtk-xsphy-support-type-switch-by-pericfg.patch b/target/linux/mediatek/patches-5.4/8010-phy-phy-mtk-xsphy-support-type-switch-by-pericfg.patch
new file mode 100644
index 0000000..c20e930
--- /dev/null
+++ b/target/linux/mediatek/patches-5.4/8010-phy-phy-mtk-xsphy-support-type-switch-by-pericfg.patch
@@ -0,0 +1,142 @@
+diff --git a/drivers/phy/mediatek/phy-mtk-xsphy.c b/drivers/phy/mediatek/phy-mtk-xsphy.c
+index 8c51131..e77092c 100644
+--- a/drivers/phy/mediatek/phy-mtk-xsphy.c
++++ b/drivers/phy/mediatek/phy-mtk-xsphy.c
+@@ -12,10 +12,12 @@
+ #include <linux/delay.h>
+ #include <linux/io.h>
+ #include <linux/iopoll.h>
++#include <linux/mfd/syscon.h>
+ #include <linux/module.h>
+ #include <linux/of_address.h>
+ #include <linux/phy/phy.h>
+ #include <linux/platform_device.h>
++#include <linux/regmap.h>
+ 
+ /* u2 phy banks */
+ #define SSUSB_SIFSLV_MISC		0x000
+@@ -88,12 +90,22 @@
+ #define XSP_SR_COEF_DIVISOR	1000
+ #define XSP_FM_DET_CYCLE_CNT	1024
+ 
++/* PHY switch between pcie/usb3/sgmii */
++#define USB_PHY_SWITCH_CTRL	0x0
++#define RG_PHY_SW_TYPE		GENMASK(3, 0)
++#define RG_PHY_SW_PCIE		0x0
++#define RG_PHY_SW_USB3		0x1
++#define RG_PHY_SW_SGMII		0x2
++
+ struct xsphy_instance {
+ 	struct phy *phy;
+ 	void __iomem *port_base;
+ 	struct clk *ref_clk;	/* reference clock of anolog phy */
+ 	u32 index;
+ 	u32 type;
++	struct regmap *type_sw;
++	u32 type_sw_reg;
++	u32 type_sw_index;
+ 	/* only for HQA test */
+ 	int efuse_intr;
+ 	int efuse_tx_imp;
+@@ -365,6 +377,62 @@ static void u3_phy_props_set(struct mtk_xsphy *xsphy,
+ 	}
+ }
+ 
++/* type switch for usb3/pcie/sgmii */
++static int phy_type_syscon_get(struct xsphy_instance *instance,
++			       struct device_node *dn)
++{
++	struct of_phandle_args args;
++	int ret;
++
++	/* type switch function is optional */
++	if (!of_property_read_bool(dn, "mediatek,syscon-type"))
++		return 0;
++
++	ret = of_parse_phandle_with_fixed_args(dn, "mediatek,syscon-type",
++					       2, 0, &args);
++	if (ret)
++		return ret;
++
++	instance->type_sw_reg = args.args[0];
++	instance->type_sw_index = args.args[1] & 0x3; /* <=3 */
++	instance->type_sw = syscon_node_to_regmap(args.np);
++	of_node_put(args.np);
++	dev_info(&instance->phy->dev, "type_sw - reg %#x, index %d\n",
++		 instance->type_sw_reg, instance->type_sw_index);
++
++	return PTR_ERR_OR_ZERO(instance->type_sw);
++}
++
++static int phy_type_set(struct xsphy_instance *instance)
++{
++	int type;
++	u32 offset;
++
++	if (!instance->type_sw)
++		return 0;
++
++	switch (instance->type) {
++	case PHY_TYPE_USB3:
++		type = RG_PHY_SW_USB3;
++		break;
++	case PHY_TYPE_PCIE:
++		type = RG_PHY_SW_PCIE;
++		break;
++	case PHY_TYPE_SGMII:
++		type = RG_PHY_SW_SGMII;
++		break;
++	case PHY_TYPE_USB2:
++	default:
++		return 0;
++	}
++
++	offset = instance->type_sw_index * BITS_PER_BYTE;
++	regmap_update_bits(instance->type_sw, instance->type_sw_reg,
++			   RG_PHY_SW_TYPE << offset, type << offset);
++
++	return 0;
++}
++
+ static int mtk_phy_init(struct phy *phy)
+ {
+ 	struct xsphy_instance *inst = phy_get_drvdata(phy);
+@@ -385,6 +453,10 @@ static int mtk_phy_init(struct phy *phy)
+ 	case PHY_TYPE_USB3:
+ 		u3_phy_props_set(xsphy, inst);
+ 		break;
++	case PHY_TYPE_PCIE:
++	case PHY_TYPE_SGMII:
++		/* nothing to do, only used to set type */
++		break;
+ 	default:
+ 		dev_err(xsphy->dev, "incompatible phy type\n");
+ 		clk_disable_unprepare(inst->ref_clk);
+@@ -463,12 +535,15 @@ static struct phy *mtk_phy_xlate(struct device *dev,
+ 
+ 	inst->type = args->args[0];
+ 	if (!(inst->type == PHY_TYPE_USB2 ||
+-	      inst->type == PHY_TYPE_USB3)) {
++	      inst->type == PHY_TYPE_USB3 ||
++	      inst->type == PHY_TYPE_PCIE ||
++	      inst->type == PHY_TYPE_SGMII)) {
+ 		dev_err(dev, "unsupported phy type: %d\n", inst->type);
+ 		return ERR_PTR(-EINVAL);
+ 	}
+ 
+ 	phy_parse_property(xsphy, inst);
++	phy_type_set(inst);
+ 
+ 	return inst->phy;
+ }
+@@ -575,6 +650,10 @@ static int mtk_xsphy_probe(struct platform_device *pdev)
+ 			retval = PTR_ERR(inst->ref_clk);
+ 			goto put_child;
+ 		}
++
++		retval = phy_type_syscon_get(inst, child_np);
++		if (retval)
++			goto put_child;
+ 	}
+ 
+ 	provider = devm_of_phy_provider_register(dev, mtk_phy_xlate);
diff --git a/target/linux/mediatek/patches-5.4/9000-PATCH-1-1-xHCI-change-compliance-mode-de-emphasis-default-as-g.patch b/target/linux/mediatek/patches-5.4/9000-PATCH-1-1-xHCI-change-compliance-mode-de-emphasis-default-as-g.patch
new file mode 100644
index 0000000..e82e1bc
--- /dev/null
+++ b/target/linux/mediatek/patches-5.4/9000-PATCH-1-1-xHCI-change-compliance-mode-de-emphasis-default-as-g.patch
@@ -0,0 +1,57 @@
+From 5f8c12ffa661e3707790f59827a45ff4102f2886 Mon Sep 17 00:00:00 2001
+From: Zhanyong Wang <zhanyong.wang@mediatek.com>
+Date: Mon, 15 Aug 2022 14:13:50 +0800
+Subject: [PATCH] xHCI: change compliance mode de-emphasis default as gen1
+
+Port0 is using Gen2 Phy for 10GHz, and Port0 is running
+on 5GHz actually. hence to change compliance mode de-
+emphasis default as Gen1.
+
+Signed-off-by: Zhanyong Wang <zhanyong.wang@mediatek.com>
+---
+ drivers/usb/host/xhci-mtk.c | 15 +++++++++++++++
+ 1 file changed, 15 insertions(+)
+
+diff --git a/drivers/usb/host/xhci-mtk.c b/drivers/usb/host/xhci-mtk.c
+index 2a4b73a658f9..b1201fb65fd6 100644
+--- a/drivers/usb/host/xhci-mtk.c
++++ b/drivers/usb/host/xhci-mtk.c
+@@ -24,6 +24,11 @@
+ #include "xhci-mtk.h"
+ #include "xhci-mtk-test.h"
+ 
++/* COMPLIANCE_CP5_CP7_TXDEEMPH_10G register */
++#define COMPLIANCE_CP5_CP7_TXDEEMPH_10G  0x2428
++#define CP5_CP7_TXDEEMPH_10G		 GENMASK(17, 0)
++#define CP5_CP7_TXDEEMPH_10G_VAL(val)	((val) & 0x03FFFF)
++
+ /* ip_pw_ctrl0 register */
+ #define CTRL0_IP_SW_RST	BIT(0)
+ 
+@@ -415,6 +420,7 @@ static int xhci_mtk_setup(struct usb_hcd *hcd)
+ {
+ 	struct xhci_hcd_mtk *mtk = hcd_to_mtk(hcd);
+ 	int ret;
++	u32 val;
+ 
+ 	if (usb_hcd_is_primary_hcd(hcd)) {
+ 		ret = xhci_mtk_ssusb_config(mtk);
+@@ -432,6 +438,15 @@ static int xhci_mtk_setup(struct usb_hcd *hcd)
+ 			return ret;
+ 	}
+ 
++	/* change COMPLIANCE_CP5_CP7_TXDEEMPH_10G  as Gen1 instead Gen2 */
++	if (hcd->rsrc_start == 0x11190000ULL) {
++		val  = readl(mtk->hcd->regs + COMPLIANCE_CP5_CP7_TXDEEMPH_10G);
++		val &= ~CP5_CP7_TXDEEMPH_10G;
++		val |= 0x00001;
++		val = CP5_CP7_TXDEEMPH_10G_VAL(val);
++		writel(val, mtk->hcd->regs + COMPLIANCE_CP5_CP7_TXDEEMPH_10G);
++	}
++
+ 	return ret;
+ }
+ 
+-- 
+2.18.0
+