plat/synquacer: enable SCMI support

Enable the SCMI protocol support in SynQuacer platform.
Aside from power domain, system power and apcore management protocol,
this commit adds the vendor specific protocol(0x80).
This vendor specific protocol is used to get the dram mapping information
from SCP.

Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
This commit is contained in:
Masahisa Kojima 2019-03-07 10:41:54 +09:00
parent c48d02bade
commit b67d20297f
11 changed files with 410 additions and 18 deletions

View File

@ -152,4 +152,9 @@ static inline void validate_scmi_channel(scmi_channel_t *ch)
assert(ch->info && ch->info->scmi_mbx_mem);
}
/*
* SCMI vendor specific protocol
*/
#define SCMI_SYS_VENDOR_EXT_PROTO_ID 0x80
#endif /* SCMI_PRIVATE_H */

60
drivers/arm/css/scmi/vendor/scmi_sq.c vendored Normal file
View File

@ -0,0 +1,60 @@
/*
* Copyright (c) 2019, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <assert.h>
#include <arch_helpers.h>
#include <common/debug.h>
#include <drivers/arm/css/scmi.h>
#include "scmi_private.h"
#include "scmi_sq.h"
#include <sq_common.h>
/* SCMI messge ID to get the available DRAM region */
#define SCMI_VENDOR_EXT_MEMINFO_GET_MSG 0x3
/*
* API to get the available DRAM region
*/
int scmi_get_draminfo(void *p, struct draminfo *info)
{
mailbox_mem_t *mbx_mem;
int token = 0, ret;
scmi_channel_t *ch = (scmi_channel_t *)p;
struct dram_info_resp response;
validate_scmi_channel(ch);
scmi_get_channel(ch);
mbx_mem = (mailbox_mem_t *)(ch->info->scmi_mbx_mem);
mbx_mem->msg_header = SCMI_MSG_CREATE(SCMI_SYS_VENDOR_EXT_PROTO_ID,
SCMI_VENDOR_EXT_MEMINFO_GET_MSG, token);
mbx_mem->len = 8;
mbx_mem->flags = SCMI_FLAG_RESP_POLL;
scmi_send_sync_command(ch);
/*
* Ensure that any read to the SCPI payload area is done after reading
* the MHU register. If these 2 reads were reordered then the CPU would
* read invalid payload data
*/
dmbld();
/* Get the return values */
SCMI_PAYLOAD_RET_VAL1(mbx_mem->payload, ret);
memcpy(&response, (void *)mbx_mem->payload, sizeof(response));
scmi_put_channel(ch);
*info = response.info;
return ret;
}

25
drivers/arm/css/scmi/vendor/scmi_sq.h vendored Normal file
View File

@ -0,0 +1,25 @@
/*
* Copyright (c) 2019, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef SCMI_SQ_H
#define SCMI_SQ_H
#include <stddef.h>
#include <stdint.h>
#include <sq_common.h>
/* Structure to represent available DRAM region */
struct dram_info_resp {
int status;
int reserved;
struct draminfo info;
};
/* API to get the available DRAM region */
int scmi_get_draminfo(void *p, struct draminfo *info);
#endif /* SCMI_SQ_H */

View File

@ -0,0 +1,239 @@
/*
* Copyright (c) 2019, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <assert.h>
#include <string.h>
#include <arch_helpers.h>
#include <common/debug.h>
#include <drivers/arm/css/css_mhu_doorbell.h>
#include <drivers/arm/css/css_scp.h>
#include <drivers/arm/css/scmi.h>
#include <plat/arm/css/common/css_pm.h>
#include <plat/common/platform.h>
#include <platform_def.h>
#include <scmi_sq.h>
#include <sq_common.h>
/*
* This file implements the SCP helper functions using SCMI protocol.
*/
DEFINE_BAKERY_LOCK(sq_scmi_lock);
#define SQ_SCMI_LOCK_GET_INSTANCE (&sq_scmi_lock)
#define SQ_SCMI_PAYLOAD_BASE PLAT_SQ_SCP_COM_SHARED_MEM_BASE
#define MHU_CPU_INTR_S_SET_OFFSET 0x308
const uint32_t sq_core_pos_to_scmi_dmn_id_map[PLATFORM_CORE_COUNT] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23
};
static scmi_channel_plat_info_t sq_scmi_plat_info = {
.scmi_mbx_mem = SQ_SCMI_PAYLOAD_BASE,
.db_reg_addr = PLAT_SQ_MHU_BASE + MHU_CPU_INTR_S_SET_OFFSET,
.db_preserve_mask = 0xfffffffe,
.db_modify_mask = 0x1,
.ring_doorbell = &mhu_ring_doorbell,
};
/*
* SCMI power state parameter bit field encoding for SynQuacer platform.
*
* 31 20 19 16 15 12 11 8 7 4 3 0
* +-------------------------------------------------------------+
* | SBZ | Max level | Level 3 | Level 2 | Level 1 | Level 0 |
* | | | state | state | state | state |
* +-------------------------------------------------------------+
*
* `Max level` encodes the highest level that has a valid power state
* encoded in the power state.
*/
#define SCMI_PWR_STATE_MAX_PWR_LVL_SHIFT 16
#define SCMI_PWR_STATE_MAX_PWR_LVL_WIDTH 4
#define SCMI_PWR_STATE_MAX_PWR_LVL_MASK \
((1 << SCMI_PWR_STATE_MAX_PWR_LVL_WIDTH) - 1)
#define SCMI_SET_PWR_STATE_MAX_PWR_LVL(_power_state, _max_level) \
(_power_state) |= ((_max_level) & SCMI_PWR_STATE_MAX_PWR_LVL_MASK)\
<< SCMI_PWR_STATE_MAX_PWR_LVL_SHIFT
#define SCMI_GET_PWR_STATE_MAX_PWR_LVL(_power_state) \
(((_power_state) >> SCMI_PWR_STATE_MAX_PWR_LVL_SHIFT) \
& SCMI_PWR_STATE_MAX_PWR_LVL_MASK)
#define SCMI_PWR_STATE_LVL_WIDTH 4
#define SCMI_PWR_STATE_LVL_MASK \
((1 << SCMI_PWR_STATE_LVL_WIDTH) - 1)
#define SCMI_SET_PWR_STATE_LVL(_power_state, _level, _level_state) \
(_power_state) |= ((_level_state) & SCMI_PWR_STATE_LVL_MASK) \
<< (SCMI_PWR_STATE_LVL_WIDTH * (_level))
#define SCMI_GET_PWR_STATE_LVL(_power_state, _level) \
(((_power_state) >> (SCMI_PWR_STATE_LVL_WIDTH * (_level))) & \
SCMI_PWR_STATE_LVL_MASK)
/*
* The SCMI power state enumeration for a power domain level
*/
typedef enum {
scmi_power_state_off = 0,
scmi_power_state_on = 1,
scmi_power_state_sleep = 2,
} scmi_power_state_t;
/*
* The global handle for invoking the SCMI driver APIs after the driver
* has been initialized.
*/
static void *sq_scmi_handle;
/* The SCMI channel global object */
static scmi_channel_t channel;
/*
* Helper function to turn off a CPU power domain and
* its parent power domains if applicable.
*/
void sq_scmi_off(const struct psci_power_state *target_state)
{
int lvl = 0, ret;
uint32_t scmi_pwr_state = 0;
/* At-least the CPU level should be specified to be OFF */
assert(target_state->pwr_domain_state[SQ_PWR_LVL0] ==
SQ_LOCAL_STATE_OFF);
for (; lvl <= PLAT_MAX_PWR_LVL; lvl++) {
if (target_state->pwr_domain_state[lvl] == SQ_LOCAL_STATE_RUN)
break;
assert(target_state->pwr_domain_state[lvl] ==
SQ_LOCAL_STATE_OFF);
SCMI_SET_PWR_STATE_LVL(scmi_pwr_state, lvl,
scmi_power_state_off);
}
SCMI_SET_PWR_STATE_MAX_PWR_LVL(scmi_pwr_state, lvl - 1);
ret = scmi_pwr_state_set(sq_scmi_handle,
sq_core_pos_to_scmi_dmn_id_map[plat_my_core_pos()],
scmi_pwr_state);
if (ret != SCMI_E_QUEUED && ret != SCMI_E_SUCCESS) {
ERROR("SCMI set power state command return 0x%x unexpected\n",
ret);
panic();
}
}
/*
* Helper function to turn ON a CPU power domain and
*its parent power domains if applicable.
*/
void sq_scmi_on(u_register_t mpidr)
{
int lvl = 0, ret, core_pos;
uint32_t scmi_pwr_state = 0;
for (; lvl <= PLAT_MAX_PWR_LVL; lvl++)
SCMI_SET_PWR_STATE_LVL(scmi_pwr_state, lvl,
scmi_power_state_on);
SCMI_SET_PWR_STATE_MAX_PWR_LVL(scmi_pwr_state, lvl - 1);
core_pos = plat_core_pos_by_mpidr(mpidr);
assert(core_pos >= 0 && core_pos < PLATFORM_CORE_COUNT);
ret = scmi_pwr_state_set(sq_scmi_handle,
sq_core_pos_to_scmi_dmn_id_map[core_pos],
scmi_pwr_state);
if (ret != SCMI_E_QUEUED && ret != SCMI_E_SUCCESS) {
ERROR("SCMI set power state command return 0x%x unexpected\n",
ret);
panic();
}
}
void __dead2 sq_scmi_system_off(int state)
{
int ret;
/*
* Disable GIC CPU interface to prevent pending interrupt from waking
* up the AP from WFI.
*/
sq_gic_cpuif_disable();
/*
* Issue SCMI command. First issue a graceful
* request and if that fails force the request.
*/
ret = scmi_sys_pwr_state_set(sq_scmi_handle,
SCMI_SYS_PWR_FORCEFUL_REQ,
state);
if (ret != SCMI_E_SUCCESS) {
ERROR("SCMI system power state set 0x%x returns unexpected 0x%x\n",
state, ret);
panic();
}
wfi();
ERROR("SCMI set power state: operation not handled.\n");
panic();
}
/*
* Helper function to reset the system via SCMI.
*/
void __dead2 sq_scmi_sys_reboot(void)
{
sq_scmi_system_off(SCMI_SYS_PWR_COLD_RESET);
}
static int scmi_ap_core_init(scmi_channel_t *ch)
{
#if PROGRAMMABLE_RESET_ADDRESS
uint32_t version;
int ret;
ret = scmi_proto_version(ch, SCMI_AP_CORE_PROTO_ID, &version);
if (ret != SCMI_E_SUCCESS) {
WARN("SCMI AP core protocol version message failed\n");
return -1;
}
if (!is_scmi_version_compatible(SCMI_AP_CORE_PROTO_VER, version)) {
WARN("SCMI AP core protocol version 0x%x incompatible with driver version 0x%x\n",
version, SCMI_AP_CORE_PROTO_VER);
return -1;
}
INFO("SCMI AP core protocol version 0x%x detected\n", version);
#endif
return 0;
}
void __init plat_sq_pwrc_setup(void)
{
channel.info = &sq_scmi_plat_info;
channel.lock = SQ_SCMI_LOCK_GET_INSTANCE;
sq_scmi_handle = scmi_init(&channel);
if (sq_scmi_handle == NULL) {
ERROR("SCMI Initialization failed\n");
panic();
}
if (scmi_ap_core_init(&channel) < 0) {
ERROR("SCMI AP core protocol initialization failed\n");
panic();
}
}
uint32_t sq_scmi_get_draminfo(struct draminfo *info)
{
scmi_get_draminfo(sq_scmi_handle, info);
return 0;
}

View File

@ -0,0 +1,21 @@
/*
* Copyright (c) 2019, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <sq_common.h>
#include "sq_scpi.h"
/*
* Helper function to get dram information from SCP.
*/
uint32_t sq_scp_get_draminfo(struct draminfo *info)
{
#if SQ_USE_SCMI_DRIVER
sq_scmi_get_draminfo(info);
#else
scpi_get_draminfo(info);
#endif
return 0;
}

View File

@ -78,5 +78,6 @@ extern void scpi_set_sq_power_state(unsigned int mpidr,
scpi_power_state_t cluster_state,
scpi_power_state_t css_state);
uint32_t scpi_sys_power_state(scpi_system_state_t system_state);
uint32_t scpi_get_draminfo(struct draminfo *info);
#endif /* SQ_SCPI_H */

View File

@ -16,6 +16,16 @@
#define PLATFORM_CORE_COUNT (PLAT_CLUSTER_COUNT * \
PLAT_MAX_CORES_PER_CLUSTER)
/* Macros to read the SQ power domain state */
#define SQ_PWR_LVL0 MPIDR_AFFLVL0
#define SQ_PWR_LVL1 MPIDR_AFFLVL1
#define SQ_PWR_LVL2 MPIDR_AFFLVL2
#define SQ_CORE_PWR_STATE(state) (state)->pwr_domain_state[SQ_PWR_LVL0]
#define SQ_CLUSTER_PWR_STATE(state) (state)->pwr_domain_state[SQ_PWR_LVL1]
#define SQ_SYSTEM_PWR_STATE(state) ((PLAT_MAX_PWR_LVL > SQ_PWR_LVL1) ?\
(state)->pwr_domain_state[SQ_PWR_LVL2] : 0)
#define PLAT_MAX_PWR_LVL U(1)
#define PLAT_MAX_RET_STATE U(1)
#define PLAT_MAX_OFF_STATE U(2)
@ -70,6 +80,7 @@
#define DRAMINFO_BASE 0x2E00FFC0
#define PLAT_SQ_MHU_BASE 0x45000000
#define PLAT_CSS_MHU_BASE PLAT_SQ_MHU_BASE
#define PLAT_SQ_SCP_COM_SHARED_MEM_BASE 0x45400000
#define SCPI_CMD_GET_DRAMINFO 0x1

View File

@ -9,6 +9,7 @@
#include <stdint.h>
#include <lib/psci/psci.h>
#include <lib/xlat_tables/xlat_tables_v2.h>
struct draminfo {
@ -22,7 +23,7 @@ struct draminfo {
uint64_t size3;
};
uint32_t scpi_get_draminfo(struct draminfo *info);
uint32_t sq_scp_get_draminfo(struct draminfo *info);
void plat_sq_pwrc_setup(void);
@ -41,4 +42,12 @@ void sq_gic_pcpu_init(void);
void sq_mmap_setup(uintptr_t total_base, size_t total_size,
const struct mmap_region *mmap);
/* SCMI API for power management by SCP */
void sq_scmi_off(const struct psci_power_state *target_state);
void sq_scmi_on(u_register_t mpidr);
void __dead2 sq_scmi_sys_reboot(void);
void __dead2 sq_scmi_system_off(int state);
/* SCMI API for vendor specific protocol */
uint32_t sq_scmi_get_draminfo(struct draminfo *info);
#endif /* SQ_COMMON_H */

View File

@ -10,9 +10,10 @@ override PROGRAMMABLE_RESET_ADDRESS := 1
override USE_COHERENT_MEM := 1
override SEPARATE_CODE_AND_RODATA := 1
override ENABLE_SVE_FOR_NS := 0
# Enable workarounds for selected Cortex-A53 erratas.
ERRATA_A53_855873 := 1
# Enable SCMI support
SQ_USE_SCMI_DRIVER ?= 0
# Libraries
include lib/xlat_tables_v2/xlat_tables.mk
@ -20,7 +21,9 @@ include lib/xlat_tables_v2/xlat_tables.mk
PLAT_PATH := plat/socionext/synquacer
PLAT_INCLUDES := -I$(PLAT_PATH)/include \
-I$(PLAT_PATH)/drivers/scpi \
-I$(PLAT_PATH)/drivers/mhu
-I$(PLAT_PATH)/drivers/mhu \
-Idrivers/arm/css/scmi \
-Idrivers/arm/css/scmi/vendor
PLAT_BL_COMMON_SOURCES += $(PLAT_PATH)/sq_helpers.S \
drivers/arm/pl011/aarch64/pl011_console.S \
@ -40,12 +43,27 @@ BL31_SOURCES += drivers/arm/ccn/ccn.c \
$(PLAT_PATH)/sq_topology.c \
$(PLAT_PATH)/sq_psci.c \
$(PLAT_PATH)/sq_gicv3.c \
$(PLAT_PATH)/sq_xlat_setup.c \
$(PLAT_PATH)/drivers/scpi/sq_scpi.c \
$(PLAT_PATH)/sq_xlat_setup.c \
$(PLAT_PATH)/drivers/scp/sq_scp.c
ifeq (${SQ_USE_SCMI_DRIVER},0)
BL31_SOURCES += $(PLAT_PATH)/drivers/scpi/sq_scpi.c \
$(PLAT_PATH)/drivers/mhu/sq_mhu.c
else
BL31_SOURCES += $(PLAT_PATH)/drivers/scp/sq_scmi.c \
drivers/arm/css/scmi/scmi_common.c \
drivers/arm/css/scmi/scmi_pwr_dmn_proto.c \
drivers/arm/css/scmi/scmi_sys_pwr_proto.c \
drivers/arm/css/scmi/vendor/scmi_sq.c \
drivers/arm/css/mhu/css_mhu_doorbell.c
endif
ifeq (${ENABLE_SPM},1)
$(eval $(call add_define,PLAT_EXTRA_LD_SCRIPT))
BL31_SOURCES += $(PLAT_PATH)/sq_spm.c
endif
ifeq (${SQ_USE_SCMI_DRIVER},1)
$(eval $(call add_define,SQ_USE_SCMI_DRIVER))
endif

View File

@ -14,7 +14,6 @@
#include <common/debug.h>
#include <drivers/arm/pl011.h>
#include <lib/mmio.h>
#include <sq_common.h>
static console_pl011_t console;
@ -83,7 +82,7 @@ void bl31_early_platform_setup2(u_register_t arg0, u_register_t arg1,
#ifdef SPD_opteed
struct draminfo di = {0};
scpi_get_draminfo(&di);
sq_scp_get_draminfo(&di);
/*
* Check if OP-TEE has been loaded in Secure RAM allocated
@ -154,7 +153,7 @@ void bl31_plat_runtime_setup(void)
{
struct draminfo *di = (struct draminfo *)(unsigned long)DRAMINFO_BASE;
scpi_get_draminfo(di);
sq_scp_get_draminfo(di);
}
void bl31_plat_arch_setup(void)

View File

@ -19,26 +19,20 @@
#include <sq_common.h>
#include "sq_scpi.h"
/* Macros to read the SQ power domain state */
#define SQ_PWR_LVL0 MPIDR_AFFLVL0
#define SQ_PWR_LVL1 MPIDR_AFFLVL1
#define SQ_PWR_LVL2 MPIDR_AFFLVL2
#define SQ_CORE_PWR_STATE(state) (state)->pwr_domain_state[SQ_PWR_LVL0]
#define SQ_CLUSTER_PWR_STATE(state) (state)->pwr_domain_state[SQ_PWR_LVL1]
#define SQ_SYSTEM_PWR_STATE(state) ((PLAT_MAX_PWR_LVL > SQ_PWR_LVL1) ?\
(state)->pwr_domain_state[SQ_PWR_LVL2] : 0)
uintptr_t sq_sec_entrypoint;
int sq_pwr_domain_on(u_register_t mpidr)
{
#if SQ_USE_SCMI_DRIVER
sq_scmi_on(mpidr);
#else
/*
* SCP takes care of powering up parent power domains so we
* only need to care about level 0
*/
scpi_set_sq_power_state(mpidr, scpi_power_on, scpi_power_on,
scpi_power_on);
#endif
return PSCI_E_SUCCESS;
}
@ -70,6 +64,7 @@ void sq_pwr_domain_on_finish(const psci_power_state_t *target_state)
sq_gic_cpuif_enable();
}
#if !SQ_USE_SCMI_DRIVER
static void sq_power_down_common(const psci_power_state_t *target_state)
{
uint32_t cluster_state = scpi_power_on;
@ -97,10 +92,15 @@ static void sq_power_down_common(const psci_power_state_t *target_state)
cluster_state,
system_state);
}
#endif
void sq_pwr_domain_off(const psci_power_state_t *target_state)
{
#if SQ_USE_SCMI_DRIVER
sq_scmi_off(target_state);
#else
sq_power_down_common(target_state);
#endif
}
void __dead2 sq_system_off(void)
@ -135,6 +135,9 @@ void __dead2 sq_system_off(void)
void __dead2 sq_system_reset(void)
{
#if SQ_USE_SCMI_DRIVER
sq_scmi_sys_reboot();
#else
uint32_t response;
/* Send the system reset request to the SCP */
@ -147,6 +150,7 @@ void __dead2 sq_system_reset(void)
wfi();
ERROR("SQ System Reset: operation not handled.\n");
panic();
#endif
}
void sq_cpu_standby(plat_local_state_t cpu_state)