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>
This commit is contained in:
Samuel Holland 2022-01-23 17:12:26 -06:00 committed by Joanna Farley
parent 79808f10c3
commit 2b2b565717
2 changed files with 120 additions and 1 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);