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