1127793daSHaojian Zhuang /* 2127793daSHaojian Zhuang * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. 3127793daSHaojian Zhuang * 4127793daSHaojian Zhuang * SPDX-License-Identifier: BSD-3-Clause 5127793daSHaojian Zhuang */ 6127793daSHaojian Zhuang 7127793daSHaojian Zhuang #include <arch_helpers.h> 8127793daSHaojian Zhuang #include <assert.h> 9127793daSHaojian Zhuang #include <cci.h> 10127793daSHaojian Zhuang #include <debug.h> 11127793daSHaojian Zhuang #include <gicv2.h> 12127793daSHaojian Zhuang #include <hi6220.h> 13127793daSHaojian Zhuang #include <hisi_ipc.h> 14127793daSHaojian Zhuang #include <hisi_pwrc.h> 15127793daSHaojian Zhuang #include <hisi_sram_map.h> 16127793daSHaojian Zhuang #include <mmio.h> 17127793daSHaojian Zhuang #include <psci.h> 18*1e54813aSLeo Yan #include <sp804_delay_timer.h> 19127793daSHaojian Zhuang 20127793daSHaojian Zhuang #include "hikey_def.h" 21127793daSHaojian Zhuang 22*1e54813aSLeo Yan #define CORE_PWR_STATE(state) \ 23*1e54813aSLeo Yan ((state)->pwr_domain_state[MPIDR_AFFLVL0]) 24*1e54813aSLeo Yan #define CLUSTER_PWR_STATE(state) \ 25*1e54813aSLeo Yan ((state)->pwr_domain_state[MPIDR_AFFLVL1]) 26*1e54813aSLeo Yan #define SYSTEM_PWR_STATE(state) \ 27*1e54813aSLeo Yan ((state)->pwr_domain_state[PLAT_MAX_PWR_LVL]) 28127793daSHaojian Zhuang 29127793daSHaojian Zhuang static uintptr_t hikey_sec_entrypoint; 30127793daSHaojian Zhuang 31127793daSHaojian Zhuang static int hikey_pwr_domain_on(u_register_t mpidr) 32127793daSHaojian Zhuang { 33127793daSHaojian Zhuang int cpu, cluster; 34127793daSHaojian Zhuang int curr_cluster; 35127793daSHaojian Zhuang 36127793daSHaojian Zhuang cluster = MPIDR_AFFLVL1_VAL(mpidr); 37127793daSHaojian Zhuang cpu = MPIDR_AFFLVL0_VAL(mpidr); 38127793daSHaojian Zhuang curr_cluster = MPIDR_AFFLVL1_VAL(read_mpidr()); 39127793daSHaojian Zhuang if (cluster != curr_cluster) 40127793daSHaojian Zhuang hisi_ipc_cluster_on(cpu, cluster); 41127793daSHaojian Zhuang 42127793daSHaojian Zhuang hisi_pwrc_set_core_bx_addr(cpu, cluster, hikey_sec_entrypoint); 43127793daSHaojian Zhuang hisi_ipc_cpu_on(cpu, cluster); 44127793daSHaojian Zhuang return 0; 45127793daSHaojian Zhuang } 46127793daSHaojian Zhuang 47127793daSHaojian Zhuang static void hikey_pwr_domain_on_finish(const psci_power_state_t *target_state) 48127793daSHaojian Zhuang { 49127793daSHaojian Zhuang unsigned long mpidr; 50127793daSHaojian Zhuang int cpu, cluster; 51127793daSHaojian Zhuang 52127793daSHaojian Zhuang mpidr = read_mpidr(); 53127793daSHaojian Zhuang cluster = MPIDR_AFFLVL1_VAL(mpidr); 54127793daSHaojian Zhuang cpu = MPIDR_AFFLVL0_VAL(mpidr); 55*1e54813aSLeo Yan 56*1e54813aSLeo Yan 57127793daSHaojian Zhuang /* 58127793daSHaojian Zhuang * Enable CCI coherency for this cluster. 59127793daSHaojian Zhuang * No need for locks as no other cpu is active at the moment. 60127793daSHaojian Zhuang */ 61*1e54813aSLeo Yan if (CLUSTER_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) 62127793daSHaojian Zhuang cci_enable_snoop_dvm_reqs(MPIDR_AFFLVL1_VAL(mpidr)); 63127793daSHaojian Zhuang 64127793daSHaojian Zhuang /* Zero the jump address in the mailbox for this cpu */ 65127793daSHaojian Zhuang hisi_pwrc_set_core_bx_addr(cpu, cluster, 0); 66127793daSHaojian Zhuang 67127793daSHaojian Zhuang /* Program the GIC per-cpu distributor or re-distributor interface */ 68127793daSHaojian Zhuang gicv2_pcpu_distif_init(); 69127793daSHaojian Zhuang /* Enable the GIC cpu interface */ 70127793daSHaojian Zhuang gicv2_cpuif_enable(); 71127793daSHaojian Zhuang } 72127793daSHaojian Zhuang 73127793daSHaojian Zhuang void hikey_pwr_domain_off(const psci_power_state_t *target_state) 74127793daSHaojian Zhuang { 75127793daSHaojian Zhuang unsigned long mpidr; 76127793daSHaojian Zhuang int cpu, cluster; 77127793daSHaojian Zhuang 78127793daSHaojian Zhuang mpidr = read_mpidr(); 79127793daSHaojian Zhuang cluster = MPIDR_AFFLVL1_VAL(mpidr); 80127793daSHaojian Zhuang cpu = MPIDR_AFFLVL0_VAL(mpidr); 81*1e54813aSLeo Yan 82*1e54813aSLeo Yan gicv2_cpuif_disable(); 83*1e54813aSLeo Yan hisi_ipc_cpu_off(cpu, cluster); 84*1e54813aSLeo Yan 85*1e54813aSLeo Yan if (CLUSTER_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) { 86127793daSHaojian Zhuang hisi_ipc_spin_lock(HISI_IPC_SEM_CPUIDLE); 87127793daSHaojian Zhuang cci_disable_snoop_dvm_reqs(MPIDR_AFFLVL1_VAL(mpidr)); 88127793daSHaojian Zhuang hisi_ipc_spin_unlock(HISI_IPC_SEM_CPUIDLE); 89127793daSHaojian Zhuang 90127793daSHaojian Zhuang hisi_ipc_cluster_off(cpu, cluster); 91127793daSHaojian Zhuang } 92127793daSHaojian Zhuang } 93127793daSHaojian Zhuang 94*1e54813aSLeo Yan static void hikey_pwr_domain_suspend(const psci_power_state_t *target_state) 95*1e54813aSLeo Yan { 96*1e54813aSLeo Yan u_register_t mpidr = read_mpidr_el1(); 97*1e54813aSLeo Yan unsigned int cpu = mpidr & MPIDR_CPU_MASK; 98*1e54813aSLeo Yan unsigned int cluster = 99*1e54813aSLeo Yan (mpidr & MPIDR_CLUSTER_MASK) >> MPIDR_AFFINITY_BITS; 100*1e54813aSLeo Yan 101*1e54813aSLeo Yan if (CORE_PWR_STATE(target_state) != PLAT_MAX_OFF_STATE) 102*1e54813aSLeo Yan return; 103*1e54813aSLeo Yan 104*1e54813aSLeo Yan if (CORE_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) { 105*1e54813aSLeo Yan 106*1e54813aSLeo Yan /* Program the jump address for the target cpu */ 107*1e54813aSLeo Yan hisi_pwrc_set_core_bx_addr(cpu, cluster, hikey_sec_entrypoint); 108*1e54813aSLeo Yan 109*1e54813aSLeo Yan gicv2_cpuif_disable(); 110*1e54813aSLeo Yan 111*1e54813aSLeo Yan if (SYSTEM_PWR_STATE(target_state) != PLAT_MAX_OFF_STATE) 112*1e54813aSLeo Yan hisi_ipc_cpu_suspend(cpu, cluster); 113*1e54813aSLeo Yan } 114*1e54813aSLeo Yan 115*1e54813aSLeo Yan /* Perform the common cluster specific operations */ 116*1e54813aSLeo Yan if (CLUSTER_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) { 117*1e54813aSLeo Yan hisi_ipc_spin_lock(HISI_IPC_SEM_CPUIDLE); 118*1e54813aSLeo Yan cci_disable_snoop_dvm_reqs(MPIDR_AFFLVL1_VAL(mpidr)); 119*1e54813aSLeo Yan hisi_ipc_spin_unlock(HISI_IPC_SEM_CPUIDLE); 120*1e54813aSLeo Yan 121*1e54813aSLeo Yan if (SYSTEM_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) { 122*1e54813aSLeo Yan hisi_pwrc_set_cluster_wfi(1); 123*1e54813aSLeo Yan hisi_pwrc_set_cluster_wfi(0); 124*1e54813aSLeo Yan hisi_ipc_psci_system_off(); 125*1e54813aSLeo Yan } else 126*1e54813aSLeo Yan hisi_ipc_cluster_suspend(cpu, cluster); 127*1e54813aSLeo Yan } 128*1e54813aSLeo Yan } 129*1e54813aSLeo Yan 130*1e54813aSLeo Yan static void hikey_pwr_domain_suspend_finish(const psci_power_state_t *target_state) 131*1e54813aSLeo Yan { 132*1e54813aSLeo Yan unsigned long mpidr; 133*1e54813aSLeo Yan unsigned int cluster, cpu; 134*1e54813aSLeo Yan 135*1e54813aSLeo Yan /* Nothing to be done on waking up from retention from CPU level */ 136*1e54813aSLeo Yan if (CORE_PWR_STATE(target_state) != PLAT_MAX_OFF_STATE) 137*1e54813aSLeo Yan return; 138*1e54813aSLeo Yan 139*1e54813aSLeo Yan /* Get the mpidr for this cpu */ 140*1e54813aSLeo Yan mpidr = read_mpidr_el1(); 141*1e54813aSLeo Yan cluster = (mpidr & MPIDR_CLUSTER_MASK) >> MPIDR_AFF1_SHIFT; 142*1e54813aSLeo Yan cpu = mpidr & MPIDR_CPU_MASK; 143*1e54813aSLeo Yan 144*1e54813aSLeo Yan /* Enable CCI coherency for cluster */ 145*1e54813aSLeo Yan if (CLUSTER_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) 146*1e54813aSLeo Yan cci_enable_snoop_dvm_reqs(MPIDR_AFFLVL1_VAL(mpidr)); 147*1e54813aSLeo Yan 148*1e54813aSLeo Yan hisi_pwrc_set_core_bx_addr(cpu, cluster, 0); 149*1e54813aSLeo Yan 150*1e54813aSLeo Yan if (SYSTEM_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) { 151*1e54813aSLeo Yan gicv2_distif_init(); 152*1e54813aSLeo Yan gicv2_pcpu_distif_init(); 153*1e54813aSLeo Yan gicv2_cpuif_enable(); 154*1e54813aSLeo Yan } else { 155*1e54813aSLeo Yan gicv2_pcpu_distif_init(); 156*1e54813aSLeo Yan gicv2_cpuif_enable(); 157*1e54813aSLeo Yan } 158*1e54813aSLeo Yan } 159*1e54813aSLeo Yan 160*1e54813aSLeo Yan static void hikey_get_sys_suspend_power_state(psci_power_state_t *req_state) 161*1e54813aSLeo Yan { 162*1e54813aSLeo Yan int i; 163*1e54813aSLeo Yan 164*1e54813aSLeo Yan for (i = MPIDR_AFFLVL0; i <= PLAT_MAX_PWR_LVL; i++) 165*1e54813aSLeo Yan req_state->pwr_domain_state[i] = PLAT_MAX_OFF_STATE; 166*1e54813aSLeo Yan } 167*1e54813aSLeo Yan 168*1e54813aSLeo Yan static void __dead2 hikey_system_off(void) 169*1e54813aSLeo Yan { 170*1e54813aSLeo Yan NOTICE("%s: off system\n", __func__); 171*1e54813aSLeo Yan 172*1e54813aSLeo Yan /* Pull down GPIO_0_0 to trigger PMIC shutdown */ 173*1e54813aSLeo Yan mmio_write_32(0xF8001810, 0x2); /* Pinmux */ 174*1e54813aSLeo Yan mmio_write_8(0xF8011400, 1); /* Pin direction */ 175*1e54813aSLeo Yan mmio_write_8(0xF8011004, 0); /* Pin output value */ 176*1e54813aSLeo Yan 177*1e54813aSLeo Yan /* Wait for 2s to power off system by PMIC */ 178*1e54813aSLeo Yan sp804_timer_init(SP804_TIMER0_BASE, 10, 192); 179*1e54813aSLeo Yan mdelay(2000); 180*1e54813aSLeo Yan 181*1e54813aSLeo Yan /* 182*1e54813aSLeo Yan * PMIC shutdown depends on two conditions: GPIO_0_0 (PWR_HOLD) low, 183*1e54813aSLeo Yan * and VBUS_DET < 3.6V. For HiKey, VBUS_DET is connected to VDD_4V2 184*1e54813aSLeo Yan * through Jumper 1-2. So, to complete shutdown, user needs to manually 185*1e54813aSLeo Yan * remove Jumper 1-2. 186*1e54813aSLeo Yan */ 187*1e54813aSLeo Yan NOTICE("+------------------------------------------+\n"); 188*1e54813aSLeo Yan NOTICE("| IMPORTANT: Remove Jumper 1-2 to shutdown |\n"); 189*1e54813aSLeo Yan NOTICE("| DANGER: SoC is still burning. DANGER! |\n"); 190*1e54813aSLeo Yan NOTICE("| Board will be reboot to avoid overheat |\n"); 191*1e54813aSLeo Yan NOTICE("+------------------------------------------+\n"); 192*1e54813aSLeo Yan 193*1e54813aSLeo Yan /* Send the system reset request */ 194*1e54813aSLeo Yan mmio_write_32(AO_SC_SYS_STAT0, 0x48698284); 195*1e54813aSLeo Yan 196*1e54813aSLeo Yan wfi(); 197*1e54813aSLeo Yan panic(); 198*1e54813aSLeo Yan } 199*1e54813aSLeo Yan 200127793daSHaojian Zhuang static void __dead2 hikey_system_reset(void) 201127793daSHaojian Zhuang { 202127793daSHaojian Zhuang /* Send the system reset request */ 203127793daSHaojian Zhuang mmio_write_32(AO_SC_SYS_STAT0, 0x48698284); 204127793daSHaojian Zhuang isb(); 205127793daSHaojian Zhuang dsb(); 206127793daSHaojian Zhuang 207127793daSHaojian Zhuang wfi(); 208127793daSHaojian Zhuang panic(); 209127793daSHaojian Zhuang } 210127793daSHaojian Zhuang 211127793daSHaojian Zhuang int hikey_validate_power_state(unsigned int power_state, 212127793daSHaojian Zhuang psci_power_state_t *req_state) 213127793daSHaojian Zhuang { 214127793daSHaojian Zhuang int pstate = psci_get_pstate_type(power_state); 215127793daSHaojian Zhuang int pwr_lvl = psci_get_pstate_pwrlvl(power_state); 216127793daSHaojian Zhuang int i; 217127793daSHaojian Zhuang 218127793daSHaojian Zhuang assert(req_state); 219127793daSHaojian Zhuang 220127793daSHaojian Zhuang if (pwr_lvl > PLAT_MAX_PWR_LVL) 221127793daSHaojian Zhuang return PSCI_E_INVALID_PARAMS; 222127793daSHaojian Zhuang 223127793daSHaojian Zhuang /* Sanity check the requested state */ 224127793daSHaojian Zhuang if (pstate == PSTATE_TYPE_STANDBY) { 225127793daSHaojian Zhuang /* 226127793daSHaojian Zhuang * It's possible to enter standby only on power level 0 227127793daSHaojian Zhuang * Ignore any other power level. 228127793daSHaojian Zhuang */ 229127793daSHaojian Zhuang if (pwr_lvl != MPIDR_AFFLVL0) 230127793daSHaojian Zhuang return PSCI_E_INVALID_PARAMS; 231127793daSHaojian Zhuang 232127793daSHaojian Zhuang req_state->pwr_domain_state[MPIDR_AFFLVL0] = 233127793daSHaojian Zhuang PLAT_MAX_RET_STATE; 234127793daSHaojian Zhuang } else { 235127793daSHaojian Zhuang for (i = MPIDR_AFFLVL0; i <= pwr_lvl; i++) 236127793daSHaojian Zhuang req_state->pwr_domain_state[i] = 237127793daSHaojian Zhuang PLAT_MAX_OFF_STATE; 238127793daSHaojian Zhuang } 239127793daSHaojian Zhuang 240127793daSHaojian Zhuang /* 241127793daSHaojian Zhuang * We expect the 'state id' to be zero. 242127793daSHaojian Zhuang */ 243127793daSHaojian Zhuang if (psci_get_pstate_id(power_state)) 244127793daSHaojian Zhuang return PSCI_E_INVALID_PARAMS; 245127793daSHaojian Zhuang 246127793daSHaojian Zhuang return PSCI_E_SUCCESS; 247127793daSHaojian Zhuang } 248127793daSHaojian Zhuang 249127793daSHaojian Zhuang static int hikey_validate_ns_entrypoint(uintptr_t entrypoint) 250127793daSHaojian Zhuang { 251127793daSHaojian Zhuang /* 252127793daSHaojian Zhuang * Check if the non secure entrypoint lies within the non 253127793daSHaojian Zhuang * secure DRAM. 254127793daSHaojian Zhuang */ 255127793daSHaojian Zhuang if ((entrypoint > DDR_BASE) && (entrypoint < (DDR_BASE + DDR_SIZE))) 256127793daSHaojian Zhuang return PSCI_E_SUCCESS; 257127793daSHaojian Zhuang 258127793daSHaojian Zhuang return PSCI_E_INVALID_ADDRESS; 259127793daSHaojian Zhuang } 260127793daSHaojian Zhuang 261127793daSHaojian Zhuang static const plat_psci_ops_t hikey_psci_ops = { 262127793daSHaojian Zhuang .cpu_standby = NULL, 263127793daSHaojian Zhuang .pwr_domain_on = hikey_pwr_domain_on, 264127793daSHaojian Zhuang .pwr_domain_on_finish = hikey_pwr_domain_on_finish, 265127793daSHaojian Zhuang .pwr_domain_off = hikey_pwr_domain_off, 266*1e54813aSLeo Yan .pwr_domain_suspend = hikey_pwr_domain_suspend, 267*1e54813aSLeo Yan .pwr_domain_suspend_finish = hikey_pwr_domain_suspend_finish, 268*1e54813aSLeo Yan .system_off = hikey_system_off, 269127793daSHaojian Zhuang .system_reset = hikey_system_reset, 270127793daSHaojian Zhuang .validate_power_state = hikey_validate_power_state, 271127793daSHaojian Zhuang .validate_ns_entrypoint = hikey_validate_ns_entrypoint, 272*1e54813aSLeo Yan .get_sys_suspend_power_state = hikey_get_sys_suspend_power_state, 273127793daSHaojian Zhuang }; 274127793daSHaojian Zhuang 275127793daSHaojian Zhuang int plat_setup_psci_ops(uintptr_t sec_entrypoint, 276127793daSHaojian Zhuang const plat_psci_ops_t **psci_ops) 277127793daSHaojian Zhuang { 278127793daSHaojian Zhuang hikey_sec_entrypoint = sec_entrypoint; 279127793daSHaojian Zhuang 280127793daSHaojian Zhuang /* 281127793daSHaojian Zhuang * Initialize PSCI ops struct 282127793daSHaojian Zhuang */ 283127793daSHaojian Zhuang *psci_ops = &hikey_psci_ops; 284127793daSHaojian Zhuang return 0; 285127793daSHaojian Zhuang } 286