Revert "Merge remote-tracking branch 'linux-2.6.32.y/master' into develop"
[firefly-linux-kernel-4.4.55.git] / arch / x86 / kernel / process.c
index fc6c84db0601bf87e9684e9bdf59279093d37dde..5fd5b07bf3a5774cd996e312d9fe55533a37b984 100644 (file)
@@ -438,6 +438,42 @@ static int __cpuinit mwait_usable(const struct cpuinfo_x86 *c)
        return (edx & MWAIT_EDX_C1);
 }
 
+/*
+ * Check for AMD CPUs, where APIC timer interrupt does not wake up CPU from C1e.
+ * For more information see
+ * - Erratum #400 for NPT family 0xf and family 0x10 CPUs
+ * - Erratum #365 for family 0x11 (not affected because C1e not in use)
+ */
+static int __cpuinit check_c1e_idle(const struct cpuinfo_x86 *c)
+{
+       u64 val;
+       if (c->x86_vendor != X86_VENDOR_AMD)
+               goto no_c1e_idle;
+
+       /* Family 0x0f models < rev F do not have C1E */
+       if (c->x86 == 0x0F && c->x86_model >= 0x40)
+               return 1;
+
+       if (c->x86 == 0x10) {
+               /*
+                * check OSVW bit for CPUs that are not affected
+                * by erratum #400
+                */
+               if (cpu_has(c, X86_FEATURE_OSVW)) {
+                       rdmsrl(MSR_AMD64_OSVW_ID_LENGTH, val);
+                       if (val >= 2) {
+                               rdmsrl(MSR_AMD64_OSVW_STATUS, val);
+                               if (!(val & BIT(1)))
+                                       goto no_c1e_idle;
+                       }
+               }
+               return 1;
+       }
+
+no_c1e_idle:
+       return 0;
+}
+
 static cpumask_var_t c1e_mask;
 static int c1e_detected;
 
@@ -515,8 +551,7 @@ void __cpuinit select_idle_routine(const struct cpuinfo_x86 *c)
                 */
                printk(KERN_INFO "using mwait in idle threads.\n");
                pm_idle = mwait_idle;
-       } else if (cpu_has_amd_erratum(amd_erratum_400)) {
-               /* E400: APIC timer interrupt does not wake up CPU from C1e */
+       } else if (check_c1e_idle(c)) {
                printk(KERN_INFO "using C1E aware idle routine\n");
                pm_idle = c1e_idle;
        } else