Blame view

drivers/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
  	} bits;
  	unsigned long val;
  };
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
69
  /* divide by 1000 to get VCore voltage in V. */
bd5ab26a7   Dave Jones   [CPUFREQ] constif...
70
  static const int mobile_vid_table[32] = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
71
72
73
74
75
      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,
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
76
77
  
  /* divide by 10 to get FID. */
bd5ab26a7   Dave Jones   [CPUFREQ] constif...
78
  static const int fid_codes[32] = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
      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...
95
  static unsigned int minimum_speed = -1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
96
97
98
99
100
  static unsigned int maximum_speed;
  static unsigned int number_scales;
  static unsigned int fsb;
  static unsigned int latency;
  static char have_a0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
101
102
103
104
105
106
  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...
107
  	return delta < 5;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
108
109
110
111
  }
  
  static int check_powernow(void)
  {
92cb7612a   Mike Travis   x86: convert cpui...
112
  	struct cpuinfo_x86 *c = &cpu_data(0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
113
  	unsigned int maxei, eax, ebx, ecx, edx;
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
114
  	if ((c->x86_vendor != X86_VENDOR_AMD) || (c->x86 != 6)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
115
  #ifdef MODULE
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
116
117
118
  		printk(KERN_INFO PFX "This module only works with "
  				"AMD K7 CPUs
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
119
120
121
122
123
  #endif
  		return 0;
  	}
  
  	/* Get maximum capabilities */
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
124
  	maxei = cpuid_eax(0x80000000);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
125
126
  	if (maxei < 0x80000007) {	/* Any powernow info ? */
  #ifdef MODULE
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
127
128
  		printk(KERN_INFO PFX "No powernow capabilities detected
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
129
130
131
132
133
  #endif
  		return 0;
  	}
  
  	if ((c->x86_model == 6) && (c->x86_mask == 0)) {
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
134
135
136
  		printk(KERN_INFO PFX "K7 660[A0] core detected, "
  				"enabling errata workarounds
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
137
138
139
140
141
142
143
144
  		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...
145
  	printk(KERN_INFO PFX "PowerNOW! Technology present. Can scale: ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
146
147
  
  	if (edx & 1 << 1) {
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
148
149
  		printk("frequency");
  		can_scale_bus = 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
150
151
152
  	}
  
  	if ((edx & (1 << 1 | 1 << 2)) == 0x6)
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
153
  		printk(" and ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
154
155
  
  	if (edx & 1 << 2) {
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
156
157
  		printk("voltage");
  		can_scale_vid = 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
158
  	}
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
159
160
  	printk(".
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
161
162
  	return 1;
  }
d38e73e8d   Dave Jones   [CPUFREQ] powerno...
163
  #ifdef CONFIG_X86_POWERNOW_K7_ACPI
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
164
165
166
167
  static void invalidate_entry(unsigned int entry)
  {
  	powernow_table[entry].frequency = CPUFREQ_ENTRY_INVALID;
  }
d38e73e8d   Dave Jones   [CPUFREQ] powerno...
168
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
169

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

b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
180
  	for (j = 0 ; j < number_scales; j++) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
181
182
183
184
185
186
  		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...
187
  		if ((fid_codes[fid] % 10) == 5) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
188
189
  #ifdef CONFIG_X86_POWERNOW_K7_ACPI
  			if (have_a0 == 1)
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
190
  				invalidate_entry(j);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
191
192
193
194
195
196
197
198
199
200
  #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 */
2d06d8c49   Dominik Brodowski   [CPUFREQ] use dyn...
201
  		pr_debug("   FID: 0x%x (%d.%dx [%dMHz])  "
32ee8c3e4   Dave Jones   [CPUFREQ] Lots of...
202
203
204
  			 "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
205
206
207
208
209
210
211
212
213
214
215
216
217
  			 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...
218
  	rdmsrl(MSR_K7_FID_VID_CTL, fidvidctl.val);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
219
220
221
222
223
  	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...
224
  		wrmsrl(MSR_K7_FID_VID_CTL, fidvidctl.val);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
225
226
227
228
229
230
231
  	}
  }
  
  
  static void change_VID(int vid)
  {
  	union msr_fidvidctl fidvidctl;
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
232
  	rdmsrl(MSR_K7_FID_VID_CTL, fidvidctl.val);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
233
234
235
236
237
  	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...
238
  		wrmsrl(MSR_K7_FID_VID_CTL, fidvidctl.val);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
239
240
  	}
  }
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
241
  static void change_speed(unsigned int index)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
  {
  	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...
257
  	rdmsrl(MSR_K7_FID_VID_STATUS, fidvidstatus.val);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
258
259
260
261
262
263
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
  	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...
302
  	acpi_processor_perf = kzalloc(sizeof(struct acpi_processor_performance),
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
303
  				      GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
304
305
306
307
  	if (!acpi_processor_perf) {
  		retval = -ENOMEM;
  		goto err0;
  	}
eaa958402   Yinghai Lu   cpumask: alloc ze...
308
  	if (!zalloc_cpumask_var(&acpi_processor_perf->shared_cpu_map,
2fdf66b49   Rusty Russell   cpumask: convert ...
309
310
311
312
  								GFP_KERNEL)) {
  		retval = -ENOMEM;
  		goto err05;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
313
314
315
316
  	if (acpi_processor_register_performance(acpi_processor_perf, 0)) {
  		retval = -EIO;
  		goto err1;
  	}
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
317
318
  	if (acpi_processor_perf->control_register.space_id !=
  			ACPI_ADR_SPACE_FIXED_HARDWARE) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
319
320
321
  		retval = -ENODEV;
  		goto err2;
  	}
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
322
323
  	if (acpi_processor_perf->status_register.space_id !=
  			ACPI_ADR_SPACE_FIXED_HARDWARE) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
324
325
326
327
328
329
330
331
332
333
  		retval = -ENODEV;
  		goto err2;
  	}
  
  	number_scales = acpi_processor_perf->state_count;
  
  	if (number_scales < 2) {
  		retval = -ENODEV;
  		goto err2;
  	}
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
334
335
  	powernow_table = kzalloc((sizeof(struct cpufreq_frequency_table) *
  				(number_scales + 1)), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
336
337
338
339
  	if (!powernow_table) {
  		retval = -ENOMEM;
  		goto err2;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
340
341
342
  	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...
343
344
345
  		struct acpi_processor_px *state =
  			&acpi_processor_perf->states[i];
  		unsigned int speed, speed_mhz;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
346

dc2585eb4   Daniel Drake   [CPUFREQ] powerno...
347
  		pc.val = (unsigned long) state->control;
2d06d8c49   Dominik Brodowski   [CPUFREQ] use dyn...
348
349
  		pr_debug("acpi:  P%d: %d MHz %d mW %d uS control %08x SGTC %d
  ",
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
350
  			 i,
dc2585eb4   Daniel Drake   [CPUFREQ] powerno...
351
352
353
354
  			 (u32) state->core_frequency,
  			 (u32) state->power,
  			 (u32) state->transition_latency,
  			 (u32) state->control,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
355
356
357
358
359
360
361
362
363
364
  			 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...
365
366
367
368
369
370
371
372
373
374
375
376
  		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
377

b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
378
  		if ((fid_codes[fid] % 10) == 5) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
379
  			if (have_a0 == 1)
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
380
  				invalidate_entry(i);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
381
  		}
2d06d8c49   Dominik Brodowski   [CPUFREQ] use dyn...
382
  		pr_debug("   FID: 0x%x (%d.%dx [%dMHz])  "
32ee8c3e4   Dave Jones   [CPUFREQ] Lots of...
383
384
  			 "VID: 0x%x (%d.%03dV)
  ", fid, fid_codes[fid] / 10,
dc2585eb4   Daniel Drake   [CPUFREQ] powerno...
385
  			 fid_codes[fid] % 10, speed_mhz, vid,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
386
387
  			 mobile_vid_table[vid]/1000,
  			 mobile_vid_table[vid]%1000);
dc2585eb4   Daniel Drake   [CPUFREQ] powerno...
388
389
  		if (state->core_frequency != speed_mhz) {
  			state->core_frequency = speed_mhz;
2d06d8c49   Dominik Brodowski   [CPUFREQ] use dyn...
390
391
  			pr_debug("   Corrected ACPI frequency to %d
  ",
dc2585eb4   Daniel Drake   [CPUFREQ] powerno...
392
393
  				speed_mhz);
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
  		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 ...
414
415
  	free_cpumask_var(acpi_processor_perf->shared_cpu_map);
  err05:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
416
417
  	kfree(acpi_processor_perf);
  err0:
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
418
419
420
  	printk(KERN_WARNING PFX "ACPI perflib can not be used on "
  			"this platform
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
421
422
423
424
425
426
427
428
429
430
431
432
  	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...
433
434
  static void print_pst_entry(struct pst_s *pst, unsigned int j)
  {
2d06d8c49   Dominik Brodowski   [CPUFREQ] use dyn...
435
436
437
438
  	pr_debug("PST:%d (@%p)
  ", j, pst);
  	pr_debug(" cpuid: 0x%x  fsb: %d  maxFID: 0x%x  startvid: 0x%x
  ",
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
439
440
441
442
  		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
443
444
445
446
447
448
449
450
451
  {
  	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...
452
  	for (i = 0xC0000; i < 0xffff0 ; i += 16) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
453
454
  
  		p = phys_to_virt(i);
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
455
  		if (memcmp(p, "AMDK7PNOW!",  10) == 0) {
2d06d8c49   Dominik Brodowski   [CPUFREQ] use dyn...
456
457
  			pr_debug("Found PSB header at %p
  ", p);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
458
  			psb = (struct psb_s *) p;
2d06d8c49   Dominik Brodowski   [CPUFREQ] use dyn...
459
460
  			pr_debug("Table version: 0x%x
  ", psb->tableversion);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
461
  			if (psb->tableversion != 0x12) {
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
462
463
464
  				printk(KERN_INFO PFX "Sorry, only v1.2 tables"
  						" supported right now
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
465
466
  				return -ENODEV;
  			}
2d06d8c49   Dominik Brodowski   [CPUFREQ] use dyn...
467
468
  			pr_debug("Flags: 0x%x
  ", psb->flags);
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
469
  			if ((psb->flags & 1) == 0)
2d06d8c49   Dominik Brodowski   [CPUFREQ] use dyn...
470
471
  				pr_debug("Mobile voltage regulator
  ");
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
472
  			else
2d06d8c49   Dominik Brodowski   [CPUFREQ] use dyn...
473
474
  				pr_debug("Desktop voltage regulator
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
475
476
477
  
  			latency = psb->settlingtime;
  			if (latency < 100) {
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
478
479
480
481
482
  				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
483
484
  				latency = 100;
  			}
2d06d8c49   Dominik Brodowski   [CPUFREQ] use dyn...
485
486
  			pr_debug("Settling Time: %d microseconds.
  ",
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
487
  					psb->settlingtime);
2d06d8c49   Dominik Brodowski   [CPUFREQ] use dyn...
488
  			pr_debug("Has %d PST tables. (Only dumping ones "
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
489
490
491
  					"relevant to this CPU).
  ",
  					psb->numpst);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
492

b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
493
  			p += sizeof(struct psb_s);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
494
495
  
  			pst = (struct pst_s *) p;
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
496
  			for (j = 0; j < psb->numpst; j++) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
497
498
  				pst = (struct pst_s *) p;
  				number_scales = pst->numpstates;
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
499
500
501
502
503
504
505
  				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
506
  					return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
507
  				} else {
8cbe01690   Dave Jones   [CPUFREQ] Disambi...
508
  					unsigned int k;
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
509
510
511
  					p = (char *)pst + sizeof(struct pst_s);
  					for (k = 0; k < number_scales; k++)
  						p += 2;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
512
513
  				}
  			}
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
514
515
516
517
518
519
  			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
520
521
522
523
524
525
526
527
  
  			return -EINVAL;
  		}
  		p++;
  	}
  
  	return -ENODEV;
  }
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
528
  static int powernow_target(struct cpufreq_policy *policy,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
529
530
531
532
  			    unsigned int target_freq,
  			    unsigned int relation)
  {
  	unsigned int newstate;
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
533
534
  	if (cpufreq_frequency_table_target(policy, powernow_table, target_freq,
  				relation, &newstate))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
535
536
537
538
539
540
  		return -EINVAL;
  
  	change_speed(newstate);
  
  	return 0;
  }
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
541
  static int powernow_verify(struct cpufreq_policy *policy)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
542
543
544
545
546
547
548
549
550
551
552
553
  {
  	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.
   */
307069cf6   Holger Freyther   [CPUFREQ] Fix sec...
554
  static int __cpuinit fixup_sgtc(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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
  {
  	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...
582
  	rdmsrl(MSR_K7_FID_VID_STATUS, fidvidstatus.val);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
583
  	cfid = fidvidstatus.bits.CFID;
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
584
  	return fsb * fid_codes[cfid] / 10;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
585
  }
307069cf6   Holger Freyther   [CPUFREQ] Fix sec...
586
  static int __cpuinit acer_cpufreq_pst(const struct dmi_system_id *d)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
587
  {
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
588
589
590
591
592
593
594
595
596
597
598
  	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
599
600
601
602
603
604
605
606
  	return 0;
  }
  
  /*
   * Some Athlon laptops have really fucked PST tables.
   * A BIOS update is all that can save them.
   * Mention this, and disable cpufreq.
   */
307069cf6   Holger Freyther   [CPUFREQ] Fix sec...
607
  static struct dmi_system_id __cpuinitdata powernow_dmi_table[] = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
608
609
610
611
612
613
614
615
616
617
  	{
  		.callback = acer_cpufreq_pst,
  		.ident = "Acer Aspire",
  		.matches = {
  			DMI_MATCH(DMI_SYS_VENDOR, "Insyde Software"),
  			DMI_MATCH(DMI_BIOS_VERSION, "3A71"),
  		},
  	},
  	{ }
  };
307069cf6   Holger Freyther   [CPUFREQ] Fix sec...
618
  static int __cpuinit powernow_cpu_init(struct cpufreq_policy *policy)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
619
620
621
622
623
624
  {
  	union msr_fidvidstatus fidvidstatus;
  	int result;
  
  	if (policy->cpu != 0)
  		return -ENODEV;
b9e7638a3   Dave Jones   [CPUFREQ] checkpa...
625
  	rdmsrl(MSR_K7_FID_VID_STATUS, fidvidstatus.val);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
626

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