|
@@ -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;
|
|
|
|