xref: /rk3399_ARM-atf/plat/renesas/rcar_gen5/plat_pm_scmi.c (revision 7cab2c233b38cf2a63d37d31da73f1d82f5efc3c)
1*f180a3b7SHieu Nguyen /*
2*f180a3b7SHieu Nguyen  * Copyright (c) 2025, Renesas Electronics Corporation. All rights reserved.
3*f180a3b7SHieu Nguyen  *
4*f180a3b7SHieu Nguyen  * SPDX-License-Identifier: BSD-3-Clause
5*f180a3b7SHieu Nguyen  */
6*f180a3b7SHieu Nguyen 
7*f180a3b7SHieu Nguyen #include <assert.h>
8*f180a3b7SHieu Nguyen #include <string.h>
9*f180a3b7SHieu Nguyen 
10*f180a3b7SHieu Nguyen #include <arch_helpers.h>
11*f180a3b7SHieu Nguyen #include <common/debug.h>
12*f180a3b7SHieu Nguyen #include <drivers/arm/css/css_scp.h>
13*f180a3b7SHieu Nguyen #include <drivers/arm/css/scmi.h>
14*f180a3b7SHieu Nguyen #include <lib/bakery_lock.h>
15*f180a3b7SHieu Nguyen #include <lib/mmio.h>
16*f180a3b7SHieu Nguyen #include <plat/common/platform.h>
17*f180a3b7SHieu Nguyen #include "scmi_private.h"
18*f180a3b7SHieu Nguyen 
19*f180a3b7SHieu Nguyen #include <platform_def.h>
20*f180a3b7SHieu Nguyen #include "rcar_def.h"
21*f180a3b7SHieu Nguyen #include "rcar_private.h"
22*f180a3b7SHieu Nguyen 
23*f180a3b7SHieu Nguyen /*
24*f180a3b7SHieu Nguyen  * This file implements the SCP helper functions using SCMI protocol.
25*f180a3b7SHieu Nguyen  */
26*f180a3b7SHieu Nguyen 
27*f180a3b7SHieu Nguyen #define POWER_ON_STATE		0x00000000U
28*f180a3b7SHieu Nguyen #define POWER_OFF_STATE		0x40000000U
29*f180a3b7SHieu Nguyen 
30*f180a3b7SHieu Nguyen /*
31*f180a3b7SHieu Nguyen  * The handles for invoking the SCMI driver APIs after the driver
32*f180a3b7SHieu Nguyen  * has been initialized.
33*f180a3b7SHieu Nguyen  */
34*f180a3b7SHieu Nguyen static void *scmi_handle;
35*f180a3b7SHieu Nguyen 
36*f180a3b7SHieu Nguyen /* The SCMI channel info */
37*f180a3b7SHieu Nguyen static scmi_channel_t sscmi_channel;
38*f180a3b7SHieu Nguyen 
39*f180a3b7SHieu Nguyen /*
40*f180a3b7SHieu Nguyen  * Allow use of channel specific lock instead of using a single lock for
41*f180a3b7SHieu Nguyen  * all the channels.
42*f180a3b7SHieu Nguyen  */
43*f180a3b7SHieu Nguyen static RCAR_SCMI_INSTANTIATE_LOCK;
44*f180a3b7SHieu Nguyen 
45*f180a3b7SHieu Nguyen /* The SCMI platform info holder in system ram */
46*f180a3b7SHieu Nguyen static scmi_channel_plat_info_t scmi_plat_info;
47*f180a3b7SHieu Nguyen 
48*f180a3b7SHieu Nguyen static uint32_t rcar_pwrc_core_pos(u_register_t mpidr)
49*f180a3b7SHieu Nguyen {
50*f180a3b7SHieu Nguyen 	int cpu;
51*f180a3b7SHieu Nguyen 
52*f180a3b7SHieu Nguyen 	cpu = plat_core_pos_by_mpidr(mpidr);
53*f180a3b7SHieu Nguyen 	if (cpu < 0) {
54*f180a3b7SHieu Nguyen 		ERROR("BL3-1 : The value of passed MPIDR is invalid.");
55*f180a3b7SHieu Nguyen 	}
56*f180a3b7SHieu Nguyen 
57*f180a3b7SHieu Nguyen 	return (uint32_t)cpu;
58*f180a3b7SHieu Nguyen }
59*f180a3b7SHieu Nguyen 
60*f180a3b7SHieu Nguyen static void rcar_ring_doorbell(struct scmi_channel_plat_info *plat_info)
61*f180a3b7SHieu Nguyen {
62*f180a3b7SHieu Nguyen 	u_register_t reg = plat_info->db_reg_addr;
63*f180a3b7SHieu Nguyen 	uint32_t register_value;
64*f180a3b7SHieu Nguyen 
65*f180a3b7SHieu Nguyen 	/* Write Access Control Register */
66*f180a3b7SHieu Nguyen 	/* MFIS_MFISASEICR0 Register address setting */
67*f180a3b7SHieu Nguyen 	register_value = MFISWACNTR_SCP_CODEVALUE_SET;
68*f180a3b7SHieu Nguyen 	register_value |= (reg & MFISWACNTR_SCP_REGISTERADDRESS_MASK);
69*f180a3b7SHieu Nguyen 	mmio_write_32(MFIS_MFISWACNTR_SCP, register_value);
70*f180a3b7SHieu Nguyen 	/* Send Interrupt */
71*f180a3b7SHieu Nguyen 	mmio_setbits_32(reg, plat_info->db_modify_mask);
72*f180a3b7SHieu Nguyen }
73*f180a3b7SHieu Nguyen 
74*f180a3b7SHieu Nguyen /*
75*f180a3b7SHieu Nguyen  * Function to obtain the SCMI Domain ID and SCMI Channel number from the linear
76*f180a3b7SHieu Nguyen  * core position. The SCMI Channel number is encoded in the upper 16 bits and
77*f180a3b7SHieu Nguyen  * the Domain ID is encoded in the lower 16 bits in each entry of the mapping
78*f180a3b7SHieu Nguyen  * array exported by the platform.
79*f180a3b7SHieu Nguyen  */
80*f180a3b7SHieu Nguyen static void css_scp_core_pos_to_scmi_channel(unsigned int core_pos,
81*f180a3b7SHieu Nguyen 		unsigned int *scmi_domain_id, unsigned int *scmi_channel_id)
82*f180a3b7SHieu Nguyen {
83*f180a3b7SHieu Nguyen 	unsigned int composite_id;
84*f180a3b7SHieu Nguyen 
85*f180a3b7SHieu Nguyen 	composite_id = plat_css_core_pos_to_scmi_dmn_id_map[core_pos];
86*f180a3b7SHieu Nguyen 
87*f180a3b7SHieu Nguyen 	*scmi_channel_id = GET_SCMI_CHANNEL_ID(composite_id);
88*f180a3b7SHieu Nguyen 	*scmi_domain_id = GET_SCMI_DOMAIN_ID(composite_id);
89*f180a3b7SHieu Nguyen }
90*f180a3b7SHieu Nguyen 
91*f180a3b7SHieu Nguyen /*
92*f180a3b7SHieu Nguyen  * Helper function to turn ON a CPU power domain and its parent power domains
93*f180a3b7SHieu Nguyen  * if applicable.
94*f180a3b7SHieu Nguyen  */
95*f180a3b7SHieu Nguyen void rcar_scmi_cpuon(u_register_t mpidr)
96*f180a3b7SHieu Nguyen {
97*f180a3b7SHieu Nguyen 	unsigned int channel_id, core_pos, domain_id;
98*f180a3b7SHieu Nguyen 	int ret;
99*f180a3b7SHieu Nguyen 	uint32_t scmi_pwr_state = 0;
100*f180a3b7SHieu Nguyen 
101*f180a3b7SHieu Nguyen 	core_pos = rcar_pwrc_core_pos(mpidr);
102*f180a3b7SHieu Nguyen 	assert(core_pos < PLATFORM_CORE_COUNT);
103*f180a3b7SHieu Nguyen 
104*f180a3b7SHieu Nguyen 	css_scp_core_pos_to_scmi_channel(core_pos, &domain_id,
105*f180a3b7SHieu Nguyen 			&channel_id);
106*f180a3b7SHieu Nguyen 	scmi_pwr_state = POWER_ON_STATE;
107*f180a3b7SHieu Nguyen 	ret = scmi_pwr_state_set(scmi_handle,
108*f180a3b7SHieu Nguyen 		domain_id, scmi_pwr_state);
109*f180a3b7SHieu Nguyen 	if (ret != SCMI_E_QUEUED && ret != SCMI_E_SUCCESS) {
110*f180a3b7SHieu Nguyen 		ERROR("SCMI set power state command return 0x%x unexpected\n",
111*f180a3b7SHieu Nguyen 		      ret);
112*f180a3b7SHieu Nguyen 		panic();
113*f180a3b7SHieu Nguyen 	}
114*f180a3b7SHieu Nguyen }
115*f180a3b7SHieu Nguyen 
116*f180a3b7SHieu Nguyen /*
117*f180a3b7SHieu Nguyen  * Helper function to turn off a CPU power domain and its parent power domains
118*f180a3b7SHieu Nguyen  * if applicable.
119*f180a3b7SHieu Nguyen  */
120*f180a3b7SHieu Nguyen void rcar_scmi_cpuoff(const struct psci_power_state *target_state)
121*f180a3b7SHieu Nguyen {
122*f180a3b7SHieu Nguyen 	unsigned int lvl = 0, channel_id, domain_id;
123*f180a3b7SHieu Nguyen 	uint32_t scmi_pwr_state = 0;
124*f180a3b7SHieu Nguyen 	int ret;
125*f180a3b7SHieu Nguyen 
126*f180a3b7SHieu Nguyen 	/* At-least the CPU level should be specified to be OFF */
127*f180a3b7SHieu Nguyen 	assert(target_state->pwr_domain_state[MPIDR_AFFLVL0] ==
128*f180a3b7SHieu Nguyen 							ARM_LOCAL_STATE_OFF);
129*f180a3b7SHieu Nguyen 
130*f180a3b7SHieu Nguyen 	/* PSCI CPU OFF cannot be used to turn OFF system power domain */
131*f180a3b7SHieu Nguyen 	assert(css_system_pwr_state(target_state) == ARM_LOCAL_STATE_RUN);
132*f180a3b7SHieu Nguyen 
133*f180a3b7SHieu Nguyen 	for (; lvl <= PLAT_MAX_PWR_LVL; lvl++) {
134*f180a3b7SHieu Nguyen 		if (target_state->pwr_domain_state[lvl] == ARM_LOCAL_STATE_RUN)
135*f180a3b7SHieu Nguyen 			break;
136*f180a3b7SHieu Nguyen 
137*f180a3b7SHieu Nguyen 		assert(target_state->pwr_domain_state[lvl] ==
138*f180a3b7SHieu Nguyen 							ARM_LOCAL_STATE_OFF);
139*f180a3b7SHieu Nguyen 	}
140*f180a3b7SHieu Nguyen 
141*f180a3b7SHieu Nguyen 
142*f180a3b7SHieu Nguyen 	scmi_pwr_state = POWER_OFF_STATE;
143*f180a3b7SHieu Nguyen 	css_scp_core_pos_to_scmi_channel(plat_my_core_pos(),
144*f180a3b7SHieu Nguyen 			&domain_id, &channel_id);
145*f180a3b7SHieu Nguyen 	ret = scmi_pwr_state_set(scmi_handle,
146*f180a3b7SHieu Nguyen 		domain_id, scmi_pwr_state);
147*f180a3b7SHieu Nguyen 	if (ret != SCMI_E_QUEUED && ret != SCMI_E_SUCCESS) {
148*f180a3b7SHieu Nguyen 		ERROR("SCMI set power state command return 0x%x unexpected\n",
149*f180a3b7SHieu Nguyen 				ret);
150*f180a3b7SHieu Nguyen 		panic();
151*f180a3b7SHieu Nguyen 	}
152*f180a3b7SHieu Nguyen }
153*f180a3b7SHieu Nguyen 
154*f180a3b7SHieu Nguyen /*
155*f180a3b7SHieu Nguyen  * Helper function to shutdown the system via SCMI.
156*f180a3b7SHieu Nguyen  */
157*f180a3b7SHieu Nguyen void rcar_scmi_sys_shutdown(void)
158*f180a3b7SHieu Nguyen {
159*f180a3b7SHieu Nguyen 	int ret;
160*f180a3b7SHieu Nguyen 
161*f180a3b7SHieu Nguyen 	ret = scmi_sys_pwr_state_set(scmi_handle,
162*f180a3b7SHieu Nguyen 			SCMI_SYS_PWR_GRACEFUL_REQ, SCMI_SYS_PWR_SHUTDOWN);
163*f180a3b7SHieu Nguyen 	if (ret != SCMI_E_SUCCESS) {
164*f180a3b7SHieu Nguyen 		ERROR("SCMI system power state set (SHUTDOWN) returns 0x%x\n",
165*f180a3b7SHieu Nguyen 				ret);
166*f180a3b7SHieu Nguyen 		panic();
167*f180a3b7SHieu Nguyen 	}
168*f180a3b7SHieu Nguyen }
169*f180a3b7SHieu Nguyen 
170*f180a3b7SHieu Nguyen /*
171*f180a3b7SHieu Nguyen  * Helper function to reset the system via SCMI.
172*f180a3b7SHieu Nguyen  */
173*f180a3b7SHieu Nguyen void rcar_scmi_sys_reboot(void)
174*f180a3b7SHieu Nguyen {
175*f180a3b7SHieu Nguyen 	int ret;
176*f180a3b7SHieu Nguyen 
177*f180a3b7SHieu Nguyen 	ret = scmi_sys_pwr_state_set(scmi_handle,
178*f180a3b7SHieu Nguyen 			SCMI_SYS_PWR_GRACEFUL_REQ, SCMI_SYS_PWR_COLD_RESET);
179*f180a3b7SHieu Nguyen 	if (ret != SCMI_E_SUCCESS) {
180*f180a3b7SHieu Nguyen 		ERROR("SCMI system power state set (COLD RESET) returns 0x%x\n",
181*f180a3b7SHieu Nguyen 				ret);
182*f180a3b7SHieu Nguyen 		panic();
183*f180a3b7SHieu Nguyen 	}
184*f180a3b7SHieu Nguyen }
185*f180a3b7SHieu Nguyen 
186*f180a3b7SHieu Nguyen /*
187*f180a3b7SHieu Nguyen  * Helper function to suspend the system via SCMI.
188*f180a3b7SHieu Nguyen  */
189*f180a3b7SHieu Nguyen void rcar_scmi_sys_suspend(void)
190*f180a3b7SHieu Nguyen {
191*f180a3b7SHieu Nguyen 	int ret;
192*f180a3b7SHieu Nguyen 
193*f180a3b7SHieu Nguyen 	ret = scmi_sys_pwr_state_set(scmi_handle,
194*f180a3b7SHieu Nguyen 			SCMI_SYS_PWR_GRACEFUL_REQ, SCMI_SYS_PWR_SUSPEND);
195*f180a3b7SHieu Nguyen 	if (ret != SCMI_E_SUCCESS) {
196*f180a3b7SHieu Nguyen 		/* Do not out logs and go panic when DRAM backup mode */
197*f180a3b7SHieu Nguyen 		while (true) {
198*f180a3b7SHieu Nguyen 		}
199*f180a3b7SHieu Nguyen 	}
200*f180a3b7SHieu Nguyen }
201*f180a3b7SHieu Nguyen 
202*f180a3b7SHieu Nguyen void __init plat_rcar_scmi_setup(void)
203*f180a3b7SHieu Nguyen {
204*f180a3b7SHieu Nguyen 	mailbox_mem_t *mbx_mem;
205*f180a3b7SHieu Nguyen 
206*f180a3b7SHieu Nguyen 	INFO("Initializing SCMI driver on Channel\n");
207*f180a3b7SHieu Nguyen 
208*f180a3b7SHieu Nguyen 	/* Preset info data to sram variable */
209*f180a3b7SHieu Nguyen 	scmi_plat_info.scmi_mbx_mem = RCAR_SCMI_CHANNEL_BASE;
210*f180a3b7SHieu Nguyen 	scmi_plat_info.db_reg_addr = RCAR_SCMI_MFIS_ADDR;
211*f180a3b7SHieu Nguyen 	scmi_plat_info.db_preserve_mask = RCAR_SCMI_MFIS_PRV_MASK;
212*f180a3b7SHieu Nguyen 	scmi_plat_info.db_modify_mask = RCAR_SCMI_MFIS_MOD_MASK;
213*f180a3b7SHieu Nguyen 	scmi_plat_info.ring_doorbell = rcar_ring_doorbell;
214*f180a3b7SHieu Nguyen 
215*f180a3b7SHieu Nguyen 	/* Clean-up and initialize channel memory */
216*f180a3b7SHieu Nguyen 	mbx_mem = (mailbox_mem_t *)(scmi_plat_info.scmi_mbx_mem);
217*f180a3b7SHieu Nguyen 	(void)memset(mbx_mem, 0x0, RCAR_SCMI_CHANNEL_SIZE - 0xF00);
218*f180a3b7SHieu Nguyen 
219*f180a3b7SHieu Nguyen 	/* Initialize channel status to free at first */
220*f180a3b7SHieu Nguyen 	mbx_mem->status |= (uint32_t)SCMI_CH_STATUS_FREE_MASK;
221*f180a3b7SHieu Nguyen 
222*f180a3b7SHieu Nguyen 	/* Setup and initialize channel */
223*f180a3b7SHieu Nguyen 	sscmi_channel.info = &scmi_plat_info;
224*f180a3b7SHieu Nguyen 	sscmi_channel.lock = RCAR_SCMI_LOCK_GET_INSTANCE;
225*f180a3b7SHieu Nguyen 
226*f180a3b7SHieu Nguyen #if (SET_SCMI_PARAM == 1)
227*f180a3b7SHieu Nguyen 	scmi_handle = scmi_init(&sscmi_channel);
228*f180a3b7SHieu Nguyen 
229*f180a3b7SHieu Nguyen 	if (scmi_handle == NULL) {
230*f180a3b7SHieu Nguyen 		ERROR("SCMI Initialization failed on channel\n");
231*f180a3b7SHieu Nguyen 		panic();
232*f180a3b7SHieu Nguyen 	}
233*f180a3b7SHieu Nguyen #else
234*f180a3b7SHieu Nguyen 	scmi_handle = &sscmi_channel;
235*f180a3b7SHieu Nguyen #endif
236*f180a3b7SHieu Nguyen }
237*f180a3b7SHieu Nguyen 
238*f180a3b7SHieu Nguyen const plat_psci_ops_t *plat_rcar_psci_override_pm_ops(plat_psci_ops_t *ops)
239*f180a3b7SHieu Nguyen {
240*f180a3b7SHieu Nguyen #if (SET_SCMI_PARAM == 1)
241*f180a3b7SHieu Nguyen 	uint32_t msg_attr;
242*f180a3b7SHieu Nguyen 	int ret;
243*f180a3b7SHieu Nguyen 
244*f180a3b7SHieu Nguyen 	/* Check that power domain POWER_STATE_SET message is supported */
245*f180a3b7SHieu Nguyen 	ret = scmi_proto_msg_attr(scmi_handle, SCMI_PWR_DMN_PROTO_ID,
246*f180a3b7SHieu Nguyen 				SCMI_PWR_STATE_SET_MSG, &msg_attr);
247*f180a3b7SHieu Nguyen 	if (ret != SCMI_E_SUCCESS) {
248*f180a3b7SHieu Nguyen 		ERROR("Set power state command is not supported by SCMI\n");
249*f180a3b7SHieu Nguyen 		panic();
250*f180a3b7SHieu Nguyen 	}
251*f180a3b7SHieu Nguyen 
252*f180a3b7SHieu Nguyen 	/* Check if the SCMI SYSTEM_POWER_STATE_SET message is supported */
253*f180a3b7SHieu Nguyen 	ret = scmi_proto_msg_attr(scmi_handle, SCMI_SYS_PWR_PROTO_ID,
254*f180a3b7SHieu Nguyen 				SCMI_SYS_PWR_STATE_SET_MSG, &msg_attr);
255*f180a3b7SHieu Nguyen 	if (ret != SCMI_E_SUCCESS) {
256*f180a3b7SHieu Nguyen 		/* System power management operations are not supported */
257*f180a3b7SHieu Nguyen 		ops->system_off = NULL;
258*f180a3b7SHieu Nguyen 		ops->system_reset = NULL;
259*f180a3b7SHieu Nguyen 		ops->get_sys_suspend_power_state = NULL;
260*f180a3b7SHieu Nguyen 	} else {
261*f180a3b7SHieu Nguyen 		if ((msg_attr & (uint32_t)SCMI_SYS_PWR_SUSPEND_SUPPORTED)
262*f180a3b7SHieu Nguyen 				!= (uint32_t)SCMI_SYS_PWR_SUSPEND_SUPPORTED) {
263*f180a3b7SHieu Nguyen 			/*
264*f180a3b7SHieu Nguyen 			 * System power management protocol is available, but
265*f180a3b7SHieu Nguyen 			 * it does not support SYSTEM SUSPEND.
266*f180a3b7SHieu Nguyen 			 */
267*f180a3b7SHieu Nguyen 			ops->get_sys_suspend_power_state = NULL;
268*f180a3b7SHieu Nguyen 		}
269*f180a3b7SHieu Nguyen 	}
270*f180a3b7SHieu Nguyen #endif
271*f180a3b7SHieu Nguyen 
272*f180a3b7SHieu Nguyen 	return ops;
273*f180a3b7SHieu Nguyen }
274