// 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 <dt-bindings/soc/mtk,dvfsrc.h>
#include "mtk-scpsys.h"
#include <linux/interrupt.h>
#include <linux/of_irq.h>
#if IS_ENABLED(CONFIG_MTK_DRAMC)
#include <soc/mediatek/dramc.h>
#endif

/* Private */
#define DVFSRC_OPP_BW_QUERY
#define DVFSRC_FORCE_OPP_SUPPORT
#define DVFSRC_DEBUG_ENHANCE
#define DVFSRC_PROPERTY_ENABLE
#define CREATE_TRACE_POINTS
#include <trace/events/mtk_qos_trace.h>
EXPORT_TRACEPOINT_SYMBOL_GPL(mtk_pm_qos_update_request);
/* 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),	\
}

#if !IS_ENABLED(CONFIG_MTK_DRAMC)
unsigned int mtk_dramc_get_ddr_type(void)
{
	pr_info("mtk-dvfsrc:%s not ready!", __func__);
	return 0;
}

unsigned int mtk_dramc_get_data_rate(void)
{
	pr_info("mtk-dvfsrc;%s not ready!", __func__);
	return 0;
}

unsigned int mtk_dramc_get_steps_freq(int index)
{
	pr_info("mtk-dvfsrc;%s not ready!", __func__);
	return 0;
}
#endif

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;
	u32 hrt_bw_unit;
	const struct dvfsrc_opp_desc *opps_desc;
	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;
	int irq_counter;
	const struct dvfsrc_opp_desc *curr_opps;
	void __iomem *regs;
	spinlock_t 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
	bool disable_wait_level;
};

#ifdef DVFSRC_OPP_BW_QUERY
u32 dram_type;
static inline struct device_node *dvfsrc_parse_required_opp(
	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(np, index);
	if (!required_np)
		return 0;

	if (of_property_read_u32_index(required_np, "opp-peak-KBps", dram_type, &peak_bw))
		pr_info("%s: get fail\n", __func__);

	of_node_put(required_np);
	return peak_bw;
}
EXPORT_SYMBOL(dvfsrc_get_required_opp_peak_bw);
#endif

#ifdef DVFSRC_DEBUG_ENHANCE
#define DVFSRC_DEBUG_DUMP 0
#define DVFSRC_DEBUG_AEE 1
#define DVFSRC_DEBUG_VCORE_CHK 2

#define DVFSRC_AEE_LEVEL_ERROR 0
#define DVFSRC_AEE_FORCE_ERROR 1
#define DVFSRC_AEE_VCORE_CHK_ERROR 2
#define DVFSRC_AEE_TIMEOUT_ERROR 3

static BLOCKING_NOTIFIER_HEAD(dvfsrc_debug_notifier);
int register_dvfsrc_debug_notifier(struct notifier_block *nb)
{
	return blocking_notifier_chain_register(&dvfsrc_debug_notifier, nb);
}
EXPORT_SYMBOL_GPL(register_dvfsrc_debug_notifier);

int unregister_dvfsrc_debug_notifier(struct notifier_block *nb)
{
	return blocking_notifier_chain_unregister(&dvfsrc_debug_notifier, nb);
}
EXPORT_SYMBOL_GPL(unregister_dvfsrc_debug_notifier);

static void mtk_dvfsrc_dump_notify(struct mtk_dvfsrc *dvfsrc, u32 flag)
{
	blocking_notifier_call_chain(&dvfsrc_debug_notifier,
			DVFSRC_DEBUG_DUMP, (void *) &flag);

}
static void mtk_dvfsrc_aee_notify(struct mtk_dvfsrc *dvfsrc, u32 aee_type)
{
	blocking_notifier_call_chain(&dvfsrc_debug_notifier,
			DVFSRC_DEBUG_AEE, (void *) &aee_type);

}
static void mtk_dvfsrc_vcore_check(struct mtk_dvfsrc *dvfsrc, u32 vcore_level)
{
	int ret;

	ret = blocking_notifier_call_chain(&dvfsrc_debug_notifier,
		DVFSRC_DEBUG_VCORE_CHK, (void *) &vcore_level);

	if (ret == NOTIFY_BAD) {
		dev_info(dvfsrc->dev,
			"VCORE_ERROR= %d, %d 0x%08x\n",
			vcore_level,
			dvfsrc->dvd->get_current_level(dvfsrc),
			dvfsrc->dvd->get_target_level(dvfsrc));
		mtk_dvfsrc_dump_notify(dvfsrc, 0);
		mtk_dvfsrc_aee_notify(dvfsrc, DVFSRC_AEE_VCORE_CHK_ERROR);
	}
}

#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,
	DVFSRC_TARGET_FORCE,
	DVFSRC_FORCE_MASK,
	DVFSRC_TARGET_FORCE_H,
	DVFSRC_SW_FORCE_BW,
	DVFSRC_INT,
	DVFSRC_INT_EN,
	DVFSRC_INT_CLR,
	DVFSRC_DEFAULT_OPP_2,
	DVFSRC_DEFAULT_OPP_1,
};

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_TARGET_FORCE] =		0xD70,
	[DVFSRC_INT] =			0xC4,
	[DVFSRC_INT_EN] =		0xC8,
	[DVFSRC_INT_CLR] =		0xCC,
};

static const int mt6983_regs[] = {
	[DVFSRC_BASIC_CONTROL] =	0x0,
	[DVFSRC_SW_REQ] =		0x18,
	[DVFSRC_SW_REQ2] =		0x604,
	[DVFSRC_LEVEL] =		0x5F0,
	[DVFSRC_SW_PEAK_BW] =		0x1F4,
	[DVFSRC_SW_BW] =		0x1E8,
	[DVFSRC_SW_HRT_BW] =		0x20C,
	[DVFSRC_TARGET_LEVEL] =		0x5F0,
	[DVFSRC_VCORE_REQUEST] =	0x80,
	[DVFSRC_TARGET_FORCE] =		0x5E0,
	[DVFSRC_TARGET_FORCE_H] =	0x5DC,
	[DVFSRC_FORCE_MASK] =		0x5EC,
	[DVFSRC_SW_FORCE_BW] =		0x200,
	[DVFSRC_INT] =			0xC8,
	[DVFSRC_INT_EN] =		0xCC,
	[DVFSRC_INT_CLR] =		0xD0,
	[DVFSRC_DEFAULT_OPP_2] =	0x230,
	[DVFSRC_DEFAULT_OPP_1] =	0x22C,
};

static const int mt8696_regs[] = {
	[DVFSRC_LEVEL] =	0x4A4,
	[DVFSRC_BASIC_CONTROL] = 0x4AC,
	[DVFSRC_SW_REQ] =	0x510,
	[DVFSRC_SW_REQ2] =	0x528,
	[DVFSRC_SW_BW] =	0x160,
	[DVFSRC_LAST] =		0x308,
	[DVFSRC_TARGET_LEVEL] = 0x520,
	[DVFSRC_TARGET_FORCE] = 0x4FC,
};

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)
{
	u32 hrt_bw_unit = dvfsrc->dvd->hrt_bw_unit;

	if (hrt_bw_unit)
		bw = div_u64((kbps_to_mbps(bw) + hrt_bw_unit - 1), hrt_bw_unit);
	else
		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)
{
	spin_lock(&dvfsrc->req_lock);
	dvfsrc_rmw(dvfsrc, DVFSRC_SW_REQ, level, 0x7, 12);
	spin_unlock(&dvfsrc->req_lock);
}

static void mt6873_set_vcore_level(struct mtk_dvfsrc *dvfsrc, u32 level)
{
	spin_lock(&dvfsrc->req_lock);
	dvfsrc_rmw(dvfsrc, DVFSRC_SW_REQ, level, 0x7, 4);
	spin_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));
#ifdef DVFSRC_DEBUG_ENHANCE
		mtk_dvfsrc_dump_notify(dvfsrc, 0);
		mtk_dvfsrc_aee_notify(dvfsrc, DVFSRC_AEE_FORCE_ERROR);
#endif
	}
}
#endif

static int mt6983_get_target_level(struct mtk_dvfsrc *dvfsrc)
{
	u32 reg;

	reg = dvfsrc_read(dvfsrc, DVFSRC_TARGET_LEVEL);
	if (reg & (1 << 16))
		return ((reg >> 8) & 0x3f) + 1;
	else
		return 0;
}

static int mt6983_get_current_level(struct mtk_dvfsrc *dvfsrc)
{
	u32 curr_level;

	curr_level = (dvfsrc_read(dvfsrc, DVFSRC_LEVEL) & 0x3f) + 1;
	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 u32 mt6983_get_dram_level(struct mtk_dvfsrc *dvfsrc)
{
	return (dvfsrc_read(dvfsrc, DVFSRC_SW_REQ) >> 12) & 0xf;
}

static void mt6983_set_dram_bw(struct mtk_dvfsrc *dvfsrc, u64 bw)
{
	bw = div_u64(kbps_to_mbps(bw), 100);
	bw = min_t(u64, bw, 0x3FF);
	dvfsrc_write(dvfsrc, DVFSRC_SW_BW, bw);
}

static void mt6983_set_dram_peak_bw(struct mtk_dvfsrc *dvfsrc, u64 bw)
{
	bw = div_u64(kbps_to_mbps(bw), 100);
	bw = min_t(u64, bw, 0x3FF);
	dvfsrc_write(dvfsrc, DVFSRC_SW_PEAK_BW, bw);
}

static void mt6983_set_dram_level(struct mtk_dvfsrc *dvfsrc, u32 level)
{
	spin_lock(&dvfsrc->req_lock);
	dvfsrc_rmw(dvfsrc, DVFSRC_SW_REQ, level, 0xf, 12);
	spin_unlock(&dvfsrc->req_lock);
}

static void mt6983_set_opp_level(struct mtk_dvfsrc *dvfsrc, u32 level)
{
	const struct dvfsrc_opp *opp;

	opp = &dvfsrc->curr_opps->opps[level];
	mt6983_set_dram_level(dvfsrc, opp->dram_opp);
}

#ifdef DVFSRC_FORCE_OPP_SUPPORT
static void mt6985_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);
	if (level > dvfsrc->curr_opps->num_opp - 1) {
		if (dvfsrc->opp_forced) {
			dvfsrc_rmw(dvfsrc, DVFSRC_BASIC_CONTROL, 1, 0x1, 14);
			dvfsrc_write(dvfsrc, DVFSRC_DEFAULT_OPP_2, 1);
			dvfsrc_write(dvfsrc, DVFSRC_DEFAULT_OPP_1, 0);
			dvfsrc_write(dvfsrc, DVFSRC_SW_REQ2, 0x0);
			dvfsrc_rmw(dvfsrc, DVFSRC_BASIC_CONTROL, 0, 0x1, 14);
			dvfsrc->opp_forced = false;
		}
		goto out;
	}

	if (!dvfsrc->opp_forced) {
		dvfsrc_write(dvfsrc, DVFSRC_SW_REQ2, 0xFFFFFFFF);
		udelay(STARTUP_TIME);
		dvfsrc_wait_for_idle(dvfsrc);
		udelay(STARTUP_TIME);
		dvfsrc_wait_for_idle(dvfsrc);
	}

	dvfsrc->opp_forced = true;
	dvfsrc_rmw(dvfsrc, DVFSRC_BASIC_CONTROL, 1, 0x1, 14);
	if (level >= 32) {
		dvfsrc_write(dvfsrc, DVFSRC_DEFAULT_OPP_1, 1 << (level - 32));
		dvfsrc_write(dvfsrc, DVFSRC_DEFAULT_OPP_2, 0);
	} else {
		dvfsrc_write(dvfsrc, DVFSRC_DEFAULT_OPP_2, 1 << level);
		dvfsrc_write(dvfsrc, DVFSRC_DEFAULT_OPP_1, 0);
	}
	dvfsrc_rmw(dvfsrc, DVFSRC_BASIC_CONTROL, 0, 0x1, 14);
	ret = readl_poll_timeout_atomic(
			dvfsrc->regs + dvfsrc->dvd->regs[DVFSRC_LEVEL],
			val, (val & 0x3f) == level, STARTUP_TIME, POLL_TIMEOUT);
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));
#ifdef DVFSRC_DEBUG_ENHANCE
		mtk_dvfsrc_dump_notify(dvfsrc, 0);
		mtk_dvfsrc_aee_notify(dvfsrc, DVFSRC_AEE_FORCE_ERROR);
#endif
	}
}

static void mt6983_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);
	if (level > dvfsrc->curr_opps->num_opp - 1) {
		dvfsrc_write(dvfsrc, DVFSRC_SW_FORCE_BW, 0x0);
		dvfsrc_rmw(dvfsrc, DVFSRC_BASIC_CONTROL, 0, 0x1, 14);
		dvfsrc_write(dvfsrc, DVFSRC_TARGET_FORCE, 0);
		dvfsrc_write(dvfsrc, DVFSRC_TARGET_FORCE_H, 0);
		dvfsrc->opp_forced = false;
		goto out;
	}

	if (!dvfsrc->opp_forced) {
		dvfsrc_write(dvfsrc, DVFSRC_SW_FORCE_BW, 0x3FF);
		udelay(STARTUP_TIME);
		dvfsrc_wait_for_idle(dvfsrc);
		udelay(STARTUP_TIME);
		dvfsrc_wait_for_idle(dvfsrc);
	}

	dvfsrc->opp_forced = true;

	if (level >= 32) {
		dvfsrc_write(dvfsrc, DVFSRC_TARGET_FORCE, 0);
		dvfsrc_write(dvfsrc, DVFSRC_TARGET_FORCE_H, 1 << (level - 32));
	} else {
		dvfsrc_write(dvfsrc, DVFSRC_TARGET_FORCE, 1 << level);
		dvfsrc_write(dvfsrc, DVFSRC_TARGET_FORCE_H, 0);
	}
	dvfsrc_rmw(dvfsrc, DVFSRC_FORCE_MASK, 0, 0x1, 1);
	dvfsrc_rmw(dvfsrc, DVFSRC_BASIC_CONTROL, 1, 0x1, 14);
	ret = readl_poll_timeout_atomic(
			dvfsrc->regs + dvfsrc->dvd->regs[DVFSRC_LEVEL],
			val, (val & 0x3f) == level, STARTUP_TIME, POLL_TIMEOUT);
	dvfsrc_rmw(dvfsrc, DVFSRC_FORCE_MASK, 1, 0x1, 1);
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));
#ifdef DVFSRC_DEBUG_ENHANCE
		mtk_dvfsrc_dump_notify(dvfsrc, 0);
		mtk_dvfsrc_aee_notify(dvfsrc, DVFSRC_AEE_FORCE_ERROR);
#endif
	}
}
#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);

	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_FORCEOPP_REQUEST:
		if ((dvfsrc->dvd->set_force_opp_level) && dvfsrc->dvfsrc_enable)
			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);
#ifdef DVFSRC_DEBUG_ENHANCE
		mtk_dvfsrc_vcore_check(dvfsrc, data);
#endif
		break;
	case MTK_DVFSRC_CMD_DRAM_REQUEST:
		if (!dvfsrc->disable_wait_level)
			ret = dvfsrc->dvd->wait_for_dram_level(dvfsrc, data);
		break;
	}
out:
#ifdef DVFSRC_FORCE_OPP_SUPPORT
	if (dvfsrc->opp_forced)
		return;
#endif

	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));
#ifdef DVFSRC_DEBUG_ENHANCE
		mtk_dvfsrc_dump_notify(dvfsrc, 0);
		mtk_dvfsrc_aee_notify(dvfsrc, DVFSRC_AEE_LEVEL_ERROR);
#endif
	}
}
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);

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 irqreturn_t mtk_dvfsrc_irq_handler_thread(int irq, void *data)
{
	struct mtk_dvfsrc *dvfsrc = data;
	u32 val;

	val = dvfsrc_read(dvfsrc, DVFSRC_INT);
	dvfsrc_write(dvfsrc, DVFSRC_INT_CLR, val);
	dvfsrc_write(dvfsrc, DVFSRC_INT_CLR, 0x0);

	if (val & 0x2) {
		dev_info(dvfsrc->dev, "DVFSRC Timeout Handler %d !\n", dvfsrc->irq_counter);
		if (dvfsrc->irq_counter < 3) {
			mtk_dvfsrc_dump_notify(dvfsrc, 0);
			mtk_dvfsrc_aee_notify(dvfsrc, DVFSRC_AEE_TIMEOUT_ERROR);
		} else if (dvfsrc->irq_counter == 3) {
			dvfsrc_write(dvfsrc, DVFSRC_INT_EN,
					dvfsrc_read(dvfsrc, DVFSRC_INT_EN) & ~0x2);
			dev_info(dvfsrc->dev, "DVFSRC Timeout IRQ Disable\n");
		}
		dvfsrc->irq_counter++;
	}

	return IRQ_HANDLED;
}

static int mtk_dvfsrc_probe(struct platform_device *pdev)
{
	struct arm_smccc_res ares;
	struct resource *res;
	struct mtk_dvfsrc *dvfsrc;
	int ret;
#ifdef DVFSRC_BOOT_OPP
	int boot_opp = 2, i = 0;
	int ddr_type = mtk_dramc_get_ddr_type();
	int ddr_hz = mtk_dramc_get_data_rate();
#else
#ifdef DVFSRC_PROPERTY_ENABLE
	u32 is_bringup = 0;
	u32 dvfsrc_flag = 0;
	u32 dvfsrc_vmode = 0;
	struct device_node *np = pdev->dev.of_node;
#endif
#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);
#ifdef DVFSRC_BOOT_OPP
	dvfsrc->regs = devm_ioremap(&pdev->dev, res->start, resource_size(res));
#else
	dvfsrc->regs = devm_ioremap_resource(&pdev->dev, res);
#endif
	if (IS_ERR(dvfsrc->regs))
		return PTR_ERR(dvfsrc->regs);

	spin_lock_init(&dvfsrc->req_lock);
	mutex_init(&dvfsrc->pstate_lock);
#ifdef DVFSRC_FORCE_OPP_SUPPORT
	spin_lock_init(&dvfsrc->force_lock);
#endif

#ifdef DVFSRC_BOOT_OPP
	dev_info(dvfsrc->dev, "ddr=[%d][%d]\n", ddr_type, ddr_hz);
	for (i = 0; i < 3U; i++)
		if (ddr_hz == mtk_dramc_get_steps_freq(i))
			boot_opp = i;
	dev_info(dvfsrc->dev, "boot_opp=[%d]\n", boot_opp);

	arm_smccc_smc(MTK_SIP_VCOREFS_CONTROL, MTK_SIP_DVFSRC_INIT,
				      true, boot_opp, 0, 0, 0, 0, &ares);
	dvfsrc->dvfsrc_enable = true;
#else
#ifdef DVFSRC_PROPERTY_ENABLE
	if (of_property_read_u32(np, "dvfsrc-bringup", &is_bringup))
		dev_info(dvfsrc->dev, "not dvfsrc bring up\n");
	if (of_property_read_u32(np, "dvfsrc-flag", &dvfsrc_flag))
		dev_info(dvfsrc->dev, "no dvfsrc flag\n");
	if (of_property_read_u32(np, "dvfsrc-vmode", &dvfsrc_vmode))
		dev_info(dvfsrc->dev, "no dvfsrc vmode\n");
	dvfsrc->disable_wait_level = of_property_read_bool(np, "disable-wait-level");

	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;
			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");
#else
	arm_smccc_smc(MTK_SIP_VCOREFS_CONTROL, MTK_SIP_DVFSRC_INIT, 0, 0, 0,
		0, 0, 0, &ares);

	if (!ares.a0) {
		dvfsrc->dram_type = ares.a1;
		dvfsrc->dvfsrc_enable = true;
	} else
		dev_info(dvfsrc->dev, "dvfs mode is disabled\n");
#endif
#endif

#ifdef DVFSRC_OPP_BW_QUERY
	dram_type = dvfsrc->dram_type;
#endif
	dvfsrc->curr_opps = &dvfsrc->dvd->opps_desc[dvfsrc->dram_type];
	platform_set_drvdata(pdev, dvfsrc);
	if (dvfsrc->dvd->num_domains)
		pstate_notifier_register(dvfsrc);

	ret = devm_request_threaded_irq(dvfsrc->dev, platform_get_irq(pdev, 0),
					NULL, mtk_dvfsrc_irq_handler_thread,
					IRQF_ONESHOT | IRQF_TRIGGER_NONE,
					"dvfsrc", dvfsrc);
	if (!ret) {
		dvfsrc->irq_counter = 0;
		dvfsrc_write(dvfsrc, DVFSRC_INT_EN, dvfsrc_read(dvfsrc, DVFSRC_INT_EN) | 0x2);
		dev_info(dvfsrc->dev, "DVFSRC IRQ Enable\n");
	}

	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;

	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;
}

#define DVFSRC_MT6873_SERIES_OPS			\
	.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

#define DVFSRC_MT6983_SERIES_OPS			\
	.get_target_level = mt6983_get_target_level,	\
	.get_current_level = mt6983_get_current_level,	\
	.get_vcore_level = mt6873_get_vcore_level,	\
	.get_vcp_level = mt6873_get_vcp_level,		\
	.get_dram_level = mt6983_get_dram_level,	\
	.set_dram_bw = mt6983_set_dram_bw,		\
	.set_dram_peak_bw = mt6983_set_dram_peak_bw,	\
	.set_opp_level = mt6983_set_opp_level,		\
	.set_dram_level = mt6983_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


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 const struct dvfsrc_opp dvfsrc_opp_mt8696_lp4[] = {
	{0, 0}, {0, 1},
};

static const struct dvfsrc_opp_desc dvfsrc_opp_mt8696_desc[] = {
	DVFSRC_OPP_DESC(dvfsrc_opp_mt8696_lp4),
};

static int mt8696_get_target_level(struct mtk_dvfsrc *dvfsrc)
{
	u32 rc_info;

	rc_info = dvfsrc_read(dvfsrc, DVFSRC_TARGET_LEVEL);
	return rc_info;
}

static int mt8696_get_current_level(struct mtk_dvfsrc *dvfsrc)
{
	u32 spm_dvfs_level;

	spm_dvfs_level = dvfsrc_read(dvfsrc, DVFSRC_LEVEL);
	if (spm_dvfs_level <= 1U)
		spm_dvfs_level = 0;
	else
		spm_dvfs_level = spm_dvfs_level - 1U;
	dev_dbg(dvfsrc->dev, "spm_dvfs_level=%d\n", spm_dvfs_level);
	return spm_dvfs_level;
}

static int mt8698_get_current_level(struct mtk_dvfsrc *dvfsrc)
{
	u32 spm_dvfs_level;

	spm_dvfs_level = dvfsrc_read(dvfsrc, DVFSRC_LEVEL);
	if (spm_dvfs_level <= 1U)
		spm_dvfs_level = 1;
	else
		spm_dvfs_level = 2;
	dev_dbg(dvfsrc->dev, "spm_dvfs_level=%d\n", spm_dvfs_level);
	return spm_dvfs_level;
}

static u32 mt8696_get_vcore_level(struct mtk_dvfsrc *dvfsrc)
{
	u32 vcore_level;

	vcore_level = (dvfsrc_read(dvfsrc, DVFSRC_LEVEL) >> 16) & 0x3;
	return vcore_level;
}

static void mt8696_set_dram_bw(struct mtk_dvfsrc *dvfsrc, u64 bw)
{
	bw = kbps_to_mbps(bw);
	dev_dbg(dvfsrc->dev, "%s bw=%d\n", __func__, bw);
}

static void mt8696_set_opp_level(struct mtk_dvfsrc *dvfsrc, u32 level)
{
	spin_lock(&dvfsrc->req_lock);
	if (level >= 1U)
		level = 2;
	else
		level = level + 1;

	dev_dbg(dvfsrc->dev, "set dram level=%d\n", level);
	dvfsrc_rmw(dvfsrc, DVFSRC_BASIC_CONTROL, 0, 0x1, 4);
	dvfsrc_write(dvfsrc, DVFSRC_SW_REQ, level);
	dvfsrc_rmw(dvfsrc, DVFSRC_BASIC_CONTROL, 1, 0x1, 4);
	spin_unlock(&dvfsrc->req_lock);
}

static void mt8698_set_opp_level(struct mtk_dvfsrc *dvfsrc, u32 level)
{
	spin_lock(&dvfsrc->req_lock);
	if (level > 1U)
		level = 2;
	else
		level = 1;

	dev_dbg(dvfsrc->dev, "set dram level=%d\n", level);
	dvfsrc_rmw(dvfsrc, DVFSRC_BASIC_CONTROL, 0, 0x1, 4);
	dvfsrc_write(dvfsrc, DVFSRC_SW_REQ, level);
	dvfsrc_rmw(dvfsrc, DVFSRC_BASIC_CONTROL, 1, 0x1, 4);
	spin_unlock(&dvfsrc->req_lock);
}

static void mt8696_set_vcore_level(struct mtk_dvfsrc *dvfsrc, u32 level)
{
	if (level >= 1U)
		level = 2;
	else
		level = level + 1;
	dev_dbg(dvfsrc->dev, "%s level=%d\n", __func__, level);
}

static void mt8696_set_dram_level(struct mtk_dvfsrc *dvfsrc, u32 level)
{
	spin_lock(&dvfsrc->req_lock);
	if (level >= 1U)
		level = 2;
	else
		level = level + 1;

	dev_dbg(dvfsrc->dev, "set dram level=%d\n", level);
	dvfsrc_rmw(dvfsrc, DVFSRC_BASIC_CONTROL, 0, 0x1, 4);
	dvfsrc_write(dvfsrc, DVFSRC_SW_REQ, level);
	dvfsrc_rmw(dvfsrc, DVFSRC_BASIC_CONTROL, 1, 0x1, 4);
	spin_unlock(&dvfsrc->req_lock);
}

static void mt8698_set_dram_level(struct mtk_dvfsrc *dvfsrc, u32 level)
{
	spin_lock(&dvfsrc->req_lock);
	if (level > 1U)
		level = 2;
	else
		level = 1;

	dev_dbg(dvfsrc->dev, "set dram level=%d\n", level);
	dvfsrc_rmw(dvfsrc, DVFSRC_BASIC_CONTROL, 0, 0x1, 4);
	dvfsrc_write(dvfsrc, DVFSRC_SW_REQ, level);
	dvfsrc_rmw(dvfsrc, DVFSRC_BASIC_CONTROL, 1, 0x1, 4);
	spin_unlock(&dvfsrc->req_lock);
}

static u32 mt8696_get_dram_level(struct mtk_dvfsrc *dvfsrc)
{
	u32 dram_dvfs_level;

	dram_dvfs_level = dvfsrc_read(dvfsrc, DVFSRC_LEVEL);

	if (dram_dvfs_level <= 1U)
		dram_dvfs_level = 0;
	else
		dram_dvfs_level = dram_dvfs_level - 1U;

	dev_dbg(dvfsrc->dev, "dram_dvfs_level=%d\n", dram_dvfs_level);
	return dram_dvfs_level;
}

static u32 mt8698_get_dram_level(struct mtk_dvfsrc *dvfsrc)
{
	u32 dram_dvfs_level;

	dram_dvfs_level = dvfsrc_read(dvfsrc, DVFSRC_LEVEL);

	if (dram_dvfs_level <= 1U)
		dram_dvfs_level = 1;
	else
		dram_dvfs_level = 2;

	dev_dbg(dvfsrc->dev, "dram_dvfs_level=%d\n", dram_dvfs_level);
	return dram_dvfs_level;
}

static int mt8696_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_info(dvfsrc->dev,
			 "timeout, target: %u, dram: %d, vcore: %d\n", level,
			 curr->dram_opp, curr->vcore_opp);
		return ret;
	}

	return 0;
}

#ifdef DVFSRC_FORCE_OPP_SUPPORT
static void mt8696_set_force_opp_level(struct mtk_dvfsrc *dvfsrc, u32 level)
{
	int enable = 1;
	unsigned long flags;

	if (level >= 2U)
		enable = 0;
	else
		level = 2U - level;

	spin_lock_irqsave(&dvfsrc->force_lock, flags);
	if (enable) {
		dvfsrc_rmw(dvfsrc, DVFSRC_BASIC_CONTROL, 0, 0x1, 4);
		dvfsrc_write(dvfsrc, DVFSRC_TARGET_FORCE, level);
		dvfsrc_rmw(dvfsrc, DVFSRC_BASIC_CONTROL, 1, 0x1, 2);
		dvfsrc_rmw(dvfsrc, DVFSRC_BASIC_CONTROL, 1, 0x1, 4);
	} else {
		dvfsrc_rmw(dvfsrc, DVFSRC_BASIC_CONTROL, 0, 0x1, 4);
		dvfsrc_write(dvfsrc, DVFSRC_TARGET_FORCE, 0x0);
		dvfsrc_write(dvfsrc, DVFSRC_SW_REQ2, 0x1);
		dvfsrc_rmw(dvfsrc, DVFSRC_BASIC_CONTROL, 0, 0x1, 2);
		dvfsrc_rmw(dvfsrc, DVFSRC_BASIC_CONTROL, 1, 0x1, 4);
	}
	spin_unlock_irqrestore(&dvfsrc->force_lock, flags);
}
#endif

#ifdef DVFSRC_FORCE_OPP_SUPPORT
static void mt8698_set_force_opp_level(struct mtk_dvfsrc *dvfsrc, u32 level)
{
	int enable = 1;
	unsigned long flags;

	if (level > 2U)
		enable = 0;
	else if (level <= 1)
		level = 1;
	else
		level = 2;

	spin_lock_irqsave(&dvfsrc->force_lock, flags);
	if (enable) {
		dvfsrc_rmw(dvfsrc, DVFSRC_BASIC_CONTROL, 0, 0x1, 4);
		dvfsrc_write(dvfsrc, DVFSRC_TARGET_FORCE, level);
		dvfsrc_rmw(dvfsrc, DVFSRC_BASIC_CONTROL, 1, 0x1, 2);
		dvfsrc_rmw(dvfsrc, DVFSRC_BASIC_CONTROL, 1, 0x1, 4);
	} else {
		dvfsrc_rmw(dvfsrc, DVFSRC_BASIC_CONTROL, 0, 0x1, 4);
		dvfsrc_write(dvfsrc, DVFSRC_TARGET_FORCE, 0x0);
		dvfsrc_write(dvfsrc, DVFSRC_SW_REQ2, 0x1);
		dvfsrc_rmw(dvfsrc, DVFSRC_BASIC_CONTROL, 0, 0x1, 2);
		dvfsrc_rmw(dvfsrc, DVFSRC_BASIC_CONTROL, 1, 0x1, 4);
	}
	spin_unlock_irqrestore(&dvfsrc->force_lock, flags);
}
#endif

static const struct dvfsrc_soc_data mt8696_data = {
	.opps_desc = dvfsrc_opp_mt8696_desc,
	.num_opp_desc = ARRAY_SIZE(dvfsrc_opp_mt8696_desc),
	.regs = mt8696_regs,
	.get_target_level = mt8696_get_target_level,
	.get_current_level = mt8696_get_current_level,
	.get_vcore_level = mt8696_get_vcore_level,
	.set_dram_bw = mt8696_set_dram_bw,
	.set_dram_level = mt8696_set_dram_level,
	.get_dram_level = mt8696_get_dram_level,
	.set_opp_level = mt8696_set_opp_level,
	.set_vcore_level = mt8696_set_vcore_level,
#ifdef DVFSRC_FORCE_OPP_SUPPORT
	.set_force_opp_level = mt8696_set_force_opp_level,
#endif
	.wait_for_opp_level = mt8696_wait_for_opp_level,
	.wait_for_vcore_level = dvfsrc_wait_for_vcore_level,
	.wait_for_dram_level = dvfsrc_wait_for_dram_level
};

static const struct dvfsrc_soc_data mt8698_data = {
	.opps_desc = dvfsrc_opp_mt8696_desc,
	.num_opp_desc = ARRAY_SIZE(dvfsrc_opp_mt8696_desc),
	.regs = mt8696_regs,
	.get_target_level = mt8696_get_target_level,
	.get_current_level = mt8698_get_current_level,
	.get_vcore_level = mt8696_get_vcore_level,
	.set_dram_bw = mt8696_set_dram_bw,
	.set_dram_level = mt8698_set_dram_level,
	.get_dram_level = mt8698_get_dram_level,
	.set_opp_level = mt8698_set_opp_level,
	.set_vcore_level = mt8696_set_vcore_level,
#ifdef DVFSRC_FORCE_OPP_SUPPORT
	.set_force_opp_level = mt8698_set_force_opp_level,
#endif
	.wait_for_opp_level = mt8696_wait_for_opp_level,
	.wait_for_vcore_level = dvfsrc_wait_for_vcore_level,
	.wait_for_dram_level = dvfsrc_wait_for_dram_level
};

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 = {
	DVFSRC_MT6873_SERIES_OPS,
	.opps_desc = dvfsrc_opp_mt6873_desc,
	.num_opp_desc = ARRAY_SIZE(dvfsrc_opp_mt6873_desc),
	.regs = mt6873_regs,
#ifdef DVFSRC_FORCE_OPP_SUPPORT
	.set_force_opp_level = mt6873_set_force_opp_level,
#endif
};

static const struct dvfsrc_opp dvfsrc_opp_mt6885_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},
	{0, 3}, {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_mt6885_desc[] = {
	DVFSRC_OPP_DESC(dvfsrc_opp_mt6885_lp4),
};

static const struct dvfsrc_soc_data mt6885_data = {
	DVFSRC_MT6873_SERIES_OPS,
	.opps_desc = dvfsrc_opp_mt6885_desc,
	.num_opp_desc = ARRAY_SIZE(dvfsrc_opp_mt6885_desc),
	.regs = mt6873_regs,
#ifdef DVFSRC_FORCE_OPP_SUPPORT
	.set_force_opp_level = mt6873_set_force_opp_level,
#endif
};

static const struct dvfsrc_opp dvfsrc_opp_mt6893_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},
	{0, 3}, {1, 3}, {2, 3}, {3, 3},
	{1, 4}, {2, 4}, {3, 4}, {2, 5},
	{3, 5}, {3, 6}, {4, 6}, {4, 7},
};

static const struct dvfsrc_opp_desc dvfsrc_opp_mt6893_desc[] = {
	DVFSRC_OPP_DESC(dvfsrc_opp_mt6893_lp4),
};

static const struct dvfsrc_soc_data mt6893_data = {
	DVFSRC_MT6873_SERIES_OPS,
	.opps_desc = dvfsrc_opp_mt6893_desc,
	.num_opp_desc = ARRAY_SIZE(dvfsrc_opp_mt6893_desc),
	.regs = mt6873_regs,
#ifdef DVFSRC_FORCE_OPP_SUPPORT
	.set_force_opp_level = mt6873_set_force_opp_level,
#endif
};

static const struct dvfsrc_opp dvfsrc_opp_mt6833_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},
	{0, 3}, {1, 3}, {2, 3}, {3, 3},
	{1, 4}, {2, 4}, {3, 4}, {1, 5},
	{2, 5}, {3, 5}, {2, 6}, {3, 6},
	{3, 7},
};

static const struct dvfsrc_opp_desc dvfsrc_opp_mt6833_desc[] = {
	DVFSRC_OPP_DESC(dvfsrc_opp_mt6833_lp4),
};

static const struct dvfsrc_soc_data mt6833_data = {
	DVFSRC_MT6873_SERIES_OPS,
	.opps_desc = dvfsrc_opp_mt6833_desc,
	.num_opp_desc = ARRAY_SIZE(dvfsrc_opp_mt6833_desc),
	.regs = mt6873_regs,
#ifdef DVFSRC_FORCE_OPP_SUPPORT
	.set_force_opp_level = mt6873_set_force_opp_level,
#endif
};

static const struct dvfsrc_opp dvfsrc_opp_mt6877_lp4[] = {
	{0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0},
	{0, 1}, {1, 1}, {2, 1}, {3, 1},	{4, 1},
	{0, 2}, {1, 2}, {2, 2}, {3, 2}, {4, 2},
	{0, 3}, {1, 3}, {2, 3}, {3, 3}, {4, 3},
	{1, 4}, {2, 4}, {3, 4}, {4, 4}, {2, 5},
	{3, 5}, {4, 5}, {3, 6}, {4, 6}, {4, 7},
};

static const struct dvfsrc_opp_desc dvfsrc_opp_mt6877_desc[] = {
	DVFSRC_OPP_DESC(dvfsrc_opp_mt6877_lp4),
};

static const struct dvfsrc_soc_data mt6877_data = {
	DVFSRC_MT6873_SERIES_OPS,
	.opps_desc = dvfsrc_opp_mt6877_desc,
	.num_opp_desc = ARRAY_SIZE(dvfsrc_opp_mt6877_desc),
	.regs = mt6873_regs,
#ifdef DVFSRC_FORCE_OPP_SUPPORT
	.set_force_opp_level = mt6873_set_force_opp_level,
#endif
};

static const struct dvfsrc_opp dvfsrc_opp_mt6983[] = {
	{0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0},
	{0, 1}, {1, 1}, {2, 1}, {3, 1},	{4, 1},
	{1, 2}, {2, 2}, {3, 2}, {4, 2}, {1, 3},
	{2, 3}, {3, 3}, {4, 3},
	{2, 4}, {3, 4}, {4, 4},
	{3, 5}, {4, 5},
	{3, 6}, {4, 6},
	{4, 7},
	{4, 8},
};

static const struct dvfsrc_opp_desc dvfsrc_opp_mt6983_desc[] = {
	DVFSRC_OPP_DESC(dvfsrc_opp_mt6983),
};

static const struct dvfsrc_soc_data mt6983_data = {
	DVFSRC_MT6983_SERIES_OPS,
	.opps_desc = dvfsrc_opp_mt6983_desc,
	.num_opp_desc = ARRAY_SIZE(dvfsrc_opp_mt6983_desc),
	.regs = mt6983_regs,
#ifdef DVFSRC_FORCE_OPP_SUPPORT
	.set_force_opp_level = mt6983_set_force_opp_level,
#endif
};

static const struct dvfsrc_opp dvfsrc_opp_mt6879[] = {
	{0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0},
	{0, 1}, {1, 1}, {2, 1}, {3, 1},	{4, 1},
	{0, 2}, {1, 2}, {2, 2}, {3, 2}, {4, 2},
	{0, 3}, {1, 3}, {2, 3}, {3, 3}, {4, 3},
	{0, 4}, {1, 4}, {2, 4}, {3, 4}, {4, 4},
	{1, 5}, {2, 5}, {3, 5}, {4, 5},
	{2, 6}, {3, 6}, {4, 6},
	{4, 7},
};

static const struct dvfsrc_opp_desc dvfsrc_opp_mt6879_desc[] = {
	DVFSRC_OPP_DESC(dvfsrc_opp_mt6879),
};

static const struct dvfsrc_soc_data mt6879_data = {
	DVFSRC_MT6983_SERIES_OPS,
	.opps_desc = dvfsrc_opp_mt6879_desc,
	.num_opp_desc = ARRAY_SIZE(dvfsrc_opp_mt6879_desc),
	.regs = mt6983_regs,
#ifdef DVFSRC_FORCE_OPP_SUPPORT
	.set_force_opp_level = mt6983_set_force_opp_level,
#endif
};

static const struct dvfsrc_opp dvfsrc_opp_mt6855[] = {
	{0, 0}, {1, 0}, {2, 0}, {3, 0},
	{0, 1}, {1, 1}, {2, 1}, {3, 1},
	{0, 2}, {1, 2}, {2, 2}, {3, 2},
	{0, 3}, {1, 3}, {2, 3}, {3, 3},
	{0, 4}, {1, 4}, {2, 4}, {3, 4},
	{1, 5}, {2, 5}, {3, 5},
	{2, 6}, {3, 6},
	{3, 7},
};

static const struct dvfsrc_opp_desc dvfsrc_opp_mt6855_desc[] = {
	DVFSRC_OPP_DESC(dvfsrc_opp_mt6855),
};

static const struct dvfsrc_soc_data mt6855_data = {
	DVFSRC_MT6983_SERIES_OPS,
	.opps_desc = dvfsrc_opp_mt6855_desc,
	.num_opp_desc = ARRAY_SIZE(dvfsrc_opp_mt6855_desc),
	.regs = mt6983_regs,
#ifdef DVFSRC_FORCE_OPP_SUPPORT
	.set_force_opp_level = mt6983_set_force_opp_level,
#endif
};

static const struct dvfsrc_opp dvfsrc_opp_mt6985[] = {
	{0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0},
	{0, 1}, {1, 1}, {2, 1}, {3, 1},	{4, 1},
	{0, 2}, {1, 2}, {2, 2}, {3, 2}, {4, 2},
	{1, 3}, {2, 3}, {3, 3}, {4, 3},
	{1, 4}, {2, 4}, {3, 4}, {4, 4},
	{2, 5}, {3, 5}, {4, 5},
	{3, 6}, {4, 6},
	{4, 7},
	{4, 8},
};

static const struct dvfsrc_opp_desc dvfsrc_opp_mt6985_desc[] = {
	DVFSRC_OPP_DESC(dvfsrc_opp_mt6985),
};

static const struct dvfsrc_soc_data mt6985_data = {
	DVFSRC_MT6983_SERIES_OPS,
	.opps_desc = dvfsrc_opp_mt6985_desc,
	.num_opp_desc = ARRAY_SIZE(dvfsrc_opp_mt6985_desc),
	.regs = mt6983_regs,
#ifdef DVFSRC_FORCE_OPP_SUPPORT
	.set_force_opp_level = mt6985_set_force_opp_level,
#endif
};

static const struct dvfsrc_opp dvfsrc_opp_mt6886[] = {
	{0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0},
	{0, 1}, {1, 1}, {2, 1}, {3, 1}, {4, 1},
	{0, 2}, {1, 2}, {2, 2}, {3, 2}, {4, 2},
	{1, 3}, {2, 3}, {3, 3}, {4, 3},
	{1, 4}, {2, 4}, {3, 4}, {4, 4},
	{2, 5}, {3, 5}, {4, 5},
	{3, 6}, {4, 6},
	{4, 7},
};

static const struct dvfsrc_opp_desc dvfsrc_opp_mt6886_desc[] = {
	DVFSRC_OPP_DESC(dvfsrc_opp_mt6886),
};

static const struct dvfsrc_soc_data mt6886_data = {
	DVFSRC_MT6983_SERIES_OPS,
	.opps_desc = dvfsrc_opp_mt6886_desc,
	.num_opp_desc = ARRAY_SIZE(dvfsrc_opp_mt6886_desc),
	.regs = mt6983_regs,
#ifdef DVFSRC_FORCE_OPP_SUPPORT
	.set_force_opp_level = mt6983_set_force_opp_level,
#endif
};

static const struct dvfsrc_opp dvfsrc_opp_mt6897_lp5x[] = {
	{0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0}, {5, 0},
	{0, 1}, {1, 1}, {2, 1}, {3, 1}, {4, 1}, {5, 1},
	{0, 2}, {1, 2}, {2, 2}, {3, 2}, {4, 2}, {5, 2},
	{0, 3}, {1, 3}, {2, 3}, {3, 3}, {4, 3}, {5, 3},
	{1, 4}, {2, 4}, {3, 4}, {4, 4}, {5, 4},
	{2, 5}, {3, 5}, {4, 5}, {5, 5},
	{3, 6}, {4, 6}, {5, 6},
	{4, 7}, {5, 7},
	{5, 8},
	{5, 9},
};

static const struct dvfsrc_opp dvfsrc_opp_mt6897_lp5[] = {
	{0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0}, {5, 0},
	{0, 1}, {1, 1}, {2, 1}, {3, 1}, {4, 1}, {5, 1},
	{0, 2}, {1, 2}, {2, 2}, {3, 2}, {4, 2}, {5, 2},
	{0, 3}, {1, 3}, {2, 3}, {3, 3}, {4, 3}, {5, 3},
	{1, 4}, {2, 4}, {3, 4}, {4, 4}, {5, 4},
	{2, 5}, {3, 5}, {4, 5}, {5, 5},
	{3, 6}, {4, 6}, {5, 6},
	{4, 7}, {5, 7},
	{4, 8}, {5, 8},
	{4, 9}, {5, 9},
};

static const struct dvfsrc_opp_desc dvfsrc_opp_mt6897_desc[] = {
	DVFSRC_OPP_DESC(dvfsrc_opp_mt6897_lp5x),
	DVFSRC_OPP_DESC(dvfsrc_opp_mt6897_lp5),
};

static const struct dvfsrc_soc_data mt6897_data = {
	DVFSRC_MT6983_SERIES_OPS,
	.opps_desc = dvfsrc_opp_mt6897_desc,
	.num_opp_desc = ARRAY_SIZE(dvfsrc_opp_mt6897_desc),
	.regs = mt6983_regs,
#ifdef DVFSRC_FORCE_OPP_SUPPORT
	.set_force_opp_level = mt6985_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,mt6853-dvfsrc",
		.data = &mt6873_data,
	}, {
		.compatible = "mediatek,mt6789-dvfsrc",
		.data = &mt6873_data,
	}, {
		.compatible = "mediatek,mt6885-dvfsrc",
		.data = &mt6885_data,
	}, {
		.compatible = "mediatek,mt6893-dvfsrc",
		.data = &mt6893_data,
	}, {
		.compatible = "mediatek,mt6833-dvfsrc",
		.data = &mt6833_data,
	}, {
		.compatible = "mediatek,mt6877-dvfsrc",
		.data = &mt6877_data,
	}, {
		.compatible = "mediatek,mt6983-dvfsrc",
		.data = &mt6983_data,
	}, {
		.compatible = "mediatek,mt6895-dvfsrc",
		.data = &mt6983_data,
	}, {
		.compatible = "mediatek,mt6879-dvfsrc",
		.data = &mt6879_data,
	}, {
		.compatible = "mediatek,mt6855-dvfsrc",
		.data = &mt6855_data,
	}, {
		.compatible = "mediatek,mt6985-dvfsrc",
		.data = &mt6985_data,
	}, {
		.compatible = "mediatek,mt6886-dvfsrc",
		.data = &mt6886_data,
	}, {
		.compatible = "mediatek,mt6897-dvfsrc",
		.data = &mt6897_data,
        }, {
		.compatible = "mediatek,mt8696-dvfsrc",
		.data = &mt8696_data,
	},  {
		.compatible = "mediatek,mt8698-dvfsrc",
		.data = &mt8698_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");
MODULE_SOFTDEP("post: mtk-dvfsrc-regulator");
MODULE_SOFTDEP("post: mtk-dvfsrc-devfreq");
MODULE_SOFTDEP("post: mtk-emi");

