1 /* 2 * Copyright (c) 2018-2023, ARM Limited and Contributors. All rights reserved. 3 * 4 * SPDX-License-Identifier: BSD-3-Clause 5 */ 6 7 #include <stdbool.h> 8 9 #include <arch.h> 10 #include <arch_helpers.h> 11 #include <common/debug.h> 12 #include <drivers/delay_timer.h> 13 #include <lib/mmio.h> 14 #include <lib/psci/psci.h> 15 16 #include <dram.h> 17 #include <gpc.h> 18 #include <imx8m_psci.h> 19 #include <plat_imx8.h> 20 21 int imx_validate_power_state(unsigned int power_state, 22 psci_power_state_t *req_state) 23 { 24 int pwr_lvl = psci_get_pstate_pwrlvl(power_state); 25 int pwr_type = psci_get_pstate_type(power_state); 26 int state_id = psci_get_pstate_id(power_state); 27 28 if (pwr_lvl > PLAT_MAX_PWR_LVL) 29 return PSCI_E_INVALID_PARAMS; 30 31 if (pwr_type == PSTATE_TYPE_STANDBY) { 32 CORE_PWR_STATE(req_state) = PLAT_MAX_RET_STATE; 33 CLUSTER_PWR_STATE(req_state) = PLAT_MAX_RET_STATE; 34 } 35 36 if (pwr_type == PSTATE_TYPE_POWERDOWN && state_id == 0x33) { 37 CORE_PWR_STATE(req_state) = PLAT_MAX_OFF_STATE; 38 CLUSTER_PWR_STATE(req_state) = PLAT_MAX_RET_STATE; 39 } 40 41 return PSCI_E_SUCCESS; 42 } 43 44 void imx_pwr_domain_off(const psci_power_state_t *target_state) 45 { 46 uint64_t mpidr = read_mpidr_el1(); 47 unsigned int core_id = MPIDR_AFFLVL0_VAL(mpidr); 48 49 plat_gic_cpuif_disable(); 50 imx_set_cpu_pwr_off(core_id); 51 52 /* 53 * TODO: Find out why this is still 54 * needed in order not to break suspend 55 */ 56 udelay(50); 57 } 58 59 void imx_domain_suspend(const psci_power_state_t *target_state) 60 { 61 uint64_t base_addr = BL31_START; 62 uint64_t mpidr = read_mpidr_el1(); 63 unsigned int core_id = MPIDR_AFFLVL0_VAL(mpidr); 64 65 if (is_local_state_off(CORE_PWR_STATE(target_state))) { 66 /* disable the cpu interface */ 67 plat_gic_cpuif_disable(); 68 imx_set_cpu_secure_entry(core_id, base_addr); 69 imx_set_cpu_lpm(core_id, true); 70 } else { 71 dsb(); 72 write_scr_el3(read_scr_el3() | SCR_FIQ_BIT); 73 isb(); 74 } 75 76 if (is_local_state_off(CLUSTER_PWR_STATE(target_state))) 77 imx_set_cluster_powerdown(core_id, CLUSTER_PWR_STATE(target_state)); 78 else 79 imx_set_cluster_standby(true); 80 81 if (is_local_state_retn(SYSTEM_PWR_STATE(target_state))) { 82 imx_set_sys_lpm(core_id, true); 83 dram_enter_retention(); 84 imx_anamix_override(true); 85 } 86 } 87 88 void imx_domain_suspend_finish(const psci_power_state_t *target_state) 89 { 90 uint64_t mpidr = read_mpidr_el1(); 91 unsigned int core_id = MPIDR_AFFLVL0_VAL(mpidr); 92 93 /* check the system level status */ 94 if (is_local_state_retn(SYSTEM_PWR_STATE(target_state))) { 95 imx_anamix_override(false); 96 dram_exit_retention(); 97 imx_set_sys_lpm(core_id, false); 98 imx_clear_rbc_count(); 99 } 100 101 /* check the cluster level power status */ 102 if (is_local_state_off(CLUSTER_PWR_STATE(target_state))) 103 imx_set_cluster_powerdown(core_id, PSCI_LOCAL_STATE_RUN); 104 else 105 imx_set_cluster_standby(false); 106 107 /* check the core level power status */ 108 if (is_local_state_off(CORE_PWR_STATE(target_state))) { 109 /* mark this core as awake by masking IRQ0 */ 110 imx_gpc_set_a53_core_awake(core_id); 111 /* clear the core lpm setting */ 112 imx_set_cpu_lpm(core_id, false); 113 /* enable the gic cpu interface */ 114 plat_gic_cpuif_enable(); 115 } else { 116 write_scr_el3(read_scr_el3() & (~0x4)); 117 isb(); 118 } 119 } 120 121 void imx_get_sys_suspend_power_state(psci_power_state_t *req_state) 122 { 123 unsigned int i; 124 125 for (i = IMX_PWR_LVL0; i < PLAT_MAX_PWR_LVL; i++) 126 req_state->pwr_domain_state[i] = PLAT_STOP_OFF_STATE; 127 128 req_state->pwr_domain_state[PLAT_MAX_PWR_LVL] = PLAT_MAX_RET_STATE; 129 } 130 131 static const plat_psci_ops_t imx_plat_psci_ops = { 132 .pwr_domain_on = imx_pwr_domain_on, 133 .pwr_domain_on_finish = imx_pwr_domain_on_finish, 134 .pwr_domain_off = imx_pwr_domain_off, 135 .validate_ns_entrypoint = imx_validate_ns_entrypoint, 136 .validate_power_state = imx_validate_power_state, 137 .cpu_standby = imx_cpu_standby, 138 .pwr_domain_suspend = imx_domain_suspend, 139 .pwr_domain_suspend_finish = imx_domain_suspend_finish, 140 .pwr_domain_pwr_down_wfi = imx_pwr_domain_pwr_down_wfi, 141 .get_sys_suspend_power_state = imx_get_sys_suspend_power_state, 142 .system_reset = imx_system_reset, 143 .system_reset2 = imx_system_reset2, 144 .system_off = imx_system_off, 145 }; 146 147 /* export the platform specific psci ops */ 148 int plat_setup_psci_ops(uintptr_t sec_entrypoint, 149 const plat_psci_ops_t **psci_ops) 150 { 151 imx_mailbox_init(sec_entrypoint); 152 /* sec_entrypoint is used for warm reset */ 153 *psci_ops = &imx_plat_psci_ops; 154 155 return 0; 156 } 157