diff options
Diffstat (limited to 'drivers/cpufreq/cppc_cpufreq.c')
| -rw-r--r-- | drivers/cpufreq/cppc_cpufreq.c | 68 | 
1 files changed, 57 insertions, 11 deletions
diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c index 8882b8e2ecd0..1b2f28f69a81 100644 --- a/drivers/cpufreq/cppc_cpufreq.c +++ b/drivers/cpufreq/cppc_cpufreq.c @@ -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. @@ -30,19 +39,52 @@   * performance capabilities, desired performance level   * requested etc.   */ -static struct cpudata **all_cpu_data; +static struct cppc_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)  { -	struct cpudata *cpu; +	struct cppc_cpudata *cpu;  	struct cpufreq_freqs freqs;  	int ret = 0;  	cpu = all_cpu_data[policy->cpu]; -	cpu->perf_ctrls.desired_perf = target_freq; +	cpu->perf_ctrls.desired_perf = (u64)target_freq * policy->max / cppc_dmi_max_khz;  	freqs.old = policy->cur;  	freqs.new = target_freq; @@ -66,7 +108,7 @@ static int cppc_verify_policy(struct cpufreq_policy *policy)  static void cppc_cpufreq_stop_cpu(struct cpufreq_policy *policy)  {  	int cpu_num = policy->cpu; -	struct cpudata *cpu = all_cpu_data[cpu_num]; +	struct cppc_cpudata *cpu = all_cpu_data[cpu_num];  	int ret;  	cpu->perf_ctrls.desired_perf = cpu->perf_caps.lowest_perf; @@ -79,7 +121,7 @@ static void cppc_cpufreq_stop_cpu(struct cpufreq_policy *policy)  static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy)  { -	struct cpudata *cpu; +	struct cppc_cpudata *cpu;  	unsigned int cpu_num = policy->cpu;  	int ret = 0; @@ -94,10 +136,13 @@ 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->cpuinfo.transition_latency = cppc_get_transition_latency(cpu_num);  	policy->shared_type = cpu->shared_type;  	if (policy->shared_type == CPUFREQ_SHARED_TYPE_ANY) @@ -112,7 +157,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) @@ -134,7 +180,7 @@ static struct cpufreq_driver cppc_cpufreq_driver = {  static int __init cppc_cpufreq_init(void)  {  	int i, ret = 0; -	struct cpudata *cpu; +	struct cppc_cpudata *cpu;  	if (acpi_disabled)  		return -ENODEV; @@ -144,7 +190,7 @@ static int __init cppc_cpufreq_init(void)  		return -ENOMEM;  	for_each_possible_cpu(i) { -		all_cpu_data[i] = kzalloc(sizeof(struct cpudata), GFP_KERNEL); +		all_cpu_data[i] = kzalloc(sizeof(struct cppc_cpudata), GFP_KERNEL);  		if (!all_cpu_data[i])  			goto out; @@ -175,7 +221,7 @@ out:  static void __exit cppc_cpufreq_exit(void)  { -	struct cpudata *cpu; +	struct cppc_cpudata *cpu;  	int i;  	cpufreq_unregister_driver(&cppc_cpufreq_driver);  |