|
@@ -19,10 +19,19 @@
|
|
|
#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.
|
|
@@ -32,6 +41,39 @@
|
|
|
*/
|
|
|
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)
|