Blame view

drivers/cpufreq/p4-clockmod.c 7.27 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>
fa8031aef   Andi Kleen   cpufreq: Add supp...
33
  #include <asm/cpu_device_id.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
34
35
36
37
  
  #include "speedstep-lib.h"
  
  #define PFX	"p4-clockmod: "
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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   Viresh Kumar   cpufreq: Don't ch...
59
  	if ((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
  	}
  
  	return 0;
  }
  
  
  static struct cpufreq_frequency_table p4clockmod_table[] = {
7f4b04614   Viresh Kumar   cpufreq: create a...
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   Linus Torvalds   Linux-2.6.12-rc2
104
  };
9c0ebcf78   Viresh Kumar   cpufreq: Implemen...
105
  static int cpufreq_p4_target(struct cpufreq_policy *policy, unsigned int index)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
106
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
107
  	int i;
bbfebd665   Dave Jones   [CPUFREQ] checkpa...
108
109
  	/* run on each logical CPU,
  	 * see section 13.15.3 of IA32 Intel Architecture Software
32ee8c3e4   Dave Jones   [CPUFREQ] Lots of...
110
  	 * Developer's Manual, Volume 3
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
111
  	 */
835481d9b   Rusty Russell   cpumask: convert ...
112
  	for_each_cpu(i, policy->cpus)
9c0ebcf78   Viresh Kumar   cpufreq: Implemen...
113
  		cpufreq_p4_setdc(i, p4clockmod_table[index].driver_data);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
114

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
115
116
  	return 0;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
117
118
  static unsigned int cpufreq_p4_get_frequency(struct cpuinfo_x86 *c)
  {
4e74663c5   Dominik Brodowski   [CPUFREQ] p4-cloc...
119
120
  	if (c->x86 == 0x06) {
  		if (cpu_has(c, X86_FEATURE_EST))
853cee26e   Naga Chumbalkar   [CPUFREQ] p4-cloc...
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   Dave Jones   [CPUFREQ] checkpa...
124
125
126
  			       "scaling. You should use that instead of "
  			       "p4-clockmod, if possible.
  ");
4e74663c5   Dominik Brodowski   [CPUFREQ] p4-cloc...
127
128
129
  		switch (c->x86_model) {
  		case 0x0E: /* Core */
  		case 0x0F: /* Core Duo */
8529154ec   Herton Ronaldo Krzesinski   [CPUFREQ] Add Cel...
130
  		case 0x16: /* Celeron Core */
431950377   Jarod Wilson   [CPUFREQ] add ato...
131
  		case 0x1C: /* Atom */
4e74663c5   Dominik Brodowski   [CPUFREQ] p4-cloc...
132
  			p4clockmod_driver.flags |= CPUFREQ_CONST_LOOPS;
bbfebd665   Dave Jones   [CPUFREQ] checkpa...
133
  			return speedstep_get_frequency(SPEEDSTEP_CPU_PCORE);
4e74663c5   Dominik Brodowski   [CPUFREQ] p4-cloc...
134
135
136
137
  		case 0x0D: /* Pentium M (Dothan) */
  			p4clockmod_driver.flags |= CPUFREQ_CONST_LOOPS;
  			/* fall through */
  		case 0x09: /* Pentium M (Banias) */
bbfebd665   Dave Jones   [CPUFREQ] checkpa...
138
  			return speedstep_get_frequency(SPEEDSTEP_CPU_PM);
4e74663c5   Dominik Brodowski   [CPUFREQ] p4-cloc...
139
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
140
  	}
9d1f44ee2   Dave Jones   [CPUFREQ] Remove ...
141
  	if (c->x86 != 0xF)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
142
  		return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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   Dave Jones   [CPUFREQ] checkpa...
147
  	if (speedstep_detect_processor() == SPEEDSTEP_CPU_P4M) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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   Dave Jones   [CPUFREQ] checkpa...
154
  		return speedstep_get_frequency(SPEEDSTEP_CPU_P4M);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
155
  	}
bbfebd665   Dave Jones   [CPUFREQ] checkpa...
156
  	return speedstep_get_frequency(SPEEDSTEP_CPU_P4D);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
157
  }
32ee8c3e4   Dave Jones   [CPUFREQ] Lots of...
158

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
159
160
161
  
  static int cpufreq_p4_cpu_init(struct cpufreq_policy *policy)
  {
92cb7612a   Mike Travis   x86: convert cpui...
162
  	struct cpuinfo_x86 *c = &cpu_data(policy->cpu);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
163
164
165
166
  	int cpuid = 0;
  	unsigned int i;
  
  #ifdef CONFIG_SMP
7ad728f98   Rusty Russell   cpumask: x86: con...
167
  	cpumask_copy(policy->cpus, cpu_sibling_mask(policy->cpu));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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   Dominik Brodowski   [CPUFREQ] use dyn...
178
179
  		pr_debug("has errata -- disabling low frequencies
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
180
  	}
32ee8c3e4   Dave Jones   [CPUFREQ] Lots of...
181

199785eac   Matthias-Christian Ott   [CPUFREQ] p4-cloc...
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   Linus Torvalds   Linux-2.6.12-rc2
188
189
190
191
192
193
  	/* get max frequency */
  	stock_freq = cpufreq_p4_get_frequency(c);
  	if (!stock_freq)
  		return -EINVAL;
  
  	/* table init */
bbfebd665   Dave Jones   [CPUFREQ] checkpa...
194
195
  	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
196
197
198
199
  			p4clockmod_table[i].frequency = CPUFREQ_ENTRY_INVALID;
  		else
  			p4clockmod_table[i].frequency = (stock_freq * i)/8;
  	}
32ee8c3e4   Dave Jones   [CPUFREQ] Lots of...
200

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
201
  	/* cpuinfo and default policy values */
36e8abf3e   Dave Jones   [CPUFREQ] Prevent...
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   Linus Torvalds   Linux-2.6.12-rc2
206

719ffe495   Viresh Kumar   cpufreq: p4-clock...
207
  	return cpufreq_table_validate_and_show(policy, &p4clockmod_table[0]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
208
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
209
210
  static unsigned int cpufreq_p4_get(unsigned int cpu)
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
211
  	u32 l, h;
551948bc4   Alexey Dobriyan   [CPUFREQ] p4-cloc...
212
  	rdmsr_on_cpu(cpu, MSR_IA32_THERM_CONTROL, &l, &h);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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   Dave Jones   [CPUFREQ] checkpa...
221
  		return stock_freq * l / 8;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
222
223
224
  
  	return stock_freq;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
225
  static struct cpufreq_driver p4clockmod_driver = {
522f70cef   Viresh Kumar   cpufreq: p4-clock...
226
  	.verify		= cpufreq_generic_frequency_table_verify,
9c0ebcf78   Viresh Kumar   cpufreq: Implemen...
227
  	.target_index	= cpufreq_p4_target,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
228
  	.init		= cpufreq_p4_cpu_init,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
229
230
  	.get		= cpufreq_p4_get,
  	.name		= "p4-clockmod",
522f70cef   Viresh Kumar   cpufreq: p4-clock...
231
  	.attr		= cpufreq_generic_attr,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
232
  };
fa8031aef   Andi Kleen   cpufreq: Add supp...
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   Linus Torvalds   Linux-2.6.12-rc2
242
243
  
  static int __init cpufreq_p4_init(void)
32ee8c3e4   Dave Jones   [CPUFREQ] Lots of...
244
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
245
246
247
  	int ret;
  
  	/*
32ee8c3e4   Dave Jones   [CPUFREQ] Lots of...
248
  	 * THERM_CONTROL is architectural for IA32 now, so
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
249
250
  	 * we can rely on the capability checks
  	 */
fa8031aef   Andi Kleen   cpufreq: Add supp...
251
  	if (!x86_match_cpu(cpufreq_p4_id) || !boot_cpu_has(X86_FEATURE_ACPI))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
252
253
254
255
  		return -ENODEV;
  
  	ret = cpufreq_register_driver(&p4clockmod_driver);
  	if (!ret)
bbfebd665   Dave Jones   [CPUFREQ] checkpa...
256
257
258
  		printk(KERN_INFO PFX "P4/Xeon(TM) CPU On-Demand Clock "
  				"Modulation available
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
259

bbfebd665   Dave Jones   [CPUFREQ] checkpa...
260
  	return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
261
262
263
264
265
266
267
  }
  
  
  static void __exit cpufreq_p4_exit(void)
  {
  	cpufreq_unregister_driver(&p4clockmod_driver);
  }
bbfebd665   Dave Jones   [CPUFREQ] checkpa...
268
269
270
  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
271
272
273
  
  late_initcall(cpufreq_p4_init);
  module_exit(cpufreq_p4_exit);