1 /* 2 * Copyright (c) 2013-2016, ARM Limited and Contributors. All rights reserved. 3 * 4 * SPDX-License-Identifier: BSD-3-Clause 5 */ 6 7 #include <arch_helpers.h> 8 #include <assert.h> 9 #include <debug.h> 10 #include <errno.h> 11 #include <gicv2.h> 12 #include <mmio.h> 13 #include <plat_arm.h> 14 #include <platform.h> 15 #include <psci.h> 16 #include "pm_api_sys.h" 17 #include "pm_client.h" 18 #include "zynqmp_private.h" 19 20 uintptr_t zynqmp_sec_entry; 21 22 void zynqmp_cpu_standby(plat_local_state_t cpu_state) 23 { 24 VERBOSE("%s: cpu_state: 0x%x\n", __func__, cpu_state); 25 26 dsb(); 27 wfi(); 28 } 29 30 static int zynqmp_nopmu_pwr_domain_on(u_register_t mpidr) 31 { 32 uint32_t r; 33 unsigned int cpu_id = plat_core_pos_by_mpidr(mpidr); 34 35 VERBOSE("%s: mpidr: 0x%lx\n", __func__, mpidr); 36 37 if (cpu_id == -1) 38 return PSCI_E_INTERN_FAIL; 39 40 /* program RVBAR */ 41 mmio_write_32(APU_RVBAR_L_0 + (cpu_id << 3), zynqmp_sec_entry); 42 mmio_write_32(APU_RVBAR_H_0 + (cpu_id << 3), zynqmp_sec_entry >> 32); 43 44 /* clear VINITHI */ 45 r = mmio_read_32(APU_CONFIG_0); 46 r &= ~(1 << APU_CONFIG_0_VINITHI_SHIFT << cpu_id); 47 mmio_write_32(APU_CONFIG_0, r); 48 49 /* clear power down request */ 50 r = mmio_read_32(APU_PWRCTL); 51 r &= ~(1 << cpu_id); 52 mmio_write_32(APU_PWRCTL, r); 53 54 /* power up island */ 55 mmio_write_32(PMU_GLOBAL_REQ_PWRUP_EN, 1 << cpu_id); 56 mmio_write_32(PMU_GLOBAL_REQ_PWRUP_TRIG, 1 << cpu_id); 57 /* FIXME: we should have a way to break out */ 58 while (mmio_read_32(PMU_GLOBAL_REQ_PWRUP_STATUS) & (1 << cpu_id)) 59 ; 60 61 /* release core reset */ 62 r = mmio_read_32(CRF_APB_RST_FPD_APU); 63 r &= ~((CRF_APB_RST_FPD_APU_ACPU_PWRON_RESET | 64 CRF_APB_RST_FPD_APU_ACPU_RESET) << cpu_id); 65 mmio_write_32(CRF_APB_RST_FPD_APU, r); 66 67 return PSCI_E_SUCCESS; 68 } 69 70 static int zynqmp_pwr_domain_on(u_register_t mpidr) 71 { 72 unsigned int cpu_id = plat_core_pos_by_mpidr(mpidr); 73 const struct pm_proc *proc; 74 75 VERBOSE("%s: mpidr: 0x%lx\n", __func__, mpidr); 76 77 if (cpu_id == -1) 78 return PSCI_E_INTERN_FAIL; 79 80 proc = pm_get_proc(cpu_id); 81 /* Clear power down request */ 82 pm_client_wakeup(proc); 83 84 /* Send request to PMU to wake up selected APU CPU core */ 85 pm_req_wakeup(proc->node_id, 1, zynqmp_sec_entry, REQ_ACK_BLOCKING); 86 87 return PSCI_E_SUCCESS; 88 } 89 90 static void zynqmp_nopmu_pwr_domain_off(const psci_power_state_t *target_state) 91 { 92 uint32_t r; 93 unsigned int cpu_id = plat_my_core_pos(); 94 95 for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++) 96 VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n", 97 __func__, i, target_state->pwr_domain_state[i]); 98 99 /* Prevent interrupts from spuriously waking up this cpu */ 100 gicv2_cpuif_disable(); 101 102 /* set power down request */ 103 r = mmio_read_32(APU_PWRCTL); 104 r |= (1 << cpu_id); 105 mmio_write_32(APU_PWRCTL, r); 106 } 107 108 static void zynqmp_pwr_domain_off(const psci_power_state_t *target_state) 109 { 110 unsigned int cpu_id = plat_my_core_pos(); 111 const struct pm_proc *proc = pm_get_proc(cpu_id); 112 113 for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++) 114 VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n", 115 __func__, i, target_state->pwr_domain_state[i]); 116 117 /* Prevent interrupts from spuriously waking up this cpu */ 118 gicv2_cpuif_disable(); 119 120 /* 121 * Send request to PMU to power down the appropriate APU CPU 122 * core. 123 * According to PSCI specification, CPU_off function does not 124 * have resume address and CPU core can only be woken up 125 * invoking CPU_on function, during which resume address will 126 * be set. 127 */ 128 pm_self_suspend(proc->node_id, MAX_LATENCY, PM_STATE_CPU_IDLE, 0); 129 } 130 131 static void zynqmp_nopmu_pwr_domain_suspend(const psci_power_state_t *target_state) 132 { 133 uint32_t r; 134 unsigned int cpu_id = plat_my_core_pos(); 135 136 for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++) 137 VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n", 138 __func__, i, target_state->pwr_domain_state[i]); 139 140 /* set power down request */ 141 r = mmio_read_32(APU_PWRCTL); 142 r |= (1 << cpu_id); 143 mmio_write_32(APU_PWRCTL, r); 144 145 /* program RVBAR */ 146 mmio_write_32(APU_RVBAR_L_0 + (cpu_id << 3), zynqmp_sec_entry); 147 mmio_write_32(APU_RVBAR_H_0 + (cpu_id << 3), zynqmp_sec_entry >> 32); 148 149 /* clear VINITHI */ 150 r = mmio_read_32(APU_CONFIG_0); 151 r &= ~(1 << APU_CONFIG_0_VINITHI_SHIFT << cpu_id); 152 mmio_write_32(APU_CONFIG_0, r); 153 154 /* enable power up on IRQ */ 155 mmio_write_32(PMU_GLOBAL_REQ_PWRUP_EN, 1 << cpu_id); 156 } 157 158 static void zynqmp_pwr_domain_suspend(const psci_power_state_t *target_state) 159 { 160 unsigned int state; 161 unsigned int cpu_id = plat_my_core_pos(); 162 const struct pm_proc *proc = pm_get_proc(cpu_id); 163 164 for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++) 165 VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n", 166 __func__, i, target_state->pwr_domain_state[i]); 167 168 state = target_state->pwr_domain_state[1] > PLAT_MAX_RET_STATE ? 169 PM_STATE_SUSPEND_TO_RAM : PM_STATE_CPU_IDLE; 170 171 /* Send request to PMU to suspend this core */ 172 pm_self_suspend(proc->node_id, MAX_LATENCY, state, zynqmp_sec_entry); 173 174 /* APU is to be turned off */ 175 if (target_state->pwr_domain_state[1] > PLAT_MAX_RET_STATE) { 176 /* disable coherency */ 177 plat_arm_interconnect_exit_coherency(); 178 } 179 } 180 181 static void zynqmp_pwr_domain_on_finish(const psci_power_state_t *target_state) 182 { 183 for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++) 184 VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n", 185 __func__, i, target_state->pwr_domain_state[i]); 186 187 gicv2_cpuif_enable(); 188 gicv2_pcpu_distif_init(); 189 } 190 191 static void zynqmp_nopmu_pwr_domain_suspend_finish(const psci_power_state_t *target_state) 192 { 193 uint32_t r; 194 unsigned int cpu_id = plat_my_core_pos(); 195 196 for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++) 197 VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n", 198 __func__, i, target_state->pwr_domain_state[i]); 199 200 /* disable power up on IRQ */ 201 mmio_write_32(PMU_GLOBAL_REQ_PWRUP_DIS, 1 << cpu_id); 202 203 /* clear powerdown bit */ 204 r = mmio_read_32(APU_PWRCTL); 205 r &= ~(1 << cpu_id); 206 mmio_write_32(APU_PWRCTL, r); 207 } 208 209 static void zynqmp_pwr_domain_suspend_finish(const psci_power_state_t *target_state) 210 { 211 unsigned int cpu_id = plat_my_core_pos(); 212 const struct pm_proc *proc = pm_get_proc(cpu_id); 213 214 for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++) 215 VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n", 216 __func__, i, target_state->pwr_domain_state[i]); 217 218 /* Clear the APU power control register for this cpu */ 219 pm_client_wakeup(proc); 220 221 /* enable coherency */ 222 plat_arm_interconnect_enter_coherency(); 223 /* APU was turned off */ 224 if (target_state->pwr_domain_state[1] > PLAT_MAX_RET_STATE) { 225 plat_arm_gic_init(); 226 } else { 227 gicv2_cpuif_enable(); 228 gicv2_pcpu_distif_init(); 229 } 230 } 231 232 /******************************************************************************* 233 * ZynqMP handlers to shutdown/reboot the system 234 ******************************************************************************/ 235 static void __dead2 zynqmp_nopmu_system_off(void) 236 { 237 ERROR("ZynqMP System Off: operation not handled.\n"); 238 239 /* disable coherency */ 240 plat_arm_interconnect_exit_coherency(); 241 242 panic(); 243 } 244 245 static void __dead2 zynqmp_system_off(void) 246 { 247 /* disable coherency */ 248 plat_arm_interconnect_exit_coherency(); 249 250 /* Send the power down request to the PMU */ 251 pm_system_shutdown(PMF_SHUTDOWN_TYPE_SHUTDOWN, 252 pm_get_shutdown_scope()); 253 254 while (1) 255 wfi(); 256 } 257 258 static void __dead2 zynqmp_nopmu_system_reset(void) 259 { 260 /* 261 * This currently triggers a system reset. I.e. the whole 262 * system will be reset! Including RPUs, PMU, PL, etc. 263 */ 264 265 /* disable coherency */ 266 plat_arm_interconnect_exit_coherency(); 267 268 /* bypass RPLL (needed on 1.0 silicon) */ 269 uint32_t reg = mmio_read_32(CRL_APB_RPLL_CTRL); 270 reg |= CRL_APB_RPLL_CTRL_BYPASS; 271 mmio_write_32(CRL_APB_RPLL_CTRL, reg); 272 273 /* trigger system reset */ 274 mmio_write_32(CRL_APB_RESET_CTRL, CRL_APB_RESET_CTRL_SOFT_RESET); 275 276 while (1) 277 wfi(); 278 } 279 280 static void __dead2 zynqmp_system_reset(void) 281 { 282 /* disable coherency */ 283 plat_arm_interconnect_exit_coherency(); 284 285 /* Send the system reset request to the PMU */ 286 pm_system_shutdown(PMF_SHUTDOWN_TYPE_RESET, 287 pm_get_shutdown_scope()); 288 289 while (1) 290 wfi(); 291 } 292 293 int zynqmp_validate_power_state(unsigned int power_state, 294 psci_power_state_t *req_state) 295 { 296 VERBOSE("%s: power_state: 0x%x\n", __func__, power_state); 297 298 int pstate = psci_get_pstate_type(power_state); 299 300 assert(req_state); 301 302 /* Sanity check the requested state */ 303 if (pstate == PSTATE_TYPE_STANDBY) 304 req_state->pwr_domain_state[MPIDR_AFFLVL0] = PLAT_MAX_RET_STATE; 305 else 306 req_state->pwr_domain_state[MPIDR_AFFLVL0] = PLAT_MAX_OFF_STATE; 307 308 /* We expect the 'state id' to be zero */ 309 if (psci_get_pstate_id(power_state)) 310 return PSCI_E_INVALID_PARAMS; 311 312 return PSCI_E_SUCCESS; 313 } 314 315 int zynqmp_validate_ns_entrypoint(unsigned long ns_entrypoint) 316 { 317 VERBOSE("%s: ns_entrypoint: 0x%lx\n", __func__, ns_entrypoint); 318 319 /* FIXME: Actually validate */ 320 return PSCI_E_SUCCESS; 321 } 322 323 void zynqmp_get_sys_suspend_power_state(psci_power_state_t *req_state) 324 { 325 req_state->pwr_domain_state[PSCI_CPU_PWR_LVL] = PLAT_MAX_OFF_STATE; 326 req_state->pwr_domain_state[1] = PLAT_MAX_OFF_STATE; 327 } 328 329 /******************************************************************************* 330 * Export the platform handlers to enable psci to invoke them 331 ******************************************************************************/ 332 static const struct plat_psci_ops zynqmp_psci_ops = { 333 .cpu_standby = zynqmp_cpu_standby, 334 .pwr_domain_on = zynqmp_pwr_domain_on, 335 .pwr_domain_off = zynqmp_pwr_domain_off, 336 .pwr_domain_suspend = zynqmp_pwr_domain_suspend, 337 .pwr_domain_on_finish = zynqmp_pwr_domain_on_finish, 338 .pwr_domain_suspend_finish = zynqmp_pwr_domain_suspend_finish, 339 .system_off = zynqmp_system_off, 340 .system_reset = zynqmp_system_reset, 341 .validate_power_state = zynqmp_validate_power_state, 342 .validate_ns_entrypoint = zynqmp_validate_ns_entrypoint, 343 .get_sys_suspend_power_state = zynqmp_get_sys_suspend_power_state, 344 }; 345 346 static const struct plat_psci_ops zynqmp_nopmu_psci_ops = { 347 .cpu_standby = zynqmp_cpu_standby, 348 .pwr_domain_on = zynqmp_nopmu_pwr_domain_on, 349 .pwr_domain_off = zynqmp_nopmu_pwr_domain_off, 350 .pwr_domain_suspend = zynqmp_nopmu_pwr_domain_suspend, 351 .pwr_domain_on_finish = zynqmp_pwr_domain_on_finish, 352 .pwr_domain_suspend_finish = zynqmp_nopmu_pwr_domain_suspend_finish, 353 .system_off = zynqmp_nopmu_system_off, 354 .system_reset = zynqmp_nopmu_system_reset, 355 .validate_power_state = zynqmp_validate_power_state, 356 .validate_ns_entrypoint = zynqmp_validate_ns_entrypoint, 357 .get_sys_suspend_power_state = zynqmp_get_sys_suspend_power_state, 358 }; 359 360 /******************************************************************************* 361 * Export the platform specific power ops. 362 ******************************************************************************/ 363 int plat_setup_psci_ops(uintptr_t sec_entrypoint, 364 const struct plat_psci_ops **psci_ops) 365 { 366 zynqmp_sec_entry = sec_entrypoint; 367 368 if (zynqmp_is_pmu_up()) 369 *psci_ops = &zynqmp_psci_ops; 370 else 371 *psci_ops = &zynqmp_nopmu_psci_ops; 372 373 return 0; 374 } 375