128b02e23SHaojian Zhuang /* 228b02e23SHaojian Zhuang * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. 328b02e23SHaojian Zhuang * 428b02e23SHaojian Zhuang * SPDX-License-Identifier: BSD-3-Clause 528b02e23SHaojian Zhuang */ 628b02e23SHaojian Zhuang 728b02e23SHaojian Zhuang #include <assert.h> 809d40e0eSAntonio Nino Diaz 909d40e0eSAntonio Nino Diaz #include <arch_helpers.h> 1009d40e0eSAntonio Nino Diaz #include <common/debug.h> 1109d40e0eSAntonio Nino Diaz #include <drivers/arm/cci.h> 1209d40e0eSAntonio Nino Diaz #include <drivers/arm/gicv2.h> 1309d40e0eSAntonio Nino Diaz #include <drivers/arm/pl011.h> 14*cfde1870SLeo Yan #include <drivers/arm/pl061_gpio.h> 1509d40e0eSAntonio Nino Diaz #include <drivers/delay_timer.h> 1609d40e0eSAntonio Nino Diaz #include <lib/mmio.h> 1709d40e0eSAntonio Nino Diaz #include <lib/psci/psci.h> 1809d40e0eSAntonio Nino Diaz 1928b02e23SHaojian Zhuang #include <hi3660.h> 2028b02e23SHaojian Zhuang #include <hi3660_crg.h> 2128b02e23SHaojian Zhuang 2209d40e0eSAntonio Nino Diaz #include "drivers/pwrc/hisi_pwrc.h" 2328b02e23SHaojian Zhuang #include "hikey960_def.h" 2428b02e23SHaojian Zhuang #include "hikey960_private.h" 2528b02e23SHaojian Zhuang 2628b02e23SHaojian Zhuang #define CORE_PWR_STATE(state) \ 2728b02e23SHaojian Zhuang ((state)->pwr_domain_state[MPIDR_AFFLVL0]) 2828b02e23SHaojian Zhuang #define CLUSTER_PWR_STATE(state) \ 2928b02e23SHaojian Zhuang ((state)->pwr_domain_state[MPIDR_AFFLVL1]) 3028b02e23SHaojian Zhuang #define SYSTEM_PWR_STATE(state) \ 3128b02e23SHaojian Zhuang ((state)->pwr_domain_state[PLAT_MAX_PWR_LVL]) 3228b02e23SHaojian Zhuang 3328b02e23SHaojian Zhuang #define DMAC_GLB_REG_SEC 0x694 3428b02e23SHaojian Zhuang #define AXI_CONF_BASE 0x820 3528b02e23SHaojian Zhuang 36135d713cSHaojian Zhuang static unsigned int uart_base; 37f695e1e0SAndre Przywara static console_t console; 3828b02e23SHaojian Zhuang static uintptr_t hikey960_sec_entrypoint; 3928b02e23SHaojian Zhuang 4028b02e23SHaojian Zhuang static void hikey960_pwr_domain_standby(plat_local_state_t cpu_state) 4128b02e23SHaojian Zhuang { 4228b02e23SHaojian Zhuang unsigned long scr; 4328b02e23SHaojian Zhuang 4428b02e23SHaojian Zhuang scr = read_scr_el3(); 4528b02e23SHaojian Zhuang 4628b02e23SHaojian Zhuang /* Enable Physical IRQ and FIQ to wake the CPU */ 4728b02e23SHaojian Zhuang write_scr_el3(scr | SCR_IRQ_BIT | SCR_FIQ_BIT); 4828b02e23SHaojian Zhuang 494c8a5787SLeo Yan /* Add barrier before CPU enter WFI state */ 504c8a5787SLeo Yan isb(); 514c8a5787SLeo Yan dsb(); 5228b02e23SHaojian Zhuang wfi(); 5328b02e23SHaojian Zhuang 5428b02e23SHaojian Zhuang /* 5528b02e23SHaojian Zhuang * Restore SCR to the original value, synchronisazion of 5628b02e23SHaojian Zhuang * scr_el3 is done by eret while el3_exit to save some 5728b02e23SHaojian Zhuang * execution cycles. 5828b02e23SHaojian Zhuang */ 5928b02e23SHaojian Zhuang write_scr_el3(scr); 6028b02e23SHaojian Zhuang } 6128b02e23SHaojian Zhuang 6228b02e23SHaojian Zhuang static int hikey960_pwr_domain_on(u_register_t mpidr) 6328b02e23SHaojian Zhuang { 6428b02e23SHaojian Zhuang unsigned int core = mpidr & MPIDR_CPU_MASK; 6528b02e23SHaojian Zhuang unsigned int cluster = 6628b02e23SHaojian Zhuang (mpidr & MPIDR_CLUSTER_MASK) >> MPIDR_AFFINITY_BITS; 6728b02e23SHaojian Zhuang int cluster_stat = cluster_is_powered_on(cluster); 6828b02e23SHaojian Zhuang 6928b02e23SHaojian Zhuang hisi_set_cpu_boot_flag(cluster, core); 7028b02e23SHaojian Zhuang 7128b02e23SHaojian Zhuang mmio_write_32(CRG_REG_BASE + CRG_RVBAR(cluster, core), 7228b02e23SHaojian Zhuang hikey960_sec_entrypoint >> 2); 7328b02e23SHaojian Zhuang 7428b02e23SHaojian Zhuang if (cluster_stat) 7528b02e23SHaojian Zhuang hisi_powerup_core(cluster, core); 7628b02e23SHaojian Zhuang else 7728b02e23SHaojian Zhuang hisi_powerup_cluster(cluster, core); 7828b02e23SHaojian Zhuang 7928b02e23SHaojian Zhuang return PSCI_E_SUCCESS; 8028b02e23SHaojian Zhuang } 8128b02e23SHaojian Zhuang 8228b02e23SHaojian Zhuang static void 8328b02e23SHaojian Zhuang hikey960_pwr_domain_on_finish(const psci_power_state_t *target_state) 8428b02e23SHaojian Zhuang { 8528b02e23SHaojian Zhuang if (CLUSTER_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) 8628b02e23SHaojian Zhuang cci_enable_snoop_dvm_reqs(MPIDR_AFFLVL1_VAL(read_mpidr_el1())); 8728b02e23SHaojian Zhuang 8828b02e23SHaojian Zhuang gicv2_pcpu_distif_init(); 8928b02e23SHaojian Zhuang gicv2_cpuif_enable(); 9028b02e23SHaojian Zhuang } 9128b02e23SHaojian Zhuang 9228b02e23SHaojian Zhuang void hikey960_pwr_domain_off(const psci_power_state_t *target_state) 9328b02e23SHaojian Zhuang { 9428b02e23SHaojian Zhuang unsigned long mpidr = read_mpidr_el1(); 9528b02e23SHaojian Zhuang unsigned int core = mpidr & MPIDR_CPU_MASK; 9628b02e23SHaojian Zhuang unsigned int cluster = 9728b02e23SHaojian Zhuang (mpidr & MPIDR_CLUSTER_MASK) >> MPIDR_AFFINITY_BITS; 9828b02e23SHaojian Zhuang 9928b02e23SHaojian Zhuang clr_ex(); 10028b02e23SHaojian Zhuang isb(); 10128b02e23SHaojian Zhuang dsbsy(); 10228b02e23SHaojian Zhuang 10328b02e23SHaojian Zhuang gicv2_cpuif_disable(); 10428b02e23SHaojian Zhuang 10528b02e23SHaojian Zhuang hisi_clear_cpu_boot_flag(cluster, core); 10628b02e23SHaojian Zhuang hisi_powerdn_core(cluster, core); 10728b02e23SHaojian Zhuang 10828b02e23SHaojian Zhuang /* check if any core is powered up */ 1090aedca71SLeo Yan if (hisi_test_cpu_down(cluster, core)) { 11028b02e23SHaojian Zhuang 11128b02e23SHaojian Zhuang cci_disable_snoop_dvm_reqs(MPIDR_AFFLVL1_VAL(read_mpidr_el1())); 11228b02e23SHaojian Zhuang 11328b02e23SHaojian Zhuang isb(); 11428b02e23SHaojian Zhuang dsbsy(); 11528b02e23SHaojian Zhuang 11628b02e23SHaojian Zhuang hisi_powerdn_cluster(cluster, core); 11728b02e23SHaojian Zhuang } 11828b02e23SHaojian Zhuang } 11928b02e23SHaojian Zhuang 120*cfde1870SLeo Yan static void __dead2 hikey960_system_off(void) 121*cfde1870SLeo Yan { 122*cfde1870SLeo Yan gpio_set_direction(176, GPIO_DIR_OUT); 123*cfde1870SLeo Yan gpio_set_value(176, GPIO_LEVEL_LOW); 124*cfde1870SLeo Yan panic(); 125*cfde1870SLeo Yan } 126*cfde1870SLeo Yan 12728b02e23SHaojian Zhuang static void __dead2 hikey960_system_reset(void) 12828b02e23SHaojian Zhuang { 1297dcef5ebSHaojian Zhuang dsb(); 1307dcef5ebSHaojian Zhuang isb(); 1317dcef5ebSHaojian Zhuang mdelay(2000); 13228b02e23SHaojian Zhuang mmio_write_32(SCTRL_SCPEREN1_REG, 13328b02e23SHaojian Zhuang SCPEREN1_WAIT_DDR_SELFREFRESH_DONE_BYPASS); 13428b02e23SHaojian Zhuang mmio_write_32(SCTRL_SCSYSSTAT_REG, 0xdeadbeef); 13528b02e23SHaojian Zhuang panic(); 13628b02e23SHaojian Zhuang } 13728b02e23SHaojian Zhuang 13828b02e23SHaojian Zhuang int hikey960_validate_power_state(unsigned int power_state, 13928b02e23SHaojian Zhuang psci_power_state_t *req_state) 14028b02e23SHaojian Zhuang { 141e1b27425SLeo Yan unsigned int pstate = psci_get_pstate_type(power_state); 142e1b27425SLeo Yan unsigned int pwr_lvl = psci_get_pstate_pwrlvl(power_state); 14328b02e23SHaojian Zhuang int i; 14428b02e23SHaojian Zhuang 14528b02e23SHaojian Zhuang assert(req_state); 14628b02e23SHaojian Zhuang 147e1b27425SLeo Yan if (pwr_lvl > PLAT_MAX_PWR_LVL) 14828b02e23SHaojian Zhuang return PSCI_E_INVALID_PARAMS; 14928b02e23SHaojian Zhuang 150e1b27425SLeo Yan /* Sanity check the requested state */ 151e1b27425SLeo Yan if (pstate == PSTATE_TYPE_STANDBY) { 152e1b27425SLeo Yan /* 153e1b27425SLeo Yan * It's possible to enter standby only on power level 0 154e1b27425SLeo Yan * Ignore any other power level. 155e1b27425SLeo Yan */ 156e1b27425SLeo Yan if (pwr_lvl != MPIDR_AFFLVL0) 157e1b27425SLeo Yan return PSCI_E_INVALID_PARAMS; 158fdae60b6SLeo Yan 159e1b27425SLeo Yan req_state->pwr_domain_state[MPIDR_AFFLVL0] = 160e1b27425SLeo Yan PLAT_MAX_RET_STATE; 161e1b27425SLeo Yan } else { 162e1b27425SLeo Yan for (i = MPIDR_AFFLVL0; i <= pwr_lvl; i++) 163e1b27425SLeo Yan req_state->pwr_domain_state[i] = 164e1b27425SLeo Yan PLAT_MAX_OFF_STATE; 165fdae60b6SLeo Yan } 166fdae60b6SLeo Yan 167e1b27425SLeo Yan /* 168e1b27425SLeo Yan * We expect the 'state id' to be zero. 169e1b27425SLeo Yan */ 170e1b27425SLeo Yan if (psci_get_pstate_id(power_state)) 171e1b27425SLeo Yan return PSCI_E_INVALID_PARAMS; 172e1b27425SLeo Yan 17328b02e23SHaojian Zhuang return PSCI_E_SUCCESS; 17428b02e23SHaojian Zhuang } 17528b02e23SHaojian Zhuang 17628b02e23SHaojian Zhuang static int hikey960_validate_ns_entrypoint(uintptr_t entrypoint) 17728b02e23SHaojian Zhuang { 17828b02e23SHaojian Zhuang /* 17928b02e23SHaojian Zhuang * Check if the non secure entrypoint lies within the non 18028b02e23SHaojian Zhuang * secure DRAM. 18128b02e23SHaojian Zhuang */ 18228b02e23SHaojian Zhuang if ((entrypoint > DDR_BASE) && (entrypoint < (DDR_BASE + DDR_SIZE))) 18328b02e23SHaojian Zhuang return PSCI_E_SUCCESS; 18428b02e23SHaojian Zhuang 18528b02e23SHaojian Zhuang return PSCI_E_INVALID_ADDRESS; 18628b02e23SHaojian Zhuang } 18728b02e23SHaojian Zhuang 18828b02e23SHaojian Zhuang static void hikey960_pwr_domain_suspend(const psci_power_state_t *target_state) 18928b02e23SHaojian Zhuang { 19028b02e23SHaojian Zhuang u_register_t mpidr = read_mpidr_el1(); 19128b02e23SHaojian Zhuang unsigned int core = mpidr & MPIDR_CPU_MASK; 19228b02e23SHaojian Zhuang unsigned int cluster = 19328b02e23SHaojian Zhuang (mpidr & MPIDR_CLUSTER_MASK) >> MPIDR_AFFINITY_BITS; 19428b02e23SHaojian Zhuang 19528b02e23SHaojian Zhuang if (CORE_PWR_STATE(target_state) != PLAT_MAX_OFF_STATE) 19628b02e23SHaojian Zhuang return; 19728b02e23SHaojian Zhuang 19828b02e23SHaojian Zhuang if (CORE_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) { 19928b02e23SHaojian Zhuang clr_ex(); 20028b02e23SHaojian Zhuang isb(); 20128b02e23SHaojian Zhuang dsbsy(); 20228b02e23SHaojian Zhuang 20328b02e23SHaojian Zhuang gicv2_cpuif_disable(); 20428b02e23SHaojian Zhuang 20528b02e23SHaojian Zhuang hisi_cpuidle_lock(cluster, core); 20628b02e23SHaojian Zhuang hisi_set_cpuidle_flag(cluster, core); 20728b02e23SHaojian Zhuang hisi_cpuidle_unlock(cluster, core); 20828b02e23SHaojian Zhuang 20928b02e23SHaojian Zhuang mmio_write_32(CRG_REG_BASE + CRG_RVBAR(cluster, core), 21028b02e23SHaojian Zhuang hikey960_sec_entrypoint >> 2); 21128b02e23SHaojian Zhuang 21228b02e23SHaojian Zhuang hisi_enter_core_idle(cluster, core); 21328b02e23SHaojian Zhuang } 21428b02e23SHaojian Zhuang 21528b02e23SHaojian Zhuang /* Perform the common cluster specific operations */ 21628b02e23SHaojian Zhuang if (CLUSTER_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) { 21728b02e23SHaojian Zhuang hisi_cpuidle_lock(cluster, core); 21828b02e23SHaojian Zhuang hisi_disable_pdc(cluster); 21928b02e23SHaojian Zhuang 22028b02e23SHaojian Zhuang /* check if any core is powered up */ 22128b02e23SHaojian Zhuang if (hisi_test_pwrdn_allcores(cluster, core)) { 22228b02e23SHaojian Zhuang 22328b02e23SHaojian Zhuang cci_disable_snoop_dvm_reqs(MPIDR_AFFLVL1_VAL(mpidr)); 22428b02e23SHaojian Zhuang 22528b02e23SHaojian Zhuang isb(); 22628b02e23SHaojian Zhuang dsbsy(); 22728b02e23SHaojian Zhuang 22828b02e23SHaojian Zhuang /* mask the pdc wakeup irq, then 22928b02e23SHaojian Zhuang * enable pdc to power down the core 23028b02e23SHaojian Zhuang */ 23128b02e23SHaojian Zhuang hisi_pdc_mask_cluster_wakeirq(cluster); 23228b02e23SHaojian Zhuang hisi_enable_pdc(cluster); 23328b02e23SHaojian Zhuang 23428b02e23SHaojian Zhuang hisi_cpuidle_unlock(cluster, core); 23528b02e23SHaojian Zhuang 23628b02e23SHaojian Zhuang /* check the SR flag bit to determine 23728b02e23SHaojian Zhuang * CLUSTER_IDLE_IPC or AP_SR_IPC to send 23828b02e23SHaojian Zhuang */ 2397d76df7dSWei Yu if (hisi_test_ap_suspend_flag()) 24028b02e23SHaojian Zhuang hisi_enter_ap_suspend(cluster, core); 24128b02e23SHaojian Zhuang else 24228b02e23SHaojian Zhuang hisi_enter_cluster_idle(cluster, core); 24328b02e23SHaojian Zhuang } else { 24428b02e23SHaojian Zhuang /* enable pdc */ 24528b02e23SHaojian Zhuang hisi_enable_pdc(cluster); 24628b02e23SHaojian Zhuang hisi_cpuidle_unlock(cluster, core); 24728b02e23SHaojian Zhuang } 24828b02e23SHaojian Zhuang } 24928b02e23SHaojian Zhuang } 25028b02e23SHaojian Zhuang 25128b02e23SHaojian Zhuang static void hikey960_sr_dma_reinit(void) 25228b02e23SHaojian Zhuang { 25328b02e23SHaojian Zhuang unsigned int ctr = 0; 25428b02e23SHaojian Zhuang 25528b02e23SHaojian Zhuang mmio_write_32(DMAC_BASE + DMAC_GLB_REG_SEC, 0x3); 25628b02e23SHaojian Zhuang 25728b02e23SHaojian Zhuang /* 1~15 channel is set non_secure */ 25828b02e23SHaojian Zhuang for (ctr = 1; ctr <= 15; ctr++) 25928b02e23SHaojian Zhuang mmio_write_32(DMAC_BASE + AXI_CONF_BASE + ctr * (0x40), 26028b02e23SHaojian Zhuang (1 << 6) | (1 << 18)); 26128b02e23SHaojian Zhuang } 26228b02e23SHaojian Zhuang 26328b02e23SHaojian Zhuang static void 26428b02e23SHaojian Zhuang hikey960_pwr_domain_suspend_finish(const psci_power_state_t *target_state) 26528b02e23SHaojian Zhuang { 26628b02e23SHaojian Zhuang unsigned long mpidr = read_mpidr_el1(); 2674af7fcb8STao Wang unsigned int core = mpidr & MPIDR_CPU_MASK; 26828b02e23SHaojian Zhuang unsigned int cluster = 26928b02e23SHaojian Zhuang (mpidr & MPIDR_CLUSTER_MASK) >> MPIDR_AFFINITY_BITS; 27028b02e23SHaojian Zhuang 27128b02e23SHaojian Zhuang /* Nothing to be done on waking up from retention from CPU level */ 27228b02e23SHaojian Zhuang if (CORE_PWR_STATE(target_state) != PLAT_MAX_OFF_STATE) 27328b02e23SHaojian Zhuang return; 27428b02e23SHaojian Zhuang 2754af7fcb8STao Wang hisi_cpuidle_lock(cluster, core); 2764af7fcb8STao Wang hisi_clear_cpuidle_flag(cluster, core); 2774af7fcb8STao Wang hisi_cpuidle_unlock(cluster, core); 2784af7fcb8STao Wang 2797d76df7dSWei Yu if (hisi_test_ap_suspend_flag()) { 28028b02e23SHaojian Zhuang hikey960_sr_dma_reinit(); 28128b02e23SHaojian Zhuang gicv2_cpuif_enable(); 2825189ea27SJerome Forissier console_pl011_register(uart_base, PL011_UART_CLK_IN_HZ, 2835189ea27SJerome Forissier PL011_BAUDRATE, &console); 28428b02e23SHaojian Zhuang } 28528b02e23SHaojian Zhuang 28628b02e23SHaojian Zhuang hikey960_pwr_domain_on_finish(target_state); 28728b02e23SHaojian Zhuang } 28828b02e23SHaojian Zhuang 28928b02e23SHaojian Zhuang static void hikey960_get_sys_suspend_power_state(psci_power_state_t *req_state) 29028b02e23SHaojian Zhuang { 29128b02e23SHaojian Zhuang int i; 29228b02e23SHaojian Zhuang 29328b02e23SHaojian Zhuang for (i = MPIDR_AFFLVL0; i <= PLAT_MAX_PWR_LVL; i++) 29428b02e23SHaojian Zhuang req_state->pwr_domain_state[i] = PLAT_MAX_OFF_STATE; 29528b02e23SHaojian Zhuang } 29628b02e23SHaojian Zhuang 29728b02e23SHaojian Zhuang static const plat_psci_ops_t hikey960_psci_ops = { 29828b02e23SHaojian Zhuang .cpu_standby = hikey960_pwr_domain_standby, 29928b02e23SHaojian Zhuang .pwr_domain_on = hikey960_pwr_domain_on, 30028b02e23SHaojian Zhuang .pwr_domain_on_finish = hikey960_pwr_domain_on_finish, 30128b02e23SHaojian Zhuang .pwr_domain_off = hikey960_pwr_domain_off, 30228b02e23SHaojian Zhuang .pwr_domain_suspend = hikey960_pwr_domain_suspend, 30328b02e23SHaojian Zhuang .pwr_domain_suspend_finish = hikey960_pwr_domain_suspend_finish, 304*cfde1870SLeo Yan .system_off = hikey960_system_off, 30528b02e23SHaojian Zhuang .system_reset = hikey960_system_reset, 30628b02e23SHaojian Zhuang .validate_power_state = hikey960_validate_power_state, 30728b02e23SHaojian Zhuang .validate_ns_entrypoint = hikey960_validate_ns_entrypoint, 30828b02e23SHaojian Zhuang .get_sys_suspend_power_state = hikey960_get_sys_suspend_power_state, 30928b02e23SHaojian Zhuang }; 31028b02e23SHaojian Zhuang 31128b02e23SHaojian Zhuang int plat_setup_psci_ops(uintptr_t sec_entrypoint, 31228b02e23SHaojian Zhuang const plat_psci_ops_t **psci_ops) 31328b02e23SHaojian Zhuang { 314135d713cSHaojian Zhuang unsigned int id = 0; 315135d713cSHaojian Zhuang int ret; 316135d713cSHaojian Zhuang 317135d713cSHaojian Zhuang ret = hikey960_read_boardid(&id); 318135d713cSHaojian Zhuang if (ret == 0) { 319135d713cSHaojian Zhuang if (id == 5300U) 320135d713cSHaojian Zhuang uart_base = PL011_UART5_BASE; 321135d713cSHaojian Zhuang else 322135d713cSHaojian Zhuang uart_base = PL011_UART6_BASE; 323135d713cSHaojian Zhuang } else { 324135d713cSHaojian Zhuang uart_base = PL011_UART6_BASE; 325135d713cSHaojian Zhuang } 326135d713cSHaojian Zhuang 32728b02e23SHaojian Zhuang hikey960_sec_entrypoint = sec_entrypoint; 32828b02e23SHaojian Zhuang 32928b02e23SHaojian Zhuang INFO("%s: sec_entrypoint=0x%lx\n", __func__, 33028b02e23SHaojian Zhuang (unsigned long)hikey960_sec_entrypoint); 33128b02e23SHaojian Zhuang 33228b02e23SHaojian Zhuang /* 33328b02e23SHaojian Zhuang * Initialize PSCI ops struct 33428b02e23SHaojian Zhuang */ 33528b02e23SHaojian Zhuang *psci_ops = &hikey960_psci_ops; 33628b02e23SHaojian Zhuang return 0; 33728b02e23SHaojian Zhuang } 338