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 19*e6cf7a46SAnson Huang #include "../../common/sci/imx8_mu.h" 20*e6cf7a46SAnson Huang 210bc18309SAnson Huang const static 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 25*e6cf7a46SAnson Huang /* save gic dist/redist context when GIC is power down */ 26*e6cf7a46SAnson Huang static struct plat_gic_ctx imx_gicv3_ctx; 27*e6cf7a46SAnson Huang static unsigned int gpt_lpcg, gpt_reg[2]; 28*e6cf7a46SAnson Huang 29*e6cf7a46SAnson Huang static void imx_enable_irqstr_wakeup(void) 30*e6cf7a46SAnson Huang { 31*e6cf7a46SAnson Huang uint32_t irq_mask; 32*e6cf7a46SAnson Huang gicv3_dist_ctx_t *dist_ctx = &imx_gicv3_ctx.dist_ctx; 33*e6cf7a46SAnson Huang 34*e6cf7a46SAnson Huang /* put IRQSTR into ON mode */ 35*e6cf7a46SAnson Huang sc_pm_set_resource_power_mode(ipc_handle, SC_R_IRQSTR_SCU2, SC_PM_PW_MODE_ON); 36*e6cf7a46SAnson Huang 37*e6cf7a46SAnson Huang /* enable the irqsteer to handle wakeup irq */ 38*e6cf7a46SAnson Huang mmio_write_32(IMX_WUP_IRQSTR_BASE, 0x1); 39*e6cf7a46SAnson Huang for (int i = 0; i < 15; i++) { 40*e6cf7a46SAnson Huang irq_mask = dist_ctx->gicd_isenabler[i]; 41*e6cf7a46SAnson Huang mmio_write_32(IMX_WUP_IRQSTR_BASE + 0x3c - 0x4 * i, irq_mask); 42*e6cf7a46SAnson Huang } 43*e6cf7a46SAnson Huang 44*e6cf7a46SAnson Huang /* set IRQSTR low power mode */ 45*e6cf7a46SAnson Huang if (imx_is_wakeup_src_irqsteer()) 46*e6cf7a46SAnson Huang sc_pm_set_resource_power_mode(ipc_handle, SC_R_IRQSTR_SCU2, SC_PM_PW_MODE_STBY); 47*e6cf7a46SAnson Huang else 48*e6cf7a46SAnson Huang sc_pm_set_resource_power_mode(ipc_handle, SC_R_IRQSTR_SCU2, SC_PM_PW_MODE_OFF); 49*e6cf7a46SAnson Huang } 50*e6cf7a46SAnson Huang 51*e6cf7a46SAnson Huang static void imx_disable_irqstr_wakeup(void) 52*e6cf7a46SAnson Huang { 53*e6cf7a46SAnson Huang /* Put IRQSTEER back to ON mode */ 54*e6cf7a46SAnson Huang sc_pm_set_resource_power_mode(ipc_handle, SC_R_IRQSTR_SCU2, SC_PM_PW_MODE_ON); 55*e6cf7a46SAnson Huang 56*e6cf7a46SAnson Huang /* disable the irqsteer */ 57*e6cf7a46SAnson Huang mmio_write_32(IMX_WUP_IRQSTR_BASE, 0x0); 58*e6cf7a46SAnson Huang for (int i = 0; i < 16; i++) 59*e6cf7a46SAnson Huang mmio_write_32(IMX_WUP_IRQSTR_BASE + 0x4 + 0x4 * i, 0x0); 60*e6cf7a46SAnson Huang 61*e6cf7a46SAnson Huang /* Put IRQSTEER into OFF mode */ 62*e6cf7a46SAnson Huang sc_pm_set_resource_power_mode(ipc_handle, SC_R_IRQSTR_SCU2, SC_PM_PW_MODE_OFF); 63*e6cf7a46SAnson Huang } 64*e6cf7a46SAnson 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 116*e6cf7a46SAnson Huang if (is_local_state_off(target_state->pwr_domain_state[MPIDR_AFFLVL0])) { 117762688bfSAnson Huang plat_gic_cpuif_disable(); 118*e6cf7a46SAnson 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); 121*e6cf7a46SAnson Huang } else { 122*e6cf7a46SAnson Huang dsb(); 123*e6cf7a46SAnson Huang write_scr_el3(read_scr_el3() | SCR_FIQ_BIT); 124*e6cf7a46SAnson Huang isb(); 125*e6cf7a46SAnson Huang } 126*e6cf7a46SAnson Huang 127*e6cf7a46SAnson Huang if (is_local_state_off(target_state->pwr_domain_state[MPIDR_AFFLVL1])) 128*e6cf7a46SAnson Huang sc_pm_req_low_power_mode(ipc_handle, SC_R_A35, SC_PM_PW_MODE_OFF); 129*e6cf7a46SAnson Huang 130*e6cf7a46SAnson Huang if (is_local_state_retn(target_state->pwr_domain_state[PLAT_MAX_PWR_LVL])) { 131*e6cf7a46SAnson Huang plat_gic_cpuif_disable(); 132*e6cf7a46SAnson Huang 133*e6cf7a46SAnson Huang /* save gic context */ 134*e6cf7a46SAnson Huang plat_gic_save(cpu_id, &imx_gicv3_ctx); 135*e6cf7a46SAnson Huang /* enable the irqsteer for wakeup */ 136*e6cf7a46SAnson Huang imx_enable_irqstr_wakeup(); 137*e6cf7a46SAnson Huang 138*e6cf7a46SAnson Huang /* Save GPT clock and registers, then turn off its power */ 139*e6cf7a46SAnson Huang gpt_lpcg = mmio_read_32(IMX_GPT0_LPCG_BASE); 140*e6cf7a46SAnson Huang gpt_reg[0] = mmio_read_32(IMX_GPT0_BASE); 141*e6cf7a46SAnson Huang gpt_reg[1] = mmio_read_32(IMX_GPT0_BASE + 0x4); 142*e6cf7a46SAnson Huang sc_pm_set_resource_power_mode(ipc_handle, SC_R_GPT_0, SC_PM_PW_MODE_OFF); 143*e6cf7a46SAnson Huang 144*e6cf7a46SAnson Huang sc_pm_req_low_power_mode(ipc_handle, SC_R_A35, SC_PM_PW_MODE_OFF); 145*e6cf7a46SAnson Huang sc_pm_req_sys_if_power_mode(ipc_handle, SC_R_A35, SC_PM_SYS_IF_DDR, 146*e6cf7a46SAnson Huang SC_PM_PW_MODE_ON, SC_PM_PW_MODE_OFF); 147*e6cf7a46SAnson Huang sc_pm_req_sys_if_power_mode(ipc_handle, SC_R_A35, SC_PM_SYS_IF_MU, 148*e6cf7a46SAnson Huang SC_PM_PW_MODE_ON, SC_PM_PW_MODE_OFF); 149*e6cf7a46SAnson Huang sc_pm_req_sys_if_power_mode(ipc_handle, SC_R_A35, SC_PM_SYS_IF_INTERCONNECT, 150*e6cf7a46SAnson Huang SC_PM_PW_MODE_ON, SC_PM_PW_MODE_OFF); 151*e6cf7a46SAnson Huang 152*e6cf7a46SAnson Huang /* Put GIC in OFF mode. */ 153*e6cf7a46SAnson Huang sc_pm_set_resource_power_mode(ipc_handle, SC_R_GIC, SC_PM_PW_MODE_OFF); 154*e6cf7a46SAnson Huang sc_pm_set_cpu_resume(ipc_handle, ap_core_index[cpu_id], true, BL31_BASE); 155*e6cf7a46SAnson Huang if (imx_is_wakeup_src_irqsteer()) 156*e6cf7a46SAnson Huang sc_pm_req_cpu_low_power_mode(ipc_handle, ap_core_index[cpu_id], 157*e6cf7a46SAnson Huang SC_PM_PW_MODE_OFF, SC_PM_WAKE_SRC_IRQSTEER); 158*e6cf7a46SAnson Huang else 159*e6cf7a46SAnson Huang sc_pm_req_cpu_low_power_mode(ipc_handle, ap_core_index[cpu_id], 160*e6cf7a46SAnson Huang SC_PM_PW_MODE_OFF, SC_PM_WAKE_SRC_SCU); 161*e6cf7a46SAnson 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 169*e6cf7a46SAnson Huang if (is_local_state_retn(target_state->pwr_domain_state[PLAT_MAX_PWR_LVL])) { 170*e6cf7a46SAnson Huang MU_Resume(SC_IPC_BASE); 171*e6cf7a46SAnson Huang 172*e6cf7a46SAnson Huang sc_pm_req_low_power_mode(ipc_handle, ap_core_index[cpu_id], SC_PM_PW_MODE_ON); 173*e6cf7a46SAnson Huang sc_pm_req_cpu_low_power_mode(ipc_handle, ap_core_index[cpu_id], 174*e6cf7a46SAnson Huang SC_PM_PW_MODE_ON, SC_PM_WAKE_SRC_GIC); 175*e6cf7a46SAnson Huang 176*e6cf7a46SAnson Huang /* Put GIC back to high power mode. */ 177*e6cf7a46SAnson Huang sc_pm_set_resource_power_mode(ipc_handle, SC_R_GIC, SC_PM_PW_MODE_ON); 178*e6cf7a46SAnson Huang 179*e6cf7a46SAnson Huang /* restore gic context */ 180*e6cf7a46SAnson Huang plat_gic_restore(cpu_id, &imx_gicv3_ctx); 181*e6cf7a46SAnson Huang 182*e6cf7a46SAnson Huang /* Turn on GPT power and restore its clock and registers */ 183*e6cf7a46SAnson Huang sc_pm_set_resource_power_mode(ipc_handle, SC_R_GPT_0, SC_PM_PW_MODE_ON); 184*e6cf7a46SAnson Huang sc_pm_clock_enable(ipc_handle, SC_R_GPT_0, SC_PM_CLK_PER, true, 0); 185*e6cf7a46SAnson Huang mmio_write_32(IMX_GPT0_BASE, gpt_reg[0]); 186*e6cf7a46SAnson Huang mmio_write_32(IMX_GPT0_BASE + 0x4, gpt_reg[1]); 187*e6cf7a46SAnson Huang mmio_write_32(IMX_GPT0_LPCG_BASE, gpt_lpcg); 188*e6cf7a46SAnson Huang 189*e6cf7a46SAnson Huang sc_pm_req_low_power_mode(ipc_handle, SC_R_A35, SC_PM_PW_MODE_ON); 190*e6cf7a46SAnson Huang sc_pm_req_sys_if_power_mode(ipc_handle, SC_R_A35, SC_PM_SYS_IF_DDR, 191*e6cf7a46SAnson Huang SC_PM_PW_MODE_ON, SC_PM_PW_MODE_ON); 192*e6cf7a46SAnson Huang sc_pm_req_sys_if_power_mode(ipc_handle, SC_R_A35, SC_PM_SYS_IF_MU, 193*e6cf7a46SAnson Huang SC_PM_PW_MODE_ON, SC_PM_PW_MODE_ON); 194*e6cf7a46SAnson Huang sc_pm_req_sys_if_power_mode(ipc_handle, SC_R_A35, SC_PM_SYS_IF_INTERCONNECT, 195*e6cf7a46SAnson Huang SC_PM_PW_MODE_ON, SC_PM_PW_MODE_ON); 196*e6cf7a46SAnson Huang 197*e6cf7a46SAnson Huang /* disable the irqsteer wakeup */ 198*e6cf7a46SAnson Huang imx_disable_irqstr_wakeup(); 199762688bfSAnson Huang 200762688bfSAnson Huang plat_gic_cpuif_enable(); 201762688bfSAnson Huang } 202762688bfSAnson Huang 203*e6cf7a46SAnson Huang if (is_local_state_off(target_state->pwr_domain_state[MPIDR_AFFLVL1])) 204*e6cf7a46SAnson Huang sc_pm_req_low_power_mode(ipc_handle, SC_R_A35, SC_PM_PW_MODE_ON); 205*e6cf7a46SAnson Huang 206*e6cf7a46SAnson Huang if (is_local_state_off(target_state->pwr_domain_state[MPIDR_AFFLVL0])) { 207*e6cf7a46SAnson Huang sc_pm_req_cpu_low_power_mode(ipc_handle, ap_core_index[cpu_id], 208*e6cf7a46SAnson Huang SC_PM_PW_MODE_ON, SC_PM_WAKE_SRC_GIC); 209*e6cf7a46SAnson Huang plat_gic_cpuif_enable(); 210*e6cf7a46SAnson Huang } else { 211*e6cf7a46SAnson Huang write_scr_el3(read_scr_el3() & (~SCR_FIQ_BIT)); 212*e6cf7a46SAnson Huang isb(); 213*e6cf7a46SAnson Huang } 214*e6cf7a46SAnson Huang } 215*e6cf7a46SAnson 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 235*e6cf7a46SAnson Huang /* make sure system sources power ON in low power mode by default */ 236*e6cf7a46SAnson Huang sc_pm_req_low_power_mode(ipc_handle, SC_R_A35, SC_PM_PW_MODE_ON); 237762688bfSAnson Huang 238*e6cf7a46SAnson Huang sc_pm_req_sys_if_power_mode(ipc_handle, SC_R_A35, SC_PM_SYS_IF_DDR, 239*e6cf7a46SAnson Huang SC_PM_PW_MODE_ON, SC_PM_PW_MODE_ON); 240*e6cf7a46SAnson Huang sc_pm_req_sys_if_power_mode(ipc_handle, SC_R_A35, SC_PM_SYS_IF_MU, 241*e6cf7a46SAnson Huang SC_PM_PW_MODE_ON, SC_PM_PW_MODE_ON); 242*e6cf7a46SAnson Huang sc_pm_req_sys_if_power_mode(ipc_handle, SC_R_A35, SC_PM_SYS_IF_INTERCONNECT, 243*e6cf7a46SAnson Huang SC_PM_PW_MODE_ON, SC_PM_PW_MODE_ON); 244762688bfSAnson Huang 2450bc18309SAnson Huang return 0; 2460bc18309SAnson Huang } 247