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