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 3652466ec3SSamuel Holland #define PLAT_LOCAL_PSTATE_WIDTH U(4) 3752466ec3SSamuel Holland #define PLAT_LOCAL_PSTATE_MASK ((U(1) << PLAT_LOCAL_PSTATE_WIDTH) - 1) 3852466ec3SSamuel 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 128*0cf5f08aSAndrey Skvortsov static int sunxi_system_reset2(int is_vendor, int reset_type, u_register_t cookie) 129*0cf5f08aSAndrey Skvortsov { 130*0cf5f08aSAndrey Skvortsov uint32_t ret; 131*0cf5f08aSAndrey Skvortsov 132*0cf5f08aSAndrey Skvortsov if (is_vendor || (reset_type != PSCI_RESET2_SYSTEM_WARM_RESET)) 133*0cf5f08aSAndrey Skvortsov return PSCI_E_NOT_SUPPORTED; 134*0cf5f08aSAndrey Skvortsov 135*0cf5f08aSAndrey Skvortsov gicv2_cpuif_disable(); 136*0cf5f08aSAndrey Skvortsov 137*0cf5f08aSAndrey Skvortsov /* Send the system reset request to the SCP. */ 138*0cf5f08aSAndrey Skvortsov ret = scpi_sys_power_state(scpi_system_reset); 139*0cf5f08aSAndrey Skvortsov if (ret != SCP_OK) { 140*0cf5f08aSAndrey Skvortsov ERROR("PSCI: SCPI %s failed: %d\n", "reset", ret); 141*0cf5f08aSAndrey Skvortsov return PSCI_E_INVALID_PARAMS; 142*0cf5f08aSAndrey Skvortsov } 143*0cf5f08aSAndrey Skvortsov 144*0cf5f08aSAndrey Skvortsov psci_power_down_wfi(); 145*0cf5f08aSAndrey Skvortsov 146*0cf5f08aSAndrey Skvortsov /* 147*0cf5f08aSAndrey Skvortsov * Should not reach here. 148*0cf5f08aSAndrey Skvortsov * However sunxi_system_reset2 has to return some value 149*0cf5f08aSAndrey Skvortsov * according to PSCI v1.1 spec. 150*0cf5f08aSAndrey Skvortsov */ 151*0cf5f08aSAndrey Skvortsov return PSCI_E_SUCCESS; 152*0cf5f08aSAndrey Skvortsov } 153*0cf5f08aSAndrey Skvortsov 154fe753c97SSamuel Holland static int sunxi_validate_power_state(unsigned int power_state, 155fe753c97SSamuel Holland psci_power_state_t *req_state) 156fe753c97SSamuel Holland { 157fe753c97SSamuel Holland unsigned int power_level = psci_get_pstate_pwrlvl(power_state); 15852466ec3SSamuel Holland unsigned int state_id = psci_get_pstate_id(power_state); 159fe753c97SSamuel Holland unsigned int type = psci_get_pstate_type(power_state); 16052466ec3SSamuel Holland unsigned int i; 161fe753c97SSamuel Holland 162fe753c97SSamuel Holland assert(req_state != NULL); 163fe753c97SSamuel Holland 164fe753c97SSamuel Holland if (power_level > PLAT_MAX_PWR_LVL) { 165fe753c97SSamuel Holland return PSCI_E_INVALID_PARAMS; 166fe753c97SSamuel Holland } 167fe753c97SSamuel Holland 168fe753c97SSamuel Holland if (type == PSTATE_TYPE_STANDBY) { 169fe753c97SSamuel Holland return PSCI_E_INVALID_PARAMS; 170fe753c97SSamuel Holland } 17152466ec3SSamuel Holland 17252466ec3SSamuel Holland /* Pass through the requested PSCI state as-is. */ 17352466ec3SSamuel Holland for (i = 0; i <= power_level; ++i) { 17452466ec3SSamuel Holland unsigned int local_pstate = state_id & PLAT_LOCAL_PSTATE_MASK; 17552466ec3SSamuel Holland 17652466ec3SSamuel Holland req_state->pwr_domain_state[i] = local_pstate; 17752466ec3SSamuel Holland state_id >>= PLAT_LOCAL_PSTATE_WIDTH; 178fe753c97SSamuel Holland } 17952466ec3SSamuel Holland 180fe753c97SSamuel Holland /* Higher power domain levels should all remain running */ 18152466ec3SSamuel Holland for (; i <= PLAT_MAX_PWR_LVL; ++i) { 182fe753c97SSamuel Holland req_state->pwr_domain_state[i] = PSCI_LOCAL_STATE_RUN; 183fe753c97SSamuel Holland } 184fe753c97SSamuel Holland 185fe753c97SSamuel Holland return PSCI_E_SUCCESS; 186fe753c97SSamuel Holland } 187fe753c97SSamuel Holland 188fe753c97SSamuel Holland static void sunxi_get_sys_suspend_power_state(psci_power_state_t *req_state) 189fe753c97SSamuel Holland { 190fe753c97SSamuel Holland assert(req_state != NULL); 191fe753c97SSamuel Holland 192fe753c97SSamuel Holland for (unsigned int i = 0; i <= PLAT_MAX_PWR_LVL; ++i) { 193fe753c97SSamuel Holland req_state->pwr_domain_state[i] = PLAT_MAX_OFF_STATE; 194fe753c97SSamuel Holland } 195fe753c97SSamuel Holland } 196fe753c97SSamuel Holland 197fe753c97SSamuel Holland static const plat_psci_ops_t sunxi_scpi_psci_ops = { 198fe753c97SSamuel Holland .cpu_standby = sunxi_cpu_standby, 199fe753c97SSamuel Holland .pwr_domain_on = sunxi_pwr_domain_on, 200fe753c97SSamuel Holland .pwr_domain_off = sunxi_pwr_domain_off, 201fe753c97SSamuel Holland .pwr_domain_suspend = sunxi_pwr_domain_off, 202fe753c97SSamuel Holland .pwr_domain_on_finish = sunxi_pwr_domain_on_finish, 203fe753c97SSamuel Holland .pwr_domain_suspend_finish = sunxi_pwr_domain_on_finish, 204fe753c97SSamuel Holland .system_off = sunxi_system_off, 205fe753c97SSamuel Holland .system_reset = sunxi_system_reset, 206*0cf5f08aSAndrey Skvortsov .system_reset2 = sunxi_system_reset2, 207fe753c97SSamuel Holland .validate_power_state = sunxi_validate_power_state, 208fe753c97SSamuel Holland .validate_ns_entrypoint = sunxi_validate_ns_entrypoint, 209fe753c97SSamuel Holland .get_sys_suspend_power_state = sunxi_get_sys_suspend_power_state, 210fe753c97SSamuel Holland }; 211fe753c97SSamuel Holland 212fe753c97SSamuel Holland int sunxi_set_scpi_psci_ops(const plat_psci_ops_t **psci_ops) 213fe753c97SSamuel Holland { 214fe753c97SSamuel Holland *psci_ops = &sunxi_scpi_psci_ops; 215fe753c97SSamuel Holland 216fe753c97SSamuel Holland /* Check for a valid SCP firmware. */ 217fe753c97SSamuel Holland if (mmio_read_32(SUNXI_SCP_BASE) != SCP_FIRMWARE_MAGIC) { 218fe753c97SSamuel Holland return -1; 219fe753c97SSamuel Holland } 220fe753c97SSamuel Holland 221fe753c97SSamuel Holland /* Program SCP exception vectors to the firmware entrypoint. */ 222fe753c97SSamuel Holland for (unsigned int i = OR1K_VEC_FIRST; i <= OR1K_VEC_LAST; ++i) { 223fe753c97SSamuel Holland uint32_t vector = SUNXI_SRAM_A2_BASE + OR1K_VEC_ADDR(i); 224fe753c97SSamuel Holland uint32_t offset = SUNXI_SCP_BASE - vector; 225fe753c97SSamuel Holland 226fe753c97SSamuel Holland mmio_write_32(vector, offset >> 2); 227fe753c97SSamuel Holland } 228fe753c97SSamuel Holland 229fe753c97SSamuel Holland /* Take the SCP out of reset. */ 230fe753c97SSamuel Holland mmio_setbits_32(SUNXI_R_CPUCFG_BASE, BIT(0)); 231fe753c97SSamuel Holland 232fe753c97SSamuel Holland /* Wait for the SCP firmware to boot. */ 233fe753c97SSamuel Holland return scpi_wait_ready(); 234fe753c97SSamuel Holland } 235