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 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 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 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 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