diff options
author | Samuel Holland <samuel@sholland.org> | 2022-01-23 17:12:26 -0600 |
---|---|---|
committer | Joanna Farley <joanna.farley@arm.com> | 2022-04-26 17:52:25 +0200 |
commit | 2b2b565717cc0299e75e8806004d1a3548e9fbf7 (patch) | |
tree | aa8f1059be4bbd0d97b078647cdd30dc4265f8c5 /common | |
parent | 79808f10c32d441572666551b1545846079af15b (diff) | |
download | arm-trusted-firmware-2b2b565717cc0299e75e8806004d1a3548e9fbf7.tar.gz |
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 <samuel@sholland.org>
Diffstat (limited to 'common')
-rw-r--r-- | common/fdt_fixup.c | 106 |
1 files changed, 105 insertions, 1 deletions
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 |