Home | History | Annotate | Download | only in zynqmp
      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