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