Blame view

drivers/cpufreq/speedstep-centrino.c 15.2 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
  /*
   * cpufreq driver for Enhanced SpeedStep, as found in Intel's Pentium
   * M (part of the Centrino chipset).
   *
491b07c98   Jeremy Fitzhardinge   [PATCH] redirect ...
5
6
7
   * Since the original Pentium M, most new Intel CPUs support Enhanced
   * SpeedStep.
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
8
9
10
11
12
13
   * Despite the "SpeedStep" in the name, this is almost entirely unlike
   * traditional SpeedStep.
   *
   * Modelled on speedstep.c
   *
   * Copyright (C) 2003 Jeremy Fitzhardinge <jeremy@goop.org>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
14
15
16
17
18
19
   */
  
  #include <linux/kernel.h>
  #include <linux/module.h>
  #include <linux/init.h>
  #include <linux/cpufreq.h>
4e57b6817   Tim Schmielau   [PATCH] fix missi...
20
  #include <linux/sched.h>	/* current */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
21
22
  #include <linux/delay.h>
  #include <linux/compiler.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
23
  #include <linux/gfp.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
24

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
25
26
27
  #include <asm/msr.h>
  #include <asm/processor.h>
  #include <asm/cpufeature.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
28
  #define PFX		"speedstep-centrino: "
8d5922572   Németh Márton   [CPUFREQ] correct...
29
  #define MAINTAINER	"cpufreq@vger.kernel.org"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
30

8b9c6671f   Gary Hade   [CPUFREQ] speedst...
31
  #define INTEL_MSR_RANGE	(0xffff)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
32
33
34
35
36
37
38
39
40
41
42
43
44
  
  struct cpu_id
  {
  	__u8	x86;            /* CPU family */
  	__u8	x86_model;	/* model */
  	__u8	x86_mask;	/* stepping */
  };
  
  enum {
  	CPU_BANIAS,
  	CPU_DOTHAN_A1,
  	CPU_DOTHAN_A2,
  	CPU_DOTHAN_B0,
8282864a9   Dave Jones   [CPUFREQ] speedst...
45
46
  	CPU_MP4HT_D0,
  	CPU_MP4HT_E0,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
47
48
49
50
51
52
53
  };
  
  static const struct cpu_id cpu_ids[] = {
  	[CPU_BANIAS]	= { 6,  9, 5 },
  	[CPU_DOTHAN_A1]	= { 6, 13, 1 },
  	[CPU_DOTHAN_A2]	= { 6, 13, 2 },
  	[CPU_DOTHAN_B0]	= { 6, 13, 6 },
8282864a9   Dave Jones   [CPUFREQ] speedst...
54
55
  	[CPU_MP4HT_D0]	= {15,  3, 4 },
  	[CPU_MP4HT_E0]	= {15,  4, 1 },
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
56
  };
38e548ee1   Tobias Klauser   [PATCH] arch/i386...
57
  #define N_IDS	ARRAY_SIZE(cpu_ids)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
58
59
60
61
62
63
64
65
66
  
  struct cpu_model
  {
  	const struct cpu_id *cpu_id;
  	const char	*model_name;
  	unsigned	max_freq; /* max clock in kHz */
  
  	struct cpufreq_frequency_table *op_points; /* clock/voltage pairs */
  };
c4762aba0   Mike Travis   NR_CPUS: Replace ...
67
68
  static int centrino_verify_cpu_id(const struct cpuinfo_x86 *c,
  				  const struct cpu_id *x);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
69
70
  
  /* Operating points for current CPU */
c4762aba0   Mike Travis   NR_CPUS: Replace ...
71
72
  static DEFINE_PER_CPU(struct cpu_model *, centrino_model);
  static DEFINE_PER_CPU(const struct cpu_id *, centrino_cpu);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
73
74
75
76
77
78
79
80
81
82
83
84
85
86
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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
  
  static struct cpufreq_driver centrino_driver;
  
  #ifdef CONFIG_X86_SPEEDSTEP_CENTRINO_TABLE
  
  /* Computes the correct form for IA32_PERF_CTL MSR for a particular
     frequency/voltage operating point; frequency in MHz, volts in mV.
     This is stored as "index" in the structure. */
  #define OP(mhz, mv)							\
  	{								\
  		.frequency = (mhz) * 1000,				\
  		.index = (((mhz)/100) << 8) | ((mv - 700) / 16)		\
  	}
  
  /*
   * These voltage tables were derived from the Intel Pentium M
   * datasheet, document 25261202.pdf, Table 5.  I have verified they
   * are consistent with my IBM ThinkPad X31, which has a 1.3GHz Pentium
   * M.
   */
  
  /* Ultra Low Voltage Intel Pentium M processor 900MHz (Banias) */
  static struct cpufreq_frequency_table banias_900[] =
  {
  	OP(600,  844),
  	OP(800,  988),
  	OP(900, 1004),
  	{ .frequency = CPUFREQ_TABLE_END }
  };
  
  /* Ultra Low Voltage Intel Pentium M processor 1000MHz (Banias) */
  static struct cpufreq_frequency_table banias_1000[] =
  {
  	OP(600,   844),
  	OP(800,   972),
  	OP(900,   988),
  	OP(1000, 1004),
  	{ .frequency = CPUFREQ_TABLE_END }
  };
  
  /* Low Voltage Intel Pentium M processor 1.10GHz (Banias) */
  static struct cpufreq_frequency_table banias_1100[] =
  {
  	OP( 600,  956),
  	OP( 800, 1020),
  	OP( 900, 1100),
  	OP(1000, 1164),
  	OP(1100, 1180),
  	{ .frequency = CPUFREQ_TABLE_END }
  };
  
  
  /* Low Voltage Intel Pentium M processor 1.20GHz (Banias) */
  static struct cpufreq_frequency_table banias_1200[] =
  {
  	OP( 600,  956),
  	OP( 800, 1004),
  	OP( 900, 1020),
  	OP(1000, 1100),
  	OP(1100, 1164),
  	OP(1200, 1180),
  	{ .frequency = CPUFREQ_TABLE_END }
  };
  
  /* Intel Pentium M processor 1.30GHz (Banias) */
  static struct cpufreq_frequency_table banias_1300[] =
  {
  	OP( 600,  956),
  	OP( 800, 1260),
  	OP(1000, 1292),
  	OP(1200, 1356),
  	OP(1300, 1388),
  	{ .frequency = CPUFREQ_TABLE_END }
  };
  
  /* Intel Pentium M processor 1.40GHz (Banias) */
  static struct cpufreq_frequency_table banias_1400[] =
  {
  	OP( 600,  956),
  	OP( 800, 1180),
  	OP(1000, 1308),
  	OP(1200, 1436),
  	OP(1400, 1484),
  	{ .frequency = CPUFREQ_TABLE_END }
  };
  
  /* Intel Pentium M processor 1.50GHz (Banias) */
  static struct cpufreq_frequency_table banias_1500[] =
  {
  	OP( 600,  956),
  	OP( 800, 1116),
  	OP(1000, 1228),
  	OP(1200, 1356),
  	OP(1400, 1452),
  	OP(1500, 1484),
  	{ .frequency = CPUFREQ_TABLE_END }
  };
  
  /* Intel Pentium M processor 1.60GHz (Banias) */
  static struct cpufreq_frequency_table banias_1600[] =
  {
  	OP( 600,  956),
  	OP( 800, 1036),
  	OP(1000, 1164),
  	OP(1200, 1276),
  	OP(1400, 1420),
  	OP(1600, 1484),
  	{ .frequency = CPUFREQ_TABLE_END }
  };
  
  /* Intel Pentium M processor 1.70GHz (Banias) */
  static struct cpufreq_frequency_table banias_1700[] =
  {
  	OP( 600,  956),
  	OP( 800, 1004),
  	OP(1000, 1116),
  	OP(1200, 1228),
  	OP(1400, 1308),
  	OP(1700, 1484),
  	{ .frequency = CPUFREQ_TABLE_END }
  };
  #undef OP
  
  #define _BANIAS(cpuid, max, name)	\
  {	.cpu_id		= cpuid,	\
  	.model_name	= "Intel(R) Pentium(R) M processor " name "MHz", \
  	.max_freq	= (max)*1000,	\
  	.op_points	= banias_##max,	\
  }
  #define BANIAS(max)	_BANIAS(&cpu_ids[CPU_BANIAS], max, #max)
  
  /* CPU models, their operating frequency range, and freq/voltage
     operating points */
  static struct cpu_model models[] =
  {
  	_BANIAS(&cpu_ids[CPU_BANIAS], 900, " 900"),
  	BANIAS(1000),
  	BANIAS(1100),
  	BANIAS(1200),
  	BANIAS(1300),
  	BANIAS(1400),
  	BANIAS(1500),
  	BANIAS(1600),
  	BANIAS(1700),
  
  	/* NULL model_name is a wildcard */
  	{ &cpu_ids[CPU_DOTHAN_A1], NULL, 0, NULL },
  	{ &cpu_ids[CPU_DOTHAN_A2], NULL, 0, NULL },
  	{ &cpu_ids[CPU_DOTHAN_B0], NULL, 0, NULL },
8282864a9   Dave Jones   [CPUFREQ] speedst...
222
223
  	{ &cpu_ids[CPU_MP4HT_D0], NULL, 0, NULL },
  	{ &cpu_ids[CPU_MP4HT_E0], NULL, 0, NULL },
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
224
225
226
227
228
229
230
231
  
  	{ NULL, }
  };
  #undef _BANIAS
  #undef BANIAS
  
  static int centrino_cpu_init_table(struct cpufreq_policy *policy)
  {
92cb7612a   Mike Travis   x86: convert cpui...
232
  	struct cpuinfo_x86 *cpu = &cpu_data(policy->cpu);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
233
234
235
236
237
238
239
240
241
242
  	struct cpu_model *model;
  
  	for(model = models; model->cpu_id != NULL; model++)
  		if (centrino_verify_cpu_id(cpu, model->cpu_id) &&
  		    (model->model_name == NULL ||
  		     strcmp(cpu->x86_model_id, model->model_name) == 0))
  			break;
  
  	if (model->cpu_id == NULL) {
  		/* No match at all */
2d06d8c49   Dominik Brodowski   [CPUFREQ] use dyn...
243
  		pr_debug("no support for CPU model \"%s\": "
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
244
245
246
247
248
249
250
251
  		       "send /proc/cpuinfo to " MAINTAINER "
  ",
  		       cpu->x86_model_id);
  		return -ENOENT;
  	}
  
  	if (model->op_points == NULL) {
  		/* Matched a non-match */
2d06d8c49   Dominik Brodowski   [CPUFREQ] use dyn...
252
253
  		pr_debug("no table support for CPU model \"%s\"
  ",
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
254
  		       cpu->x86_model_id);
2d06d8c49   Dominik Brodowski   [CPUFREQ] use dyn...
255
256
  		pr_debug("try using the acpi-cpufreq driver
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
257
258
  		return -ENOENT;
  	}
c4762aba0   Mike Travis   NR_CPUS: Replace ...
259
  	per_cpu(centrino_model, policy->cpu) = model;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
260

2d06d8c49   Dominik Brodowski   [CPUFREQ] use dyn...
261
262
  	pr_debug("found \"%s\": max frequency: %dkHz
  ",
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
263
264
265
266
267
268
  	       model->model_name, model->max_freq);
  
  	return 0;
  }
  
  #else
c4762aba0   Mike Travis   NR_CPUS: Replace ...
269
270
271
272
  static inline int centrino_cpu_init_table(struct cpufreq_policy *policy)
  {
  	return -ENODEV;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
273
  #endif /* CONFIG_X86_SPEEDSTEP_CENTRINO_TABLE */
c4762aba0   Mike Travis   NR_CPUS: Replace ...
274
275
  static int centrino_verify_cpu_id(const struct cpuinfo_x86 *c,
  				  const struct cpu_id *x)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
  {
  	if ((c->x86 == x->x86) &&
  	    (c->x86_model == x->x86_model) &&
  	    (c->x86_mask == x->x86_mask))
  		return 1;
  	return 0;
  }
  
  /* To be called only after centrino_model is initialized */
  static unsigned extract_clock(unsigned msr, unsigned int cpu, int failsafe)
  {
  	int i;
  
  	/*
  	 * Extract clock in kHz from PERF_CTL value
  	 * for centrino, as some DSDTs are buggy.
  	 * Ideally, this can be done using the acpi_data structure.
  	 */
c4762aba0   Mike Travis   NR_CPUS: Replace ...
294
295
296
  	if ((per_cpu(centrino_cpu, cpu) == &cpu_ids[CPU_BANIAS]) ||
  	    (per_cpu(centrino_cpu, cpu) == &cpu_ids[CPU_DOTHAN_A1]) ||
  	    (per_cpu(centrino_cpu, cpu) == &cpu_ids[CPU_DOTHAN_B0])) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
297
298
299
  		msr = (msr >> 8) & 0xff;
  		return msr * 100000;
  	}
c4762aba0   Mike Travis   NR_CPUS: Replace ...
300
301
  	if ((!per_cpu(centrino_model, cpu)) ||
  	    (!per_cpu(centrino_model, cpu)->op_points))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
302
303
304
  		return 0;
  
  	msr &= 0xffff;
c4762aba0   Mike Travis   NR_CPUS: Replace ...
305
306
307
308
309
310
311
  	for (i = 0;
  		per_cpu(centrino_model, cpu)->op_points[i].frequency
  							!= CPUFREQ_TABLE_END;
  	     i++) {
  		if (msr == per_cpu(centrino_model, cpu)->op_points[i].index)
  			return per_cpu(centrino_model, cpu)->
  							op_points[i].frequency;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
312
313
  	}
  	if (failsafe)
c4762aba0   Mike Travis   NR_CPUS: Replace ...
314
  		return per_cpu(centrino_model, cpu)->op_points[i-1].frequency;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
315
316
317
318
319
320
321
322
323
  	else
  		return 0;
  }
  
  /* Return the current CPU frequency in kHz */
  static unsigned int get_cur_freq(unsigned int cpu)
  {
  	unsigned l, h;
  	unsigned clock_freq;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
324

e3f996c26   Rusty Russell   [CPUFREQ] cpumask...
325
  	rdmsr_on_cpu(cpu, MSR_IA32_PERF_STATUS, &l, &h);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
326
327
328
329
330
331
332
333
334
  	clock_freq = extract_clock(l, cpu, 0);
  
  	if (unlikely(clock_freq == 0)) {
  		/*
  		 * On some CPUs, we can see transient MSR values (which are
  		 * not present in _PSS), while CPU is doing some automatic
  		 * P-state transition (like TM2). Get the last freq set 
  		 * in PERF_CTL.
  		 */
e3f996c26   Rusty Russell   [CPUFREQ] cpumask...
335
  		rdmsr_on_cpu(cpu, MSR_IA32_PERF_CTL, &l, &h);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
336
337
  		clock_freq = extract_clock(l, cpu, 1);
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
338
339
  	return clock_freq;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
340
341
  static int centrino_cpu_init(struct cpufreq_policy *policy)
  {
92cb7612a   Mike Travis   x86: convert cpui...
342
  	struct cpuinfo_x86 *cpu = &cpu_data(policy->cpu);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
343
344
345
346
347
348
  	unsigned freq;
  	unsigned l, h;
  	int ret;
  	int i;
  
  	/* Only Intel makes Enhanced Speedstep-capable CPUs */
c4762aba0   Mike Travis   NR_CPUS: Replace ...
349
350
  	if (cpu->x86_vendor != X86_VENDOR_INTEL ||
  	    !cpu_has(cpu, X86_FEATURE_EST))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
351
  		return -ENODEV;
8ad5496d2   Dave Jones   [CPUFREQ] Remove ...
352
  	if (cpu_has(cpu, X86_FEATURE_CONSTANT_TSC))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
353
  		centrino_driver.flags |= CPUFREQ_CONST_LOOPS;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
354

68485695e   Adrian Bunk   [CPUFREQ] the ove...
355
356
  	if (policy->cpu != 0)
  		return -ENODEV;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
357

68485695e   Adrian Bunk   [CPUFREQ] the ove...
358
359
360
  	for (i = 0; i < N_IDS; i++)
  		if (centrino_verify_cpu_id(cpu, &cpu_ids[i]))
  			break;
f914be79a   Venkatesh Pallipadi   [CPUFREQ] speedst...
361

68485695e   Adrian Bunk   [CPUFREQ] the ove...
362
  	if (i != N_IDS)
c4762aba0   Mike Travis   NR_CPUS: Replace ...
363
  		per_cpu(centrino_cpu, policy->cpu) = &cpu_ids[i];
f914be79a   Venkatesh Pallipadi   [CPUFREQ] speedst...
364

c4762aba0   Mike Travis   NR_CPUS: Replace ...
365
  	if (!per_cpu(centrino_cpu, policy->cpu)) {
2d06d8c49   Dominik Brodowski   [CPUFREQ] use dyn...
366
  		pr_debug("found unsupported CPU with "
68485695e   Adrian Bunk   [CPUFREQ] the ove...
367
368
369
370
371
  		"Enhanced SpeedStep: send /proc/cpuinfo to "
  		MAINTAINER "
  ");
  		return -ENODEV;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
372

68485695e   Adrian Bunk   [CPUFREQ] the ove...
373
374
  	if (centrino_cpu_init_table(policy)) {
  		return -ENODEV;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
375
376
377
378
379
  	}
  
  	/* Check to see if Enhanced SpeedStep is enabled, and try to
  	   enable it if not. */
  	rdmsr(MSR_IA32_MISC_ENABLE, l, h);
ecab22aa6   Vegard Nossum   x86: use symbolic...
380
381
  	if (!(l & MSR_IA32_MISC_ENABLE_ENHANCED_SPEEDSTEP)) {
  		l |= MSR_IA32_MISC_ENABLE_ENHANCED_SPEEDSTEP;
2d06d8c49   Dominik Brodowski   [CPUFREQ] use dyn...
382
383
  		pr_debug("trying to enable Enhanced SpeedStep (%x)
  ", l);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
384
385
386
387
  		wrmsr(MSR_IA32_MISC_ENABLE, l, h);
  
  		/* check to see if it stuck */
  		rdmsr(MSR_IA32_MISC_ENABLE, l, h);
ecab22aa6   Vegard Nossum   x86: use symbolic...
388
  		if (!(l & MSR_IA32_MISC_ENABLE_ENHANCED_SPEEDSTEP)) {
c4762aba0   Mike Travis   NR_CPUS: Replace ...
389
390
391
  			printk(KERN_INFO PFX
  				"couldn't enable Enhanced SpeedStep
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
392
393
394
395
396
  			return -ENODEV;
  		}
  	}
  
  	freq = get_cur_freq(policy->cpu);
c4762aba0   Mike Travis   NR_CPUS: Replace ...
397
398
  	policy->cpuinfo.transition_latency = 10000;
  						/* 10uS transition latency */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
399
  	policy->cur = freq;
2d06d8c49   Dominik Brodowski   [CPUFREQ] use dyn...
400
401
  	pr_debug("centrino_cpu_init: cur=%dkHz
  ", policy->cur);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
402

c4762aba0   Mike Travis   NR_CPUS: Replace ...
403
404
  	ret = cpufreq_frequency_table_cpuinfo(policy,
  		per_cpu(centrino_model, policy->cpu)->op_points);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
405
406
  	if (ret)
  		return (ret);
c4762aba0   Mike Travis   NR_CPUS: Replace ...
407
408
  	cpufreq_frequency_table_get_attr(
  		per_cpu(centrino_model, policy->cpu)->op_points, policy->cpu);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
409
410
411
412
413
414
415
  
  	return 0;
  }
  
  static int centrino_cpu_exit(struct cpufreq_policy *policy)
  {
  	unsigned int cpu = policy->cpu;
c4762aba0   Mike Travis   NR_CPUS: Replace ...
416
  	if (!per_cpu(centrino_model, cpu))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
417
418
419
  		return -ENODEV;
  
  	cpufreq_frequency_table_put_attr(cpu);
c4762aba0   Mike Travis   NR_CPUS: Replace ...
420
  	per_cpu(centrino_model, cpu) = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
421
422
423
424
425
426
427
428
429
430
431
432
433
  
  	return 0;
  }
  
  /**
   * centrino_verify - verifies a new CPUFreq policy
   * @policy: new policy
   *
   * Limit must be within this model's frequency range at least one
   * border included.
   */
  static int centrino_verify (struct cpufreq_policy *policy)
  {
c4762aba0   Mike Travis   NR_CPUS: Replace ...
434
435
  	return cpufreq_frequency_table_verify(policy,
  			per_cpu(centrino_model, policy->cpu)->op_points);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
436
437
438
439
440
441
  }
  
  /**
   * centrino_setpolicy - set a new CPUFreq policy
   * @policy: new policy
   * @target_freq: the target frequency
c4762aba0   Mike Travis   NR_CPUS: Replace ...
442
443
   * @relation: how that frequency relates to achieved frequency
   *	(CPUFREQ_RELATION_L or CPUFREQ_RELATION_H)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
444
445
446
447
448
449
450
451
   *
   * Sets a new CPUFreq policy.
   */
  static int centrino_target (struct cpufreq_policy *policy,
  			    unsigned int target_freq,
  			    unsigned int relation)
  {
  	unsigned int    newstate = 0;
c52851b60   Venkatesh Pallipadi   P-state software ...
452
  	unsigned int	msr, oldmsr = 0, h = 0, cpu = policy->cpu;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
453
  	struct cpufreq_freqs	freqs;
c52851b60   Venkatesh Pallipadi   P-state software ...
454
455
  	int			retval = 0;
  	unsigned int		j, k, first_cpu, tmp;
e3f996c26   Rusty Russell   [CPUFREQ] cpumask...
456
  	cpumask_var_t covered_cpus;
eb53fac5c   Mike Travis   cpumask: Use opti...
457

e3f996c26   Rusty Russell   [CPUFREQ] cpumask...
458
  	if (unlikely(!zalloc_cpumask_var(&covered_cpus, GFP_KERNEL)))
5cb0535f1   Rusty Russell   cpumask: replace ...
459
  		return -ENOMEM;
eb53fac5c   Mike Travis   cpumask: Use opti...
460

c4762aba0   Mike Travis   NR_CPUS: Replace ...
461
  	if (unlikely(per_cpu(centrino_model, cpu) == NULL)) {
eb53fac5c   Mike Travis   cpumask: Use opti...
462
463
464
  		retval = -ENODEV;
  		goto out;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
465

c52851b60   Venkatesh Pallipadi   P-state software ...
466
  	if (unlikely(cpufreq_frequency_table_target(policy,
c4762aba0   Mike Travis   NR_CPUS: Replace ...
467
  			per_cpu(centrino_model, cpu)->op_points,
c52851b60   Venkatesh Pallipadi   P-state software ...
468
469
470
  			target_freq,
  			relation,
  			&newstate))) {
eb53fac5c   Mike Travis   cpumask: Use opti...
471
472
  		retval = -EINVAL;
  		goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
473
  	}
c52851b60   Venkatesh Pallipadi   P-state software ...
474
  	first_cpu = 1;
835481d9b   Rusty Russell   cpumask: convert ...
475
  	for_each_cpu(j, policy->cpus) {
e3f996c26   Rusty Russell   [CPUFREQ] cpumask...
476
  		int good_cpu;
9963d1aad   Rusty Russell   [CPUFREQ] clean u...
477
478
479
480
  
  		/* cpufreq holds the hotplug lock, so we are safe here */
  		if (!cpu_online(j))
  			continue;
c52851b60   Venkatesh Pallipadi   P-state software ...
481
482
483
484
  		/*
  		 * Support for SMP systems.
  		 * Make sure we are running on CPU that wants to change freq
  		 */
c52851b60   Venkatesh Pallipadi   P-state software ...
485
  		if (policy->shared_type == CPUFREQ_SHARED_TYPE_ANY)
e3f996c26   Rusty Russell   [CPUFREQ] cpumask...
486
487
  			good_cpu = cpumask_any_and(policy->cpus,
  						   cpu_online_mask);
c52851b60   Venkatesh Pallipadi   P-state software ...
488
  		else
e3f996c26   Rusty Russell   [CPUFREQ] cpumask...
489
  			good_cpu = j;
c52851b60   Venkatesh Pallipadi   P-state software ...
490

e3f996c26   Rusty Russell   [CPUFREQ] cpumask...
491
  		if (good_cpu >= nr_cpu_ids) {
2d06d8c49   Dominik Brodowski   [CPUFREQ] use dyn...
492
493
  			pr_debug("couldn't limit to CPUs in this domain
  ");
c52851b60   Venkatesh Pallipadi   P-state software ...
494
495
496
  			retval = -EAGAIN;
  			if (first_cpu) {
  				/* We haven't started the transition yet. */
e3f996c26   Rusty Russell   [CPUFREQ] cpumask...
497
  				goto out;
c52851b60   Venkatesh Pallipadi   P-state software ...
498
499
500
  			}
  			break;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
501

c4762aba0   Mike Travis   NR_CPUS: Replace ...
502
  		msr = per_cpu(centrino_model, cpu)->op_points[newstate].index;
c52851b60   Venkatesh Pallipadi   P-state software ...
503
504
  
  		if (first_cpu) {
e3f996c26   Rusty Russell   [CPUFREQ] cpumask...
505
  			rdmsr_on_cpu(good_cpu, MSR_IA32_PERF_CTL, &oldmsr, &h);
c52851b60   Venkatesh Pallipadi   P-state software ...
506
  			if (msr == (oldmsr & 0xffff)) {
2d06d8c49   Dominik Brodowski   [CPUFREQ] use dyn...
507
  				pr_debug("no change needed - msr was and needs "
c52851b60   Venkatesh Pallipadi   P-state software ...
508
509
510
  					"to be %x
  ", oldmsr);
  				retval = 0;
e3f996c26   Rusty Russell   [CPUFREQ] cpumask...
511
  				goto out;
c52851b60   Venkatesh Pallipadi   P-state software ...
512
513
514
515
  			}
  
  			freqs.old = extract_clock(oldmsr, cpu, 0);
  			freqs.new = extract_clock(msr, cpu, 0);
2d06d8c49   Dominik Brodowski   [CPUFREQ] use dyn...
516
517
  			pr_debug("target=%dkHz old=%d new=%d msr=%04x
  ",
c52851b60   Venkatesh Pallipadi   P-state software ...
518
  				target_freq, freqs.old, freqs.new, msr);
835481d9b   Rusty Russell   cpumask: convert ...
519
  			for_each_cpu(k, policy->cpus) {
9963d1aad   Rusty Russell   [CPUFREQ] clean u...
520
521
  				if (!cpu_online(k))
  					continue;
c52851b60   Venkatesh Pallipadi   P-state software ...
522
523
524
525
526
527
528
529
530
531
532
  				freqs.cpu = k;
  				cpufreq_notify_transition(&freqs,
  					CPUFREQ_PRECHANGE);
  			}
  
  			first_cpu = 0;
  			/* all but 16 LSB are reserved, treat them with care */
  			oldmsr &= ~0xffff;
  			msr &= 0xffff;
  			oldmsr |= msr;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
533

e3f996c26   Rusty Russell   [CPUFREQ] cpumask...
534
535
  		wrmsr_on_cpu(good_cpu, MSR_IA32_PERF_CTL, oldmsr, h);
  		if (policy->shared_type == CPUFREQ_SHARED_TYPE_ANY)
c52851b60   Venkatesh Pallipadi   P-state software ...
536
  			break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
537

e3f996c26   Rusty Russell   [CPUFREQ] cpumask...
538
  		cpumask_set_cpu(j, covered_cpus);
c52851b60   Venkatesh Pallipadi   P-state software ...
539
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
540

835481d9b   Rusty Russell   cpumask: convert ...
541
  	for_each_cpu(k, policy->cpus) {
9963d1aad   Rusty Russell   [CPUFREQ] clean u...
542
543
  		if (!cpu_online(k))
  			continue;
c52851b60   Venkatesh Pallipadi   P-state software ...
544
545
546
  		freqs.cpu = k;
  		cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
547

c52851b60   Venkatesh Pallipadi   P-state software ...
548
549
550
551
552
553
554
  	if (unlikely(retval)) {
  		/*
  		 * We have failed halfway through the frequency change.
  		 * We have sent callbacks to policy->cpus and
  		 * MSRs have already been written on coverd_cpus.
  		 * Best effort undo..
  		 */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
555

e3f996c26   Rusty Russell   [CPUFREQ] cpumask...
556
557
  		for_each_cpu(j, covered_cpus)
  			wrmsr_on_cpu(j, MSR_IA32_PERF_CTL, oldmsr, h);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
558

c52851b60   Venkatesh Pallipadi   P-state software ...
559
560
561
  		tmp = freqs.new;
  		freqs.new = freqs.old;
  		freqs.old = tmp;
835481d9b   Rusty Russell   cpumask: convert ...
562
  		for_each_cpu(j, policy->cpus) {
9963d1aad   Rusty Russell   [CPUFREQ] clean u...
563
564
  			if (!cpu_online(j))
  				continue;
c52851b60   Venkatesh Pallipadi   P-state software ...
565
566
567
568
  			cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
  			cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
  		}
  	}
eb53fac5c   Mike Travis   cpumask: Use opti...
569
  	retval = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
570

eb53fac5c   Mike Travis   cpumask: Use opti...
571
  out:
5cb0535f1   Rusty Russell   cpumask: replace ...
572
  	free_cpumask_var(covered_cpus);
eb53fac5c   Mike Travis   cpumask: Use opti...
573
  	return retval;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
  }
  
  static struct freq_attr* centrino_attr[] = {
  	&cpufreq_freq_attr_scaling_available_freqs,
  	NULL,
  };
  
  static struct cpufreq_driver centrino_driver = {
  	.name		= "centrino", /* should be speedstep-centrino,
  					 but there's a 16 char limit */
  	.init		= centrino_cpu_init,
  	.exit		= centrino_cpu_exit,
  	.verify		= centrino_verify,
  	.target		= centrino_target,
  	.get		= get_cur_freq,
  	.attr           = centrino_attr,
  	.owner		= THIS_MODULE,
  };
  
  
  /**
   * centrino_init - initializes the Enhanced SpeedStep CPUFreq driver
   *
   * Initializes the Enhanced SpeedStep support. Returns -ENODEV on
   * unsupported devices, -ENOENT if there's no voltage table for this
   * particular CPU model, -EINVAL on problems during initiatization,
   * and zero on success.
   *
   * This is quite picky.  Not only does the CPU have to advertise the
   * "est" flag in the cpuid capability flags, we look for a specific
   * CPU model and stepping, and we need to have the exact model name in
   * our voltage tables.  That is, be paranoid about not releasing
   * someone's valuable magic smoke.
   */
  static int __init centrino_init(void)
  {
92cb7612a   Mike Travis   x86: convert cpui...
610
  	struct cpuinfo_x86 *cpu = &cpu_data(0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
  
  	if (!cpu_has(cpu, X86_FEATURE_EST))
  		return -ENODEV;
  
  	return cpufreq_register_driver(&centrino_driver);
  }
  
  static void __exit centrino_exit(void)
  {
  	cpufreq_unregister_driver(&centrino_driver);
  }
  
  MODULE_AUTHOR ("Jeremy Fitzhardinge <jeremy@goop.org>");
  MODULE_DESCRIPTION ("Enhanced SpeedStep driver for Intel Pentium M processors.");
  MODULE_LICENSE ("GPL");
  
  late_initcall(centrino_init);
  module_exit(centrino_exit);