xref: /rk3399_ARM-atf/plat/arm/board/fvp/fvp_pm.c (revision 4adb10c1ede9773ce018ddb3b56f5eb900f220f8)
13fc4124cSDan Handley /*
26355f234SVikram Kanigiri  * Copyright (c) 2013-2016, ARM Limited and Contributors. All rights reserved.
33fc4124cSDan Handley  *
482cb2c1aSdp-arm  * SPDX-License-Identifier: BSD-3-Clause
53fc4124cSDan Handley  */
63fc4124cSDan Handley 
73fc4124cSDan Handley #include <arch_helpers.h>
83fc4124cSDan Handley #include <arm_config.h>
93fc4124cSDan Handley #include <assert.h>
103fc4124cSDan Handley #include <debug.h>
113fc4124cSDan Handley #include <errno.h>
123fc4124cSDan Handley #include <mmio.h>
133fc4124cSDan Handley #include <plat_arm.h>
14*4adb10c1SIsla Mitchell #include <platform.h>
153fc4124cSDan Handley #include <psci.h>
163fc4124cSDan Handley #include <v2m_def.h>
173fc4124cSDan Handley #include "drivers/pwrc/fvp_pwrc.h"
183fc4124cSDan Handley #include "fvp_def.h"
193fc4124cSDan Handley #include "fvp_private.h"
203fc4124cSDan Handley 
213fc4124cSDan Handley 
222204afdeSSoby Mathew #if ARM_RECOM_STATE_ID_ENC
232204afdeSSoby Mathew /*
242204afdeSSoby Mathew  *  The table storing the valid idle power states. Ensure that the
252204afdeSSoby Mathew  *  array entries are populated in ascending order of state-id to
262204afdeSSoby Mathew  *  enable us to use binary search during power state validation.
272204afdeSSoby Mathew  *  The table must be terminated by a NULL entry.
282204afdeSSoby Mathew  */
292204afdeSSoby Mathew const unsigned int arm_pm_idle_states[] = {
302204afdeSSoby Mathew 	/* State-id - 0x01 */
312204afdeSSoby Mathew 	arm_make_pwrstate_lvl1(ARM_LOCAL_STATE_RUN, ARM_LOCAL_STATE_RET,
322204afdeSSoby Mathew 			ARM_PWR_LVL0, PSTATE_TYPE_STANDBY),
332204afdeSSoby Mathew 	/* State-id - 0x02 */
342204afdeSSoby Mathew 	arm_make_pwrstate_lvl1(ARM_LOCAL_STATE_RUN, ARM_LOCAL_STATE_OFF,
352204afdeSSoby Mathew 			ARM_PWR_LVL0, PSTATE_TYPE_POWERDOWN),
362204afdeSSoby Mathew 	/* State-id - 0x22 */
372204afdeSSoby Mathew 	arm_make_pwrstate_lvl1(ARM_LOCAL_STATE_OFF, ARM_LOCAL_STATE_OFF,
382204afdeSSoby Mathew 			ARM_PWR_LVL1, PSTATE_TYPE_POWERDOWN),
392204afdeSSoby Mathew 	0,
402204afdeSSoby Mathew };
412204afdeSSoby Mathew #endif
422204afdeSSoby Mathew 
433fc4124cSDan Handley /*******************************************************************************
443fc4124cSDan Handley  * Function which implements the common FVP specific operations to power down a
453fc4124cSDan Handley  * cluster in response to a CPU_OFF or CPU_SUSPEND request.
463fc4124cSDan Handley  ******************************************************************************/
473fc4124cSDan Handley static void fvp_cluster_pwrdwn_common(void)
483fc4124cSDan Handley {
493fc4124cSDan Handley 	uint64_t mpidr = read_mpidr_el1();
503fc4124cSDan Handley 
51d832aee9Sdp-arm #if ENABLE_SPE_FOR_LOWER_ELS
52d832aee9Sdp-arm 	/*
53d832aee9Sdp-arm 	 * On power down we need to disable statistical profiling extensions
54d832aee9Sdp-arm 	 * before exiting coherency.
55d832aee9Sdp-arm 	 */
56d832aee9Sdp-arm 	arm_disable_spe();
57d832aee9Sdp-arm #endif
58d832aee9Sdp-arm 
593fc4124cSDan Handley 	/* Disable coherency if this cluster is to be turned off */
606355f234SVikram Kanigiri 	fvp_interconnect_disable();
613fc4124cSDan Handley 
623fc4124cSDan Handley 	/* Program the power controller to turn the cluster off */
633fc4124cSDan Handley 	fvp_pwrc_write_pcoffr(mpidr);
643fc4124cSDan Handley }
653fc4124cSDan Handley 
66f14d1886SSoby Mathew static void fvp_power_domain_on_finish_common(const psci_power_state_t *target_state)
67f14d1886SSoby Mathew {
68f14d1886SSoby Mathew 	unsigned long mpidr;
69f14d1886SSoby Mathew 
70f14d1886SSoby Mathew 	assert(target_state->pwr_domain_state[ARM_PWR_LVL0] ==
71f14d1886SSoby Mathew 					ARM_LOCAL_STATE_OFF);
72f14d1886SSoby Mathew 
73f14d1886SSoby Mathew 	/* Get the mpidr for this cpu */
74f14d1886SSoby Mathew 	mpidr = read_mpidr_el1();
75f14d1886SSoby Mathew 
76f14d1886SSoby Mathew 	/* Perform the common cluster specific operations */
77f14d1886SSoby Mathew 	if (target_state->pwr_domain_state[ARM_PWR_LVL1] ==
78f14d1886SSoby Mathew 					ARM_LOCAL_STATE_OFF) {
79f14d1886SSoby Mathew 		/*
80f14d1886SSoby Mathew 		 * This CPU might have woken up whilst the cluster was
81f14d1886SSoby Mathew 		 * attempting to power down. In this case the FVP power
82f14d1886SSoby Mathew 		 * controller will have a pending cluster power off request
83f14d1886SSoby Mathew 		 * which needs to be cleared by writing to the PPONR register.
84f14d1886SSoby Mathew 		 * This prevents the power controller from interpreting a
85f14d1886SSoby Mathew 		 * subsequent entry of this cpu into a simple wfi as a power
86f14d1886SSoby Mathew 		 * down request.
87f14d1886SSoby Mathew 		 */
88f14d1886SSoby Mathew 		fvp_pwrc_write_pponr(mpidr);
89f14d1886SSoby Mathew 
90f14d1886SSoby Mathew 		/* Enable coherency if this cluster was off */
916355f234SVikram Kanigiri 		fvp_interconnect_enable();
92f14d1886SSoby Mathew 	}
93f14d1886SSoby Mathew 
94f14d1886SSoby Mathew 	/*
95f14d1886SSoby Mathew 	 * Clear PWKUPR.WEN bit to ensure interrupts do not interfere
96f14d1886SSoby Mathew 	 * with a cpu power down unless the bit is set again
97f14d1886SSoby Mathew 	 */
98f14d1886SSoby Mathew 	fvp_pwrc_clr_wen(mpidr);
99f14d1886SSoby Mathew }
100f14d1886SSoby Mathew 
101f14d1886SSoby Mathew 
1023fc4124cSDan Handley /*******************************************************************************
10338dce70fSSoby Mathew  * FVP handler called when a CPU is about to enter standby.
1043fc4124cSDan Handley  ******************************************************************************/
10538dce70fSSoby Mathew void fvp_cpu_standby(plat_local_state_t cpu_state)
1063fc4124cSDan Handley {
10738dce70fSSoby Mathew 
10838dce70fSSoby Mathew 	assert(cpu_state == ARM_LOCAL_STATE_RET);
10938dce70fSSoby Mathew 
1103fc4124cSDan Handley 	/*
1113fc4124cSDan Handley 	 * Enter standby state
1123fc4124cSDan Handley 	 * dsb is good practice before using wfi to enter low power states
1133fc4124cSDan Handley 	 */
1143fc4124cSDan Handley 	dsb();
1153fc4124cSDan Handley 	wfi();
1163fc4124cSDan Handley }
1173fc4124cSDan Handley 
1183fc4124cSDan Handley /*******************************************************************************
11938dce70fSSoby Mathew  * FVP handler called when a power domain is about to be turned on. The
12038dce70fSSoby Mathew  * mpidr determines the CPU to be turned on.
1213fc4124cSDan Handley  ******************************************************************************/
12238dce70fSSoby Mathew int fvp_pwr_domain_on(u_register_t mpidr)
1233fc4124cSDan Handley {
1243fc4124cSDan Handley 	int rc = PSCI_E_SUCCESS;
1253fc4124cSDan Handley 	unsigned int psysr;
1263fc4124cSDan Handley 
1273fc4124cSDan Handley 	/*
1280f09c8f7SSandrine Bailleux 	 * Ensure that we do not cancel an inflight power off request for the
1290f09c8f7SSandrine Bailleux 	 * target cpu. That would leave it in a zombie wfi. Wait for it to power
1300f09c8f7SSandrine Bailleux 	 * off and then program the power controller to turn that CPU on.
1313fc4124cSDan Handley 	 */
1323fc4124cSDan Handley 	do {
1333fc4124cSDan Handley 		psysr = fvp_pwrc_read_psysr(mpidr);
1343fc4124cSDan Handley 	} while (psysr & PSYSR_AFF_L0);
1353fc4124cSDan Handley 
1363fc4124cSDan Handley 	fvp_pwrc_write_pponr(mpidr);
1373fc4124cSDan Handley 	return rc;
1383fc4124cSDan Handley }
1393fc4124cSDan Handley 
1403fc4124cSDan Handley /*******************************************************************************
14138dce70fSSoby Mathew  * FVP handler called when a power domain is about to be turned off. The
14238dce70fSSoby Mathew  * target_state encodes the power state that each level should transition to.
1433fc4124cSDan Handley  ******************************************************************************/
14438dce70fSSoby Mathew void fvp_pwr_domain_off(const psci_power_state_t *target_state)
1453fc4124cSDan Handley {
14638dce70fSSoby Mathew 	assert(target_state->pwr_domain_state[ARM_PWR_LVL0] ==
14738dce70fSSoby Mathew 					ARM_LOCAL_STATE_OFF);
1483fc4124cSDan Handley 
1493fc4124cSDan Handley 	/*
15038dce70fSSoby Mathew 	 * If execution reaches this stage then this power domain will be
15138dce70fSSoby Mathew 	 * suspended. Perform at least the cpu specific actions followed
15238dce70fSSoby Mathew 	 * by the cluster specific operations if applicable.
1533fc4124cSDan Handley 	 */
15474a9578cSJeenu Viswambharan 
15574a9578cSJeenu Viswambharan 	/* Prevent interrupts from spuriously waking up this cpu */
15674a9578cSJeenu Viswambharan 	plat_arm_gic_cpuif_disable();
15774a9578cSJeenu Viswambharan 
15874a9578cSJeenu Viswambharan 	/* Turn redistributor off */
15974a9578cSJeenu Viswambharan 	plat_arm_gic_redistif_off();
16074a9578cSJeenu Viswambharan 
16174a9578cSJeenu Viswambharan 	/* Program the power controller to power off this cpu. */
16274a9578cSJeenu Viswambharan 	fvp_pwrc_write_ppoffr(read_mpidr_el1());
1633fc4124cSDan Handley 
16438dce70fSSoby Mathew 	if (target_state->pwr_domain_state[ARM_PWR_LVL1] ==
16538dce70fSSoby Mathew 					ARM_LOCAL_STATE_OFF)
1663fc4124cSDan Handley 		fvp_cluster_pwrdwn_common();
1673fc4124cSDan Handley 
1683fc4124cSDan Handley }
1693fc4124cSDan Handley 
1703fc4124cSDan Handley /*******************************************************************************
17138dce70fSSoby Mathew  * FVP handler called when a power domain is about to be suspended. The
17238dce70fSSoby Mathew  * target_state encodes the power state that each level should transition to.
1733fc4124cSDan Handley  ******************************************************************************/
17438dce70fSSoby Mathew void fvp_pwr_domain_suspend(const psci_power_state_t *target_state)
1753fc4124cSDan Handley {
1763fc4124cSDan Handley 	unsigned long mpidr;
1773fc4124cSDan Handley 
17838dce70fSSoby Mathew 	/*
17938dce70fSSoby Mathew 	 * FVP has retention only at cpu level. Just return
18038dce70fSSoby Mathew 	 * as nothing is to be done for retention.
18138dce70fSSoby Mathew 	 */
18238dce70fSSoby Mathew 	if (target_state->pwr_domain_state[ARM_PWR_LVL0] ==
18338dce70fSSoby Mathew 					ARM_LOCAL_STATE_RET)
1843fc4124cSDan Handley 		return;
1853fc4124cSDan Handley 
18638dce70fSSoby Mathew 	assert(target_state->pwr_domain_state[ARM_PWR_LVL0] ==
18738dce70fSSoby Mathew 					ARM_LOCAL_STATE_OFF);
18838dce70fSSoby Mathew 
1893fc4124cSDan Handley 	/* Get the mpidr for this cpu */
1903fc4124cSDan Handley 	mpidr = read_mpidr_el1();
1913fc4124cSDan Handley 
1923fc4124cSDan Handley 	/* Program the power controller to enable wakeup interrupts. */
1933fc4124cSDan Handley 	fvp_pwrc_set_wen(mpidr);
1943fc4124cSDan Handley 
19574a9578cSJeenu Viswambharan 	/* Prevent interrupts from spuriously waking up this cpu */
19674a9578cSJeenu Viswambharan 	plat_arm_gic_cpuif_disable();
19774a9578cSJeenu Viswambharan 
19874a9578cSJeenu Viswambharan 	/*
19974a9578cSJeenu Viswambharan 	 * The Redistributor is not powered off as it can potentially prevent
20074a9578cSJeenu Viswambharan 	 * wake up events reaching the CPUIF and/or might lead to losing
20174a9578cSJeenu Viswambharan 	 * register context.
20274a9578cSJeenu Viswambharan 	 */
20374a9578cSJeenu Viswambharan 
20474a9578cSJeenu Viswambharan 	/* Program the power controller to power off this cpu. */
20574a9578cSJeenu Viswambharan 	fvp_pwrc_write_ppoffr(read_mpidr_el1());
2063fc4124cSDan Handley 
2073fc4124cSDan Handley 	/* Perform the common cluster specific operations */
20838dce70fSSoby Mathew 	if (target_state->pwr_domain_state[ARM_PWR_LVL1] ==
20938dce70fSSoby Mathew 					ARM_LOCAL_STATE_OFF)
2103fc4124cSDan Handley 		fvp_cluster_pwrdwn_common();
2113fc4124cSDan Handley }
2123fc4124cSDan Handley 
2133fc4124cSDan Handley /*******************************************************************************
21438dce70fSSoby Mathew  * FVP handler called when a power domain has just been powered on after
21538dce70fSSoby Mathew  * being turned off earlier. The target_state encodes the low power state that
21638dce70fSSoby Mathew  * each level has woken up from.
2173fc4124cSDan Handley  ******************************************************************************/
21838dce70fSSoby Mathew void fvp_pwr_domain_on_finish(const psci_power_state_t *target_state)
2193fc4124cSDan Handley {
220f14d1886SSoby Mathew 	fvp_power_domain_on_finish_common(target_state);
2213fc4124cSDan Handley 
2223fc4124cSDan Handley 	/* Enable the gic cpu interface */
22327573c59SAchin Gupta 	plat_arm_gic_pcpu_init();
22427573c59SAchin Gupta 
22527573c59SAchin Gupta 	/* Program the gic per-cpu distributor or re-distributor interface */
22627573c59SAchin Gupta 	plat_arm_gic_cpuif_enable();
2273fc4124cSDan Handley }
2283fc4124cSDan Handley 
2293fc4124cSDan Handley /*******************************************************************************
23038dce70fSSoby Mathew  * FVP handler called when a power domain has just been powered on after
23138dce70fSSoby Mathew  * having been suspended earlier. The target_state encodes the low power state
23238dce70fSSoby Mathew  * that each level has woken up from.
2333fc4124cSDan Handley  * TODO: At the moment we reuse the on finisher and reinitialize the secure
2343fc4124cSDan Handley  * context. Need to implement a separate suspend finisher.
2353fc4124cSDan Handley  ******************************************************************************/
23638dce70fSSoby Mathew void fvp_pwr_domain_suspend_finish(const psci_power_state_t *target_state)
2373fc4124cSDan Handley {
23838dce70fSSoby Mathew 	/*
23938dce70fSSoby Mathew 	 * Nothing to be done on waking up from retention from CPU level.
24038dce70fSSoby Mathew 	 */
24138dce70fSSoby Mathew 	if (target_state->pwr_domain_state[ARM_PWR_LVL0] ==
24238dce70fSSoby Mathew 					ARM_LOCAL_STATE_RET)
24338dce70fSSoby Mathew 		return;
24438dce70fSSoby Mathew 
245f14d1886SSoby Mathew 	fvp_power_domain_on_finish_common(target_state);
246f14d1886SSoby Mathew 
247f14d1886SSoby Mathew 	/* Enable the gic cpu interface */
24827573c59SAchin Gupta 	plat_arm_gic_cpuif_enable();
2493fc4124cSDan Handley }
2503fc4124cSDan Handley 
2513fc4124cSDan Handley /*******************************************************************************
2523fc4124cSDan Handley  * FVP handlers to shutdown/reboot the system
2533fc4124cSDan Handley  ******************************************************************************/
2543fc4124cSDan Handley static void __dead2 fvp_system_off(void)
2553fc4124cSDan Handley {
2563fc4124cSDan Handley 	/* Write the System Configuration Control Register */
2573fc4124cSDan Handley 	mmio_write_32(V2M_SYSREGS_BASE + V2M_SYS_CFGCTRL,
2583fc4124cSDan Handley 		V2M_CFGCTRL_START |
2593fc4124cSDan Handley 		V2M_CFGCTRL_RW |
2603fc4124cSDan Handley 		V2M_CFGCTRL_FUNC(V2M_FUNC_SHUTDOWN));
2613fc4124cSDan Handley 	wfi();
2623fc4124cSDan Handley 	ERROR("FVP System Off: operation not handled.\n");
2633fc4124cSDan Handley 	panic();
2643fc4124cSDan Handley }
2653fc4124cSDan Handley 
2663fc4124cSDan Handley static void __dead2 fvp_system_reset(void)
2673fc4124cSDan Handley {
2683fc4124cSDan Handley 	/* Write the System Configuration Control Register */
2693fc4124cSDan Handley 	mmio_write_32(V2M_SYSREGS_BASE + V2M_SYS_CFGCTRL,
2703fc4124cSDan Handley 		V2M_CFGCTRL_START |
2713fc4124cSDan Handley 		V2M_CFGCTRL_RW |
2723fc4124cSDan Handley 		V2M_CFGCTRL_FUNC(V2M_FUNC_REBOOT));
2733fc4124cSDan Handley 	wfi();
2743fc4124cSDan Handley 	ERROR("FVP System Reset: operation not handled.\n");
2753fc4124cSDan Handley 	panic();
2763fc4124cSDan Handley }
2773fc4124cSDan Handley 
2781298ae02SJeenu Viswambharan static int fvp_node_hw_state(u_register_t target_cpu,
2791298ae02SJeenu Viswambharan 			     unsigned int power_level)
2801298ae02SJeenu Viswambharan {
2811298ae02SJeenu Viswambharan 	unsigned int psysr;
2821298ae02SJeenu Viswambharan 	int ret;
2831298ae02SJeenu Viswambharan 
2841298ae02SJeenu Viswambharan 	/*
2851298ae02SJeenu Viswambharan 	 * The format of 'power_level' is implementation-defined, but 0 must
2861298ae02SJeenu Viswambharan 	 * mean a CPU. We also allow 1 to denote the cluster
2871298ae02SJeenu Viswambharan 	 */
2881298ae02SJeenu Viswambharan 	if (power_level != ARM_PWR_LVL0 && power_level != ARM_PWR_LVL1)
2891298ae02SJeenu Viswambharan 		return PSCI_E_INVALID_PARAMS;
2901298ae02SJeenu Viswambharan 
2911298ae02SJeenu Viswambharan 	/*
2921298ae02SJeenu Viswambharan 	 * Read the status of the given MPDIR from FVP power controller. The
2931298ae02SJeenu Viswambharan 	 * power controller only gives us on/off status, so map that to expected
2941298ae02SJeenu Viswambharan 	 * return values of the PSCI call
2951298ae02SJeenu Viswambharan 	 */
2961298ae02SJeenu Viswambharan 	psysr = fvp_pwrc_read_psysr(target_cpu);
2971298ae02SJeenu Viswambharan 	if (psysr == PSYSR_INVALID)
2981298ae02SJeenu Viswambharan 		return PSCI_E_INVALID_PARAMS;
2991298ae02SJeenu Viswambharan 
3001298ae02SJeenu Viswambharan 	switch (power_level) {
3011298ae02SJeenu Viswambharan 	case ARM_PWR_LVL0:
3021298ae02SJeenu Viswambharan 		ret = (psysr & PSYSR_AFF_L0) ? HW_ON : HW_OFF;
3031298ae02SJeenu Viswambharan 		break;
3041298ae02SJeenu Viswambharan 	case ARM_PWR_LVL1:
3051298ae02SJeenu Viswambharan 		ret = (psysr & PSYSR_AFF_L1) ? HW_ON : HW_OFF;
3061298ae02SJeenu Viswambharan 		break;
3071298ae02SJeenu Viswambharan 	}
3081298ae02SJeenu Viswambharan 
3091298ae02SJeenu Viswambharan 	return ret;
3101298ae02SJeenu Viswambharan }
3111298ae02SJeenu Viswambharan 
3123fc4124cSDan Handley /*******************************************************************************
313785fb92bSSoby Mathew  * Export the platform handlers via plat_arm_psci_pm_ops. The ARM Standard
314785fb92bSSoby Mathew  * platform layer will take care of registering the handlers with PSCI.
3153fc4124cSDan Handley  ******************************************************************************/
3165486a965SSoby Mathew plat_psci_ops_t plat_arm_psci_pm_ops = {
31738dce70fSSoby Mathew 	.cpu_standby = fvp_cpu_standby,
31838dce70fSSoby Mathew 	.pwr_domain_on = fvp_pwr_domain_on,
31938dce70fSSoby Mathew 	.pwr_domain_off = fvp_pwr_domain_off,
32038dce70fSSoby Mathew 	.pwr_domain_suspend = fvp_pwr_domain_suspend,
32138dce70fSSoby Mathew 	.pwr_domain_on_finish = fvp_pwr_domain_on_finish,
32238dce70fSSoby Mathew 	.pwr_domain_suspend_finish = fvp_pwr_domain_suspend_finish,
3233fc4124cSDan Handley 	.system_off = fvp_system_off,
3243fc4124cSDan Handley 	.system_reset = fvp_system_reset,
325f9e858b1SSoby Mathew 	.validate_power_state = arm_validate_power_state,
3261298ae02SJeenu Viswambharan 	.validate_ns_entrypoint = arm_validate_ns_entrypoint,
3271298ae02SJeenu Viswambharan 	.get_node_hw_state = fvp_node_hw_state
3283fc4124cSDan Handley };
329