xref: /rk3399_ARM-atf/plat/amlogic/gxbb/gxbb_pm.c (revision 4a079c752beef8c2e8072b55a267d4b597b1e05b)
1*4a079c75SCarlo Caione /*
2*4a079c75SCarlo Caione  * Copyright (c) 2018, ARM Limited and Contributors. All rights reserved.
3*4a079c75SCarlo Caione  *
4*4a079c75SCarlo Caione  * SPDX-License-Identifier: BSD-3-Clause
5*4a079c75SCarlo Caione  */
6*4a079c75SCarlo Caione 
7*4a079c75SCarlo Caione #include <assert.h>
8*4a079c75SCarlo Caione #include <errno.h>
9*4a079c75SCarlo Caione 
10*4a079c75SCarlo Caione #include <platform_def.h>
11*4a079c75SCarlo Caione 
12*4a079c75SCarlo Caione #include <arch_helpers.h>
13*4a079c75SCarlo Caione #include <common/debug.h>
14*4a079c75SCarlo Caione #include <drivers/arm/gicv2.h>
15*4a079c75SCarlo Caione #include <drivers/console.h>
16*4a079c75SCarlo Caione #include <lib/mmio.h>
17*4a079c75SCarlo Caione #include <lib/psci/psci.h>
18*4a079c75SCarlo Caione #include <plat/common/platform.h>
19*4a079c75SCarlo Caione 
20*4a079c75SCarlo Caione #include "gxbb_private.h"
21*4a079c75SCarlo Caione 
22*4a079c75SCarlo Caione #define SCPI_POWER_ON		0
23*4a079c75SCarlo Caione #define SCPI_POWER_RETENTION	1
24*4a079c75SCarlo Caione #define SCPI_POWER_OFF		3
25*4a079c75SCarlo Caione 
26*4a079c75SCarlo Caione #define SCPI_SYSTEM_SHUTDOWN	0
27*4a079c75SCarlo Caione #define SCPI_SYSTEM_REBOOT	1
28*4a079c75SCarlo Caione 
29*4a079c75SCarlo Caione static uintptr_t gxbb_sec_entrypoint;
30*4a079c75SCarlo Caione static volatile uint32_t gxbb_cpu0_go;
31*4a079c75SCarlo Caione 
32*4a079c75SCarlo Caione static void gxbb_program_mailbox(u_register_t mpidr, uint64_t value)
33*4a079c75SCarlo Caione {
34*4a079c75SCarlo Caione 	unsigned int core = plat_gxbb_calc_core_pos(mpidr);
35*4a079c75SCarlo Caione 	uintptr_t cpu_mailbox_addr = GXBB_PSCI_MAILBOX_BASE + (core << 4);
36*4a079c75SCarlo Caione 
37*4a079c75SCarlo Caione 	mmio_write_64(cpu_mailbox_addr, value);
38*4a079c75SCarlo Caione 	flush_dcache_range(cpu_mailbox_addr, sizeof(uint64_t));
39*4a079c75SCarlo Caione }
40*4a079c75SCarlo Caione 
41*4a079c75SCarlo Caione static void __dead2 gxbb_system_reset(void)
42*4a079c75SCarlo Caione {
43*4a079c75SCarlo Caione 	INFO("BL31: PSCI_SYSTEM_RESET\n");
44*4a079c75SCarlo Caione 
45*4a079c75SCarlo Caione 	uint32_t status = mmio_read_32(GXBB_AO_RTI_STATUS_REG3);
46*4a079c75SCarlo Caione 
47*4a079c75SCarlo Caione 	NOTICE("BL31: Reboot reason: 0x%x\n", status);
48*4a079c75SCarlo Caione 
49*4a079c75SCarlo Caione 	status &= 0xFFFF0FF0;
50*4a079c75SCarlo Caione 
51*4a079c75SCarlo Caione 	console_flush();
52*4a079c75SCarlo Caione 
53*4a079c75SCarlo Caione 	mmio_write_32(GXBB_AO_RTI_STATUS_REG3, status);
54*4a079c75SCarlo Caione 
55*4a079c75SCarlo Caione 	int ret = scpi_sys_power_state(SCPI_SYSTEM_REBOOT);
56*4a079c75SCarlo Caione 
57*4a079c75SCarlo Caione 	if (ret != 0) {
58*4a079c75SCarlo Caione 		ERROR("BL31: PSCI_SYSTEM_RESET: SCP error: %u\n", ret);
59*4a079c75SCarlo Caione 		panic();
60*4a079c75SCarlo Caione 	}
61*4a079c75SCarlo Caione 
62*4a079c75SCarlo Caione 	wfi();
63*4a079c75SCarlo Caione 
64*4a079c75SCarlo Caione 	ERROR("BL31: PSCI_SYSTEM_RESET: Operation not handled\n");
65*4a079c75SCarlo Caione 	panic();
66*4a079c75SCarlo Caione }
67*4a079c75SCarlo Caione 
68*4a079c75SCarlo Caione static void __dead2 gxbb_system_off(void)
69*4a079c75SCarlo Caione {
70*4a079c75SCarlo Caione 	INFO("BL31: PSCI_SYSTEM_OFF\n");
71*4a079c75SCarlo Caione 
72*4a079c75SCarlo Caione 	unsigned int ret = scpi_sys_power_state(SCPI_SYSTEM_SHUTDOWN);
73*4a079c75SCarlo Caione 
74*4a079c75SCarlo Caione 	if (ret != 0) {
75*4a079c75SCarlo Caione 		ERROR("BL31: PSCI_SYSTEM_OFF: SCP error %u\n", ret);
76*4a079c75SCarlo Caione 		panic();
77*4a079c75SCarlo Caione 	}
78*4a079c75SCarlo Caione 
79*4a079c75SCarlo Caione 	gxbb_program_mailbox(read_mpidr_el1(), 0);
80*4a079c75SCarlo Caione 
81*4a079c75SCarlo Caione 	wfi();
82*4a079c75SCarlo Caione 
83*4a079c75SCarlo Caione 	ERROR("BL31: PSCI_SYSTEM_OFF: Operation not handled\n");
84*4a079c75SCarlo Caione 	panic();
85*4a079c75SCarlo Caione }
86*4a079c75SCarlo Caione 
87*4a079c75SCarlo Caione static int32_t gxbb_pwr_domain_on(u_register_t mpidr)
88*4a079c75SCarlo Caione {
89*4a079c75SCarlo Caione 	unsigned int core = plat_gxbb_calc_core_pos(mpidr);
90*4a079c75SCarlo Caione 
91*4a079c75SCarlo Caione 	/* CPU0 can't be turned OFF, emulate it with a WFE loop */
92*4a079c75SCarlo Caione 	if (core == GXBB_PRIMARY_CPU) {
93*4a079c75SCarlo Caione 		VERBOSE("BL31: Releasing CPU0 from wait loop...\n");
94*4a079c75SCarlo Caione 
95*4a079c75SCarlo Caione 		gxbb_cpu0_go = 1;
96*4a079c75SCarlo Caione 		flush_dcache_range((uintptr_t)&gxbb_cpu0_go, sizeof(gxbb_cpu0_go));
97*4a079c75SCarlo Caione 		dsb();
98*4a079c75SCarlo Caione 		isb();
99*4a079c75SCarlo Caione 
100*4a079c75SCarlo Caione 		sev();
101*4a079c75SCarlo Caione 
102*4a079c75SCarlo Caione 		return PSCI_E_SUCCESS;
103*4a079c75SCarlo Caione 	}
104*4a079c75SCarlo Caione 
105*4a079c75SCarlo Caione 	gxbb_program_mailbox(mpidr, gxbb_sec_entrypoint);
106*4a079c75SCarlo Caione 	scpi_set_css_power_state(mpidr,
107*4a079c75SCarlo Caione 				 SCPI_POWER_ON, SCPI_POWER_ON, SCPI_POWER_ON);
108*4a079c75SCarlo Caione 	dmbsy();
109*4a079c75SCarlo Caione 	sev();
110*4a079c75SCarlo Caione 
111*4a079c75SCarlo Caione 	return PSCI_E_SUCCESS;
112*4a079c75SCarlo Caione }
113*4a079c75SCarlo Caione 
114*4a079c75SCarlo Caione static void gxbb_pwr_domain_on_finish(const psci_power_state_t *target_state)
115*4a079c75SCarlo Caione {
116*4a079c75SCarlo Caione 	unsigned int core = plat_gxbb_calc_core_pos(read_mpidr_el1());
117*4a079c75SCarlo Caione 
118*4a079c75SCarlo Caione 	assert(target_state->pwr_domain_state[MPIDR_AFFLVL0] ==
119*4a079c75SCarlo Caione 					PLAT_LOCAL_STATE_OFF);
120*4a079c75SCarlo Caione 
121*4a079c75SCarlo Caione 	if (core == GXBB_PRIMARY_CPU) {
122*4a079c75SCarlo Caione 		gxbb_cpu0_go = 0;
123*4a079c75SCarlo Caione 		flush_dcache_range((uintptr_t)&gxbb_cpu0_go, sizeof(gxbb_cpu0_go));
124*4a079c75SCarlo Caione 		dsb();
125*4a079c75SCarlo Caione 		isb();
126*4a079c75SCarlo Caione 	}
127*4a079c75SCarlo Caione 
128*4a079c75SCarlo Caione 	gicv2_pcpu_distif_init();
129*4a079c75SCarlo Caione 	gicv2_cpuif_enable();
130*4a079c75SCarlo Caione }
131*4a079c75SCarlo Caione 
132*4a079c75SCarlo Caione static void gxbb_pwr_domain_off(const psci_power_state_t *target_state)
133*4a079c75SCarlo Caione {
134*4a079c75SCarlo Caione 	u_register_t mpidr = read_mpidr_el1();
135*4a079c75SCarlo Caione 	unsigned int core = plat_gxbb_calc_core_pos(mpidr);
136*4a079c75SCarlo Caione 	uintptr_t addr = GXBB_PSCI_MAILBOX_BASE + 8 + (core << 4);
137*4a079c75SCarlo Caione 
138*4a079c75SCarlo Caione 	mmio_write_32(addr, 0xFFFFFFFF);
139*4a079c75SCarlo Caione 	flush_dcache_range(addr, sizeof(uint32_t));
140*4a079c75SCarlo Caione 
141*4a079c75SCarlo Caione 	gicv2_cpuif_disable();
142*4a079c75SCarlo Caione 
143*4a079c75SCarlo Caione 	/* CPU0 can't be turned OFF, emulate it with a WFE loop */
144*4a079c75SCarlo Caione 	if (core == GXBB_PRIMARY_CPU)
145*4a079c75SCarlo Caione 		return;
146*4a079c75SCarlo Caione 
147*4a079c75SCarlo Caione 	scpi_set_css_power_state(mpidr,
148*4a079c75SCarlo Caione 				 SCPI_POWER_OFF, SCPI_POWER_ON, SCPI_POWER_ON);
149*4a079c75SCarlo Caione }
150*4a079c75SCarlo Caione 
151*4a079c75SCarlo Caione static void __dead2 gxbb_pwr_domain_pwr_down_wfi(const psci_power_state_t
152*4a079c75SCarlo Caione 						 *target_state)
153*4a079c75SCarlo Caione {
154*4a079c75SCarlo Caione 	unsigned int core = plat_gxbb_calc_core_pos(read_mpidr_el1());
155*4a079c75SCarlo Caione 
156*4a079c75SCarlo Caione 	/* CPU0 can't be turned OFF, emulate it with a WFE loop */
157*4a079c75SCarlo Caione 	if (core == GXBB_PRIMARY_CPU) {
158*4a079c75SCarlo Caione 		VERBOSE("BL31: CPU0 entering wait loop...\n");
159*4a079c75SCarlo Caione 
160*4a079c75SCarlo Caione 		while (gxbb_cpu0_go == 0)
161*4a079c75SCarlo Caione 			wfe();
162*4a079c75SCarlo Caione 
163*4a079c75SCarlo Caione 		VERBOSE("BL31: CPU0 resumed.\n");
164*4a079c75SCarlo Caione 
165*4a079c75SCarlo Caione 		write_rmr_el3(RMR_EL3_RR_BIT | RMR_EL3_AA64_BIT);
166*4a079c75SCarlo Caione 	}
167*4a079c75SCarlo Caione 
168*4a079c75SCarlo Caione 	dsbsy();
169*4a079c75SCarlo Caione 
170*4a079c75SCarlo Caione 	for (;;)
171*4a079c75SCarlo Caione 		wfi();
172*4a079c75SCarlo Caione }
173*4a079c75SCarlo Caione 
174*4a079c75SCarlo Caione /*******************************************************************************
175*4a079c75SCarlo Caione  * Platform handlers and setup function.
176*4a079c75SCarlo Caione  ******************************************************************************/
177*4a079c75SCarlo Caione static const plat_psci_ops_t gxbb_ops = {
178*4a079c75SCarlo Caione 	.pwr_domain_on			= gxbb_pwr_domain_on,
179*4a079c75SCarlo Caione 	.pwr_domain_on_finish		= gxbb_pwr_domain_on_finish,
180*4a079c75SCarlo Caione 	.pwr_domain_off			= gxbb_pwr_domain_off,
181*4a079c75SCarlo Caione 	.pwr_domain_pwr_down_wfi	= gxbb_pwr_domain_pwr_down_wfi,
182*4a079c75SCarlo Caione 	.system_off			= gxbb_system_off,
183*4a079c75SCarlo Caione 	.system_reset			= gxbb_system_reset,
184*4a079c75SCarlo Caione };
185*4a079c75SCarlo Caione 
186*4a079c75SCarlo Caione int plat_setup_psci_ops(uintptr_t sec_entrypoint,
187*4a079c75SCarlo Caione 			const plat_psci_ops_t **psci_ops)
188*4a079c75SCarlo Caione {
189*4a079c75SCarlo Caione 	gxbb_sec_entrypoint = sec_entrypoint;
190*4a079c75SCarlo Caione 	*psci_ops = &gxbb_ops;
191*4a079c75SCarlo Caione 	gxbb_cpu0_go = 0;
192*4a079c75SCarlo Caione 	return 0;
193*4a079c75SCarlo Caione }
194