cpuidle/powernv: Parse device tree to setup idle states
authorPreeti U Murthy <preeti@linux.vnet.ibm.com>
Wed, 26 Feb 2014 00:09:20 +0000 (05:39 +0530)
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>
Wed, 5 Mar 2014 04:57:08 +0000 (15:57 +1100)
Add deep idle states such as nap and fast sleep to the cpuidle state table
only if they are discovered from the device tree during cpuidle initialization.

Signed-off-by: Preeti U Murthy <preeti@linux.vnet.ibm.com>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
drivers/cpuidle/cpuidle-powernv.c

index 4fb97cef82fde8b115ad3ad1c969186083bbfebd..fdae1c476e2755e4fd76cb102bdbab83c77fb2ff 100644 (file)
 #include <linux/cpu.h>
 #include <linux/notifier.h>
 #include <linux/clockchips.h>
+#include <linux/of.h>
 
 #include <asm/machdep.h>
 #include <asm/firmware.h>
 
+/* Flags and constants used in PowerNV platform */
+
+#define MAX_POWERNV_IDLE_STATES        8
+#define IDLE_USE_INST_NAP      0x00010000 /* Use nap instruction */
+#define IDLE_USE_INST_SLEEP    0x00020000 /* Use sleep instruction */
+
 struct cpuidle_driver powernv_idle_driver = {
        .name             = "powernv_idle",
        .owner            = THIS_MODULE,
@@ -79,7 +86,7 @@ static int fastsleep_loop(struct cpuidle_device *dev,
 /*
  * States for dedicated partition case.
  */
-static struct cpuidle_state powernv_states[] = {
+static struct cpuidle_state powernv_states[MAX_POWERNV_IDLE_STATES] = {
        { /* Snooze */
                .name = "snooze",
                .desc = "snooze",
@@ -87,20 +94,6 @@ static struct cpuidle_state powernv_states[] = {
                .exit_latency = 0,
                .target_residency = 0,
                .enter = &snooze_loop },
-       { /* NAP */
-               .name = "NAP",
-               .desc = "NAP",
-               .flags = CPUIDLE_FLAG_TIME_VALID,
-               .exit_latency = 10,
-               .target_residency = 100,
-               .enter = &nap_loop },
-        { /* Fastsleep */
-               .name = "fastsleep",
-               .desc = "fastsleep",
-               .flags = CPUIDLE_FLAG_TIME_VALID,
-               .exit_latency = 10,
-               .target_residency = 100,
-               .enter = &fastsleep_loop },
 };
 
 static int powernv_cpuidle_add_cpu_notifier(struct notifier_block *n,
@@ -161,19 +154,74 @@ static int powernv_cpuidle_driver_init(void)
        return 0;
 }
 
+static int powernv_add_idle_states(void)
+{
+       struct device_node *power_mgt;
+       struct property *prop;
+       int nr_idle_states = 1; /* Snooze */
+       int dt_idle_states;
+       u32 *flags;
+       int i;
+
+       /* Currently we have snooze statically defined */
+
+       power_mgt = of_find_node_by_path("/ibm,opal/power-mgt");
+       if (!power_mgt) {
+               pr_warn("opal: PowerMgmt Node not found\n");
+               return nr_idle_states;
+       }
+
+       prop = of_find_property(power_mgt, "ibm,cpu-idle-state-flags", NULL);
+       if (!prop) {
+               pr_warn("DT-PowerMgmt: missing ibm,cpu-idle-state-flags\n");
+               return nr_idle_states;
+       }
+
+       dt_idle_states = prop->length / sizeof(u32);
+       flags = (u32 *) prop->value;
+
+       for (i = 0; i < dt_idle_states; i++) {
+
+               if (flags[i] & IDLE_USE_INST_NAP) {
+                       /* Add NAP state */
+                       strcpy(powernv_states[nr_idle_states].name, "Nap");
+                       strcpy(powernv_states[nr_idle_states].desc, "Nap");
+                       powernv_states[nr_idle_states].flags = CPUIDLE_FLAG_TIME_VALID;
+                       powernv_states[nr_idle_states].exit_latency = 10;
+                       powernv_states[nr_idle_states].target_residency = 100;
+                       powernv_states[nr_idle_states].enter = &nap_loop;
+                       nr_idle_states++;
+               }
+
+               if (flags[i] & IDLE_USE_INST_SLEEP) {
+                       /* Add FASTSLEEP state */
+                       strcpy(powernv_states[nr_idle_states].name, "FastSleep");
+                       strcpy(powernv_states[nr_idle_states].desc, "FastSleep");
+                       powernv_states[nr_idle_states].flags =
+                               CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TIMER_STOP;
+                       powernv_states[nr_idle_states].exit_latency = 300;
+                       powernv_states[nr_idle_states].target_residency = 1000000;
+                       powernv_states[nr_idle_states].enter = &fastsleep_loop;
+                       nr_idle_states++;
+               }
+       }
+
+       return nr_idle_states;
+}
+
 /*
  * powernv_idle_probe()
  * Choose state table for shared versus dedicated partition
  */
 static int powernv_idle_probe(void)
 {
-
        if (cpuidle_disable != IDLE_NO_OVERRIDE)
                return -ENODEV;
 
        if (firmware_has_feature(FW_FEATURE_OPALv3)) {
                cpuidle_state_table = powernv_states;
-               max_idle_state = ARRAY_SIZE(powernv_states);
+               /* Device tree can indicate more idle states */
+               max_idle_state = powernv_add_idle_states();
        } else
                return -ENODEV;