1 /* 2 * Copyright (c) 2018, 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 <lib/mmio.h> 13 #include <lib/psci/psci.h> 14 15 #include <gpc.h> 16 #include <plat_imx8.h> 17 18 #define CORE_PWR_STATE(state) ((state)->pwr_domain_state[MPIDR_AFFLVL0]) 19 #define CLUSTER_PWR_STATE(state) ((state)->pwr_domain_state[MPIDR_AFFLVL1]) 20 #define SYSTEM_PWR_STATE(state) ((state)->pwr_domain_state[PLAT_MAX_PWR_LVL]) 21 22 int imx_pwr_domain_on(u_register_t mpidr) 23 { 24 unsigned int core_id; 25 uint64_t base_addr = BL31_BASE; 26 27 core_id = MPIDR_AFFLVL0_VAL(mpidr); 28 29 /* set the secure entrypoint */ 30 imx_set_cpu_secure_entry(core_id, base_addr); 31 /* power up the core */ 32 imx_set_cpu_pwr_on(core_id); 33 34 return PSCI_E_SUCCESS; 35 } 36 37 void imx_pwr_domain_on_finish(const psci_power_state_t *target_state) 38 { 39 /* program the GIC per cpu dist and rdist interface */ 40 plat_gic_pcpu_init(); 41 /* enable the GICv3 cpu interface */ 42 plat_gic_cpuif_enable(); 43 } 44 45 void imx_pwr_domain_off(const psci_power_state_t *target_state) 46 { 47 uint64_t mpidr = read_mpidr_el1(); 48 unsigned int core_id = MPIDR_AFFLVL0_VAL(mpidr); 49 50 /* disable the GIC cpu interface first */ 51 plat_gic_cpuif_disable(); 52 /* config the core for power down */ 53 imx_set_cpu_pwr_off(core_id); 54 } 55 56 int imx_validate_ns_entrypoint(uintptr_t ns_entrypoint) 57 { 58 /* The non-secure entrypoint should be in RAM space */ 59 if (ns_entrypoint < PLAT_NS_IMAGE_OFFSET) 60 return PSCI_E_INVALID_PARAMS; 61 62 return PSCI_E_SUCCESS; 63 } 64 65 int imx_validate_power_state(unsigned int power_state, 66 psci_power_state_t *req_state) 67 { 68 int pwr_lvl = psci_get_pstate_pwrlvl(power_state); 69 int pwr_type = psci_get_pstate_type(power_state); 70 int state_id = psci_get_pstate_id(power_state); 71 72 if (pwr_lvl > PLAT_MAX_PWR_LVL) 73 return PSCI_E_INVALID_PARAMS; 74 75 if (pwr_type == PSTATE_TYPE_STANDBY) { 76 CORE_PWR_STATE(req_state) = PLAT_MAX_RET_STATE; 77 CLUSTER_PWR_STATE(req_state) = PLAT_MAX_RET_STATE; 78 } 79 80 if (pwr_type == PSTATE_TYPE_POWERDOWN && state_id == 0x33) { 81 CORE_PWR_STATE(req_state) = PLAT_MAX_OFF_STATE; 82 CLUSTER_PWR_STATE(req_state) = PLAT_MAX_RET_STATE; 83 } 84 85 return PSCI_E_SUCCESS; 86 } 87 88 void imx_cpu_standby(plat_local_state_t cpu_state) 89 { 90 dsb(); 91 write_scr_el3(read_scr_el3() | SCR_FIQ_BIT); 92 isb(); 93 94 wfi(); 95 96 write_scr_el3(read_scr_el3() & (~SCR_FIQ_BIT)); 97 isb(); 98 } 99 100 void imx_domain_suspend(const psci_power_state_t *target_state) 101 { 102 uint64_t base_addr = BL31_BASE; 103 uint64_t mpidr = read_mpidr_el1(); 104 unsigned int core_id = MPIDR_AFFLVL0_VAL(mpidr); 105 106 if (is_local_state_off(CORE_PWR_STATE(target_state))) { 107 /* disable the cpu interface */ 108 plat_gic_cpuif_disable(); 109 imx_set_cpu_secure_entry(core_id, base_addr); 110 imx_set_cpu_lpm(core_id, true); 111 } else { 112 dsb(); 113 write_scr_el3(read_scr_el3() | SCR_FIQ_BIT); 114 isb(); 115 } 116 117 if (is_local_state_off(CLUSTER_PWR_STATE(target_state))) 118 imx_set_cluster_powerdown(core_id, true); 119 else 120 imx_set_cluster_standby(true); 121 122 if (is_local_state_retn(SYSTEM_PWR_STATE(target_state))) { 123 imx_set_sys_lpm(true); 124 } 125 } 126 127 void imx_domain_suspend_finish(const psci_power_state_t *target_state) 128 { 129 uint64_t mpidr = read_mpidr_el1(); 130 unsigned int core_id = MPIDR_AFFLVL0_VAL(mpidr); 131 132 /* check the system level status */ 133 if (is_local_state_retn(SYSTEM_PWR_STATE(target_state))) { 134 imx_set_sys_lpm(false); 135 imx_clear_rbc_count(); 136 } 137 138 /* check the cluster level power status */ 139 if (is_local_state_off(CLUSTER_PWR_STATE(target_state))) 140 imx_set_cluster_powerdown(core_id, false); 141 else 142 imx_set_cluster_standby(false); 143 144 /* check the core level power status */ 145 if (is_local_state_off(CORE_PWR_STATE(target_state))) { 146 /* clear the core lpm setting */ 147 imx_set_cpu_lpm(core_id, false); 148 /* enable the gic cpu interface */ 149 plat_gic_cpuif_enable(); 150 } else { 151 write_scr_el3(read_scr_el3() & (~0x4)); 152 isb(); 153 } 154 } 155 156 void imx_get_sys_suspend_power_state(psci_power_state_t *req_state) 157 { 158 unsigned int i; 159 160 for (i = IMX_PWR_LVL0; i < PLAT_MAX_PWR_LVL; i++) 161 req_state->pwr_domain_state[i] = PLAT_STOP_OFF_STATE; 162 163 req_state->pwr_domain_state[PLAT_MAX_PWR_LVL] = PLAT_MAX_RET_STATE; 164 } 165 166 void __dead2 imx_system_reset(void) 167 { 168 uintptr_t wdog_base = IMX_WDOG_BASE; 169 unsigned int val; 170 171 /* WDOG_B reset */ 172 val = mmio_read_16(wdog_base); 173 #ifdef IMX_WDOG_B_RESET 174 val = (val & 0x00FF) | WDOG_WCR_WDZST | WDOG_WCR_WDE | 175 WDOG_WCR_WDT | WDOG_WCR_SRS; 176 #else 177 val = (val & 0x00FF) | WDOG_WCR_WDZST | WDOG_WCR_SRS; 178 #endif 179 mmio_write_16(wdog_base, val); 180 181 mmio_write_16(wdog_base + WDOG_WSR, 0x5555); 182 mmio_write_16(wdog_base + WDOG_WSR, 0xaaaa); 183 while (1) 184 ; 185 } 186 187 188 189 void __dead2 imx_system_off(void) 190 { 191 mmio_write_32(IMX_SNVS_BASE + SNVS_LPCR, SNVS_LPCR_SRTC_ENV | 192 SNVS_LPCR_DP_EN | SNVS_LPCR_TOP); 193 194 while (1) 195 ; 196 } 197 198 void __dead2 imx_pwr_domain_pwr_down_wfi(const psci_power_state_t *target_state) 199 { 200 if (is_local_state_off(CLUSTER_PWR_STATE(target_state))) 201 imx_set_rbc_count(); 202 203 while (1) 204 wfi(); 205 } 206 207 static const plat_psci_ops_t imx_plat_psci_ops = { 208 .pwr_domain_on = imx_pwr_domain_on, 209 .pwr_domain_on_finish = imx_pwr_domain_on_finish, 210 .pwr_domain_off = imx_pwr_domain_off, 211 .validate_ns_entrypoint = imx_validate_ns_entrypoint, 212 .validate_power_state = imx_validate_power_state, 213 .cpu_standby = imx_cpu_standby, 214 .pwr_domain_suspend = imx_domain_suspend, 215 .pwr_domain_suspend_finish = imx_domain_suspend_finish, 216 .pwr_domain_pwr_down_wfi = imx_pwr_domain_pwr_down_wfi, 217 .get_sys_suspend_power_state = imx_get_sys_suspend_power_state, 218 .system_reset = imx_system_reset, 219 .system_off = imx_system_off, 220 }; 221 222 /* export the platform specific psci ops */ 223 int plat_setup_psci_ops(uintptr_t sec_entrypoint, 224 const plat_psci_ops_t **psci_ops) 225 { 226 imx_mailbox_init(sec_entrypoint); 227 /* sec_entrypoint is used for warm reset */ 228 *psci_ops = &imx_plat_psci_ops; 229 230 return 0; 231 } 232