cpufreq: CPPC: Force reporting values in KHz to fix user space interface
[cascardo/linux.git] / drivers / cpufreq / cppc_cpufreq.c
index 8882b8e..6debc18 100644 (file)
 #include <linux/delay.h>
 #include <linux/cpu.h>
 #include <linux/cpufreq.h>
+#include <linux/dmi.h>
 #include <linux/vmalloc.h>
 
+#include <asm/unaligned.h>
+
 #include <acpi/cppc_acpi.h>
 
+/* Minimum struct length needed for the DMI processor entry we want */
+#define DMI_ENTRY_PROCESSOR_MIN_LENGTH 48
+
+/* Offest in the DMI processor structure for the max frequency */
+#define DMI_PROCESSOR_MAX_SPEED  0x14
+
 /*
  * These structs contain information parsed from per CPU
  * ACPI _CPC structures.
  */
 static struct cpudata **all_cpu_data;
 
+/* Capture the max KHz from DMI */
+static u64 cppc_dmi_max_khz;
+
+/* Callback function used to retrieve the max frequency from DMI */
+static void cppc_find_dmi_mhz(const struct dmi_header *dm, void *private)
+{
+       const u8 *dmi_data = (const u8 *)dm;
+       u16 *mhz = (u16 *)private;
+
+       if (dm->type == DMI_ENTRY_PROCESSOR &&
+           dm->length >= DMI_ENTRY_PROCESSOR_MIN_LENGTH) {
+               u16 val = (u16)get_unaligned((const u16 *)
+                               (dmi_data + DMI_PROCESSOR_MAX_SPEED));
+               *mhz = val > *mhz ? val : *mhz;
+       }
+}
+
+/* Look up the max frequency in DMI */
+static u64 cppc_get_dmi_max_khz(void)
+{
+       u16 mhz = 0;
+
+       dmi_walk(cppc_find_dmi_mhz, &mhz);
+
+       /*
+        * Real stupid fallback value, just in case there is no
+        * actual value set.
+        */
+       mhz = mhz ? mhz : 1;
+
+       return (1000 * mhz);
+}
+
 static int cppc_cpufreq_set_target(struct cpufreq_policy *policy,
                unsigned int target_freq,
                unsigned int relation)
@@ -42,7 +84,7 @@ static int cppc_cpufreq_set_target(struct cpufreq_policy *policy,
 
        cpu = all_cpu_data[policy->cpu];
 
-       cpu->perf_ctrls.desired_perf = target_freq;
+       cpu->perf_ctrls.desired_perf = target_freq * policy->max / cppc_dmi_max_khz;
        freqs.old = policy->cur;
        freqs.new = target_freq;
 
@@ -94,8 +136,10 @@ static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy)
                return ret;
        }
 
-       policy->min = cpu->perf_caps.lowest_perf;
-       policy->max = cpu->perf_caps.highest_perf;
+       cppc_dmi_max_khz = cppc_get_dmi_max_khz();
+
+       policy->min = cpu->perf_caps.lowest_perf * cppc_dmi_max_khz / cpu->perf_caps.highest_perf;
+       policy->max = cppc_dmi_max_khz;
        policy->cpuinfo.min_freq = policy->min;
        policy->cpuinfo.max_freq = policy->max;
        policy->shared_type = cpu->shared_type;
@@ -112,7 +156,8 @@ static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy)
        cpu->cur_policy = policy;
 
        /* Set policy->cur to max now. The governors will adjust later. */
-       policy->cur = cpu->perf_ctrls.desired_perf = cpu->perf_caps.highest_perf;
+       policy->cur = cppc_dmi_max_khz;
+       cpu->perf_ctrls.desired_perf = cpu->perf_caps.highest_perf;
 
        ret = cppc_set_perf(cpu_num, &cpu->perf_ctrls);
        if (ret)