1*91f16700Schasinglulu /* 2*91f16700Schasinglulu * Copyright (c) 2014-2020, ARM Limited and Contributors. All rights reserved. 3*91f16700Schasinglulu * 4*91f16700Schasinglulu * SPDX-License-Identifier: BSD-3-Clause 5*91f16700Schasinglulu */ 6*91f16700Schasinglulu 7*91f16700Schasinglulu #include <assert.h> 8*91f16700Schasinglulu #include <string.h> 9*91f16700Schasinglulu 10*91f16700Schasinglulu #include <arch_helpers.h> 11*91f16700Schasinglulu #include <common/debug.h> 12*91f16700Schasinglulu #include <drivers/arm/css/css_mhu.h> 13*91f16700Schasinglulu #include <drivers/arm/css/css_scpi.h> 14*91f16700Schasinglulu #include <lib/utils.h> 15*91f16700Schasinglulu #include <plat/common/platform.h> 16*91f16700Schasinglulu #include <platform_def.h> 17*91f16700Schasinglulu 18*91f16700Schasinglulu #define SCPI_SHARED_MEM_SCP_TO_AP PLAT_CSS_SCP_COM_SHARED_MEM_BASE 19*91f16700Schasinglulu #define SCPI_SHARED_MEM_AP_TO_SCP (PLAT_CSS_SCP_COM_SHARED_MEM_BASE \ 20*91f16700Schasinglulu + 0x100) 21*91f16700Schasinglulu 22*91f16700Schasinglulu /* Header and payload addresses for commands from AP to SCP */ 23*91f16700Schasinglulu #define SCPI_CMD_HEADER_AP_TO_SCP \ 24*91f16700Schasinglulu ((scpi_cmd_t *) SCPI_SHARED_MEM_AP_TO_SCP) 25*91f16700Schasinglulu #define SCPI_CMD_PAYLOAD_AP_TO_SCP \ 26*91f16700Schasinglulu ((void *) (SCPI_SHARED_MEM_AP_TO_SCP + sizeof(scpi_cmd_t))) 27*91f16700Schasinglulu 28*91f16700Schasinglulu /* Header and payload addresses for responses from SCP to AP */ 29*91f16700Schasinglulu #define SCPI_RES_HEADER_SCP_TO_AP \ 30*91f16700Schasinglulu ((scpi_cmd_t *) SCPI_SHARED_MEM_SCP_TO_AP) 31*91f16700Schasinglulu #define SCPI_RES_PAYLOAD_SCP_TO_AP \ 32*91f16700Schasinglulu ((void *) (SCPI_SHARED_MEM_SCP_TO_AP + sizeof(scpi_cmd_t))) 33*91f16700Schasinglulu 34*91f16700Schasinglulu /* ID of the MHU slot used for the SCPI protocol */ 35*91f16700Schasinglulu #define SCPI_MHU_SLOT_ID 0 36*91f16700Schasinglulu 37*91f16700Schasinglulu static void scpi_secure_message_start(void) 38*91f16700Schasinglulu { 39*91f16700Schasinglulu mhu_secure_message_start(SCPI_MHU_SLOT_ID); 40*91f16700Schasinglulu } 41*91f16700Schasinglulu 42*91f16700Schasinglulu static void scpi_secure_message_send(size_t payload_size) 43*91f16700Schasinglulu { 44*91f16700Schasinglulu /* 45*91f16700Schasinglulu * Ensure that any write to the SCPI payload area is seen by SCP before 46*91f16700Schasinglulu * we write to the MHU register. If these 2 writes were reordered by 47*91f16700Schasinglulu * the CPU then SCP would read stale payload data 48*91f16700Schasinglulu */ 49*91f16700Schasinglulu dmbst(); 50*91f16700Schasinglulu 51*91f16700Schasinglulu mhu_secure_message_send(SCPI_MHU_SLOT_ID); 52*91f16700Schasinglulu } 53*91f16700Schasinglulu 54*91f16700Schasinglulu static int scpi_secure_message_receive(scpi_cmd_t *cmd) 55*91f16700Schasinglulu { 56*91f16700Schasinglulu uint32_t mhu_status; 57*91f16700Schasinglulu 58*91f16700Schasinglulu assert(cmd != NULL); 59*91f16700Schasinglulu 60*91f16700Schasinglulu mhu_status = mhu_secure_message_wait(); 61*91f16700Schasinglulu 62*91f16700Schasinglulu /* Expect an SCPI message, reject any other protocol */ 63*91f16700Schasinglulu if (mhu_status != (1 << SCPI_MHU_SLOT_ID)) { 64*91f16700Schasinglulu ERROR("MHU: Unexpected protocol (MHU status: 0x%x)\n", 65*91f16700Schasinglulu mhu_status); 66*91f16700Schasinglulu return -1; 67*91f16700Schasinglulu } 68*91f16700Schasinglulu 69*91f16700Schasinglulu /* 70*91f16700Schasinglulu * Ensure that any read to the SCPI payload area is done after reading 71*91f16700Schasinglulu * the MHU register. If these 2 reads were reordered then the CPU would 72*91f16700Schasinglulu * read invalid payload data 73*91f16700Schasinglulu */ 74*91f16700Schasinglulu dmbld(); 75*91f16700Schasinglulu 76*91f16700Schasinglulu memcpy(cmd, (void *) SCPI_SHARED_MEM_SCP_TO_AP, sizeof(*cmd)); 77*91f16700Schasinglulu 78*91f16700Schasinglulu return 0; 79*91f16700Schasinglulu } 80*91f16700Schasinglulu 81*91f16700Schasinglulu static void scpi_secure_message_end(void) 82*91f16700Schasinglulu { 83*91f16700Schasinglulu mhu_secure_message_end(SCPI_MHU_SLOT_ID); 84*91f16700Schasinglulu } 85*91f16700Schasinglulu 86*91f16700Schasinglulu int scpi_wait_ready(void) 87*91f16700Schasinglulu { 88*91f16700Schasinglulu scpi_cmd_t scpi_cmd; 89*91f16700Schasinglulu int rc; 90*91f16700Schasinglulu 91*91f16700Schasinglulu VERBOSE("Waiting for SCP_READY command...\n"); 92*91f16700Schasinglulu 93*91f16700Schasinglulu /* Get a message from the SCP */ 94*91f16700Schasinglulu scpi_secure_message_start(); 95*91f16700Schasinglulu rc = scpi_secure_message_receive(&scpi_cmd); 96*91f16700Schasinglulu scpi_secure_message_end(); 97*91f16700Schasinglulu 98*91f16700Schasinglulu /* If no message was received, don't send a response */ 99*91f16700Schasinglulu if (rc != 0) 100*91f16700Schasinglulu return rc; 101*91f16700Schasinglulu 102*91f16700Schasinglulu /* We are expecting 'SCP Ready', produce correct error if it's not */ 103*91f16700Schasinglulu scpi_status_t status = SCP_OK; 104*91f16700Schasinglulu if (scpi_cmd.id != SCPI_CMD_SCP_READY) { 105*91f16700Schasinglulu ERROR("Unexpected SCP command: expected command #%u, got command #%u\n", 106*91f16700Schasinglulu SCPI_CMD_SCP_READY, scpi_cmd.id); 107*91f16700Schasinglulu status = SCP_E_SUPPORT; 108*91f16700Schasinglulu } else if (scpi_cmd.size != 0) { 109*91f16700Schasinglulu ERROR("SCP_READY command has incorrect size: expected 0, got %u\n", 110*91f16700Schasinglulu scpi_cmd.size); 111*91f16700Schasinglulu status = SCP_E_SIZE; 112*91f16700Schasinglulu } 113*91f16700Schasinglulu 114*91f16700Schasinglulu VERBOSE("Sending response for SCP_READY command\n"); 115*91f16700Schasinglulu 116*91f16700Schasinglulu /* 117*91f16700Schasinglulu * Send our response back to SCP. 118*91f16700Schasinglulu * We are using the same SCPI header, just update the status field. 119*91f16700Schasinglulu */ 120*91f16700Schasinglulu scpi_cmd.status = status; 121*91f16700Schasinglulu scpi_secure_message_start(); 122*91f16700Schasinglulu memcpy((void *) SCPI_SHARED_MEM_AP_TO_SCP, &scpi_cmd, sizeof(scpi_cmd)); 123*91f16700Schasinglulu scpi_secure_message_send(0); 124*91f16700Schasinglulu scpi_secure_message_end(); 125*91f16700Schasinglulu 126*91f16700Schasinglulu return status == SCP_OK ? 0 : -1; 127*91f16700Schasinglulu } 128*91f16700Schasinglulu 129*91f16700Schasinglulu void scpi_set_css_power_state(unsigned int mpidr, 130*91f16700Schasinglulu scpi_power_state_t cpu_state, scpi_power_state_t cluster_state, 131*91f16700Schasinglulu scpi_power_state_t css_state) 132*91f16700Schasinglulu { 133*91f16700Schasinglulu scpi_cmd_t *cmd; 134*91f16700Schasinglulu uint32_t state = 0; 135*91f16700Schasinglulu uint32_t *payload_addr; 136*91f16700Schasinglulu 137*91f16700Schasinglulu #if ARM_PLAT_MT 138*91f16700Schasinglulu /* 139*91f16700Schasinglulu * The current SCPI driver only caters for single-threaded platforms. 140*91f16700Schasinglulu * Hence we ignore the thread ID (which is always 0) for such platforms. 141*91f16700Schasinglulu */ 142*91f16700Schasinglulu state |= (mpidr >> MPIDR_AFF1_SHIFT) & 0x0f; /* CPU ID */ 143*91f16700Schasinglulu state |= ((mpidr >> MPIDR_AFF2_SHIFT) & 0x0f) << 4; /* Cluster ID */ 144*91f16700Schasinglulu #else 145*91f16700Schasinglulu state |= mpidr & 0x0f; /* CPU ID */ 146*91f16700Schasinglulu state |= (mpidr & 0xf00) >> 4; /* Cluster ID */ 147*91f16700Schasinglulu #endif /* ARM_PLAT_MT */ 148*91f16700Schasinglulu 149*91f16700Schasinglulu state |= cpu_state << 8; 150*91f16700Schasinglulu state |= cluster_state << 12; 151*91f16700Schasinglulu state |= css_state << 16; 152*91f16700Schasinglulu 153*91f16700Schasinglulu scpi_secure_message_start(); 154*91f16700Schasinglulu 155*91f16700Schasinglulu /* Populate the command header */ 156*91f16700Schasinglulu cmd = SCPI_CMD_HEADER_AP_TO_SCP; 157*91f16700Schasinglulu cmd->id = SCPI_CMD_SET_CSS_POWER_STATE; 158*91f16700Schasinglulu cmd->set = SCPI_SET_NORMAL; 159*91f16700Schasinglulu cmd->sender = 0; 160*91f16700Schasinglulu cmd->size = sizeof(state); 161*91f16700Schasinglulu /* Populate the command payload */ 162*91f16700Schasinglulu payload_addr = SCPI_CMD_PAYLOAD_AP_TO_SCP; 163*91f16700Schasinglulu *payload_addr = state; 164*91f16700Schasinglulu scpi_secure_message_send(sizeof(state)); 165*91f16700Schasinglulu /* 166*91f16700Schasinglulu * SCP does not reply to this command in order to avoid MHU interrupts 167*91f16700Schasinglulu * from the sender, which could interfere with its power state request. 168*91f16700Schasinglulu */ 169*91f16700Schasinglulu 170*91f16700Schasinglulu scpi_secure_message_end(); 171*91f16700Schasinglulu } 172*91f16700Schasinglulu 173*91f16700Schasinglulu /* 174*91f16700Schasinglulu * Query and obtain CSS power state from SCP. 175*91f16700Schasinglulu * 176*91f16700Schasinglulu * In response to the query, SCP returns power states of all CPUs in all 177*91f16700Schasinglulu * clusters of the system. The returned response is then filtered based on the 178*91f16700Schasinglulu * supplied MPIDR. Power states of requested cluster and CPUs within are updated 179*91f16700Schasinglulu * via supplied non-NULL pointer arguments. 180*91f16700Schasinglulu * 181*91f16700Schasinglulu * Returns 0 on success, or -1 on errors. 182*91f16700Schasinglulu */ 183*91f16700Schasinglulu int scpi_get_css_power_state(unsigned int mpidr, unsigned int *cpu_state_p, 184*91f16700Schasinglulu unsigned int *cluster_state_p) 185*91f16700Schasinglulu { 186*91f16700Schasinglulu scpi_cmd_t *cmd; 187*91f16700Schasinglulu scpi_cmd_t response; 188*91f16700Schasinglulu int power_state, cpu, cluster, rc = -1; 189*91f16700Schasinglulu 190*91f16700Schasinglulu /* 191*91f16700Schasinglulu * Extract CPU and cluster membership of the given MPIDR. SCPI caters 192*91f16700Schasinglulu * for only up to 0xf clusters, and 8 CPUs per cluster 193*91f16700Schasinglulu */ 194*91f16700Schasinglulu #if ARM_PLAT_MT 195*91f16700Schasinglulu /* 196*91f16700Schasinglulu * The current SCPI driver only caters for single-threaded platforms. 197*91f16700Schasinglulu * Hence we ignore the thread ID (which is always 0) for such platforms. 198*91f16700Schasinglulu */ 199*91f16700Schasinglulu cpu = (mpidr >> MPIDR_AFF1_SHIFT) & MPIDR_AFFLVL_MASK; 200*91f16700Schasinglulu cluster = (mpidr >> MPIDR_AFF2_SHIFT) & MPIDR_AFFLVL_MASK; 201*91f16700Schasinglulu #else 202*91f16700Schasinglulu cpu = mpidr & MPIDR_AFFLVL_MASK; 203*91f16700Schasinglulu cluster = (mpidr >> MPIDR_AFF1_SHIFT) & MPIDR_AFFLVL_MASK; 204*91f16700Schasinglulu #endif /* ARM_PLAT_MT */ 205*91f16700Schasinglulu if (cpu >= 8 || cluster >= 0xf) 206*91f16700Schasinglulu return -1; 207*91f16700Schasinglulu 208*91f16700Schasinglulu scpi_secure_message_start(); 209*91f16700Schasinglulu 210*91f16700Schasinglulu /* Populate request headers */ 211*91f16700Schasinglulu zeromem(SCPI_CMD_HEADER_AP_TO_SCP, sizeof(*cmd)); 212*91f16700Schasinglulu cmd = SCPI_CMD_HEADER_AP_TO_SCP; 213*91f16700Schasinglulu cmd->id = SCPI_CMD_GET_CSS_POWER_STATE; 214*91f16700Schasinglulu 215*91f16700Schasinglulu /* 216*91f16700Schasinglulu * Send message and wait for SCP's response 217*91f16700Schasinglulu */ 218*91f16700Schasinglulu scpi_secure_message_send(0); 219*91f16700Schasinglulu if (scpi_secure_message_receive(&response) != 0) 220*91f16700Schasinglulu goto exit; 221*91f16700Schasinglulu 222*91f16700Schasinglulu if (response.status != SCP_OK) 223*91f16700Schasinglulu goto exit; 224*91f16700Schasinglulu 225*91f16700Schasinglulu /* Validate SCP response */ 226*91f16700Schasinglulu if (!CHECK_RESPONSE(response, cluster)) 227*91f16700Schasinglulu goto exit; 228*91f16700Schasinglulu 229*91f16700Schasinglulu /* Extract power states for required cluster */ 230*91f16700Schasinglulu power_state = *(((uint16_t *) SCPI_RES_PAYLOAD_SCP_TO_AP) + cluster); 231*91f16700Schasinglulu if (CLUSTER_ID(power_state) != cluster) 232*91f16700Schasinglulu goto exit; 233*91f16700Schasinglulu 234*91f16700Schasinglulu /* Update power state via pointers */ 235*91f16700Schasinglulu if (cluster_state_p) 236*91f16700Schasinglulu *cluster_state_p = CLUSTER_POWER_STATE(power_state); 237*91f16700Schasinglulu if (cpu_state_p) 238*91f16700Schasinglulu *cpu_state_p = CPU_POWER_STATE(power_state); 239*91f16700Schasinglulu rc = 0; 240*91f16700Schasinglulu 241*91f16700Schasinglulu exit: 242*91f16700Schasinglulu scpi_secure_message_end(); 243*91f16700Schasinglulu return rc; 244*91f16700Schasinglulu } 245*91f16700Schasinglulu 246*91f16700Schasinglulu uint32_t scpi_sys_power_state(scpi_system_state_t system_state) 247*91f16700Schasinglulu { 248*91f16700Schasinglulu scpi_cmd_t *cmd; 249*91f16700Schasinglulu uint8_t *payload_addr; 250*91f16700Schasinglulu scpi_cmd_t response; 251*91f16700Schasinglulu 252*91f16700Schasinglulu scpi_secure_message_start(); 253*91f16700Schasinglulu 254*91f16700Schasinglulu /* Populate the command header */ 255*91f16700Schasinglulu cmd = SCPI_CMD_HEADER_AP_TO_SCP; 256*91f16700Schasinglulu cmd->id = SCPI_CMD_SYS_POWER_STATE; 257*91f16700Schasinglulu cmd->set = 0; 258*91f16700Schasinglulu cmd->sender = 0; 259*91f16700Schasinglulu cmd->size = sizeof(*payload_addr); 260*91f16700Schasinglulu /* Populate the command payload */ 261*91f16700Schasinglulu payload_addr = SCPI_CMD_PAYLOAD_AP_TO_SCP; 262*91f16700Schasinglulu *payload_addr = system_state & 0xff; 263*91f16700Schasinglulu scpi_secure_message_send(sizeof(*payload_addr)); 264*91f16700Schasinglulu 265*91f16700Schasinglulu /* If no response is received, fill in an error status */ 266*91f16700Schasinglulu if (scpi_secure_message_receive(&response) != 0) 267*91f16700Schasinglulu response.status = SCP_E_TIMEOUT; 268*91f16700Schasinglulu 269*91f16700Schasinglulu scpi_secure_message_end(); 270*91f16700Schasinglulu 271*91f16700Schasinglulu return response.status; 272*91f16700Schasinglulu } 273