10387aa42SAntonio Nino Diaz /* 2*98367c80SSamuel Holland * Copyright (c) 2014-2020, ARM Limited and Contributors. All rights reserved. 30387aa42SAntonio Nino Diaz * 40387aa42SAntonio Nino Diaz * SPDX-License-Identifier: BSD-3-Clause 50387aa42SAntonio Nino Diaz */ 60387aa42SAntonio Nino Diaz 70387aa42SAntonio Nino Diaz #include <assert.h> 80387aa42SAntonio Nino Diaz #include <string.h> 90387aa42SAntonio Nino Diaz 100387aa42SAntonio Nino Diaz #include <arch_helpers.h> 110387aa42SAntonio Nino Diaz #include <common/debug.h> 120387aa42SAntonio Nino Diaz #include <drivers/arm/css/css_mhu.h> 130387aa42SAntonio Nino Diaz #include <drivers/arm/css/css_scpi.h> 140387aa42SAntonio Nino Diaz #include <lib/utils.h> 150387aa42SAntonio Nino Diaz #include <plat/common/platform.h> 160387aa42SAntonio Nino Diaz #include <platform_def.h> 170387aa42SAntonio Nino Diaz 180387aa42SAntonio Nino Diaz #define SCPI_SHARED_MEM_SCP_TO_AP PLAT_CSS_SCP_COM_SHARED_MEM_BASE 190387aa42SAntonio Nino Diaz #define SCPI_SHARED_MEM_AP_TO_SCP (PLAT_CSS_SCP_COM_SHARED_MEM_BASE \ 200387aa42SAntonio Nino Diaz + 0x100) 210387aa42SAntonio Nino Diaz 220387aa42SAntonio Nino Diaz /* Header and payload addresses for commands from AP to SCP */ 230387aa42SAntonio Nino Diaz #define SCPI_CMD_HEADER_AP_TO_SCP \ 240387aa42SAntonio Nino Diaz ((scpi_cmd_t *) SCPI_SHARED_MEM_AP_TO_SCP) 250387aa42SAntonio Nino Diaz #define SCPI_CMD_PAYLOAD_AP_TO_SCP \ 260387aa42SAntonio Nino Diaz ((void *) (SCPI_SHARED_MEM_AP_TO_SCP + sizeof(scpi_cmd_t))) 270387aa42SAntonio Nino Diaz 280387aa42SAntonio Nino Diaz /* Header and payload addresses for responses from SCP to AP */ 290387aa42SAntonio Nino Diaz #define SCPI_RES_HEADER_SCP_TO_AP \ 300387aa42SAntonio Nino Diaz ((scpi_cmd_t *) SCPI_SHARED_MEM_SCP_TO_AP) 310387aa42SAntonio Nino Diaz #define SCPI_RES_PAYLOAD_SCP_TO_AP \ 320387aa42SAntonio Nino Diaz ((void *) (SCPI_SHARED_MEM_SCP_TO_AP + sizeof(scpi_cmd_t))) 330387aa42SAntonio Nino Diaz 340387aa42SAntonio Nino Diaz /* ID of the MHU slot used for the SCPI protocol */ 350387aa42SAntonio Nino Diaz #define SCPI_MHU_SLOT_ID 0 360387aa42SAntonio Nino Diaz 370387aa42SAntonio Nino Diaz static void scpi_secure_message_start(void) 380387aa42SAntonio Nino Diaz { 390387aa42SAntonio Nino Diaz mhu_secure_message_start(SCPI_MHU_SLOT_ID); 400387aa42SAntonio Nino Diaz } 410387aa42SAntonio Nino Diaz 420387aa42SAntonio Nino Diaz static void scpi_secure_message_send(size_t payload_size) 430387aa42SAntonio Nino Diaz { 440387aa42SAntonio Nino Diaz /* 450387aa42SAntonio Nino Diaz * Ensure that any write to the SCPI payload area is seen by SCP before 460387aa42SAntonio Nino Diaz * we write to the MHU register. If these 2 writes were reordered by 470387aa42SAntonio Nino Diaz * the CPU then SCP would read stale payload data 480387aa42SAntonio Nino Diaz */ 490387aa42SAntonio Nino Diaz dmbst(); 500387aa42SAntonio Nino Diaz 510387aa42SAntonio Nino Diaz mhu_secure_message_send(SCPI_MHU_SLOT_ID); 520387aa42SAntonio Nino Diaz } 530387aa42SAntonio Nino Diaz 54*98367c80SSamuel Holland static int scpi_secure_message_receive(scpi_cmd_t *cmd) 550387aa42SAntonio Nino Diaz { 560387aa42SAntonio Nino Diaz uint32_t mhu_status; 570387aa42SAntonio Nino Diaz 580387aa42SAntonio Nino Diaz assert(cmd != NULL); 590387aa42SAntonio Nino Diaz 600387aa42SAntonio Nino Diaz mhu_status = mhu_secure_message_wait(); 610387aa42SAntonio Nino Diaz 620387aa42SAntonio Nino Diaz /* Expect an SCPI message, reject any other protocol */ 630387aa42SAntonio Nino Diaz if (mhu_status != (1 << SCPI_MHU_SLOT_ID)) { 640387aa42SAntonio Nino Diaz ERROR("MHU: Unexpected protocol (MHU status: 0x%x)\n", 650387aa42SAntonio Nino Diaz mhu_status); 66*98367c80SSamuel Holland return -1; 670387aa42SAntonio Nino Diaz } 680387aa42SAntonio Nino Diaz 690387aa42SAntonio Nino Diaz /* 700387aa42SAntonio Nino Diaz * Ensure that any read to the SCPI payload area is done after reading 710387aa42SAntonio Nino Diaz * the MHU register. If these 2 reads were reordered then the CPU would 720387aa42SAntonio Nino Diaz * read invalid payload data 730387aa42SAntonio Nino Diaz */ 740387aa42SAntonio Nino Diaz dmbld(); 750387aa42SAntonio Nino Diaz 760387aa42SAntonio Nino Diaz memcpy(cmd, (void *) SCPI_SHARED_MEM_SCP_TO_AP, sizeof(*cmd)); 77*98367c80SSamuel Holland 78*98367c80SSamuel Holland return 0; 790387aa42SAntonio Nino Diaz } 800387aa42SAntonio Nino Diaz 810387aa42SAntonio Nino Diaz static void scpi_secure_message_end(void) 820387aa42SAntonio Nino Diaz { 830387aa42SAntonio Nino Diaz mhu_secure_message_end(SCPI_MHU_SLOT_ID); 840387aa42SAntonio Nino Diaz } 850387aa42SAntonio Nino Diaz 860387aa42SAntonio Nino Diaz int scpi_wait_ready(void) 870387aa42SAntonio Nino Diaz { 880387aa42SAntonio Nino Diaz scpi_cmd_t scpi_cmd; 89*98367c80SSamuel Holland int rc; 900387aa42SAntonio Nino Diaz 910387aa42SAntonio Nino Diaz VERBOSE("Waiting for SCP_READY command...\n"); 920387aa42SAntonio Nino Diaz 930387aa42SAntonio Nino Diaz /* Get a message from the SCP */ 940387aa42SAntonio Nino Diaz scpi_secure_message_start(); 95*98367c80SSamuel Holland rc = scpi_secure_message_receive(&scpi_cmd); 960387aa42SAntonio Nino Diaz scpi_secure_message_end(); 970387aa42SAntonio Nino Diaz 98*98367c80SSamuel Holland /* If no message was received, don't send a response */ 99*98367c80SSamuel Holland if (rc != 0) 100*98367c80SSamuel Holland return rc; 101*98367c80SSamuel Holland 1020387aa42SAntonio Nino Diaz /* We are expecting 'SCP Ready', produce correct error if it's not */ 1030387aa42SAntonio Nino Diaz scpi_status_t status = SCP_OK; 1040387aa42SAntonio Nino Diaz if (scpi_cmd.id != SCPI_CMD_SCP_READY) { 1050387aa42SAntonio Nino Diaz ERROR("Unexpected SCP command: expected command #%u, got command #%u\n", 1060387aa42SAntonio Nino Diaz SCPI_CMD_SCP_READY, scpi_cmd.id); 1070387aa42SAntonio Nino Diaz status = SCP_E_SUPPORT; 1080387aa42SAntonio Nino Diaz } else if (scpi_cmd.size != 0) { 1090387aa42SAntonio Nino Diaz ERROR("SCP_READY command has incorrect size: expected 0, got %u\n", 1100387aa42SAntonio Nino Diaz scpi_cmd.size); 1110387aa42SAntonio Nino Diaz status = SCP_E_SIZE; 1120387aa42SAntonio Nino Diaz } 1130387aa42SAntonio Nino Diaz 1140387aa42SAntonio Nino Diaz VERBOSE("Sending response for SCP_READY command\n"); 1150387aa42SAntonio Nino Diaz 1160387aa42SAntonio Nino Diaz /* 1170387aa42SAntonio Nino Diaz * Send our response back to SCP. 1180387aa42SAntonio Nino Diaz * We are using the same SCPI header, just update the status field. 1190387aa42SAntonio Nino Diaz */ 1200387aa42SAntonio Nino Diaz scpi_cmd.status = status; 1210387aa42SAntonio Nino Diaz scpi_secure_message_start(); 1220387aa42SAntonio Nino Diaz memcpy((void *) SCPI_SHARED_MEM_AP_TO_SCP, &scpi_cmd, sizeof(scpi_cmd)); 1230387aa42SAntonio Nino Diaz scpi_secure_message_send(0); 1240387aa42SAntonio Nino Diaz scpi_secure_message_end(); 1250387aa42SAntonio Nino Diaz 1260387aa42SAntonio Nino Diaz return status == SCP_OK ? 0 : -1; 1270387aa42SAntonio Nino Diaz } 1280387aa42SAntonio Nino Diaz 1290387aa42SAntonio Nino Diaz void scpi_set_css_power_state(unsigned int mpidr, 1300387aa42SAntonio Nino Diaz scpi_power_state_t cpu_state, scpi_power_state_t cluster_state, 1310387aa42SAntonio Nino Diaz scpi_power_state_t css_state) 1320387aa42SAntonio Nino Diaz { 1330387aa42SAntonio Nino Diaz scpi_cmd_t *cmd; 1340387aa42SAntonio Nino Diaz uint32_t state = 0; 1350387aa42SAntonio Nino Diaz uint32_t *payload_addr; 1360387aa42SAntonio Nino Diaz 1370387aa42SAntonio Nino Diaz #if ARM_PLAT_MT 1380387aa42SAntonio Nino Diaz /* 1390387aa42SAntonio Nino Diaz * The current SCPI driver only caters for single-threaded platforms. 1400387aa42SAntonio Nino Diaz * Hence we ignore the thread ID (which is always 0) for such platforms. 1410387aa42SAntonio Nino Diaz */ 1420387aa42SAntonio Nino Diaz state |= (mpidr >> MPIDR_AFF1_SHIFT) & 0x0f; /* CPU ID */ 1430387aa42SAntonio Nino Diaz state |= ((mpidr >> MPIDR_AFF2_SHIFT) & 0x0f) << 4; /* Cluster ID */ 1440387aa42SAntonio Nino Diaz #else 1450387aa42SAntonio Nino Diaz state |= mpidr & 0x0f; /* CPU ID */ 1460387aa42SAntonio Nino Diaz state |= (mpidr & 0xf00) >> 4; /* Cluster ID */ 1470387aa42SAntonio Nino Diaz #endif /* ARM_PLAT_MT */ 1480387aa42SAntonio Nino Diaz 1490387aa42SAntonio Nino Diaz state |= cpu_state << 8; 1500387aa42SAntonio Nino Diaz state |= cluster_state << 12; 1510387aa42SAntonio Nino Diaz state |= css_state << 16; 1520387aa42SAntonio Nino Diaz 1530387aa42SAntonio Nino Diaz scpi_secure_message_start(); 1540387aa42SAntonio Nino Diaz 1550387aa42SAntonio Nino Diaz /* Populate the command header */ 1560387aa42SAntonio Nino Diaz cmd = SCPI_CMD_HEADER_AP_TO_SCP; 1570387aa42SAntonio Nino Diaz cmd->id = SCPI_CMD_SET_CSS_POWER_STATE; 1580387aa42SAntonio Nino Diaz cmd->set = SCPI_SET_NORMAL; 1590387aa42SAntonio Nino Diaz cmd->sender = 0; 1600387aa42SAntonio Nino Diaz cmd->size = sizeof(state); 1610387aa42SAntonio Nino Diaz /* Populate the command payload */ 1620387aa42SAntonio Nino Diaz payload_addr = SCPI_CMD_PAYLOAD_AP_TO_SCP; 1630387aa42SAntonio Nino Diaz *payload_addr = state; 1640387aa42SAntonio Nino Diaz scpi_secure_message_send(sizeof(state)); 1650387aa42SAntonio Nino Diaz /* 1660387aa42SAntonio Nino Diaz * SCP does not reply to this command in order to avoid MHU interrupts 1670387aa42SAntonio Nino Diaz * from the sender, which could interfere with its power state request. 1680387aa42SAntonio Nino Diaz */ 1690387aa42SAntonio Nino Diaz 1700387aa42SAntonio Nino Diaz scpi_secure_message_end(); 1710387aa42SAntonio Nino Diaz } 1720387aa42SAntonio Nino Diaz 1730387aa42SAntonio Nino Diaz /* 1740387aa42SAntonio Nino Diaz * Query and obtain CSS power state from SCP. 1750387aa42SAntonio Nino Diaz * 1760387aa42SAntonio Nino Diaz * In response to the query, SCP returns power states of all CPUs in all 1770387aa42SAntonio Nino Diaz * clusters of the system. The returned response is then filtered based on the 1780387aa42SAntonio Nino Diaz * supplied MPIDR. Power states of requested cluster and CPUs within are updated 17973308618SAntonio Nino Diaz * via supplied non-NULL pointer arguments. 1800387aa42SAntonio Nino Diaz * 1810387aa42SAntonio Nino Diaz * Returns 0 on success, or -1 on errors. 1820387aa42SAntonio Nino Diaz */ 1830387aa42SAntonio Nino Diaz int scpi_get_css_power_state(unsigned int mpidr, unsigned int *cpu_state_p, 1840387aa42SAntonio Nino Diaz unsigned int *cluster_state_p) 1850387aa42SAntonio Nino Diaz { 1860387aa42SAntonio Nino Diaz scpi_cmd_t *cmd; 1870387aa42SAntonio Nino Diaz scpi_cmd_t response; 1880387aa42SAntonio Nino Diaz int power_state, cpu, cluster, rc = -1; 1890387aa42SAntonio Nino Diaz 1900387aa42SAntonio Nino Diaz /* 1910387aa42SAntonio Nino Diaz * Extract CPU and cluster membership of the given MPIDR. SCPI caters 1920387aa42SAntonio Nino Diaz * for only up to 0xf clusters, and 8 CPUs per cluster 1930387aa42SAntonio Nino Diaz */ 1940387aa42SAntonio Nino Diaz #if ARM_PLAT_MT 1950387aa42SAntonio Nino Diaz /* 1960387aa42SAntonio Nino Diaz * The current SCPI driver only caters for single-threaded platforms. 1970387aa42SAntonio Nino Diaz * Hence we ignore the thread ID (which is always 0) for such platforms. 1980387aa42SAntonio Nino Diaz */ 1990387aa42SAntonio Nino Diaz cpu = (mpidr >> MPIDR_AFF1_SHIFT) & MPIDR_AFFLVL_MASK; 2000387aa42SAntonio Nino Diaz cluster = (mpidr >> MPIDR_AFF2_SHIFT) & MPIDR_AFFLVL_MASK; 2010387aa42SAntonio Nino Diaz #else 2020387aa42SAntonio Nino Diaz cpu = mpidr & MPIDR_AFFLVL_MASK; 2030387aa42SAntonio Nino Diaz cluster = (mpidr >> MPIDR_AFF1_SHIFT) & MPIDR_AFFLVL_MASK; 2040387aa42SAntonio Nino Diaz #endif /* ARM_PLAT_MT */ 2050387aa42SAntonio Nino Diaz if (cpu >= 8 || cluster >= 0xf) 2060387aa42SAntonio Nino Diaz return -1; 2070387aa42SAntonio Nino Diaz 2080387aa42SAntonio Nino Diaz scpi_secure_message_start(); 2090387aa42SAntonio Nino Diaz 2100387aa42SAntonio Nino Diaz /* Populate request headers */ 2110387aa42SAntonio Nino Diaz zeromem(SCPI_CMD_HEADER_AP_TO_SCP, sizeof(*cmd)); 2120387aa42SAntonio Nino Diaz cmd = SCPI_CMD_HEADER_AP_TO_SCP; 2130387aa42SAntonio Nino Diaz cmd->id = SCPI_CMD_GET_CSS_POWER_STATE; 2140387aa42SAntonio Nino Diaz 2150387aa42SAntonio Nino Diaz /* 2160387aa42SAntonio Nino Diaz * Send message and wait for SCP's response 2170387aa42SAntonio Nino Diaz */ 2180387aa42SAntonio Nino Diaz scpi_secure_message_send(0); 219*98367c80SSamuel Holland if (scpi_secure_message_receive(&response) != 0) 220*98367c80SSamuel Holland goto exit; 2210387aa42SAntonio Nino Diaz 2220387aa42SAntonio Nino Diaz if (response.status != SCP_OK) 2230387aa42SAntonio Nino Diaz goto exit; 2240387aa42SAntonio Nino Diaz 2250387aa42SAntonio Nino Diaz /* Validate SCP response */ 2260387aa42SAntonio Nino Diaz if (!CHECK_RESPONSE(response, cluster)) 2270387aa42SAntonio Nino Diaz goto exit; 2280387aa42SAntonio Nino Diaz 2290387aa42SAntonio Nino Diaz /* Extract power states for required cluster */ 2300387aa42SAntonio Nino Diaz power_state = *(((uint16_t *) SCPI_RES_PAYLOAD_SCP_TO_AP) + cluster); 2310387aa42SAntonio Nino Diaz if (CLUSTER_ID(power_state) != cluster) 2320387aa42SAntonio Nino Diaz goto exit; 2330387aa42SAntonio Nino Diaz 23473308618SAntonio Nino Diaz /* Update power state via pointers */ 2350387aa42SAntonio Nino Diaz if (cluster_state_p) 2360387aa42SAntonio Nino Diaz *cluster_state_p = CLUSTER_POWER_STATE(power_state); 2370387aa42SAntonio Nino Diaz if (cpu_state_p) 2380387aa42SAntonio Nino Diaz *cpu_state_p = CPU_POWER_STATE(power_state); 2390387aa42SAntonio Nino Diaz rc = 0; 2400387aa42SAntonio Nino Diaz 2410387aa42SAntonio Nino Diaz exit: 2420387aa42SAntonio Nino Diaz scpi_secure_message_end(); 2430387aa42SAntonio Nino Diaz return rc; 2440387aa42SAntonio Nino Diaz } 2450387aa42SAntonio Nino Diaz 2460387aa42SAntonio Nino Diaz uint32_t scpi_sys_power_state(scpi_system_state_t system_state) 2470387aa42SAntonio Nino Diaz { 2480387aa42SAntonio Nino Diaz scpi_cmd_t *cmd; 2490387aa42SAntonio Nino Diaz uint8_t *payload_addr; 2500387aa42SAntonio Nino Diaz scpi_cmd_t response; 2510387aa42SAntonio Nino Diaz 2520387aa42SAntonio Nino Diaz scpi_secure_message_start(); 2530387aa42SAntonio Nino Diaz 2540387aa42SAntonio Nino Diaz /* Populate the command header */ 2550387aa42SAntonio Nino Diaz cmd = SCPI_CMD_HEADER_AP_TO_SCP; 2560387aa42SAntonio Nino Diaz cmd->id = SCPI_CMD_SYS_POWER_STATE; 2570387aa42SAntonio Nino Diaz cmd->set = 0; 2580387aa42SAntonio Nino Diaz cmd->sender = 0; 2590387aa42SAntonio Nino Diaz cmd->size = sizeof(*payload_addr); 2600387aa42SAntonio Nino Diaz /* Populate the command payload */ 2610387aa42SAntonio Nino Diaz payload_addr = SCPI_CMD_PAYLOAD_AP_TO_SCP; 2620387aa42SAntonio Nino Diaz *payload_addr = system_state & 0xff; 2630387aa42SAntonio Nino Diaz scpi_secure_message_send(sizeof(*payload_addr)); 2640387aa42SAntonio Nino Diaz 265*98367c80SSamuel Holland /* If no response is received, fill in an error status */ 266*98367c80SSamuel Holland if (scpi_secure_message_receive(&response) != 0) 267*98367c80SSamuel Holland response.status = SCP_E_TIMEOUT; 2680387aa42SAntonio Nino Diaz 2690387aa42SAntonio Nino Diaz scpi_secure_message_end(); 2700387aa42SAntonio Nino Diaz 2710387aa42SAntonio Nino Diaz return response.status; 2720387aa42SAntonio Nino Diaz } 273