From 2b2b565717cc0299e75e8806004d1a3548e9fbf7 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 23 Jan 2022 17:12:26 -0600 Subject: [PATCH] feat(fdt): add the ability to supply idle state information Some platforms require extra firmware to implement CPU_SUSPEND, or only have working CPU_SUSPEND in certain configurations. On these platforms, CPU idle states should only be listed in the devicetree when they are actually available. Add a function BL31 can use to dynamically supply this idle state information. Change-Id: I64fcc288303faba8abec4f59efd13a04220d54dc Signed-off-by: Samuel Holland --- common/fdt_fixup.c | 106 ++++++++++++++++++++++++++++++++++++- include/common/fdt_fixup.h | 15 ++++++ 2 files changed, 120 insertions(+), 1 deletion(-) diff --git a/common/fdt_fixup.c b/common/fdt_fixup.c index de02b46e8..b1d628cc0 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 @@ 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 diff --git a/include/common/fdt_fixup.h b/include/common/fdt_fixup.h index 7a590b2dc..c35e9be8d 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 +#include +#include + #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);