Blame view

arch/x86/kernel/cpu/cpufreq/powernow-k7.c 17.1 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
  /*
   *  AMD K7 Powernow driver.
f4432c5ca   Dave Jones   Update email addr...
3
   *  (C) 2003 Dave Jones on behalf of SuSE Labs.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
4
5
6
7
8
   *  (C) 2003-2004 Dave Jones <davej@redhat.com>
   *
   *  Licensed under the terms of the GNU GPL License version 2.
   *  Based upon datasheets & sample CPUs kindly provided by AMD.
   *
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
9
10
11
12
13
14
   * Errata 5:
   *  CPU may fail to execute a FID/VID change in presence of interrupt.
   *  - We cli/sti on stepping A0 CPUs around the FID/VID transition.
   * Errata 15:
   *  CPU with half frequency multipliers may hang upon wakeup from disconnect.
   *  - We disable half multipliers if ACPI is used on A0 stepping CPUs.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
15
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
16
17
18
19
20
21
22
23
  #include <linux/kernel.h>
  #include <linux/module.h>
  #include <linux/moduleparam.h>
  #include <linux/init.h>
  #include <linux/cpufreq.h>
  #include <linux/slab.h>
  #include <linux/string.h>
  #include <linux/dmi.h>
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
24
25
  #include <linux/timex.h>
  #include <linux/io.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
26

b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
27
  #include <asm/timer.h>		/* Needed for recalibrate_cpu_khz() */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
28
  #include <asm/msr.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
  #include <asm/system.h>
  
  #ifdef CONFIG_X86_POWERNOW_K7_ACPI
  #include <linux/acpi.h>
  #include <acpi/processor.h>
  #endif
  
  #include "powernow-k7.h"
  
  #define PFX "powernow: "
  
  
  struct psb_s {
  	u8 signature[10];
  	u8 tableversion;
  	u8 flags;
  	u16 settlingtime;
  	u8 reserved1;
  	u8 numpst;
  };
  
  struct pst_s {
  	u32 cpuid;
  	u8 fsbspeed;
  	u8 maxfid;
  	u8 startvid;
  	u8 numpstates;
  };
  
  #ifdef CONFIG_X86_POWERNOW_K7_ACPI
  union powernow_acpi_control_t {
  	struct {
  		unsigned long fid:5,
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
62
63
64
  			vid:5,
  			sgtc:20,
  			res1:2;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
65
66
67
68
69
70
71
  	} bits;
  	unsigned long val;
  };
  #endif
  
  #ifdef CONFIG_CPU_FREQ_DEBUG
  /* divide by 1000 to get VCore voltage in V. */
bd5ab26a7   Dave Jones   [CPUFREQ] constif...
72
  static const int mobile_vid_table[32] = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
73
74
75
76
77
78
79
80
      2000, 1950, 1900, 1850, 1800, 1750, 1700, 1650,
      1600, 1550, 1500, 1450, 1400, 1350, 1300, 0,
      1275, 1250, 1225, 1200, 1175, 1150, 1125, 1100,
      1075, 1050, 1025, 1000, 975, 950, 925, 0,
  };
  #endif
  
  /* divide by 10 to get FID. */
bd5ab26a7   Dave Jones   [CPUFREQ] constif...
81
  static const int fid_codes[32] = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
      110, 115, 120, 125, 50, 55, 60, 65,
      70, 75, 80, 85, 90, 95, 100, 105,
      30, 190, 40, 200, 130, 135, 140, 210,
      150, 225, 160, 165, 170, 180, -1, -1,
  };
  
  /* This parameter is used in order to force ACPI instead of legacy method for
   * configuration purpose.
   */
  
  static int acpi_force;
  
  static struct cpufreq_frequency_table *powernow_table;
  
  static unsigned int can_scale_bus;
  static unsigned int can_scale_vid;
fff78ad5c   Dave Jones   [CPUFREQ] Stupidl...
98
  static unsigned int minimum_speed = -1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
99
100
101
102
103
  static unsigned int maximum_speed;
  static unsigned int number_scales;
  static unsigned int fsb;
  static unsigned int latency;
  static char have_a0;
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
104
105
  #define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, \
  		"powernow-k7", msg)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
106
107
108
109
110
111
112
  
  static int check_fsb(unsigned int fsbspeed)
  {
  	int delta;
  	unsigned int f = fsb / 1000;
  
  	delta = (fsbspeed > f) ? fsbspeed - f : f - fsbspeed;
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
113
  	return delta < 5;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
114
115
116
117
  }
  
  static int check_powernow(void)
  {
92cb7612a   Mike Travis   x86: convert cpui...
118
  	struct cpuinfo_x86 *c = &cpu_data(0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
119
  	unsigned int maxei, eax, ebx, ecx, edx;
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
120
  	if ((c->x86_vendor != X86_VENDOR_AMD) || (c->x86 != 6)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
121
  #ifdef MODULE
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
122
123
124
  		printk(KERN_INFO PFX "This module only works with "
  				"AMD K7 CPUs
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
125
126
127
128
129
  #endif
  		return 0;
  	}
  
  	/* Get maximum capabilities */
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
130
  	maxei = cpuid_eax(0x80000000);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
131
132
  	if (maxei < 0x80000007) {	/* Any powernow info ? */
  #ifdef MODULE
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
133
134
  		printk(KERN_INFO PFX "No powernow capabilities detected
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
135
136
137
138
139
  #endif
  		return 0;
  	}
  
  	if ((c->x86_model == 6) && (c->x86_mask == 0)) {
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
140
141
142
  		printk(KERN_INFO PFX "K7 660[A0] core detected, "
  				"enabling errata workarounds
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
143
144
145
146
147
148
149
150
  		have_a0 = 1;
  	}
  
  	cpuid(0x80000007, &eax, &ebx, &ecx, &edx);
  
  	/* Check we can actually do something before we say anything.*/
  	if (!(edx & (1 << 1 | 1 << 2)))
  		return 0;
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
151
  	printk(KERN_INFO PFX "PowerNOW! Technology present. Can scale: ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
152
153
  
  	if (edx & 1 << 1) {
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
154
155
  		printk("frequency");
  		can_scale_bus = 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
156
157
158
  	}
  
  	if ((edx & (1 << 1 | 1 << 2)) == 0x6)
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
159
  		printk(" and ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
160
161
  
  	if (edx & 1 << 2) {
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
162
163
  		printk("voltage");
  		can_scale_vid = 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
164
  	}
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
165
166
  	printk(".
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
167
168
  	return 1;
  }
d38e73e8d   Dave Jones   [CPUFREQ] powerno...
169
  #ifdef CONFIG_X86_POWERNOW_K7_ACPI
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
170
171
172
173
  static void invalidate_entry(unsigned int entry)
  {
  	powernow_table[entry].frequency = CPUFREQ_ENTRY_INVALID;
  }
d38e73e8d   Dave Jones   [CPUFREQ] powerno...
174
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
175

b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
176
  static int get_ranges(unsigned char *pst)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
177
178
179
180
  {
  	unsigned int j;
  	unsigned int speed;
  	u8 fid, vid;
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
181
182
  	powernow_table = kzalloc((sizeof(struct cpufreq_frequency_table) *
  				(number_scales + 1)), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
183
184
  	if (!powernow_table)
  		return -ENOMEM;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
185

b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
186
  	for (j = 0 ; j < number_scales; j++) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
187
188
189
190
191
192
  		fid = *pst++;
  
  		powernow_table[j].frequency = (fsb * fid_codes[fid]) / 10;
  		powernow_table[j].index = fid; /* lower 8 bits */
  
  		speed = powernow_table[j].frequency;
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
193
  		if ((fid_codes[fid] % 10) == 5) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
194
195
  #ifdef CONFIG_X86_POWERNOW_K7_ACPI
  			if (have_a0 == 1)
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
196
  				invalidate_entry(j);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
197
198
199
200
201
202
203
204
205
206
  #endif
  		}
  
  		if (speed < minimum_speed)
  			minimum_speed = speed;
  		if (speed > maximum_speed)
  			maximum_speed = speed;
  
  		vid = *pst++;
  		powernow_table[j].index |= (vid << 8); /* upper 8 bits */
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
207
  		dprintk("   FID: 0x%x (%d.%dx [%dMHz])  "
32ee8c3e4   Dave Jones   [CPUFREQ] Lots of...
208
209
210
  			 "VID: 0x%x (%d.%03dV)
  ", fid, fid_codes[fid] / 10,
  			 fid_codes[fid] % 10, speed/1000, vid,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
211
212
213
214
215
216
217
218
219
220
221
222
223
  			 mobile_vid_table[vid]/1000,
  			 mobile_vid_table[vid]%1000);
  	}
  	powernow_table[number_scales].frequency = CPUFREQ_TABLE_END;
  	powernow_table[number_scales].index = 0;
  
  	return 0;
  }
  
  
  static void change_FID(int fid)
  {
  	union msr_fidvidctl fidvidctl;
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
224
  	rdmsrl(MSR_K7_FID_VID_CTL, fidvidctl.val);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
225
226
227
228
229
  	if (fidvidctl.bits.FID != fid) {
  		fidvidctl.bits.SGTC = latency;
  		fidvidctl.bits.FID = fid;
  		fidvidctl.bits.VIDC = 0;
  		fidvidctl.bits.FIDC = 1;
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
230
  		wrmsrl(MSR_K7_FID_VID_CTL, fidvidctl.val);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
231
232
233
234
235
236
237
  	}
  }
  
  
  static void change_VID(int vid)
  {
  	union msr_fidvidctl fidvidctl;
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
238
  	rdmsrl(MSR_K7_FID_VID_CTL, fidvidctl.val);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
239
240
241
242
243
  	if (fidvidctl.bits.VID != vid) {
  		fidvidctl.bits.SGTC = latency;
  		fidvidctl.bits.VID = vid;
  		fidvidctl.bits.FIDC = 0;
  		fidvidctl.bits.VIDC = 1;
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
244
  		wrmsrl(MSR_K7_FID_VID_CTL, fidvidctl.val);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
245
246
  	}
  }
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
247
  static void change_speed(unsigned int index)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
  {
  	u8 fid, vid;
  	struct cpufreq_freqs freqs;
  	union msr_fidvidstatus fidvidstatus;
  	int cfid;
  
  	/* fid are the lower 8 bits of the index we stored into
  	 * the cpufreq frequency table in powernow_decode_bios,
  	 * vid are the upper 8 bits.
  	 */
  
  	fid = powernow_table[index].index & 0xFF;
  	vid = (powernow_table[index].index & 0xFF00) >> 8;
  
  	freqs.cpu = 0;
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
263
  	rdmsrl(MSR_K7_FID_VID_STATUS, fidvidstatus.val);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
  	cfid = fidvidstatus.bits.CFID;
  	freqs.old = fsb * fid_codes[cfid] / 10;
  
  	freqs.new = powernow_table[index].frequency;
  
  	cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
  
  	/* Now do the magic poking into the MSRs.  */
  
  	if (have_a0 == 1)	/* A0 errata 5 */
  		local_irq_disable();
  
  	if (freqs.old > freqs.new) {
  		/* Going down, so change FID first */
  		change_FID(fid);
  		change_VID(vid);
  	} else {
  		/* Going up, so change VID first */
  		change_VID(vid);
  		change_FID(fid);
  	}
  
  
  	if (have_a0 == 1)
  		local_irq_enable();
  
  	cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
  }
  
  
  #ifdef CONFIG_X86_POWERNOW_K7_ACPI
  
  static struct acpi_processor_performance *acpi_processor_perf;
  
  static int powernow_acpi_init(void)
  {
  	int i;
  	int retval = 0;
  	union powernow_acpi_control_t pc;
  
  	if (acpi_processor_perf != NULL && powernow_table != NULL) {
  		retval = -EINVAL;
  		goto err0;
  	}
bfdc708dc   Dave Jones   [CPUFREQ] kzalloc...
308
  	acpi_processor_perf = kzalloc(sizeof(struct acpi_processor_performance),
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
309
  				      GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
310
311
312
313
  	if (!acpi_processor_perf) {
  		retval = -ENOMEM;
  		goto err0;
  	}
eaa958402   Yinghai Lu   cpumask: alloc ze...
314
  	if (!zalloc_cpumask_var(&acpi_processor_perf->shared_cpu_map,
2fdf66b49   Rusty Russell   cpumask: convert ...
315
316
317
318
  								GFP_KERNEL)) {
  		retval = -ENOMEM;
  		goto err05;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
319
320
321
322
  	if (acpi_processor_register_performance(acpi_processor_perf, 0)) {
  		retval = -EIO;
  		goto err1;
  	}
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
323
324
  	if (acpi_processor_perf->control_register.space_id !=
  			ACPI_ADR_SPACE_FIXED_HARDWARE) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
325
326
327
  		retval = -ENODEV;
  		goto err2;
  	}
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
328
329
  	if (acpi_processor_perf->status_register.space_id !=
  			ACPI_ADR_SPACE_FIXED_HARDWARE) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
330
331
332
333
334
335
336
337
338
339
  		retval = -ENODEV;
  		goto err2;
  	}
  
  	number_scales = acpi_processor_perf->state_count;
  
  	if (number_scales < 2) {
  		retval = -ENODEV;
  		goto err2;
  	}
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
340
341
  	powernow_table = kzalloc((sizeof(struct cpufreq_frequency_table) *
  				(number_scales + 1)), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
342
343
344
345
  	if (!powernow_table) {
  		retval = -ENOMEM;
  		goto err2;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
346
347
348
  	pc.val = (unsigned long) acpi_processor_perf->states[0].control;
  	for (i = 0; i < number_scales; i++) {
  		u8 fid, vid;
dc2585eb4   Daniel Drake   [CPUFREQ] powerno...
349
350
351
  		struct acpi_processor_px *state =
  			&acpi_processor_perf->states[i];
  		unsigned int speed, speed_mhz;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
352

dc2585eb4   Daniel Drake   [CPUFREQ] powerno...
353
  		pc.val = (unsigned long) state->control;
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
354
355
  		dprintk("acpi:  P%d: %d MHz %d mW %d uS control %08x SGTC %d
  ",
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
356
  			 i,
dc2585eb4   Daniel Drake   [CPUFREQ] powerno...
357
358
359
360
  			 (u32) state->core_frequency,
  			 (u32) state->power,
  			 (u32) state->transition_latency,
  			 (u32) state->control,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
361
362
363
364
365
366
367
368
369
370
  			 pc.bits.sgtc);
  
  		vid = pc.bits.vid;
  		fid = pc.bits.fid;
  
  		powernow_table[i].frequency = fsb * fid_codes[fid] / 10;
  		powernow_table[i].index = fid; /* lower 8 bits */
  		powernow_table[i].index |= (vid << 8); /* upper 8 bits */
  
  		speed = powernow_table[i].frequency;
dc2585eb4   Daniel Drake   [CPUFREQ] powerno...
371
372
373
374
375
376
377
378
379
380
381
382
  		speed_mhz = speed / 1000;
  
  		/* processor_perflib will multiply the MHz value by 1000 to
  		 * get a KHz value (e.g. 1266000). However, powernow-k7 works
  		 * with true KHz values (e.g. 1266768). To ensure that all
  		 * powernow frequencies are available, we must ensure that
  		 * ACPI doesn't restrict them, so we round up the MHz value
  		 * to ensure that perflib's computed KHz value is greater than
  		 * or equal to powernow's KHz value.
  		 */
  		if (speed % 1000 > 0)
  			speed_mhz++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
383

b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
384
  		if ((fid_codes[fid] % 10) == 5) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
385
  			if (have_a0 == 1)
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
386
  				invalidate_entry(i);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
387
  		}
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
388
  		dprintk("   FID: 0x%x (%d.%dx [%dMHz])  "
32ee8c3e4   Dave Jones   [CPUFREQ] Lots of...
389
390
  			 "VID: 0x%x (%d.%03dV)
  ", fid, fid_codes[fid] / 10,
dc2585eb4   Daniel Drake   [CPUFREQ] powerno...
391
  			 fid_codes[fid] % 10, speed_mhz, vid,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
392
393
  			 mobile_vid_table[vid]/1000,
  			 mobile_vid_table[vid]%1000);
dc2585eb4   Daniel Drake   [CPUFREQ] powerno...
394
395
396
397
398
399
  		if (state->core_frequency != speed_mhz) {
  			state->core_frequency = speed_mhz;
  			dprintk("   Corrected ACPI frequency to %d
  ",
  				speed_mhz);
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
  		if (latency < pc.bits.sgtc)
  			latency = pc.bits.sgtc;
  
  		if (speed < minimum_speed)
  			minimum_speed = speed;
  		if (speed > maximum_speed)
  			maximum_speed = speed;
  	}
  
  	powernow_table[i].frequency = CPUFREQ_TABLE_END;
  	powernow_table[i].index = 0;
  
  	/* notify BIOS that we exist */
  	acpi_processor_notify_smm(THIS_MODULE);
  
  	return 0;
  
  err2:
  	acpi_processor_unregister_performance(acpi_processor_perf, 0);
  err1:
2fdf66b49   Rusty Russell   cpumask: convert ...
420
421
  	free_cpumask_var(acpi_processor_perf->shared_cpu_map);
  err05:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
422
423
  	kfree(acpi_processor_perf);
  err0:
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
424
425
426
  	printk(KERN_WARNING PFX "ACPI perflib can not be used on "
  			"this platform
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
427
428
429
430
431
432
433
434
435
436
437
438
  	acpi_processor_perf = NULL;
  	return retval;
  }
  #else
  static int powernow_acpi_init(void)
  {
  	printk(KERN_INFO PFX "no support for ACPI processor found."
  	       "  Please recompile your kernel with ACPI processor
  ");
  	return -EINVAL;
  }
  #endif
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
439
440
441
442
443
444
445
446
447
448
  static void print_pst_entry(struct pst_s *pst, unsigned int j)
  {
  	dprintk("PST:%d (@%p)
  ", j, pst);
  	dprintk(" cpuid: 0x%x  fsb: %d  maxFID: 0x%x  startvid: 0x%x
  ",
  		pst->cpuid, pst->fsbspeed, pst->maxfid, pst->startvid);
  }
  
  static int powernow_decode_bios(int maxfid, int startvid)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
449
450
451
452
453
454
455
456
457
  {
  	struct psb_s *psb;
  	struct pst_s *pst;
  	unsigned int i, j;
  	unsigned char *p;
  	unsigned int etuple;
  	unsigned int ret;
  
  	etuple = cpuid_eax(0x80000001);
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
458
  	for (i = 0xC0000; i < 0xffff0 ; i += 16) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
459
460
  
  		p = phys_to_virt(i);
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
461
462
463
  		if (memcmp(p, "AMDK7PNOW!",  10) == 0) {
  			dprintk("Found PSB header at %p
  ", p);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
464
  			psb = (struct psb_s *) p;
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
465
466
  			dprintk("Table version: 0x%x
  ", psb->tableversion);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
467
  			if (psb->tableversion != 0x12) {
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
468
469
470
  				printk(KERN_INFO PFX "Sorry, only v1.2 tables"
  						" supported right now
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
471
472
  				return -ENODEV;
  			}
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
473
474
475
476
477
478
479
480
  			dprintk("Flags: 0x%x
  ", psb->flags);
  			if ((psb->flags & 1) == 0)
  				dprintk("Mobile voltage regulator
  ");
  			else
  				dprintk("Desktop voltage regulator
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
481
482
483
  
  			latency = psb->settlingtime;
  			if (latency < 100) {
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
484
485
486
487
488
  				printk(KERN_INFO PFX "BIOS set settling time "
  						"to %d microseconds. "
  						"Should be at least 100. "
  						"Correcting.
  ", latency);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
489
490
  				latency = 100;
  			}
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
491
492
493
494
495
496
497
  			dprintk("Settling Time: %d microseconds.
  ",
  					psb->settlingtime);
  			dprintk("Has %d PST tables. (Only dumping ones "
  					"relevant to this CPU).
  ",
  					psb->numpst);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
498

b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
499
  			p += sizeof(struct psb_s);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
500
501
  
  			pst = (struct pst_s *) p;
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
502
  			for (j = 0; j < psb->numpst; j++) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
503
504
  				pst = (struct pst_s *) p;
  				number_scales = pst->numpstates;
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
505
506
507
508
509
510
511
  				if ((etuple == pst->cpuid) &&
  				    check_fsb(pst->fsbspeed) &&
  				    (maxfid == pst->maxfid) &&
  				    (startvid == pst->startvid)) {
  					print_pst_entry(pst, j);
  					p = (char *)pst + sizeof(struct pst_s);
  					ret = get_ranges(p);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
512
  					return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
513
  				} else {
8cbe01690   Dave Jones   [CPUFREQ] Disambi...
514
  					unsigned int k;
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
515
516
517
  					p = (char *)pst + sizeof(struct pst_s);
  					for (k = 0; k < number_scales; k++)
  						p += 2;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
518
519
  				}
  			}
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
520
521
522
523
524
525
  			printk(KERN_INFO PFX "No PST tables match this cpuid "
  					"(0x%x)
  ", etuple);
  			printk(KERN_INFO PFX "This is indicative of a broken "
  					"BIOS.
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
526
527
528
529
530
531
532
533
  
  			return -EINVAL;
  		}
  		p++;
  	}
  
  	return -ENODEV;
  }
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
534
  static int powernow_target(struct cpufreq_policy *policy,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
535
536
537
538
  			    unsigned int target_freq,
  			    unsigned int relation)
  {
  	unsigned int newstate;
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
539
540
  	if (cpufreq_frequency_table_target(policy, powernow_table, target_freq,
  				relation, &newstate))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
541
542
543
544
545
546
  		return -EINVAL;
  
  	change_speed(newstate);
  
  	return 0;
  }
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
547
  static int powernow_verify(struct cpufreq_policy *policy)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
  {
  	return cpufreq_frequency_table_verify(policy, powernow_table);
  }
  
  /*
   * We use the fact that the bus frequency is somehow
   * a multiple of 100000/3 khz, then we compute sgtc according
   * to this multiple.
   * That way, we match more how AMD thinks all of that work.
   * We will then get the same kind of behaviour already tested under
   * the "well-known" other OS.
   */
  static int __init fixup_sgtc(void)
  {
  	unsigned int sgtc;
  	unsigned int m;
  
  	m = fsb / 3333;
  	if ((m % 10) >= 5)
  		m += 5;
  
  	m /= 10;
  
  	sgtc = 100 * m * latency;
  	sgtc = sgtc / 3;
  	if (sgtc > 0xfffff) {
  		printk(KERN_WARNING PFX "SGTC too large %d
  ", sgtc);
  		sgtc = 0xfffff;
  	}
  	return sgtc;
  }
  
  static unsigned int powernow_get(unsigned int cpu)
  {
  	union msr_fidvidstatus fidvidstatus;
  	unsigned int cfid;
  
  	if (cpu)
  		return 0;
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
588
  	rdmsrl(MSR_K7_FID_VID_STATUS, fidvidstatus.val);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
589
  	cfid = fidvidstatus.bits.CFID;
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
590
  	return fsb * fid_codes[cfid] / 10;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
591
  }
0b4b5dde4   Thomas Gleixner   x86: fix dmi cons...
592
  static int __init acer_cpufreq_pst(const struct dmi_system_id *d)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
593
  {
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
594
595
596
597
598
599
600
601
602
603
604
  	printk(KERN_WARNING PFX
  		"%s laptop with broken PST tables in BIOS detected.
  ",
  		d->ident);
  	printk(KERN_WARNING PFX
  		"You need to downgrade to 3A21 (09/09/2002), or try a newer "
  		"BIOS than 3A71 (01/20/2003)
  ");
  	printk(KERN_WARNING PFX
  		"cpufreq scaling has been disabled as a result of this.
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
  	return 0;
  }
  
  /*
   * Some Athlon laptops have really fucked PST tables.
   * A BIOS update is all that can save them.
   * Mention this, and disable cpufreq.
   */
  static struct dmi_system_id __initdata powernow_dmi_table[] = {
  	{
  		.callback = acer_cpufreq_pst,
  		.ident = "Acer Aspire",
  		.matches = {
  			DMI_MATCH(DMI_SYS_VENDOR, "Insyde Software"),
  			DMI_MATCH(DMI_BIOS_VERSION, "3A71"),
  		},
  	},
  	{ }
  };
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
624
  static int __init powernow_cpu_init(struct cpufreq_policy *policy)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
625
626
627
628
629
630
  {
  	union msr_fidvidstatus fidvidstatus;
  	int result;
  
  	if (policy->cpu != 0)
  		return -ENODEV;
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
631
  	rdmsrl(MSR_K7_FID_VID_STATUS, fidvidstatus.val);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
632

436fe7b8b   Dave Jones   [CPUFREQ] Make po...
633
  	recalibrate_cpu_khz();
91350ed49   Dave Jones   [CPUFREQ] Recalib...
634
635
  
  	fsb = (10 * cpu_khz) / fid_codes[fidvidstatus.bits.CFID];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
636
637
638
639
640
  	if (!fsb) {
  		printk(KERN_WARNING PFX "can not determine bus frequency
  ");
  		return -EINVAL;
  	}
7eb53d882   Dave Jones   [CPUFREQ] powerno...
641
642
  	dprintk("FSB: %3dMHz
  ", fsb/1000);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
643
644
  
  	if (dmi_check_system(powernow_dmi_table) || acpi_force) {
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
645
646
647
  		printk(KERN_INFO PFX "PSB/PST known to be broken.  "
  				"Trying ACPI instead
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
648
649
  		result = powernow_acpi_init();
  	} else {
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
650
651
  		result = powernow_decode_bios(fidvidstatus.bits.MFID,
  				fidvidstatus.bits.SVID);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
652
  		if (result) {
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
653
654
  			printk(KERN_INFO PFX "Trying ACPI perflib
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
655
656
657
658
659
  			maximum_speed = 0;
  			minimum_speed = -1;
  			latency = 0;
  			result = powernow_acpi_init();
  			if (result) {
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
660
661
662
  				printk(KERN_INFO PFX
  					"ACPI and legacy methods failed
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
663
664
665
666
667
668
669
670
671
672
673
  			}
  		} else {
  			/* SGTC use the bus clock as timer */
  			latency = fixup_sgtc();
  			printk(KERN_INFO PFX "SGTC: %d
  ", latency);
  		}
  	}
  
  	if (result)
  		return result;
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
674
675
  	printk(KERN_INFO PFX "Minimum speed %d MHz. Maximum speed %d MHz.
  ",
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
676
  				minimum_speed/1000, maximum_speed/1000);
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
677
678
  	policy->cpuinfo.transition_latency =
  		cpufreq_scale(2000000UL, fsb, latency);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
679
680
681
682
683
684
685
  
  	policy->cur = powernow_get(0);
  
  	cpufreq_frequency_table_get_attr(powernow_table, policy->cpu);
  
  	return cpufreq_frequency_table_cpuinfo(policy, powernow_table);
  }
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
686
687
  static int powernow_cpu_exit(struct cpufreq_policy *policy)
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
688
689
690
691
692
  	cpufreq_frequency_table_put_attr(policy->cpu);
  
  #ifdef CONFIG_X86_POWERNOW_K7_ACPI
  	if (acpi_processor_perf) {
  		acpi_processor_unregister_performance(acpi_processor_perf, 0);
2fdf66b49   Rusty Russell   cpumask: convert ...
693
  		free_cpumask_var(acpi_processor_perf->shared_cpu_map);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
694
695
696
  		kfree(acpi_processor_perf);
  	}
  #endif
4ae6673e0   Jesper Juhl   [PATCH] get rid o...
697
  	kfree(powernow_table);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
698
699
  	return 0;
  }
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
700
  static struct freq_attr *powernow_table_attr[] = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
701
702
703
  	&cpufreq_freq_attr_scaling_available_freqs,
  	NULL,
  };
221dee285   Linus Torvalds   Revert "[CPUFREQ]...
704
  static struct cpufreq_driver powernow_driver = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
705
706
707
708
709
710
711
712
713
  	.verify	= powernow_verify,
  	.target	= powernow_target,
  	.get	= powernow_get,
  	.init	= powernow_cpu_init,
  	.exit	= powernow_cpu_exit,
  	.name	= "powernow-k7",
  	.owner	= THIS_MODULE,
  	.attr	= powernow_table_attr,
  };
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
714
  static int __init powernow_init(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
715
  {
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
716
  	if (check_powernow() == 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
717
718
719
  		return -ENODEV;
  	return cpufreq_register_driver(&powernow_driver);
  }
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
720
  static void __exit powernow_exit(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
721
722
723
724
725
726
  {
  	cpufreq_unregister_driver(&powernow_driver);
  }
  
  module_param(acpi_force,  int, 0444);
  MODULE_PARM_DESC(acpi_force, "Force ACPI to be used.");
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
727
728
729
  MODULE_AUTHOR("Dave Jones <davej@redhat.com>");
  MODULE_DESCRIPTION("Powernow driver for AMD K7 processors.");
  MODULE_LICENSE("GPL");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
730
731
732
  
  late_initcall(powernow_init);
  module_exit(powernow_exit);