arm-trusted-firmware/plat/mediatek/drivers/spm/mt8196/mt_spm.c
Wenzhen Yu a24b53e0e5 feat(mt8196): add SPM common driver support
This patch mainly initializes the SPM and provides common APIs for SPM
to enable the use of its various features.

Signed-off-by: Wenzhen Yu <wenzhen.yu@mediatek.com>
Change-Id: I9facb6bf9962bb2d5fcacd945846bfaeb4c87a55
2025-01-22 15:28:08 +08:00

464 lines
9.6 KiB
C

/*
* Copyright (c) 2025, Mediatek Inc. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <assert.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <arch.h>
#include <common/debug.h>
#include <drivers/console.h>
#include <drivers/delay_timer.h>
#include <drivers/gpio.h>
#include <lib/bakery_lock.h>
#include <lib/mmio.h>
#include <lib/utils_def.h>
#include <platform_def.h>
#include <constraints/mt_spm_rc_api.h>
#include <constraints/mt_spm_rc_internal.h>
#include <drivers/spm/mt_spm_resource_req.h>
#include <lib/mtk_init/mtk_init.h>
#include <lib/pm/mtk_pm.h>
#include <lpm_v2/mt_lp_rm.h>
#include <lpm_v2/mt_lp_rqm.h>
#include <lpm_v2/mt_lpm_smc.h>
#include <mt_plat_spm_setting.h>
#include <mt_spm.h>
#include <mt_spm_common.h>
#include <mt_spm_conservation.h>
#include <mt_spm_constraint.h>
#include <mt_spm_dispatcher.h>
#include <mt_spm_hwreq.h>
#include <mt_spm_idle.h>
#include <mt_spm_internal.h>
#include <mt_spm_reg.h>
#include <mt_spm_suspend.h>
#include <mtk_mmap_pool.h>
#include <sleep_def.h>
#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
uint32_t mt_spm_version;
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_10)
& 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_10)
& 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_mt8196_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;
if (type == PLAT_AP_MDSRC_REQ) {
spm_ap_mdsrc_req(set);
} else if (type == PLAT_AP_MDSRC_ACK) {
ret = spm_ap_mdsrc_ack();
} else if (type == PLAT_AP_IS_MD_SLEEP) {
ret = spm_is_md_sleep(priv);
} else if (type == PLAT_AP_MDSRC_SETTLE) {
if (!priv)
return -1;
*(int *)priv = AP_MDSRC_REQ_MD_26M_SETTLE;
} else if (type == PLAT_AP_GPUEB_PLL_CONTROL) {
spm_ap_gpueb_pll_control(set);
} else if (type == PLAT_AP_GPUEB_PWR_STATUS) {
if (!priv)
return -1;
*(uint32_t *)priv = spm_ap_gpueb_get_pwr_status();
} else if (type == PLAT_AP_GPUEB_MFG0_PWR_CON) {
if (!priv)
return -1;
*(uint32_t *)priv = spm_ap_gpueb_get_mfg0_pwr_con();
} else if (type == PLAT_AP_SPM_RESOURCE_REQUEST_UPDATE) {
struct spm_lp_scen *spmlp = NULL;
#ifdef MT_SPM_COMMON_SODI_SUPPORT
mt_spm_common_sodi_get_spm_lp(&spmlp);
#else
#if defined(MT_SPM_FEATURE_SUPPORT)
mt_spm_idle_generic_get_spm_lp(&spmlp);
#endif
#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);
} else if (type == PLAT_AP_SPM_WDT_TRIGGER) {
mmio_write_32(PCM_WDT_VAL, 0x1);
mmio_setbits_32(PCM_CON1, SPM_REGWR_CFG_KEY |
REG_PCM_WDT_EN_LSB | REG_PCM_WDT_WAKE_LSB);
} else {
/* Not supported type */
return -1;
}
return ret;
}
#ifndef MTK_PLAT_SPM_UNSUPPORT
struct mt_resource_manager plat_mt8196_rm = {
.update = NULL,
.hwctrl = mt_spm_hwctrl,
.consts = plat_constraints,
};
#else
struct mt_resource_manager plat_mt8196_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;
}
#ifdef MT_SPM_COMMON_SODI_SUPPORT
int mt_spm_common_sodi_get_spm_pcm_flag(uint32_t *lp, uint32_t idx)
{
struct spm_lp_scen *spmlp;
struct pwr_ctrl *pwrctrl;
mt_spm_common_sodi_get_spm_lp(&spmlp);
pwrctrl = spmlp->pwrctrl;
if (!lp || idx > 1)
return -1;
switch (idx) {
case 0:
*lp = pwrctrl->pcm_flags;
break;
case 1:
*lp = pwrctrl->pcm_flags;
break;
default:
return -1;
}
return 0;
}
void mt_spm_common_sodi_en(bool en)
{
struct spm_lp_scen *spmlp;
struct pwr_ctrl *pwrctrl;
mt_spm_common_sodi_get_spm_lp(&spmlp);
pwrctrl = spmlp->pwrctrl;
#if defined(CONFIG_MTK_VCOREDVFS_SUPPORT)
__spm_sync_vcore_dvfs_pcm_flags(&pwrctrl->pcm_flags,
&__spm_vcorefs.pwrctrl->pcm_flags);
#endif
if (en)
pwrctrl->pcm_flags |= SPM_FLAG_ENABLE_COMMON_SODI5;
else
pwrctrl->pcm_flags &= (~SPM_FLAG_ENABLE_COMMON_SODI5);
/* Set PCM flags */
__spm_set_pcm_flags(pwrctrl);
__spm_send_cpu_wakeup_event();
}
int mt_spm_common_sodi_get_spm_lp(struct spm_lp_scen **lp)
{
if (!lp)
return -1;
*lp = &__spm_common_sodi;
return 0;
}
void mt_spm_set_common_sodi_pwrctr(void)
{
struct resource_req_status common_sodi_spm_resource_req = {
.id = MT_LP_RQ_ID_ALL_USAGE,
.val = 0,
};
struct spm_lp_scen *spmlp;
mt_lp_rq_get_status(PLAT_RQ_REQ_USAGE,
&common_sodi_spm_resource_req);
mt_spm_common_sodi_get_spm_lp(&spmlp);
__spm_set_power_control(spmlp->pwrctrl,
common_sodi_spm_resource_req.val);
}
void mt_spm_set_common_sodi_pcm_flags(void)
{
struct spm_lp_scen *spmlp;
struct pwr_ctrl *pwrctrl;
mt_spm_common_sodi_get_spm_lp(&spmlp);
pwrctrl = spmlp->pwrctrl;
#if defined(CONFIG_MTK_VCOREDVFS_SUPPORT)
/* Set PCM flags */
__spm_sync_vcore_dvfs_pcm_flags(&pwrctrl->pcm_flags,
&__spm_vcorefs.pwrctrl->pcm_flags);
#endif
__spm_set_pcm_flags(pwrctrl);
__spm_send_cpu_wakeup_event();
}
#endif
static void spm_gpio_init(void)
{
gpio_set_direction(EC_SUSPEND_PIN, GPIO_DIR_OUT);
gpio_set_value(EC_SUSPEND_PIN, GPIO_LEVEL_HIGH);
}
int spm_boot_init(void)
{
plat_spm_lock_init();
#if defined(MT_SPM_FEATURE_SUPPORT)
plat_spm_pmic_wrap_init();
#endif
mt_lp_rm_register(&plat_mt8196_rm);
#ifndef MTK_PLAT_SPM_UNSUPPORT
mt_lp_resource_request_manager_register(&plat_mt8196_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
spm_gpio_init();
spm_irq_num = 0xFFFFFFFF;
INFO("[%s:%d] - spm finished, version = %u, PC = 0x%x\n",
__func__, __LINE__,
mt_spm_version, mmio_read_32(MD32PCM_PC));
return 0;
}
MTK_PLAT_SETUP_1_INIT(spm_boot_init);