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