Commit 0888839c authored by Preeti U Murthy's avatar Preeti U Murthy Committed by Benjamin Herrenschmidt

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: default avatarPreeti U Murthy <preeti@linux.vnet.ibm.com>
Signed-off-by: default avatarBenjamin Herrenschmidt <benh@kernel.crashing.org>
parent 0d948730
...@@ -12,10 +12,17 @@ ...@@ -12,10 +12,17 @@
#include <linux/cpu.h> #include <linux/cpu.h>
#include <linux/notifier.h> #include <linux/notifier.h>
#include <linux/clockchips.h> #include <linux/clockchips.h>
#include <linux/of.h>
#include <asm/machdep.h> #include <asm/machdep.h>
#include <asm/firmware.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 = { struct cpuidle_driver powernv_idle_driver = {
.name = "powernv_idle", .name = "powernv_idle",
.owner = THIS_MODULE, .owner = THIS_MODULE,
...@@ -79,7 +86,7 @@ static int fastsleep_loop(struct cpuidle_device *dev, ...@@ -79,7 +86,7 @@ static int fastsleep_loop(struct cpuidle_device *dev,
/* /*
* States for dedicated partition case. * States for dedicated partition case.
*/ */
static struct cpuidle_state powernv_states[] = { static struct cpuidle_state powernv_states[MAX_POWERNV_IDLE_STATES] = {
{ /* Snooze */ { /* Snooze */
.name = "snooze", .name = "snooze",
.desc = "snooze", .desc = "snooze",
...@@ -87,20 +94,6 @@ static struct cpuidle_state powernv_states[] = { ...@@ -87,20 +94,6 @@ static struct cpuidle_state powernv_states[] = {
.exit_latency = 0, .exit_latency = 0,
.target_residency = 0, .target_residency = 0,
.enter = &snooze_loop }, .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, static int powernv_cpuidle_add_cpu_notifier(struct notifier_block *n,
...@@ -161,19 +154,74 @@ static int powernv_cpuidle_driver_init(void) ...@@ -161,19 +154,74 @@ static int powernv_cpuidle_driver_init(void)
return 0; 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() * powernv_idle_probe()
* Choose state table for shared versus dedicated partition * Choose state table for shared versus dedicated partition
*/ */
static int powernv_idle_probe(void) static int powernv_idle_probe(void)
{ {
if (cpuidle_disable != IDLE_NO_OVERRIDE) if (cpuidle_disable != IDLE_NO_OVERRIDE)
return -ENODEV; return -ENODEV;
if (firmware_has_feature(FW_FEATURE_OPALv3)) { if (firmware_has_feature(FW_FEATURE_OPALv3)) {
cpuidle_state_table = powernv_states; 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 } else
return -ENODEV; return -ENODEV;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment