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