1f91c3cb1SSiva Durga Prasad Paladugu /* 2d4821739STejas Patel * Copyright (c) 2018-2019, ARM Limited and Contributors. All rights reserved. 3f91c3cb1SSiva Durga Prasad Paladugu * 4f91c3cb1SSiva Durga Prasad Paladugu * SPDX-License-Identifier: BSD-3-Clause 5f91c3cb1SSiva Durga Prasad Paladugu */ 6f91c3cb1SSiva Durga Prasad Paladugu 7*5a8ffeabSTejas Patel #include <assert.h> 8*5a8ffeabSTejas Patel #include <plat_arm.h> 9d4821739STejas Patel #include <plat_private.h> 10394a65aaSTejas Patel #include <pm_common.h> 1109d40e0eSAntonio Nino Diaz #include <common/debug.h> 1209d40e0eSAntonio Nino Diaz #include <lib/mmio.h> 1309d40e0eSAntonio Nino Diaz #include <lib/psci/psci.h> 1409d40e0eSAntonio Nino Diaz #include <plat/common/platform.h> 1509d40e0eSAntonio Nino Diaz 16394a65aaSTejas Patel #include "pm_api_sys.h" 17394a65aaSTejas Patel #include "pm_client.h" 18394a65aaSTejas Patel 19f91c3cb1SSiva Durga Prasad Paladugu static uintptr_t versal_sec_entry; 20f91c3cb1SSiva Durga Prasad Paladugu 21394a65aaSTejas Patel static int versal_pwr_domain_on(u_register_t mpidr) 22f91c3cb1SSiva Durga Prasad Paladugu { 23f91c3cb1SSiva Durga Prasad Paladugu unsigned int cpu_id = plat_core_pos_by_mpidr(mpidr); 24394a65aaSTejas Patel const struct pm_proc *proc; 25f91c3cb1SSiva Durga Prasad Paladugu 26f91c3cb1SSiva Durga Prasad Paladugu VERBOSE("%s: mpidr: 0x%lx\n", __func__, mpidr); 27f91c3cb1SSiva Durga Prasad Paladugu 28f91c3cb1SSiva Durga Prasad Paladugu if (cpu_id == -1) 29f91c3cb1SSiva Durga Prasad Paladugu return PSCI_E_INTERN_FAIL; 30f91c3cb1SSiva Durga Prasad Paladugu 31394a65aaSTejas Patel proc = pm_get_proc(cpu_id); 32f91c3cb1SSiva Durga Prasad Paladugu 33394a65aaSTejas Patel /* Send request to PMC to wake up selected ACPU core */ 34394a65aaSTejas Patel pm_req_wakeup(proc->node_id, (versal_sec_entry & 0xFFFFFFFF) | 0x1, 35394a65aaSTejas Patel versal_sec_entry >> 32, 0); 36f91c3cb1SSiva Durga Prasad Paladugu 37394a65aaSTejas Patel /* Clear power down request */ 38394a65aaSTejas Patel pm_client_wakeup(proc); 39f91c3cb1SSiva Durga Prasad Paladugu 40f91c3cb1SSiva Durga Prasad Paladugu return PSCI_E_SUCCESS; 41f91c3cb1SSiva Durga Prasad Paladugu } 42f91c3cb1SSiva Durga Prasad Paladugu 43*5a8ffeabSTejas Patel /** 44*5a8ffeabSTejas Patel * versal_pwr_domain_suspend() - This function sends request to PMC to suspend 45*5a8ffeabSTejas Patel * core. 46*5a8ffeabSTejas Patel * 47*5a8ffeabSTejas Patel * @target_state Targated state 48*5a8ffeabSTejas Patel */ 49*5a8ffeabSTejas Patel static void versal_pwr_domain_suspend(const psci_power_state_t *target_state) 50*5a8ffeabSTejas Patel { 51*5a8ffeabSTejas Patel unsigned int state; 52*5a8ffeabSTejas Patel unsigned int cpu_id = plat_my_core_pos(); 53*5a8ffeabSTejas Patel const struct pm_proc *proc = pm_get_proc(cpu_id); 54*5a8ffeabSTejas Patel 55*5a8ffeabSTejas Patel for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++) 56*5a8ffeabSTejas Patel VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n", 57*5a8ffeabSTejas Patel __func__, i, target_state->pwr_domain_state[i]); 58*5a8ffeabSTejas Patel 59*5a8ffeabSTejas Patel plat_versal_gic_cpuif_disable(); 60*5a8ffeabSTejas Patel 61*5a8ffeabSTejas Patel plat_versal_gic_save(); 62*5a8ffeabSTejas Patel 63*5a8ffeabSTejas Patel state = target_state->pwr_domain_state[1] > PLAT_MAX_RET_STATE ? 64*5a8ffeabSTejas Patel PM_STATE_SUSPEND_TO_RAM : PM_STATE_CPU_IDLE; 65*5a8ffeabSTejas Patel 66*5a8ffeabSTejas Patel /* Send request to PMC to suspend this core */ 67*5a8ffeabSTejas Patel pm_self_suspend(proc->node_id, MAX_LATENCY, state, versal_sec_entry); 68*5a8ffeabSTejas Patel 69*5a8ffeabSTejas Patel /* APU is to be turned off */ 70*5a8ffeabSTejas Patel if (target_state->pwr_domain_state[1] > PLAT_MAX_RET_STATE) { 71*5a8ffeabSTejas Patel /* disable coherency */ 72*5a8ffeabSTejas Patel plat_arm_interconnect_exit_coherency(); 73*5a8ffeabSTejas Patel } 74*5a8ffeabSTejas Patel } 75*5a8ffeabSTejas Patel 76*5a8ffeabSTejas Patel /** 77*5a8ffeabSTejas Patel * versal_pwr_domain_suspend_finish() - This function performs actions to finish 78*5a8ffeabSTejas Patel * suspend procedure. 79*5a8ffeabSTejas Patel * 80*5a8ffeabSTejas Patel * @target_state Targated state 81*5a8ffeabSTejas Patel */ 82*5a8ffeabSTejas Patel static void versal_pwr_domain_suspend_finish( 83*5a8ffeabSTejas Patel const psci_power_state_t *target_state) 84*5a8ffeabSTejas Patel { 85*5a8ffeabSTejas Patel unsigned int cpu_id = plat_my_core_pos(); 86*5a8ffeabSTejas Patel const struct pm_proc *proc = pm_get_proc(cpu_id); 87*5a8ffeabSTejas Patel 88*5a8ffeabSTejas Patel for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++) 89*5a8ffeabSTejas Patel VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n", 90*5a8ffeabSTejas Patel __func__, i, target_state->pwr_domain_state[i]); 91*5a8ffeabSTejas Patel 92*5a8ffeabSTejas Patel /* Clear the APU power control register for this cpu */ 93*5a8ffeabSTejas Patel pm_client_wakeup(proc); 94*5a8ffeabSTejas Patel 95*5a8ffeabSTejas Patel /* enable coherency */ 96*5a8ffeabSTejas Patel plat_arm_interconnect_enter_coherency(); 97*5a8ffeabSTejas Patel 98*5a8ffeabSTejas Patel /* APU was turned off, so restore GIC context */ 99*5a8ffeabSTejas Patel if (target_state->pwr_domain_state[1] > PLAT_MAX_RET_STATE) { 100*5a8ffeabSTejas Patel plat_versal_gic_resume(); 101*5a8ffeabSTejas Patel plat_versal_gic_cpuif_enable(); 102*5a8ffeabSTejas Patel } else { 103*5a8ffeabSTejas Patel plat_versal_gic_cpuif_enable(); 104*5a8ffeabSTejas Patel plat_versal_gic_pcpu_init(); 105*5a8ffeabSTejas Patel } 106*5a8ffeabSTejas Patel } 107*5a8ffeabSTejas Patel 108f91c3cb1SSiva Durga Prasad Paladugu void versal_pwr_domain_on_finish(const psci_power_state_t *target_state) 109f91c3cb1SSiva Durga Prasad Paladugu { 110f91c3cb1SSiva Durga Prasad Paladugu /* Enable the gic cpu interface */ 111f91c3cb1SSiva Durga Prasad Paladugu plat_versal_gic_pcpu_init(); 112f91c3cb1SSiva Durga Prasad Paladugu 113f91c3cb1SSiva Durga Prasad Paladugu /* Program the gic per-cpu distributor or re-distributor interface */ 114f91c3cb1SSiva Durga Prasad Paladugu plat_versal_gic_cpuif_enable(); 115f91c3cb1SSiva Durga Prasad Paladugu } 116f91c3cb1SSiva Durga Prasad Paladugu 117*5a8ffeabSTejas Patel /** 118*5a8ffeabSTejas Patel * versal_pwr_domain_off() - This function performs actions to turn off core 119*5a8ffeabSTejas Patel * 120*5a8ffeabSTejas Patel * @target_state Targated state 121*5a8ffeabSTejas Patel */ 122*5a8ffeabSTejas Patel static void versal_pwr_domain_off(const psci_power_state_t *target_state) 123*5a8ffeabSTejas Patel { 124*5a8ffeabSTejas Patel unsigned int cpu_id = plat_my_core_pos(); 125*5a8ffeabSTejas Patel const struct pm_proc *proc = pm_get_proc(cpu_id); 126*5a8ffeabSTejas Patel 127*5a8ffeabSTejas Patel for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++) 128*5a8ffeabSTejas Patel VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n", 129*5a8ffeabSTejas Patel __func__, i, target_state->pwr_domain_state[i]); 130*5a8ffeabSTejas Patel 131*5a8ffeabSTejas Patel /* Prevent interrupts from spuriously waking up this cpu */ 132*5a8ffeabSTejas Patel plat_versal_gic_cpuif_disable(); 133*5a8ffeabSTejas Patel 134*5a8ffeabSTejas Patel /* 135*5a8ffeabSTejas Patel * Send request to PMC to power down the appropriate APU CPU 136*5a8ffeabSTejas Patel * core. 137*5a8ffeabSTejas Patel * According to PSCI specification, CPU_off function does not 138*5a8ffeabSTejas Patel * have resume address and CPU core can only be woken up 139*5a8ffeabSTejas Patel * invoking CPU_on function, during which resume address will 140*5a8ffeabSTejas Patel * be set. 141*5a8ffeabSTejas Patel */ 142*5a8ffeabSTejas Patel pm_self_suspend(proc->node_id, MAX_LATENCY, PM_STATE_CPU_IDLE, 0); 143*5a8ffeabSTejas Patel } 144*5a8ffeabSTejas Patel 145*5a8ffeabSTejas Patel /** 146*5a8ffeabSTejas Patel * versal_validate_power_state() - This function ensures that the power state 147*5a8ffeabSTejas Patel * parameter in request is valid. 148*5a8ffeabSTejas Patel * 149*5a8ffeabSTejas Patel * @power_state Power state of core 150*5a8ffeabSTejas Patel * @req_state Requested state 151*5a8ffeabSTejas Patel * 152*5a8ffeabSTejas Patel * @return Returns status, either success or reason 153*5a8ffeabSTejas Patel */ 154*5a8ffeabSTejas Patel static int versal_validate_power_state(unsigned int power_state, 155*5a8ffeabSTejas Patel psci_power_state_t *req_state) 156*5a8ffeabSTejas Patel { 157*5a8ffeabSTejas Patel VERBOSE("%s: power_state: 0x%x\n", __func__, power_state); 158*5a8ffeabSTejas Patel 159*5a8ffeabSTejas Patel int pstate = psci_get_pstate_type(power_state); 160*5a8ffeabSTejas Patel 161*5a8ffeabSTejas Patel assert(req_state); 162*5a8ffeabSTejas Patel 163*5a8ffeabSTejas Patel /* Sanity check the requested state */ 164*5a8ffeabSTejas Patel if (pstate == PSTATE_TYPE_STANDBY) 165*5a8ffeabSTejas Patel req_state->pwr_domain_state[MPIDR_AFFLVL0] = PLAT_MAX_RET_STATE; 166*5a8ffeabSTejas Patel else 167*5a8ffeabSTejas Patel req_state->pwr_domain_state[MPIDR_AFFLVL0] = PLAT_MAX_OFF_STATE; 168*5a8ffeabSTejas Patel 169*5a8ffeabSTejas Patel /* We expect the 'state id' to be zero */ 170*5a8ffeabSTejas Patel if (psci_get_pstate_id(power_state)) 171*5a8ffeabSTejas Patel return PSCI_E_INVALID_PARAMS; 172*5a8ffeabSTejas Patel 173*5a8ffeabSTejas Patel return PSCI_E_SUCCESS; 174*5a8ffeabSTejas Patel } 175*5a8ffeabSTejas Patel 176*5a8ffeabSTejas Patel /** 177*5a8ffeabSTejas Patel * versal_get_sys_suspend_power_state() - Get power state for system suspend 178*5a8ffeabSTejas Patel * 179*5a8ffeabSTejas Patel * @req_state Requested state 180*5a8ffeabSTejas Patel */ 181*5a8ffeabSTejas Patel static void versal_get_sys_suspend_power_state(psci_power_state_t *req_state) 182*5a8ffeabSTejas Patel { 183*5a8ffeabSTejas Patel req_state->pwr_domain_state[PSCI_CPU_PWR_LVL] = PLAT_MAX_OFF_STATE; 184*5a8ffeabSTejas Patel req_state->pwr_domain_state[1] = PLAT_MAX_OFF_STATE; 185*5a8ffeabSTejas Patel } 186*5a8ffeabSTejas Patel 187f91c3cb1SSiva Durga Prasad Paladugu static const struct plat_psci_ops versal_nopmc_psci_ops = { 188394a65aaSTejas Patel .pwr_domain_on = versal_pwr_domain_on, 189*5a8ffeabSTejas Patel .pwr_domain_off = versal_pwr_domain_off, 190f91c3cb1SSiva Durga Prasad Paladugu .pwr_domain_on_finish = versal_pwr_domain_on_finish, 191*5a8ffeabSTejas Patel .pwr_domain_suspend = versal_pwr_domain_suspend, 192*5a8ffeabSTejas Patel .pwr_domain_suspend_finish = versal_pwr_domain_suspend_finish, 193*5a8ffeabSTejas Patel .validate_power_state = versal_validate_power_state, 194*5a8ffeabSTejas Patel .get_sys_suspend_power_state = versal_get_sys_suspend_power_state, 195f91c3cb1SSiva Durga Prasad Paladugu }; 196f91c3cb1SSiva Durga Prasad Paladugu 197f91c3cb1SSiva Durga Prasad Paladugu /******************************************************************************* 198f91c3cb1SSiva Durga Prasad Paladugu * Export the platform specific power ops. 199f91c3cb1SSiva Durga Prasad Paladugu ******************************************************************************/ 200f91c3cb1SSiva Durga Prasad Paladugu int plat_setup_psci_ops(uintptr_t sec_entrypoint, 201f91c3cb1SSiva Durga Prasad Paladugu const struct plat_psci_ops **psci_ops) 202f91c3cb1SSiva Durga Prasad Paladugu { 203f91c3cb1SSiva Durga Prasad Paladugu versal_sec_entry = sec_entrypoint; 204f91c3cb1SSiva Durga Prasad Paladugu 205f91c3cb1SSiva Durga Prasad Paladugu *psci_ops = &versal_nopmc_psci_ops; 206f91c3cb1SSiva Durga Prasad Paladugu 207f91c3cb1SSiva Durga Prasad Paladugu return 0; 208f91c3cb1SSiva Durga Prasad Paladugu } 209