diff options
author | Preeti U Murthy <preeti@linux.vnet.ibm.com> | 2014-02-26 05:39:20 +0530 |
---|---|---|
committer | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2014-03-05 15:57:08 +1100 |
commit | 0888839c5b62c44a55ac9d28acc273ba663c65ea (patch) | |
tree | 9365dde8014ff622dee120e8f080320f607753d6 | |
parent | 0d94873011a10cea78d1bc0ba8cfc4203559d534 (diff) |
cpuidle/powernv: Parse device tree to setup idle states
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>
-rw-r--r-- | drivers/cpuidle/cpuidle-powernv.c | 82 |
1 files changed, 65 insertions, 17 deletions
diff --git a/drivers/cpuidle/cpuidle-powernv.c b/drivers/cpuidle/cpuidle-powernv.c index 4fb97cef82f..fdae1c476e2 100644 --- a/drivers/cpuidle/cpuidle-powernv.c +++ b/drivers/cpuidle/cpuidle-powernv.c @@ -12,10 +12,17 @@ #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; |