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
This commit is contained in:
André Przywara 2022-05-04 02:10:02 +02:00 committed by TrustedFirmware Code Review
commit 3e0a087f30
14 changed files with 275 additions and 60 deletions

View file

@ -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 @@ int fdt_add_cpus_node(void *dtb, unsigned int afflv0,
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

View file

@ -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);

View file

@ -27,6 +27,7 @@ BL31_SOURCES += drivers/allwinner/axp/common.c \
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 @@ SUNXI_SETUP_REGULATORS ?= 1
$(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

View file

@ -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) + \

View file

@ -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 @@ static inline void sunxi_set_native_psci_ops(const plat_psci_ops_t **psci_ops)
}
#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 @@ void sunxi_set_gpio_out(char port, int pin, bool level_high);
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)

View file

@ -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 entry_point_info_t bl33_image_ep_info;
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 @@ static const gicv2_driver_data_t sunxi_gic_data = {
* 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 @@ static void *sunxi_find_dtb(void)
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 @@ static void *sunxi_find_dtb(void)
continue;
/* end of the image: base address + size */
dtb_base = (void *)((char *)u_boot_base + u_boot_base[i + 1]);
dtb_base = (char *)u_boot_base + u_boot_base[i + 1];
if (fdt_check_header(dtb_base) != 0)
continue;
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 @@ void bl31_platform_setup(void)
{
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 @@ void bl31_platform_setup(void)
generic_delay_timer_init();
fdt = sunxi_find_dtb();
sunxi_find_dtb();
if (fdt) {
const char *model;
int length;
@ -180,9 +179,15 @@ void bl31_platform_setup(void)
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)

View file

@ -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 @@ int plat_setup_psci_ops(uintptr_t sec_entrypoint,
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);

View file

@ -19,25 +19,34 @@ void sunxi_prepare_dtb(void *fdt)
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");
}

View file

@ -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 @@ static void sunxi_pwr_domain_off(const psci_power_state_t *target_state)
}
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 @@ static int sunxi_validate_power_state(unsigned int power_state,
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 @@ static int sunxi_validate_power_state(unsigned int power_state,
}
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;
}

View file

@ -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
},
{}
};

View file

@ -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[] = {
{}
};

View file

@ -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 @@ endif
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))

View file

@ -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[] = {
{}
};

View file

@ -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[] = {
{}
};