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 36fe753c97SSamuel Holland #define CPU_PWR_LVL MPIDR_AFFLVL0 37fe753c97SSamuel Holland #define CLUSTER_PWR_LVL MPIDR_AFFLVL1 38fe753c97SSamuel Holland #define SYSTEM_PWR_LVL MPIDR_AFFLVL2 39fe753c97SSamuel Holland 40fe753c97SSamuel Holland #define CPU_PWR_STATE(state) \ 41fe753c97SSamuel Holland ((state)->pwr_domain_state[CPU_PWR_LVL]) 42fe753c97SSamuel Holland #define CLUSTER_PWR_STATE(state) \ 43fe753c97SSamuel Holland ((state)->pwr_domain_state[CLUSTER_PWR_LVL]) 44fe753c97SSamuel Holland #define SYSTEM_PWR_STATE(state) \ 45fe753c97SSamuel Holland ((state)->pwr_domain_state[SYSTEM_PWR_LVL]) 46fe753c97SSamuel Holland 47fe753c97SSamuel Holland static void sunxi_cpu_standby(plat_local_state_t cpu_state) 48fe753c97SSamuel Holland { 49fe753c97SSamuel Holland u_register_t scr = read_scr_el3(); 50fe753c97SSamuel Holland 51fe753c97SSamuel Holland assert(is_local_state_retn(cpu_state)); 52fe753c97SSamuel Holland 53fe753c97SSamuel Holland write_scr_el3(scr | SCR_IRQ_BIT); 54fe753c97SSamuel Holland wfi(); 55fe753c97SSamuel Holland write_scr_el3(scr); 56fe753c97SSamuel Holland } 57fe753c97SSamuel Holland 58fe753c97SSamuel Holland static int sunxi_pwr_domain_on(u_register_t mpidr) 59fe753c97SSamuel Holland { 60fe753c97SSamuel Holland scpi_set_css_power_state(mpidr, 61fe753c97SSamuel Holland scpi_power_on, 62fe753c97SSamuel Holland scpi_power_on, 63fe753c97SSamuel Holland scpi_power_on); 64fe753c97SSamuel Holland 65fe753c97SSamuel Holland return PSCI_E_SUCCESS; 66fe753c97SSamuel Holland } 67fe753c97SSamuel Holland 68fe753c97SSamuel Holland static void sunxi_pwr_domain_off(const psci_power_state_t *target_state) 69fe753c97SSamuel Holland { 70fe753c97SSamuel Holland plat_local_state_t cpu_pwr_state = CPU_PWR_STATE(target_state); 71fe753c97SSamuel Holland plat_local_state_t cluster_pwr_state = CLUSTER_PWR_STATE(target_state); 72fe753c97SSamuel Holland plat_local_state_t system_pwr_state = SYSTEM_PWR_STATE(target_state); 73fe753c97SSamuel Holland 74fe753c97SSamuel Holland if (is_local_state_off(cpu_pwr_state)) { 75fe753c97SSamuel Holland gicv2_cpuif_disable(); 76fe753c97SSamuel Holland } 77fe753c97SSamuel Holland 78fe753c97SSamuel Holland scpi_set_css_power_state(read_mpidr(), 79*159c36fdSSamuel Holland cpu_pwr_state, 80*159c36fdSSamuel Holland cluster_pwr_state, 81*159c36fdSSamuel Holland system_pwr_state); 82fe753c97SSamuel Holland } 83fe753c97SSamuel Holland 84fe753c97SSamuel Holland static void sunxi_pwr_domain_on_finish(const psci_power_state_t *target_state) 85fe753c97SSamuel Holland { 86fe753c97SSamuel Holland if (is_local_state_off(SYSTEM_PWR_STATE(target_state))) { 87fe753c97SSamuel Holland gicv2_distif_init(); 88fe753c97SSamuel Holland } 89fe753c97SSamuel Holland if (is_local_state_off(CPU_PWR_STATE(target_state))) { 90fe753c97SSamuel Holland gicv2_pcpu_distif_init(); 91fe753c97SSamuel Holland gicv2_cpuif_enable(); 92fe753c97SSamuel Holland } 93fe753c97SSamuel Holland } 94fe753c97SSamuel Holland 95fe753c97SSamuel Holland static void __dead2 sunxi_system_off(void) 96fe753c97SSamuel Holland { 97fe753c97SSamuel Holland uint32_t ret; 98fe753c97SSamuel Holland 99fe753c97SSamuel Holland gicv2_cpuif_disable(); 100fe753c97SSamuel Holland 101fe753c97SSamuel Holland /* Send the power down request to the SCP. */ 102fe753c97SSamuel Holland ret = scpi_sys_power_state(scpi_system_shutdown); 103fe753c97SSamuel Holland if (ret != SCP_OK) { 104fe753c97SSamuel Holland ERROR("PSCI: SCPI %s failed: %d\n", "shutdown", ret); 105fe753c97SSamuel Holland } 106fe753c97SSamuel Holland 107fe753c97SSamuel Holland psci_power_down_wfi(); 108fe753c97SSamuel Holland } 109fe753c97SSamuel Holland 110fe753c97SSamuel Holland static void __dead2 sunxi_system_reset(void) 111fe753c97SSamuel Holland { 112fe753c97SSamuel Holland uint32_t ret; 113fe753c97SSamuel Holland 114fe753c97SSamuel Holland gicv2_cpuif_disable(); 115fe753c97SSamuel Holland 116fe753c97SSamuel Holland /* Send the system reset request to the SCP. */ 117fe753c97SSamuel Holland ret = scpi_sys_power_state(scpi_system_reboot); 118fe753c97SSamuel Holland if (ret != SCP_OK) { 119fe753c97SSamuel Holland ERROR("PSCI: SCPI %s failed: %d\n", "reboot", ret); 120fe753c97SSamuel Holland } 121fe753c97SSamuel Holland 122fe753c97SSamuel Holland psci_power_down_wfi(); 123fe753c97SSamuel Holland } 124fe753c97SSamuel Holland 125fe753c97SSamuel Holland static int sunxi_validate_power_state(unsigned int power_state, 126fe753c97SSamuel Holland psci_power_state_t *req_state) 127fe753c97SSamuel Holland { 128fe753c97SSamuel Holland unsigned int power_level = psci_get_pstate_pwrlvl(power_state); 129fe753c97SSamuel Holland unsigned int type = psci_get_pstate_type(power_state); 130fe753c97SSamuel Holland 131fe753c97SSamuel Holland assert(req_state != NULL); 132fe753c97SSamuel Holland 133fe753c97SSamuel Holland if (power_level > PLAT_MAX_PWR_LVL) { 134fe753c97SSamuel Holland return PSCI_E_INVALID_PARAMS; 135fe753c97SSamuel Holland } 136fe753c97SSamuel Holland 137fe753c97SSamuel Holland if (type == PSTATE_TYPE_STANDBY) { 138fe753c97SSamuel Holland /* Only one retention power state is supported. */ 139fe753c97SSamuel Holland if (psci_get_pstate_id(power_state) > 0) { 140fe753c97SSamuel Holland return PSCI_E_INVALID_PARAMS; 141fe753c97SSamuel Holland } 142fe753c97SSamuel Holland /* The SoC cannot be suspended without losing state */ 143fe753c97SSamuel Holland if (power_level == SYSTEM_PWR_LVL) { 144fe753c97SSamuel Holland return PSCI_E_INVALID_PARAMS; 145fe753c97SSamuel Holland } 146fe753c97SSamuel Holland for (unsigned int i = 0; i <= power_level; ++i) { 147fe753c97SSamuel Holland req_state->pwr_domain_state[i] = PLAT_MAX_RET_STATE; 148fe753c97SSamuel Holland } 149fe753c97SSamuel Holland } else { 150fe753c97SSamuel Holland /* Only one off power state is supported. */ 151fe753c97SSamuel Holland if (psci_get_pstate_id(power_state) > 0) { 152fe753c97SSamuel Holland return PSCI_E_INVALID_PARAMS; 153fe753c97SSamuel Holland } 154fe753c97SSamuel Holland for (unsigned int i = 0; i <= power_level; ++i) { 155fe753c97SSamuel Holland req_state->pwr_domain_state[i] = PLAT_MAX_OFF_STATE; 156fe753c97SSamuel Holland } 157fe753c97SSamuel Holland } 158fe753c97SSamuel Holland /* Higher power domain levels should all remain running */ 159fe753c97SSamuel Holland for (unsigned int i = power_level + 1; i <= PLAT_MAX_PWR_LVL; ++i) { 160fe753c97SSamuel Holland req_state->pwr_domain_state[i] = PSCI_LOCAL_STATE_RUN; 161fe753c97SSamuel Holland } 162fe753c97SSamuel Holland 163fe753c97SSamuel Holland return PSCI_E_SUCCESS; 164fe753c97SSamuel Holland } 165fe753c97SSamuel Holland 166fe753c97SSamuel Holland static void sunxi_get_sys_suspend_power_state(psci_power_state_t *req_state) 167fe753c97SSamuel Holland { 168fe753c97SSamuel Holland assert(req_state != NULL); 169fe753c97SSamuel Holland 170fe753c97SSamuel Holland for (unsigned int i = 0; i <= PLAT_MAX_PWR_LVL; ++i) { 171fe753c97SSamuel Holland req_state->pwr_domain_state[i] = PLAT_MAX_OFF_STATE; 172fe753c97SSamuel Holland } 173fe753c97SSamuel Holland } 174fe753c97SSamuel Holland 175fe753c97SSamuel Holland static const plat_psci_ops_t sunxi_scpi_psci_ops = { 176fe753c97SSamuel Holland .cpu_standby = sunxi_cpu_standby, 177fe753c97SSamuel Holland .pwr_domain_on = sunxi_pwr_domain_on, 178fe753c97SSamuel Holland .pwr_domain_off = sunxi_pwr_domain_off, 179fe753c97SSamuel Holland .pwr_domain_suspend = sunxi_pwr_domain_off, 180fe753c97SSamuel Holland .pwr_domain_on_finish = sunxi_pwr_domain_on_finish, 181fe753c97SSamuel Holland .pwr_domain_suspend_finish = sunxi_pwr_domain_on_finish, 182fe753c97SSamuel Holland .system_off = sunxi_system_off, 183fe753c97SSamuel Holland .system_reset = sunxi_system_reset, 184fe753c97SSamuel Holland .validate_power_state = sunxi_validate_power_state, 185fe753c97SSamuel Holland .validate_ns_entrypoint = sunxi_validate_ns_entrypoint, 186fe753c97SSamuel Holland .get_sys_suspend_power_state = sunxi_get_sys_suspend_power_state, 187fe753c97SSamuel Holland }; 188fe753c97SSamuel Holland 189fe753c97SSamuel Holland int sunxi_set_scpi_psci_ops(const plat_psci_ops_t **psci_ops) 190fe753c97SSamuel Holland { 191fe753c97SSamuel Holland *psci_ops = &sunxi_scpi_psci_ops; 192fe753c97SSamuel Holland 193fe753c97SSamuel Holland /* Check for a valid SCP firmware. */ 194fe753c97SSamuel Holland if (mmio_read_32(SUNXI_SCP_BASE) != SCP_FIRMWARE_MAGIC) { 195fe753c97SSamuel Holland return -1; 196fe753c97SSamuel Holland } 197fe753c97SSamuel Holland 198fe753c97SSamuel Holland /* Program SCP exception vectors to the firmware entrypoint. */ 199fe753c97SSamuel Holland for (unsigned int i = OR1K_VEC_FIRST; i <= OR1K_VEC_LAST; ++i) { 200fe753c97SSamuel Holland uint32_t vector = SUNXI_SRAM_A2_BASE + OR1K_VEC_ADDR(i); 201fe753c97SSamuel Holland uint32_t offset = SUNXI_SCP_BASE - vector; 202fe753c97SSamuel Holland 203fe753c97SSamuel Holland mmio_write_32(vector, offset >> 2); 204fe753c97SSamuel Holland } 205fe753c97SSamuel Holland 206fe753c97SSamuel Holland /* Take the SCP out of reset. */ 207fe753c97SSamuel Holland mmio_setbits_32(SUNXI_R_CPUCFG_BASE, BIT(0)); 208fe753c97SSamuel Holland 209fe753c97SSamuel Holland /* Wait for the SCP firmware to boot. */ 210fe753c97SSamuel Holland return scpi_wait_ready(); 211fe753c97SSamuel Holland } 212