Blame view

drivers/cpufreq/p4-clockmod.c 8.19 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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   Dave Jones   [CPUFREQ] Lots of...
17
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
18
19
20
21
   *	Date		Errata			Description
   *	20020525	N44, O17	12.5% or 25% DC causes lockup
   *
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
22
  #include <linux/kernel.h>
32ee8c3e4   Dave Jones   [CPUFREQ] Lots of...
23
  #include <linux/module.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
24
25
26
  #include <linux/init.h>
  #include <linux/smp.h>
  #include <linux/cpufreq.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
27
  #include <linux/cpumask.h>
bbfebd665   Dave Jones   [CPUFREQ] checkpa...
28
  #include <linux/timex.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
29

32ee8c3e4   Dave Jones   [CPUFREQ] Lots of...
30
  #include <asm/processor.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
31
  #include <asm/msr.h>
199785eac   Matthias-Christian Ott   [CPUFREQ] p4-cloc...
32
  #include <asm/timer.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
33
34
35
36
  
  #include "speedstep-lib.h"
  
  #define PFX	"p4-clockmod: "
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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   Dave Jones   [CPUFREQ] checkpa...
58
59
  	if (!cpu_online(cpu) ||
  	    (newstate > DC_DISABLE) || (newstate == DC_RESV))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
60
  		return -EINVAL;
551948bc4   Alexey Dobriyan   [CPUFREQ] p4-cloc...
61
  	rdmsr_on_cpu(cpu, MSR_IA32_THERM_STATUS, &l, &h);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
62
63
  
  	if (l & 0x01)
2d06d8c49   Dominik Brodowski   [CPUFREQ] use dyn...
64
65
  		pr_debug("CPU#%d currently thermal throttled
  ", cpu);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
66

bbfebd665   Dave Jones   [CPUFREQ] checkpa...
67
68
  	if (has_N44_O17_errata[cpu] &&
  	    (newstate == DC_25PT || newstate == DC_DFLT))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
69
  		newstate = DC_38PT;
551948bc4   Alexey Dobriyan   [CPUFREQ] p4-cloc...
70
  	rdmsr_on_cpu(cpu, MSR_IA32_THERM_CONTROL, &l, &h);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
71
  	if (newstate == DC_DISABLE) {
2d06d8c49   Dominik Brodowski   [CPUFREQ] use dyn...
72
73
  		pr_debug("CPU#%d disabling modulation
  ", cpu);
551948bc4   Alexey Dobriyan   [CPUFREQ] p4-cloc...
74
  		wrmsr_on_cpu(cpu, MSR_IA32_THERM_CONTROL, l & ~(1<<4), h);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
75
  	} else {
2d06d8c49   Dominik Brodowski   [CPUFREQ] use dyn...
76
77
  		pr_debug("CPU#%d setting duty cycle to %d%%
  ",
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
78
  			cpu, ((125 * newstate) / 10));
32ee8c3e4   Dave Jones   [CPUFREQ] Lots of...
79
  		/* bits 63 - 5	: reserved
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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   Alexey Dobriyan   [CPUFREQ] p4-cloc...
86
  		wrmsr_on_cpu(cpu, MSR_IA32_THERM_CONTROL, l, h);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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   Linus Torvalds   Linux-2.6.12-rc2
113
  	int i;
bbfebd665   Dave Jones   [CPUFREQ] checkpa...
114
115
  	if (cpufreq_frequency_table_target(policy, &p4clockmod_table[0],
  				target_freq, relation, &newstate))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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   Rusty Russell   cpumask: convert ...
125
  	for_each_cpu(i, policy->cpus) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
126
127
128
  		freqs.cpu = i;
  		cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
  	}
bbfebd665   Dave Jones   [CPUFREQ] checkpa...
129
130
  	/* run on each logical CPU,
  	 * see section 13.15.3 of IA32 Intel Architecture Software
32ee8c3e4   Dave Jones   [CPUFREQ] Lots of...
131
  	 * Developer's Manual, Volume 3
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
132
  	 */
835481d9b   Rusty Russell   cpumask: convert ...
133
  	for_each_cpu(i, policy->cpus)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
134
  		cpufreq_p4_setdc(i, p4clockmod_table[newstate].index);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
135
136
  
  	/* notifiers */
835481d9b   Rusty Russell   cpumask: convert ...
137
  	for_each_cpu(i, policy->cpus) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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   Dominik Brodowski   [CPUFREQ] p4-cloc...
154
155
  	if (c->x86 == 0x06) {
  		if (cpu_has(c, X86_FEATURE_EST))
853cee26e   Naga Chumbalkar   [CPUFREQ] p4-cloc...
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   Dave Jones   [CPUFREQ] checkpa...
159
160
161
  			       "scaling. You should use that instead of "
  			       "p4-clockmod, if possible.
  ");
4e74663c5   Dominik Brodowski   [CPUFREQ] p4-cloc...
162
163
164
  		switch (c->x86_model) {
  		case 0x0E: /* Core */
  		case 0x0F: /* Core Duo */
8529154ec   Herton Ronaldo Krzesinski   [CPUFREQ] Add Cel...
165
  		case 0x16: /* Celeron Core */
431950377   Jarod Wilson   [CPUFREQ] add ato...
166
  		case 0x1C: /* Atom */
4e74663c5   Dominik Brodowski   [CPUFREQ] p4-cloc...
167
  			p4clockmod_driver.flags |= CPUFREQ_CONST_LOOPS;
bbfebd665   Dave Jones   [CPUFREQ] checkpa...
168
  			return speedstep_get_frequency(SPEEDSTEP_CPU_PCORE);
4e74663c5   Dominik Brodowski   [CPUFREQ] p4-cloc...
169
170
171
172
  		case 0x0D: /* Pentium M (Dothan) */
  			p4clockmod_driver.flags |= CPUFREQ_CONST_LOOPS;
  			/* fall through */
  		case 0x09: /* Pentium M (Banias) */
bbfebd665   Dave Jones   [CPUFREQ] checkpa...
173
  			return speedstep_get_frequency(SPEEDSTEP_CPU_PM);
4e74663c5   Dominik Brodowski   [CPUFREQ] p4-cloc...
174
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
175
  	}
9d1f44ee2   Dave Jones   [CPUFREQ] Remove ...
176
  	if (c->x86 != 0xF)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
177
  		return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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   Dave Jones   [CPUFREQ] checkpa...
182
  	if (speedstep_detect_processor() == SPEEDSTEP_CPU_P4M) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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   Dave Jones   [CPUFREQ] checkpa...
189
  		return speedstep_get_frequency(SPEEDSTEP_CPU_P4M);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
190
  	}
bbfebd665   Dave Jones   [CPUFREQ] checkpa...
191
  	return speedstep_get_frequency(SPEEDSTEP_CPU_P4D);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
192
  }
32ee8c3e4   Dave Jones   [CPUFREQ] Lots of...
193

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
194
195
196
  
  static int cpufreq_p4_cpu_init(struct cpufreq_policy *policy)
  {
92cb7612a   Mike Travis   x86: convert cpui...
197
  	struct cpuinfo_x86 *c = &cpu_data(policy->cpu);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
198
199
200
201
  	int cpuid = 0;
  	unsigned int i;
  
  #ifdef CONFIG_SMP
7ad728f98   Rusty Russell   cpumask: x86: con...
202
  	cpumask_copy(policy->cpus, cpu_sibling_mask(policy->cpu));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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   Dominik Brodowski   [CPUFREQ] use dyn...
213
214
  		pr_debug("has errata -- disabling low frequencies
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
215
  	}
32ee8c3e4   Dave Jones   [CPUFREQ] Lots of...
216

199785eac   Matthias-Christian Ott   [CPUFREQ] p4-cloc...
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   Linus Torvalds   Linux-2.6.12-rc2
223
224
225
226
227
228
  	/* get max frequency */
  	stock_freq = cpufreq_p4_get_frequency(c);
  	if (!stock_freq)
  		return -EINVAL;
  
  	/* table init */
bbfebd665   Dave Jones   [CPUFREQ] checkpa...
229
230
  	for (i = 1; (p4clockmod_table[i].frequency != CPUFREQ_TABLE_END); i++) {
  		if ((i < 2) && (has_N44_O17_errata[policy->cpu]))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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   Dave Jones   [CPUFREQ] Lots of...
236

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
237
  	/* cpuinfo and default policy values */
36e8abf3e   Dave Jones   [CPUFREQ] Prevent...
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   Linus Torvalds   Linux-2.6.12-rc2
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   Dave Jones   [CPUFREQ] Lots of...
250
  	cpufreq_frequency_table_put_attr(policy->cpu);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
251
252
253
254
255
  	return 0;
  }
  
  static unsigned int cpufreq_p4_get(unsigned int cpu)
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
256
  	u32 l, h;
551948bc4   Alexey Dobriyan   [CPUFREQ] p4-cloc...
257
  	rdmsr_on_cpu(cpu, MSR_IA32_THERM_CONTROL, &l, &h);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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   Dave Jones   [CPUFREQ] checkpa...
266
  		return stock_freq * l / 8;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
267
268
269
  
  	return stock_freq;
  }
bbfebd665   Dave Jones   [CPUFREQ] checkpa...
270
  static struct freq_attr *p4clockmod_attr[] = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
271
272
273
274
275
  	&cpufreq_freq_attr_scaling_available_freqs,
  	NULL,
  };
  
  static struct cpufreq_driver p4clockmod_driver = {
32ee8c3e4   Dave Jones   [CPUFREQ] Lots of...
276
  	.verify		= cpufreq_p4_verify,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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   Dave Jones   [CPUFREQ] Lots of...
288
  {
92cb7612a   Mike Travis   x86: convert cpui...
289
  	struct cpuinfo_x86 *c = &cpu_data(0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
290
291
292
  	int ret;
  
  	/*
32ee8c3e4   Dave Jones   [CPUFREQ] Lots of...
293
  	 * THERM_CONTROL is architectural for IA32 now, so
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
294
295
296
297
  	 * we can rely on the capability checks
  	 */
  	if (c->x86_vendor != X86_VENDOR_INTEL)
  		return -ENODEV;
8ce116e59   Ingo Molnar   x86: clean up cpu...
298
299
  	if (!test_cpu_cap(c, X86_FEATURE_ACPI) ||
  				!test_cpu_cap(c, X86_FEATURE_ACC))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
300
301
302
303
  		return -ENODEV;
  
  	ret = cpufreq_register_driver(&p4clockmod_driver);
  	if (!ret)
bbfebd665   Dave Jones   [CPUFREQ] checkpa...
304
305
306
  		printk(KERN_INFO PFX "P4/Xeon(TM) CPU On-Demand Clock "
  				"Modulation available
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
307

bbfebd665   Dave Jones   [CPUFREQ] checkpa...
308
  	return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
309
310
311
312
313
314
315
  }
  
  
  static void __exit cpufreq_p4_exit(void)
  {
  	cpufreq_unregister_driver(&p4clockmod_driver);
  }
bbfebd665   Dave Jones   [CPUFREQ] checkpa...
316
317
318
  MODULE_AUTHOR("Zwane Mwaikambo <zwane@commfireservices.com>");
  MODULE_DESCRIPTION("cpufreq driver for Pentium(TM) 4/Xeon(TM)");
  MODULE_LICENSE("GPL");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
319
320
321
  
  late_initcall(cpufreq_p4_init);
  module_exit(cpufreq_p4_exit);