1*fe753c97SSamuel Holland /* 2*fe753c97SSamuel Holland * Copyright (c) 2017-2021, ARM Limited and Contributors. All rights reserved. 3*fe753c97SSamuel Holland * 4*fe753c97SSamuel Holland * SPDX-License-Identifier: BSD-3-Clause 5*fe753c97SSamuel Holland */ 6*fe753c97SSamuel Holland 7*fe753c97SSamuel Holland #include <assert.h> 8*fe753c97SSamuel Holland 9*fe753c97SSamuel Holland #include <platform_def.h> 10*fe753c97SSamuel Holland 11*fe753c97SSamuel Holland #include <arch_helpers.h> 12*fe753c97SSamuel Holland #include <common/debug.h> 13*fe753c97SSamuel Holland #include <drivers/arm/css/css_scpi.h> 14*fe753c97SSamuel Holland #include <drivers/arm/gicv2.h> 15*fe753c97SSamuel Holland #include <lib/mmio.h> 16*fe753c97SSamuel Holland #include <lib/psci/psci.h> 17*fe753c97SSamuel Holland 18*fe753c97SSamuel Holland #include <sunxi_mmap.h> 19*fe753c97SSamuel Holland #include <sunxi_private.h> 20*fe753c97SSamuel Holland 21*fe753c97SSamuel Holland /* 22*fe753c97SSamuel Holland * The addresses for the SCP exception vectors are defined in the or1k 23*fe753c97SSamuel Holland * architecture specification. 24*fe753c97SSamuel Holland */ 25*fe753c97SSamuel Holland #define OR1K_VEC_FIRST 0x01 26*fe753c97SSamuel Holland #define OR1K_VEC_LAST 0x0e 27*fe753c97SSamuel Holland #define OR1K_VEC_ADDR(n) (0x100 * (n)) 28*fe753c97SSamuel Holland 29*fe753c97SSamuel Holland /* 30*fe753c97SSamuel Holland * This magic value is the little-endian representation of the or1k 31*fe753c97SSamuel Holland * instruction "l.mfspr r2, r0, 0x12", which is guaranteed to be the 32*fe753c97SSamuel Holland * first instruction in the SCP firmware. 33*fe753c97SSamuel Holland */ 34*fe753c97SSamuel Holland #define SCP_FIRMWARE_MAGIC 0xb4400012 35*fe753c97SSamuel Holland 36*fe753c97SSamuel Holland #define CPU_PWR_LVL MPIDR_AFFLVL0 37*fe753c97SSamuel Holland #define CLUSTER_PWR_LVL MPIDR_AFFLVL1 38*fe753c97SSamuel Holland #define SYSTEM_PWR_LVL MPIDR_AFFLVL2 39*fe753c97SSamuel Holland 40*fe753c97SSamuel Holland #define CPU_PWR_STATE(state) \ 41*fe753c97SSamuel Holland ((state)->pwr_domain_state[CPU_PWR_LVL]) 42*fe753c97SSamuel Holland #define CLUSTER_PWR_STATE(state) \ 43*fe753c97SSamuel Holland ((state)->pwr_domain_state[CLUSTER_PWR_LVL]) 44*fe753c97SSamuel Holland #define SYSTEM_PWR_STATE(state) \ 45*fe753c97SSamuel Holland ((state)->pwr_domain_state[SYSTEM_PWR_LVL]) 46*fe753c97SSamuel Holland 47*fe753c97SSamuel Holland static inline scpi_power_state_t scpi_map_state(plat_local_state_t psci_state) 48*fe753c97SSamuel Holland { 49*fe753c97SSamuel Holland if (is_local_state_run(psci_state)) { 50*fe753c97SSamuel Holland return scpi_power_on; 51*fe753c97SSamuel Holland } 52*fe753c97SSamuel Holland if (is_local_state_retn(psci_state)) { 53*fe753c97SSamuel Holland return scpi_power_retention; 54*fe753c97SSamuel Holland } 55*fe753c97SSamuel Holland return scpi_power_off; 56*fe753c97SSamuel Holland } 57*fe753c97SSamuel Holland 58*fe753c97SSamuel Holland static void sunxi_cpu_standby(plat_local_state_t cpu_state) 59*fe753c97SSamuel Holland { 60*fe753c97SSamuel Holland u_register_t scr = read_scr_el3(); 61*fe753c97SSamuel Holland 62*fe753c97SSamuel Holland assert(is_local_state_retn(cpu_state)); 63*fe753c97SSamuel Holland 64*fe753c97SSamuel Holland write_scr_el3(scr | SCR_IRQ_BIT); 65*fe753c97SSamuel Holland wfi(); 66*fe753c97SSamuel Holland write_scr_el3(scr); 67*fe753c97SSamuel Holland } 68*fe753c97SSamuel Holland 69*fe753c97SSamuel Holland static int sunxi_pwr_domain_on(u_register_t mpidr) 70*fe753c97SSamuel Holland { 71*fe753c97SSamuel Holland scpi_set_css_power_state(mpidr, 72*fe753c97SSamuel Holland scpi_power_on, 73*fe753c97SSamuel Holland scpi_power_on, 74*fe753c97SSamuel Holland scpi_power_on); 75*fe753c97SSamuel Holland 76*fe753c97SSamuel Holland return PSCI_E_SUCCESS; 77*fe753c97SSamuel Holland } 78*fe753c97SSamuel Holland 79*fe753c97SSamuel Holland static void sunxi_pwr_domain_off(const psci_power_state_t *target_state) 80*fe753c97SSamuel Holland { 81*fe753c97SSamuel Holland plat_local_state_t cpu_pwr_state = CPU_PWR_STATE(target_state); 82*fe753c97SSamuel Holland plat_local_state_t cluster_pwr_state = CLUSTER_PWR_STATE(target_state); 83*fe753c97SSamuel Holland plat_local_state_t system_pwr_state = SYSTEM_PWR_STATE(target_state); 84*fe753c97SSamuel Holland 85*fe753c97SSamuel Holland if (is_local_state_off(cpu_pwr_state)) { 86*fe753c97SSamuel Holland gicv2_cpuif_disable(); 87*fe753c97SSamuel Holland } 88*fe753c97SSamuel Holland 89*fe753c97SSamuel Holland scpi_set_css_power_state(read_mpidr(), 90*fe753c97SSamuel Holland scpi_map_state(cpu_pwr_state), 91*fe753c97SSamuel Holland scpi_map_state(cluster_pwr_state), 92*fe753c97SSamuel Holland scpi_map_state(system_pwr_state)); 93*fe753c97SSamuel Holland } 94*fe753c97SSamuel Holland 95*fe753c97SSamuel Holland static void sunxi_pwr_domain_on_finish(const psci_power_state_t *target_state) 96*fe753c97SSamuel Holland { 97*fe753c97SSamuel Holland if (is_local_state_off(SYSTEM_PWR_STATE(target_state))) { 98*fe753c97SSamuel Holland gicv2_distif_init(); 99*fe753c97SSamuel Holland } 100*fe753c97SSamuel Holland if (is_local_state_off(CPU_PWR_STATE(target_state))) { 101*fe753c97SSamuel Holland gicv2_pcpu_distif_init(); 102*fe753c97SSamuel Holland gicv2_cpuif_enable(); 103*fe753c97SSamuel Holland } 104*fe753c97SSamuel Holland } 105*fe753c97SSamuel Holland 106*fe753c97SSamuel Holland static void __dead2 sunxi_system_off(void) 107*fe753c97SSamuel Holland { 108*fe753c97SSamuel Holland uint32_t ret; 109*fe753c97SSamuel Holland 110*fe753c97SSamuel Holland gicv2_cpuif_disable(); 111*fe753c97SSamuel Holland 112*fe753c97SSamuel Holland /* Send the power down request to the SCP. */ 113*fe753c97SSamuel Holland ret = scpi_sys_power_state(scpi_system_shutdown); 114*fe753c97SSamuel Holland if (ret != SCP_OK) { 115*fe753c97SSamuel Holland ERROR("PSCI: SCPI %s failed: %d\n", "shutdown", ret); 116*fe753c97SSamuel Holland } 117*fe753c97SSamuel Holland 118*fe753c97SSamuel Holland psci_power_down_wfi(); 119*fe753c97SSamuel Holland } 120*fe753c97SSamuel Holland 121*fe753c97SSamuel Holland static void __dead2 sunxi_system_reset(void) 122*fe753c97SSamuel Holland { 123*fe753c97SSamuel Holland uint32_t ret; 124*fe753c97SSamuel Holland 125*fe753c97SSamuel Holland gicv2_cpuif_disable(); 126*fe753c97SSamuel Holland 127*fe753c97SSamuel Holland /* Send the system reset request to the SCP. */ 128*fe753c97SSamuel Holland ret = scpi_sys_power_state(scpi_system_reboot); 129*fe753c97SSamuel Holland if (ret != SCP_OK) { 130*fe753c97SSamuel Holland ERROR("PSCI: SCPI %s failed: %d\n", "reboot", ret); 131*fe753c97SSamuel Holland } 132*fe753c97SSamuel Holland 133*fe753c97SSamuel Holland psci_power_down_wfi(); 134*fe753c97SSamuel Holland } 135*fe753c97SSamuel Holland 136*fe753c97SSamuel Holland static int sunxi_validate_power_state(unsigned int power_state, 137*fe753c97SSamuel Holland psci_power_state_t *req_state) 138*fe753c97SSamuel Holland { 139*fe753c97SSamuel Holland unsigned int power_level = psci_get_pstate_pwrlvl(power_state); 140*fe753c97SSamuel Holland unsigned int type = psci_get_pstate_type(power_state); 141*fe753c97SSamuel Holland 142*fe753c97SSamuel Holland assert(req_state != NULL); 143*fe753c97SSamuel Holland 144*fe753c97SSamuel Holland if (power_level > PLAT_MAX_PWR_LVL) { 145*fe753c97SSamuel Holland return PSCI_E_INVALID_PARAMS; 146*fe753c97SSamuel Holland } 147*fe753c97SSamuel Holland 148*fe753c97SSamuel Holland if (type == PSTATE_TYPE_STANDBY) { 149*fe753c97SSamuel Holland /* Only one retention power state is supported. */ 150*fe753c97SSamuel Holland if (psci_get_pstate_id(power_state) > 0) { 151*fe753c97SSamuel Holland return PSCI_E_INVALID_PARAMS; 152*fe753c97SSamuel Holland } 153*fe753c97SSamuel Holland /* The SoC cannot be suspended without losing state */ 154*fe753c97SSamuel Holland if (power_level == SYSTEM_PWR_LVL) { 155*fe753c97SSamuel Holland return PSCI_E_INVALID_PARAMS; 156*fe753c97SSamuel Holland } 157*fe753c97SSamuel Holland for (unsigned int i = 0; i <= power_level; ++i) { 158*fe753c97SSamuel Holland req_state->pwr_domain_state[i] = PLAT_MAX_RET_STATE; 159*fe753c97SSamuel Holland } 160*fe753c97SSamuel Holland } else { 161*fe753c97SSamuel Holland /* Only one off power state is supported. */ 162*fe753c97SSamuel Holland if (psci_get_pstate_id(power_state) > 0) { 163*fe753c97SSamuel Holland return PSCI_E_INVALID_PARAMS; 164*fe753c97SSamuel Holland } 165*fe753c97SSamuel Holland for (unsigned int i = 0; i <= power_level; ++i) { 166*fe753c97SSamuel Holland req_state->pwr_domain_state[i] = PLAT_MAX_OFF_STATE; 167*fe753c97SSamuel Holland } 168*fe753c97SSamuel Holland } 169*fe753c97SSamuel Holland /* Higher power domain levels should all remain running */ 170*fe753c97SSamuel Holland for (unsigned int i = power_level + 1; i <= PLAT_MAX_PWR_LVL; ++i) { 171*fe753c97SSamuel Holland req_state->pwr_domain_state[i] = PSCI_LOCAL_STATE_RUN; 172*fe753c97SSamuel Holland } 173*fe753c97SSamuel Holland 174*fe753c97SSamuel Holland return PSCI_E_SUCCESS; 175*fe753c97SSamuel Holland } 176*fe753c97SSamuel Holland 177*fe753c97SSamuel Holland static void sunxi_get_sys_suspend_power_state(psci_power_state_t *req_state) 178*fe753c97SSamuel Holland { 179*fe753c97SSamuel Holland assert(req_state != NULL); 180*fe753c97SSamuel Holland 181*fe753c97SSamuel Holland for (unsigned int i = 0; i <= PLAT_MAX_PWR_LVL; ++i) { 182*fe753c97SSamuel Holland req_state->pwr_domain_state[i] = PLAT_MAX_OFF_STATE; 183*fe753c97SSamuel Holland } 184*fe753c97SSamuel Holland } 185*fe753c97SSamuel Holland 186*fe753c97SSamuel Holland static const plat_psci_ops_t sunxi_scpi_psci_ops = { 187*fe753c97SSamuel Holland .cpu_standby = sunxi_cpu_standby, 188*fe753c97SSamuel Holland .pwr_domain_on = sunxi_pwr_domain_on, 189*fe753c97SSamuel Holland .pwr_domain_off = sunxi_pwr_domain_off, 190*fe753c97SSamuel Holland .pwr_domain_suspend = sunxi_pwr_domain_off, 191*fe753c97SSamuel Holland .pwr_domain_on_finish = sunxi_pwr_domain_on_finish, 192*fe753c97SSamuel Holland .pwr_domain_suspend_finish = sunxi_pwr_domain_on_finish, 193*fe753c97SSamuel Holland .system_off = sunxi_system_off, 194*fe753c97SSamuel Holland .system_reset = sunxi_system_reset, 195*fe753c97SSamuel Holland .validate_power_state = sunxi_validate_power_state, 196*fe753c97SSamuel Holland .validate_ns_entrypoint = sunxi_validate_ns_entrypoint, 197*fe753c97SSamuel Holland .get_sys_suspend_power_state = sunxi_get_sys_suspend_power_state, 198*fe753c97SSamuel Holland }; 199*fe753c97SSamuel Holland 200*fe753c97SSamuel Holland int sunxi_set_scpi_psci_ops(const plat_psci_ops_t **psci_ops) 201*fe753c97SSamuel Holland { 202*fe753c97SSamuel Holland *psci_ops = &sunxi_scpi_psci_ops; 203*fe753c97SSamuel Holland 204*fe753c97SSamuel Holland /* Check for a valid SCP firmware. */ 205*fe753c97SSamuel Holland if (mmio_read_32(SUNXI_SCP_BASE) != SCP_FIRMWARE_MAGIC) { 206*fe753c97SSamuel Holland return -1; 207*fe753c97SSamuel Holland } 208*fe753c97SSamuel Holland 209*fe753c97SSamuel Holland /* Program SCP exception vectors to the firmware entrypoint. */ 210*fe753c97SSamuel Holland for (unsigned int i = OR1K_VEC_FIRST; i <= OR1K_VEC_LAST; ++i) { 211*fe753c97SSamuel Holland uint32_t vector = SUNXI_SRAM_A2_BASE + OR1K_VEC_ADDR(i); 212*fe753c97SSamuel Holland uint32_t offset = SUNXI_SCP_BASE - vector; 213*fe753c97SSamuel Holland 214*fe753c97SSamuel Holland mmio_write_32(vector, offset >> 2); 215*fe753c97SSamuel Holland clean_dcache_range(vector, sizeof(uint32_t)); 216*fe753c97SSamuel Holland } 217*fe753c97SSamuel Holland 218*fe753c97SSamuel Holland /* Take the SCP out of reset. */ 219*fe753c97SSamuel Holland mmio_setbits_32(SUNXI_R_CPUCFG_BASE, BIT(0)); 220*fe753c97SSamuel Holland 221*fe753c97SSamuel Holland /* Wait for the SCP firmware to boot. */ 222*fe753c97SSamuel Holland return scpi_wait_ready(); 223*fe753c97SSamuel Holland } 224