// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2019 MediaTek Inc. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mtk-scpsys.h" #include #ifdef CONFIG_MTK_AEE_FEATURE #include #endif #define MTK_SIP_GET_SPM_INFO 0x3 #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 MBps_to_kBps(x) ((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 /** VCORE UV table */ static u32 num_vopp; static u32 *vopp_uv_tlb; #define MTK_SIP_VCOREFS_VCORE_NUM 0x6 #define MTK_SIP_VCOREFS_VCORE_UV 0x4 /* Group of bits used for function enable */ #define HAS_CAP(_c, _x) (((_c) & (_x)) == (_x)) #define DVFSRC_CAP_CLK_INIT BIT(0) #define DVFSRC_CAP_V_OPP_INIT BIT(1) #define DVFSRC_CAP_V_CHECKER BIT(2) #define DVFSRC_CAP_MTKQOS BIT(3) struct dvfsrc_opp { u32 vcore_opp; u32 dram_opp; }; struct dvfsrc_setting { u32 offset; u32 val; }; struct dvfsrc_setting_desc { const struct dvfsrc_setting *setting; u32 size; }; struct dvfsrc_domain { u32 id; u32 state; }; struct mtk_dvfsrc; struct dvfsrc_qos_data { struct device *dev; int max_vcore_opp; int max_dram_opp; void (*pm_qos_init)(struct mtk_dvfsrc *dvfsrc); struct notifier_block pm_qos_bw_notify; struct notifier_block pm_qos_ext_bw_notify; struct notifier_block pm_qos_hrtbw_notify; struct notifier_block pm_qos_ddr_notify; struct notifier_block pm_qos_vcore_notify; struct notifier_block pm_qos_scp_notify; }; struct dvfsrc_soc_data { const int *regs; u32 num_opp; const struct dvfsrc_opp **opps; u32 num_setting; const struct dvfsrc_setting_desc *init_setting; struct dvfsrc_qos_data *qos_data; u32 caps; 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); void (*set_dram_bw)(struct mtk_dvfsrc *dvfsrc, u64 bw); void (*set_dram_ext_bw)(struct mtk_dvfsrc *dvfsrc, u64 bw); void (*set_opp_level)(struct mtk_dvfsrc *dvfsrc, u32 level); void (*set_dram_hrtbw)(struct mtk_dvfsrc *dvfsrc, u64 bw); void (*set_dram_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); void (*set_ext_dram_level)(struct mtk_dvfsrc *dvfsrc, u32 level); int (*set_force_opp_level)(struct mtk_dvfsrc *dvfsrc, u32 level); int (*wait_for_dram_level)(struct mtk_dvfsrc *dvfsrc, u32 level); int (*wait_for_vcore_level)(struct mtk_dvfsrc *dvfsrc, u32 level); int (*wait_for_opp_level)(struct mtk_dvfsrc *dvfsrc, u32 level); int (*is_dvfsrc_enabled)(struct mtk_dvfsrc *dvfsrc); }; struct mtk_dvfsrc { struct device *dev; struct clk *clk_dvfsrc; const struct dvfsrc_soc_data *dvd; int dram_type; u32 vmode; u32 flag; int num_domains; struct dvfsrc_domain *domains; void __iomem *regs; struct mutex lock; struct mutex sw_req_lock; struct notifier_block scpsys_notifier; struct regulator *vcore_power; bool opp_forced; }; static DEFINE_MUTEX(pstate_lock); static DEFINE_SPINLOCK(force_req_lock); static bool is_dvfsrc_init_complete; static BLOCKING_NOTIFIER_HEAD(dvfsrc_vchk_notifier); int register_dvfsrc_vchk_notifier(struct notifier_block *nb) { return blocking_notifier_chain_register(&dvfsrc_vchk_notifier, nb); } EXPORT_SYMBOL_GPL(register_dvfsrc_vchk_notifier); int unregister_dvfsrc_vchk_notifier(struct notifier_block *nb) { return blocking_notifier_chain_unregister(&dvfsrc_vchk_notifier, nb); } EXPORT_SYMBOL_GPL(unregister_dvfsrc_vchk_notifier); static BLOCKING_NOTIFIER_HEAD(dvfsrc_dump_notifier); int register_dvfsrc_dump_notifier(struct notifier_block *nb) { return blocking_notifier_chain_register(&dvfsrc_dump_notifier, nb); } EXPORT_SYMBOL_GPL(register_dvfsrc_dump_notifier); int unregister_dvfsrc_dump_notifier(struct notifier_block *nb) { return blocking_notifier_chain_unregister(&dvfsrc_dump_notifier, nb); } EXPORT_SYMBOL_GPL(unregister_dvfsrc_dump_notifier); int mtk_dvfsrc_vcore_uv_table(u32 opp) { if ((!vopp_uv_tlb) || (opp >= num_vopp)) return 0; return vopp_uv_tlb[num_vopp - opp - 1]; } EXPORT_SYMBOL(mtk_dvfsrc_vcore_uv_table); int mtk_dvfsrc_vcore_opp_count(void) { return num_vopp; } EXPORT_SYMBOL(mtk_dvfsrc_vcore_opp_count); static void mtk_dvfsrc_setup_vopp_table(struct mtk_dvfsrc *dvfsrc) { int i; struct arm_smccc_res ares; u32 num_opp = dvfsrc->dvd->num_opp; num_vopp = dvfsrc->dvd->opps[dvfsrc->dram_type][num_opp - 1].vcore_opp + 1; vopp_uv_tlb = kcalloc(num_vopp, sizeof(u32), GFP_KERNEL); if (!vopp_uv_tlb) return; for (i = 0; i < num_vopp; i++) { arm_smccc_smc(MTK_SIP_VCOREFS_CONTROL, MTK_SIP_VCOREFS_VCORE_UV, i, 0, 0, 0, 0, 0, &ares); if (!ares.a0) vopp_uv_tlb[i] = ares.a1; else { kfree(vopp_uv_tlb); vopp_uv_tlb = NULL; break; } } for (i = 0; i < num_vopp; i++) dev_info(dvfsrc->dev, "dvfsrc vopp[%d] = %d\n", i, mtk_dvfsrc_vcore_uv_table(i)); } static void mtk_dvfsrc_dump_info(struct mtk_dvfsrc *dvfsrc) { blocking_notifier_call_chain(&dvfsrc_dump_notifier, 0, NULL); } static void mtk_dvfsrc_vcore_check(struct mtk_dvfsrc *dvfsrc, u32 level) { int ret; ret = blocking_notifier_call_chain(&dvfsrc_vchk_notifier, level, NULL); if (ret == NOTIFY_BAD) { dev_info(dvfsrc->dev, "DVFS FAIL= %d, 0x%08x 0x%08x\n", level, dvfsrc->dvd->get_current_level(dvfsrc), dvfsrc->dvd->get_target_level(dvfsrc)); mtk_dvfsrc_dump_info(dvfsrc); #ifdef CONFIG_MTK_AEE_FEATURE aee_kernel_warning("DVFSRC", "VCORE fail"); #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_SW_REQ, DVFSRC_EXT_SW_REQ, DVFSRC_LEVEL, DVFSRC_SW_BW, DVFSRC_SW_EXT_BW, DVFSRC_SW_HRT_BW, DVFSRC_TARGET_LEVEL, DVFSRC_VCORE_REQUEST, DVFSRC_BASIC_CONTROL, DVFSRC_TARGET_FORCE, }; static const int mt8183_regs[] = { [DVFSRC_SW_REQ] = 0x4, [DVFSRC_LEVEL] = 0xDC, [DVFSRC_SW_BW] = 0x160, }; static const int mt6779_regs[] = { [DVFSRC_SW_REQ] = 0xC, [DVFSRC_LEVEL] = 0xD44, [DVFSRC_SW_BW] = 0x26C, [DVFSRC_SW_EXT_BW] = 0x260, [DVFSRC_SW_HRT_BW] = 0x290, [DVFSRC_TARGET_LEVEL] = 0xD48, [DVFSRC_VCORE_REQUEST] = 0x6C, [DVFSRC_BASIC_CONTROL] = 0x0, [DVFSRC_TARGET_FORCE] = 0xD70, }; static const int mt6761_regs[] = { [DVFSRC_SW_REQ] = 0x4, [DVFSRC_EXT_SW_REQ] = 0x8, [DVFSRC_LEVEL] = 0xDC, [DVFSRC_SW_BW] = 0x16C, [DVFSRC_SW_EXT_BW] = 0x160, [DVFSRC_VCORE_REQUEST] = 0x48, [DVFSRC_BASIC_CONTROL] = 0x0, [DVFSRC_TARGET_FORCE] = 0x300, }; static const struct dvfsrc_opp *get_current_opp(struct mtk_dvfsrc *dvfsrc) { int level; level = dvfsrc->dvd->get_current_level(dvfsrc); return &dvfsrc->dvd->opps[dvfsrc->dram_type][level]; } static bool 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_opp_level(struct mtk_dvfsrc *dvfsrc, u32 level) { const struct dvfsrc_opp *target, *curr; target = &dvfsrc->dvd->opps[dvfsrc->dram_type][level]; return readx_poll_timeout_atomic(get_current_opp, dvfsrc, curr, curr->dram_opp >= target->dram_opp, 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 mt6779_dvfsrc_enabled(struct mtk_dvfsrc *dvfsrc) { return dvfsrc_read(dvfsrc, DVFSRC_BASIC_CONTROL) & 0x1; } static int mt6779_get_target_level(struct mtk_dvfsrc *dvfsrc) { return dvfsrc_read(dvfsrc, DVFSRC_TARGET_LEVEL); } static int mt6779_get_current_level(struct mtk_dvfsrc *dvfsrc) { u32 curr_level; curr_level = ffs(dvfsrc_read(dvfsrc, DVFSRC_LEVEL)); if (curr_level > dvfsrc->dvd->num_opp) curr_level = 0; else curr_level = dvfsrc->dvd->num_opp - curr_level; return curr_level; } static u32 mt6779_get_vcore_level(struct mtk_dvfsrc *dvfsrc) { return (dvfsrc_read(dvfsrc, DVFSRC_SW_REQ) >> 4) & 0x7; } static u32 mt6779_get_vcp_level(struct mtk_dvfsrc *dvfsrc) { return (dvfsrc_read(dvfsrc, DVFSRC_VCORE_REQUEST) >> 12) & 0x7; } static void mt6779_set_dram_bw(struct mtk_dvfsrc *dvfsrc, u64 bw) { bw = div_u64(kBps_to_MBps(bw), 100); bw = (bw < 0xFF) ? bw : 0xff; dvfsrc_write(dvfsrc, DVFSRC_SW_BW, bw); } static void mt6779_set_dram_ext_bw(struct mtk_dvfsrc *dvfsrc, u64 bw) { bw = div_u64(kBps_to_MBps(bw), 100); bw = (bw < 0xFF) ? bw : 0xff; dvfsrc_write(dvfsrc, DVFSRC_SW_EXT_BW, bw); } /* bw unit 30MBps */ static void mt6779_set_dram_hrtbw(struct mtk_dvfsrc *dvfsrc, u64 bw) { bw = div_u64((kBps_to_MBps(bw) + 29), 30); if (bw > 0x3FF) bw = 0x3FF; dvfsrc_write(dvfsrc, DVFSRC_SW_HRT_BW, bw); } static void mt6779_set_vcore_level(struct mtk_dvfsrc *dvfsrc, u32 level) { mutex_lock(&dvfsrc->sw_req_lock); dvfsrc_rmw(dvfsrc, DVFSRC_SW_REQ, level, 0x7, 4); mutex_unlock(&dvfsrc->sw_req_lock); } static void mt6779_set_vscp_level(struct mtk_dvfsrc *dvfsrc, u32 level) { dvfsrc_rmw(dvfsrc, DVFSRC_VCORE_REQUEST, level, 0x7, 12); } static void mt6779_set_dram_level(struct mtk_dvfsrc *dvfsrc, u32 level) { mutex_lock(&dvfsrc->sw_req_lock); dvfsrc_rmw(dvfsrc, DVFSRC_SW_REQ, level, 0x7, 12); mutex_unlock(&dvfsrc->sw_req_lock); } static void mt6779_set_opp_level(struct mtk_dvfsrc *dvfsrc, u32 level) { const struct dvfsrc_opp *opp; opp = &dvfsrc->dvd->opps[dvfsrc->dram_type][level]; mt6779_set_dram_level(dvfsrc, opp->dram_opp); } static int mt6779_set_force_opp_level(struct mtk_dvfsrc *dvfsrc, u32 level) { unsigned long flags; int val; int ret = 0; if (level > dvfsrc->dvd->num_opp - 1) { dvfsrc_rmw(dvfsrc, DVFSRC_BASIC_CONTROL, 0, 0x1, 15); dvfsrc_write(dvfsrc, DVFSRC_TARGET_FORCE, 0); dvfsrc->opp_forced = false; return 0; } dvfsrc->opp_forced = true; spin_lock_irqsave(&force_req_lock, flags); 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); 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)); mtk_dvfsrc_dump_info(dvfsrc); #ifdef CONFIG_MTK_AEE_FEATURE aee_kernel_warning("DVFSRC", "FORCE OPP fail"); #endif } dvfsrc_write(dvfsrc, DVFSRC_TARGET_FORCE, 0); spin_unlock_irqrestore(&force_req_lock, flags); return ret; } static int mt6761_dvfsrc_enabled(struct mtk_dvfsrc *dvfsrc) { return dvfsrc_read(dvfsrc, DVFSRC_BASIC_CONTROL) & 0x1; } static int mt6761_get_target_level(struct mtk_dvfsrc *dvfsrc) { return DVFSRC_GET_TARGET_LEVEL(dvfsrc_read(dvfsrc, DVFSRC_LEVEL)); } static int mt6761_get_current_level(struct mtk_dvfsrc *dvfsrc) { u32 curr_level; curr_level = dvfsrc_read(dvfsrc, DVFSRC_LEVEL); curr_level = ffs(DVFSRC_GET_CURRENT_LEVEL(curr_level)); if ((curr_level > 0) && (curr_level <= dvfsrc->dvd->num_opp)) return curr_level - 1; else return 0; } static u32 mt6761_get_vcore_level(struct mtk_dvfsrc *dvfsrc) { return (dvfsrc_read(dvfsrc, DVFSRC_SW_REQ) >> 2) & 0x3; } static u32 mt6761_get_vcp_level(struct mtk_dvfsrc *dvfsrc) { return (dvfsrc_read(dvfsrc, DVFSRC_VCORE_REQUEST) >> 30) & 0x3; } static void mt6761_set_dram_bw(struct mtk_dvfsrc *dvfsrc, u64 bw) { bw = div_u64(kBps_to_MBps(bw), 100); bw = (bw < 0xFF) ? bw : 0xff; dvfsrc_write(dvfsrc, DVFSRC_SW_BW, bw); } static void mt6761_set_dram_ext_bw(struct mtk_dvfsrc *dvfsrc, u64 bw) { bw = div_u64(kBps_to_MBps(bw), 100); bw = (bw < 0xFF) ? bw : 0xff; dvfsrc_write(dvfsrc, DVFSRC_SW_EXT_BW, bw); } static void mt6761_set_vcore_level(struct mtk_dvfsrc *dvfsrc, u32 level) { mutex_lock(&dvfsrc->sw_req_lock); dvfsrc_rmw(dvfsrc, DVFSRC_SW_REQ, level, 0x3, 2); mutex_unlock(&dvfsrc->sw_req_lock); } static void mt6761_set_vscp_level(struct mtk_dvfsrc *dvfsrc, u32 level) { dvfsrc_rmw(dvfsrc, DVFSRC_VCORE_REQUEST, level, 0x3, 30); } static void mt6761_set_dram_level(struct mtk_dvfsrc *dvfsrc, u32 level) { mutex_lock(&dvfsrc->sw_req_lock); dvfsrc_rmw(dvfsrc, DVFSRC_SW_REQ, level, 0x3, 0); mutex_unlock(&dvfsrc->sw_req_lock); } static void mt6761_set_ext_dram_level(struct mtk_dvfsrc *dvfsrc, u32 level) { const struct dvfsrc_soc_data *dvd = dvfsrc->dvd; int max_dram_opp; u32 opp = dvfsrc->dvd->num_opp; u32 dram_type = dvfsrc->dram_type; max_dram_opp = dvd->opps[dram_type][opp - 1].dram_opp; if (level > max_dram_opp) level = 0; dvfsrc_rmw(dvfsrc, DVFSRC_EXT_SW_REQ, level, 0x3, 0); } static void mt6761_set_opp_level(struct mtk_dvfsrc *dvfsrc, u32 level) { const struct dvfsrc_opp *opp; opp = &dvfsrc->dvd->opps[dvfsrc->dram_type][level]; mt6761_set_dram_level(dvfsrc, opp->dram_opp); } static int mt6761_set_force_opp_level(struct mtk_dvfsrc *dvfsrc, u32 level) { unsigned long flags; int val; int ret = 0; if (level > dvfsrc->dvd->num_opp - 1) { dvfsrc_rmw(dvfsrc, DVFSRC_BASIC_CONTROL, 0, 0x1, 15); dvfsrc_write(dvfsrc, DVFSRC_TARGET_FORCE, 0); dvfsrc->opp_forced = false; return 0; } dvfsrc->opp_forced = true; spin_lock_irqsave(&force_req_lock, flags); level = dvfsrc->dvd->num_opp - 1 - level; 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, DVFSRC_GET_CURRENT_LEVEL(val) == (1 << level), STARTUP_TIME, POLL_TIMEOUT); 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)); mtk_dvfsrc_dump_info(dvfsrc); #ifdef CONFIG_MTK_AEE_FEATURE aee_kernel_warning("DVFSRC", "FORCE OPP fail"); #endif } dvfsrc_write(dvfsrc, DVFSRC_TARGET_FORCE, 0); spin_unlock_irqrestore(&force_req_lock, flags); return ret; } 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) { return ffs(DVFSRC_GET_CURRENT_LEVEL(dvfsrc_read(dvfsrc, DVFSRC_LEVEL))); } 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->dvd->opps[dvfsrc->dram_type][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 int dvfsrc_pm_qos_bw_notify(struct notifier_block *b, unsigned long l, void *v) { struct dvfsrc_qos_data *qos; qos = container_of(b, struct dvfsrc_qos_data, pm_qos_bw_notify); mtk_dvfsrc_send_request(qos->dev, MTK_DVFSRC_CMD_BW_REQUEST, MBps_to_kBps(l)); return NOTIFY_OK; } static int dvfsrc_pm_qos_ext_bw_notify(struct notifier_block *b, unsigned long l, void *v) { struct dvfsrc_qos_data *qos; qos = container_of(b, struct dvfsrc_qos_data, pm_qos_ext_bw_notify); mtk_dvfsrc_send_request(qos->dev, MTK_DVFSRC_CMD_EXT_BW_REQUEST, MBps_to_kBps(l)); return NOTIFY_OK; } static int dvfsrc_pm_qos_hrtbw_notify(struct notifier_block *b, unsigned long l, void *v) { struct dvfsrc_qos_data *qos; qos = container_of(b, struct dvfsrc_qos_data, pm_qos_hrtbw_notify); mtk_dvfsrc_send_request(qos->dev, MTK_DVFSRC_CMD_HRTBW_REQUEST, MBps_to_kBps(l)); return NOTIFY_OK; } static int dvfsrc_pm_qos_ddr_notify(struct notifier_block *b, unsigned long l, void *v) { struct dvfsrc_qos_data *qos; int level; qos = container_of(b, struct dvfsrc_qos_data, pm_qos_ddr_notify); if (l > qos->max_dram_opp || l < 0) level = 0; else level = qos->max_dram_opp - l; mtk_dvfsrc_send_request(qos->dev, MTK_DVFSRC_CMD_DRAM_REQUEST, level); return NOTIFY_OK; } static int dvfsrc_pm_qos_vcore_notify(struct notifier_block *b, unsigned long l, void *v) { struct dvfsrc_qos_data *qos; int level; qos = container_of(b, struct dvfsrc_qos_data, pm_qos_vcore_notify); if (l > qos->max_vcore_opp || l < 0) level = 0; else level = qos->max_vcore_opp - l; mtk_dvfsrc_send_request(qos->dev, MTK_DVFSRC_CMD_VCORE_REQUEST, level); return NOTIFY_OK; } static int dvfsrc_pm_qos_scp_notify(struct notifier_block *b, unsigned long l, void *v) { struct dvfsrc_qos_data *qos; int level; qos = container_of(b, struct dvfsrc_qos_data, pm_qos_scp_notify); if (l > qos->max_vcore_opp || l < 0) level = 0; else level = l; mtk_dvfsrc_send_request(qos->dev, MTK_DVFSRC_CMD_VSCP_REQUEST, level); return NOTIFY_OK; } static void dvfsrc_qos_init(struct mtk_dvfsrc *dvfsrc) { u32 opp = dvfsrc->dvd->num_opp; u32 dram_type = dvfsrc->dram_type; const struct dvfsrc_soc_data *dvd = dvfsrc->dvd; struct dvfsrc_qos_data *qos = dvd->qos_data; qos->dev = dvfsrc->dev; qos->max_vcore_opp = dvd->opps[dram_type][opp - 1].vcore_opp; qos->max_dram_opp = dvd->opps[dram_type][opp - 1].dram_opp; mtk_pm_qos_add_notifier(MTK_PM_QOS_MEMORY_BANDWIDTH, &qos->pm_qos_bw_notify); mtk_pm_qos_add_notifier(MTK_PM_QOS_MEMORY_EXT_BANDWIDTH, &qos->pm_qos_ext_bw_notify); mtk_pm_qos_add_notifier(MTK_PM_QOS_HRT_BANDWIDTH, &qos->pm_qos_hrtbw_notify); mtk_pm_qos_add_notifier(MTK_PM_QOS_DDR_OPP, &qos->pm_qos_ddr_notify); mtk_pm_qos_add_notifier(MTK_PM_QOS_VCORE_OPP, &qos->pm_qos_vcore_notify); mtk_pm_qos_add_notifier(MTK_PM_QOS_SCP_VCORE_REQUEST, &qos->pm_qos_scp_notify); } static struct dvfsrc_qos_data qos_data_dvfsrc = { .pm_qos_init = dvfsrc_qos_init, .pm_qos_bw_notify.notifier_call = dvfsrc_pm_qos_bw_notify, .pm_qos_ext_bw_notify.notifier_call = dvfsrc_pm_qos_ext_bw_notify, .pm_qos_hrtbw_notify.notifier_call = dvfsrc_pm_qos_hrtbw_notify, .pm_qos_ddr_notify.notifier_call = dvfsrc_pm_qos_ddr_notify, .pm_qos_vcore_notify.notifier_call = dvfsrc_pm_qos_vcore_notify, .pm_qos_scp_notify.notifier_call = dvfsrc_pm_qos_scp_notify, }; void mtk_dvfsrc_send_request(const struct device *dev, u32 cmd, u64 data) { struct mtk_dvfsrc *dvfsrc = dev_get_drvdata(dev); int ret = 0; if (cmd == MTK_DVFSRC_CMD_FORCE_OPP_REQUEST) { if (dvfsrc->dvd->set_force_opp_level) dvfsrc->dvd->set_force_opp_level(dvfsrc, data); return; } switch (cmd) { case MTK_DVFSRC_CMD_BW_REQUEST: dvfsrc->dvd->set_dram_bw(dvfsrc, data); goto out; case MTK_DVFSRC_CMD_EXT_BW_REQUEST: if (dvfsrc->dvd->set_dram_ext_bw) dvfsrc->dvd->set_dram_ext_bw(dvfsrc, data); goto out; case MTK_DVFSRC_CMD_OPP_REQUEST: dvfsrc->dvd->set_opp_level(dvfsrc, data); break; case MTK_DVFSRC_CMD_DRAM_REQUEST: dvfsrc->dvd->set_dram_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_VSCP_REQUEST: dvfsrc->dvd->set_vscp_level(dvfsrc, data); break; case MTK_DVFSRC_CMD_EXT_DRAM_REQUEST: if (dvfsrc->dvd->set_ext_dram_level) dvfsrc->dvd->set_ext_dram_level(dvfsrc, data); goto out; default: dev_err(dvfsrc->dev, "unknown command: %d\n", cmd); goto out; break; } if (dvfsrc->opp_forced) goto out; if (dvfsrc->dvd->is_dvfsrc_enabled && !dvfsrc->dvd->is_dvfsrc_enabled(dvfsrc)) goto out; udelay(STARTUP_TIME); dvfsrc_wait_for_idle(dvfsrc); 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_DRAM_REQUEST: if (dvfsrc->dvd->wait_for_dram_level) ret = dvfsrc->dvd->wait_for_dram_level(dvfsrc, data); break; case MTK_DVFSRC_CMD_VCORE_REQUEST: case MTK_DVFSRC_CMD_VSCP_REQUEST: if (dvfsrc->dvd->wait_for_vcore_level) { ret = dvfsrc->dvd->wait_for_vcore_level(dvfsrc, data); if (HAS_CAP(dvfsrc->dvd->caps, DVFSRC_CAP_V_CHECKER)) mtk_dvfsrc_vcore_check(dvfsrc, data); } break; } out: if (ret) { dev_warn(dvfsrc->dev, "[%s][%d] wait idle, level: %llu, last: %d -> %x\n", __func__, cmd, data, dvfsrc->dvd->get_current_level(dvfsrc), dvfsrc->dvd->get_target_level(dvfsrc)); mtk_dvfsrc_dump_info(dvfsrc); #ifdef CONFIG_MTK_AEE_FEATURE aee_kernel_warning("DVFSRC", "LEVEL CHANGE fail"); #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_QUERY: *data = dvfsrc->dvd->get_vcore_level(dvfsrc); break; case MTK_DVFSRC_CMD_VCP_QUERY: *data = dvfsrc->dvd->get_vcp_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 l, void *v) { int i; bool match = false; u32 highest; struct mtk_dvfsrc *dvfsrc; struct scp_event_data *sc = v; struct dvfsrc_domain *d; if (sc->event_type != MTK_SCPSYS_PSTATE) return 0; dvfsrc = container_of(b, struct mtk_dvfsrc, scpsys_notifier); d = dvfsrc->domains; if (l > dvfsrc->dvd->num_opp) { dev_err(dvfsrc->dev, "pstate out of range = %ld\n", l); goto out; } mutex_lock(&pstate_lock); for (i = 0, highest = 0; i < dvfsrc->num_domains; i++, d++) { if (sc->domain_id == d->id) { d->state = l; match = true; } highest = max(highest, d->state); } if (!match) goto out; if (highest != 0) highest = highest - 1; mtk_dvfsrc_send_request(dvfsrc->dev, MTK_DVFSRC_CMD_OPP_REQUEST, highest); out: mutex_unlock(&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 void dvfsrc_init_settings(struct mtk_dvfsrc *dvfsrc) { int i; const struct dvfsrc_setting_desc *desc; const struct dvfsrc_setting *setting; desc = &dvfsrc->dvd->init_setting[dvfsrc->dram_type]; setting = desc->setting; for (i = 0; i < desc->size; i++) writel(setting[i].val, dvfsrc->regs + setting[i].offset); } static int mtk_dvfsrc_probe(struct platform_device *pdev) { struct arm_smccc_res ares; struct resource *res; struct mtk_dvfsrc *dvfsrc; struct device_node *node = pdev->dev.of_node; int i; int ret; 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); dvfsrc->num_domains = of_count_phandle_with_args(node, "perf-domains", NULL); if (dvfsrc->num_domains > 0) { dvfsrc->domains = devm_kzalloc(&pdev->dev, sizeof(struct dvfsrc_domain) * dvfsrc->num_domains, GFP_KERNEL); if (!dvfsrc->domains) return -ENOMEM; for (i = 0; i < dvfsrc->num_domains; i++) { ret = of_property_read_u32_index(node, "perf-domains", i, &dvfsrc->domains[i].id); if (ret) dev_info(dvfsrc->dev, "Invalid favor domain idx = %d\n", i); } } else dvfsrc->num_domains = 0; if (HAS_CAP(dvfsrc->dvd->caps, DVFSRC_CAP_CLK_INIT)) { dvfsrc->clk_dvfsrc = devm_clk_get(dvfsrc->dev, "dvfsrc"); if (IS_ERR(dvfsrc->clk_dvfsrc)) { dev_err(dvfsrc->dev, "failed to get clock: %ld\n", PTR_ERR(dvfsrc->clk_dvfsrc)); return PTR_ERR(dvfsrc->clk_dvfsrc); } ret = clk_prepare_enable(dvfsrc->clk_dvfsrc); if (ret) return ret; } of_property_read_u32(node, "dvfsrc,mode", &dvfsrc->flag); of_property_read_u32(node, "dvfsrc_flag", &dvfsrc->flag); of_property_read_u32(node, "dvfsrc_vmode", &dvfsrc->vmode); mutex_init(&dvfsrc->lock); mutex_init(&dvfsrc->sw_req_lock); arm_smccc_smc(MTK_SIP_VCOREFS_CONTROL, MTK_SIP_GET_SPM_INFO, 0, 0, 0, 0, 0, 0, &ares); if (!ares.a0) { if (ares.a1 < dvfsrc->dvd->num_setting) dvfsrc->dram_type = ares.a1; else return -EINVAL; } else return -ENODEV; dev_info(dvfsrc->dev, "dram_type: %d\n", dvfsrc->dram_type); arm_smccc_smc(MTK_SIP_VCOREFS_CONTROL, MTK_SIP_SPM_DVFSRC_INIT, dvfsrc->flag, dvfsrc->vmode, 0, 0, 0, 0, &ares); if (!ares.a0) dvfsrc_init_settings(dvfsrc); else dev_info(dvfsrc->dev, "spm init fails: %lx\n", ares.a0); platform_set_drvdata(pdev, dvfsrc); pstate_notifier_register(dvfsrc); if (HAS_CAP(dvfsrc->dvd->caps, DVFSRC_CAP_V_OPP_INIT) || HAS_CAP(dvfsrc->dvd->caps, DVFSRC_CAP_V_CHECKER)) mtk_dvfsrc_setup_vopp_table(dvfsrc); ret = devm_of_platform_populate(&pdev->dev); if (ret) dev_err(&pdev->dev, "Failed to populate dvfsrc context\n"); if (HAS_CAP(dvfsrc->dvd->caps, DVFSRC_CAP_MTKQOS)) dvfsrc->dvd->qos_data->pm_qos_init(dvfsrc); is_dvfsrc_init_complete = true; return 0; } 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 *dvfsrc_opp_mt8183[] = { [MT8183_DVFSRC_OPP_LP4] = dvfsrc_opp_mt8183_lp4, [MT8183_DVFSRC_OPP_LP4X] = dvfsrc_opp_mt8183_lp3, [MT8183_DVFSRC_OPP_LP3] = dvfsrc_opp_mt8183_lp3, }; static const struct dvfsrc_soc_data mt8183_data = { .opps = dvfsrc_opp_mt8183, .num_opp = ARRAY_SIZE(dvfsrc_opp_mt8183_lp4), .regs = mt8183_regs, .get_target_level = mt8183_get_target_level, .get_current_level = mt8183_get_current_level, .set_dram_bw = mt8183_set_dram_bw, .set_opp_level = mt8183_set_opp_level, }; static const struct dvfsrc_setting dvfsrc_mt6779_setting_0[] = { {0xF8, 0x0000002B}, {0xC8, 0x00000002}, {0x280, 0x0000407C}, {0x78, 0x21110000}, {0xA00, 0x00004322}, {0xA08, 0x00000055}, {0xA10, 0x00322000}, {0xA18, 0x54000000}, {0xCF4, 0x55000000}, {0xA34, 0x00000019}, {0xA38, 0x00000026}, {0xA3C, 0x00000033}, {0xA40, 0x0000004C}, {0xA44, 0x00000066}, {0xD18, 0x00000077}, {0xD1C, 0x00000077}, {0xA84, 0x0000011E}, {0xA88, 0x0000D3D3}, {0xA8C, 0x00200802}, {0xA90, 0x00200800}, {0xA94, 0x00200002}, {0xA98, 0x00200802}, {0xA9C, 0x00400802}, {0xAA0, 0x00601404}, {0xAA4, 0x00902008}, {0xAA8, 0x00E0380E}, {0xACC, 0x00000000}, {0xAD0, 0x00000000}, {0xAD4, 0x00034C00}, {0xAAC, 0x0360D836}, {0xAB0, 0x0360D800}, {0xAB4, 0x03600036}, {0xAB8, 0x0360D836}, {0xABC, 0x0360D836}, {0xAC0, 0x0360D836}, {0xAC4, 0x0360D836}, {0xAC8, 0x0360D836}, {0xAD8, 0x00000000}, {0xADC, 0x00000000}, {0xAE0, 0x00034C00}, {0xAF4, 0x070804B0}, {0xAF0, 0x11830B80}, {0xAEC, 0x18A618A6}, {0xD38, 0x000018A6}, {0xB00, 0x070704AF}, {0xAFC, 0x11820B7F}, {0xAF8, 0x18A518A5}, {0xD3C, 0x000018A5}, {0xAE8, 0x05554322}, {0x308, 0x4C4C0AB0}, {0xB04, 0x05543210}, {0xD74, 0x03333210}, {0x100, 0x40225032}, {0x104, 0x20223012}, {0x108, 0x40211012}, {0x10C, 0x20213011}, {0x110, 0x30101011}, {0x114, 0x10102000}, {0x118, 0x00000000}, {0x11C, 0x00000000}, {0xD6C, 0x00000001}, {0x0, 0x3599404B}, {0x0, 0x3599014B}, {0xD6C, 0x00000000}, }; static const struct dvfsrc_setting_desc dvfsrc_setting_mt6779[] = { { .setting = dvfsrc_mt6779_setting_0, .size = ARRAY_SIZE(dvfsrc_mt6779_setting_0), } }; static const struct dvfsrc_opp dvfsrc_opp_mt6779_lp4[] = { {0, 0}, {0, 1}, {0, 2}, {0, 3}, {1, 1}, {1, 2}, {1, 3}, {1, 4}, {2, 1}, {2, 2}, {2, 3}, {2, 4}, {2, 5}, }; static const struct dvfsrc_opp *dvfsrc_opp_mt6779[] = { [0] = dvfsrc_opp_mt6779_lp4, }; static const struct dvfsrc_soc_data mt6779_data = { .opps = dvfsrc_opp_mt6779, .num_opp = ARRAY_SIZE(dvfsrc_opp_mt6779_lp4), .init_setting = dvfsrc_setting_mt6779, .num_setting = ARRAY_SIZE(dvfsrc_setting_mt6779), .regs = mt6779_regs, .caps = DVFSRC_CAP_CLK_INIT | DVFSRC_CAP_MTKQOS | DVFSRC_CAP_V_OPP_INIT | DVFSRC_CAP_V_CHECKER, .qos_data = &qos_data_dvfsrc, .get_target_level = mt6779_get_target_level, .get_current_level = mt6779_get_current_level, .get_vcore_level = mt6779_get_vcore_level, .get_vcp_level = mt6779_get_vcp_level, .wait_for_vcore_level = dvfsrc_wait_for_vcore_level, .wait_for_dram_level = dvfsrc_wait_for_dram_level, .wait_for_opp_level = dvfsrc_wait_for_opp_level, .set_dram_bw = mt6779_set_dram_bw, .set_dram_ext_bw = mt6779_set_dram_ext_bw, .set_dram_level = mt6779_set_dram_level, .set_opp_level = mt6779_set_opp_level, .set_dram_hrtbw = mt6779_set_dram_hrtbw, .set_vcore_level = mt6779_set_vcore_level, .set_vscp_level = mt6779_set_vscp_level, .set_force_opp_level = mt6779_set_force_opp_level, .is_dvfsrc_enabled = mt6779_dvfsrc_enabled, }; static const struct dvfsrc_setting dvfsrc_mt6761_setting_0[] = { {0xC, 0x00240009}, {0x14, 0x09000000}, {0x18, 0x003E362C}, {0x24, 0x00000033}, {0x28, 0x0000004C}, {0x3C, 0x00000007}, {0x40, 0x00000038}, {0x44, 0x000080C0}, {0x78, 0x000080C0}, {0x48, 0x000C0000}, {0x50, 0x00000036}, {0x84, 0x20000000}, {0xD8, 0x00000014}, {0x9C, 0x00000003}, {0xE0, 0x00010000}, {0xE4, 0x00020101}, {0xE8, 0x01020012}, {0xEC, 0x02120112}, {0xF0, 0x00230013}, {0xF4, 0x01230113}, {0xF8, 0x02230213}, {0xFC, 0x03230323}, {0x300, 0x20000000}, {0x604, 0x0000000C}, {0x180, 0x0000407F}, {0x0, 0x0000407B}, {0x0, 0x0000017B}, {0x300, 0x00000000}, }; static const struct dvfsrc_setting dvfsrc_mt6761_setting_1[] = { {0xC, 0x00240009 }, {0x14, 0x09000000}, {0x18, 0x00000020 }, {0x24, 0x00000026}, {0x28, 0x00000033 }, {0x3C, 0x00000007}, {0x40, 0x00000038 }, {0x44, 0x000080C0}, {0x50, 0x00000020 }, {0x84, 0x20000000}, {0xD8, 0x00000014 }, {0x9C, 0x00000003}, {0xE0, 0x00010000 }, {0xE4, 0x00020101}, {0xE8, 0x01020012 }, {0xEC, 0x02120112}, {0xF0, 0x00230013 }, {0xF4, 0x01230113}, {0xF8, 0x02230213 }, {0xFC, 0x03230323}, {0x300, 0x20000000 }, {0x604, 0x0000000C}, {0x180, 0x0000407F }, {0x0, 0x0000407B}, {0x0, 0x0000017B}, {0x300, 0x00000000}, }; static const struct dvfsrc_setting_desc dvfsrc_setting_mt6761[] = { { .setting = dvfsrc_mt6761_setting_0, .size = ARRAY_SIZE(dvfsrc_mt6761_setting_0), }, { .setting = dvfsrc_mt6761_setting_0, .size = ARRAY_SIZE(dvfsrc_mt6761_setting_0), }, { .setting = dvfsrc_mt6761_setting_1, .size = ARRAY_SIZE(dvfsrc_mt6761_setting_1), }, { .setting = dvfsrc_mt6761_setting_0, .size = ARRAY_SIZE(dvfsrc_mt6761_setting_0), }, { .setting = dvfsrc_mt6761_setting_0, .size = ARRAY_SIZE(dvfsrc_mt6761_setting_0), } }; static const struct dvfsrc_opp dvfsrc_opp_mt6761_0[] = { {0, 0}, {1, 0}, {1, 0}, {2, 0}, {2, 1}, {2, 0}, {2, 1}, {2, 1}, {3, 1}, {3, 2}, {3, 1}, {3, 2}, {3, 1}, {3, 2}, {3, 2}, {3, 2}, }; static const struct dvfsrc_opp *dvfsrc_opps_mt6761[] = { [0] = dvfsrc_opp_mt6761_0, [1] = dvfsrc_opp_mt6761_0, [2] = dvfsrc_opp_mt6761_0, [3] = dvfsrc_opp_mt6761_0, [4] = dvfsrc_opp_mt6761_0, }; static const struct dvfsrc_soc_data mt6761_data = { .opps = dvfsrc_opps_mt6761, .num_opp = ARRAY_SIZE(dvfsrc_opp_mt6761_0), .init_setting = dvfsrc_setting_mt6761, .num_setting = ARRAY_SIZE(dvfsrc_setting_mt6761), .regs = mt6761_regs, .caps = DVFSRC_CAP_V_OPP_INIT | DVFSRC_CAP_V_CHECKER | DVFSRC_CAP_MTKQOS, .qos_data = &qos_data_dvfsrc, .get_target_level = mt6761_get_target_level, .get_current_level = mt6761_get_current_level, .get_vcore_level = mt6761_get_vcore_level, .get_vcp_level = mt6761_get_vcp_level, .wait_for_dram_level = dvfsrc_wait_for_dram_level, .wait_for_vcore_level = dvfsrc_wait_for_vcore_level, .wait_for_opp_level = dvfsrc_wait_for_opp_level, .set_dram_bw = mt6761_set_dram_bw, .set_dram_ext_bw = mt6761_set_dram_ext_bw, .set_dram_level = mt6761_set_dram_level, .set_vcore_level = mt6761_set_vcore_level, .set_opp_level = mt6761_set_opp_level, .set_vscp_level = mt6761_set_vscp_level, .set_ext_dram_level = mt6761_set_ext_dram_level, .set_force_opp_level = mt6761_set_force_opp_level, .is_dvfsrc_enabled = mt6761_dvfsrc_enabled, }; static const struct dvfsrc_setting dvfsrc_mt6765_setting_0[] = { {0xC, 0x00240009}, {0x14, 0x09000000}, {0x18, 0x003E362C}, {0x24, 0x00000033}, {0x28, 0x0000004C}, {0x3C, 0x00000007}, {0x40, 0x00000038}, {0x44, 0x000080C0}, {0x78, 0x000080C0}, {0x48, 0x000C0000}, {0x50, 0x00000036}, {0x84, 0x20000000}, {0xD8, 0x00000014}, {0x9C, 0x00000003}, {0xE0, 0x00010000}, {0xE4, 0x00020101}, {0xE8, 0x01020012}, {0xEC, 0x02120112}, {0xF0, 0x00230013}, {0xF4, 0x01230113}, {0xF8, 0x02230213}, {0xFC, 0x03230323}, {0x300, 0x20000000}, {0x604, 0x0000000C}, {0x180, 0x0000407F}, {0x0, 0x0000407B}, {0x0, 0x0000017B}, {0x300, 0x00000000}, }; static const struct dvfsrc_setting dvfsrc_mt6765_setting_1[] = { {0xC, 0x00240009 }, {0x14, 0x09000000}, {0x18, 0x00000020 }, {0x24, 0x00000026}, {0x28, 0x00000033 }, {0x3C, 0x00000007}, {0x40, 0x00000038 }, {0x44, 0x000080C0}, {0x50, 0x00000020 }, {0x84, 0x20000000}, {0xD8, 0x00000014 }, {0x9C, 0x00000003}, {0xE0, 0x00010000 }, {0xE4, 0x00020101}, {0xE8, 0x01020012 }, {0xEC, 0x02120112}, {0xF0, 0x00230013 }, {0xF4, 0x01230113}, {0xF8, 0x02230213 }, {0xFC, 0x03230323}, {0x300, 0x20000000 }, {0x604, 0x0000000C}, {0x180, 0x0000407F }, {0x0, 0x0000407B}, {0x0, 0x0000017B}, {0x300, 0x00000000}, }; static const struct dvfsrc_setting_desc dvfsrc_setting_mt6765[] = { { .setting = dvfsrc_mt6765_setting_0, .size = ARRAY_SIZE(dvfsrc_mt6765_setting_0), }, { .setting = dvfsrc_mt6765_setting_0, .size = ARRAY_SIZE(dvfsrc_mt6765_setting_0), }, { .setting = dvfsrc_mt6765_setting_1, .size = ARRAY_SIZE(dvfsrc_mt6765_setting_1), }, { .setting = dvfsrc_mt6765_setting_0, .size = ARRAY_SIZE(dvfsrc_mt6765_setting_0), }, { .setting = dvfsrc_mt6765_setting_0, .size = ARRAY_SIZE(dvfsrc_mt6765_setting_0), } }; static const struct dvfsrc_opp dvfsrc_opp_mt6765_0[] = { {0, 0}, {1, 0}, {1, 0}, {2, 0}, {2, 1}, {2, 0}, {2, 1}, {2, 1}, {3, 1}, {3, 2}, {3, 1}, {3, 2}, {3, 1}, {3, 2}, {3, 2}, {3, 2}, }; static const struct dvfsrc_opp *dvfsrc_opps_mt6765[] = { [0] = dvfsrc_opp_mt6765_0, [1] = dvfsrc_opp_mt6765_0, [2] = dvfsrc_opp_mt6765_0, [3] = dvfsrc_opp_mt6765_0, [4] = dvfsrc_opp_mt6765_0, }; static const struct dvfsrc_soc_data mt6765_data = { .opps = dvfsrc_opps_mt6765, .num_opp = ARRAY_SIZE(dvfsrc_opp_mt6765_0), .init_setting = dvfsrc_setting_mt6765, .num_setting = ARRAY_SIZE(dvfsrc_setting_mt6765), .regs = mt6761_regs, .caps = DVFSRC_CAP_V_OPP_INIT | DVFSRC_CAP_V_CHECKER | DVFSRC_CAP_MTKQOS, .qos_data = &qos_data_dvfsrc, .get_target_level = mt6761_get_target_level, .get_current_level = mt6761_get_current_level, .get_vcore_level = mt6761_get_vcore_level, .get_vcp_level = mt6761_get_vcp_level, .wait_for_dram_level = dvfsrc_wait_for_dram_level, .wait_for_vcore_level = dvfsrc_wait_for_vcore_level, .wait_for_opp_level = dvfsrc_wait_for_opp_level, .set_dram_bw = mt6761_set_dram_bw, .set_dram_ext_bw = mt6761_set_dram_ext_bw, .set_dram_level = mt6761_set_dram_level, .set_vcore_level = mt6761_set_vcore_level, .set_opp_level = mt6761_set_opp_level, .set_vscp_level = mt6761_set_vscp_level, .set_ext_dram_level = mt6761_set_ext_dram_level, .set_force_opp_level = mt6761_set_force_opp_level, .is_dvfsrc_enabled = mt6761_dvfsrc_enabled, }; static int mtk_dvfsrc_remove(struct platform_device *pdev) { struct mtk_dvfsrc *dvfsrc = platform_get_drvdata(pdev); if (HAS_CAP(dvfsrc->dvd->caps, DVFSRC_CAP_CLK_INIT)) clk_disable_unprepare(dvfsrc->clk_dvfsrc); return 0; } static const struct of_device_id mtk_dvfsrc_of_match[] = { { .compatible = "mediatek,mt8183-dvfsrc", .data = &mt8183_data, }, { .compatible = "mediatek,mt6779-dvfsrc", .data = &mt6779_data, }, { .compatible = "mediatek,mt6761-dvfsrc", .data = &mt6761_data, }, { .compatible = "mediatek,mt6765-dvfsrc", .data = &mt6765_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), }, }; builtin_platform_driver(mtk_dvfsrc_driver); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("MTK DVFSRC driver");