]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - arch/powerpc/platforms/powernv/idle.c
Merge tag 'char-misc-4.13-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/gregk...
[karo-tx-linux.git] / arch / powerpc / platforms / powernv / idle.c
index 2abee070373fb3a8b757b8d3cb269e5d0b89dff6..a553aeea7af683812ba2f5a80d65e97cda163919 100644 (file)
@@ -56,6 +56,7 @@ u64 pnv_first_deep_stop_state = MAX_STOP_STATE;
  */
 static u64 pnv_deepest_stop_psscr_val;
 static u64 pnv_deepest_stop_psscr_mask;
+static u64 pnv_deepest_stop_flag;
 static bool deepest_stop_found;
 
 static int pnv_save_sprs_for_deep_states(void)
@@ -185,8 +186,40 @@ static void pnv_alloc_idle_core_states(void)
 
        update_subcore_sibling_mask();
 
-       if (supported_cpuidle_states & OPAL_PM_LOSE_FULL_CONTEXT)
-               pnv_save_sprs_for_deep_states();
+       if (supported_cpuidle_states & OPAL_PM_LOSE_FULL_CONTEXT) {
+               int rc = pnv_save_sprs_for_deep_states();
+
+               if (likely(!rc))
+                       return;
+
+               /*
+                * The stop-api is unable to restore hypervisor
+                * resources on wakeup from platform idle states which
+                * lose full context. So disable such states.
+                */
+               supported_cpuidle_states &= ~OPAL_PM_LOSE_FULL_CONTEXT;
+               pr_warn("cpuidle-powernv: Disabling idle states that lose full context\n");
+               pr_warn("cpuidle-powernv: Idle power-savings, CPU-Hotplug affected\n");
+
+               if (cpu_has_feature(CPU_FTR_ARCH_300) &&
+                   (pnv_deepest_stop_flag & OPAL_PM_LOSE_FULL_CONTEXT)) {
+                       /*
+                        * Use the default stop state for CPU-Hotplug
+                        * if available.
+                        */
+                       if (default_stop_found) {
+                               pnv_deepest_stop_psscr_val =
+                                       pnv_default_stop_val;
+                               pnv_deepest_stop_psscr_mask =
+                                       pnv_default_stop_mask;
+                               pr_warn("cpuidle-powernv: Offlined CPUs will stop with psscr = 0x%016llx\n",
+                                       pnv_deepest_stop_psscr_val);
+                       } else { /* Fallback to snooze loop for CPU-Hotplug */
+                               deepest_stop_found = false;
+                               pr_warn("cpuidle-powernv: Offlined CPUs will busy wait\n");
+                       }
+               }
+       }
 }
 
 u32 pnv_get_supported_cpuidle_states(void)
@@ -375,7 +408,8 @@ unsigned long pnv_cpu_offline(unsigned int cpu)
                                                pnv_deepest_stop_psscr_val;
                srr1 = power9_idle_stop(psscr);
 
-       } else if (idle_states & OPAL_PM_WINKLE_ENABLED) {
+       } else if ((idle_states & OPAL_PM_WINKLE_ENABLED) &&
+                  (idle_states & OPAL_PM_LOSE_FULL_CONTEXT)) {
                srr1 = power7_idle_insn(PNV_THREAD_WINKLE);
        } else if ((idle_states & OPAL_PM_SLEEP_ENABLED) ||
                   (idle_states & OPAL_PM_SLEEP_ENABLED_ER1)) {
@@ -553,6 +587,7 @@ static int __init pnv_power9_idle_init(struct device_node *np, u32 *flags,
                        max_residency_ns = residency_ns[i];
                        pnv_deepest_stop_psscr_val = psscr_val[i];
                        pnv_deepest_stop_psscr_mask = psscr_mask[i];
+                       pnv_deepest_stop_flag = flags[i];
                        deepest_stop_found = true;
                }