diff --git a/plat/mediatek/mt8195/drivers/spmc/mtspmc.c b/plat/mediatek/mt8195/drivers/spmc/mtspmc.c index 7ccebd6abe811d0fa3d60668f57cd29dc02cc4ec..9b332a00c61ca88f9d547fb8085bb6e48ee2e5c6 100644 --- a/plat/mediatek/mt8195/drivers/spmc/mtspmc.c +++ b/plat/mediatek/mt8195/drivers/spmc/mtspmc.c @@ -15,31 +15,31 @@ #include -void mcucfg_disable_gic_wakeup(uint32_t cluster, uint32_t cpu) +void mcucfg_disable_gic_wakeup(unsigned int cluster, unsigned int cpu) { mmio_setbits_32(MCUCFG_CPC_FLOW_CTRL_CFG, GIC_WAKEUP_IGNORE(cpu)); } -void mcucfg_enable_gic_wakeup(uint32_t cluster, uint32_t cpu) +void mcucfg_enable_gic_wakeup(unsigned int cluster, unsigned int cpu) { mmio_clrbits_32(MCUCFG_CPC_FLOW_CTRL_CFG, GIC_WAKEUP_IGNORE(cpu)); } -void mcucfg_set_bootaddr(uint32_t cluster, uint32_t cpu, uintptr_t bootaddr) +void mcucfg_set_bootaddr(unsigned int cluster, unsigned int cpu, uintptr_t bootaddr) { assert(cluster == 0U); mmio_write_32(per_cpu(cluster, cpu, MCUCFG_BOOTADDR), bootaddr); } -uintptr_t mcucfg_get_bootaddr(uint32_t cluster, uint32_t cpu) +uintptr_t mcucfg_get_bootaddr(unsigned int cluster, unsigned int cpu) { assert(cluster == 0U); return (uintptr_t)mmio_read_32(per_cpu(cluster, cpu, MCUCFG_BOOTADDR)); } -void mcucfg_init_archstate(uint32_t cluster, uint32_t cpu, bool arm64) +void mcucfg_init_archstate(unsigned int cluster, unsigned int cpu, bool arm64) { uint32_t reg; @@ -57,7 +57,7 @@ void mcucfg_init_archstate(uint32_t cluster, uint32_t cpu, bool arm64) /** * Return subsystem's power state. * - * @mask: mask to SPM_CPU_PWR_STATUS to query the power state + * @mask: mask to MCUCFG_CPC_SPMC_PWR_STATUS to query the power state * of one subsystem. * RETURNS: * 0 (the subsys was powered off) @@ -65,17 +65,17 @@ void mcucfg_init_archstate(uint32_t cluster, uint32_t cpu, bool arm64) */ bool spm_get_powerstate(uint32_t mask) { - return (mmio_read_32(SPM_CPU_PWR_STATUS) & mask) != 0U; + return (mmio_read_32(MCUCFG_CPC_SPMC_PWR_STATUS) & mask) != 0U; } -bool spm_get_cluster_powerstate(uint32_t cluster) +bool spm_get_cluster_powerstate(unsigned int cluster) { assert(cluster == 0U); - return spm_get_powerstate(MP0_CPUTOP); + return spm_get_powerstate(BIT(14)); } -bool spm_get_cpu_powerstate(uint32_t cluster, uint32_t cpu) +bool spm_get_cpu_powerstate(unsigned int cluster, unsigned int cpu) { uint32_t mask = BIT(cpu); @@ -98,19 +98,12 @@ int spmc_init(void) mmio_setbits_32(per_cpu(0, 6, SPM_CPU_PWR), PWR_RST_B); mmio_setbits_32(per_cpu(0, 7, SPM_CPU_PWR), PWR_RST_B); - mmio_setbits_32(MCUCFG_CPC_FLOW_CTRL_CFG, GIC_WAKEUP_IGNORE(1)); - mmio_setbits_32(MCUCFG_CPC_FLOW_CTRL_CFG, GIC_WAKEUP_IGNORE(2)); - mmio_setbits_32(MCUCFG_CPC_FLOW_CTRL_CFG, GIC_WAKEUP_IGNORE(3)); - mmio_setbits_32(MCUCFG_CPC_FLOW_CTRL_CFG, GIC_WAKEUP_IGNORE(4)); - mmio_setbits_32(MCUCFG_CPC_FLOW_CTRL_CFG, GIC_WAKEUP_IGNORE(5)); - mmio_setbits_32(MCUCFG_CPC_FLOW_CTRL_CFG, GIC_WAKEUP_IGNORE(6)); - mmio_setbits_32(MCUCFG_CPC_FLOW_CTRL_CFG, GIC_WAKEUP_IGNORE(7)); - mmio_clrbits_32(SPM_MCUSYS_PWR_CON, RESETPWRON_CONFIG); mmio_clrbits_32(SPM_MP0_CPUTOP_PWR_CON, RESETPWRON_CONFIG); mmio_clrbits_32(per_cpu(0, 0, SPM_CPU_PWR), RESETPWRON_CONFIG); mmio_setbits_32(MCUCFG_CPC_FLOW_CTRL_CFG, CPC_CTRL_ENABLE); + mmio_setbits_32(MCUCFG_CPC_FLOW_CTRL_CFG, SSPM_CORE_PWR_ON_EN); return 0; } @@ -121,24 +114,20 @@ int spmc_init(void) * @cluster: the cluster ID of the CPU which to be powered on * @cpu: the CPU ID of the CPU which to be powered on */ -void spm_poweron_cpu(uint32_t cluster, uint32_t cpu) +void spm_poweron_cpu(unsigned int cluster, unsigned int cpu) { + uintptr_t cpu_pwr_con = per_cpu(cluster, cpu, SPM_CPU_PWR); + /* set to 0 after BIG VPROC bulk on & before B-core power on seq. */ if (cpu >= 4U) { mmio_write_32(DREQ20_BIG_VPROC_ISO, 0U); } - mmio_setbits_32(MCUCFG_CPC_FLOW_CTRL_CFG, SSPM_ALL_PWR_CTRL_EN); - mmio_setbits_32(per_cpu(cluster, cpu, SPM_CPU_PWR), PWR_ON); + mmio_setbits_32(cpu_pwr_con, PWR_ON); while (!spm_get_cpu_powerstate(cluster, cpu)) { - } - - mmio_clrbits_32(MCUCFG_CPC_FLOW_CTRL_CFG, SSPM_ALL_PWR_CTRL_EN); - - /* Enable Big CPU Last PC */ - if (cpu >= 4U) { - mmio_clrbits_32(LAST_PC_REG(cpu), BIT(3)); + mmio_clrbits_32(cpu_pwr_con, PWR_ON); + mmio_setbits_32(cpu_pwr_con, PWR_ON); } } @@ -148,7 +137,7 @@ void spm_poweron_cpu(uint32_t cluster, uint32_t cpu) * @cluster: the cluster ID of the CPU which to be powered off * @cpu: the CPU ID of the CPU which to be powered off */ -void spm_poweroff_cpu(uint32_t cluster, uint32_t cpu) +void spm_poweroff_cpu(unsigned int cluster, unsigned int cpu) { /* Set mp0_spmc_pwr_on_cpuX = 0 */ mmio_clrbits_32(per_cpu(cluster, cpu, SPM_CPU_PWR), PWR_ON); @@ -159,7 +148,7 @@ void spm_poweroff_cpu(uint32_t cluster, uint32_t cpu) * * @cluster: the cluster index which to be powered off */ -void spm_poweroff_cluster(uint32_t cluster) +void spm_poweroff_cluster(unsigned int cluster) { /* No need to power on/off cluster on single cluster platform */ assert(false); @@ -170,7 +159,7 @@ void spm_poweroff_cluster(uint32_t cluster) * * @cluster: the cluster index which to be powered on */ -void spm_poweron_cluster(uint32_t cluster) +void spm_poweron_cluster(unsigned int cluster) { /* No need to power on/off cluster on single cluster platform */ assert(false); diff --git a/plat/mediatek/mt8195/drivers/spmc/mtspmc.h b/plat/mediatek/mt8195/drivers/spmc/mtspmc.h index 7ed2e62209dac2ae5f68459ff8da18a2355ec4e5..34e93d0ef7d0801d52a6a23c034b13f5ce541557 100644 --- a/plat/mediatek/mt8195/drivers/spmc/mtspmc.h +++ b/plat/mediatek/mt8195/drivers/spmc/mtspmc.h @@ -11,21 +11,21 @@ int spmc_init(void); -void spm_poweron_cpu(uint32_t cluster, uint32_t cpu); -void spm_poweroff_cpu(uint32_t cluster, uint32_t cpu); +void spm_poweron_cpu(unsigned int cluster, unsigned int cpu); +void spm_poweroff_cpu(unsigned int cluster, unsigned int cpu); -void spm_poweroff_cluster(uint32_t cluster); -void spm_poweron_cluster(uint32_t cluster); +void spm_poweroff_cluster(unsigned int cluster); +void spm_poweron_cluster(unsigned int cluster); -bool spm_get_cpu_powerstate(uint32_t cluster, uint32_t cpu); -bool spm_get_cluster_powerstate(uint32_t cluster); +bool spm_get_cpu_powerstate(unsigned int cluster, unsigned int cpu); +bool spm_get_cluster_powerstate(unsigned int cluster); bool spm_get_powerstate(uint32_t mask); -void mcucfg_init_archstate(uint32_t cluster, uint32_t cpu, bool arm64); -void mcucfg_set_bootaddr(uint32_t cluster, uint32_t cpu, uintptr_t bootaddr); -uintptr_t mcucfg_get_bootaddr(uint32_t cluster, uint32_t cpu); +void mcucfg_init_archstate(unsigned int cluster, unsigned int cpu, bool arm64); +void mcucfg_set_bootaddr(unsigned int cluster, unsigned int cpu, uintptr_t bootaddr); +uintptr_t mcucfg_get_bootaddr(unsigned int cluster, unsigned int cpu); -void mcucfg_disable_gic_wakeup(uint32_t cluster, uint32_t cpu); -void mcucfg_enable_gic_wakeup(uint32_t cluster, uint32_t cpu); +void mcucfg_disable_gic_wakeup(unsigned int cluster, unsigned int cpu); +void mcucfg_enable_gic_wakeup(unsigned int cluster, unsigned int cpu); #endif /* MTSPMC_H */ diff --git a/plat/mediatek/mt8195/drivers/spmc/mtspmc_private.h b/plat/mediatek/mt8195/drivers/spmc/mtspmc_private.h index ad782955d62ca550f065318141d1bcc90cec9bd1..bf4092eb2b3af42caa72d8677afab73c79be4b5f 100644 --- a/plat/mediatek/mt8195/drivers/spmc/mtspmc_private.h +++ b/plat/mediatek/mt8195/drivers/spmc/mtspmc_private.h @@ -122,19 +122,18 @@ static const struct per_cpu_reg MCUCFG_INITARCH[] = { }; #define MCUCFG_INITARCH_CPU_BIT(cpu) BIT(16U + cpu) -#define LAST_PC_REG(cpu) (MCUCFG_REG(0x308) + (cpu * 0x800)) - /* === CPC control */ #define MCUCFG_CPC_FLOW_CTRL_CFG MCUCFG_REG(0xa814) #define MCUCFG_CPC_SPMC_PWR_STATUS MCUCFG_REG(0xa840) /* bit fields of CPC_FLOW_CTRL_CFG */ #define CPC_CTRL_ENABLE BIT(16) +#define SSPM_CORE_PWR_ON_EN BIT(7) /* for cpu-hotplug */ #define SSPM_ALL_PWR_CTRL_EN BIT(13) /* for cpu-hotplug */ #define GIC_WAKEUP_IGNORE(cpu) BIT(21 + cpu) /* bit fields of CPC_SPMC_PWR_STATUS */ -#define CORE_SPMC_PWR_ON_ACK GENMASK(15, 0) +#define CORE_SPMC_PWR_ON_ACK GENMASK(11, 0) /* === APB Module infracfg_ao */ #define INFRA_TOPAXI_PROTECTEN INFRACFG_AO_REG(0x0220) diff --git a/plat/mediatek/mt8195/plat_pm.c b/plat/mediatek/mt8195/plat_pm.c index 61d2cc91951f5bc8d0e5e69be05fdbccc4bbe25a..48ab6ae64e0a8468b6c125dbbc96fbda77472722 100644 --- a/plat/mediatek/mt8195/plat_pm.c +++ b/plat/mediatek/mt8195/plat_pm.c @@ -4,15 +4,361 @@ * SPDX-License-Identifier: BSD-3-Clause */ +/* common headers */ +#include + +#include +#include #include +/* platform specific headers */ +#include +#include +#include +#include +#include + +/* + * Cluster state request: + * [0] : The CPU requires cluster power down + * [1] : The CPU requires cluster power on + */ +#define coordinate_cluster(onoff) write_clusterpwrdn_el1(onoff) +#define coordinate_cluster_pwron() coordinate_cluster(1) +#define coordinate_cluster_pwroff() coordinate_cluster(0) + +/* platform secure entry point */ +static uintptr_t secure_entrypoint; +/* per-CPU power state */ +static unsigned int plat_power_state[PLATFORM_CORE_COUNT]; + +/* platform CPU power domain - ops */ +static const struct mt_lpm_tz *plat_mt_pm; + +#define plat_mt_pm_invoke(_name, _cpu, _state) ({ \ + int ret = -1; \ + if (plat_mt_pm != NULL && plat_mt_pm->_name != NULL) { \ + ret = plat_mt_pm->_name(_cpu, _state); \ + } \ + ret; }) + +#define plat_mt_pm_invoke_no_check(_name, _cpu, _state) ({ \ + if (plat_mt_pm != NULL && plat_mt_pm->_name != NULL) { \ + (void) plat_mt_pm->_name(_cpu, _state); \ + } \ + }) + +/* + * Common MTK_platform operations to power on/off a + * CPU in response to a CPU_ON, CPU_OFF or CPU_SUSPEND request. + */ + +static void plat_cpu_pwrdwn_common(unsigned int cpu, + const psci_power_state_t *state, unsigned int req_pstate) +{ + assert(cpu == plat_my_core_pos()); + + plat_mt_pm_invoke_no_check(pwr_cpu_dwn, cpu, state); + + if ((psci_get_pstate_pwrlvl(req_pstate) >= MTK_AFFLVL_CLUSTER) || + (req_pstate == 0U)) { /* hotplug off */ + coordinate_cluster_pwroff(); + } + + /* Prevent interrupts from spuriously waking up this CPU */ + mt_gic_rdistif_save(); + gicv3_cpuif_disable(cpu); + gicv3_rdistif_off(cpu); +} + +static void plat_cpu_pwron_common(unsigned int cpu, + const psci_power_state_t *state, unsigned int req_pstate) +{ + assert(cpu == plat_my_core_pos()); + + plat_mt_pm_invoke_no_check(pwr_cpu_on, cpu, state); + + coordinate_cluster_pwron(); + + /* Enable the GIC CPU interface */ + gicv3_rdistif_on(cpu); + gicv3_cpuif_enable(cpu); + mt_gic_rdistif_init(); + + /* + * If mcusys does power down before then restore + * all CPUs' GIC Redistributors + */ + if (IS_MCUSYS_OFF_STATE(state)) { + mt_gic_rdistif_restore_all(); + } else { + mt_gic_rdistif_restore(); + } +} + +/* + * Common MTK_platform operations to power on/off a + * cluster in response to a CPU_ON, CPU_OFF or CPU_SUSPEND request. + */ + +static void plat_cluster_pwrdwn_common(unsigned int cpu, + const psci_power_state_t *state, unsigned int req_pstate) +{ + assert(cpu == plat_my_core_pos()); + + if (plat_mt_pm_invoke(pwr_cluster_dwn, cpu, state) != 0) { + coordinate_cluster_pwron(); + + /* TODO: return on fail. + * Add a 'return' here before adding any code following + * the if-block. + */ + } +} + +static void plat_cluster_pwron_common(unsigned int cpu, + const psci_power_state_t *state, unsigned int req_pstate) +{ + assert(cpu == plat_my_core_pos()); + + if (plat_mt_pm_invoke(pwr_cluster_on, cpu, state) != 0) { + /* TODO: return on fail. + * Add a 'return' here before adding any code following + * the if-block. + */ + } +} + +/* + * Common MTK_platform operations to power on/off a + * mcusys in response to a CPU_ON, CPU_OFF or CPU_SUSPEND request. + */ + +static void plat_mcusys_pwrdwn_common(unsigned int cpu, + const psci_power_state_t *state, unsigned int req_pstate) +{ + assert(cpu == plat_my_core_pos()); + + if (plat_mt_pm_invoke(pwr_mcusys_dwn, cpu, state) != 0) { + return; /* return on fail */ + } + + mt_gic_distif_save(); + gic_sgi_save_all(); +} + +static void plat_mcusys_pwron_common(unsigned int cpu, + const psci_power_state_t *state, unsigned int req_pstate) +{ + assert(cpu == plat_my_core_pos()); + + if (plat_mt_pm_invoke(pwr_mcusys_on, cpu, state) != 0) { + return; /* return on fail */ + } + + mt_gic_init(); + mt_gic_distif_restore(); + gic_sgi_restore_all(); + + plat_mt_pm_invoke_no_check(pwr_mcusys_on_finished, cpu, state); +} + +/* + * plat_psci_ops implementation + */ + +static void plat_cpu_standby(plat_local_state_t cpu_state) +{ + uint64_t scr; + + scr = read_scr_el3(); + write_scr_el3(scr | SCR_IRQ_BIT | SCR_FIQ_BIT); + + isb(); + dsb(); + wfi(); + + write_scr_el3(scr); +} + +static int plat_power_domain_on(u_register_t mpidr) +{ + unsigned int cpu = (unsigned int)plat_core_pos_by_mpidr(mpidr); + unsigned int cluster = 0U; + + if (cpu >= PLATFORM_CORE_COUNT) { + return PSCI_E_INVALID_PARAMS; + } + + if (!spm_get_cluster_powerstate(cluster)) { + spm_poweron_cluster(cluster); + } + + /* init CPU reset arch as AARCH64 */ + mcucfg_init_archstate(cluster, cpu, true); + mcucfg_set_bootaddr(cluster, cpu, secure_entrypoint); + spm_poweron_cpu(cluster, cpu); + + return PSCI_E_SUCCESS; +} + +static void plat_power_domain_on_finish(const psci_power_state_t *state) +{ + unsigned long mpidr = read_mpidr_el1(); + unsigned int cpu = (unsigned int)plat_core_pos_by_mpidr(mpidr); + + assert(cpu < PLATFORM_CORE_COUNT); + + /* Allow IRQs to wakeup this core in IDLE flow */ + mcucfg_enable_gic_wakeup(0U, cpu); + + if (IS_CLUSTER_OFF_STATE(state)) { + plat_cluster_pwron_common(cpu, state, 0U); + } + + plat_cpu_pwron_common(cpu, state, 0U); +} + +static void plat_power_domain_off(const psci_power_state_t *state) +{ + unsigned long mpidr = read_mpidr_el1(); + unsigned int cpu = (unsigned int)plat_core_pos_by_mpidr(mpidr); + + assert(cpu < PLATFORM_CORE_COUNT); + + plat_cpu_pwrdwn_common(cpu, state, 0U); + spm_poweroff_cpu(0U, cpu); + + /* prevent unintended IRQs from waking up the hot-unplugged core */ + mcucfg_disable_gic_wakeup(0U, cpu); + + if (IS_CLUSTER_OFF_STATE(state)) { + plat_cluster_pwrdwn_common(cpu, state, 0U); + } +} + +static void plat_power_domain_suspend(const psci_power_state_t *state) +{ + unsigned int cpu = plat_my_core_pos(); + + assert(cpu < PLATFORM_CORE_COUNT); + + plat_mt_pm_invoke_no_check(pwr_prompt, cpu, state); + + /* Perform the common CPU specific operations */ + plat_cpu_pwrdwn_common(cpu, state, plat_power_state[cpu]); + + if (IS_CLUSTER_OFF_STATE(state)) { + /* Perform the common cluster specific operations */ + plat_cluster_pwrdwn_common(cpu, state, plat_power_state[cpu]); + } + + if (IS_MCUSYS_OFF_STATE(state)) { + /* Perform the common mcusys specific operations */ + plat_mcusys_pwrdwn_common(cpu, state, plat_power_state[cpu]); + } +} + +static void plat_power_domain_suspend_finish(const psci_power_state_t *state) +{ + unsigned int cpu = plat_my_core_pos(); + + assert(cpu < PLATFORM_CORE_COUNT); + + if (IS_MCUSYS_OFF_STATE(state)) { + /* Perform the common mcusys specific operations */ + plat_mcusys_pwron_common(cpu, state, plat_power_state[cpu]); + } + + if (IS_CLUSTER_OFF_STATE(state)) { + /* Perform the common cluster specific operations */ + plat_cluster_pwron_common(cpu, state, plat_power_state[cpu]); + } + + /* Perform the common CPU specific operations */ + plat_cpu_pwron_common(cpu, state, plat_power_state[cpu]); + + plat_mt_pm_invoke_no_check(pwr_reflect, cpu, state); +} + +static int plat_validate_power_state(unsigned int power_state, + psci_power_state_t *req_state) +{ + unsigned int pstate = psci_get_pstate_type(power_state); + unsigned int aff_lvl = psci_get_pstate_pwrlvl(power_state); + unsigned int cpu = plat_my_core_pos(); + + if (aff_lvl > PLAT_MAX_PWR_LVL) { + return PSCI_E_INVALID_PARAMS; + } + + if (pstate == PSTATE_TYPE_STANDBY) { + req_state->pwr_domain_state[0] = PLAT_MAX_RET_STATE; + } else { + unsigned int i; + unsigned int pstate_id = psci_get_pstate_id(power_state); + plat_local_state_t s = MTK_LOCAL_STATE_OFF; + + /* Use pstate_id to be power domain state */ + if (pstate_id > s) { + s = (plat_local_state_t)pstate_id; + } + + for (i = 0U; i <= aff_lvl; i++) { + req_state->pwr_domain_state[i] = s; + } + } + + plat_power_state[cpu] = power_state; + return PSCI_E_SUCCESS; +} + +static void plat_get_sys_suspend_power_state(psci_power_state_t *req_state) +{ + unsigned int lv; + unsigned int cpu = plat_my_core_pos(); + + for (lv = PSCI_CPU_PWR_LVL; lv <= PLAT_MAX_PWR_LVL; lv++) { + req_state->pwr_domain_state[lv] = PLAT_MAX_OFF_STATE; + } + + plat_power_state[cpu] = + psci_make_powerstate( + MT_PLAT_PWR_STATE_SYSTEM_SUSPEND, + PSTATE_TYPE_POWERDOWN, PLAT_MAX_PWR_LVL); + + flush_dcache_range((uintptr_t) + &plat_power_state[cpu], + sizeof(plat_power_state[cpu])); +} + static const plat_psci_ops_t plat_psci_ops = { + .cpu_standby = plat_cpu_standby, + .pwr_domain_on = plat_power_domain_on, + .pwr_domain_on_finish = plat_power_domain_on_finish, + .pwr_domain_off = plat_power_domain_off, + .pwr_domain_suspend = plat_power_domain_suspend, + .pwr_domain_suspend_finish = plat_power_domain_suspend_finish, + .validate_power_state = plat_validate_power_state, + .get_sys_suspend_power_state = plat_get_sys_suspend_power_state }; int plat_setup_psci_ops(uintptr_t sec_entrypoint, const plat_psci_ops_t **psci_ops) { *psci_ops = &plat_psci_ops; + secure_entrypoint = sec_entrypoint; + + /* + * init the warm reset config for boot CPU + * reset arch as AARCH64 + * reset addr as function bl31_warm_entrypoint() + */ + mcucfg_init_archstate(0U, 0U, true); + mcucfg_set_bootaddr(0U, 0U, secure_entrypoint); + + spmc_init(); + plat_mt_pm = mt_plat_cpu_pm_init(); return 0; }