diff --git a/plat/mediatek/common/lpm_v2/mt_lp_api.c b/plat/mediatek/common/lpm_v2/mt_lp_api.c new file mode 100644 index 000000000..e33f64c41 --- /dev/null +++ b/plat/mediatek/common/lpm_v2/mt_lp_api.c @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2025, MediaTek Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include + +#define UPDATE_STATUS(val, status, bit) \ + ((val) ? ((status) | (1 << (bit))) : ((status) & ~(1 << (bit)))) + +static uint64_t lp_status; + +int mt_audio_update(int type) +{ + int ret, val; + + switch (type) { + case AUDIO_AFE_ENTER: + case AUDIO_AFE_LEAVE: + val = (type == AUDIO_AFE_ENTER) ? 1 : 0; + lp_status = UPDATE_STATUS(val, lp_status, AUDIO_AFE); + ret = mt_lp_rm_do_update(-1, PLAT_RC_IS_FMAUDIO, &val); + break; + case AUDIO_DSP_ENTER: + case AUDIO_DSP_LEAVE: + val = (type == AUDIO_DSP_ENTER) ? 1 : 0; + lp_status = UPDATE_STATUS(val, lp_status, AUDIO_DSP); + ret = mt_lp_rm_do_update(-1, PLAT_RC_IS_ADSP, &val); + break; + default: + ret = -1; + break; + } + + return ret; +} + +int mt_usb_update(int type) +{ + int ret, val; + + switch (type) { + case LPM_USB_ENTER: + case LPM_USB_LEAVE: + val = (type == LPM_USB_ENTER) ? 1 : 0; + ret = mt_lp_rm_do_update(-1, PLAT_RC_IS_USB_INFRA, &val); + break; + case USB_HEADSET_ENTER: + case USB_HEADSET_LEAVE: + val = (type == USB_HEADSET_ENTER) ? 1 : 0; + lp_status = UPDATE_STATUS(val, lp_status, USB_HEADSET); + ret = mt_lp_rm_do_update(-1, PLAT_RC_IS_USB_HEADSET, &val); + break; + default: + ret = -1; + break; + } + + return ret; +} + +uint64_t mt_get_lp_scenario_status(void) +{ + return lp_status; +} + +int mt_gpueb_hwctrl(int type, void *priv) +{ + int ret, val; + + switch (type) { + case GPUEB_PLL_EN: + case GPUEB_PLL_DIS: + val = (type == GPUEB_PLL_EN) ? 1 : 0; + ret = mt_lp_rm_do_hwctrl(PLAT_AP_GPUEB_PLL_CONTROL, val, priv); + break; + case GPUEB_GET_PWR_STATUS: + ret = mt_lp_rm_do_hwctrl(PLAT_AP_GPUEB_PWR_STATUS, 0, priv); + break; + case GPUEB_GET_MFG0_PWR_CON: + ret = mt_lp_rm_do_hwctrl(PLAT_AP_GPUEB_MFG0_PWR_CON, 0, priv); + break; + default: + ret = -1; + break; + } + + return ret; +} diff --git a/plat/mediatek/common/lpm_v2/mt_lp_mmap.c b/plat/mediatek/common/lpm_v2/mt_lp_mmap.c new file mode 100644 index 000000000..11392f6fe --- /dev/null +++ b/plat/mediatek/common/lpm_v2/mt_lp_mmap.c @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2025, MediaTek Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include + +#include + +static const mmap_region_t lpm_sram_mmap[] MTK_MMAP_SECTION = { +#if (MTK_LPM_SRAM_BASE && MTK_LPM_SRAM_MAP_SIZE) + /* LPM used syssram */ + MAP_REGION_FLAT(MTK_LPM_SRAM_BASE, MTK_LPM_SRAM_MAP_SIZE, + MT_DEVICE | MT_RW | MT_SECURE), +#endif + {0} +}; +DECLARE_MTK_MMAP_REGIONS(lpm_sram_mmap); diff --git a/plat/mediatek/common/lpm_v2/mt_lp_rm.c b/plat/mediatek/common/lpm_v2/mt_lp_rm.c new file mode 100644 index 000000000..f9f44bc3b --- /dev/null +++ b/plat/mediatek/common/lpm_v2/mt_lp_rm.c @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2025, MediaTek Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include + +#include + +struct platform_mt_resource_manager { + unsigned int count; + struct mt_resource_manager *plat_rm; +}; + +static struct platform_mt_resource_manager plat_mt_rm; + +int mt_lp_rm_register(struct mt_resource_manager *rm) +{ + unsigned int i; + struct mt_resource_constraint *const *rc; + + if ((rm == NULL) || (rm->consts == NULL) || + (plat_mt_rm.plat_rm != NULL)) { + return MT_RM_STATUS_BAD; + } + + for (i = 0U, rc = rm->consts; *rc != NULL; i++, rc++) { + if ((*rc)->init != NULL) + (*rc)->init(); + } + + plat_mt_rm.plat_rm = rm; + plat_mt_rm.count = i; + + return MT_RM_STATUS_OK; +} + +int mt_lp_rm_reset_constraint(unsigned int idx, unsigned int cpuid, int stateid) +{ + struct mt_resource_constraint const *rc = NULL; + + if ((plat_mt_rm.plat_rm == NULL) || (idx >= plat_mt_rm.count)) + return MT_RM_STATUS_BAD; + + rc = plat_mt_rm.plat_rm->consts[idx]; + + if ((rc == NULL) || (rc->reset == NULL)) + return MT_RM_STATUS_BAD; + + return rc->reset(cpuid, stateid); +} + +int mt_lp_rm_get_status(unsigned int type, void *priv) +{ + int res = 0; + struct mt_resource_constraint *const *con; + struct mt_resource_manager *rm = plat_mt_rm.plat_rm; + + if ((rm == NULL) || (type >= PLAT_RC_MAX)) + return -1; + + for (con = rm->consts; *con != NULL; con++) { + if ((*con)->get_status == NULL) + continue; + + res = (*con)->get_status(type, priv); + if (res == MT_RM_STATUS_STOP) + break; + } + + return res; +} + +int mt_lp_rm_do_constraint(unsigned int constraint_id, unsigned int cpuid, int stateid) +{ + int res = MT_RM_STATUS_BAD; + struct mt_resource_constraint const *rc; + struct mt_resource_manager *rm = plat_mt_rm.plat_rm; + + if ((rm == NULL) || (constraint_id >= plat_mt_rm.count)) + return res; + + rc = rm->consts[constraint_id]; + if ((rc != NULL) && (rc->run != NULL)) + res = rc->run(cpuid, stateid); + + return res; +} + +int mt_lp_rm_find_constraint(unsigned int idx, unsigned int cpuid, + int stateid, void *priv) +{ + unsigned int i; + int res = MT_RM_STATUS_BAD; + struct mt_resource_constraint *const *rc; + struct mt_resource_manager *rm = plat_mt_rm.plat_rm; + + if ((rm == NULL) || (idx >= plat_mt_rm.count)) + return res; + + /* If subsys clk/mtcmos is on, add block-resource-off flag */ + if (rm->update != NULL) { + res = rm->update(rm->consts, plat_mt_rm.count, stateid, priv); + if (res != 0) + return MT_RM_STATUS_BAD; + } + + res = MT_RM_STATUS_BAD; + for (i = idx, rc = (rm->consts + idx); *rc != NULL; i++, rc++) { + if (((*rc)->is_valid != NULL) && + ((*rc)->is_valid(cpuid, stateid))) { + res = i; + break; + } + } + + return res; +} + +int mt_lp_rm_find_and_run_constraint(unsigned int idx, unsigned int cpuid, + int stateid, void *priv) +{ + int res = MT_RM_STATUS_BAD; + + res = mt_lp_rm_find_constraint(idx, cpuid, stateid, priv); + if (res != MT_RM_STATUS_BAD) + mt_lp_rm_do_constraint(res, cpuid, stateid); + + return res; +} + +int mt_lp_rm_do_update(int stateid, int type, void const *p) +{ + int res = MT_RM_STATUS_BAD; + struct mt_resource_constraint *const *rc; + struct mt_resource_manager *rm = plat_mt_rm.plat_rm; + + if (rm == NULL) + return res; + + for (rc = rm->consts; *rc != NULL; rc++) { + if ((*rc)->update != NULL) { + res = (*rc)->update(stateid, type, p); + if (res != MT_RM_STATUS_OK) + break; + } + } + + return res; +} + +int mt_lp_rm_do_hwctrl(unsigned int type, int set, void *priv) +{ + struct mt_resource_manager *rm = plat_mt_rm.plat_rm; + int res = 0; + + if (!rm || !rm->hwctrl || (type >= PLAT_AP_HW_CTRL_MAX)) + return -1; + + res = rm->hwctrl(type, set, priv); + + return res; +} diff --git a/plat/mediatek/common/lpm_v2/mt_lp_rq.c b/plat/mediatek/common/lpm_v2/mt_lp_rq.c new file mode 100644 index 000000000..0fbe88dd3 --- /dev/null +++ b/plat/mediatek/common/lpm_v2/mt_lp_rq.c @@ -0,0 +1,239 @@ +/* + * Copyright (c) 2025, MediaTek Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include + +#include +#include +#include + +#include +#include + +struct _mt_lp_res_req_m_ { + unsigned int uname[MT_LP_RQ_USER_MAX]; + unsigned int user_num; + unsigned int user_valid; + unsigned int resource_num; + unsigned int generic_resource_req; + unsigned int flag; + struct mt_resource_req_manager *plat_rqm; +}; + +static struct _mt_lp_res_req_m_ plat_mt_rqm; +static spinlock_t mt_lp_rq_lock; + +static int mt_lp_resource_update_and_set(struct mt_lp_resource_user *this); + +static int mt_lp_resource_request(struct mt_lp_resource_user *this, + unsigned int resource) +{ + int i; + struct mt_lp_res_req *const *rs; + int ret; + + if ((this == NULL) || (resource == 0) || (resource > MT_LP_RQ_ALL)) { + ERROR("invalid request(%x)\n", resource); + return MT_LP_RQ_STA_BAD; + } + + spin_lock(&mt_lp_rq_lock); + + rs = (plat_mt_rqm.plat_rqm)->res; + for (i = 0; i < plat_mt_rqm.resource_num; i++) { + if ((resource & rs[i]->res_id) != 0) + rs[i]->res_usage |= this->umask; + } + + plat_mt_rqm.flag = MT_LP_RQ_FLAG_NEED_UPDATE; + spin_unlock(&mt_lp_rq_lock); + + ret = mt_lp_resource_update_and_set(this); + + return ret; +} + +static int mt_lp_resource_release(struct mt_lp_resource_user *this) +{ + int i; + struct mt_lp_res_req *const *rs; + int ret; + + if (this == NULL) + return MT_LP_RQ_STA_BAD; + + spin_lock(&mt_lp_rq_lock); + + rs = (plat_mt_rqm.plat_rqm)->res; + for (i = 0; i < plat_mt_rqm.resource_num; i++) + rs[i]->res_usage &= ~(this->umask); + + plat_mt_rqm.flag = MT_LP_RQ_FLAG_NEED_UPDATE; + spin_unlock(&mt_lp_rq_lock); + + ret = mt_lp_resource_update_and_set(this); + + return ret; +} + +int mt_lp_resource_request_manager_register(struct mt_resource_req_manager *rm) +{ + unsigned int count; + struct mt_lp_res_req *const *rs; + + if (!rm || !(rm->res) || (plat_mt_rqm.plat_rqm != NULL)) + return MT_LP_RQ_STA_BAD; + + rs = rm->res; + count = 0; + while (*rs != NULL) { + count++; + rs++; + } + + plat_mt_rqm.plat_rqm = rm; + plat_mt_rqm.resource_num = count; + + return MT_LP_RQ_STA_OK; +} + + +int mt_lp_resource_user_register(char *user, struct mt_lp_resource_user *ru) +{ + int i, len; + unsigned int uname; + + if ((plat_mt_rqm.plat_rqm == NULL) || (user == NULL)) + goto invalid; + + len = strnlen(user, MT_LP_RQ_USER_NAME_LEN); + + uname = 0; + for (i = 0; i < len; i++) + uname |= (user[i] << (MT_LP_RQ_USER_CHAR_U * i)); + + spin_lock(&mt_lp_rq_lock); + + if (plat_mt_rqm.user_num >= MT_LP_RQ_USER_MAX) { + spin_unlock(&mt_lp_rq_lock); + goto invalid; + } + + i = plat_mt_rqm.user_num; + plat_mt_rqm.user_num += 1; + plat_mt_rqm.uname[i] = uname; + plat_mt_rqm.user_valid |= BIT(i); + spin_unlock(&mt_lp_rq_lock); + + ru->umask = BIT(i); + ru->uid = i; + ru->request = mt_lp_resource_request; + ru->release = mt_lp_resource_release; + INFO("%s register by %s, uid = %d\n", __func__, user, ru->uid); + + return MT_LP_RQ_STA_OK; + +invalid: + ru->uid = MT_LP_RQ_USER_INVALID; + ru->umask = 0; + ru->request = NULL; + ru->release = NULL; + return MT_LP_RQ_STA_BAD; +} + +int mt_lp_rq_get_status(int type, void *p) +{ + int i; + unsigned int update_sta = 0; + struct mt_lp_res_req *const *rs; + struct resource_req_status *rq_sta = (struct resource_req_status *)p; + + spin_lock(&mt_lp_rq_lock); + + if (plat_mt_rqm.flag) { + rs = (plat_mt_rqm.plat_rqm)->res; + for (i = 0; i < plat_mt_rqm.resource_num; i++) { + if ((rs[i]->res_usage & plat_mt_rqm.user_valid) != 0) + update_sta |= rs[i]->res_rq; + } + + plat_mt_rqm.generic_resource_req = update_sta; + plat_mt_rqm.flag = MT_LP_RQ_FLAG_DONE; + } + + switch (type) { + case PLAT_RQ_REQ_USAGE: + rs = (plat_mt_rqm.plat_rqm)->res; + rq_sta->val = plat_mt_rqm.generic_resource_req; + if (rq_sta->id < plat_mt_rqm.resource_num) + rq_sta->val = rs[rq_sta->id]->res_usage; + break; + case PLAT_RQ_USER_NUM: + rq_sta->val = plat_mt_rqm.user_num; + break; + case PLAT_RQ_USER_VALID: + rq_sta->val = plat_mt_rqm.user_valid; + break; + case PLAT_RQ_PER_USER_NAME: + rq_sta->val = (rq_sta->id < plat_mt_rqm.user_num) ? + plat_mt_rqm.uname[rq_sta->id] : 0; + break; + case PLAT_RQ_REQ_NUM: + rq_sta->val = plat_mt_rqm.resource_num; + break; + default: + break; + } + + spin_unlock(&mt_lp_rq_lock); + + return MT_LP_RQ_STA_OK; +} + +int mt_lp_rq_update_status(int type, void *p) +{ + unsigned int user; + struct resource_req_status *rq_sta = (struct resource_req_status *)p; + + spin_lock(&mt_lp_rq_lock); + + switch (type) { + case PLAT_RQ_USER_VALID: + if (rq_sta->id >= plat_mt_rqm.user_num) + break; + user = BIT(rq_sta->id); + plat_mt_rqm.user_valid = (rq_sta->val == 0) ? + (plat_mt_rqm.user_valid & ~(user)) : + (plat_mt_rqm.user_valid | user); + plat_mt_rqm.flag = MT_LP_RQ_FLAG_NEED_UPDATE; + break; + default: + break; + } + + spin_unlock(&mt_lp_rq_lock); + + return MT_LP_RQ_STA_OK; +} + +static int mt_lp_resource_update_and_set(struct mt_lp_resource_user *this) +{ + unsigned int ret = MT_LP_RQ_STA_OK; + struct resource_req_status generic_spm_resource_req = { + .id = MT_LP_RQ_ID_ALL_USAGE, + .val = 0, + }; + + mt_lp_rq_get_status(PLAT_RQ_REQ_USAGE, &generic_spm_resource_req); + ret = mt_lp_rm_do_hwctrl(PLAT_AP_SPM_RESOURCE_REQUEST_UPDATE, 1, + &generic_spm_resource_req.val); + + if (ret) + ret = MT_LP_RQ_STA_BAD; + + return ret; +} diff --git a/plat/mediatek/common/lpm_v2/mt_lpm_dispatch.c b/plat/mediatek/common/lpm_v2/mt_lpm_dispatch.c new file mode 100644 index 000000000..da049dff7 --- /dev/null +++ b/plat/mediatek/common/lpm_v2/mt_lpm_dispatch.c @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2025, MediaTek Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include + +#include + +/* + * Notice, this data don't link to bss section. + * It means data structure won't be set as zero. + * Please make sure the member will be initialized. + */ +struct mt_dispatch_ctrl mt_dispatcher +__section("mt_lpm_s") = { + .enable = 0, +}; + +struct mt_dispatch_ctrl mt_secure_dispatcher +__section("mt_secure_lpm_s") = { + .enable = 0, +}; + +u_register_t invoke_mt_lpm_dispatch(u_register_t x1, u_register_t x2, + u_register_t x3, u_register_t x4, + void *handle, struct smccc_res *smccc_ret) +{ + uint64_t res = 0; + uint32_t user; + + if (!IS_MT_LPM_SMC(x1)) + return res; + + user = MT_LPM_SMC_USER(x1); + if ((user < MT_LPM_SMC_USER_MAX) && + (mt_dispatcher.enable & (1 << user))) { + res = mt_dispatcher.fn[user](MT_LPM_SMC_USER_ID(x1), + x2, x3, x4, + handle, smccc_ret); + } + + return res; +} +DECLARE_SMC_HANDLER(MTK_SIP_MTK_LPM_CONTROL, invoke_mt_lpm_dispatch); + +u_register_t invoke_mt_secure_lpm_dispatch(u_register_t x1, u_register_t x2, + u_register_t x3, u_register_t x4, + void *handle, + struct smccc_res *smccc_ret) +{ + uint64_t res = 0; + unsigned int user; + + if (!IS_MT_LPM_SMC(x1)) + return res; + + user = MT_LPM_SMC_USER(x1); + if (mt_secure_dispatcher.enable & (1 << user)) { + res = mt_secure_dispatcher.fn[user](MT_LPM_SMC_USER_ID(x1), x2, + x3, x4, handle, smccc_ret); + } + + return res; +} +DECLARE_SMC_HANDLER(MTK_SIP_BL_LPM_CONTROL, invoke_mt_secure_lpm_dispatch); + +/* Check lpm smc user number at compile time */ +CASSERT(MT_LPM_SMC_USER_MAX <= MTK_DISPATCH_ID_MAX, + lpm_smc_user_declare_too_large); + +void mt_lpm_dispatcher_registry(unsigned int id, mt_lpm_dispatch_fn fn) +{ + if (id >= MT_LPM_SMC_USER_MAX) + return; + + mt_dispatcher.enable |= BIT(id); + mt_dispatcher.fn[id] = fn; +} + +void mt_secure_lpm_dispatcher_registry(unsigned int id, mt_lpm_dispatch_fn fn) +{ + if (id >= MT_LPM_SMC_USER_MAX) + return; + + mt_secure_dispatcher.enable |= BIT(id); + mt_secure_dispatcher.fn[id] = fn; +} diff --git a/plat/mediatek/common/lpm_v2/rules.mk b/plat/mediatek/common/lpm_v2/rules.mk new file mode 100644 index 000000000..94d44fa6f --- /dev/null +++ b/plat/mediatek/common/lpm_v2/rules.mk @@ -0,0 +1,19 @@ +# +# Copyright (c) 2025, MediaTek Inc. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +LOCAL_DIR := $(call GET_LOCAL_DIR) + +MODULE := lpm_v2 + +LOCAL_SRCS-y += ${LOCAL_DIR}/mt_lp_api.c +LOCAL_SRCS-y += ${LOCAL_DIR}/mt_lp_rm.c +LOCAL_SRCS-y += ${LOCAL_DIR}/mt_lp_rq.c +LOCAL_SRCS-y += ${LOCAL_DIR}/mt_lp_mmap.c +LOCAL_SRCS-y += ${LOCAL_DIR}/mt_lpm_dispatch.c + +PLAT_INCLUDES += -I${LOCAL_DIR} + +$(eval $(call MAKE_MODULE,$(MODULE),$(LOCAL_SRCS-y),$(MTK_BL))) diff --git a/plat/mediatek/include/lpm_v2/mt_lp_api.h b/plat/mediatek/include/lpm_v2/mt_lp_api.h new file mode 100644 index 000000000..1e4afd3bb --- /dev/null +++ b/plat/mediatek/include/lpm_v2/mt_lp_api.h @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2025, MediaTek Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef MT_LP_API_H +#define MT_LP_API_H + +#include + +#if MTK_PUBEVENT_ENABLE +#include +#endif + +/* UFS clk enum for PLAT_RC_CLKBUF_STATUS */ +enum rc_update_ex_ufs_ref_clk { + UFS_REF_CLK_OFF = 0, + UFS_REF_CLK_ON, +}; + +/* Enum for flight mode */ +enum rc_update_ex_flight_mode { + FLIGHT_MODE_OFF = 0, + FLIGHT_MODE_ON, +}; + +struct mt_lpm_pubevent_data { + unsigned int u32; +}; + +enum mt_lpm_pubevents_id { + MT_LPM_PUBEVENTS_BBLPM_ENTER, + MT_LPM_PUBEVENTS_BBLPM_LEAVE, + MT_LPM_PUBEVENTS_TARGET_CORE, + MT_LPM_PUBEVENTS_SYS_POWER_OFF, + MT_LPM_PUBEVENTS_SYS_POWER_ON, +}; + +struct mt_lp_publish_event { + unsigned int id; + struct mt_lpm_pubevent_data val; + unsigned int level; +}; + +#if MTK_PUBEVENT_ENABLE +#define MT_LP_PUBLISH_EVENT(x) ({ \ + PUBLISH_EVENT_ARG(lpm_publish_event, (const void *)(x)); }) +#define MT_LP_SUSPEND_PUBLISH_EVENT(x) ({ \ + PUBLISH_EVENT_ARG(suspend_publish_event, (const void *)(x)); }) + +#define MT_LP_SUBSCRIBE_SUSPEND(func) \ + SUBSCRIBE_TO_EVENT(suspend_publish_event, func) +#define MT_LP_SUBSCRIBE_LPM(func) \ + SUBSCRIBE_TO_EVENT(lpm_publish_event, func) +#else +#define MT_LP_PUBLISH_EVENT(x) ({ (void)x; }) +#define MT_LP_SUSPEND_PUBLISH_EVENT(x) ({ (void)x; }) +#define MT_LP_SUBSCRIBE_SUSPEND(func) +#define MT_LP_SUBSCRIBE_LPM(func) +#endif + +/* MTK low power API types for audio */ +enum mt_lp_api_audio_type { + AUDIO_AFE_ENTER, + AUDIO_AFE_LEAVE, + AUDIO_DSP_ENTER, + AUDIO_DSP_LEAVE, +}; + +/* MTK low power API types for usb */ +enum mt_lp_api_usb_type { + LPM_USB_ENTER, + LPM_USB_LEAVE, + USB_HEADSET_ENTER, + USB_HEADSET_LEAVE, +}; + +int mt_audio_update(int type); +int mt_usb_update(int type); + +/* MTK Low Power Scenario Types for logging */ +enum mtk_lp_scenario_status { + AUDIO_AFE, + AUDIO_DSP, + USB_HEADSET, + MTK_LP_SCENE_NUM, +}; + +/* MTK Low Power API Types for CCCI */ +enum mt_lp_api_ccci_type { + CCCI_AP_MDSRC_REQUEST, + CCCI_AP_MDSRC_RELEASE, + CCCI_AP_MDSRC_ACK, + CCCI_AP_MDSRC_GET_SETTLE, + CCCI_AP_IS_MD_SLEEP, +}; + +/* System power level */ +#define MT_LP_SYSPOWER_LEVEL_APMCU BIT(0) +#define MT_LP_SYSPOWER_LEVEL_DRAM BIT(1) +#define MT_LP_SYSPOWER_LEVEL_SYSPLL BIT(2) +#define MT_LP_SYSPOWER_LEVEL_PMIC_LP BIT(3) +#define MT_LP_SYSPOWER_LEVEL_BUS26M BIT(4) +#define MT_LP_SYSPOWER_LEVEL_VCORE0V BIT(5) +#define MT_LP_SYSPOWER_LEVEL_SUSPEND BIT(6) + + +enum mt_lpm_pubevent_wake_src { + MT_LPM_WAKE_MD_WAKEUP_CCIF0 = 1, + MT_LPM_WAKE_MD_WAKEUP_CCIF1, + MT_LPM_WAKE_MD_WAKEUP_CLDMA, + MT_LPM_WAKE_MD_WAKEUP_DPMAIF, + MT_LPM_WAKE_MD_WDT, +}; + +/* MTK Low Power API Types for GPUEB */ +enum mt_lp_api_gpueb_type { + GPUEB_PLL_EN, + GPUEB_PLL_DIS, + GPUEB_GET_PWR_STATUS, + GPUEB_GET_MFG0_PWR_CON, +}; + +int mt_ccci_hwctrl(int type, void *priv); +int mt_gpueb_hwctrl(int type, void *priv); +uint64_t mt_get_lp_scenario_status(void); + +#endif /* MT_LP_API_H */ diff --git a/plat/mediatek/include/lpm_v2/mt_lp_rm.h b/plat/mediatek/include/lpm_v2/mt_lp_rm.h new file mode 100644 index 000000000..a579e701d --- /dev/null +++ b/plat/mediatek/include/lpm_v2/mt_lp_rm.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2025, MediaTek Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef MT_LP_RM_H +#define MT_LP_RM_H + +#include + +#define MT_RM_STATUS_OK 0 +#define MT_RM_STATUS_BAD -1 +#define MT_RM_STATUS_STOP -2 + +enum plat_mt_lpm_rc_type { + PLAT_RC_UPDATE_CONDITION, + PLAT_RC_STATUS, + PLAT_RC_UPDATE_REMAIN_IRQS, + PLAT_RC_IS_FMAUDIO, + PLAT_RC_IS_ADSP, + PLAT_RC_ENTER_CNT, + PLAT_RC_CLKBUF_STATUS, + PLAT_RC_UFS_STATUS, + PLAT_RC_IS_USB_PERI, + PLAT_RC_IS_USB_INFRA, + PLAT_RC_IS_USB_HEADSET, + PLAT_RC_MAX, +}; + +enum plat_mt_lpm_hw_ctrl_type { + PLAT_AP_MDSRC_REQ, + PLAT_AP_MDSRC_ACK, + PLAT_AP_IS_MD_SLEEP, + PLAT_AP_MDSRC_SETTLE, + PLAT_AP_GPUEB_PLL_CONTROL, + PLAT_AP_GPUEB_PWR_STATUS, + PLAT_AP_GPUEB_MFG0_PWR_CON, + PLAT_AP_ASSERT_SPM_IRQ, + PLAT_AP_SPM_RESOURCE_REQUEST_UPDATE, + PLAT_AP_SPM_WDT_TRIGGER, + PLAT_AP_HW_CTRL_MAX, +}; + +struct mt_resource_constraint { + int level; + int (*init)(void); + bool (*is_valid)(unsigned int cpu, int stateid); + int (*update)(int stateid, int type, const void *p); + int (*run)(unsigned int cpu, int stateid); + int (*reset)(unsigned int cpu, int stateid); + int (*get_status)(unsigned int type, void *priv); + unsigned int (*allow)(int stateid); +}; + +struct mt_resource_manager { + int (*update)(struct mt_resource_constraint **con, unsigned int num, + int stateid, void *priv); + struct mt_resource_constraint **consts; + int (*hwctrl)(unsigned int type, int set, void *priv); +}; + +extern int mt_lp_rm_register(struct mt_resource_manager *rm); +extern int mt_lp_rm_do_constraint(unsigned int constraint_id, + unsigned int cpuid, int stateid); +extern int mt_lp_rm_find_constraint(unsigned int idx, unsigned int cpuid, + int stateid, void *priv); +extern int mt_lp_rm_find_and_run_constraint(unsigned int idx, + unsigned int cpuid, + int stateid, void *priv); +extern int mt_lp_rm_reset_constraint(unsigned int idx, + unsigned int cpuid, int stateid); +extern int mt_lp_rm_do_update(int stateid, int type, void const *p); +extern int mt_lp_rm_get_status(unsigned int type, void *priv); +extern int mt_lp_rm_do_hwctrl(unsigned int type, int set, void *priv); + +#endif /* MT_LP_RM_H */ diff --git a/plat/mediatek/include/lpm_v2/mt_lp_rq.h b/plat/mediatek/include/lpm_v2/mt_lp_rq.h new file mode 100644 index 000000000..002a2ba6a --- /dev/null +++ b/plat/mediatek/include/lpm_v2/mt_lp_rq.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2025, MediaTek Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef MT_LP_RQ_H +#define MT_LP_RQ_H + +/* Determine the generic resource request public type */ +#define MT_LP_RQ_XO_FPM BIT(0) +#define MT_LP_RQ_26M BIT(1) +#define MT_LP_RQ_INFRA BIT(2) +#define MT_LP_RQ_SYSPLL BIT(3) +#define MT_LP_RQ_DRAM BIT(4) +#define MT_LP_RQ_VCORE BIT(6) +#define MT_LP_RQ_EMI BIT(7) +#define MT_LP_RQ_PMIC BIT(8) +#define MT_LP_RQ_ALL 0xFFFFFFFF + +struct mt_lp_resource_user { + /* Determine the resource user mask */ + unsigned int umask; + /* Determine the resource request user identify */ + unsigned int uid; + /* Request the resource */ + int (*request)(struct mt_lp_resource_user *this, unsigned int res); + /* Release the resource */ + int (*release)(struct mt_lp_resource_user *this); +}; + +int mt_lp_resource_user_register(char *uname, struct mt_lp_resource_user *ru); + +#endif /* MT_LP_RQ_H */ diff --git a/plat/mediatek/include/lpm_v2/mt_lp_rqm.h b/plat/mediatek/include/lpm_v2/mt_lp_rqm.h new file mode 100644 index 000000000..1f00b1637 --- /dev/null +++ b/plat/mediatek/include/lpm_v2/mt_lp_rqm.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2025, MediaTek Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef MT_LP_RQM_H +#define MT_LP_RQM_H + +#include + +enum plat_mt_lpm_rq_update_type { + PLAT_RQ_USER_NUM, + PLAT_RQ_USER_VALID, + PLAT_RQ_USER_REQ, + PLAT_RQ_USER_REL, + PLAT_RQ_PER_USER_NAME, + PLAT_RQ_REQ_NUM, + PLAT_RQ_REQ_USAGE, +}; + +/* Determine the request valid */ +#define MT_LP_RQ_VALID 0x1 +#define MT_LP_RQ_INVALID 0x0 + +/* Determine the request user opertions */ +#define MT_LP_RQ_USER_INVALID -1 +#define MT_LP_RQ_USER_MAX 32 +#define MT_LP_RQ_USER_NAME_LEN 4 +#define MT_LP_RQ_USER_CHAR_U 8 + +/* Determine the request update flag */ +#define MT_LP_RQ_FLAG_DONE 0 +#define MT_LP_RQ_FLAG_NEED_UPDATE BIT(6) + +/* Determine the resource update id */ +#define MT_LP_RQ_ID_ALL_USAGE -1 + +/* Determine the return status */ +#define MT_LP_RQ_STA_OK 0 +#define MT_LP_RQ_STA_BAD -1 + +struct mt_lp_res_req { + /* Determine the resource req public identify */ + const unsigned int res_id; + /* Determine the resource bitwise internal control */ + const unsigned int res_rq; + /* Determine the users per bit for current resource usage */ + unsigned int res_usage; +}; + +struct mt_resource_req_manager { + /* Determine the set of resources */ + struct mt_lp_res_req **res; +}; + +struct resource_req_status { + /* Determine the status id */ + unsigned int id; + /* Determine the status value */ + unsigned int val; +}; + +int mt_lp_resource_request_manager_register(struct mt_resource_req_manager *rm); +int mt_lp_rq_update_status(int type, void *p); +int mt_lp_rq_get_status(int type, void *p); + +#endif /* MT_LP_RQM_H */ diff --git a/plat/mediatek/include/lpm_v2/mt_lpm_dispatch.h b/plat/mediatek/include/lpm_v2/mt_lpm_dispatch.h new file mode 100644 index 000000000..e941eda66 --- /dev/null +++ b/plat/mediatek/include/lpm_v2/mt_lpm_dispatch.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2025, Mediatek Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef MT_LPM_DISPATCH_H +#define MT_LPM_DISPATCH_H + +#include + +#include +#include + +#define MTK_DISPATCH_ID_MAX 32 + +typedef uint64_t (*mt_lpm_dispatch_fn)(u_register_t x1, u_register_t x2, + u_register_t x3, u_register_t x4, + void *handle, + struct smccc_res *smccc_ret); + +struct mt_dispatch_ctrl { + unsigned int enable; + mt_lpm_dispatch_fn fn[MT_LPM_SMC_USER_MAX]; +}; + +void mt_lpm_dispatcher_registry(unsigned int id, mt_lpm_dispatch_fn fn); + +void mt_secure_lpm_dispatcher_registry(unsigned int id, mt_lpm_dispatch_fn fn); + +extern struct mt_dispatch_ctrl mt_dispatcher; +extern struct mt_dispatch_ctrl mt_secure_dispatcher; + +#endif /* MT_LPM_DISPATCH_H */ + diff --git a/plat/mediatek/include/lpm_v2/mt_lpm_smc.h b/plat/mediatek/include/lpm_v2/mt_lpm_smc.h new file mode 100644 index 000000000..5d4fd80fd --- /dev/null +++ b/plat/mediatek/include/lpm_v2/mt_lpm_smc.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2025, MediaTek Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef MT_LPM_SMC_H +#define MT_LPM_SMC_H + +/* + * MTK LPM smc user format: + * bit[31:24]: magic number + * bit[23:16]: user number + * bit[15:00]: user id + */ + +#define MT_LPM_SMC_MAGIC 0xDA000000 +#define MT_LPM_SMC_MAGIC_MASK 0xFF000000 +#define MT_LPM_SMC_USER_MASK 0xFF +#define MT_LPM_SMC_USER_SHIFT 16 + +#define MT_LPM_SMC_USER_ID_MASK 0x0000FFFF + +enum mt_lpm_smc_user_id { + MT_LPM_SMC_USER_CPU_PM = 0, + MT_LPM_SMC_USER_SPM_DBG, + MT_LPM_SMC_USER_SPM, + MT_LPM_SMC_USER_CPU_PM_LP, + MT_LPM_SMC_USER_SECURE_CPU_PM, + MT_LPM_SMC_USER_SECURE_SPM_DBG, + MT_LPM_SMC_USER_SECURE_SPM, + MT_LPM_SMC_USER_MAX, +}; + +#define IS_MT_LPM_SMC(smcid) \ + (((smcid) & MT_LPM_SMC_MAGIC_MASK) == MT_LPM_SMC_MAGIC) +#define MT_LPM_SMC_USER(id) \ + (((id) >> MT_LPM_SMC_USER_SHIFT) & MT_LPM_SMC_USER_MASK) +#define MT_LPM_SMC_USER_ID(uid) (uid & MT_LPM_SMC_USER_ID_MASK) +#define MT_LPM_SMC_USER_SINK(user, uid) \ + ((((uid) & MT_LPM_SMC_USER_ID_MASK) | \ + (((user) & MT_LPM_SMC_USER_MASK) << MT_LPM_SMC_USER_SHIFT)) | \ + MT_LPM_SMC_MAGIC) +#define MT_LPM_SMC_USER_ID_CPU_PM(uid) \ + MT_LPM_SMC_USER_SINK(MT_LPM_SMC_USER_CPU_PM, uid) +#define MT_LPM_SMC_USER_ID_SPM(uid) \ + MT_LPM_SMC_USER_SINK(MT_LPM_SMC_USER_SPM, uid) +#define MT_LPM_SMC_USER_CPU_PM(uid) MT_LPM_SMC_USER_ID_CPU_PM(uid) + +#define MT_LPM_SMC_USER_SPM(uid) MT_LPM_SMC_USER_ID_SPM(uid) + +/* Behavior */ +#define MT_LPM_SMC_ACT_SET BIT(0) +#define MT_LPM_SMC_ACT_CLR BIT(1) +#define MT_LPM_SMC_ACT_GET BIT(2) +#define MT_LPM_SMC_ACT_PUSH BIT(3) +#define MT_LPM_SMC_ACT_POP BIT(4) +#define MT_LPM_SMC_ACT_SUBMIT BIT(5) + +/* Compatible action for legacy smc from lk */ +#define MT_LPM_SMC_ACT_COMPAT BIT(31) + +enum mt_lpm_spmc_compat_id { + MT_LPM_SPMC_COMPAT_LK_FW_INIT, + MT_LPM_SPMC_COMPAT_LK_MCDI_WDT_DUMP, +}; + +#endif /* MT_LPM_SMC_H */ diff --git a/plat/mediatek/mt8196/platform.mk b/plat/mediatek/mt8196/platform.mk index f9268b172..36b144551 100644 --- a/plat/mediatek/mt8196/platform.mk +++ b/plat/mediatek/mt8196/platform.mk @@ -24,7 +24,7 @@ PLAT_INCLUDES := -I${MTK_PLAT}/common \ -Idrivers/arm/gic \ MODULES-y += $(MTK_PLAT)/common -MODULES-y += $(MTK_PLAT)/common/lpm +MODULES-y += $(MTK_PLAT)/common/lpm_v2 MODULES-y += $(MTK_PLAT)/lib/mtk_init MODULES-y += $(MTK_PLAT)/lib/pm MODULES-y += $(MTK_PLAT)/drivers/apusys