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