[Feature] add GA346 baseline version

Change-Id: Ic62933698569507dcf98240cdf5d9931ae34348f
diff --git a/src/kernel/linux/v4.19/drivers/soc/mediatek/mtk-dvfsrc.c b/src/kernel/linux/v4.19/drivers/soc/mediatek/mtk-dvfsrc.c
new file mode 100644
index 0000000..f19be36
--- /dev/null
+++ b/src/kernel/linux/v4.19/drivers/soc/mediatek/mtk-dvfsrc.c
@@ -0,0 +1,1234 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ */
+#include <linux/arm-smccc.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/notifier.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/soc/mediatek/mtk_dvfsrc.h>
+#include <linux/soc/mediatek/mtk_sip_svc.h>
+#include "mtk-scpsys.h"
+
+/* Private */
+#define DVFSRC_OPP_BW_QUERY
+#define DVFSRC_FORCE_OPP_SUPPORT
+#define DVFSRC_PROPERTY_ENABLE
+/* End */
+
+#define DVFSRC_IDLE     0x00
+#define DVFSRC_GET_TARGET_LEVEL(x)  (((x) >> 0) & 0x0000ffff)
+#define DVFSRC_GET_CURRENT_LEVEL(x) (((x) >> 16) & 0x0000ffff)
+#define kbps_to_mbps(x) (div_u64(x, 1000))
+
+#define MT8183_DVFSRC_OPP_LP4   0
+#define MT8183_DVFSRC_OPP_LP4X  1
+#define MT8183_DVFSRC_OPP_LP3   2
+
+#define POLL_TIMEOUT        1000
+#define STARTUP_TIME        1
+
+#define MTK_SIP_DVFSRC_INIT		0x00
+
+#define DVFSRC_OPP_DESC(_opp_table)	\
+{	\
+	.opps = _opp_table,	\
+	.num_opp = ARRAY_SIZE(_opp_table),	\
+}
+
+struct dvfsrc_opp {
+	u32 vcore_opp;
+	u32 dram_opp;
+};
+
+struct dvfsrc_domain {
+	u32 id;
+	u32 state;
+};
+
+struct dvfsrc_opp_desc {
+	const struct dvfsrc_opp *opps;
+	u32 num_opp;
+};
+
+struct mtk_dvfsrc;
+struct dvfsrc_soc_data {
+	const int *regs;
+	u32 num_domains;
+	struct dvfsrc_domain *domains;
+	u32 num_opp_desc;
+	const struct dvfsrc_opp_desc *opps_desc;
+	void (*dvfsrc_hw_init)(struct mtk_dvfsrc *dvfsrc);
+	int (*get_target_level)(struct mtk_dvfsrc *dvfsrc);
+	int (*get_current_level)(struct mtk_dvfsrc *dvfsrc);
+	u32 (*get_vcore_level)(struct mtk_dvfsrc *dvfsrc);
+	u32 (*get_vcp_level)(struct mtk_dvfsrc *dvfsrc);
+	u32 (*get_dram_level)(struct mtk_dvfsrc *dvfsrc);
+	void (*set_dram_bw)(struct mtk_dvfsrc *dvfsrc, u64 bw);
+	void (*set_dram_peak_bw)(struct mtk_dvfsrc *dvfsrc, u64 bw);
+	void (*set_dram_hrtbw)(struct mtk_dvfsrc *dvfsrc, u64 bw);
+	void (*set_dram_level)(struct mtk_dvfsrc *dvfsrc, u32 level);
+	void (*set_opp_level)(struct mtk_dvfsrc *dvfsrc, u32 level);
+	void (*set_vcore_level)(struct mtk_dvfsrc *dvfsrc, u32 level);
+	void (*set_vscp_level)(struct mtk_dvfsrc *dvfsrc, u32 level);
+	int (*wait_for_opp_level)(struct mtk_dvfsrc *dvfsrc, u32 level);
+	int (*wait_for_vcore_level)(struct mtk_dvfsrc *dvfsrc, u32 level);
+	int (*wait_for_dram_level)(struct mtk_dvfsrc *dvfsrc, u32 level);
+#ifdef DVFSRC_FORCE_OPP_SUPPORT
+	void (*set_force_opp_level)(struct mtk_dvfsrc *dvfsrc, u32 level);
+#endif
+};
+
+struct mtk_dvfsrc {
+	struct device *dev;
+	struct platform_device *dvfsrc_start;
+	struct platform_device *devfreq;
+	struct platform_device *regulator;
+	struct platform_device *icc;
+	const struct dvfsrc_soc_data *dvd;
+	int dram_type;
+	const struct dvfsrc_opp_desc *curr_opps;
+	void __iomem *regs;
+	struct mutex req_lock;
+	struct mutex pstate_lock;
+	struct notifier_block scpsys_notifier;
+	bool dvfsrc_enable;
+#ifdef DVFSRC_FORCE_OPP_SUPPORT
+	bool opp_forced;
+	spinlock_t force_lock;
+#endif
+};
+
+static struct mtk_dvfsrc *dvfsrc_data;
+
+#ifdef DVFSRC_OPP_BW_QUERY
+u32 dram_type;
+static inline struct device_node *dvfsrc_parse_required_opp_bw(
+	struct device_node *np, int index)
+{
+	struct device_node *required_np;
+
+	required_np = of_parse_phandle(np, "required-opps", index);
+	if (unlikely(!required_np)) {
+		pr_notice("%s: Unable to parse required-opps: %pOF, index: %d\n",
+		       __func__, np, index);
+	}
+	return required_np;
+}
+u32 dvfsrc_get_required_opp_peak_bw(struct device_node *np, int index)
+{
+	struct device_node *required_np;
+	u32 peak_bw = 0;
+
+	required_np = dvfsrc_parse_required_opp_bw(np, index);
+	if (!required_np)
+		return 0;
+
+	of_property_read_u32_index(required_np, "opp-peak-KBps", dram_type,
+		&peak_bw);
+
+	of_node_put(required_np);
+	return peak_bw;
+}
+EXPORT_SYMBOL(dvfsrc_get_required_opp_peak_bw);
+#endif
+
+static u32 dvfsrc_read(struct mtk_dvfsrc *dvfs, u32 offset)
+{
+	return readl(dvfs->regs + dvfs->dvd->regs[offset]);
+}
+
+static void dvfsrc_write(struct mtk_dvfsrc *dvfs, u32 offset, u32 val)
+{
+	writel(val, dvfs->regs + dvfs->dvd->regs[offset]);
+}
+
+#define dvfsrc_rmw(dvfs, offset, val, mask, shift) \
+	dvfsrc_write(dvfs, offset, \
+		(dvfsrc_read(dvfs, offset) & ~(mask << shift)) | (val << shift))
+
+enum dvfsrc_regs {
+	DVFSRC_BASIC_CONTROL,
+	DVFSRC_SW_REQ,
+	DVFSRC_SW_REQ2,
+	DVFSRC_LEVEL,
+	DVFSRC_TARGET_LEVEL,
+	DVFSRC_SW_BW,
+	DVFSRC_SW_PEAK_BW,
+	DVFSRC_SW_HRT_BW,
+	DVFSRC_VCORE_REQUEST,
+	DVFSRC_LAST,
+
+	/* MT6873 ip series regs */
+	DVFSRC_INT_EN,
+	DVFSRC_VCORE_USER_REQ,
+	DVFSRC_BW_USER_REQ,
+	DVFSRC_TIMEOUT_NEXTREQ,
+	DVFSRC_LEVEL_LABEL_0_1,
+	DVFSRC_LEVEL_LABEL_2_3,
+	DVFSRC_LEVEL_LABEL_4_5,
+	DVFSRC_LEVEL_LABEL_6_7,
+	DVFSRC_LEVEL_LABEL_8_9,
+	DVFSRC_LEVEL_LABEL_10_11,
+	DVFSRC_LEVEL_LABEL_12_13,
+	DVFSRC_LEVEL_LABEL_14_15,
+	DVFSRC_QOS_EN,
+	DVFSRC_HRT_BW_BASE,
+	DVFSRC_EMI_MON_DEBOUNCE_TIME,
+	DVFSRC_MD_LATENCY_IMPROVE,
+	DVFSRC_BASIC_CONTROL_3,
+	DVFSRC_DEBOUNCE_TIME,
+	DVFSRC_LEVEL_MASK,
+	DVFSRC_95MD_SCEN_BW0,
+	DVFSRC_95MD_SCEN_BW1,
+	DVFSRC_95MD_SCEN_BW2,
+	DVFSRC_95MD_SCEN_BW3,
+	DVFSRC_95MD_SCEN_BW0_T,
+	DVFSRC_95MD_SCEN_BW1_T,
+	DVFSRC_95MD_SCEN_BW2_T,
+	DVFSRC_95MD_SCEN_BW3_T,
+	DVFSRC_95MD_SCEN_BW4,
+	DVFSRC_RSRV_4,
+	DVFSRC_RSRV_5,
+	DVFSRC_DDR_REQUEST,
+	DVFSRC_DDR_REQUEST3,
+	DVFSRC_DDR_REQUEST5,
+	DVFSRC_DDR_REQUEST6,
+	DVFSRC_DDR_REQUEST7,
+	DVFSRC_DDR_QOS0,
+	DVFSRC_DDR_QOS1,
+	DVFSRC_DDR_QOS2,
+	DVFSRC_DDR_QOS3,
+	DVFSRC_DDR_QOS4,
+	DVFSRC_HRT_REQ_UNIT,
+	DVSFRC_HRT_REQ_MD_URG,
+	DVFSRC_HRT_REQ_MD_BW_0,
+	DVFSRC_HRT_REQ_MD_BW_1,
+	DVFSRC_HRT_REQ_MD_BW_2,
+	DVFSRC_HRT_REQ_MD_BW_3,
+	DVFSRC_HRT_REQ_MD_BW_4,
+	DVFSRC_HRT_REQ_MD_BW_5,
+	DVFSRC_HRT_REQ_MD_BW_6,
+	DVFSRC_HRT_REQ_MD_BW_7,
+	DVFSRC_HRT1_REQ_MD_BW_0,
+	DVFSRC_HRT1_REQ_MD_BW_1,
+	DVFSRC_HRT1_REQ_MD_BW_2,
+	DVFSRC_HRT1_REQ_MD_BW_3,
+	DVFSRC_HRT1_REQ_MD_BW_4,
+	DVFSRC_HRT1_REQ_MD_BW_5,
+	DVFSRC_HRT1_REQ_MD_BW_6,
+	DVFSRC_HRT1_REQ_MD_BW_7,
+	DVFSRC_HRT_REQ_MD_BW_8,
+	DVFSRC_HRT_REQ_MD_BW_9,
+	DVFSRC_HRT_REQ_MD_BW_10,
+	DVFSRC_HRT1_REQ_MD_BW_8,
+	DVFSRC_HRT1_REQ_MD_BW_9,
+	DVFSRC_HRT1_REQ_MD_BW_10,
+	DVFSRC_HRT_REQUEST,
+	DVFSRC_HRT_HIGH_2,
+	DVFSRC_HRT_HIGH_1,
+	DVFSRC_HRT_HIGH,
+	DVFSRC_HRT_LOW_2,
+	DVFSRC_HRT_LOW_1,
+	DVFSRC_HRT_LOW,
+	DVFSRC_DDR_ADD_REQUEST,
+	DVFSRC_DDR_QOS5,
+	DVFSRC_DDR_QOS6,
+	DVFSRC_HRT_HIGH_3,
+	DVFSRC_HRT_LOW_3,
+	DVFSRC_LEVEL_LABEL_16_17,
+	DVFSRC_LEVEL_LABEL_18_19,
+	DVFSRC_LEVEL_LABEL_20_21,
+	DVFSRC_LEVEL_LABEL_22_23,
+	DVFSRC_LEVEL_LABEL_24_25,
+	DVFSRC_LEVEL_LABEL_26_27,
+	DVFSRC_LEVEL_LABEL_28_29,
+	DVFSRC_LEVEL_LABEL_30_31,
+	DVFSRC_CURRENT_FORCE,
+	DVFSRC_TARGET_FORCE,
+};
+
+static const int mt8183_regs[] = {
+	[DVFSRC_SW_REQ] =	0x4,
+	[DVFSRC_SW_REQ2] =	0x8,
+	[DVFSRC_LEVEL] =	0xDC,
+	[DVFSRC_SW_BW] =	0x160,
+	[DVFSRC_LAST] =		0x308,
+};
+
+static const int mt6873_regs[] = {
+	[DVFSRC_BASIC_CONTROL] =	0x0,
+	[DVFSRC_SW_REQ] =		0xC,
+	[DVFSRC_LEVEL] =		0xD44,
+	[DVFSRC_SW_PEAK_BW] =		0x278,
+	[DVFSRC_SW_BW] =		0x26C,
+	[DVFSRC_SW_HRT_BW] =		0x290,
+	[DVFSRC_TARGET_LEVEL] =		0xD48,
+	[DVFSRC_VCORE_REQUEST] =	0x6C,
+
+	[DVFSRC_INT_EN] =		0xC8,
+	[DVFSRC_VCORE_USER_REQ] =	0xE4,
+	[DVFSRC_BW_USER_REQ] =		0xE8,
+	[DVFSRC_TIMEOUT_NEXTREQ] =	0xF8,
+	[DVFSRC_LEVEL_LABEL_0_1] =	0x100,
+	[DVFSRC_LEVEL_LABEL_2_3] =	0x104,
+	[DVFSRC_LEVEL_LABEL_4_5] =	0x108,
+	[DVFSRC_LEVEL_LABEL_6_7] =	0x10C,
+	[DVFSRC_LEVEL_LABEL_8_9] =	0x110,
+	[DVFSRC_LEVEL_LABEL_10_11] =	0x114,
+	[DVFSRC_LEVEL_LABEL_12_13] =	0x118,
+	[DVFSRC_LEVEL_LABEL_14_15] =	0x11C,
+	[DVFSRC_QOS_EN] =		0x280,
+	[DVFSRC_HRT_BW_BASE] =		0x294,
+	[DVFSRC_EMI_MON_DEBOUNCE_TIME] =	0x308,
+	[DVFSRC_MD_LATENCY_IMPROVE] =	0x30C,
+	[DVFSRC_BASIC_CONTROL_3] =	0x310,
+	[DVFSRC_DEBOUNCE_TIME] =	0x314,
+	[DVFSRC_LEVEL_MASK] =		0x318,
+	[DVFSRC_95MD_SCEN_BW0] =	0x524,
+	[DVFSRC_95MD_SCEN_BW1] =	0x528,
+	[DVFSRC_95MD_SCEN_BW2] =	0x52C,
+	[DVFSRC_95MD_SCEN_BW3] =	0x530,
+	[DVFSRC_95MD_SCEN_BW0_T] =	0x534,
+	[DVFSRC_95MD_SCEN_BW1_T] =	0x538,
+	[DVFSRC_95MD_SCEN_BW2_T] =	0x53C,
+	[DVFSRC_95MD_SCEN_BW3_T] =	0x540,
+	[DVFSRC_95MD_SCEN_BW4] =	0x544,
+	[DVFSRC_RSRV_4] =		0x610,
+	[DVFSRC_RSRV_5] =		0x614,
+	[DVFSRC_DDR_REQUEST] =		0xA00,
+	[DVFSRC_DDR_REQUEST3] =		0xA08,
+	[DVFSRC_DDR_REQUEST5] =		0xA10,
+	[DVFSRC_DDR_REQUEST6] =		0xA14,
+	[DVFSRC_DDR_REQUEST7] =		0xA18,
+	[DVFSRC_DDR_QOS0] =		0xA34,
+	[DVFSRC_DDR_QOS1] =		0xA38,
+	[DVFSRC_DDR_QOS2] =		0xA3C,
+	[DVFSRC_DDR_QOS3] =		0xA40,
+	[DVFSRC_DDR_QOS4] =		0xA44,
+	[DVFSRC_HRT_REQ_UNIT] =		0xA60,
+	[DVSFRC_HRT_REQ_MD_URG] =	0xA64,
+	[DVFSRC_HRT_REQ_MD_BW_0] =	0xA68,
+	[DVFSRC_HRT_REQ_MD_BW_1] =	0xA6C,
+	[DVFSRC_HRT_REQ_MD_BW_2] =	0xA70,
+	[DVFSRC_HRT_REQ_MD_BW_3] =	0xA74,
+	[DVFSRC_HRT_REQ_MD_BW_4] =	0xA78,
+	[DVFSRC_HRT_REQ_MD_BW_5] =	0xA7C,
+	[DVFSRC_HRT_REQ_MD_BW_6] =	0xA80,
+	[DVFSRC_HRT_REQ_MD_BW_7] =	0xA84,
+	[DVFSRC_HRT1_REQ_MD_BW_0] =	0xA88,
+	[DVFSRC_HRT1_REQ_MD_BW_1] =	0xA8C,
+	[DVFSRC_HRT1_REQ_MD_BW_2] =	0xA90,
+	[DVFSRC_HRT1_REQ_MD_BW_3] =	0xA94,
+	[DVFSRC_HRT1_REQ_MD_BW_4] =	0xA98,
+	[DVFSRC_HRT1_REQ_MD_BW_5] =	0xA9C,
+	[DVFSRC_HRT1_REQ_MD_BW_6] =	0xAA0,
+	[DVFSRC_HRT1_REQ_MD_BW_7] =	0xAA4,
+	[DVFSRC_HRT_REQ_MD_BW_8] =	0xAA8,
+	[DVFSRC_HRT_REQ_MD_BW_9] =	0xAAC,
+	[DVFSRC_HRT_REQ_MD_BW_10] =	0xAB0,
+	[DVFSRC_HRT1_REQ_MD_BW_8] =	0xAB4,
+	[DVFSRC_HRT1_REQ_MD_BW_9] =	0xAB8,
+	[DVFSRC_HRT1_REQ_MD_BW_10] =	0xABC,
+	[DVFSRC_HRT_REQUEST] =		0xAC4,
+	[DVFSRC_HRT_HIGH_2] =		0xAC8,
+	[DVFSRC_HRT_HIGH_1] =		0xACC,
+	[DVFSRC_HRT_HIGH] =		0xAD0,
+	[DVFSRC_HRT_LOW_2] =		0xAD4,
+	[DVFSRC_HRT_LOW_1] =		0xAD8,
+	[DVFSRC_HRT_LOW] =		0xADC,
+	[DVFSRC_DDR_ADD_REQUEST] =	0xAE0,
+	[DVFSRC_DDR_QOS5] =		0xD18,
+	[DVFSRC_DDR_QOS6] =		0xD1C,
+	[DVFSRC_HRT_HIGH_3] =		0xD38,
+	[DVFSRC_HRT_LOW_3] =		0xD3C,
+	[DVFSRC_LEVEL_LABEL_16_17] =	0xD4C,
+	[DVFSRC_LEVEL_LABEL_18_19] =	0xD50,
+	[DVFSRC_LEVEL_LABEL_20_21] =	0xD54,
+	[DVFSRC_LEVEL_LABEL_22_23] =	0xD58,
+	[DVFSRC_LEVEL_LABEL_24_25] =	0xD5C,
+	[DVFSRC_LEVEL_LABEL_26_27] =	0xD60,
+	[DVFSRC_LEVEL_LABEL_28_29] =	0xD64,
+	[DVFSRC_LEVEL_LABEL_30_31] =	0xD68,
+	[DVFSRC_CURRENT_FORCE] =	0xD6C,
+	[DVFSRC_TARGET_FORCE] =		0xD70,
+};
+
+static const struct dvfsrc_opp *get_current_opp(struct mtk_dvfsrc *dvfsrc)
+{
+	int level;
+
+	level = dvfsrc->dvd->get_current_level(dvfsrc);
+	return &dvfsrc->curr_opps->opps[level];
+}
+
+static int dvfsrc_is_idle(struct mtk_dvfsrc *dvfsrc)
+{
+	if (!dvfsrc->dvd->get_target_level)
+		return true;
+
+	return dvfsrc->dvd->get_target_level(dvfsrc);
+}
+
+static int dvfsrc_wait_for_idle(struct mtk_dvfsrc *dvfsrc)
+{
+	int state;
+
+	return readx_poll_timeout_atomic(dvfsrc_is_idle, dvfsrc,
+		state, state == DVFSRC_IDLE,
+		STARTUP_TIME, POLL_TIMEOUT);
+}
+
+static int dvfsrc_wait_for_vcore_level(struct mtk_dvfsrc *dvfsrc, u32 level)
+{
+	const struct dvfsrc_opp *curr;
+
+	return readx_poll_timeout_atomic(get_current_opp, dvfsrc, curr,
+		curr->vcore_opp >= level, STARTUP_TIME,
+		POLL_TIMEOUT);
+}
+
+static int dvfsrc_wait_for_dram_level(struct mtk_dvfsrc *dvfsrc, u32 level)
+{
+	const struct dvfsrc_opp *curr;
+
+	return readx_poll_timeout_atomic(get_current_opp, dvfsrc, curr,
+		curr->dram_opp >= level, STARTUP_TIME,
+		POLL_TIMEOUT);
+}
+
+static int mt8183_wait_for_opp_level(struct mtk_dvfsrc *dvfsrc, u32 level)
+{
+	const struct dvfsrc_opp *target, *curr;
+	int ret;
+
+	target = &dvfsrc->curr_opps->opps[level];
+	ret = readx_poll_timeout(get_current_opp, dvfsrc, curr,
+				 curr->dram_opp >= target->dram_opp &&
+				 curr->vcore_opp >= target->vcore_opp,
+				 STARTUP_TIME, POLL_TIMEOUT);
+	if (ret < 0) {
+		dev_warn(dvfsrc->dev,
+			 "timeout, target: %u, dram: %d, vcore: %d\n", level,
+			 curr->dram_opp, curr->vcore_opp);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int mt8183_get_target_level(struct mtk_dvfsrc *dvfsrc)
+{
+	return DVFSRC_GET_TARGET_LEVEL(dvfsrc_read(dvfsrc, DVFSRC_LEVEL));
+}
+
+static int mt8183_get_current_level(struct mtk_dvfsrc *dvfsrc)
+{
+	int level;
+
+	/* HW level 0 is begin from 0x10000 */
+	level = DVFSRC_GET_CURRENT_LEVEL(dvfsrc_read(dvfsrc, DVFSRC_LEVEL));
+	/* Array index start from 0 */
+	return ffs(level) - 1;
+}
+
+static u32 mt8183_get_vcore_level(struct mtk_dvfsrc *dvfsrc)
+{
+	return (dvfsrc_read(dvfsrc, DVFSRC_SW_REQ2) >> 2) & 0x3;
+}
+
+static void mt8183_set_dram_bw(struct mtk_dvfsrc *dvfsrc, u64 bw)
+{
+	dvfsrc_write(dvfsrc, DVFSRC_SW_BW, div_u64(kbps_to_mbps(bw), 100));
+}
+
+static void mt8183_set_opp_level(struct mtk_dvfsrc *dvfsrc, u32 level)
+{
+	int vcore_opp, dram_opp;
+	const struct dvfsrc_opp *opp;
+
+	/* translate pstate to dvfsrc level, and set it to DVFSRC HW */
+	opp = &dvfsrc->curr_opps->opps[level];
+	vcore_opp = opp->vcore_opp;
+	dram_opp = opp->dram_opp;
+
+	dev_dbg(dvfsrc->dev, "vcore_opp: %d, dram_opp: %d\n",
+		vcore_opp, dram_opp);
+	dvfsrc_write(dvfsrc, DVFSRC_SW_REQ, dram_opp | vcore_opp << 2);
+}
+
+static void mt8183_set_vcore_level(struct mtk_dvfsrc *dvfsrc, u32 level)
+{
+	dvfsrc_write(dvfsrc, DVFSRC_SW_REQ2, level << 2);
+}
+
+static int mt6873_get_target_level(struct mtk_dvfsrc *dvfsrc)
+{
+	return dvfsrc_read(dvfsrc, DVFSRC_TARGET_LEVEL);
+}
+
+static int mt6873_get_current_level(struct mtk_dvfsrc *dvfsrc)
+{
+	u32 curr_level;
+
+	/* HW level 0 is begin from 0x1, and max opp is 0x1*/
+	curr_level = ffs(dvfsrc_read(dvfsrc, DVFSRC_LEVEL));
+	if (curr_level > dvfsrc->curr_opps->num_opp)
+		curr_level = 0;
+	else
+		curr_level = dvfsrc->curr_opps->num_opp - curr_level;
+
+	return curr_level;
+}
+
+static int mt6873_wait_for_opp_level(struct mtk_dvfsrc *dvfsrc, u32 level)
+{
+	const struct dvfsrc_opp *target, *curr;
+
+	target = &dvfsrc->curr_opps->opps[level];
+	return readx_poll_timeout_atomic(get_current_opp, dvfsrc, curr,
+		curr->dram_opp >= target->dram_opp,
+		STARTUP_TIME, POLL_TIMEOUT);
+}
+
+static u32 mt6873_get_vcore_level(struct mtk_dvfsrc *dvfsrc)
+{
+	return (dvfsrc_read(dvfsrc, DVFSRC_SW_REQ) >> 4) & 0x7;
+}
+
+static u32 mt6873_get_dram_level(struct mtk_dvfsrc *dvfsrc)
+{
+	return (dvfsrc_read(dvfsrc, DVFSRC_SW_REQ) >> 12) & 0x7;
+}
+
+static u32 mt6873_get_vcp_level(struct mtk_dvfsrc *dvfsrc)
+{
+	return (dvfsrc_read(dvfsrc, DVFSRC_VCORE_REQUEST) >> 12) & 0x7;
+}
+
+static void mt6873_set_dram_bw(struct mtk_dvfsrc *dvfsrc, u64 bw)
+{
+	bw = div_u64(kbps_to_mbps(bw), 100);
+	bw = min_t(u64, bw, 0xFF);
+	dvfsrc_write(dvfsrc, DVFSRC_SW_BW, bw);
+}
+
+static void mt6873_set_dram_peak_bw(struct mtk_dvfsrc *dvfsrc, u64 bw)
+{
+	bw = div_u64(kbps_to_mbps(bw), 100);
+	bw = min_t(u64, bw, 0xFF);
+	dvfsrc_write(dvfsrc, DVFSRC_SW_PEAK_BW, bw);
+}
+
+static void mt6873_set_dram_hrtbw(struct mtk_dvfsrc *dvfsrc, u64 bw)
+{
+	bw = div_u64((kbps_to_mbps(bw) + 29), 30);
+	bw = min_t(u64, bw, 0x3FF);
+	dvfsrc_write(dvfsrc, DVFSRC_SW_HRT_BW, bw);
+}
+
+static void mt6873_set_dram_level(struct mtk_dvfsrc *dvfsrc, u32 level)
+{
+	mutex_lock(&dvfsrc->req_lock);
+	dvfsrc_rmw(dvfsrc, DVFSRC_SW_REQ, level, 0x7, 12);
+	mutex_unlock(&dvfsrc->req_lock);
+}
+
+static void mt6873_set_vcore_level(struct mtk_dvfsrc *dvfsrc, u32 level)
+{
+	mutex_lock(&dvfsrc->req_lock);
+	dvfsrc_rmw(dvfsrc, DVFSRC_SW_REQ, level, 0x7, 4);
+	mutex_unlock(&dvfsrc->req_lock);
+}
+
+static void mt6873_set_vscp_level(struct mtk_dvfsrc *dvfsrc, u32 level)
+{
+	dvfsrc_rmw(dvfsrc, DVFSRC_VCORE_REQUEST, level, 0x7, 12);
+}
+
+static void mt6873_set_opp_level(struct mtk_dvfsrc *dvfsrc, u32 level)
+{
+	const struct dvfsrc_opp *opp;
+
+	opp = &dvfsrc->curr_opps->opps[level];
+	mt6873_set_dram_level(dvfsrc, opp->dram_opp);
+}
+
+#ifdef DVFSRC_FORCE_OPP_SUPPORT
+static void mt6873_set_force_opp_level(struct mtk_dvfsrc *dvfsrc, u32 level)
+{
+	unsigned long flags;
+	int val;
+	int ret = 0;
+
+	spin_lock_irqsave(&dvfsrc->force_lock, flags);
+	dvfsrc->opp_forced = true;
+	if (level > dvfsrc->curr_opps->num_opp - 1) {
+		dvfsrc_rmw(dvfsrc, DVFSRC_BASIC_CONTROL, 0, 0x1, 15);
+		dvfsrc_write(dvfsrc, DVFSRC_TARGET_FORCE, 0);
+		dvfsrc->opp_forced = false;
+		goto out;
+	}
+	dvfsrc_write(dvfsrc, DVFSRC_TARGET_FORCE, 1 << level);
+	dvfsrc_rmw(dvfsrc, DVFSRC_BASIC_CONTROL, 1, 0x1, 15);
+	ret = readl_poll_timeout_atomic(
+			dvfsrc->regs + dvfsrc->dvd->regs[DVFSRC_LEVEL],
+			val, val == (1 << level), STARTUP_TIME, POLL_TIMEOUT);
+	dvfsrc_write(dvfsrc, DVFSRC_TARGET_FORCE, 0);
+out:
+	spin_unlock_irqrestore(&dvfsrc->force_lock, flags);
+	if (ret < 0) {
+		dev_info(dvfsrc->dev,
+			"[%s] wait idle, level: %d, last: %d -> %x\n",
+			__func__, level,
+			dvfsrc->dvd->get_current_level(dvfsrc),
+			dvfsrc->dvd->get_target_level(dvfsrc));
+	}
+}
+#endif
+
+void mtk_dvfsrc_send_request(const struct device *dev, u32 cmd, u64 data)
+{
+	int ret = 0;
+	struct mtk_dvfsrc *dvfsrc = dev_get_drvdata(dev);
+
+	dev_dbg(dvfsrc->dev, "cmd: %d, data: %llu\n", cmd, data);
+
+	switch (cmd) {
+	case MTK_DVFSRC_CMD_BW_REQUEST:
+		dvfsrc->dvd->set_dram_bw(dvfsrc, data);
+		goto out;
+	case MTK_DVFSRC_CMD_PEAK_BW_REQUEST:
+		if (dvfsrc->dvd->set_dram_peak_bw)
+			dvfsrc->dvd->set_dram_peak_bw(dvfsrc, data);
+		goto out;
+	case MTK_DVFSRC_CMD_OPP_REQUEST:
+		dvfsrc->dvd->set_opp_level(dvfsrc, data);
+		break;
+	case MTK_DVFSRC_CMD_VCORE_REQUEST:
+		dvfsrc->dvd->set_vcore_level(dvfsrc, data);
+		break;
+	case MTK_DVFSRC_CMD_HRTBW_REQUEST:
+		if (dvfsrc->dvd->set_dram_hrtbw)
+			dvfsrc->dvd->set_dram_hrtbw(dvfsrc, data);
+		else
+			goto out;
+		break;
+	case MTK_DVFSRC_CMD_DRAM_REQUEST:
+		dvfsrc->dvd->set_dram_level(dvfsrc, data);
+		break;
+	case MTK_DVFSRC_CMD_VSCP_REQUEST:
+		dvfsrc->dvd->set_vscp_level(dvfsrc, data);
+		break;
+#ifdef DVFSRC_FORCE_OPP_SUPPORT
+	case MTK_DVFSRC_CMD_FORCE_OPP_REQUEST:
+		if (dvfsrc->dvd->set_force_opp_level)
+			dvfsrc->dvd->set_force_opp_level(dvfsrc, data);
+		goto out;
+#endif
+	default:
+		dev_err(dvfsrc->dev, "unknown command: %d\n", cmd);
+		goto out;
+	}
+
+	if (!dvfsrc->dvfsrc_enable)
+		return;
+
+#ifdef DVFSRC_FORCE_OPP_SUPPORT
+	if (dvfsrc->opp_forced)
+		return;
+#endif
+	/* DVFSRC need to wait at least 2T(~196ns) to handle request
+	 * after recieving command
+	 */
+	udelay(STARTUP_TIME);
+	dvfsrc_wait_for_idle(dvfsrc);
+	/* The previous change may be requested by previous request.
+	 * So we delay 1us , then start checking opp is reached enough.
+	 */
+	udelay(STARTUP_TIME);
+
+	switch (cmd) {
+	case MTK_DVFSRC_CMD_OPP_REQUEST:
+		if (dvfsrc->dvd->wait_for_opp_level)
+			ret = dvfsrc->dvd->wait_for_opp_level(dvfsrc, data);
+		break;
+	case MTK_DVFSRC_CMD_VCORE_REQUEST:
+	case MTK_DVFSRC_CMD_VSCP_REQUEST:
+		ret = dvfsrc->dvd->wait_for_vcore_level(dvfsrc, data);
+		break;
+	case MTK_DVFSRC_CMD_DRAM_REQUEST:
+		ret = dvfsrc->dvd->wait_for_dram_level(dvfsrc, data);
+		break;
+	}
+out:
+	if (ret < 0) {
+		dev_warn(dvfsrc->dev,
+			 "%d: idle timeout, data: %llu, last: %d -> %d\n",
+			 cmd, data,
+			 dvfsrc->dvd->get_current_level(dvfsrc),
+			 dvfsrc->dvd->get_target_level(dvfsrc));
+	}
+}
+EXPORT_SYMBOL(mtk_dvfsrc_send_request);
+
+int mtk_dvfsrc_query_info(const struct device *dev, u32 cmd, int *data)
+{
+	struct mtk_dvfsrc *dvfsrc = dev_get_drvdata(dev);
+
+	switch (cmd) {
+	case MTK_DVFSRC_CMD_VCORE_LEVEL_QUERY:
+		*data = dvfsrc->dvd->get_vcore_level(dvfsrc);
+		break;
+	case MTK_DVFSRC_CMD_VSCP_LEVEL_QUERY:
+		*data = dvfsrc->dvd->get_vcp_level(dvfsrc);
+		break;
+	case MTK_DVFSRC_CMD_DRAM_LEVEL_QUERY:
+		*data = dvfsrc->dvd->get_dram_level(dvfsrc);
+		break;
+	case MTK_DVFSRC_CMD_CURR_LEVEL_QUERY:
+		*data = dvfsrc->dvd->get_current_level(dvfsrc);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(mtk_dvfsrc_query_info);
+
+void mtk_dvfsrc_force_opp(int opp)
+{
+#ifdef DVFSRC_FORCE_OPP_SUPPORT
+	if (dvfsrc_data)
+		mtk_dvfsrc_send_request(dvfsrc_data->dev,
+				MTK_DVFSRC_CMD_FORCE_OPP_REQUEST,
+				opp);
+#endif
+}
+EXPORT_SYMBOL(mtk_dvfsrc_force_opp);
+
+static int dvfsrc_set_performance(struct notifier_block *b,
+				  unsigned long pstate, void *v)
+{
+	bool match = false;
+	int i;
+	struct mtk_dvfsrc *dvfsrc;
+	struct scp_event_data *sc = v;
+	struct dvfsrc_domain *d;
+	u32 highest;
+
+	if (sc->event_type != MTK_SCPSYS_PSTATE)
+		return 0;
+
+	dvfsrc = container_of(b, struct mtk_dvfsrc, scpsys_notifier);
+
+	/* feature not support */
+	if (!dvfsrc->dvd->num_domains)
+		return 0;
+
+	d = dvfsrc->dvd->domains;
+
+	if (pstate > dvfsrc->curr_opps->num_opp) {
+		dev_err(dvfsrc->dev, "pstate out of range = %ld\n", pstate);
+		return 0;
+	}
+
+	mutex_lock(&dvfsrc->pstate_lock);
+
+	for (i = 0, highest = 0; i < dvfsrc->dvd->num_domains; i++, d++) {
+		if (sc->domain_id == d->id) {
+			d->state = pstate;
+			match = true;
+		}
+		highest = max(highest, d->state);
+	}
+
+	if (!match)
+		goto out;
+
+	/* pstat start from level 1, array index start from 0 */
+	mtk_dvfsrc_send_request(dvfsrc->dev, MTK_DVFSRC_CMD_OPP_REQUEST,
+				highest - 1);
+
+out:
+	mutex_unlock(&dvfsrc->pstate_lock);
+	return 0;
+}
+
+static void pstate_notifier_register(struct mtk_dvfsrc *dvfsrc)
+{
+	dvfsrc->scpsys_notifier.notifier_call = dvfsrc_set_performance;
+	register_scpsys_notifier(&dvfsrc->scpsys_notifier);
+}
+
+static int mtk_dvfsrc_probe(struct platform_device *pdev)
+{
+	struct arm_smccc_res ares;
+	struct resource *res;
+	struct mtk_dvfsrc *dvfsrc;
+	int ret;
+	u32 is_bringup = 0;
+	u32 dvfsrc_flag = 0;
+	u32 dvfsrc_vmode = 0;
+#ifdef DVFSRC_PROPERTY_ENABLE
+	struct device_node *np = pdev->dev.of_node;
+#endif
+
+	dvfsrc = devm_kzalloc(&pdev->dev, sizeof(*dvfsrc), GFP_KERNEL);
+	if (!dvfsrc)
+		return -ENOMEM;
+
+	dvfsrc->dvd = of_device_get_match_data(&pdev->dev);
+	dvfsrc->dev = &pdev->dev;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	dvfsrc->regs = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(dvfsrc->regs))
+		return PTR_ERR(dvfsrc->regs);
+
+	mutex_init(&dvfsrc->req_lock);
+	mutex_init(&dvfsrc->pstate_lock);
+#ifdef DVFSRC_FORCE_OPP_SUPPORT
+	spin_lock_init(&dvfsrc->force_lock);
+#endif
+
+#ifdef DVFSRC_PROPERTY_ENABLE
+	of_property_read_u32(np, "dvfsrc,bringup", &is_bringup);
+	of_property_read_u32(np, "dvfsrc_flag", &dvfsrc_flag);
+	of_property_read_u32(np, "dvfsrc_vmode", &dvfsrc_vmode);
+#endif
+	if (!is_bringup) {
+		arm_smccc_smc(MTK_SIP_VCOREFS_CONTROL, MTK_SIP_DVFSRC_INIT,
+			      dvfsrc_flag, dvfsrc_vmode, 0, 0, 0, 0, &ares);
+		if (!ares.a0) {
+			dvfsrc->dram_type = ares.a1;
+#ifdef DVFSRC_OPP_BW_QUERY
+			dram_type = dvfsrc->dram_type;
+#endif
+			dvfsrc->dvd->dvfsrc_hw_init(dvfsrc);
+			dvfsrc->dvfsrc_enable = true;
+		} else
+			dev_info(dvfsrc->dev, "dvfs mode is disabled\n");
+	} else
+		dev_info(dvfsrc->dev, "dvfs mode is bringup mode\n");
+	dvfsrc->curr_opps = &dvfsrc->dvd->opps_desc[dvfsrc->dram_type];
+	platform_set_drvdata(pdev, dvfsrc);
+	if (dvfsrc->dvd->num_domains)
+		pstate_notifier_register(dvfsrc);
+
+	dvfsrc->regulator = platform_device_register_data(dvfsrc->dev,
+			"mtk-dvfsrc-regulator", -1, NULL, 0);
+	if (IS_ERR(dvfsrc->regulator)) {
+		dev_err(dvfsrc->dev, "Failed create regulator device\n");
+		ret = PTR_ERR(dvfsrc->regulator);
+		goto err;
+	}
+
+	dvfsrc->icc = platform_device_register_data(dvfsrc->dev,
+			"mediatek-emi-icc", -1, NULL, 0);
+	if (IS_ERR(dvfsrc->icc)) {
+		dev_err(dvfsrc->dev, "Failed create icc device\n");
+		ret = PTR_ERR(dvfsrc->icc);
+		goto unregister_regulator;
+	}
+
+	dvfsrc->devfreq = platform_device_register_data(dvfsrc->dev,
+			"mtk-dvfsrc-devfreq", -1, NULL, 0);
+	if (IS_ERR(dvfsrc->devfreq)) {
+		dev_err(dvfsrc->dev, "Failed create devfreq device\n");
+		ret = PTR_ERR(dvfsrc->devfreq);
+		goto unregister_icc;
+	}
+
+	dvfsrc->dvfsrc_start = platform_device_register_data(dvfsrc->dev,
+			"mtk-dvfsrc-start", -1, NULL, 0);
+	if (IS_ERR(dvfsrc->dvfsrc_start)) {
+		dev_err(dvfsrc->dev, "Failed create dvfsrc-start device\n");
+		ret = PTR_ERR(dvfsrc->dvfsrc_start);
+		goto unregister_devfreq;
+	}
+
+	ret = devm_of_platform_populate(dvfsrc->dev);
+	if (ret < 0)
+		goto unregister_start;
+
+	dvfsrc_data = dvfsrc;
+	return 0;
+
+unregister_start:
+	platform_device_unregister(dvfsrc->dvfsrc_start);
+unregister_devfreq:
+	platform_device_unregister(dvfsrc->devfreq);
+unregister_icc:
+	platform_device_unregister(dvfsrc->icc);
+unregister_regulator:
+	platform_device_unregister(dvfsrc->regulator);
+err:
+	return ret;
+}
+
+static const struct dvfsrc_opp dvfsrc_opp_mt8183_lp4[] = {
+	{0, 0}, {0, 1}, {0, 2}, {1, 2},
+};
+
+static const struct dvfsrc_opp dvfsrc_opp_mt8183_lp3[] = {
+	{0, 0}, {0, 1}, {1, 1}, {1, 2},
+};
+
+static const struct dvfsrc_opp_desc dvfsrc_opp_mt8183_desc[] = {
+	DVFSRC_OPP_DESC(dvfsrc_opp_mt8183_lp4),
+	DVFSRC_OPP_DESC(dvfsrc_opp_mt8183_lp3),
+	DVFSRC_OPP_DESC(dvfsrc_opp_mt8183_lp3),
+};
+
+
+static const struct dvfsrc_soc_data mt8183_data = {
+	.opps_desc = dvfsrc_opp_mt8183_desc,
+	.num_opp_desc = ARRAY_SIZE(dvfsrc_opp_mt8183_desc),
+	.regs = mt8183_regs,
+	.get_target_level = mt8183_get_target_level,
+	.get_current_level = mt8183_get_current_level,
+	.get_vcore_level = mt8183_get_vcore_level,
+	.set_dram_bw = mt8183_set_dram_bw,
+	.set_opp_level = mt8183_set_opp_level,
+	.set_vcore_level = mt8183_set_vcore_level,
+	.wait_for_opp_level = mt8183_wait_for_opp_level,
+	.wait_for_vcore_level = dvfsrc_wait_for_vcore_level,
+};
+
+static void dvfsrc_init_mt6873(struct mtk_dvfsrc *dvfsrc)
+{
+	/* Setup opp table */
+	dvfsrc_write(dvfsrc, DVFSRC_LEVEL_LABEL_0_1, 0x50436053);
+	dvfsrc_write(dvfsrc, DVFSRC_LEVEL_LABEL_2_3, 0x40335042);
+	dvfsrc_write(dvfsrc, DVFSRC_LEVEL_LABEL_4_5, 0x40314032);
+	dvfsrc_write(dvfsrc, DVFSRC_LEVEL_LABEL_6_7, 0x30223023);
+	dvfsrc_write(dvfsrc, DVFSRC_LEVEL_LABEL_8_9, 0x20133021);
+	dvfsrc_write(dvfsrc, DVFSRC_LEVEL_LABEL_10_11, 0x20112012);
+	dvfsrc_write(dvfsrc, DVFSRC_LEVEL_LABEL_12_13, 0x10032010);
+	dvfsrc_write(dvfsrc, DVFSRC_LEVEL_LABEL_14_15, 0x10011002);
+	dvfsrc_write(dvfsrc, DVFSRC_LEVEL_LABEL_16_17, 0x00131000);
+	dvfsrc_write(dvfsrc, DVFSRC_LEVEL_LABEL_18_19, 0x00110012);
+	dvfsrc_write(dvfsrc, DVFSRC_LEVEL_LABEL_20_21, 0x00000010);
+	/* Setup hw emi qos policy */
+	dvfsrc_write(dvfsrc, DVFSRC_DDR_REQUEST, 0x00004321);
+	dvfsrc_write(dvfsrc, DVFSRC_DDR_REQUEST3, 0x00000065);
+	/* Setup up HRT QOS policy */
+	dvfsrc_write(dvfsrc, DVFSRC_HRT_BW_BASE, 0x00000004);
+	dvfsrc_write(dvfsrc, DVFSRC_HRT_REQ_UNIT, 0x0000001E);
+	dvfsrc_write(dvfsrc, DVFSRC_HRT_HIGH_3, 0x18A618A6);
+	dvfsrc_write(dvfsrc, DVFSRC_HRT_HIGH_2, 0x18A61183);
+	dvfsrc_write(dvfsrc, DVFSRC_HRT_HIGH_1, 0x0D690B80);
+	dvfsrc_write(dvfsrc, DVFSRC_HRT_HIGH, 0x070804B0);
+	dvfsrc_write(dvfsrc, DVFSRC_HRT_LOW_3, 0x18A518A5);
+	dvfsrc_write(dvfsrc, DVFSRC_HRT_LOW_2, 0x18A51182);
+	dvfsrc_write(dvfsrc, DVFSRC_HRT_LOW_1, 0x0D680B7F);
+	dvfsrc_write(dvfsrc, DVFSRC_HRT_LOW, 0x070704AF);
+	dvfsrc_write(dvfsrc, DVFSRC_HRT_REQUEST, 0x66654321);
+	/* Setup up SRT QOS policy */
+	dvfsrc_write(dvfsrc, DVFSRC_QOS_EN, 0x0011007C);
+	dvfsrc_write(dvfsrc, DVFSRC_DDR_QOS0, 0x00000019);
+	dvfsrc_write(dvfsrc, DVFSRC_DDR_QOS1, 0x00000026);
+	dvfsrc_write(dvfsrc, DVFSRC_DDR_QOS2, 0x00000033);
+	dvfsrc_write(dvfsrc, DVFSRC_DDR_QOS3, 0x0000003B);
+	dvfsrc_write(dvfsrc, DVFSRC_DDR_QOS4, 0x0000004C);
+	dvfsrc_write(dvfsrc, DVFSRC_DDR_QOS5, 0x00000066);
+	dvfsrc_write(dvfsrc, DVFSRC_DDR_QOS6, 0x00000066);
+	dvfsrc_write(dvfsrc, DVFSRC_DDR_REQUEST5, 0x54321000);
+	dvfsrc_write(dvfsrc, DVFSRC_DDR_REQUEST7, 0x66000000);
+	/* Setup up MD request policy */
+	dvfsrc_write(dvfsrc, DVFSRC_BASIC_CONTROL_3, 0x00000006);
+	dvfsrc_write(dvfsrc, DVFSRC_DEBOUNCE_TIME, 0x00001965);
+	dvfsrc_write(dvfsrc, DVFSRC_MD_LATENCY_IMPROVE, 0x00000040);
+	dvfsrc_write(dvfsrc, DVFSRC_DDR_ADD_REQUEST, 0x66543210);
+	dvfsrc_write(dvfsrc, DVFSRC_EMI_MON_DEBOUNCE_TIME, 0x4C2D0000);
+	dvfsrc_write(dvfsrc, DVFSRC_LEVEL_MASK, 0x000EE000);
+	dvfsrc_write(dvfsrc, DVSFRC_HRT_REQ_MD_URG, 0x000D20D2);
+	dvfsrc_write(dvfsrc, DVFSRC_HRT_REQ_MD_BW_0, 0x00200802);
+	dvfsrc_write(dvfsrc, DVFSRC_HRT_REQ_MD_BW_1, 0x00200802);
+	dvfsrc_write(dvfsrc, DVFSRC_HRT_REQ_MD_BW_2, 0x00200800);
+	dvfsrc_write(dvfsrc, DVFSRC_HRT_REQ_MD_BW_3, 0x00400802);
+	dvfsrc_write(dvfsrc, DVFSRC_HRT_REQ_MD_BW_4, 0x00601404);
+	dvfsrc_write(dvfsrc, DVFSRC_HRT_REQ_MD_BW_5, 0x00D02C09);
+	dvfsrc_write(dvfsrc, DVFSRC_HRT_REQ_MD_BW_6, 0x00000012);
+	dvfsrc_write(dvfsrc, DVFSRC_HRT_REQ_MD_BW_7, 0x00000024);
+	dvfsrc_write(dvfsrc, DVFSRC_HRT_REQ_MD_BW_8, 0x00000000);
+	dvfsrc_write(dvfsrc, DVFSRC_HRT_REQ_MD_BW_9, 0x00000000);
+	dvfsrc_write(dvfsrc, DVFSRC_HRT_REQ_MD_BW_10, 0x00034800);
+	dvfsrc_write(dvfsrc, DVFSRC_HRT1_REQ_MD_BW_0, 0x04B12C4B);
+	dvfsrc_write(dvfsrc, DVFSRC_HRT1_REQ_MD_BW_1, 0x04B12C4B);
+	dvfsrc_write(dvfsrc, DVFSRC_HRT1_REQ_MD_BW_2, 0x04B12C00);
+	dvfsrc_write(dvfsrc, DVFSRC_HRT1_REQ_MD_BW_3, 0x04B12C4B);
+	dvfsrc_write(dvfsrc, DVFSRC_HRT1_REQ_MD_BW_4, 0x04B12C4B);
+	dvfsrc_write(dvfsrc, DVFSRC_HRT1_REQ_MD_BW_5, 0x04B12C4B);
+	dvfsrc_write(dvfsrc, DVFSRC_HRT1_REQ_MD_BW_6, 0x0000004B);
+	dvfsrc_write(dvfsrc, DVFSRC_HRT1_REQ_MD_BW_7, 0x0000005C);
+	dvfsrc_write(dvfsrc, DVFSRC_HRT1_REQ_MD_BW_8, 0x00000000);
+	dvfsrc_write(dvfsrc, DVFSRC_HRT1_REQ_MD_BW_9, 0x00000000);
+	dvfsrc_write(dvfsrc, DVFSRC_HRT1_REQ_MD_BW_10, 0x00034800);
+	dvfsrc_write(dvfsrc, DVFSRC_95MD_SCEN_BW0_T, 0x40444440);
+	dvfsrc_write(dvfsrc, DVFSRC_95MD_SCEN_BW1_T, 0x44444444);
+	dvfsrc_write(dvfsrc, DVFSRC_95MD_SCEN_BW2_T, 0x00400444);
+	dvfsrc_write(dvfsrc, DVFSRC_95MD_SCEN_BW3_T, 0x60000000);
+	dvfsrc_write(dvfsrc, DVFSRC_95MD_SCEN_BW0, 0x20222220);
+	dvfsrc_write(dvfsrc, DVFSRC_95MD_SCEN_BW1, 0x22222222);
+	dvfsrc_write(dvfsrc, DVFSRC_95MD_SCEN_BW2, 0x00200222);
+	dvfsrc_write(dvfsrc, DVFSRC_95MD_SCEN_BW3, 0x60000000);
+	dvfsrc_write(dvfsrc, DVFSRC_95MD_SCEN_BW4, 0x00000006);
+	/* Setup up hifi request policy */
+	dvfsrc_write(dvfsrc, DVFSRC_DDR_REQUEST6, 0x66543210);
+	/* Setup up hw request vcore policy */
+	dvfsrc_write(dvfsrc, DVFSRC_VCORE_USER_REQ, 0x00010A29);
+	/* Setup misc*/
+	dvfsrc_write(dvfsrc, DVFSRC_TIMEOUT_NEXTREQ, 0x00000015);
+	dvfsrc_write(dvfsrc, DVFSRC_RSRV_5, 0x00000001);
+	dvfsrc_write(dvfsrc, DVFSRC_INT_EN, 0x00000002);
+	/* Init opp and trigger dvfsrc to run*/
+	dvfsrc_write(dvfsrc, DVFSRC_CURRENT_FORCE, 0x00000001);
+	dvfsrc_write(dvfsrc, DVFSRC_BASIC_CONTROL, 0x6698444B);
+	dvfsrc_write(dvfsrc, DVFSRC_BASIC_CONTROL, 0x6698054B);
+	dvfsrc_write(dvfsrc, DVFSRC_CURRENT_FORCE, 0x00000000);
+}
+
+static const struct dvfsrc_opp dvfsrc_opp_mt6873_lp4[] = {
+	{0, 0}, {1, 0}, {2, 0}, {3, 0},
+	{0, 1}, {1, 1}, {2, 1}, {3, 1},
+	{0, 2}, {1, 2}, {2, 2}, {3, 2},
+	{1, 3}, {2, 3}, {3, 3}, {1, 4},
+	{2, 4}, {3, 4}, {2, 5}, {3, 5},
+	{3, 6},
+};
+
+static const struct dvfsrc_opp_desc dvfsrc_opp_mt6873_desc[] = {
+	DVFSRC_OPP_DESC(dvfsrc_opp_mt6873_lp4),
+};
+
+static const struct dvfsrc_soc_data mt6873_data = {
+	.opps_desc = dvfsrc_opp_mt6873_desc,
+	.num_opp_desc = ARRAY_SIZE(dvfsrc_opp_mt6873_desc),
+	.regs = mt6873_regs,
+	.dvfsrc_hw_init = dvfsrc_init_mt6873,
+	.get_target_level = mt6873_get_target_level,
+	.get_current_level = mt6873_get_current_level,
+	.get_vcore_level = mt6873_get_vcore_level,
+	.get_vcp_level = mt6873_get_vcp_level,
+	.get_dram_level = mt6873_get_dram_level,
+	.set_dram_bw = mt6873_set_dram_bw,
+	.set_dram_peak_bw = mt6873_set_dram_peak_bw,
+	.set_opp_level = mt6873_set_opp_level,
+	.set_dram_level = mt6873_set_dram_level,
+	.set_dram_hrtbw = mt6873_set_dram_hrtbw,
+	.set_vcore_level = mt6873_set_vcore_level,
+	.set_vscp_level = mt6873_set_vscp_level,
+	.wait_for_opp_level = mt6873_wait_for_opp_level,
+	.wait_for_vcore_level = dvfsrc_wait_for_vcore_level,
+	.wait_for_dram_level = dvfsrc_wait_for_dram_level,
+#ifdef DVFSRC_FORCE_OPP_SUPPORT
+	.set_force_opp_level = mt6873_set_force_opp_level,
+#endif
+};
+
+static void dvfsrc_init_mt6880(struct mtk_dvfsrc *dvfsrc)
+{
+	struct device_node *of_node = dvfsrc->dev->of_node;
+
+	/* Setup opp table */
+	dvfsrc_write(dvfsrc, DVFSRC_LEVEL_LABEL_0_1, 0x50436053);
+	dvfsrc_write(dvfsrc, DVFSRC_LEVEL_LABEL_2_3, 0x40335042);
+	dvfsrc_write(dvfsrc, DVFSRC_LEVEL_LABEL_4_5, 0x40314032);
+	dvfsrc_write(dvfsrc, DVFSRC_LEVEL_LABEL_6_7, 0x30223023);
+	dvfsrc_write(dvfsrc, DVFSRC_LEVEL_LABEL_8_9, 0x20133021);
+	dvfsrc_write(dvfsrc, DVFSRC_LEVEL_LABEL_10_11, 0x20112012);
+	dvfsrc_write(dvfsrc, DVFSRC_LEVEL_LABEL_12_13, 0x10032010);
+	dvfsrc_write(dvfsrc, DVFSRC_LEVEL_LABEL_14_15, 0x10011002);
+	dvfsrc_write(dvfsrc, DVFSRC_LEVEL_LABEL_16_17, 0x00131000);
+	dvfsrc_write(dvfsrc, DVFSRC_LEVEL_LABEL_18_19, 0x00110012);
+	dvfsrc_write(dvfsrc, DVFSRC_LEVEL_LABEL_20_21, 0x00000010);
+	/* Setup hw emi qos policy */
+	dvfsrc_write(dvfsrc, DVFSRC_DDR_REQUEST, 0x00004321);
+	dvfsrc_write(dvfsrc, DVFSRC_DDR_REQUEST3, 0x00000065);
+	/* Setup up HRT QOS policy */
+	dvfsrc_write(dvfsrc, DVFSRC_HRT_BW_BASE, 0x00000004);
+	dvfsrc_write(dvfsrc, DVFSRC_HRT_REQ_UNIT, 0x0000001E);
+	dvfsrc_write(dvfsrc, DVFSRC_HRT_HIGH_3, 0x18A618A6);
+	dvfsrc_write(dvfsrc, DVFSRC_HRT_HIGH_2, 0x18A61183);
+	dvfsrc_write(dvfsrc, DVFSRC_HRT_HIGH_1, 0x0D690B80);
+	dvfsrc_write(dvfsrc, DVFSRC_HRT_HIGH, 0x070804B0);
+	dvfsrc_write(dvfsrc, DVFSRC_HRT_LOW_3, 0x18A518A5);
+	dvfsrc_write(dvfsrc, DVFSRC_HRT_LOW_2, 0x18A51182);
+	dvfsrc_write(dvfsrc, DVFSRC_HRT_LOW_1, 0x0D680B7F);
+	dvfsrc_write(dvfsrc, DVFSRC_HRT_LOW, 0x070704AF);
+	dvfsrc_write(dvfsrc, DVFSRC_HRT_REQUEST, 0x66654321);
+	/* Setup up SRT QOS policy */
+	dvfsrc_write(dvfsrc, DVFSRC_QOS_EN, 0x0011007C);
+	dvfsrc_write(dvfsrc, DVFSRC_DDR_QOS0, 0x00000019);
+	dvfsrc_write(dvfsrc, DVFSRC_DDR_QOS1, 0x00000026);
+	dvfsrc_write(dvfsrc, DVFSRC_DDR_QOS2, 0x00000033);
+	dvfsrc_write(dvfsrc, DVFSRC_DDR_QOS3, 0x0000003B);
+	dvfsrc_write(dvfsrc, DVFSRC_DDR_QOS4, 0x0000004C);
+	dvfsrc_write(dvfsrc, DVFSRC_DDR_QOS5, 0x00000066);
+	dvfsrc_write(dvfsrc, DVFSRC_DDR_QOS6, 0x00000066);
+	dvfsrc_write(dvfsrc, DVFSRC_DDR_REQUEST5, 0x54321000);
+	dvfsrc_write(dvfsrc, DVFSRC_DDR_REQUEST7, 0x66000000);
+	/* Setup up MD request policy */
+	dvfsrc_write(dvfsrc, DVFSRC_BASIC_CONTROL_3, 0x00000006);
+	dvfsrc_write(dvfsrc, DVFSRC_DEBOUNCE_TIME, 0x00001965);
+	dvfsrc_write(dvfsrc, DVFSRC_MD_LATENCY_IMPROVE, 0x00000040);
+	dvfsrc_write(dvfsrc, DVFSRC_DDR_ADD_REQUEST, 0x66543210);
+	dvfsrc_write(dvfsrc, DVFSRC_EMI_MON_DEBOUNCE_TIME, 0x4C2D0000);
+	dvfsrc_write(dvfsrc, DVFSRC_LEVEL_MASK, 0x000EE000);
+	/* TODO */
+	dvfsrc_write(dvfsrc, DVSFRC_HRT_REQ_MD_URG, 0x000D20D2);
+	dvfsrc_write(dvfsrc, DVFSRC_HRT_REQ_MD_BW_0, 0x00200802);
+	dvfsrc_write(dvfsrc, DVFSRC_HRT_REQ_MD_BW_1, 0x00200802);
+	dvfsrc_write(dvfsrc, DVFSRC_HRT_REQ_MD_BW_2, 0x00200800);
+	dvfsrc_write(dvfsrc, DVFSRC_HRT_REQ_MD_BW_3, 0x00400802);
+	dvfsrc_write(dvfsrc, DVFSRC_HRT_REQ_MD_BW_4, 0x00601404);
+	dvfsrc_write(dvfsrc, DVFSRC_HRT_REQ_MD_BW_5, 0x00D02C09);
+	dvfsrc_write(dvfsrc, DVFSRC_HRT_REQ_MD_BW_6, 0x00000012);
+	dvfsrc_write(dvfsrc, DVFSRC_HRT_REQ_MD_BW_7, 0x00000024);
+	dvfsrc_write(dvfsrc, DVFSRC_HRT_REQ_MD_BW_8, 0x00000000);
+	dvfsrc_write(dvfsrc, DVFSRC_HRT_REQ_MD_BW_9, 0x00000000);
+	dvfsrc_write(dvfsrc, DVFSRC_HRT_REQ_MD_BW_10, 0x00034800);
+	dvfsrc_write(dvfsrc, DVFSRC_HRT1_REQ_MD_BW_0, 0x04B12C4B);
+	dvfsrc_write(dvfsrc, DVFSRC_HRT1_REQ_MD_BW_1, 0x04B12C4B);
+	dvfsrc_write(dvfsrc, DVFSRC_HRT1_REQ_MD_BW_2, 0x04B12C00);
+	dvfsrc_write(dvfsrc, DVFSRC_HRT1_REQ_MD_BW_3, 0x04B12C4B);
+	dvfsrc_write(dvfsrc, DVFSRC_HRT1_REQ_MD_BW_4, 0x04B12C4B);
+	dvfsrc_write(dvfsrc, DVFSRC_HRT1_REQ_MD_BW_5, 0x04B12C4B);
+	dvfsrc_write(dvfsrc, DVFSRC_HRT1_REQ_MD_BW_6, 0x0000004B);
+	dvfsrc_write(dvfsrc, DVFSRC_HRT1_REQ_MD_BW_7, 0x0000005C);
+	dvfsrc_write(dvfsrc, DVFSRC_HRT1_REQ_MD_BW_8, 0x00000000);
+	dvfsrc_write(dvfsrc, DVFSRC_HRT1_REQ_MD_BW_9, 0x00000000);
+	dvfsrc_write(dvfsrc, DVFSRC_HRT1_REQ_MD_BW_10, 0x00034800);
+
+	if (of_device_is_compatible(of_node, "mediatek,mt6890-dvfsrc")) {
+		dvfsrc_write(dvfsrc, DVFSRC_95MD_SCEN_BW0_T, 0x44444440);
+		dvfsrc_write(dvfsrc, DVFSRC_95MD_SCEN_BW1_T, 0x22244444);
+		dvfsrc_write(dvfsrc, DVFSRC_95MD_SCEN_BW2_T, 0x00400444);
+		dvfsrc_write(dvfsrc, DVFSRC_95MD_SCEN_BW3_T, 0x60000000);
+		dvfsrc_write(dvfsrc, DVFSRC_95MD_SCEN_BW0, 0x22222220);
+		dvfsrc_write(dvfsrc, DVFSRC_95MD_SCEN_BW1, 0x00022222);
+		dvfsrc_write(dvfsrc, DVFSRC_95MD_SCEN_BW2, 0x00200222);
+		dvfsrc_write(dvfsrc, DVFSRC_95MD_SCEN_BW3, 0x60000000);
+		dvfsrc_write(dvfsrc, DVFSRC_95MD_SCEN_BW4, 0x00000006);
+	} else {
+		dvfsrc_write(dvfsrc, DVFSRC_95MD_SCEN_BW0_T, 0x22222220);
+		dvfsrc_write(dvfsrc, DVFSRC_95MD_SCEN_BW1_T, 0x22222222);
+		dvfsrc_write(dvfsrc, DVFSRC_95MD_SCEN_BW2_T, 0x00400422);
+		dvfsrc_write(dvfsrc, DVFSRC_95MD_SCEN_BW3_T, 0x60000000);
+		dvfsrc_write(dvfsrc, DVFSRC_95MD_SCEN_BW0, 0x22222220);
+		dvfsrc_write(dvfsrc, DVFSRC_95MD_SCEN_BW1, 0x22222222);
+		dvfsrc_write(dvfsrc, DVFSRC_95MD_SCEN_BW2, 0x00400422);
+		dvfsrc_write(dvfsrc, DVFSRC_95MD_SCEN_BW3, 0x60000000);
+		dvfsrc_write(dvfsrc, DVFSRC_95MD_SCEN_BW4, 0x00000006);
+	}
+	/* Setup up hw request vcore and ddr policy */
+	dvfsrc_write(dvfsrc, DVFSRC_VCORE_USER_REQ, 0x00010A29);
+	dvfsrc_write(dvfsrc, DVFSRC_BW_USER_REQ, 0x00010A29);
+	/* Setup misc*/
+	dvfsrc_write(dvfsrc, DVFSRC_TIMEOUT_NEXTREQ, 0x00000015);
+	dvfsrc_write(dvfsrc, DVFSRC_RSRV_5, 0x00000001);
+	dvfsrc_write(dvfsrc, DVFSRC_INT_EN, 0x00000002);
+	/* Init opp and trigger dvfsrc to run*/
+	dvfsrc_write(dvfsrc, DVFSRC_CURRENT_FORCE, 0x00000001);
+	dvfsrc_write(dvfsrc, DVFSRC_BASIC_CONTROL, 0x4290444B);
+	dvfsrc_write(dvfsrc, DVFSRC_BASIC_CONTROL, 0x4290054B);
+	dvfsrc_write(dvfsrc, DVFSRC_CURRENT_FORCE, 0x00000000);
+}
+
+
+static const struct dvfsrc_opp dvfsrc_opp_mt6880_lp4[] = {
+	{0, 0}, {1, 0}, {2, 0}, {3, 0},
+	{0, 1}, {1, 1}, {2, 1}, {3, 1},
+	{0, 2}, {1, 2}, {2, 2}, {3, 2},
+	{1, 3}, {2, 3}, {3, 3}, {1, 4},
+	{2, 4}, {3, 4}, {2, 5}, {3, 5},
+	{3, 6},
+};
+
+static const struct dvfsrc_opp_desc dvfsrc_opp_mt6880_desc[] = {
+	DVFSRC_OPP_DESC(dvfsrc_opp_mt6880_lp4),
+};
+
+static const struct dvfsrc_soc_data mt6880_data = {
+	.opps_desc = dvfsrc_opp_mt6880_desc,
+	.num_opp_desc = ARRAY_SIZE(dvfsrc_opp_mt6880_desc),
+	.regs = mt6873_regs,
+	.dvfsrc_hw_init = dvfsrc_init_mt6880,
+	.get_target_level = mt6873_get_target_level,
+	.get_current_level = mt6873_get_current_level,
+	.get_vcore_level = mt6873_get_vcore_level,
+	.get_vcp_level = mt6873_get_vcp_level,
+	.get_dram_level = mt6873_get_dram_level,
+	.set_dram_bw = mt6873_set_dram_bw,
+	.set_dram_peak_bw = mt6873_set_dram_peak_bw,
+	.set_opp_level = mt6873_set_opp_level,
+	.set_dram_level = mt6873_set_dram_level,
+	.set_dram_hrtbw = mt6873_set_dram_hrtbw,
+	.set_vcore_level = mt6873_set_vcore_level,
+	.set_vscp_level = mt6873_set_vscp_level,
+	.wait_for_opp_level = mt6873_wait_for_opp_level,
+	.wait_for_vcore_level = dvfsrc_wait_for_vcore_level,
+	.wait_for_dram_level = dvfsrc_wait_for_dram_level,
+#ifdef DVFSRC_FORCE_OPP_SUPPORT
+	.set_force_opp_level = mt6873_set_force_opp_level,
+#endif
+};
+
+static int mtk_dvfsrc_remove(struct platform_device *pdev)
+{
+	struct mtk_dvfsrc *dvfsrc = platform_get_drvdata(pdev);
+
+	platform_device_unregister(dvfsrc->regulator);
+	platform_device_unregister(dvfsrc->icc);
+
+	return 0;
+}
+
+static const struct of_device_id mtk_dvfsrc_of_match[] = {
+	{
+		.compatible = "mediatek,mt8183-dvfsrc",
+		.data = &mt8183_data,
+	}, {
+		.compatible = "mediatek,mt6873-dvfsrc",
+		.data = &mt6873_data,
+	}, {
+		.compatible = "mediatek,mt6880-dvfsrc",
+		.data = &mt6880_data,
+	}, {
+		.compatible = "mediatek,mt6890-dvfsrc",
+		.data = &mt6880_data,
+	}, {
+		/* sentinel */
+	},
+};
+
+static struct platform_driver mtk_dvfsrc_driver = {
+	.probe	= mtk_dvfsrc_probe,
+	.remove	= mtk_dvfsrc_remove,
+	.driver = {
+		.name = "mtk-dvfsrc",
+		.of_match_table = of_match_ptr(mtk_dvfsrc_of_match),
+	},
+};
+
+static int __init mtk_dvfsrc_init(void)
+{
+	return platform_driver_register(&mtk_dvfsrc_driver);
+}
+subsys_initcall(mtk_dvfsrc_init);
+
+static void __exit mtk_dvfsrc_exit(void)
+{
+	platform_driver_unregister(&mtk_dvfsrc_driver);
+}
+module_exit(mtk_dvfsrc_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MTK DVFSRC driver");