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