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