@@ -772,6 +772,10 @@ extern struct kobject *opal_kobj;
/* /ibm,opal */
extern struct device_node *opal_node;
+/* Flags used for idle state discovery from the device tree */
+#define IDLE_INST_NAP 0x00010000 /* nap instruction can be used */
+#define IDLE_INST_SLEEP 0x00020000 /* sleep instruction can be used */
+
/* API functions */
int64_t opal_invalid_call(void);
int64_t opal_console_write(int64_t term_number, __be64 *length,
@@ -23,6 +23,13 @@ static inline int pnv_pci_dma_set_mask(struct pci_dev *pdev, u64 dma_mask)
}
#endif
+/* Flags to indicate which of the CPU idle states are available for use */
+
+#define IDLE_USE_NAP (1UL << 0)
+#define IDLE_USE_SLEEP (1UL << 1)
+
+extern unsigned int pnv_get_supported_cpuidle_states(void);
+
extern void pnv_lpc_init(void);
bool cpu_core_split_required(void);
@@ -282,6 +282,57 @@ static void __init pnv_setup_machdep_rtas(void)
}
#endif /* CONFIG_PPC_POWERNV_RTAS */
+static unsigned int supported_cpuidle_states;
+
+unsigned int pnv_get_supported_cpuidle_states(void)
+{
+ return supported_cpuidle_states;
+}
+
+static int __init pnv_probe_idle_states(void)
+{
+ struct device_node *power_mgt;
+ struct property *prop;
+ int dt_idle_states;
+ u32 *flags;
+ int i;
+
+ supported_cpuidle_states = 0;
+
+ if (cpuidle_disable != IDLE_NO_OVERRIDE)
+ return 0;
+
+ if (!firmware_has_feature(FW_FEATURE_OPALv3))
+ return 0;
+
+ power_mgt = of_find_node_by_path("/ibm,opal/power-mgt");
+ if (!power_mgt) {
+ pr_warn("opal: PowerMgmt Node not found\n");
+ return 0;
+ }
+
+ 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 0;
+ }
+
+ dt_idle_states = prop->length / sizeof(u32);
+ flags = (u32 *) prop->value;
+
+ for (i = 0; i < dt_idle_states; i++) {
+ if (flags[i] & IDLE_INST_NAP)
+ supported_cpuidle_states |= IDLE_USE_NAP;
+
+ if (flags[i] & IDLE_INST_SLEEP)
+ supported_cpuidle_states |= IDLE_USE_SLEEP;
+ }
+
+ return 0;
+}
+
+subsys_initcall(pnv_probe_idle_states);
+
static int __init pnv_probe(void)
{
unsigned long root = of_get_flat_dt_root();
@@ -149,6 +149,7 @@ static int pnv_smp_cpu_disable(void)
static void pnv_smp_cpu_kill_self(void)
{
unsigned int cpu;
+ unsigned long idle_states;
/* Standard hot unplug procedure */
local_irq_disable();
@@ -159,13 +160,21 @@ static void pnv_smp_cpu_kill_self(void)
generic_set_cpu_dead(cpu);
smp_wmb();
+ idle_states = pnv_get_supported_cpuidle_states();
+
/* We don't want to take decrementer interrupts while we are offline,
* so clear LPCR:PECE1. We keep PECE2 enabled.
*/
mtspr(SPRN_LPCR, mfspr(SPRN_LPCR) & ~(u64)LPCR_PECE1);
while (!generic_check_cpu_restart(cpu)) {
ppc64_runlatch_off();
- power7_nap(1);
+
+ /* If sleep is supported, go to sleep, instead of nap */
+ if (idle_states & IDLE_USE_SLEEP)
+ power7_sleep();
+ else
+ power7_nap(1);
+
ppc64_runlatch_on();
/* Reenable IRQs briefly to clear the IPI that woke us */
@@ -16,13 +16,12 @@
#include <asm/machdep.h>
#include <asm/firmware.h>
+#include <asm/opal.h>
#include <asm/runlatch.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",
@@ -185,7 +184,7 @@ static int powernv_add_idle_states(void)
for (i = 0; i < dt_idle_states; i++) {
flags = be32_to_cpu(idle_state_flags[i]);
- if (flags & IDLE_USE_INST_NAP) {
+ if (flags & IDLE_INST_NAP) {
/* Add NAP state */
strcpy(powernv_states[nr_idle_states].name, "Nap");
strcpy(powernv_states[nr_idle_states].desc, "Nap");
@@ -196,7 +195,7 @@ static int powernv_add_idle_states(void)
nr_idle_states++;
}
- if (flags & IDLE_USE_INST_SLEEP) {
+ if (flags & IDLE_INST_SLEEP) {
/* Add FASTSLEEP state */
strcpy(powernv_states[nr_idle_states].name, "FastSleep");
strcpy(powernv_states[nr_idle_states].desc, "FastSleep");