|
@@ -20,6 +20,10 @@
|
|
|
#include <asm/opal.h>
|
|
|
#include <asm/runlatch.h>
|
|
|
|
|
|
+/*
|
|
|
+ * Expose only those Hardware idle states via the cpuidle framework
|
|
|
+ * that have latency value below POWERNV_THRESHOLD_LATENCY_NS.
|
|
|
+ */
|
|
|
#define POWERNV_THRESHOLD_LATENCY_NS 200000
|
|
|
|
|
|
static struct cpuidle_driver powernv_idle_driver = {
|
|
@@ -167,6 +171,24 @@ static int powernv_cpuidle_driver_init(void)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static inline void add_powernv_state(int index, const char *name,
|
|
|
+ unsigned int flags,
|
|
|
+ int (*idle_fn)(struct cpuidle_device *,
|
|
|
+ struct cpuidle_driver *,
|
|
|
+ int),
|
|
|
+ unsigned int target_residency,
|
|
|
+ unsigned int exit_latency,
|
|
|
+ u64 psscr_val)
|
|
|
+{
|
|
|
+ strlcpy(powernv_states[index].name, name, CPUIDLE_NAME_LEN);
|
|
|
+ strlcpy(powernv_states[index].desc, name, CPUIDLE_NAME_LEN);
|
|
|
+ powernv_states[index].flags = flags;
|
|
|
+ powernv_states[index].target_residency = target_residency;
|
|
|
+ powernv_states[index].exit_latency = exit_latency;
|
|
|
+ powernv_states[index].enter = idle_fn;
|
|
|
+ stop_psscr_table[index] = psscr_val;
|
|
|
+}
|
|
|
+
|
|
|
static int powernv_add_idle_states(void)
|
|
|
{
|
|
|
struct device_node *power_mgt;
|
|
@@ -236,6 +258,7 @@ static int powernv_add_idle_states(void)
|
|
|
"ibm,cpu-idle-state-residency-ns", residency_ns, dt_idle_states);
|
|
|
|
|
|
for (i = 0; i < dt_idle_states; i++) {
|
|
|
+ unsigned int exit_latency, target_residency;
|
|
|
/*
|
|
|
* If an idle state has exit latency beyond
|
|
|
* POWERNV_THRESHOLD_LATENCY_NS then don't use it
|
|
@@ -243,28 +266,33 @@ static int powernv_add_idle_states(void)
|
|
|
*/
|
|
|
if (latency_ns[i] > POWERNV_THRESHOLD_LATENCY_NS)
|
|
|
continue;
|
|
|
+ /*
|
|
|
+ * Firmware passes residency and latency values in ns.
|
|
|
+ * cpuidle expects it in us.
|
|
|
+ */
|
|
|
+ exit_latency = latency_ns[i] / 1000;
|
|
|
+ if (!rc)
|
|
|
+ target_residency = residency_ns[i] / 1000;
|
|
|
+ else
|
|
|
+ target_residency = 0;
|
|
|
|
|
|
/*
|
|
|
- * Cpuidle accepts exit_latency and target_residency in us.
|
|
|
- * Use default target_residency values if f/w does not expose it.
|
|
|
+ * For nap and fastsleep, use default target_residency
|
|
|
+ * values if f/w does not expose it.
|
|
|
*/
|
|
|
if (flags[i] & OPAL_PM_NAP_ENABLED) {
|
|
|
+ if (!rc)
|
|
|
+ target_residency = 100;
|
|
|
/* 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 = 0;
|
|
|
- powernv_states[nr_idle_states].target_residency = 100;
|
|
|
- powernv_states[nr_idle_states].enter = nap_loop;
|
|
|
+ add_powernv_state(nr_idle_states, "Nap",
|
|
|
+ CPUIDLE_FLAG_NONE, nap_loop,
|
|
|
+ target_residency, exit_latency, 0);
|
|
|
} else if ((flags[i] & OPAL_PM_STOP_INST_FAST) &&
|
|
|
!(flags[i] & OPAL_PM_TIMEBASE_STOP)) {
|
|
|
- strncpy(powernv_states[nr_idle_states].name,
|
|
|
- names[i], CPUIDLE_NAME_LEN);
|
|
|
- strncpy(powernv_states[nr_idle_states].desc,
|
|
|
- names[i], CPUIDLE_NAME_LEN);
|
|
|
- powernv_states[nr_idle_states].flags = 0;
|
|
|
-
|
|
|
- powernv_states[nr_idle_states].enter = stop_loop;
|
|
|
- stop_psscr_table[nr_idle_states] = psscr_val[i];
|
|
|
+ add_powernv_state(nr_idle_states, names[i],
|
|
|
+ CPUIDLE_FLAG_NONE, stop_loop,
|
|
|
+ target_residency, exit_latency,
|
|
|
+ psscr_val[i]);
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -274,32 +302,21 @@ static int powernv_add_idle_states(void)
|
|
|
#ifdef CONFIG_TICK_ONESHOT
|
|
|
if (flags[i] & OPAL_PM_SLEEP_ENABLED ||
|
|
|
flags[i] & OPAL_PM_SLEEP_ENABLED_ER1) {
|
|
|
+ if (!rc)
|
|
|
+ target_residency = 300000;
|
|
|
/* 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_TIMER_STOP;
|
|
|
- powernv_states[nr_idle_states].target_residency = 300000;
|
|
|
- powernv_states[nr_idle_states].enter = fastsleep_loop;
|
|
|
+ add_powernv_state(nr_idle_states, "FastSleep",
|
|
|
+ CPUIDLE_FLAG_TIMER_STOP,
|
|
|
+ fastsleep_loop,
|
|
|
+ target_residency, exit_latency, 0);
|
|
|
} else if ((flags[i] & OPAL_PM_STOP_INST_DEEP) &&
|
|
|
(flags[i] & OPAL_PM_TIMEBASE_STOP)) {
|
|
|
- strncpy(powernv_states[nr_idle_states].name,
|
|
|
- names[i], CPUIDLE_NAME_LEN);
|
|
|
- strncpy(powernv_states[nr_idle_states].desc,
|
|
|
- names[i], CPUIDLE_NAME_LEN);
|
|
|
-
|
|
|
- powernv_states[nr_idle_states].flags = CPUIDLE_FLAG_TIMER_STOP;
|
|
|
- powernv_states[nr_idle_states].enter = stop_loop;
|
|
|
- stop_psscr_table[nr_idle_states] = psscr_val[i];
|
|
|
+ add_powernv_state(nr_idle_states, names[i],
|
|
|
+ CPUIDLE_FLAG_TIMER_STOP, stop_loop,
|
|
|
+ target_residency, exit_latency,
|
|
|
+ psscr_val[i]);
|
|
|
}
|
|
|
#endif
|
|
|
- powernv_states[nr_idle_states].exit_latency =
|
|
|
- ((unsigned int)latency_ns[i]) / 1000;
|
|
|
-
|
|
|
- if (!rc) {
|
|
|
- powernv_states[nr_idle_states].target_residency =
|
|
|
- ((unsigned int)residency_ns[i]) / 1000;
|
|
|
- }
|
|
|
-
|
|
|
nr_idle_states++;
|
|
|
}
|
|
|
out:
|