b7a3a7f
From a21211672c9a1d730a39aa65d4a5b3414700adfb Mon Sep 17 00:00:00 2001
b7a3a7f
From: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
b7a3a7f
Date: Wed, 23 Mar 2016 21:07:39 -0700
b7a3a7f
Subject: [PATCH] ACPI / processor: Request native thermal interrupt handling
b7a3a7f
 via _OSC
b7a3a7f
b7a3a7f
There are several reports of freeze on enabling HWP (Hardware PStates)
b7a3a7f
feature on Skylake-based systems by the Intel P-states driver. The root
b7a3a7f
cause is identified as the HWP interrupts causing BIOS code to freeze.
b7a3a7f
b7a3a7f
HWP interrupts use the thermal LVT which can be handled by Linux
b7a3a7f
natively, but on the affected Skylake-based systems SMM will respond
b7a3a7f
to it by default.  This is a problem for several reasons:
b7a3a7f
 - On the affected systems the SMM thermal LVT handler is broken (it
b7a3a7f
   will crash when invoked) and a BIOS update is necessary to fix it.
b7a3a7f
 - With thermal interrupt handled in SMM we lose all of the reporting
b7a3a7f
   features of the arch/x86/kernel/cpu/mcheck/therm_throt driver.
b7a3a7f
 - Some thermal drivers like x86-package-temp depend on the thermal
b7a3a7f
   threshold interrupts signaled via the thermal LVT.
b7a3a7f
 - The HWP interrupts are useful for debugging and tuning
b7a3a7f
   performance (if the kernel can handle them).
b7a3a7f
The native handling of thermal interrupts needs to be enabled
b7a3a7f
because of that.
b7a3a7f
b7a3a7f
This requires some way to tell SMM that the OS can handle thermal
b7a3a7f
interrupts.  That can be done by using _OSC/_PDC in processor
b7a3a7f
scope very early during ACPI initialization.
b7a3a7f
b7a3a7f
The meaning of _OSC/_PDC bit 12 in processor scope is whether or
b7a3a7f
not the OS supports native handling of interrupts for Collaborative
b7a3a7f
Processor Performance Control (CPPC) notifications.  Since on
b7a3a7f
HWP-capable systems CPPC is a firmware interface to HWP, setting
b7a3a7f
this bit effectively tells the firmware that the OS will handle
b7a3a7f
thermal interrupts natively going forward.
b7a3a7f
b7a3a7f
For details on _OSC/_PDC refer to:
b7a3a7f
http://www.intel.com/content/www/us/en/standards/processor-vendor-specific-acpi-specification.html
b7a3a7f
b7a3a7f
To implement the _OSC/_PDC handshake as described, introduce a new
b7a3a7f
function, acpi_early_processor_osc(), that walks the ACPI
b7a3a7f
namespace looking for ACPI processor objects and invokes _OSC for
b7a3a7f
them with bit 12 in the capabilities buffer set and terminates the
b7a3a7f
namespace walk on the first success.
b7a3a7f
b7a3a7f
Also modify intel_thermal_interrupt() to clear HWP status bits in
b7a3a7f
the HWP_STATUS MSR to acknowledge HWP interrupts (which prevents
b7a3a7f
them from firing continuously).
b7a3a7f
b7a3a7f
Signed-off-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
b7a3a7f
[ rjw: Subject & changelog, function rename ]
b7a3a7f
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
b7a3a7f
---
b7a3a7f
 arch/x86/kernel/cpu/mcheck/therm_throt.c |  3 ++
b7a3a7f
 drivers/acpi/acpi_processor.c            | 52 ++++++++++++++++++++++++++++++++
b7a3a7f
 drivers/acpi/bus.c                       |  3 ++
b7a3a7f
 drivers/acpi/internal.h                  |  6 ++++
b7a3a7f
 4 files changed, 64 insertions(+)
b7a3a7f
b7a3a7f
diff --git a/arch/x86/kernel/cpu/mcheck/therm_throt.c b/arch/x86/kernel/cpu/mcheck/therm_throt.c
b7a3a7f
index 2c5aaf8..0553858 100644
b7a3a7f
--- a/arch/x86/kernel/cpu/mcheck/therm_throt.c
b7a3a7f
+++ b/arch/x86/kernel/cpu/mcheck/therm_throt.c
b7a3a7f
@@ -385,6 +385,9 @@ static void intel_thermal_interrupt(void)
b7a3a7f
 {
b7a3a7f
 	__u64 msr_val;
b7a3a7f
 
b7a3a7f
+	if (static_cpu_has(X86_FEATURE_HWP))
b7a3a7f
+		wrmsrl_safe(MSR_HWP_STATUS, 0);
b7a3a7f
+
b7a3a7f
 	rdmsrl(MSR_IA32_THERM_STATUS, msr_val);
b7a3a7f
 
b7a3a7f
 	/* Check for violation of core thermal thresholds*/
b7a3a7f
diff --git a/drivers/acpi/acpi_processor.c b/drivers/acpi/acpi_processor.c
b7a3a7f
index b5e54f2..0d92d0f 100644
b7a3a7f
--- a/drivers/acpi/acpi_processor.c
b7a3a7f
+++ b/drivers/acpi/acpi_processor.c
b7a3a7f
@@ -491,6 +491,58 @@ static void acpi_processor_remove(struct acpi_device *device)
b7a3a7f
 }
b7a3a7f
 #endif /* CONFIG_ACPI_HOTPLUG_CPU */
b7a3a7f
 
b7a3a7f
+#ifdef CONFIG_X86
b7a3a7f
+static bool acpi_hwp_native_thermal_lvt_set;
b7a3a7f
+static acpi_status __init acpi_hwp_native_thermal_lvt_osc(acpi_handle handle,
b7a3a7f
+							  u32 lvl,
b7a3a7f
+							  void *context,
b7a3a7f
+							  void **rv)
b7a3a7f
+{
b7a3a7f
+	u8 sb_uuid_str[] = "4077A616-290C-47BE-9EBD-D87058713953";
b7a3a7f
+	u32 capbuf[2];
b7a3a7f
+	struct acpi_osc_context osc_context = {
b7a3a7f
+		.uuid_str = sb_uuid_str,
b7a3a7f
+		.rev = 1,
b7a3a7f
+		.cap.length = 8,
b7a3a7f
+		.cap.pointer = capbuf,
b7a3a7f
+	};
b7a3a7f
+
b7a3a7f
+	if (acpi_hwp_native_thermal_lvt_set)
b7a3a7f
+		return AE_CTRL_TERMINATE;
b7a3a7f
+
b7a3a7f
+	capbuf[0] = 0x0000;
b7a3a7f
+	capbuf[1] = 0x1000; /* set bit 12 */
b7a3a7f
+
b7a3a7f
+	if (ACPI_SUCCESS(acpi_run_osc(handle, &osc_context))) {
b7a3a7f
+		if (osc_context.ret.pointer && osc_context.ret.length > 1) {
b7a3a7f
+			u32 *capbuf_ret = osc_context.ret.pointer;
b7a3a7f
+
b7a3a7f
+			if (capbuf_ret[1] & 0x1000) {
b7a3a7f
+				acpi_handle_info(handle,
b7a3a7f
+					"_OSC native thermal LVT Acked\n");
b7a3a7f
+				acpi_hwp_native_thermal_lvt_set = true;
b7a3a7f
+			}
b7a3a7f
+		}
b7a3a7f
+		kfree(osc_context.ret.pointer);
b7a3a7f
+	}
b7a3a7f
+
b7a3a7f
+	return AE_OK;
b7a3a7f
+}
b7a3a7f
+
b7a3a7f
+void __init acpi_early_processor_osc(void)
b7a3a7f
+{
b7a3a7f
+	if (boot_cpu_has(X86_FEATURE_HWP)) {
b7a3a7f
+		acpi_walk_namespace(ACPI_TYPE_PROCESSOR, ACPI_ROOT_OBJECT,
b7a3a7f
+				    ACPI_UINT32_MAX,
b7a3a7f
+				    acpi_hwp_native_thermal_lvt_osc,
b7a3a7f
+				    NULL, NULL, NULL);
b7a3a7f
+		acpi_get_devices(ACPI_PROCESSOR_DEVICE_HID,
b7a3a7f
+				 acpi_hwp_native_thermal_lvt_osc,
b7a3a7f
+				 NULL, NULL);
b7a3a7f
+	}
b7a3a7f
+}
b7a3a7f
+#endif
b7a3a7f
+
b7a3a7f
 /*
b7a3a7f
  * The following ACPI IDs are known to be suitable for representing as
b7a3a7f
  * processor devices.
b7a3a7f
diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c
b7a3a7f
index 891c42d..f9081b7 100644
b7a3a7f
--- a/drivers/acpi/bus.c
b7a3a7f
+++ b/drivers/acpi/bus.c
b7a3a7f
@@ -1005,6 +1005,9 @@ static int __init acpi_bus_init(void)
b7a3a7f
 		goto error1;
b7a3a7f
 	}
b7a3a7f
 
b7a3a7f
+	/* Set capability bits for _OSC under processor scope */
b7a3a7f
+	acpi_early_processor_osc();
b7a3a7f
+
b7a3a7f
 	/*
b7a3a7f
 	 * _OSC method may exist in module level code,
b7a3a7f
 	 * so it must be run after ACPI_FULL_INITIALIZATION
b7a3a7f
diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h
b7a3a7f
index 1e6833a..6f41c73 100644
b7a3a7f
--- a/drivers/acpi/internal.h
b7a3a7f
+++ b/drivers/acpi/internal.h
b7a3a7f
@@ -138,6 +138,12 @@ void acpi_early_processor_set_pdc(void);
b7a3a7f
 static inline void acpi_early_processor_set_pdc(void) {}
b7a3a7f
 #endif
b7a3a7f
 
b7a3a7f
+#ifdef CONFIG_X86
b7a3a7f
+void acpi_early_processor_osc(void);
b7a3a7f
+#else
b7a3a7f
+static inline void acpi_early_processor_osc(void) {}
b7a3a7f
+#endif
b7a3a7f
+
b7a3a7f
 /* --------------------------------------------------------------------------
b7a3a7f
                                   Embedded Controller
b7a3a7f
    -------------------------------------------------------------------------- */
b7a3a7f
-- 
b7a3a7f
2.5.5
b7a3a7f