kernel-brax3-ubuntu-touch/drivers/soc/mediatek/mtk-dvfsrc.c
erascape f319b992b1 kernel-5.15: Initial import brax3 UT kernel
* halium configs enabled

Signed-off-by: erascape <erascape@proton.me>
2025-09-23 15:17:10 +00:00

1414 lines
38 KiB
C

// 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>
/* Private */
#define DVFSRC_OPP_BW_QUERY
#define DVFSRC_FORCE_OPP_SUPPORT
#define DVFSRC_DEBUG_ENHANCE
#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;
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,
DVFSRC_HALT_CONTROL,
};
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] = 0x1C,
[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,
[DVFSRC_HALT_CONTROL] = 0xC4,
};
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_HALT_CONTROL, 1, 0x1, 1);
udelay(STARTUP_TIME);
dvfsrc_wait_for_idle(dvfsrc);
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_HALT_CONTROL, 0, 0x1, 1);
dvfsrc->opp_forced = false;
}
goto out;
}
dvfsrc->opp_forced = true;
dvfsrc_rmw(dvfsrc, DVFSRC_HALT_CONTROL, 1, 0x1, 1);
udelay(STARTUP_TIME);
dvfsrc_wait_for_idle(dvfsrc);
dvfsrc_write(dvfsrc, DVFSRC_SW_REQ2, 0xFFFFFFFF);
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_HALT_CONTROL, 0, 0x1, 1);
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_PROPERTY_ENABLE
u32 is_bringup = 0;
u32 dvfsrc_flag = 0;
u32 dvfsrc_vmode = 0;
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);
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_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);
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
#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_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_soc_data mt6835_data = {
DVFSRC_MT6983_SERIES_OPS,
.opps_desc = dvfsrc_opp_mt6873_desc,
.num_opp_desc = ARRAY_SIZE(dvfsrc_opp_mt6873_desc),
.regs = mt6983_regs,
#ifdef DVFSRC_FORCE_OPP_SUPPORT
.set_force_opp_level = mt6983_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,mt6835-dvfsrc",
.data = &mt6835_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");