[PATCH v1] intel_idle: Avoid using deep idle states during initialization

"Rafael J. Wysocki" <[email protected]>
Newsgroups gmane.linux.kernel,gmane.linux.power-management.general
Organization Linux Kernel Development - Intel
Message-ID <[email protected]>
From: Rafael J. Wysocki <[email protected]>

Commit c0f691388992 ("intel_idle: Use subsys_initcall_sync() for
initialization") effectively made intel_idle initialize earlier which
turns out to interfere with USB EHCI probing on some platforms [1].
Investigation led to the conclusion that this was related to allowing
package idle states to be used earlier than before.

Work around that issue by making intel_idle set a CPU latency QoS
request to prevent package idle states from being used on all platforms
supported by it for the duration of the device_initcall() initialization
phase.

Fixes: c0f691388992 ("intel_idle: Use subsys_initcall_sync() for initialization")
Reported-by: Julian Silver <[email protected]>
Closes: https://lore.kernel.org/linux-acpi/[email protected]/
Signed-off-by: Rafael J. Wysocki <[email protected]>
---
 drivers/idle/intel_idle.c |   21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)

--- a/drivers/idle/intel_idle.c
+++ b/drivers/idle/intel_idle.c
@@ -53,6 +53,7 @@
 #include <linux/notifier.h>
 #include <linux/cpu.h>
 #include <linux/moduleparam.h>
+#include <linux/pm_qos.h>
 #include <linux/sysfs.h>
 #include <asm/cpuid/api.h>
 #include <asm/cpu_device_id.h>
@@ -2822,6 +2823,9 @@ error:
 	pr_info("Failed to adjust C-states with data from 'intel_idle.table'\n");
 }
 
+#define INTEL_IDLE_INIT_QOS	20
+static struct pm_qos_request qos_req __initdata;
+
 static int __init intel_idle_init(void)
 {
 	const struct x86_cpu_id *id;
@@ -2891,6 +2895,13 @@ static int __init intel_idle_init(void)
 	if (retval)
 		pr_warn("failed to initialized sysfs");
 
+	/*
+	 * Some platforms, in particular the Intel S1200BTL motherboard, have a
+	 * problem with using package idle states too early, so prevent that
+	 * from taking place until the device_initcall() phase is over.
+	 */
+	cpu_latency_qos_add_request(&qos_req, INTEL_IDLE_INIT_QOS);
+
 	retval = cpuidle_register_driver(&intel_idle_driver);
 	if (retval) {
 		struct cpuidle_driver *drv = cpuidle_get_driver();
@@ -2915,6 +2926,9 @@ hp_setup_fail:
 	intel_idle_cpuidle_devices_uninit();
 	cpuidle_unregister_driver(&intel_idle_driver);
 init_driver_fail:
+	if (cpu_latency_qos_request_active((&qos_req)))
+		cpu_latency_qos_remove_request(&qos_req);
+
 	intel_idle_sysfs_uninit();
 	free_percpu(intel_idle_cpuidle_devices);
 	return retval;
@@ -2922,6 +2934,15 @@ init_driver_fail:
 }
 subsys_initcall_sync(intel_idle_init);
 
+static int __init intel_idle_init_complete(void)
+{
+	if (cpu_latency_qos_request_active((&qos_req)))
+		cpu_latency_qos_remove_request(&qos_req);
+
+	return 0;
+}
+device_initcall_sync(intel_idle_init_complete);
+
 /*
  * We are not really modular, but we used to support that.  Meaning we also
  * support "intel_idle.max_cstate=..." at boot and also a read-only export of
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.