/* * Copyright (c) 2025, Mediatek Inc. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef MT_SPM_USING_BAKERY_LOCK DEFINE_BAKERY_LOCK(spm_lock); #define plat_spm_lock_init() bakery_lock_init(&spm_lock) #else spinlock_t spm_lock; #define plat_spm_lock_init() #endif static uint32_t spm_irq_num; void spm_set_sysclk_settle(void) { uint32_t settle; mmio_write_32(SPM_CLK_SETTLE, SPM_SYSCLK_SETTLE); settle = mmio_read_32(SPM_CLK_SETTLE); INFO("md_settle = %u, settle = %u\n", SPM_SYSCLK_SETTLE, settle); } void spm_set_irq_num(uint32_t num) { spm_irq_num = num; } void spm_irq0_handler(uint64_t x1, uint64_t x2) { if (x2 == 0) { mmio_setbits_32(SPM_IRQ_MASK, ISRM_ALL_EXC_TWAM); mmio_write_32(SPM_IRQ_STA, x1); mmio_write_32(SPM_SWINT_CLR, PCM_SW_INT0); } } static int spm_ap_mdsrc_ack(void) { int ack, md_state = 0; /* Check ap_mdsrc_ack = 1'b1, for md internal resource on ack */ ack = !!(mmio_read_32(AP_MDSRC_REQ) & AP_MDSMSRC_ACK_LSB); if (!ack) { /* Check md_apsrc_req = 1'b0, for md state 0:sleep, 1:wakeup */ md_state = !!(mmio_read_32(SPM_REQ_STA_9) & MD_APSRC_REQ_LSB); ERROR("[SPM] error: md_sleep = %d\n", md_state); ERROR("%s can not get AP_MDSRC_ACK\n", __func__); return -1; } return 0; } static void spm_ap_mdsrc_req(int set) { spm_lock_get(); if (set) mmio_setbits_32(AP_MDSRC_REQ, AP_MDSMSRC_REQ_LSB); else mmio_clrbits_32(AP_MDSRC_REQ, AP_MDSMSRC_REQ_LSB); spm_lock_release(); } static int spm_is_md_sleep(void *priv) { int md_state = 0; int *sleep = (int *)priv; if (!priv) return -1; /* Check md_apsrc_req = 1'b0, for md state 0:sleep, 1:wakeup */ md_state = !!(mmio_read_32(SPM_REQ_STA_9) & MD_APSRC_REQ_LSB); if (md_state == 0) *sleep = 1; else *sleep = 0; return 0; } static void spm_ap_gpueb_pll_control(int set) { spm_lock_get(); if (set) mmio_setbits_32(SPM2GPUPM_CON, SC_MFG_PLL_EN_LSB); else mmio_clrbits_32(SPM2GPUPM_CON, SC_MFG_PLL_EN_LSB); spm_lock_release(); } static uint32_t spm_ap_gpueb_get_pwr_status(void) { uint32_t ret; ret = mmio_read_32(XPU_PWR_STATUS); return ret; } static uint32_t spm_ap_gpueb_get_mfg0_pwr_con(void) { uint32_t ret; ret = mmio_read_32(MFG0_PWR_CON); return ret; } #ifndef MTK_PLAT_SPM_UNSUPPORT struct mt_lp_res_req rq_xo_fpm = { .res_id = MT_LP_RQ_XO_FPM, .res_rq = MT_SPM_XO_FPM, .res_usage = 0, }; struct mt_lp_res_req rq_26m = { .res_id = MT_LP_RQ_26M, .res_rq = MT_SPM_26M, .res_usage = 0, }; struct mt_lp_res_req rq_infra = { .res_id = MT_LP_RQ_INFRA, .res_rq = MT_SPM_INFRA, .res_usage = 0, }; struct mt_lp_res_req rq_syspll = { .res_id = MT_LP_RQ_SYSPLL, .res_rq = MT_SPM_SYSPLL, .res_usage = 0, }; struct mt_lp_res_req rq_dram_s0 = { .res_id = MT_LP_RQ_DRAM, .res_rq = MT_SPM_DRAM_S0, .res_usage = 0, }; struct mt_lp_res_req rq_dram_s1 = { .res_id = MT_LP_RQ_DRAM, .res_rq = MT_SPM_DRAM_S1, .res_usage = 0, }; struct mt_lp_res_req rq_vcore = { .res_id = MT_LP_RQ_VCORE, .res_rq = MT_SPM_VCORE, .res_usage = 0, }; struct mt_lp_res_req rq_emi = { .res_id = MT_LP_RQ_EMI, .res_rq = MT_SPM_EMI, .res_usage = 0, }; struct mt_lp_res_req rq_pmic = { .res_id = MT_LP_RQ_PMIC, .res_rq = MT_SPM_PMIC, .res_usage = 0, }; struct mt_lp_res_req *spm_resources[] = { &rq_xo_fpm, &rq_26m, &rq_infra, &rq_syspll, &rq_dram_s0, &rq_dram_s1, &rq_vcore, &rq_emi, &rq_pmic, NULL, }; struct mt_resource_req_manager plat_mt8189_rq = { .res = spm_resources, }; struct mt_resource_constraint plat_constraint_vcore = { .is_valid = spm_is_valid_rc_vcore, .update = spm_update_rc_vcore, .allow = spm_allow_rc_vcore, .run = spm_run_rc_vcore, .reset = spm_reset_rc_vcore, .get_status = spm_get_status_rc_vcore, }; struct mt_resource_constraint plat_constraint_bus26m = { .is_valid = spm_is_valid_rc_bus26m, .update = spm_update_rc_bus26m, .allow = spm_allow_rc_bus26m, .run = spm_run_rc_bus26m, .reset = spm_reset_rc_bus26m, .get_status = spm_get_status_rc_bus26m, }; struct mt_resource_constraint plat_constraint_syspll = { .is_valid = spm_is_valid_rc_syspll, .update = spm_update_rc_syspll, .allow = spm_allow_rc_syspll, .run = spm_run_rc_syspll, .reset = spm_reset_rc_syspll, .get_status = spm_get_status_rc_syspll, }; struct mt_resource_constraint *plat_constraints[] = { &plat_constraint_vcore, &plat_constraint_bus26m, &plat_constraint_syspll, NULL, }; #endif int mt_spm_hwctrl(uint32_t type, int set, void *priv) { int ret = 0; switch (type) { case PLAT_AP_MDSRC_REQ: spm_ap_mdsrc_req(set); break; case PLAT_AP_MDSRC_ACK: ret = spm_ap_mdsrc_ack(); break; case PLAT_AP_IS_MD_SLEEP: ret = spm_is_md_sleep(priv); break; case PLAT_AP_MDSRC_SETTLE: if (!priv) return -1; *(int *)priv = AP_MDSRC_REQ_MD_26M_SETTLE; break; case PLAT_AP_GPUEB_PLL_CONTROL: spm_ap_gpueb_pll_control(set); break; case PLAT_AP_GPUEB_PWR_STATUS: if (!priv) return -1; *(uint32_t *)priv = spm_ap_gpueb_get_pwr_status(); break; case PLAT_AP_GPUEB_MFG0_PWR_CON: if (!priv) return -1; *(uint32_t *)priv = spm_ap_gpueb_get_mfg0_pwr_con(); break; case PLAT_AP_ASSERT_SPM_IRQ: mt_irq_set_pending(spm_irq_num); break; case PLAT_AP_SPM_RESOURCE_REQUEST_UPDATE: struct spm_lp_scen *spmlp; #if defined(MT_SPM_FEATURE_SUPPORT) mt_spm_idle_generic_get_spm_lp(&spmlp); #endif if (!spmlp) return -1; __spm_set_power_control(spmlp->pwrctrl, *(uint32_t *)priv); ret = __spm_wait_spm_request_ack(*(uint32_t *)priv, SPM_ACK_TIMEOUT_US); break; default: /* not supported type */ ret = -1; break; } return ret; } #ifndef MTK_PLAT_SPM_UNSUPPORT struct mt_resource_manager plat_mt8189_rm = { .update = NULL, .hwctrl = mt_spm_hwctrl, .consts = plat_constraints, }; #else struct mt_resource_manager plat_mt8189_rm = { .hwctrl = mt_spm_hwctrl, }; #endif /* Determine for spm sw resource user */ static struct mt_lp_resource_user spm_res_user; struct mt_lp_resource_user *get_spm_res_user(void) { return &spm_res_user; } int spm_boot_init(void) { plat_spm_lock_init(); #if defined(MT_SPM_FEATURE_SUPPORT) plat_spm_pmic_wrap_init(); #endif plat_spm_cond_init(); mt_lp_rm_register(&plat_mt8189_rm); /* If spm service won't run when spm not ready */ #ifndef MTK_PLAT_SPM_UNSUPPORT mt_lp_resource_request_manager_register(&plat_mt8189_rq); mt_lp_resource_user_register("SPM", &spm_res_user); mt_spm_dispatcher_init(); #endif #if defined(MT_SPM_FEATURE_SUPPORT) spm_hwreq_init(); #endif INFO("[%s:%d] - spm finished\n", __func__, __LINE__); return 0; } MTK_ARCH_INIT(spm_boot_init);