From 598a7acb19af3763aa17926168e24d8131f3a57d Mon Sep 17 00:00:00 2001
From: Brendan Jackman <brendan.jackman@arm.com>
Date: Mon, 25 Jul 2016 14:26:08 +0100
Subject: [PATCH] cpuidle: dt: Parse states from power domain nodes
This commit allows bindings similar to those in [1] to be used without
requiring CPU PM domains. The cpuidle DT parsing code will first read
idle states from the "cpu-idle-states" property, then walk up the
power-domains tree (assuming that it is indeed a tree) for the CPU and
read idle states from the "power-states" property.
This is a temporary hack to enable a prototype EAS energy model in the
device tree, for which we want to use CPU power-domains to describe
energy costs in a topologically-aware way.
[1] https://patchwork.kernel.org/patch/9193651/
---
drivers/cpuidle/dt_idle_states.c | 85 ++++++++++++++++++++++++++++++++++++++--
1 file changed, 82 insertions(+), 3 deletions(-)
@@ -21,6 +21,8 @@
#include "dt_idle_states.h"
+#define MAX_PD_DEPTH 8
+
static int init_state_node(struct cpuidle_state *idle_state,
const struct of_device_id *matches,
struct device_node *state_node)
@@ -92,6 +94,84 @@ static int init_state_node(struct cpuidle_state *idle_state,
}
/*
+ * First look in cpu-idle-states, then walk up the power-domains tree, returning
+ * the idx'th idle state node encountered, or NULL if it can't be found.
+ *
+ * Returns the device_node pointer with refcount incremented. Use
+ * of_node_put() on it when done.
+ */
+static struct device_node *get_idle_state_node(struct device_node *cpu_node,
+ int idx)
+{
+ struct device_node *state_node, *consumer_node;
+ int num_cpu_idle_states, num_pd_idle_states;
+ int idx_pd;
+ unsigned int i;
+
+ state_node = of_parse_phandle(cpu_node, "cpu-idle-states", idx);
+ if (state_node)
+ return state_node;
+
+ num_cpu_idle_states = of_property_count_elems_of_size(cpu_node,
+ "cpu-idle-states",
+ 4);
+ if (num_cpu_idle_states < 1) {
+ pr_devel("%s: no cpu-idle-states for %s\n", __func__,
+ cpu_node->name);
+ num_cpu_idle_states = 0;
+ }
+
+ pr_devel("%s: Looking in %s's power domains tree for state %d\n",
+ __func__, cpu_node->full_name, idx);
+
+ BUG_ON(idx < num_cpu_idle_states);
+ idx_pd = idx - num_cpu_idle_states;
+
+ /* Use a loop counter so we don't infiniloop if there's a cycle in the
+ * power-domains graph. */
+ consumer_node = cpu_node;
+ for (i = 0; i < MAX_PD_DEPTH; i++) {
+ int num_pd_providers;
+ struct device_node *pd_node = of_parse_phandle(
+ consumer_node, "power-domains", 0);
+ if (!pd_node) {
+ return NULL;
+ }
+
+ /* We're assuming the power-domains graph is a tree, complain if
+ * not. */
+ num_pd_providers = of_property_count_elems_of_size(
+ consumer_node, "power-domains", 4);
+ BUG_ON(num_pd_providers < 1);
+ if (num_pd_providers > 1)
+ pr_warn("power-domains graph for %s not a tree "
+ "(%s has multiple power-domains)\n",
+ cpu_node->full_name, consumer_node->full_name);
+
+
+ state_node = of_parse_phandle(pd_node, "power-states", idx_pd);
+ if (state_node) {
+ of_node_put(pd_node);
+ return state_node;
+ }
+
+ num_pd_idle_states = of_property_count_elems_of_size(
+ pd_node, "power-states", 4);
+ if (num_pd_idle_states < 1)
+ num_pd_idle_states = 0;
+
+ BUG_ON(idx_pd < num_pd_idle_states);
+ idx_pd -= num_pd_idle_states;
+
+ of_node_put(pd_node);
+ consumer_node = pd_node;
+ }
+
+ WARN(1, "DT CPU power domain graph too deep. Probably has a cycle.");
+ return NULL;
+}
+
+/*
* Check that the idle state is uniform across all CPUs in the CPUidle driver
* cpumask
*/
@@ -112,8 +192,7 @@ static bool idle_state_valid(struct device_node *state_node, unsigned int idx,
for (cpu = cpumask_next(cpumask_first(cpumask), cpumask);
cpu < nr_cpu_ids; cpu = cpumask_next(cpu, cpumask)) {
cpu_node = of_cpu_device_node_get(cpu);
- curr_state_node = of_parse_phandle(cpu_node, "cpu-idle-states",
- idx);
+ curr_state_node = get_idle_state_node(cpu_node, idx);
if (state_node != curr_state_node)
valid = false;
@@ -170,7 +249,7 @@ int dt_init_idle_driver(struct cpuidle_driver *drv,
cpu_node = of_cpu_device_node_get(cpumask_first(cpumask));
for (i = 0; ; i++) {
- state_node = of_parse_phandle(cpu_node, "cpu-idle-states", i);
+ state_node = get_idle_state_node(cpu_node,i);
if (!state_node)
break;
--
1.9.1