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