10bc18309SAnson Huang /* 20bc18309SAnson Huang * Copyright (c) 2015-2018, ARM Limited and Contributors. All rights reserved. 30bc18309SAnson Huang * 40bc18309SAnson Huang * SPDX-License-Identifier: BSD-3-Clause 50bc18309SAnson Huang */ 60bc18309SAnson Huang 709d40e0eSAntonio Nino Diaz #include <stdbool.h> 809d40e0eSAntonio Nino Diaz 90bc18309SAnson Huang #include <arch.h> 100bc18309SAnson Huang #include <arch_helpers.h> 1109d40e0eSAntonio Nino Diaz #include <common/debug.h> 1209d40e0eSAntonio Nino Diaz #include <drivers/arm/gicv3.h> 1309d40e0eSAntonio Nino Diaz #include <lib/mmio.h> 1409d40e0eSAntonio Nino Diaz #include <lib/psci/psci.h> 1509d40e0eSAntonio Nino Diaz 160bc18309SAnson Huang #include <plat_imx8.h> 170bc18309SAnson Huang #include <sci/sci.h> 180bc18309SAnson Huang 19e6cf7a46SAnson Huang #include "../../common/sci/imx8_mu.h" 20e6cf7a46SAnson Huang 21*f4b8470fSBoyan Karatotev static const int ap_core_index[PLATFORM_CORE_COUNT] = { 220bc18309SAnson Huang SC_R_A35_0, SC_R_A35_1, SC_R_A35_2, SC_R_A35_3 230bc18309SAnson Huang }; 240bc18309SAnson Huang 25e6cf7a46SAnson Huang /* save gic dist/redist context when GIC is power down */ 26e6cf7a46SAnson Huang static struct plat_gic_ctx imx_gicv3_ctx; 27e6cf7a46SAnson Huang static unsigned int gpt_lpcg, gpt_reg[2]; 28e6cf7a46SAnson Huang 29e6cf7a46SAnson Huang static void imx_enable_irqstr_wakeup(void) 30e6cf7a46SAnson Huang { 31e6cf7a46SAnson Huang uint32_t irq_mask; 32e6cf7a46SAnson Huang gicv3_dist_ctx_t *dist_ctx = &imx_gicv3_ctx.dist_ctx; 33e6cf7a46SAnson Huang 34e6cf7a46SAnson Huang /* put IRQSTR into ON mode */ 35e6cf7a46SAnson Huang sc_pm_set_resource_power_mode(ipc_handle, SC_R_IRQSTR_SCU2, SC_PM_PW_MODE_ON); 36e6cf7a46SAnson Huang 37e6cf7a46SAnson Huang /* enable the irqsteer to handle wakeup irq */ 38e6cf7a46SAnson Huang mmio_write_32(IMX_WUP_IRQSTR_BASE, 0x1); 39e6cf7a46SAnson Huang for (int i = 0; i < 15; i++) { 40e6cf7a46SAnson Huang irq_mask = dist_ctx->gicd_isenabler[i]; 41e6cf7a46SAnson Huang mmio_write_32(IMX_WUP_IRQSTR_BASE + 0x3c - 0x4 * i, irq_mask); 42e6cf7a46SAnson Huang } 43e6cf7a46SAnson Huang 44e6cf7a46SAnson Huang /* set IRQSTR low power mode */ 45e6cf7a46SAnson Huang if (imx_is_wakeup_src_irqsteer()) 46e6cf7a46SAnson Huang sc_pm_set_resource_power_mode(ipc_handle, SC_R_IRQSTR_SCU2, SC_PM_PW_MODE_STBY); 47e6cf7a46SAnson Huang else 48e6cf7a46SAnson Huang sc_pm_set_resource_power_mode(ipc_handle, SC_R_IRQSTR_SCU2, SC_PM_PW_MODE_OFF); 49e6cf7a46SAnson Huang } 50e6cf7a46SAnson Huang 51e6cf7a46SAnson Huang static void imx_disable_irqstr_wakeup(void) 52e6cf7a46SAnson Huang { 53e6cf7a46SAnson Huang /* Put IRQSTEER back to ON mode */ 54e6cf7a46SAnson Huang sc_pm_set_resource_power_mode(ipc_handle, SC_R_IRQSTR_SCU2, SC_PM_PW_MODE_ON); 55e6cf7a46SAnson Huang 56e6cf7a46SAnson Huang /* disable the irqsteer */ 57e6cf7a46SAnson Huang mmio_write_32(IMX_WUP_IRQSTR_BASE, 0x0); 58e6cf7a46SAnson Huang for (int i = 0; i < 16; i++) 59e6cf7a46SAnson Huang mmio_write_32(IMX_WUP_IRQSTR_BASE + 0x4 + 0x4 * i, 0x0); 60e6cf7a46SAnson Huang 61e6cf7a46SAnson Huang /* Put IRQSTEER into OFF mode */ 62e6cf7a46SAnson Huang sc_pm_set_resource_power_mode(ipc_handle, SC_R_IRQSTR_SCU2, SC_PM_PW_MODE_OFF); 63e6cf7a46SAnson Huang } 64e6cf7a46SAnson Huang 650bc18309SAnson Huang int imx_pwr_domain_on(u_register_t mpidr) 660bc18309SAnson Huang { 670bc18309SAnson Huang int ret = PSCI_E_SUCCESS; 680bc18309SAnson Huang unsigned int cpu_id; 690bc18309SAnson Huang 700bc18309SAnson Huang cpu_id = MPIDR_AFFLVL0_VAL(mpidr); 710bc18309SAnson Huang 7239b6cc66SAntonio Nino Diaz printf("imx_pwr_domain_on cpu_id %d\n", cpu_id); 730bc18309SAnson Huang 740bc18309SAnson Huang if (sc_pm_set_resource_power_mode(ipc_handle, ap_core_index[cpu_id], 750bc18309SAnson Huang SC_PM_PW_MODE_ON) != SC_ERR_NONE) { 760bc18309SAnson Huang ERROR("core %d power on failed!\n", cpu_id); 770bc18309SAnson Huang ret = PSCI_E_INTERN_FAIL; 780bc18309SAnson Huang } 790bc18309SAnson Huang 800bc18309SAnson Huang if (sc_pm_cpu_start(ipc_handle, ap_core_index[cpu_id], 810bc18309SAnson Huang true, BL31_BASE) != SC_ERR_NONE) { 820bc18309SAnson Huang ERROR("boot core %d failed!\n", cpu_id); 830bc18309SAnson Huang ret = PSCI_E_INTERN_FAIL; 840bc18309SAnson Huang } 850bc18309SAnson Huang 860bc18309SAnson Huang return ret; 870bc18309SAnson Huang } 880bc18309SAnson Huang 890bc18309SAnson Huang void imx_pwr_domain_on_finish(const psci_power_state_t *target_state) 900bc18309SAnson Huang { 910bc18309SAnson Huang plat_gic_pcpu_init(); 920bc18309SAnson Huang plat_gic_cpuif_enable(); 930bc18309SAnson Huang } 940bc18309SAnson Huang 950bc18309SAnson Huang int imx_validate_ns_entrypoint(uintptr_t ns_entrypoint) 960bc18309SAnson Huang { 970bc18309SAnson Huang return PSCI_E_SUCCESS; 980bc18309SAnson Huang } 990bc18309SAnson Huang 1003260f5c7SAnson Huang void imx_pwr_domain_off(const psci_power_state_t *target_state) 1013260f5c7SAnson Huang { 1023260f5c7SAnson Huang u_register_t mpidr = read_mpidr_el1(); 1033260f5c7SAnson Huang unsigned int cpu_id = MPIDR_AFFLVL0_VAL(mpidr); 1043260f5c7SAnson Huang 1053260f5c7SAnson Huang plat_gic_cpuif_disable(); 1063260f5c7SAnson Huang sc_pm_req_cpu_low_power_mode(ipc_handle, ap_core_index[cpu_id], 1073260f5c7SAnson Huang SC_PM_PW_MODE_OFF, SC_PM_WAKE_SRC_NONE); 10839b6cc66SAntonio Nino Diaz printf("turn off core:%d\n", cpu_id); 1093260f5c7SAnson Huang } 1103260f5c7SAnson Huang 111762688bfSAnson Huang void imx_domain_suspend(const psci_power_state_t *target_state) 112762688bfSAnson Huang { 113762688bfSAnson Huang u_register_t mpidr = read_mpidr_el1(); 114762688bfSAnson Huang unsigned int cpu_id = MPIDR_AFFLVL0_VAL(mpidr); 115762688bfSAnson Huang 116e6cf7a46SAnson Huang if (is_local_state_off(target_state->pwr_domain_state[MPIDR_AFFLVL0])) { 117762688bfSAnson Huang plat_gic_cpuif_disable(); 118e6cf7a46SAnson Huang sc_pm_set_cpu_resume(ipc_handle, ap_core_index[cpu_id], true, BL31_BASE); 119762688bfSAnson Huang sc_pm_req_cpu_low_power_mode(ipc_handle, ap_core_index[cpu_id], 120762688bfSAnson Huang SC_PM_PW_MODE_OFF, SC_PM_WAKE_SRC_GIC); 121e6cf7a46SAnson Huang } else { 122e6cf7a46SAnson Huang dsb(); 123e6cf7a46SAnson Huang write_scr_el3(read_scr_el3() | SCR_FIQ_BIT); 124e6cf7a46SAnson Huang isb(); 125e6cf7a46SAnson Huang } 126e6cf7a46SAnson Huang 127e6cf7a46SAnson Huang if (is_local_state_off(target_state->pwr_domain_state[MPIDR_AFFLVL1])) 128e6cf7a46SAnson Huang sc_pm_req_low_power_mode(ipc_handle, SC_R_A35, SC_PM_PW_MODE_OFF); 129e6cf7a46SAnson Huang 130e6cf7a46SAnson Huang if (is_local_state_retn(target_state->pwr_domain_state[PLAT_MAX_PWR_LVL])) { 131e6cf7a46SAnson Huang plat_gic_cpuif_disable(); 132e6cf7a46SAnson Huang 133e6cf7a46SAnson Huang /* save gic context */ 134e6cf7a46SAnson Huang plat_gic_save(cpu_id, &imx_gicv3_ctx); 135e6cf7a46SAnson Huang /* enable the irqsteer for wakeup */ 136e6cf7a46SAnson Huang imx_enable_irqstr_wakeup(); 137e6cf7a46SAnson Huang 138e6cf7a46SAnson Huang /* Save GPT clock and registers, then turn off its power */ 139e6cf7a46SAnson Huang gpt_lpcg = mmio_read_32(IMX_GPT0_LPCG_BASE); 140e6cf7a46SAnson Huang gpt_reg[0] = mmio_read_32(IMX_GPT0_BASE); 141e6cf7a46SAnson Huang gpt_reg[1] = mmio_read_32(IMX_GPT0_BASE + 0x4); 142e6cf7a46SAnson Huang sc_pm_set_resource_power_mode(ipc_handle, SC_R_GPT_0, SC_PM_PW_MODE_OFF); 143e6cf7a46SAnson Huang 144e6cf7a46SAnson Huang sc_pm_req_low_power_mode(ipc_handle, SC_R_A35, SC_PM_PW_MODE_OFF); 145e6cf7a46SAnson Huang sc_pm_req_sys_if_power_mode(ipc_handle, SC_R_A35, SC_PM_SYS_IF_DDR, 146e6cf7a46SAnson Huang SC_PM_PW_MODE_ON, SC_PM_PW_MODE_OFF); 147e6cf7a46SAnson Huang sc_pm_req_sys_if_power_mode(ipc_handle, SC_R_A35, SC_PM_SYS_IF_MU, 148e6cf7a46SAnson Huang SC_PM_PW_MODE_ON, SC_PM_PW_MODE_OFF); 149e6cf7a46SAnson Huang sc_pm_req_sys_if_power_mode(ipc_handle, SC_R_A35, SC_PM_SYS_IF_INTERCONNECT, 150e6cf7a46SAnson Huang SC_PM_PW_MODE_ON, SC_PM_PW_MODE_OFF); 151e6cf7a46SAnson Huang 152e6cf7a46SAnson Huang /* Put GIC in OFF mode. */ 153e6cf7a46SAnson Huang sc_pm_set_resource_power_mode(ipc_handle, SC_R_GIC, SC_PM_PW_MODE_OFF); 154e6cf7a46SAnson Huang sc_pm_set_cpu_resume(ipc_handle, ap_core_index[cpu_id], true, BL31_BASE); 155e6cf7a46SAnson Huang if (imx_is_wakeup_src_irqsteer()) 156e6cf7a46SAnson Huang sc_pm_req_cpu_low_power_mode(ipc_handle, ap_core_index[cpu_id], 157e6cf7a46SAnson Huang SC_PM_PW_MODE_OFF, SC_PM_WAKE_SRC_IRQSTEER); 158e6cf7a46SAnson Huang else 159e6cf7a46SAnson Huang sc_pm_req_cpu_low_power_mode(ipc_handle, ap_core_index[cpu_id], 160e6cf7a46SAnson Huang SC_PM_PW_MODE_OFF, SC_PM_WAKE_SRC_SCU); 161e6cf7a46SAnson Huang } 162762688bfSAnson Huang } 163762688bfSAnson Huang 164762688bfSAnson Huang void imx_domain_suspend_finish(const psci_power_state_t *target_state) 165762688bfSAnson Huang { 166762688bfSAnson Huang u_register_t mpidr = read_mpidr_el1(); 167762688bfSAnson Huang unsigned int cpu_id = MPIDR_AFFLVL0_VAL(mpidr); 168762688bfSAnson Huang 169e6cf7a46SAnson Huang if (is_local_state_retn(target_state->pwr_domain_state[PLAT_MAX_PWR_LVL])) { 170e6cf7a46SAnson Huang MU_Resume(SC_IPC_BASE); 171e6cf7a46SAnson Huang 172e6cf7a46SAnson Huang sc_pm_req_low_power_mode(ipc_handle, ap_core_index[cpu_id], SC_PM_PW_MODE_ON); 173e6cf7a46SAnson Huang sc_pm_req_cpu_low_power_mode(ipc_handle, ap_core_index[cpu_id], 174e6cf7a46SAnson Huang SC_PM_PW_MODE_ON, SC_PM_WAKE_SRC_GIC); 175e6cf7a46SAnson Huang 176e6cf7a46SAnson Huang /* Put GIC back to high power mode. */ 177e6cf7a46SAnson Huang sc_pm_set_resource_power_mode(ipc_handle, SC_R_GIC, SC_PM_PW_MODE_ON); 178e6cf7a46SAnson Huang 179e6cf7a46SAnson Huang /* restore gic context */ 180e6cf7a46SAnson Huang plat_gic_restore(cpu_id, &imx_gicv3_ctx); 181e6cf7a46SAnson Huang 182e6cf7a46SAnson Huang /* Turn on GPT power and restore its clock and registers */ 183e6cf7a46SAnson Huang sc_pm_set_resource_power_mode(ipc_handle, SC_R_GPT_0, SC_PM_PW_MODE_ON); 184e6cf7a46SAnson Huang sc_pm_clock_enable(ipc_handle, SC_R_GPT_0, SC_PM_CLK_PER, true, 0); 185e6cf7a46SAnson Huang mmio_write_32(IMX_GPT0_BASE, gpt_reg[0]); 186e6cf7a46SAnson Huang mmio_write_32(IMX_GPT0_BASE + 0x4, gpt_reg[1]); 187e6cf7a46SAnson Huang mmio_write_32(IMX_GPT0_LPCG_BASE, gpt_lpcg); 188e6cf7a46SAnson Huang 189e6cf7a46SAnson Huang sc_pm_req_low_power_mode(ipc_handle, SC_R_A35, SC_PM_PW_MODE_ON); 190e6cf7a46SAnson Huang sc_pm_req_sys_if_power_mode(ipc_handle, SC_R_A35, SC_PM_SYS_IF_DDR, 191e6cf7a46SAnson Huang SC_PM_PW_MODE_ON, SC_PM_PW_MODE_ON); 192e6cf7a46SAnson Huang sc_pm_req_sys_if_power_mode(ipc_handle, SC_R_A35, SC_PM_SYS_IF_MU, 193e6cf7a46SAnson Huang SC_PM_PW_MODE_ON, SC_PM_PW_MODE_ON); 194e6cf7a46SAnson Huang sc_pm_req_sys_if_power_mode(ipc_handle, SC_R_A35, SC_PM_SYS_IF_INTERCONNECT, 195e6cf7a46SAnson Huang SC_PM_PW_MODE_ON, SC_PM_PW_MODE_ON); 196e6cf7a46SAnson Huang 197e6cf7a46SAnson Huang /* disable the irqsteer wakeup */ 198e6cf7a46SAnson Huang imx_disable_irqstr_wakeup(); 199762688bfSAnson Huang 200762688bfSAnson Huang plat_gic_cpuif_enable(); 201762688bfSAnson Huang } 202762688bfSAnson Huang 203e6cf7a46SAnson Huang if (is_local_state_off(target_state->pwr_domain_state[MPIDR_AFFLVL1])) 204e6cf7a46SAnson Huang sc_pm_req_low_power_mode(ipc_handle, SC_R_A35, SC_PM_PW_MODE_ON); 205e6cf7a46SAnson Huang 206e6cf7a46SAnson Huang if (is_local_state_off(target_state->pwr_domain_state[MPIDR_AFFLVL0])) { 207e6cf7a46SAnson Huang sc_pm_req_cpu_low_power_mode(ipc_handle, ap_core_index[cpu_id], 208e6cf7a46SAnson Huang SC_PM_PW_MODE_ON, SC_PM_WAKE_SRC_GIC); 209e6cf7a46SAnson Huang plat_gic_cpuif_enable(); 210e6cf7a46SAnson Huang } else { 211e6cf7a46SAnson Huang write_scr_el3(read_scr_el3() & (~SCR_FIQ_BIT)); 212e6cf7a46SAnson Huang isb(); 213e6cf7a46SAnson Huang } 214e6cf7a46SAnson Huang } 215e6cf7a46SAnson Huang 2160bc18309SAnson Huang static const plat_psci_ops_t imx_plat_psci_ops = { 2170bc18309SAnson Huang .pwr_domain_on = imx_pwr_domain_on, 2180bc18309SAnson Huang .pwr_domain_on_finish = imx_pwr_domain_on_finish, 2190bc18309SAnson Huang .validate_ns_entrypoint = imx_validate_ns_entrypoint, 2208972694eSAnson Huang .system_off = imx_system_off, 221351e3731SAnson Huang .system_reset = imx_system_reset, 2223260f5c7SAnson Huang .pwr_domain_off = imx_pwr_domain_off, 223762688bfSAnson Huang .pwr_domain_suspend = imx_domain_suspend, 224762688bfSAnson Huang .pwr_domain_suspend_finish = imx_domain_suspend_finish, 225762688bfSAnson Huang .get_sys_suspend_power_state = imx_get_sys_suspend_power_state, 226762688bfSAnson Huang .validate_power_state = imx_validate_power_state, 2270bc18309SAnson Huang }; 2280bc18309SAnson Huang 2290bc18309SAnson Huang int plat_setup_psci_ops(uintptr_t sec_entrypoint, 2300bc18309SAnson Huang const plat_psci_ops_t **psci_ops) 2310bc18309SAnson Huang { 2320bc18309SAnson Huang imx_mailbox_init(sec_entrypoint); 2330bc18309SAnson Huang *psci_ops = &imx_plat_psci_ops; 2340bc18309SAnson Huang 235e6cf7a46SAnson Huang /* make sure system sources power ON in low power mode by default */ 236e6cf7a46SAnson Huang sc_pm_req_low_power_mode(ipc_handle, SC_R_A35, SC_PM_PW_MODE_ON); 237762688bfSAnson Huang 238e6cf7a46SAnson Huang sc_pm_req_sys_if_power_mode(ipc_handle, SC_R_A35, SC_PM_SYS_IF_DDR, 239e6cf7a46SAnson Huang SC_PM_PW_MODE_ON, SC_PM_PW_MODE_ON); 240e6cf7a46SAnson Huang sc_pm_req_sys_if_power_mode(ipc_handle, SC_R_A35, SC_PM_SYS_IF_MU, 241e6cf7a46SAnson Huang SC_PM_PW_MODE_ON, SC_PM_PW_MODE_ON); 242e6cf7a46SAnson Huang sc_pm_req_sys_if_power_mode(ipc_handle, SC_R_A35, SC_PM_SYS_IF_INTERCONNECT, 243e6cf7a46SAnson Huang SC_PM_PW_MODE_ON, SC_PM_PW_MODE_ON); 244762688bfSAnson Huang 2450bc18309SAnson Huang return 0; 2460bc18309SAnson Huang } 247