Merge changes from topic "allwinner-idle" into integration
* changes:
feat(allwinner): provide CPU idle states to the rich OS
feat(allwinner): simplify CPU_SUSPEND power state encoding
feat(allwinner): choose PSCI states to avoid translation
feat(fdt): add the ability to supply idle state information
fix(allwinner): improve DTB patching error handling
refactor(allwinner): patch the DTB after setting up PSCI
refactor(allwinner): move DTB change code into allwinner/common
diff --git a/common/fdt_fixup.c b/common/fdt_fixup.c
index de02b46..b1d628c 100644
--- a/common/fdt_fixup.c
+++ b/common/fdt_fixup.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016-2020, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2016-2022, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
@@ -394,6 +394,110 @@
return offs;
}
+/*******************************************************************************
+ * fdt_add_cpu_idle_states() - add PSCI CPU idle states to cpu nodes in the DT
+ * @dtb: pointer to the device tree blob in memory
+ * @states: array of idle state descriptions, ending with empty element
+ *
+ * Add information about CPU idle states to the devicetree. This function
+ * assumes that CPU idle states are not already present in the devicetree, and
+ * that all CPU states are equally applicable to all CPUs.
+ *
+ * See arm/idle-states.yaml and arm/psci.yaml in the (Linux kernel) DT binding
+ * documentation for more details.
+ *
+ * Return: 0 on success, a negative error value otherwise.
+ ******************************************************************************/
+int fdt_add_cpu_idle_states(void *dtb, const struct psci_cpu_idle_state *state)
+{
+ int cpu_node, cpus_node, idle_states_node, ret;
+ uint32_t count, phandle;
+
+ ret = fdt_find_max_phandle(dtb, &phandle);
+ phandle++;
+ if (ret < 0) {
+ return ret;
+ }
+
+ cpus_node = fdt_path_offset(dtb, "/cpus");
+ if (cpus_node < 0) {
+ return cpus_node;
+ }
+
+ /* Create the idle-states node and its child nodes. */
+ idle_states_node = fdt_add_subnode(dtb, cpus_node, "idle-states");
+ if (idle_states_node < 0) {
+ return idle_states_node;
+ }
+
+ ret = fdt_setprop_string(dtb, idle_states_node, "entry-method", "psci");
+ if (ret < 0) {
+ return ret;
+ }
+
+ for (count = 0U; state->name != NULL; count++, phandle++, state++) {
+ int idle_state_node;
+
+ idle_state_node = fdt_add_subnode(dtb, idle_states_node,
+ state->name);
+ if (idle_state_node < 0) {
+ return idle_state_node;
+ }
+
+ fdt_setprop_string(dtb, idle_state_node, "compatible",
+ "arm,idle-state");
+ fdt_setprop_u32(dtb, idle_state_node, "arm,psci-suspend-param",
+ state->power_state);
+ if (state->local_timer_stop) {
+ fdt_setprop_empty(dtb, idle_state_node,
+ "local-timer-stop");
+ }
+ fdt_setprop_u32(dtb, idle_state_node, "entry-latency-us",
+ state->entry_latency_us);
+ fdt_setprop_u32(dtb, idle_state_node, "exit-latency-us",
+ state->exit_latency_us);
+ fdt_setprop_u32(dtb, idle_state_node, "min-residency-us",
+ state->min_residency_us);
+ if (state->wakeup_latency_us) {
+ fdt_setprop_u32(dtb, idle_state_node,
+ "wakeup-latency-us",
+ state->wakeup_latency_us);
+ }
+ fdt_setprop_u32(dtb, idle_state_node, "phandle", phandle);
+ }
+
+ if (count == 0U) {
+ return 0;
+ }
+
+ /* Link each cpu node to the idle state nodes. */
+ fdt_for_each_subnode(cpu_node, dtb, cpus_node) {
+ const char *device_type;
+ fdt32_t *value;
+
+ /* Only process child nodes with device_type = "cpu". */
+ device_type = fdt_getprop(dtb, cpu_node, "device_type", NULL);
+ if (device_type == NULL || strcmp(device_type, "cpu") != 0) {
+ continue;
+ }
+
+ /* Allocate space for the list of phandles. */
+ ret = fdt_setprop_placeholder(dtb, cpu_node, "cpu-idle-states",
+ count * sizeof(phandle),
+ (void **)&value);
+ if (ret < 0) {
+ return ret;
+ }
+
+ /* Fill in the phandles of the idle state nodes. */
+ for (uint32_t i = 0U; i < count; ++i) {
+ value[i] = cpu_to_fdt32(phandle - count + i);
+ }
+ }
+
+ return 0;
+}
+
/**
* fdt_adjust_gic_redist() - Adjust GICv3 redistributor size
* @dtb: Pointer to the DT blob in memory
diff --git a/include/common/fdt_fixup.h b/include/common/fdt_fixup.h
index 7a590b2..c35e9be 100644
--- a/include/common/fdt_fixup.h
+++ b/include/common/fdt_fixup.h
@@ -7,14 +7,29 @@
#ifndef FDT_FIXUP_H
#define FDT_FIXUP_H
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
#define INVALID_BASE_ADDR ((uintptr_t)~0UL)
+struct psci_cpu_idle_state {
+ const char *name;
+ uint32_t power_state;
+ bool local_timer_stop;
+ uint32_t entry_latency_us;
+ uint32_t exit_latency_us;
+ uint32_t min_residency_us;
+ uint32_t wakeup_latency_us;
+};
+
int dt_add_psci_node(void *fdt);
int dt_add_psci_cpu_enable_methods(void *fdt);
int fdt_add_reserved_memory(void *dtb, const char *node_name,
uintptr_t base, size_t size);
int fdt_add_cpus_node(void *dtb, unsigned int afflv0,
unsigned int afflv1, unsigned int afflv2);
+int fdt_add_cpu_idle_states(void *dtb, const struct psci_cpu_idle_state *state);
int fdt_adjust_gic_redist(void *dtb, unsigned int nr_cores, uintptr_t gicr_base,
unsigned int gicr_frame_size);
diff --git a/plat/allwinner/common/allwinner-common.mk b/plat/allwinner/common/allwinner-common.mk
index 34fdaf6..61c1dbe 100644
--- a/plat/allwinner/common/allwinner-common.mk
+++ b/plat/allwinner/common/allwinner-common.mk
@@ -27,6 +27,7 @@
plat/common/plat_gicv2.c \
plat/common/plat_psci_common.c \
${AW_PLAT}/common/sunxi_bl31_setup.c \
+ ${AW_PLAT}/${PLAT}/sunxi_idle_states.c \
${AW_PLAT}/common/sunxi_pm.c \
${AW_PLAT}/${PLAT}/sunxi_power.c \
${AW_PLAT}/common/sunxi_security.c \
@@ -65,6 +66,23 @@
$(eval $(call assert_boolean,SUNXI_SETUP_REGULATORS))
$(eval $(call add_define,SUNXI_SETUP_REGULATORS))
+SUNXI_BL31_IN_DRAM ?= 0
+$(eval $(call assert_boolean,SUNXI_BL31_IN_DRAM))
+
+ifeq (${SUNXI_BL31_IN_DRAM},1)
+SUNXI_AMEND_DTB := 1
+$(eval $(call add_define,SUNXI_BL31_IN_DRAM))
+endif
+
+SUNXI_AMEND_DTB ?= 0
+$(eval $(call assert_boolean,SUNXI_AMEND_DTB))
+$(eval $(call add_define,SUNXI_AMEND_DTB))
+
+ifeq (${SUNXI_AMEND_DTB},1)
+BL31_SOURCES += common/fdt_fixup.c \
+ ${AW_PLAT}/common/sunxi_prepare_dtb.c
+endif
+
# The bootloader is guaranteed to only run on CPU 0 by the boot ROM.
COLD_BOOT_SINGLE_CPU := 1
diff --git a/plat/allwinner/common/include/platform_def.h b/plat/allwinner/common/include/platform_def.h
index 49951e0..c9d075a 100644
--- a/plat/allwinner/common/include/platform_def.h
+++ b/plat/allwinner/common/include/platform_def.h
@@ -57,9 +57,10 @@
#define PLAT_CSS_SCP_COM_SHARED_MEM_BASE \
(SUNXI_SRAM_A2_BASE + SUNXI_SRAM_A2_SIZE - 0x200)
-#define PLAT_MAX_PWR_LVL_STATES U(2)
+/* These states are used directly for SCPI communication. */
+#define PLAT_MAX_PWR_LVL_STATES U(3)
#define PLAT_MAX_RET_STATE U(1)
-#define PLAT_MAX_OFF_STATE U(2)
+#define PLAT_MAX_OFF_STATE U(3)
#define PLAT_MAX_PWR_LVL U(2)
#define PLAT_NUM_PWR_DOMAINS (U(1) + \
diff --git a/plat/allwinner/common/include/sunxi_private.h b/plat/allwinner/common/include/sunxi_private.h
index 6cf4670..6a38657 100644
--- a/plat/allwinner/common/include/sunxi_private.h
+++ b/plat/allwinner/common/include/sunxi_private.h
@@ -7,8 +7,12 @@
#ifndef SUNXI_PRIVATE_H
#define SUNXI_PRIVATE_H
+#include <common/fdt_fixup.h>
+
#include <lib/psci/psci.h>
+extern const struct psci_cpu_idle_state sunxi_idle_states[];
+
void sunxi_configure_mmu_el3(int flags);
void sunxi_cpu_on(u_register_t mpidr);
@@ -24,8 +28,13 @@
}
#endif
#if SUNXI_PSCI_USE_SCPI
+bool sunxi_psci_is_scpi(void);
int sunxi_set_scpi_psci_ops(const plat_psci_ops_t **psci_ops);
#else
+static inline bool sunxi_psci_is_scpi(void)
+{
+ return false;
+}
static inline int sunxi_set_scpi_psci_ops(const plat_psci_ops_t **psci_ops)
{
return -1;
@@ -41,7 +50,7 @@
int sunxi_init_platform_r_twi(uint16_t socid, bool use_rsb);
void sunxi_execute_arisc_code(uint32_t *code, size_t size, uint16_t param);
-#ifdef SUNXI_BL31_IN_DRAM
+#if SUNXI_AMEND_DTB
void sunxi_prepare_dtb(void *fdt);
#else
static inline void sunxi_prepare_dtb(void *fdt)
diff --git a/plat/allwinner/common/sunxi_bl31_setup.c b/plat/allwinner/common/sunxi_bl31_setup.c
index 14049e8..a32124a 100644
--- a/plat/allwinner/common/sunxi_bl31_setup.c
+++ b/plat/allwinner/common/sunxi_bl31_setup.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2017-2022, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
@@ -32,6 +32,8 @@
static console_t console;
+static void *fdt;
+
static const gicv2_driver_data_t sunxi_gic_data = {
.gicd_base = SUNXI_GICD_BASE,
.gicc_base = SUNXI_GICC_BASE,
@@ -49,7 +51,7 @@
* entry point to find the load address, which should be followed by the
* size. Adding those together gives us the address of the DTB.
*/
-static void *sunxi_find_dtb(void)
+static void sunxi_find_dtb(void)
{
uint64_t *u_boot_base;
int i;
@@ -57,7 +59,7 @@
u_boot_base = (void *)SUNXI_BL33_VIRT_BASE;
for (i = 0; i < 2048 / sizeof(uint64_t); i++) {
- uint32_t *dtb_base;
+ void *dtb_base;
if (u_boot_base[i] != PRELOADED_BL33_BASE)
continue;
@@ -67,15 +69,13 @@
continue;
/* end of the image: base address + size */
- dtb_base = (void *)((char *)u_boot_base + u_boot_base[i + 1]);
-
- if (fdt_check_header(dtb_base) != 0)
- continue;
+ dtb_base = (char *)u_boot_base + u_boot_base[i + 1];
- return dtb_base;
+ if (fdt_check_header(dtb_base) == 0) {
+ fdt = dtb_base;
+ return;
+ }
}
-
- return NULL;
}
void bl31_early_platform_setup2(u_register_t arg0, u_register_t arg1,
@@ -113,7 +113,6 @@
{
const char *soc_name;
uint16_t soc_id = sunxi_read_soc_id();
- void *fdt;
switch (soc_id) {
case SUNXI_SOC_A64:
@@ -139,7 +138,7 @@
generic_delay_timer_init();
- fdt = sunxi_find_dtb();
+ sunxi_find_dtb();
if (fdt) {
const char *model;
int length;
@@ -180,9 +179,15 @@
sunxi_pmic_setup(soc_id, fdt);
+ INFO("BL31: Platform setup done\n");
+}
+
+void bl31_plat_runtime_setup(void)
+{
+ /* Change the DTB if the configuration requires so. */
sunxi_prepare_dtb(fdt);
- INFO("BL31: Platform setup done\n");
+ console_switch_state(CONSOLE_FLAG_RUNTIME);
}
entry_point_info_t *bl31_plat_get_next_image_ep_info(uint32_t type)
diff --git a/plat/allwinner/common/sunxi_pm.c b/plat/allwinner/common/sunxi_pm.c
index eb1b7e7..3772b4a 100644
--- a/plat/allwinner/common/sunxi_pm.c
+++ b/plat/allwinner/common/sunxi_pm.c
@@ -9,12 +9,22 @@
#include <platform_def.h>
#include <common/debug.h>
+#include <common/fdt_fixup.h>
#include <lib/mmio.h>
#include <lib/psci/psci.h>
#include <sunxi_cpucfg.h>
#include <sunxi_private.h>
+static bool psci_is_scpi;
+
+#if SUNXI_PSCI_USE_SCPI
+bool sunxi_psci_is_scpi(void)
+{
+ return psci_is_scpi;
+}
+#endif
+
int sunxi_validate_ns_entrypoint(uintptr_t ns_entrypoint)
{
/* The non-secure entry point must be in DRAM */
@@ -40,6 +50,7 @@
if (sunxi_set_scpi_psci_ops(psci_ops) == 0) {
INFO("PSCI: Suspend is available via SCPI\n");
+ psci_is_scpi = true;
} else {
INFO("PSCI: Suspend is unavailable\n");
sunxi_set_native_psci_ops(psci_ops);
diff --git a/plat/allwinner/sun50i_h616/prepare_dtb.c b/plat/allwinner/common/sunxi_prepare_dtb.c
similarity index 69%
rename from plat/allwinner/sun50i_h616/prepare_dtb.c
rename to plat/allwinner/common/sunxi_prepare_dtb.c
index e94b0b4..66af35a 100644
--- a/plat/allwinner/sun50i_h616/prepare_dtb.c
+++ b/plat/allwinner/common/sunxi_prepare_dtb.c
@@ -19,25 +19,34 @@
if (fdt == NULL || fdt_check_header(fdt) != 0) {
return;
}
- ret = fdt_open_into(fdt, fdt, 0x100000);
+
+ ret = fdt_open_into(fdt, fdt, 0x10000);
if (ret < 0) {
ERROR("Preparing devicetree at %p: error %d\n", fdt, ret);
return;
}
+#ifdef SUNXI_BL31_IN_DRAM
/* Reserve memory used by Trusted Firmware. */
if (fdt_add_reserved_memory(fdt, "tf-a@40000000", BL31_BASE,
BL31_LIMIT - BL31_BASE)) {
WARN("Failed to add reserved memory nodes to DT.\n");
- return;
}
+#endif
+
+ if (sunxi_psci_is_scpi()) {
+ ret = fdt_add_cpu_idle_states(fdt, sunxi_idle_states);
+ if (ret < 0) {
+ WARN("Failed to add idle states to DT: %d\n", ret);
+ }
+ }
ret = fdt_pack(fdt);
if (ret < 0) {
ERROR("Failed to pack devicetree at %p: error %d\n",
fdt, ret);
- } else {
- clean_dcache_range((uintptr_t)fdt, fdt_blob_size(fdt));
- INFO("Changed devicetree to reserve BL31 memory.\n");
}
+
+ clean_dcache_range((uintptr_t)fdt, fdt_blob_size(fdt));
+ INFO("Changed devicetree.\n");
}
diff --git a/plat/allwinner/common/sunxi_scpi_pm.c b/plat/allwinner/common/sunxi_scpi_pm.c
index eb37daa..41dc563 100644
--- a/plat/allwinner/common/sunxi_scpi_pm.c
+++ b/plat/allwinner/common/sunxi_scpi_pm.c
@@ -33,6 +33,9 @@
*/
#define SCP_FIRMWARE_MAGIC 0xb4400012
+#define PLAT_LOCAL_PSTATE_WIDTH U(4)
+#define PLAT_LOCAL_PSTATE_MASK ((U(1) << PLAT_LOCAL_PSTATE_WIDTH) - 1)
+
#define CPU_PWR_LVL MPIDR_AFFLVL0
#define CLUSTER_PWR_LVL MPIDR_AFFLVL1
#define SYSTEM_PWR_LVL MPIDR_AFFLVL2
@@ -44,17 +47,6 @@
#define SYSTEM_PWR_STATE(state) \
((state)->pwr_domain_state[SYSTEM_PWR_LVL])
-static inline scpi_power_state_t scpi_map_state(plat_local_state_t psci_state)
-{
- if (is_local_state_run(psci_state)) {
- return scpi_power_on;
- }
- if (is_local_state_retn(psci_state)) {
- return scpi_power_retention;
- }
- return scpi_power_off;
-}
-
static void sunxi_cpu_standby(plat_local_state_t cpu_state)
{
u_register_t scr = read_scr_el3();
@@ -87,9 +79,9 @@
}
scpi_set_css_power_state(read_mpidr(),
- scpi_map_state(cpu_pwr_state),
- scpi_map_state(cluster_pwr_state),
- scpi_map_state(system_pwr_state));
+ cpu_pwr_state,
+ cluster_pwr_state,
+ system_pwr_state);
}
static void sunxi_pwr_domain_on_finish(const psci_power_state_t *target_state)
@@ -137,7 +129,9 @@
psci_power_state_t *req_state)
{
unsigned int power_level = psci_get_pstate_pwrlvl(power_state);
+ unsigned int state_id = psci_get_pstate_id(power_state);
unsigned int type = psci_get_pstate_type(power_state);
+ unsigned int i;
assert(req_state != NULL);
@@ -146,28 +140,19 @@
}
if (type == PSTATE_TYPE_STANDBY) {
- /* Only one retention power state is supported. */
- if (psci_get_pstate_id(power_state) > 0) {
- return PSCI_E_INVALID_PARAMS;
- }
- /* The SoC cannot be suspended without losing state */
- if (power_level == SYSTEM_PWR_LVL) {
- return PSCI_E_INVALID_PARAMS;
- }
- for (unsigned int i = 0; i <= power_level; ++i) {
- req_state->pwr_domain_state[i] = PLAT_MAX_RET_STATE;
- }
- } else {
- /* Only one off power state is supported. */
- if (psci_get_pstate_id(power_state) > 0) {
- return PSCI_E_INVALID_PARAMS;
- }
- for (unsigned int i = 0; i <= power_level; ++i) {
- req_state->pwr_domain_state[i] = PLAT_MAX_OFF_STATE;
- }
+ return PSCI_E_INVALID_PARAMS;
}
+
+ /* Pass through the requested PSCI state as-is. */
+ for (i = 0; i <= power_level; ++i) {
+ unsigned int local_pstate = state_id & PLAT_LOCAL_PSTATE_MASK;
+
+ req_state->pwr_domain_state[i] = local_pstate;
+ state_id >>= PLAT_LOCAL_PSTATE_WIDTH;
+ }
+
/* Higher power domain levels should all remain running */
- for (unsigned int i = power_level + 1; i <= PLAT_MAX_PWR_LVL; ++i) {
+ for (; i <= PLAT_MAX_PWR_LVL; ++i) {
req_state->pwr_domain_state[i] = PSCI_LOCAL_STATE_RUN;
}
diff --git a/plat/allwinner/sun50i_a64/sunxi_idle_states.c b/plat/allwinner/sun50i_a64/sunxi_idle_states.c
new file mode 100644
index 0000000..2918bb7
--- /dev/null
+++ b/plat/allwinner/sun50i_a64/sunxi_idle_states.c
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2022, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <sunxi_private.h>
+
+const struct psci_cpu_idle_state sunxi_idle_states[] = {
+ {
+ .name = "cpu-sleep",
+ .power_state = 0x00010003,
+ .local_timer_stop = true,
+ .entry_latency_us = 800,
+ .exit_latency_us = 1500,
+ .min_residency_us = 25000
+ },
+ {
+ .name = "cluster-sleep",
+ .power_state = 0x01010013,
+ .local_timer_stop = true,
+ .entry_latency_us = 850,
+ .exit_latency_us = 1500,
+ .min_residency_us = 50000
+ },
+ {}
+};
diff --git a/plat/allwinner/sun50i_h6/sunxi_idle_states.c b/plat/allwinner/sun50i_h6/sunxi_idle_states.c
new file mode 100644
index 0000000..4339bcd
--- /dev/null
+++ b/plat/allwinner/sun50i_h6/sunxi_idle_states.c
@@ -0,0 +1,11 @@
+/*
+ * Copyright (c) 2022, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <sunxi_private.h>
+
+const struct psci_cpu_idle_state sunxi_idle_states[] = {
+ {}
+};
diff --git a/plat/allwinner/sun50i_h616/platform.mk b/plat/allwinner/sun50i_h616/platform.mk
index fc09af7..de494a2 100644
--- a/plat/allwinner/sun50i_h616/platform.mk
+++ b/plat/allwinner/sun50i_h616/platform.mk
@@ -4,6 +4,8 @@
# SPDX-License-Identifier: BSD-3-Clause
#
+SUNXI_BL31_IN_DRAM := 1
+
# Without a management processor there is no SCPI support.
SUNXI_PSCI_USE_SCPI := 0
SUNXI_PSCI_USE_NATIVE := 1
@@ -18,7 +20,3 @@
BL31_SOURCES += drivers/allwinner/axp/axp805.c \
drivers/allwinner/sunxi_rsb.c \
- common/fdt_fixup.c \
- ${AW_PLAT}/${PLAT}/prepare_dtb.c
-
-$(eval $(call add_define,SUNXI_BL31_IN_DRAM))
diff --git a/plat/allwinner/sun50i_h616/sunxi_idle_states.c b/plat/allwinner/sun50i_h616/sunxi_idle_states.c
new file mode 100644
index 0000000..4339bcd
--- /dev/null
+++ b/plat/allwinner/sun50i_h616/sunxi_idle_states.c
@@ -0,0 +1,11 @@
+/*
+ * Copyright (c) 2022, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <sunxi_private.h>
+
+const struct psci_cpu_idle_state sunxi_idle_states[] = {
+ {}
+};
diff --git a/plat/allwinner/sun50i_r329/sunxi_idle_states.c b/plat/allwinner/sun50i_r329/sunxi_idle_states.c
new file mode 100644
index 0000000..4339bcd
--- /dev/null
+++ b/plat/allwinner/sun50i_r329/sunxi_idle_states.c
@@ -0,0 +1,11 @@
+/*
+ * Copyright (c) 2022, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <sunxi_private.h>
+
+const struct psci_cpu_idle_state sunxi_idle_states[] = {
+ {}
+};