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> 181e54813aSLeo Yan #include <sp804_delay_timer.h> 19127793daSHaojian Zhuang 20127793daSHaojian Zhuang #include "hikey_def.h" 21127793daSHaojian Zhuang 221e54813aSLeo Yan #define CORE_PWR_STATE(state) \ 231e54813aSLeo Yan ((state)->pwr_domain_state[MPIDR_AFFLVL0]) 241e54813aSLeo Yan #define CLUSTER_PWR_STATE(state) \ 251e54813aSLeo Yan ((state)->pwr_domain_state[MPIDR_AFFLVL1]) 261e54813aSLeo Yan #define SYSTEM_PWR_STATE(state) \ 271e54813aSLeo 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); 43*e246617bSLeo Yan hisi_pwrc_enable_debug(cpu, cluster); 44127793daSHaojian Zhuang hisi_ipc_cpu_on(cpu, cluster); 45*e246617bSLeo Yan 46127793daSHaojian Zhuang return 0; 47127793daSHaojian Zhuang } 48127793daSHaojian Zhuang 49127793daSHaojian Zhuang static void hikey_pwr_domain_on_finish(const psci_power_state_t *target_state) 50127793daSHaojian Zhuang { 51127793daSHaojian Zhuang unsigned long mpidr; 52127793daSHaojian Zhuang int cpu, cluster; 53127793daSHaojian Zhuang 54127793daSHaojian Zhuang mpidr = read_mpidr(); 55127793daSHaojian Zhuang cluster = MPIDR_AFFLVL1_VAL(mpidr); 56127793daSHaojian Zhuang cpu = MPIDR_AFFLVL0_VAL(mpidr); 571e54813aSLeo Yan 581e54813aSLeo Yan 59127793daSHaojian Zhuang /* 60127793daSHaojian Zhuang * Enable CCI coherency for this cluster. 61127793daSHaojian Zhuang * No need for locks as no other cpu is active at the moment. 62127793daSHaojian Zhuang */ 631e54813aSLeo Yan if (CLUSTER_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) 64127793daSHaojian Zhuang cci_enable_snoop_dvm_reqs(MPIDR_AFFLVL1_VAL(mpidr)); 65127793daSHaojian Zhuang 66127793daSHaojian Zhuang /* Zero the jump address in the mailbox for this cpu */ 67127793daSHaojian Zhuang hisi_pwrc_set_core_bx_addr(cpu, cluster, 0); 68127793daSHaojian Zhuang 69127793daSHaojian Zhuang /* Program the GIC per-cpu distributor or re-distributor interface */ 70127793daSHaojian Zhuang gicv2_pcpu_distif_init(); 71127793daSHaojian Zhuang /* Enable the GIC cpu interface */ 72127793daSHaojian Zhuang gicv2_cpuif_enable(); 73127793daSHaojian Zhuang } 74127793daSHaojian Zhuang 75127793daSHaojian Zhuang void hikey_pwr_domain_off(const psci_power_state_t *target_state) 76127793daSHaojian Zhuang { 77127793daSHaojian Zhuang unsigned long mpidr; 78127793daSHaojian Zhuang int cpu, cluster; 79127793daSHaojian Zhuang 80127793daSHaojian Zhuang mpidr = read_mpidr(); 81127793daSHaojian Zhuang cluster = MPIDR_AFFLVL1_VAL(mpidr); 82127793daSHaojian Zhuang cpu = MPIDR_AFFLVL0_VAL(mpidr); 831e54813aSLeo Yan 841e54813aSLeo Yan gicv2_cpuif_disable(); 851e54813aSLeo Yan hisi_ipc_cpu_off(cpu, cluster); 861e54813aSLeo Yan 871e54813aSLeo Yan if (CLUSTER_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) { 88127793daSHaojian Zhuang hisi_ipc_spin_lock(HISI_IPC_SEM_CPUIDLE); 89127793daSHaojian Zhuang cci_disable_snoop_dvm_reqs(MPIDR_AFFLVL1_VAL(mpidr)); 90127793daSHaojian Zhuang hisi_ipc_spin_unlock(HISI_IPC_SEM_CPUIDLE); 91127793daSHaojian Zhuang 92127793daSHaojian Zhuang hisi_ipc_cluster_off(cpu, cluster); 93127793daSHaojian Zhuang } 94127793daSHaojian Zhuang } 95127793daSHaojian Zhuang 961e54813aSLeo Yan static void hikey_pwr_domain_suspend(const psci_power_state_t *target_state) 971e54813aSLeo Yan { 981e54813aSLeo Yan u_register_t mpidr = read_mpidr_el1(); 991e54813aSLeo Yan unsigned int cpu = mpidr & MPIDR_CPU_MASK; 1001e54813aSLeo Yan unsigned int cluster = 1011e54813aSLeo Yan (mpidr & MPIDR_CLUSTER_MASK) >> MPIDR_AFFINITY_BITS; 1021e54813aSLeo Yan 1031e54813aSLeo Yan if (CORE_PWR_STATE(target_state) != PLAT_MAX_OFF_STATE) 1041e54813aSLeo Yan return; 1051e54813aSLeo Yan 1061e54813aSLeo Yan if (CORE_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) { 1071e54813aSLeo Yan 1081e54813aSLeo Yan /* Program the jump address for the target cpu */ 1091e54813aSLeo Yan hisi_pwrc_set_core_bx_addr(cpu, cluster, hikey_sec_entrypoint); 1101e54813aSLeo Yan 1111e54813aSLeo Yan gicv2_cpuif_disable(); 1121e54813aSLeo Yan 1131e54813aSLeo Yan if (SYSTEM_PWR_STATE(target_state) != PLAT_MAX_OFF_STATE) 1141e54813aSLeo Yan hisi_ipc_cpu_suspend(cpu, cluster); 1151e54813aSLeo Yan } 1161e54813aSLeo Yan 1171e54813aSLeo Yan /* Perform the common cluster specific operations */ 1181e54813aSLeo Yan if (CLUSTER_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) { 1191e54813aSLeo Yan hisi_ipc_spin_lock(HISI_IPC_SEM_CPUIDLE); 1201e54813aSLeo Yan cci_disable_snoop_dvm_reqs(MPIDR_AFFLVL1_VAL(mpidr)); 1211e54813aSLeo Yan hisi_ipc_spin_unlock(HISI_IPC_SEM_CPUIDLE); 1221e54813aSLeo Yan 1231e54813aSLeo Yan if (SYSTEM_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) { 1241e54813aSLeo Yan hisi_pwrc_set_cluster_wfi(1); 1251e54813aSLeo Yan hisi_pwrc_set_cluster_wfi(0); 1261e54813aSLeo Yan hisi_ipc_psci_system_off(); 1271e54813aSLeo Yan } else 1281e54813aSLeo Yan hisi_ipc_cluster_suspend(cpu, cluster); 1291e54813aSLeo Yan } 1301e54813aSLeo Yan } 1311e54813aSLeo Yan 1321e54813aSLeo Yan static void hikey_pwr_domain_suspend_finish(const psci_power_state_t *target_state) 1331e54813aSLeo Yan { 1341e54813aSLeo Yan unsigned long mpidr; 1351e54813aSLeo Yan unsigned int cluster, cpu; 1361e54813aSLeo Yan 1371e54813aSLeo Yan /* Nothing to be done on waking up from retention from CPU level */ 1381e54813aSLeo Yan if (CORE_PWR_STATE(target_state) != PLAT_MAX_OFF_STATE) 1391e54813aSLeo Yan return; 1401e54813aSLeo Yan 1411e54813aSLeo Yan /* Get the mpidr for this cpu */ 1421e54813aSLeo Yan mpidr = read_mpidr_el1(); 1431e54813aSLeo Yan cluster = (mpidr & MPIDR_CLUSTER_MASK) >> MPIDR_AFF1_SHIFT; 1441e54813aSLeo Yan cpu = mpidr & MPIDR_CPU_MASK; 1451e54813aSLeo Yan 1461e54813aSLeo Yan /* Enable CCI coherency for cluster */ 1471e54813aSLeo Yan if (CLUSTER_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) 1481e54813aSLeo Yan cci_enable_snoop_dvm_reqs(MPIDR_AFFLVL1_VAL(mpidr)); 1491e54813aSLeo Yan 1501e54813aSLeo Yan hisi_pwrc_set_core_bx_addr(cpu, cluster, 0); 1511e54813aSLeo Yan 1521e54813aSLeo Yan if (SYSTEM_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) { 1531e54813aSLeo Yan gicv2_distif_init(); 1541e54813aSLeo Yan gicv2_pcpu_distif_init(); 1551e54813aSLeo Yan gicv2_cpuif_enable(); 1561e54813aSLeo Yan } else { 1571e54813aSLeo Yan gicv2_pcpu_distif_init(); 1581e54813aSLeo Yan gicv2_cpuif_enable(); 1591e54813aSLeo Yan } 1601e54813aSLeo Yan } 1611e54813aSLeo Yan 1621e54813aSLeo Yan static void hikey_get_sys_suspend_power_state(psci_power_state_t *req_state) 1631e54813aSLeo Yan { 1641e54813aSLeo Yan int i; 1651e54813aSLeo Yan 1661e54813aSLeo Yan for (i = MPIDR_AFFLVL0; i <= PLAT_MAX_PWR_LVL; i++) 1671e54813aSLeo Yan req_state->pwr_domain_state[i] = PLAT_MAX_OFF_STATE; 1681e54813aSLeo Yan } 1691e54813aSLeo Yan 1701e54813aSLeo Yan static void __dead2 hikey_system_off(void) 1711e54813aSLeo Yan { 1721e54813aSLeo Yan NOTICE("%s: off system\n", __func__); 1731e54813aSLeo Yan 1741e54813aSLeo Yan /* Pull down GPIO_0_0 to trigger PMIC shutdown */ 1751e54813aSLeo Yan mmio_write_32(0xF8001810, 0x2); /* Pinmux */ 1761e54813aSLeo Yan mmio_write_8(0xF8011400, 1); /* Pin direction */ 1771e54813aSLeo Yan mmio_write_8(0xF8011004, 0); /* Pin output value */ 1781e54813aSLeo Yan 1791e54813aSLeo Yan /* Wait for 2s to power off system by PMIC */ 1801e54813aSLeo Yan sp804_timer_init(SP804_TIMER0_BASE, 10, 192); 1811e54813aSLeo Yan mdelay(2000); 1821e54813aSLeo Yan 1831e54813aSLeo Yan /* 1841e54813aSLeo Yan * PMIC shutdown depends on two conditions: GPIO_0_0 (PWR_HOLD) low, 1851e54813aSLeo Yan * and VBUS_DET < 3.6V. For HiKey, VBUS_DET is connected to VDD_4V2 1861e54813aSLeo Yan * through Jumper 1-2. So, to complete shutdown, user needs to manually 1871e54813aSLeo Yan * remove Jumper 1-2. 1881e54813aSLeo Yan */ 1891e54813aSLeo Yan NOTICE("+------------------------------------------+\n"); 1901e54813aSLeo Yan NOTICE("| IMPORTANT: Remove Jumper 1-2 to shutdown |\n"); 1911e54813aSLeo Yan NOTICE("| DANGER: SoC is still burning. DANGER! |\n"); 1921e54813aSLeo Yan NOTICE("| Board will be reboot to avoid overheat |\n"); 1931e54813aSLeo Yan NOTICE("+------------------------------------------+\n"); 1941e54813aSLeo Yan 1951e54813aSLeo Yan /* Send the system reset request */ 1961e54813aSLeo Yan mmio_write_32(AO_SC_SYS_STAT0, 0x48698284); 1971e54813aSLeo Yan 1981e54813aSLeo Yan wfi(); 1991e54813aSLeo Yan panic(); 2001e54813aSLeo Yan } 2011e54813aSLeo Yan 202127793daSHaojian Zhuang static void __dead2 hikey_system_reset(void) 203127793daSHaojian Zhuang { 204127793daSHaojian Zhuang /* Send the system reset request */ 205127793daSHaojian Zhuang mmio_write_32(AO_SC_SYS_STAT0, 0x48698284); 206127793daSHaojian Zhuang isb(); 207127793daSHaojian Zhuang dsb(); 208127793daSHaojian Zhuang 209127793daSHaojian Zhuang wfi(); 210127793daSHaojian Zhuang panic(); 211127793daSHaojian Zhuang } 212127793daSHaojian Zhuang 213127793daSHaojian Zhuang int hikey_validate_power_state(unsigned int power_state, 214127793daSHaojian Zhuang psci_power_state_t *req_state) 215127793daSHaojian Zhuang { 216127793daSHaojian Zhuang int pstate = psci_get_pstate_type(power_state); 217127793daSHaojian Zhuang int pwr_lvl = psci_get_pstate_pwrlvl(power_state); 218127793daSHaojian Zhuang int i; 219127793daSHaojian Zhuang 220127793daSHaojian Zhuang assert(req_state); 221127793daSHaojian Zhuang 222127793daSHaojian Zhuang if (pwr_lvl > PLAT_MAX_PWR_LVL) 223127793daSHaojian Zhuang return PSCI_E_INVALID_PARAMS; 224127793daSHaojian Zhuang 225127793daSHaojian Zhuang /* Sanity check the requested state */ 226127793daSHaojian Zhuang if (pstate == PSTATE_TYPE_STANDBY) { 227127793daSHaojian Zhuang /* 228127793daSHaojian Zhuang * It's possible to enter standby only on power level 0 229127793daSHaojian Zhuang * Ignore any other power level. 230127793daSHaojian Zhuang */ 231127793daSHaojian Zhuang if (pwr_lvl != MPIDR_AFFLVL0) 232127793daSHaojian Zhuang return PSCI_E_INVALID_PARAMS; 233127793daSHaojian Zhuang 234127793daSHaojian Zhuang req_state->pwr_domain_state[MPIDR_AFFLVL0] = 235127793daSHaojian Zhuang PLAT_MAX_RET_STATE; 236127793daSHaojian Zhuang } else { 237127793daSHaojian Zhuang for (i = MPIDR_AFFLVL0; i <= pwr_lvl; i++) 238127793daSHaojian Zhuang req_state->pwr_domain_state[i] = 239127793daSHaojian Zhuang PLAT_MAX_OFF_STATE; 240127793daSHaojian Zhuang } 241127793daSHaojian Zhuang 242127793daSHaojian Zhuang /* 243127793daSHaojian Zhuang * We expect the 'state id' to be zero. 244127793daSHaojian Zhuang */ 245127793daSHaojian Zhuang if (psci_get_pstate_id(power_state)) 246127793daSHaojian Zhuang return PSCI_E_INVALID_PARAMS; 247127793daSHaojian Zhuang 248127793daSHaojian Zhuang return PSCI_E_SUCCESS; 249127793daSHaojian Zhuang } 250127793daSHaojian Zhuang 251127793daSHaojian Zhuang static int hikey_validate_ns_entrypoint(uintptr_t entrypoint) 252127793daSHaojian Zhuang { 253127793daSHaojian Zhuang /* 254127793daSHaojian Zhuang * Check if the non secure entrypoint lies within the non 255127793daSHaojian Zhuang * secure DRAM. 256127793daSHaojian Zhuang */ 257127793daSHaojian Zhuang if ((entrypoint > DDR_BASE) && (entrypoint < (DDR_BASE + DDR_SIZE))) 258127793daSHaojian Zhuang return PSCI_E_SUCCESS; 259127793daSHaojian Zhuang 260127793daSHaojian Zhuang return PSCI_E_INVALID_ADDRESS; 261127793daSHaojian Zhuang } 262127793daSHaojian Zhuang 263127793daSHaojian Zhuang static const plat_psci_ops_t hikey_psci_ops = { 264127793daSHaojian Zhuang .cpu_standby = NULL, 265127793daSHaojian Zhuang .pwr_domain_on = hikey_pwr_domain_on, 266127793daSHaojian Zhuang .pwr_domain_on_finish = hikey_pwr_domain_on_finish, 267127793daSHaojian Zhuang .pwr_domain_off = hikey_pwr_domain_off, 2681e54813aSLeo Yan .pwr_domain_suspend = hikey_pwr_domain_suspend, 2691e54813aSLeo Yan .pwr_domain_suspend_finish = hikey_pwr_domain_suspend_finish, 2701e54813aSLeo Yan .system_off = hikey_system_off, 271127793daSHaojian Zhuang .system_reset = hikey_system_reset, 272127793daSHaojian Zhuang .validate_power_state = hikey_validate_power_state, 273127793daSHaojian Zhuang .validate_ns_entrypoint = hikey_validate_ns_entrypoint, 2741e54813aSLeo Yan .get_sys_suspend_power_state = hikey_get_sys_suspend_power_state, 275127793daSHaojian Zhuang }; 276127793daSHaojian Zhuang 277127793daSHaojian Zhuang int plat_setup_psci_ops(uintptr_t sec_entrypoint, 278127793daSHaojian Zhuang const plat_psci_ops_t **psci_ops) 279127793daSHaojian Zhuang { 280127793daSHaojian Zhuang hikey_sec_entrypoint = sec_entrypoint; 281127793daSHaojian Zhuang 282127793daSHaojian Zhuang /* 283127793daSHaojian Zhuang * Initialize PSCI ops struct 284127793daSHaojian Zhuang */ 285127793daSHaojian Zhuang *psci_ops = &hikey_psci_ops; 286127793daSHaojian Zhuang return 0; 287127793daSHaojian Zhuang } 288