diff options
Diffstat (limited to 'drivers/cpufreq/cppc_cpufreq.c')
-rw-r--r-- | drivers/cpufreq/cppc_cpufreq.c | 74 |
1 files changed, 63 insertions, 11 deletions
diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c index 8882b8e2ecd0..4852d9efe74e 100644 --- a/drivers/cpufreq/cppc_cpufreq.c +++ b/drivers/cpufreq/cppc_cpufreq.c | |||
@@ -19,10 +19,19 @@ | |||
19 | #include <linux/delay.h> | 19 | #include <linux/delay.h> |
20 | #include <linux/cpu.h> | 20 | #include <linux/cpu.h> |
21 | #include <linux/cpufreq.h> | 21 | #include <linux/cpufreq.h> |
22 | #include <linux/dmi.h> | ||
22 | #include <linux/vmalloc.h> | 23 | #include <linux/vmalloc.h> |
23 | 24 | ||
25 | #include <asm/unaligned.h> | ||
26 | |||
24 | #include <acpi/cppc_acpi.h> | 27 | #include <acpi/cppc_acpi.h> |
25 | 28 | ||
29 | /* Minimum struct length needed for the DMI processor entry we want */ | ||
30 | #define DMI_ENTRY_PROCESSOR_MIN_LENGTH 48 | ||
31 | |||
32 | /* Offest in the DMI processor structure for the max frequency */ | ||
33 | #define DMI_PROCESSOR_MAX_SPEED 0x14 | ||
34 | |||
26 | /* | 35 | /* |
27 | * These structs contain information parsed from per CPU | 36 | * These structs contain information parsed from per CPU |
28 | * ACPI _CPC structures. | 37 | * ACPI _CPC structures. |
@@ -30,19 +39,58 @@ | |||
30 | * performance capabilities, desired performance level | 39 | * performance capabilities, desired performance level |
31 | * requested etc. | 40 | * requested etc. |
32 | */ | 41 | */ |
33 | static struct cpudata **all_cpu_data; | 42 | static struct cppc_cpudata **all_cpu_data; |
43 | |||
44 | /* Capture the max KHz from DMI */ | ||
45 | static u64 cppc_dmi_max_khz; | ||
46 | |||
47 | /* Callback function used to retrieve the max frequency from DMI */ | ||
48 | static void cppc_find_dmi_mhz(const struct dmi_header *dm, void *private) | ||
49 | { | ||
50 | const u8 *dmi_data = (const u8 *)dm; | ||
51 | u16 *mhz = (u16 *)private; | ||
52 | |||
53 | if (dm->type == DMI_ENTRY_PROCESSOR && | ||
54 | dm->length >= DMI_ENTRY_PROCESSOR_MIN_LENGTH) { | ||
55 | u16 val = (u16)get_unaligned((const u16 *) | ||
56 | (dmi_data + DMI_PROCESSOR_MAX_SPEED)); | ||
57 | *mhz = val > *mhz ? val : *mhz; | ||
58 | } | ||
59 | } | ||
60 | |||
61 | /* Look up the max frequency in DMI */ | ||
62 | static u64 cppc_get_dmi_max_khz(void) | ||
63 | { | ||
64 | u16 mhz = 0; | ||
65 | |||
66 | dmi_walk(cppc_find_dmi_mhz, &mhz); | ||
67 | |||
68 | /* | ||
69 | * Real stupid fallback value, just in case there is no | ||
70 | * actual value set. | ||
71 | */ | ||
72 | mhz = mhz ? mhz : 1; | ||
73 | |||
74 | return (1000 * mhz); | ||
75 | } | ||
34 | 76 | ||
35 | static int cppc_cpufreq_set_target(struct cpufreq_policy *policy, | 77 | static int cppc_cpufreq_set_target(struct cpufreq_policy *policy, |
36 | unsigned int target_freq, | 78 | unsigned int target_freq, |
37 | unsigned int relation) | 79 | unsigned int relation) |
38 | { | 80 | { |
39 | struct cpudata *cpu; | 81 | struct cppc_cpudata *cpu; |
40 | struct cpufreq_freqs freqs; | 82 | struct cpufreq_freqs freqs; |
83 | u32 desired_perf; | ||
41 | int ret = 0; | 84 | int ret = 0; |
42 | 85 | ||
43 | cpu = all_cpu_data[policy->cpu]; | 86 | cpu = all_cpu_data[policy->cpu]; |
44 | 87 | ||
45 | cpu->perf_ctrls.desired_perf = target_freq; | 88 | desired_perf = (u64)target_freq * cpu->perf_caps.highest_perf / cppc_dmi_max_khz; |
89 | /* Return if it is exactly the same perf */ | ||
90 | if (desired_perf == cpu->perf_ctrls.desired_perf) | ||
91 | return ret; | ||
92 | |||
93 | cpu->perf_ctrls.desired_perf = desired_perf; | ||
46 | freqs.old = policy->cur; | 94 | freqs.old = policy->cur; |
47 | freqs.new = target_freq; | 95 | freqs.new = target_freq; |
48 | 96 | ||
@@ -66,7 +114,7 @@ static int cppc_verify_policy(struct cpufreq_policy *policy) | |||
66 | static void cppc_cpufreq_stop_cpu(struct cpufreq_policy *policy) | 114 | static void cppc_cpufreq_stop_cpu(struct cpufreq_policy *policy) |
67 | { | 115 | { |
68 | int cpu_num = policy->cpu; | 116 | int cpu_num = policy->cpu; |
69 | struct cpudata *cpu = all_cpu_data[cpu_num]; | 117 | struct cppc_cpudata *cpu = all_cpu_data[cpu_num]; |
70 | int ret; | 118 | int ret; |
71 | 119 | ||
72 | cpu->perf_ctrls.desired_perf = cpu->perf_caps.lowest_perf; | 120 | cpu->perf_ctrls.desired_perf = cpu->perf_caps.lowest_perf; |
@@ -79,7 +127,7 @@ static void cppc_cpufreq_stop_cpu(struct cpufreq_policy *policy) | |||
79 | 127 | ||
80 | static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy) | 128 | static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy) |
81 | { | 129 | { |
82 | struct cpudata *cpu; | 130 | struct cppc_cpudata *cpu; |
83 | unsigned int cpu_num = policy->cpu; | 131 | unsigned int cpu_num = policy->cpu; |
84 | int ret = 0; | 132 | int ret = 0; |
85 | 133 | ||
@@ -94,10 +142,13 @@ static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy) | |||
94 | return ret; | 142 | return ret; |
95 | } | 143 | } |
96 | 144 | ||
97 | policy->min = cpu->perf_caps.lowest_perf; | 145 | cppc_dmi_max_khz = cppc_get_dmi_max_khz(); |
98 | policy->max = cpu->perf_caps.highest_perf; | 146 | |
147 | policy->min = cpu->perf_caps.lowest_perf * cppc_dmi_max_khz / cpu->perf_caps.highest_perf; | ||
148 | policy->max = cppc_dmi_max_khz; | ||
99 | policy->cpuinfo.min_freq = policy->min; | 149 | policy->cpuinfo.min_freq = policy->min; |
100 | policy->cpuinfo.max_freq = policy->max; | 150 | policy->cpuinfo.max_freq = policy->max; |
151 | policy->cpuinfo.transition_latency = cppc_get_transition_latency(cpu_num); | ||
101 | policy->shared_type = cpu->shared_type; | 152 | policy->shared_type = cpu->shared_type; |
102 | 153 | ||
103 | if (policy->shared_type == CPUFREQ_SHARED_TYPE_ANY) | 154 | if (policy->shared_type == CPUFREQ_SHARED_TYPE_ANY) |
@@ -112,7 +163,8 @@ static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy) | |||
112 | cpu->cur_policy = policy; | 163 | cpu->cur_policy = policy; |
113 | 164 | ||
114 | /* Set policy->cur to max now. The governors will adjust later. */ | 165 | /* Set policy->cur to max now. The governors will adjust later. */ |
115 | policy->cur = cpu->perf_ctrls.desired_perf = cpu->perf_caps.highest_perf; | 166 | policy->cur = cppc_dmi_max_khz; |
167 | cpu->perf_ctrls.desired_perf = cpu->perf_caps.highest_perf; | ||
116 | 168 | ||
117 | ret = cppc_set_perf(cpu_num, &cpu->perf_ctrls); | 169 | ret = cppc_set_perf(cpu_num, &cpu->perf_ctrls); |
118 | if (ret) | 170 | if (ret) |
@@ -134,7 +186,7 @@ static struct cpufreq_driver cppc_cpufreq_driver = { | |||
134 | static int __init cppc_cpufreq_init(void) | 186 | static int __init cppc_cpufreq_init(void) |
135 | { | 187 | { |
136 | int i, ret = 0; | 188 | int i, ret = 0; |
137 | struct cpudata *cpu; | 189 | struct cppc_cpudata *cpu; |
138 | 190 | ||
139 | if (acpi_disabled) | 191 | if (acpi_disabled) |
140 | return -ENODEV; | 192 | return -ENODEV; |
@@ -144,7 +196,7 @@ static int __init cppc_cpufreq_init(void) | |||
144 | return -ENOMEM; | 196 | return -ENOMEM; |
145 | 197 | ||
146 | for_each_possible_cpu(i) { | 198 | for_each_possible_cpu(i) { |
147 | all_cpu_data[i] = kzalloc(sizeof(struct cpudata), GFP_KERNEL); | 199 | all_cpu_data[i] = kzalloc(sizeof(struct cppc_cpudata), GFP_KERNEL); |
148 | if (!all_cpu_data[i]) | 200 | if (!all_cpu_data[i]) |
149 | goto out; | 201 | goto out; |
150 | 202 | ||
@@ -175,7 +227,7 @@ out: | |||
175 | 227 | ||
176 | static void __exit cppc_cpufreq_exit(void) | 228 | static void __exit cppc_cpufreq_exit(void) |
177 | { | 229 | { |
178 | struct cpudata *cpu; | 230 | struct cppc_cpudata *cpu; |
179 | int i; | 231 | int i; |
180 | 232 | ||
181 | cpufreq_unregister_driver(&cppc_cpufreq_driver); | 233 | cpufreq_unregister_driver(&cppc_cpufreq_driver); |