|
@@ -13,6 +13,7 @@
|
|
|
#include <linux/notifier.h>
|
|
|
#include <linux/clockchips.h>
|
|
|
#include <linux/of.h>
|
|
|
+#include <linux/slab.h>
|
|
|
|
|
|
#include <asm/machdep.h>
|
|
|
#include <asm/firmware.h>
|
|
@@ -159,69 +160,79 @@ static int powernv_add_idle_states(void)
|
|
|
int nr_idle_states = 1; /* Snooze */
|
|
|
int dt_idle_states;
|
|
|
const __be32 *idle_state_flags;
|
|
|
- const __be32 *idle_state_latency;
|
|
|
- u32 len_flags, flags, latency_ns;
|
|
|
- int i;
|
|
|
+ u32 len_flags, flags;
|
|
|
+ u32 *latency_ns, *residency_ns;
|
|
|
+ int i, rc;
|
|
|
|
|
|
/* 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;
|
|
|
+ goto out;
|
|
|
}
|
|
|
|
|
|
- idle_state_flags = of_get_property(power_mgt, "ibm,cpu-idle-state-flags", &len_flags);
|
|
|
+ idle_state_flags = of_get_property(power_mgt,
|
|
|
+ "ibm,cpu-idle-state-flags", &len_flags);
|
|
|
if (!idle_state_flags) {
|
|
|
- pr_warn("DT-PowerMgmt: missing ibm,cpu-idle-state-flags\n");
|
|
|
- return nr_idle_states;
|
|
|
+ pr_warn("cpuidle-powernv: missing ibm,cpu-idle-state-flags in DT\n");
|
|
|
+ goto out;
|
|
|
}
|
|
|
|
|
|
- idle_state_latency = of_get_property(power_mgt,
|
|
|
- "ibm,cpu-idle-state-latencies-ns", NULL);
|
|
|
- if (!idle_state_latency) {
|
|
|
- pr_warn("DT-PowerMgmt: missing ibm,cpu-idle-state-latencies-ns\n");
|
|
|
- return nr_idle_states;
|
|
|
+ dt_idle_states = len_flags / sizeof(u32);
|
|
|
+
|
|
|
+ latency_ns = kzalloc(sizeof(*latency_ns) * dt_idle_states, GFP_KERNEL);
|
|
|
+ rc = of_property_read_u32_array(power_mgt,
|
|
|
+ "ibm,cpu-idle-state-latencies-ns", latency_ns, dt_idle_states);
|
|
|
+ if (rc) {
|
|
|
+ pr_warn("cpuidle-powernv: missing ibm,cpu-idle-state-latencies-ns in DT\n");
|
|
|
+ goto out_free_latency;
|
|
|
}
|
|
|
|
|
|
- dt_idle_states = len_flags / sizeof(u32);
|
|
|
+ residency_ns = kzalloc(sizeof(*residency_ns) * dt_idle_states, GFP_KERNEL);
|
|
|
+ rc = of_property_read_u32_array(power_mgt,
|
|
|
+ "ibm,cpu-idle-state-residency-ns", residency_ns, dt_idle_states);
|
|
|
|
|
|
for (i = 0; i < dt_idle_states; i++) {
|
|
|
|
|
|
flags = be32_to_cpu(idle_state_flags[i]);
|
|
|
|
|
|
- /* Cpuidle accepts exit_latency in us and we estimate
|
|
|
- * target residency to be 10x exit_latency
|
|
|
+ /*
|
|
|
+ * Cpuidle accepts exit_latency and target_residency in us.
|
|
|
+ * Use default target_residency values if f/w does not expose it.
|
|
|
*/
|
|
|
- latency_ns = be32_to_cpu(idle_state_latency[i]);
|
|
|
if (flags & OPAL_PM_NAP_ENABLED) {
|
|
|
/* 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].exit_latency =
|
|
|
- ((unsigned int)latency_ns) / 1000;
|
|
|
- powernv_states[nr_idle_states].target_residency =
|
|
|
- ((unsigned int)latency_ns / 100);
|
|
|
+ powernv_states[nr_idle_states].target_residency = 100;
|
|
|
powernv_states[nr_idle_states].enter = &nap_loop;
|
|
|
- nr_idle_states++;
|
|
|
- }
|
|
|
-
|
|
|
- if (flags & OPAL_PM_SLEEP_ENABLED ||
|
|
|
+ } else if (flags & OPAL_PM_SLEEP_ENABLED ||
|
|
|
flags & OPAL_PM_SLEEP_ENABLED_ER1) {
|
|
|
/* 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].exit_latency =
|
|
|
- ((unsigned int)latency_ns) / 1000;
|
|
|
- powernv_states[nr_idle_states].target_residency =
|
|
|
- ((unsigned int)latency_ns / 100);
|
|
|
+ powernv_states[nr_idle_states].target_residency = 300000;
|
|
|
powernv_states[nr_idle_states].enter = &fastsleep_loop;
|
|
|
- nr_idle_states++;
|
|
|
}
|
|
|
+
|
|
|
+ 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++;
|
|
|
}
|
|
|
|
|
|
+ kfree(residency_ns);
|
|
|
+out_free_latency:
|
|
|
+ kfree(latency_ns);
|
|
|
+out:
|
|
|
return nr_idle_states;
|
|
|
}
|
|
|
|