Blame view
drivers/cpufreq/p4-clockmod.c
8.19 KB
1da177e4c
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
/* * Pentium 4/Xeon CPU on demand clock modulation/speed scaling * (C) 2002 - 2003 Dominik Brodowski <linux@brodo.de> * (C) 2002 Zwane Mwaikambo <zwane@commfireservices.com> * (C) 2002 Arjan van de Ven <arjanv@redhat.com> * (C) 2002 Tora T. Engstad * All Rights Reserved * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * * The author(s) of this software shall not be held liable for damages * of any nature resulting due to the use of this software. This * software is provided AS-IS with no warranties. |
32ee8c3e4
|
17 |
* |
1da177e4c
|
18 19 20 21 |
* Date Errata Description * 20020525 N44, O17 12.5% or 25% DC causes lockup * */ |
1da177e4c
|
22 |
#include <linux/kernel.h> |
32ee8c3e4
|
23 |
#include <linux/module.h> |
1da177e4c
|
24 25 26 |
#include <linux/init.h> #include <linux/smp.h> #include <linux/cpufreq.h> |
1da177e4c
|
27 |
#include <linux/cpumask.h> |
bbfebd665
|
28 |
#include <linux/timex.h> |
1da177e4c
|
29 |
|
32ee8c3e4
|
30 |
#include <asm/processor.h> |
1da177e4c
|
31 |
#include <asm/msr.h> |
199785eac
|
32 |
#include <asm/timer.h> |
1da177e4c
|
33 34 35 36 |
#include "speedstep-lib.h" #define PFX "p4-clockmod: " |
1da177e4c
|
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
/* * Duty Cycle (3bits), note DC_DISABLE is not specified in * intel docs i just use it to mean disable */ enum { DC_RESV, DC_DFLT, DC_25PT, DC_38PT, DC_50PT, DC_64PT, DC_75PT, DC_88PT, DC_DISABLE }; #define DC_ENTRIES 8 static int has_N44_O17_errata[NR_CPUS]; static unsigned int stock_freq; static struct cpufreq_driver p4clockmod_driver; static unsigned int cpufreq_p4_get(unsigned int cpu); static int cpufreq_p4_setdc(unsigned int cpu, unsigned int newstate) { u32 l, h; |
bbfebd665
|
58 59 |
if (!cpu_online(cpu) || (newstate > DC_DISABLE) || (newstate == DC_RESV)) |
1da177e4c
|
60 |
return -EINVAL; |
551948bc4
|
61 |
rdmsr_on_cpu(cpu, MSR_IA32_THERM_STATUS, &l, &h); |
1da177e4c
|
62 63 |
if (l & 0x01) |
2d06d8c49
|
64 65 |
pr_debug("CPU#%d currently thermal throttled ", cpu); |
1da177e4c
|
66 |
|
bbfebd665
|
67 68 |
if (has_N44_O17_errata[cpu] && (newstate == DC_25PT || newstate == DC_DFLT)) |
1da177e4c
|
69 |
newstate = DC_38PT; |
551948bc4
|
70 |
rdmsr_on_cpu(cpu, MSR_IA32_THERM_CONTROL, &l, &h); |
1da177e4c
|
71 |
if (newstate == DC_DISABLE) { |
2d06d8c49
|
72 73 |
pr_debug("CPU#%d disabling modulation ", cpu); |
551948bc4
|
74 |
wrmsr_on_cpu(cpu, MSR_IA32_THERM_CONTROL, l & ~(1<<4), h); |
1da177e4c
|
75 |
} else { |
2d06d8c49
|
76 77 |
pr_debug("CPU#%d setting duty cycle to %d%% ", |
1da177e4c
|
78 |
cpu, ((125 * newstate) / 10)); |
32ee8c3e4
|
79 |
/* bits 63 - 5 : reserved |
1da177e4c
|
80 81 82 83 84 85 |
* bit 4 : enable/disable * bits 3-1 : duty cycle * bit 0 : reserved */ l = (l & ~14); l = l | (1<<4) | ((newstate & 0x7)<<1); |
551948bc4
|
86 |
wrmsr_on_cpu(cpu, MSR_IA32_THERM_CONTROL, l, h); |
1da177e4c
|
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
} return 0; } static struct cpufreq_frequency_table p4clockmod_table[] = { {DC_RESV, CPUFREQ_ENTRY_INVALID}, {DC_DFLT, 0}, {DC_25PT, 0}, {DC_38PT, 0}, {DC_50PT, 0}, {DC_64PT, 0}, {DC_75PT, 0}, {DC_88PT, 0}, {DC_DISABLE, 0}, {DC_RESV, CPUFREQ_TABLE_END}, }; static int cpufreq_p4_target(struct cpufreq_policy *policy, unsigned int target_freq, unsigned int relation) { unsigned int newstate = DC_RESV; struct cpufreq_freqs freqs; |
1da177e4c
|
113 |
int i; |
bbfebd665
|
114 115 |
if (cpufreq_frequency_table_target(policy, &p4clockmod_table[0], target_freq, relation, &newstate)) |
1da177e4c
|
116 117 118 119 120 121 122 123 124 |
return -EINVAL; freqs.old = cpufreq_p4_get(policy->cpu); freqs.new = stock_freq * p4clockmod_table[newstate].index / 8; if (freqs.new == freqs.old) return 0; /* notifiers */ |
835481d9b
|
125 |
for_each_cpu(i, policy->cpus) { |
1da177e4c
|
126 127 128 |
freqs.cpu = i; cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); } |
bbfebd665
|
129 130 |
/* run on each logical CPU, * see section 13.15.3 of IA32 Intel Architecture Software |
32ee8c3e4
|
131 |
* Developer's Manual, Volume 3 |
1da177e4c
|
132 |
*/ |
835481d9b
|
133 |
for_each_cpu(i, policy->cpus) |
1da177e4c
|
134 |
cpufreq_p4_setdc(i, p4clockmod_table[newstate].index); |
1da177e4c
|
135 136 |
/* notifiers */ |
835481d9b
|
137 |
for_each_cpu(i, policy->cpus) { |
1da177e4c
|
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
freqs.cpu = i; cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); } return 0; } static int cpufreq_p4_verify(struct cpufreq_policy *policy) { return cpufreq_frequency_table_verify(policy, &p4clockmod_table[0]); } static unsigned int cpufreq_p4_get_frequency(struct cpuinfo_x86 *c) { |
4e74663c5
|
154 155 |
if (c->x86 == 0x06) { if (cpu_has(c, X86_FEATURE_EST)) |
853cee26e
|
156 157 158 |
printk_once(KERN_WARNING PFX "Warning: EST-capable " "CPU detected. The acpi-cpufreq module offers " "voltage scaling in addition to frequency " |
bbfebd665
|
159 160 161 |
"scaling. You should use that instead of " "p4-clockmod, if possible. "); |
4e74663c5
|
162 163 164 |
switch (c->x86_model) { case 0x0E: /* Core */ case 0x0F: /* Core Duo */ |
8529154ec
|
165 |
case 0x16: /* Celeron Core */ |
431950377
|
166 |
case 0x1C: /* Atom */ |
4e74663c5
|
167 |
p4clockmod_driver.flags |= CPUFREQ_CONST_LOOPS; |
bbfebd665
|
168 |
return speedstep_get_frequency(SPEEDSTEP_CPU_PCORE); |
4e74663c5
|
169 170 171 172 |
case 0x0D: /* Pentium M (Dothan) */ p4clockmod_driver.flags |= CPUFREQ_CONST_LOOPS; /* fall through */ case 0x09: /* Pentium M (Banias) */ |
bbfebd665
|
173 |
return speedstep_get_frequency(SPEEDSTEP_CPU_PM); |
4e74663c5
|
174 |
} |
1da177e4c
|
175 |
} |
9d1f44ee2
|
176 |
if (c->x86 != 0xF) |
1da177e4c
|
177 |
return 0; |
1da177e4c
|
178 179 180 181 |
/* on P-4s, the TSC runs with constant frequency independent whether * throttling is active or not. */ p4clockmod_driver.flags |= CPUFREQ_CONST_LOOPS; |
bbfebd665
|
182 |
if (speedstep_detect_processor() == SPEEDSTEP_CPU_P4M) { |
1da177e4c
|
183 184 185 186 187 188 |
printk(KERN_WARNING PFX "Warning: Pentium 4-M detected. " "The speedstep-ich or acpi cpufreq modules offer " "voltage scaling in addition of frequency scaling. " "You should use either one instead of p4-clockmod, " "if possible. "); |
bbfebd665
|
189 |
return speedstep_get_frequency(SPEEDSTEP_CPU_P4M); |
1da177e4c
|
190 |
} |
bbfebd665
|
191 |
return speedstep_get_frequency(SPEEDSTEP_CPU_P4D); |
1da177e4c
|
192 |
} |
32ee8c3e4
|
193 |
|
1da177e4c
|
194 195 196 |
static int cpufreq_p4_cpu_init(struct cpufreq_policy *policy) { |
92cb7612a
|
197 |
struct cpuinfo_x86 *c = &cpu_data(policy->cpu); |
1da177e4c
|
198 199 200 201 |
int cpuid = 0; unsigned int i; #ifdef CONFIG_SMP |
7ad728f98
|
202 |
cpumask_copy(policy->cpus, cpu_sibling_mask(policy->cpu)); |
1da177e4c
|
203 204 205 206 207 208 209 210 211 212 |
#endif /* Errata workaround */ cpuid = (c->x86 << 8) | (c->x86_model << 4) | c->x86_mask; switch (cpuid) { case 0x0f07: case 0x0f0a: case 0x0f11: case 0x0f12: has_N44_O17_errata[policy->cpu] = 1; |
2d06d8c49
|
213 214 |
pr_debug("has errata -- disabling low frequencies "); |
1da177e4c
|
215 |
} |
32ee8c3e4
|
216 |
|
199785eac
|
217 218 219 220 221 222 |
if (speedstep_detect_processor() == SPEEDSTEP_CPU_P4D && c->x86_model < 2) { /* switch to maximum frequency and measure result */ cpufreq_p4_setdc(policy->cpu, DC_DISABLE); recalibrate_cpu_khz(); } |
1da177e4c
|
223 224 225 226 227 228 |
/* get max frequency */ stock_freq = cpufreq_p4_get_frequency(c); if (!stock_freq) return -EINVAL; /* table init */ |
bbfebd665
|
229 230 |
for (i = 1; (p4clockmod_table[i].frequency != CPUFREQ_TABLE_END); i++) { if ((i < 2) && (has_N44_O17_errata[policy->cpu])) |
1da177e4c
|
231 232 233 234 235 |
p4clockmod_table[i].frequency = CPUFREQ_ENTRY_INVALID; else p4clockmod_table[i].frequency = (stock_freq * i)/8; } cpufreq_frequency_table_get_attr(p4clockmod_table, policy->cpu); |
32ee8c3e4
|
236 |
|
1da177e4c
|
237 |
/* cpuinfo and default policy values */ |
36e8abf3e
|
238 239 240 241 |
/* the transition latency is set to be 1 higher than the maximum * transition latency of the ondemand governor */ policy->cpuinfo.transition_latency = 10000001; |
1da177e4c
|
242 243 244 245 246 247 248 249 |
policy->cur = stock_freq; return cpufreq_frequency_table_cpuinfo(policy, &p4clockmod_table[0]); } static int cpufreq_p4_cpu_exit(struct cpufreq_policy *policy) { |
32ee8c3e4
|
250 |
cpufreq_frequency_table_put_attr(policy->cpu); |
1da177e4c
|
251 252 253 254 255 |
return 0; } static unsigned int cpufreq_p4_get(unsigned int cpu) { |
1da177e4c
|
256 |
u32 l, h; |
551948bc4
|
257 |
rdmsr_on_cpu(cpu, MSR_IA32_THERM_CONTROL, &l, &h); |
1da177e4c
|
258 259 260 261 262 263 264 265 |
if (l & 0x10) { l = l >> 1; l &= 0x7; } else l = DC_DISABLE; if (l != DC_DISABLE) |
bbfebd665
|
266 |
return stock_freq * l / 8; |
1da177e4c
|
267 268 269 |
return stock_freq; } |
bbfebd665
|
270 |
static struct freq_attr *p4clockmod_attr[] = { |
1da177e4c
|
271 272 273 274 275 |
&cpufreq_freq_attr_scaling_available_freqs, NULL, }; static struct cpufreq_driver p4clockmod_driver = { |
32ee8c3e4
|
276 |
.verify = cpufreq_p4_verify, |
1da177e4c
|
277 278 279 280 281 282 283 284 285 286 287 |
.target = cpufreq_p4_target, .init = cpufreq_p4_cpu_init, .exit = cpufreq_p4_cpu_exit, .get = cpufreq_p4_get, .name = "p4-clockmod", .owner = THIS_MODULE, .attr = p4clockmod_attr, }; static int __init cpufreq_p4_init(void) |
32ee8c3e4
|
288 |
{ |
92cb7612a
|
289 |
struct cpuinfo_x86 *c = &cpu_data(0); |
1da177e4c
|
290 291 292 |
int ret; /* |
32ee8c3e4
|
293 |
* THERM_CONTROL is architectural for IA32 now, so |
1da177e4c
|
294 295 296 297 |
* we can rely on the capability checks */ if (c->x86_vendor != X86_VENDOR_INTEL) return -ENODEV; |
8ce116e59
|
298 299 |
if (!test_cpu_cap(c, X86_FEATURE_ACPI) || !test_cpu_cap(c, X86_FEATURE_ACC)) |
1da177e4c
|
300 301 302 303 |
return -ENODEV; ret = cpufreq_register_driver(&p4clockmod_driver); if (!ret) |
bbfebd665
|
304 305 306 |
printk(KERN_INFO PFX "P4/Xeon(TM) CPU On-Demand Clock " "Modulation available "); |
1da177e4c
|
307 |
|
bbfebd665
|
308 |
return ret; |
1da177e4c
|
309 310 311 312 313 314 315 |
} static void __exit cpufreq_p4_exit(void) { cpufreq_unregister_driver(&p4clockmod_driver); } |
bbfebd665
|
316 317 318 |
MODULE_AUTHOR("Zwane Mwaikambo <zwane@commfireservices.com>"); MODULE_DESCRIPTION("cpufreq driver for Pentium(TM) 4/Xeon(TM)"); MODULE_LICENSE("GPL"); |
1da177e4c
|
319 320 321 |
late_initcall(cpufreq_p4_init); module_exit(cpufreq_p4_exit); |