summaryrefslogtreecommitdiff
path: root/plat/socionext
diff options
context:
space:
mode:
authorMasahisa Kojima <masahisa.kojima@linaro.org>2019-03-07 10:41:54 +0900
committerMasahisa Kojima <masahisa.kojima@linaro.org>2019-03-13 09:54:15 +0900
commitb67d20297fd67d29931283c6a01e4aab7898d570 (patch)
tree780bdb534b7f1eaa9fa26adbc27c4c4ce77ec391 /plat/socionext
parentc48d02bade88b07fa7f43aa44e5217f68e5d047f (diff)
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>
Diffstat (limited to 'plat/socionext')
-rw-r--r--plat/socionext/synquacer/drivers/scp/sq_scmi.c239
-rw-r--r--plat/socionext/synquacer/drivers/scp/sq_scp.c21
-rw-r--r--plat/socionext/synquacer/drivers/scpi/sq_scpi.h1
-rw-r--r--plat/socionext/synquacer/include/platform_def.h11
-rw-r--r--plat/socionext/synquacer/include/sq_common.h11
-rw-r--r--plat/socionext/synquacer/platform.mk26
-rw-r--r--plat/socionext/synquacer/sq_bl31_setup.c5
-rw-r--r--plat/socionext/synquacer/sq_psci.c24
8 files changed, 320 insertions, 18 deletions
diff --git a/plat/socionext/synquacer/drivers/scp/sq_scmi.c b/plat/socionext/synquacer/drivers/scp/sq_scmi.c
new file mode 100644
index 00000000..e2013cc5
--- /dev/null
+++ b/plat/socionext/synquacer/drivers/scp/sq_scmi.c
@@ -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;
+}
diff --git a/plat/socionext/synquacer/drivers/scp/sq_scp.c b/plat/socionext/synquacer/drivers/scp/sq_scp.c
new file mode 100644
index 00000000..e494022b
--- /dev/null
+++ b/plat/socionext/synquacer/drivers/scp/sq_scp.c
@@ -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;
+}
diff --git a/plat/socionext/synquacer/drivers/scpi/sq_scpi.h b/plat/socionext/synquacer/drivers/scpi/sq_scpi.h
index 38cc19e5..eb6ce5c9 100644
--- a/plat/socionext/synquacer/drivers/scpi/sq_scpi.h
+++ b/plat/socionext/synquacer/drivers/scpi/sq_scpi.h
@@ -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 */
diff --git a/plat/socionext/synquacer/include/platform_def.h b/plat/socionext/synquacer/include/platform_def.h
index 0cec81b8..263b215f 100644
--- a/plat/socionext/synquacer/include/platform_def.h
+++ b/plat/socionext/synquacer/include/platform_def.h
@@ -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
diff --git a/plat/socionext/synquacer/include/sq_common.h b/plat/socionext/synquacer/include/sq_common.h
index abd90904..a9858229 100644
--- a/plat/socionext/synquacer/include/sq_common.h
+++ b/plat/socionext/synquacer/include/sq_common.h
@@ -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 */
diff --git a/plat/socionext/synquacer/platform.mk b/plat/socionext/synquacer/platform.mk
index 53c39a0f..f5e72cb6 100644
--- a/plat/socionext/synquacer/platform.mk
+++ b/plat/socionext/synquacer/platform.mk
@@ -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
diff --git a/plat/socionext/synquacer/sq_bl31_setup.c b/plat/socionext/synquacer/sq_bl31_setup.c
index fef84efd..c78fe918 100644
--- a/plat/socionext/synquacer/sq_bl31_setup.c
+++ b/plat/socionext/synquacer/sq_bl31_setup.c
@@ -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)
diff --git a/plat/socionext/synquacer/sq_psci.c b/plat/socionext/synquacer/sq_psci.c
index 134224dc..731b19a3 100644
--- a/plat/socionext/synquacer/sq_psci.c
+++ b/plat/socionext/synquacer/sq_psci.c
@@ -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)