Blame view
drivers/cpufreq/p4-clockmod.c
7.27 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> |
fa8031aef
|
33 |
#include <asm/cpu_device_id.h> |
1da177e4c
|
34 35 36 37 |
#include "speedstep-lib.h" #define PFX "p4-clockmod: " |
1da177e4c
|
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
/* * 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; |
e9f51837c
|
59 |
if ((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 |
} return 0; } static struct cpufreq_frequency_table p4clockmod_table[] = { |
7f4b04614
|
94 95 96 97 98 99 100 101 102 103 |
{0, DC_RESV, CPUFREQ_ENTRY_INVALID}, {0, DC_DFLT, 0}, {0, DC_25PT, 0}, {0, DC_38PT, 0}, {0, DC_50PT, 0}, {0, DC_64PT, 0}, {0, DC_75PT, 0}, {0, DC_88PT, 0}, {0, DC_DISABLE, 0}, {0, DC_RESV, CPUFREQ_TABLE_END}, |
1da177e4c
|
104 |
}; |
9c0ebcf78
|
105 |
static int cpufreq_p4_target(struct cpufreq_policy *policy, unsigned int index) |
1da177e4c
|
106 |
{ |
1da177e4c
|
107 |
int i; |
bbfebd665
|
108 109 |
/* run on each logical CPU, * see section 13.15.3 of IA32 Intel Architecture Software |
32ee8c3e4
|
110 |
* Developer's Manual, Volume 3 |
1da177e4c
|
111 |
*/ |
835481d9b
|
112 |
for_each_cpu(i, policy->cpus) |
9c0ebcf78
|
113 |
cpufreq_p4_setdc(i, p4clockmod_table[index].driver_data); |
1da177e4c
|
114 |
|
1da177e4c
|
115 116 |
return 0; } |
1da177e4c
|
117 118 |
static unsigned int cpufreq_p4_get_frequency(struct cpuinfo_x86 *c) { |
4e74663c5
|
119 120 |
if (c->x86 == 0x06) { if (cpu_has(c, X86_FEATURE_EST)) |
853cee26e
|
121 122 123 |
printk_once(KERN_WARNING PFX "Warning: EST-capable " "CPU detected. The acpi-cpufreq module offers " "voltage scaling in addition to frequency " |
bbfebd665
|
124 125 126 |
"scaling. You should use that instead of " "p4-clockmod, if possible. "); |
4e74663c5
|
127 128 129 |
switch (c->x86_model) { case 0x0E: /* Core */ case 0x0F: /* Core Duo */ |
8529154ec
|
130 |
case 0x16: /* Celeron Core */ |
431950377
|
131 |
case 0x1C: /* Atom */ |
4e74663c5
|
132 |
p4clockmod_driver.flags |= CPUFREQ_CONST_LOOPS; |
bbfebd665
|
133 |
return speedstep_get_frequency(SPEEDSTEP_CPU_PCORE); |
4e74663c5
|
134 135 136 137 |
case 0x0D: /* Pentium M (Dothan) */ p4clockmod_driver.flags |= CPUFREQ_CONST_LOOPS; /* fall through */ case 0x09: /* Pentium M (Banias) */ |
bbfebd665
|
138 |
return speedstep_get_frequency(SPEEDSTEP_CPU_PM); |
4e74663c5
|
139 |
} |
1da177e4c
|
140 |
} |
9d1f44ee2
|
141 |
if (c->x86 != 0xF) |
1da177e4c
|
142 |
return 0; |
1da177e4c
|
143 144 145 146 |
/* on P-4s, the TSC runs with constant frequency independent whether * throttling is active or not. */ p4clockmod_driver.flags |= CPUFREQ_CONST_LOOPS; |
bbfebd665
|
147 |
if (speedstep_detect_processor() == SPEEDSTEP_CPU_P4M) { |
1da177e4c
|
148 149 150 151 152 153 |
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
|
154 |
return speedstep_get_frequency(SPEEDSTEP_CPU_P4M); |
1da177e4c
|
155 |
} |
bbfebd665
|
156 |
return speedstep_get_frequency(SPEEDSTEP_CPU_P4D); |
1da177e4c
|
157 |
} |
32ee8c3e4
|
158 |
|
1da177e4c
|
159 160 161 |
static int cpufreq_p4_cpu_init(struct cpufreq_policy *policy) { |
92cb7612a
|
162 |
struct cpuinfo_x86 *c = &cpu_data(policy->cpu); |
1da177e4c
|
163 164 165 166 |
int cpuid = 0; unsigned int i; #ifdef CONFIG_SMP |
7ad728f98
|
167 |
cpumask_copy(policy->cpus, cpu_sibling_mask(policy->cpu)); |
1da177e4c
|
168 169 170 171 172 173 174 175 176 177 |
#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
|
178 179 |
pr_debug("has errata -- disabling low frequencies "); |
1da177e4c
|
180 |
} |
32ee8c3e4
|
181 |
|
199785eac
|
182 183 184 185 186 187 |
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
|
188 189 190 191 192 193 |
/* get max frequency */ stock_freq = cpufreq_p4_get_frequency(c); if (!stock_freq) return -EINVAL; /* table init */ |
bbfebd665
|
194 195 |
for (i = 1; (p4clockmod_table[i].frequency != CPUFREQ_TABLE_END); i++) { if ((i < 2) && (has_N44_O17_errata[policy->cpu])) |
1da177e4c
|
196 197 198 199 |
p4clockmod_table[i].frequency = CPUFREQ_ENTRY_INVALID; else p4clockmod_table[i].frequency = (stock_freq * i)/8; } |
32ee8c3e4
|
200 |
|
1da177e4c
|
201 |
/* cpuinfo and default policy values */ |
36e8abf3e
|
202 203 204 205 |
/* the transition latency is set to be 1 higher than the maximum * transition latency of the ondemand governor */ policy->cpuinfo.transition_latency = 10000001; |
1da177e4c
|
206 |
|
719ffe495
|
207 |
return cpufreq_table_validate_and_show(policy, &p4clockmod_table[0]); |
1da177e4c
|
208 |
} |
1da177e4c
|
209 210 |
static unsigned int cpufreq_p4_get(unsigned int cpu) { |
1da177e4c
|
211 |
u32 l, h; |
551948bc4
|
212 |
rdmsr_on_cpu(cpu, MSR_IA32_THERM_CONTROL, &l, &h); |
1da177e4c
|
213 214 215 216 217 218 219 220 |
if (l & 0x10) { l = l >> 1; l &= 0x7; } else l = DC_DISABLE; if (l != DC_DISABLE) |
bbfebd665
|
221 |
return stock_freq * l / 8; |
1da177e4c
|
222 223 224 |
return stock_freq; } |
1da177e4c
|
225 |
static struct cpufreq_driver p4clockmod_driver = { |
522f70cef
|
226 |
.verify = cpufreq_generic_frequency_table_verify, |
9c0ebcf78
|
227 |
.target_index = cpufreq_p4_target, |
1da177e4c
|
228 |
.init = cpufreq_p4_cpu_init, |
1da177e4c
|
229 230 |
.get = cpufreq_p4_get, .name = "p4-clockmod", |
522f70cef
|
231 |
.attr = cpufreq_generic_attr, |
1da177e4c
|
232 |
}; |
fa8031aef
|
233 234 235 236 237 238 239 240 241 |
static const struct x86_cpu_id cpufreq_p4_id[] = { { X86_VENDOR_INTEL, X86_FAMILY_ANY, X86_MODEL_ANY, X86_FEATURE_ACC }, {} }; /* * Intentionally no MODULE_DEVICE_TABLE here: this driver should not * be auto loaded. Please don't add one. */ |
1da177e4c
|
242 243 |
static int __init cpufreq_p4_init(void) |
32ee8c3e4
|
244 |
{ |
1da177e4c
|
245 246 247 |
int ret; /* |
32ee8c3e4
|
248 |
* THERM_CONTROL is architectural for IA32 now, so |
1da177e4c
|
249 250 |
* we can rely on the capability checks */ |
fa8031aef
|
251 |
if (!x86_match_cpu(cpufreq_p4_id) || !boot_cpu_has(X86_FEATURE_ACPI)) |
1da177e4c
|
252 253 254 255 |
return -ENODEV; ret = cpufreq_register_driver(&p4clockmod_driver); if (!ret) |
bbfebd665
|
256 257 258 |
printk(KERN_INFO PFX "P4/Xeon(TM) CPU On-Demand Clock " "Modulation available "); |
1da177e4c
|
259 |
|
bbfebd665
|
260 |
return ret; |
1da177e4c
|
261 262 263 264 265 266 267 |
} static void __exit cpufreq_p4_exit(void) { cpufreq_unregister_driver(&p4clockmod_driver); } |
bbfebd665
|
268 269 270 |
MODULE_AUTHOR("Zwane Mwaikambo <zwane@commfireservices.com>"); MODULE_DESCRIPTION("cpufreq driver for Pentium(TM) 4/Xeon(TM)"); MODULE_LICENSE("GPL"); |
1da177e4c
|
271 272 273 |
late_initcall(cpufreq_p4_init); module_exit(cpufreq_p4_exit); |