cpufreq: intel_pstate: Request P-states control from SMM if needed
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>
Thu, 17 Nov 2016 21:47:47 +0000 (22:47 +0100)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Thu, 17 Nov 2016 21:47:47 +0000 (22:47 +0100)
Currently, intel_pstate is unable to control P-states on my
IvyBridge-based Acer Aspire S5, because they are controlled by SMM
on that machine by default and it is necessary to request OS control
of P-states from it via the SMI Command register exposed in the ACPI
FADT.  intel_pstate doesn't do that now, but acpi-cpufreq and other
cpufreq drivers for x86 platforms do.

Address this problem by making intel_pstate use the ACPI-defined
mechanism as well.  However, intel_pstate is not modular and it
doesn't need the module refcount tricks played by
acpi_processor_notify_smm(), so export the core of this function
to it as acpi_processor_pstate_control() and make it call that.
[The changes in processor_perflib.c related to this should not
make any functional difference for the acpi_processor_notify_smm()
users].

To be safe, only call acpi_processor_notify_smm() from intel_pstate
if ACPI _PPC support is enabled in it.

Suggested-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Acked-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
drivers/acpi/processor_perflib.c
drivers/cpufreq/intel_pstate.c
include/acpi/processor.h

index bb01dea39fdccfd50e0b61713b5ba983bb62285b..9c67faa034b5d3490312b53105ae9be39d5fe96b 100644 (file)
@@ -465,11 +465,33 @@ int acpi_processor_get_performance_info(struct acpi_processor *pr)
        return result;
 }
 EXPORT_SYMBOL_GPL(acpi_processor_get_performance_info);
-int acpi_processor_notify_smm(struct module *calling_module)
+
+int acpi_processor_pstate_control(void)
 {
        acpi_status status;
-       static int is_done = 0;
 
+       if (!acpi_gbl_FADT.smi_command || !acpi_gbl_FADT.pstate_control)
+               return 0;
+
+       ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+                         "Writing pstate_control [0x%x] to smi_command [0x%x]\n",
+                         acpi_gbl_FADT.pstate_control, acpi_gbl_FADT.smi_command));
+
+       status = acpi_os_write_port(acpi_gbl_FADT.smi_command,
+                                   (u32)acpi_gbl_FADT.pstate_control, 8);
+       if (ACPI_SUCCESS(status))
+               return 1;
+
+       ACPI_EXCEPTION((AE_INFO, status,
+                       "Failed to write pstate_control [0x%x] to smi_command [0x%x]",
+                       acpi_gbl_FADT.pstate_control, acpi_gbl_FADT.smi_command));
+       return -EIO;
+}
+
+int acpi_processor_notify_smm(struct module *calling_module)
+{
+       static int is_done = 0;
+       int result;
 
        if (!(acpi_processor_ppc_status & PPC_REGISTERED))
                return -EBUSY;
@@ -492,26 +514,15 @@ int acpi_processor_notify_smm(struct module *calling_module)
 
        is_done = -EIO;
 
-       /* Can't write pstate_control to smi_command if either value is zero */
-       if ((!acpi_gbl_FADT.smi_command) || (!acpi_gbl_FADT.pstate_control)) {
+       result = acpi_processor_pstate_control();
+       if (!result) {
                ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No SMI port or pstate_control\n"));
                module_put(calling_module);
                return 0;
        }
-
-       ACPI_DEBUG_PRINT((ACPI_DB_INFO,
-                         "Writing pstate_control [0x%x] to smi_command [0x%x]\n",
-                         acpi_gbl_FADT.pstate_control, acpi_gbl_FADT.smi_command));
-
-       status = acpi_os_write_port(acpi_gbl_FADT.smi_command,
-                                   (u32) acpi_gbl_FADT.pstate_control, 8);
-       if (ACPI_FAILURE(status)) {
-               ACPI_EXCEPTION((AE_INFO, status,
-                               "Failed to write pstate_control [0x%x] to "
-                               "smi_command [0x%x]", acpi_gbl_FADT.pstate_control,
-                               acpi_gbl_FADT.smi_command));
+       if (result < 0) {
                module_put(calling_module);
-               return status;
+               return result;
        }
 
        /* Success. If there's no _PPC, we need to fear nothing, so
index f07e591c0e1fe3490b468c07814ddcb3bbc84c0c..ec1664bf6ef09f56f50ffce348e0e7bed042e685 100644 (file)
@@ -1909,9 +1909,20 @@ static bool __init intel_pstate_platform_pwr_mgmt_exists(void)
 
        return false;
 }
+
+static void intel_pstate_request_control_from_smm(void)
+{
+       /*
+        * It may be unsafe to request P-states control from SMM if _PPC support
+        * has not been enabled.
+        */
+       if (acpi_ppc)
+               acpi_processor_pstate_control();
+}
 #else /* CONFIG_ACPI not enabled */
 static inline bool intel_pstate_platform_pwr_mgmt_exists(void) { return false; }
 static inline bool intel_pstate_has_acpi_ppc(void) { return false; }
+static inline void intel_pstate_request_control_from_smm(void) {}
 #endif /* CONFIG_ACPI */
 
 static const struct x86_cpu_id hwp_support_ids[] __initconst = {
@@ -1963,6 +1974,8 @@ hwp_cpu_matched:
        if (!hwp_active && hwp_only)
                goto out;
 
+       intel_pstate_request_control_from_smm();
+
        rc = cpufreq_register_driver(&intel_pstate_driver);
        if (rc)
                goto out;
index f3db11c2465446cfa2cc1a160be13fb98a7af650..dd0cb04046ceb6dd452240c35753fc787d893cc3 100644 (file)
@@ -249,6 +249,7 @@ extern int acpi_processor_register_performance(struct acpi_processor_performance
                                               *performance, unsigned int cpu);
 extern void acpi_processor_unregister_performance(unsigned int cpu);
 
+int acpi_processor_pstate_control(void);
 /* note: this locks both the calling module and the processor module
          if a _PPC object exists, rmmod is disallowed then */
 int acpi_processor_notify_smm(struct module *calling_module);