diff mbox series

[v2,1/2] lib: utils: Add fdt_add_cpu_idle_states() helper function

Message ID 20230123063208.19590-1-samuel@sholland.org
State Accepted
Headers show
Series [v2,1/2] lib: utils: Add fdt_add_cpu_idle_states() helper function | expand

Commit Message

Samuel Holland Jan. 23, 2023, 6:32 a.m. UTC
Since the availability and latency properties of CPU idle states depend
on the specific SBI HSM implementation, it is appropriate that the idle
states are added to the devicetree at runtime by that implementation.

This helper function adds a platform-provided array of idle states to
the devicetree, following the SBI idle state binding.

Reviewed-by: Anup Patel <anup@brainfault.org>
Signed-off-by: Samuel Holland <samuel@sholland.org>
---

Changes in v2:
 - Do nothing if the idle-states node already exists

 include/sbi_utils/fdt/fdt_fixup.h | 23 +++++++++
 lib/utils/fdt/fdt_fixup.c         | 85 +++++++++++++++++++++++++++++++
 2 files changed, 108 insertions(+)

Comments

Anup Patel Jan. 24, 2023, 12:32 p.m. UTC | #1
On Mon, Jan 23, 2023 at 12:02 PM Samuel Holland <samuel@sholland.org> wrote:
>
> Since the availability and latency properties of CPU idle states depend
> on the specific SBI HSM implementation, it is appropriate that the idle
> states are added to the devicetree at runtime by that implementation.
>
> This helper function adds a platform-provided array of idle states to
> the devicetree, following the SBI idle state binding.
>
> Reviewed-by: Anup Patel <anup@brainfault.org>
> Signed-off-by: Samuel Holland <samuel@sholland.org>

Applied this patch to the riscv/opensbi repo.

Thanks,
Anup

> ---
>
> Changes in v2:
>  - Do nothing if the idle-states node already exists
>
>  include/sbi_utils/fdt/fdt_fixup.h | 23 +++++++++
>  lib/utils/fdt/fdt_fixup.c         | 85 +++++++++++++++++++++++++++++++
>  2 files changed, 108 insertions(+)
>
> diff --git a/include/sbi_utils/fdt/fdt_fixup.h b/include/sbi_utils/fdt/fdt_fixup.h
> index fb076ba..cab3f0f 100644
> --- a/include/sbi_utils/fdt/fdt_fixup.h
> +++ b/include/sbi_utils/fdt/fdt_fixup.h
> @@ -9,6 +9,29 @@
>  #ifndef __FDT_FIXUP_H__
>  #define __FDT_FIXUP_H__
>
> +struct sbi_cpu_idle_state {
> +       const char *name;
> +       uint32_t suspend_param;
> +       bool local_timer_stop;
> +       uint32_t entry_latency_us;
> +       uint32_t exit_latency_us;
> +       uint32_t min_residency_us;
> +       uint32_t wakeup_latency_us;
> +};
> +
> +/**
> + * Add CPU idle states to cpu nodes in the DT
> + *
> + * 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.
> + *
> + * @param fdt: device tree blob
> + * @param states: array of idle state descriptions, ending with empty element
> + * @return zero on success and -ve on failure
> + */
> +int fdt_add_cpu_idle_states(void *dtb, const struct sbi_cpu_idle_state *state);
> +
>  /**
>   * Fix up the CPU node in the device tree
>   *
> diff --git a/lib/utils/fdt/fdt_fixup.c b/lib/utils/fdt/fdt_fixup.c
> index 42692cc..e57a4e1 100644
> --- a/lib/utils/fdt/fdt_fixup.c
> +++ b/lib/utils/fdt/fdt_fixup.c
> @@ -18,6 +18,91 @@
>  #include <sbi_utils/fdt/fdt_pmu.h>
>  #include <sbi_utils/fdt/fdt_helper.h>
>
> +int fdt_add_cpu_idle_states(void *fdt, const struct sbi_cpu_idle_state *state)
> +{
> +       int cpu_node, cpus_node, err, idle_states_node;
> +       uint32_t count, phandle;
> +
> +       err = fdt_open_into(fdt, fdt, fdt_totalsize(fdt) + 1024);
> +       if (err < 0)
> +               return err;
> +
> +       err = fdt_find_max_phandle(fdt, &phandle);
> +       phandle++;
> +       if (err < 0)
> +               return err;
> +
> +       cpus_node = fdt_path_offset(fdt, "/cpus");
> +       if (cpus_node < 0)
> +               return cpus_node;
> +
> +       /* Do nothing if the idle-states node already exists. */
> +       idle_states_node = fdt_subnode_offset(fdt, cpus_node, "idle-states");
> +       if (idle_states_node >= 0)
> +               return 0;
> +
> +       /* Create the idle-states node and its child nodes. */
> +       idle_states_node = fdt_add_subnode(fdt, cpus_node, "idle-states");
> +       if (idle_states_node < 0)
> +               return idle_states_node;
> +
> +       for (count = 0; state->name; count++, phandle++, state++) {
> +               int idle_state_node;
> +
> +               idle_state_node = fdt_add_subnode(fdt, idle_states_node,
> +                                                 state->name);
> +               if (idle_state_node < 0)
> +                       return idle_state_node;
> +
> +               fdt_setprop_string(fdt, idle_state_node, "compatible",
> +                                  "riscv,idle-state");
> +               fdt_setprop_u32(fdt, idle_state_node,
> +                               "riscv,sbi-suspend-param",
> +                               state->suspend_param);
> +               if (state->local_timer_stop)
> +                       fdt_setprop_empty(fdt, idle_state_node,
> +                                         "local-timer-stop");
> +               fdt_setprop_u32(fdt, idle_state_node, "entry-latency-us",
> +                               state->entry_latency_us);
> +               fdt_setprop_u32(fdt, idle_state_node, "exit-latency-us",
> +                               state->exit_latency_us);
> +               fdt_setprop_u32(fdt, idle_state_node, "min-residency-us",
> +                               state->min_residency_us);
> +               if (state->wakeup_latency_us)
> +                       fdt_setprop_u32(fdt, idle_state_node,
> +                                       "wakeup-latency-us",
> +                                       state->wakeup_latency_us);
> +               fdt_setprop_u32(fdt, idle_state_node, "phandle", phandle);
> +       }
> +
> +       if (count == 0)
> +               return 0;
> +
> +       /* Link each cpu node to the idle state nodes. */
> +       fdt_for_each_subnode(cpu_node, fdt, cpus_node) {
> +               const char *device_type;
> +               fdt32_t *value;
> +
> +               /* Only process child nodes with device_type = "cpu". */
> +               device_type = fdt_getprop(fdt, cpu_node, "device_type", NULL);
> +               if (!device_type || strcmp(device_type, "cpu"))
> +                       continue;
> +
> +               /* Allocate space for the list of phandles. */
> +               err = fdt_setprop_placeholder(fdt, cpu_node, "cpu-idle-states",
> +                                             count * sizeof(phandle),
> +                                             (void **)&value);
> +               if (err < 0)
> +                       return err;
> +
> +               /* Fill in the phandles of the idle state nodes. */
> +               for (uint32_t i = 0; i < count; ++i)
> +                       value[i] = cpu_to_fdt32(phandle - count + i);
> +       }
> +
> +       return 0;
> +}
> +
>  void fdt_cpu_fixup(void *fdt)
>  {
>         struct sbi_domain *dom = sbi_domain_thishart_ptr();
> --
> 2.37.4
>
diff mbox series

Patch

diff --git a/include/sbi_utils/fdt/fdt_fixup.h b/include/sbi_utils/fdt/fdt_fixup.h
index fb076ba..cab3f0f 100644
--- a/include/sbi_utils/fdt/fdt_fixup.h
+++ b/include/sbi_utils/fdt/fdt_fixup.h
@@ -9,6 +9,29 @@ 
 #ifndef __FDT_FIXUP_H__
 #define __FDT_FIXUP_H__
 
+struct sbi_cpu_idle_state {
+	const char *name;
+	uint32_t suspend_param;
+	bool local_timer_stop;
+	uint32_t entry_latency_us;
+	uint32_t exit_latency_us;
+	uint32_t min_residency_us;
+	uint32_t wakeup_latency_us;
+};
+
+/**
+ * Add CPU idle states to cpu nodes in the DT
+ *
+ * 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.
+ *
+ * @param fdt: device tree blob
+ * @param states: array of idle state descriptions, ending with empty element
+ * @return zero on success and -ve on failure
+ */
+int fdt_add_cpu_idle_states(void *dtb, const struct sbi_cpu_idle_state *state);
+
 /**
  * Fix up the CPU node in the device tree
  *
diff --git a/lib/utils/fdt/fdt_fixup.c b/lib/utils/fdt/fdt_fixup.c
index 42692cc..e57a4e1 100644
--- a/lib/utils/fdt/fdt_fixup.c
+++ b/lib/utils/fdt/fdt_fixup.c
@@ -18,6 +18,91 @@ 
 #include <sbi_utils/fdt/fdt_pmu.h>
 #include <sbi_utils/fdt/fdt_helper.h>
 
+int fdt_add_cpu_idle_states(void *fdt, const struct sbi_cpu_idle_state *state)
+{
+	int cpu_node, cpus_node, err, idle_states_node;
+	uint32_t count, phandle;
+
+	err = fdt_open_into(fdt, fdt, fdt_totalsize(fdt) + 1024);
+	if (err < 0)
+		return err;
+
+	err = fdt_find_max_phandle(fdt, &phandle);
+	phandle++;
+	if (err < 0)
+		return err;
+
+	cpus_node = fdt_path_offset(fdt, "/cpus");
+	if (cpus_node < 0)
+		return cpus_node;
+
+	/* Do nothing if the idle-states node already exists. */
+	idle_states_node = fdt_subnode_offset(fdt, cpus_node, "idle-states");
+	if (idle_states_node >= 0)
+		return 0;
+
+	/* Create the idle-states node and its child nodes. */
+	idle_states_node = fdt_add_subnode(fdt, cpus_node, "idle-states");
+	if (idle_states_node < 0)
+		return idle_states_node;
+
+	for (count = 0; state->name; count++, phandle++, state++) {
+		int idle_state_node;
+
+		idle_state_node = fdt_add_subnode(fdt, idle_states_node,
+						  state->name);
+		if (idle_state_node < 0)
+			return idle_state_node;
+
+		fdt_setprop_string(fdt, idle_state_node, "compatible",
+				   "riscv,idle-state");
+		fdt_setprop_u32(fdt, idle_state_node,
+				"riscv,sbi-suspend-param",
+				state->suspend_param);
+		if (state->local_timer_stop)
+			fdt_setprop_empty(fdt, idle_state_node,
+					  "local-timer-stop");
+		fdt_setprop_u32(fdt, idle_state_node, "entry-latency-us",
+				state->entry_latency_us);
+		fdt_setprop_u32(fdt, idle_state_node, "exit-latency-us",
+				state->exit_latency_us);
+		fdt_setprop_u32(fdt, idle_state_node, "min-residency-us",
+				state->min_residency_us);
+		if (state->wakeup_latency_us)
+			fdt_setprop_u32(fdt, idle_state_node,
+					"wakeup-latency-us",
+					state->wakeup_latency_us);
+		fdt_setprop_u32(fdt, idle_state_node, "phandle", phandle);
+	}
+
+	if (count == 0)
+		return 0;
+
+	/* Link each cpu node to the idle state nodes. */
+	fdt_for_each_subnode(cpu_node, fdt, cpus_node) {
+		const char *device_type;
+		fdt32_t *value;
+
+		/* Only process child nodes with device_type = "cpu". */
+		device_type = fdt_getprop(fdt, cpu_node, "device_type", NULL);
+		if (!device_type || strcmp(device_type, "cpu"))
+			continue;
+
+		/* Allocate space for the list of phandles. */
+		err = fdt_setprop_placeholder(fdt, cpu_node, "cpu-idle-states",
+					      count * sizeof(phandle),
+					      (void **)&value);
+		if (err < 0)
+			return err;
+
+		/* Fill in the phandles of the idle state nodes. */
+		for (uint32_t i = 0; i < count; ++i)
+			value[i] = cpu_to_fdt32(phandle - count + i);
+	}
+
+	return 0;
+}
+
 void fdt_cpu_fixup(void *fdt)
 {
 	struct sbi_domain *dom = sbi_domain_thishart_ptr();