blob: a8363e5be4ef5b6b9ce78497b7d2a4aebf3a7f13 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * AMD K7 Powernow driver.
Dave Jonesf4432c52008-10-20 13:31:45 -04003 * (C) 2003 Dave Jones on behalf of SuSE Labs.
Linus Torvalds1da177e2005-04-16 15:20:36 -07004 * (C) 2003-2004 Dave Jones <davej@redhat.com>
5 *
6 * Licensed under the terms of the GNU GPL License version 2.
7 * Based upon datasheets & sample CPUs kindly provided by AMD.
8 *
Dave Jonesb9e76382009-01-18 00:32:26 -05009 * Errata 5:
10 * CPU may fail to execute a FID/VID change in presence of interrupt.
11 * - We cli/sti on stepping A0 CPUs around the FID/VID transition.
12 * Errata 15:
13 * CPU with half frequency multipliers may hang upon wakeup from disconnect.
14 * - We disable half multipliers if ACPI is used on A0 stepping CPUs.
Linus Torvalds1da177e2005-04-16 15:20:36 -070015 */
16
Linus Torvalds1da177e2005-04-16 15:20:36 -070017#include <linux/kernel.h>
18#include <linux/module.h>
19#include <linux/moduleparam.h>
20#include <linux/init.h>
21#include <linux/cpufreq.h>
22#include <linux/slab.h>
23#include <linux/string.h>
24#include <linux/dmi.h>
Dave Jonesb9e76382009-01-18 00:32:26 -050025#include <linux/timex.h>
26#include <linux/io.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070027
Dave Jonesb9e76382009-01-18 00:32:26 -050028#include <asm/timer.h> /* Needed for recalibrate_cpu_khz() */
Linus Torvalds1da177e2005-04-16 15:20:36 -070029#include <asm/msr.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070030#include <asm/system.h>
31
32#ifdef CONFIG_X86_POWERNOW_K7_ACPI
33#include <linux/acpi.h>
34#include <acpi/processor.h>
35#endif
36
37#include "powernow-k7.h"
38
39#define PFX "powernow: "
40
41
42struct psb_s {
43 u8 signature[10];
44 u8 tableversion;
45 u8 flags;
46 u16 settlingtime;
47 u8 reserved1;
48 u8 numpst;
49};
50
51struct pst_s {
52 u32 cpuid;
53 u8 fsbspeed;
54 u8 maxfid;
55 u8 startvid;
56 u8 numpstates;
57};
58
59#ifdef CONFIG_X86_POWERNOW_K7_ACPI
60union powernow_acpi_control_t {
61 struct {
62 unsigned long fid:5,
Dave Jonesb9e76382009-01-18 00:32:26 -050063 vid:5,
64 sgtc:20,
65 res1:2;
Linus Torvalds1da177e2005-04-16 15:20:36 -070066 } bits;
67 unsigned long val;
68};
69#endif
70
71#ifdef CONFIG_CPU_FREQ_DEBUG
72/* divide by 1000 to get VCore voltage in V. */
Dave Jonesbd5ab262007-02-22 19:11:16 -050073static const int mobile_vid_table[32] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -070074 2000, 1950, 1900, 1850, 1800, 1750, 1700, 1650,
75 1600, 1550, 1500, 1450, 1400, 1350, 1300, 0,
76 1275, 1250, 1225, 1200, 1175, 1150, 1125, 1100,
77 1075, 1050, 1025, 1000, 975, 950, 925, 0,
78};
79#endif
80
81/* divide by 10 to get FID. */
Dave Jonesbd5ab262007-02-22 19:11:16 -050082static const int fid_codes[32] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -070083 110, 115, 120, 125, 50, 55, 60, 65,
84 70, 75, 80, 85, 90, 95, 100, 105,
85 30, 190, 40, 200, 130, 135, 140, 210,
86 150, 225, 160, 165, 170, 180, -1, -1,
87};
88
89/* This parameter is used in order to force ACPI instead of legacy method for
90 * configuration purpose.
91 */
92
93static int acpi_force;
94
95static struct cpufreq_frequency_table *powernow_table;
96
97static unsigned int can_scale_bus;
98static unsigned int can_scale_vid;
Dave Jonesfff78ad2009-01-17 22:28:42 -050099static unsigned int minimum_speed = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700100static unsigned int maximum_speed;
101static unsigned int number_scales;
102static unsigned int fsb;
103static unsigned int latency;
104static char have_a0;
105
Dave Jonesb9e76382009-01-18 00:32:26 -0500106#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, \
107 "powernow-k7", msg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700108
109static int check_fsb(unsigned int fsbspeed)
110{
111 int delta;
112 unsigned int f = fsb / 1000;
113
114 delta = (fsbspeed > f) ? fsbspeed - f : f - fsbspeed;
Dave Jonesb9e76382009-01-18 00:32:26 -0500115 return delta < 5;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700116}
117
118static int check_powernow(void)
119{
Mike Travis92cb7612007-10-19 20:35:04 +0200120 struct cpuinfo_x86 *c = &cpu_data(0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700121 unsigned int maxei, eax, ebx, ecx, edx;
122
Dave Jonesb9e76382009-01-18 00:32:26 -0500123 if ((c->x86_vendor != X86_VENDOR_AMD) || (c->x86 != 6)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700124#ifdef MODULE
Dave Jonesb9e76382009-01-18 00:32:26 -0500125 printk(KERN_INFO PFX "This module only works with "
126 "AMD K7 CPUs\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127#endif
128 return 0;
129 }
130
131 /* Get maximum capabilities */
Dave Jonesb9e76382009-01-18 00:32:26 -0500132 maxei = cpuid_eax(0x80000000);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700133 if (maxei < 0x80000007) { /* Any powernow info ? */
134#ifdef MODULE
Dave Jonesb9e76382009-01-18 00:32:26 -0500135 printk(KERN_INFO PFX "No powernow capabilities detected\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136#endif
137 return 0;
138 }
139
140 if ((c->x86_model == 6) && (c->x86_mask == 0)) {
Dave Jonesb9e76382009-01-18 00:32:26 -0500141 printk(KERN_INFO PFX "K7 660[A0] core detected, "
142 "enabling errata workarounds\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143 have_a0 = 1;
144 }
145
146 cpuid(0x80000007, &eax, &ebx, &ecx, &edx);
147
148 /* Check we can actually do something before we say anything.*/
149 if (!(edx & (1 << 1 | 1 << 2)))
150 return 0;
151
Dave Jonesb9e76382009-01-18 00:32:26 -0500152 printk(KERN_INFO PFX "PowerNOW! Technology present. Can scale: ");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700153
154 if (edx & 1 << 1) {
Dave Jonesb9e76382009-01-18 00:32:26 -0500155 printk("frequency");
156 can_scale_bus = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700157 }
158
159 if ((edx & (1 << 1 | 1 << 2)) == 0x6)
Dave Jonesb9e76382009-01-18 00:32:26 -0500160 printk(" and ");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700161
162 if (edx & 1 << 2) {
Dave Jonesb9e76382009-01-18 00:32:26 -0500163 printk("voltage");
164 can_scale_vid = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700165 }
166
Dave Jonesb9e76382009-01-18 00:32:26 -0500167 printk(".\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700168 return 1;
169}
170
Dave Jonesd38e73e2009-04-23 13:36:12 -0400171#ifdef CONFIG_X86_POWERNOW_K7_ACPI
Dave Jonesb9e76382009-01-18 00:32:26 -0500172static void invalidate_entry(unsigned int entry)
173{
174 powernow_table[entry].frequency = CPUFREQ_ENTRY_INVALID;
175}
Dave Jonesd38e73e2009-04-23 13:36:12 -0400176#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700177
Dave Jonesb9e76382009-01-18 00:32:26 -0500178static int get_ranges(unsigned char *pst)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700179{
180 unsigned int j;
181 unsigned int speed;
182 u8 fid, vid;
183
Dave Jonesb9e76382009-01-18 00:32:26 -0500184 powernow_table = kzalloc((sizeof(struct cpufreq_frequency_table) *
185 (number_scales + 1)), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700186 if (!powernow_table)
187 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700188
Dave Jonesb9e76382009-01-18 00:32:26 -0500189 for (j = 0 ; j < number_scales; j++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700190 fid = *pst++;
191
192 powernow_table[j].frequency = (fsb * fid_codes[fid]) / 10;
193 powernow_table[j].index = fid; /* lower 8 bits */
194
195 speed = powernow_table[j].frequency;
196
Dave Jonesb9e76382009-01-18 00:32:26 -0500197 if ((fid_codes[fid] % 10) == 5) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700198#ifdef CONFIG_X86_POWERNOW_K7_ACPI
199 if (have_a0 == 1)
Dave Jonesb9e76382009-01-18 00:32:26 -0500200 invalidate_entry(j);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700201#endif
202 }
203
204 if (speed < minimum_speed)
205 minimum_speed = speed;
206 if (speed > maximum_speed)
207 maximum_speed = speed;
208
209 vid = *pst++;
210 powernow_table[j].index |= (vid << 8); /* upper 8 bits */
211
Dave Jonesb9e76382009-01-18 00:32:26 -0500212 dprintk(" FID: 0x%x (%d.%dx [%dMHz]) "
Dave Jones32ee8c32006-02-28 00:43:23 -0500213 "VID: 0x%x (%d.%03dV)\n", fid, fid_codes[fid] / 10,
214 fid_codes[fid] % 10, speed/1000, vid,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700215 mobile_vid_table[vid]/1000,
216 mobile_vid_table[vid]%1000);
217 }
218 powernow_table[number_scales].frequency = CPUFREQ_TABLE_END;
219 powernow_table[number_scales].index = 0;
220
221 return 0;
222}
223
224
225static void change_FID(int fid)
226{
227 union msr_fidvidctl fidvidctl;
228
Dave Jonesb9e76382009-01-18 00:32:26 -0500229 rdmsrl(MSR_K7_FID_VID_CTL, fidvidctl.val);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700230 if (fidvidctl.bits.FID != fid) {
231 fidvidctl.bits.SGTC = latency;
232 fidvidctl.bits.FID = fid;
233 fidvidctl.bits.VIDC = 0;
234 fidvidctl.bits.FIDC = 1;
Dave Jonesb9e76382009-01-18 00:32:26 -0500235 wrmsrl(MSR_K7_FID_VID_CTL, fidvidctl.val);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700236 }
237}
238
239
240static void change_VID(int vid)
241{
242 union msr_fidvidctl fidvidctl;
243
Dave Jonesb9e76382009-01-18 00:32:26 -0500244 rdmsrl(MSR_K7_FID_VID_CTL, fidvidctl.val);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700245 if (fidvidctl.bits.VID != vid) {
246 fidvidctl.bits.SGTC = latency;
247 fidvidctl.bits.VID = vid;
248 fidvidctl.bits.FIDC = 0;
249 fidvidctl.bits.VIDC = 1;
Dave Jonesb9e76382009-01-18 00:32:26 -0500250 wrmsrl(MSR_K7_FID_VID_CTL, fidvidctl.val);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700251 }
252}
253
254
Dave Jonesb9e76382009-01-18 00:32:26 -0500255static void change_speed(unsigned int index)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700256{
257 u8 fid, vid;
258 struct cpufreq_freqs freqs;
259 union msr_fidvidstatus fidvidstatus;
260 int cfid;
261
262 /* fid are the lower 8 bits of the index we stored into
263 * the cpufreq frequency table in powernow_decode_bios,
264 * vid are the upper 8 bits.
265 */
266
267 fid = powernow_table[index].index & 0xFF;
268 vid = (powernow_table[index].index & 0xFF00) >> 8;
269
270 freqs.cpu = 0;
271
Dave Jonesb9e76382009-01-18 00:32:26 -0500272 rdmsrl(MSR_K7_FID_VID_STATUS, fidvidstatus.val);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700273 cfid = fidvidstatus.bits.CFID;
274 freqs.old = fsb * fid_codes[cfid] / 10;
275
276 freqs.new = powernow_table[index].frequency;
277
278 cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
279
280 /* Now do the magic poking into the MSRs. */
281
282 if (have_a0 == 1) /* A0 errata 5 */
283 local_irq_disable();
284
285 if (freqs.old > freqs.new) {
286 /* Going down, so change FID first */
287 change_FID(fid);
288 change_VID(vid);
289 } else {
290 /* Going up, so change VID first */
291 change_VID(vid);
292 change_FID(fid);
293 }
294
295
296 if (have_a0 == 1)
297 local_irq_enable();
298
299 cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
300}
301
302
303#ifdef CONFIG_X86_POWERNOW_K7_ACPI
304
305static struct acpi_processor_performance *acpi_processor_perf;
306
307static int powernow_acpi_init(void)
308{
309 int i;
310 int retval = 0;
311 union powernow_acpi_control_t pc;
312
313 if (acpi_processor_perf != NULL && powernow_table != NULL) {
314 retval = -EINVAL;
315 goto err0;
316 }
317
Dave Jonesbfdc7082005-10-20 15:16:15 -0700318 acpi_processor_perf = kzalloc(sizeof(struct acpi_processor_performance),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700319 GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700320 if (!acpi_processor_perf) {
321 retval = -ENOMEM;
322 goto err0;
323 }
324
Rusty Russell2fdf66b2008-12-31 18:08:47 -0800325 if (!alloc_cpumask_var(&acpi_processor_perf->shared_cpu_map,
326 GFP_KERNEL)) {
327 retval = -ENOMEM;
328 goto err05;
329 }
330
Linus Torvalds1da177e2005-04-16 15:20:36 -0700331 if (acpi_processor_register_performance(acpi_processor_perf, 0)) {
332 retval = -EIO;
333 goto err1;
334 }
335
Dave Jonesb9e76382009-01-18 00:32:26 -0500336 if (acpi_processor_perf->control_register.space_id !=
337 ACPI_ADR_SPACE_FIXED_HARDWARE) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700338 retval = -ENODEV;
339 goto err2;
340 }
341
Dave Jonesb9e76382009-01-18 00:32:26 -0500342 if (acpi_processor_perf->status_register.space_id !=
343 ACPI_ADR_SPACE_FIXED_HARDWARE) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700344 retval = -ENODEV;
345 goto err2;
346 }
347
348 number_scales = acpi_processor_perf->state_count;
349
350 if (number_scales < 2) {
351 retval = -ENODEV;
352 goto err2;
353 }
354
Dave Jonesb9e76382009-01-18 00:32:26 -0500355 powernow_table = kzalloc((sizeof(struct cpufreq_frequency_table) *
356 (number_scales + 1)), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700357 if (!powernow_table) {
358 retval = -ENOMEM;
359 goto err2;
360 }
361
Linus Torvalds1da177e2005-04-16 15:20:36 -0700362 pc.val = (unsigned long) acpi_processor_perf->states[0].control;
363 for (i = 0; i < number_scales; i++) {
364 u8 fid, vid;
Daniel Drakedc2585e2007-05-02 23:19:05 +0100365 struct acpi_processor_px *state =
366 &acpi_processor_perf->states[i];
367 unsigned int speed, speed_mhz;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700368
Daniel Drakedc2585e2007-05-02 23:19:05 +0100369 pc.val = (unsigned long) state->control;
Dave Jonesb9e76382009-01-18 00:32:26 -0500370 dprintk("acpi: P%d: %d MHz %d mW %d uS control %08x SGTC %d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700371 i,
Daniel Drakedc2585e2007-05-02 23:19:05 +0100372 (u32) state->core_frequency,
373 (u32) state->power,
374 (u32) state->transition_latency,
375 (u32) state->control,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376 pc.bits.sgtc);
377
378 vid = pc.bits.vid;
379 fid = pc.bits.fid;
380
381 powernow_table[i].frequency = fsb * fid_codes[fid] / 10;
382 powernow_table[i].index = fid; /* lower 8 bits */
383 powernow_table[i].index |= (vid << 8); /* upper 8 bits */
384
385 speed = powernow_table[i].frequency;
Daniel Drakedc2585e2007-05-02 23:19:05 +0100386 speed_mhz = speed / 1000;
387
388 /* processor_perflib will multiply the MHz value by 1000 to
389 * get a KHz value (e.g. 1266000). However, powernow-k7 works
390 * with true KHz values (e.g. 1266768). To ensure that all
391 * powernow frequencies are available, we must ensure that
392 * ACPI doesn't restrict them, so we round up the MHz value
393 * to ensure that perflib's computed KHz value is greater than
394 * or equal to powernow's KHz value.
395 */
396 if (speed % 1000 > 0)
397 speed_mhz++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700398
Dave Jonesb9e76382009-01-18 00:32:26 -0500399 if ((fid_codes[fid] % 10) == 5) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700400 if (have_a0 == 1)
Dave Jonesb9e76382009-01-18 00:32:26 -0500401 invalidate_entry(i);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700402 }
403
Dave Jonesb9e76382009-01-18 00:32:26 -0500404 dprintk(" FID: 0x%x (%d.%dx [%dMHz]) "
Dave Jones32ee8c32006-02-28 00:43:23 -0500405 "VID: 0x%x (%d.%03dV)\n", fid, fid_codes[fid] / 10,
Daniel Drakedc2585e2007-05-02 23:19:05 +0100406 fid_codes[fid] % 10, speed_mhz, vid,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700407 mobile_vid_table[vid]/1000,
408 mobile_vid_table[vid]%1000);
409
Daniel Drakedc2585e2007-05-02 23:19:05 +0100410 if (state->core_frequency != speed_mhz) {
411 state->core_frequency = speed_mhz;
412 dprintk(" Corrected ACPI frequency to %d\n",
413 speed_mhz);
414 }
415
Linus Torvalds1da177e2005-04-16 15:20:36 -0700416 if (latency < pc.bits.sgtc)
417 latency = pc.bits.sgtc;
418
419 if (speed < minimum_speed)
420 minimum_speed = speed;
421 if (speed > maximum_speed)
422 maximum_speed = speed;
423 }
424
425 powernow_table[i].frequency = CPUFREQ_TABLE_END;
426 powernow_table[i].index = 0;
427
428 /* notify BIOS that we exist */
429 acpi_processor_notify_smm(THIS_MODULE);
430
431 return 0;
432
433err2:
434 acpi_processor_unregister_performance(acpi_processor_perf, 0);
435err1:
Rusty Russell2fdf66b2008-12-31 18:08:47 -0800436 free_cpumask_var(acpi_processor_perf->shared_cpu_map);
437err05:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700438 kfree(acpi_processor_perf);
439err0:
Dave Jonesb9e76382009-01-18 00:32:26 -0500440 printk(KERN_WARNING PFX "ACPI perflib can not be used on "
441 "this platform\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700442 acpi_processor_perf = NULL;
443 return retval;
444}
445#else
446static int powernow_acpi_init(void)
447{
448 printk(KERN_INFO PFX "no support for ACPI processor found."
449 " Please recompile your kernel with ACPI processor\n");
450 return -EINVAL;
451}
452#endif
453
Dave Jonesb9e76382009-01-18 00:32:26 -0500454static void print_pst_entry(struct pst_s *pst, unsigned int j)
455{
456 dprintk("PST:%d (@%p)\n", j, pst);
457 dprintk(" cpuid: 0x%x fsb: %d maxFID: 0x%x startvid: 0x%x\n",
458 pst->cpuid, pst->fsbspeed, pst->maxfid, pst->startvid);
459}
460
461static int powernow_decode_bios(int maxfid, int startvid)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700462{
463 struct psb_s *psb;
464 struct pst_s *pst;
465 unsigned int i, j;
466 unsigned char *p;
467 unsigned int etuple;
468 unsigned int ret;
469
470 etuple = cpuid_eax(0x80000001);
471
Dave Jonesb9e76382009-01-18 00:32:26 -0500472 for (i = 0xC0000; i < 0xffff0 ; i += 16) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700473
474 p = phys_to_virt(i);
475
Dave Jonesb9e76382009-01-18 00:32:26 -0500476 if (memcmp(p, "AMDK7PNOW!", 10) == 0) {
477 dprintk("Found PSB header at %p\n", p);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700478 psb = (struct psb_s *) p;
Dave Jonesb9e76382009-01-18 00:32:26 -0500479 dprintk("Table version: 0x%x\n", psb->tableversion);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700480 if (psb->tableversion != 0x12) {
Dave Jonesb9e76382009-01-18 00:32:26 -0500481 printk(KERN_INFO PFX "Sorry, only v1.2 tables"
482 " supported right now\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700483 return -ENODEV;
484 }
485
Dave Jonesb9e76382009-01-18 00:32:26 -0500486 dprintk("Flags: 0x%x\n", psb->flags);
487 if ((psb->flags & 1) == 0)
488 dprintk("Mobile voltage regulator\n");
489 else
490 dprintk("Desktop voltage regulator\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700491
492 latency = psb->settlingtime;
493 if (latency < 100) {
Dave Jonesb9e76382009-01-18 00:32:26 -0500494 printk(KERN_INFO PFX "BIOS set settling time "
495 "to %d microseconds. "
496 "Should be at least 100. "
497 "Correcting.\n", latency);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498 latency = 100;
499 }
Dave Jonesb9e76382009-01-18 00:32:26 -0500500 dprintk("Settling Time: %d microseconds.\n",
501 psb->settlingtime);
502 dprintk("Has %d PST tables. (Only dumping ones "
503 "relevant to this CPU).\n",
504 psb->numpst);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700505
Dave Jonesb9e76382009-01-18 00:32:26 -0500506 p += sizeof(struct psb_s);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700507
508 pst = (struct pst_s *) p;
509
Dave Jonesb9e76382009-01-18 00:32:26 -0500510 for (j = 0; j < psb->numpst; j++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700511 pst = (struct pst_s *) p;
512 number_scales = pst->numpstates;
513
Dave Jonesb9e76382009-01-18 00:32:26 -0500514 if ((etuple == pst->cpuid) &&
515 check_fsb(pst->fsbspeed) &&
516 (maxfid == pst->maxfid) &&
517 (startvid == pst->startvid)) {
518 print_pst_entry(pst, j);
519 p = (char *)pst + sizeof(struct pst_s);
520 ret = get_ranges(p);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700521 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700522 } else {
Dave Jones8cbe0162006-05-30 17:26:08 -0400523 unsigned int k;
Dave Jonesb9e76382009-01-18 00:32:26 -0500524 p = (char *)pst + sizeof(struct pst_s);
525 for (k = 0; k < number_scales; k++)
526 p += 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700527 }
528 }
Dave Jonesb9e76382009-01-18 00:32:26 -0500529 printk(KERN_INFO PFX "No PST tables match this cpuid "
530 "(0x%x)\n", etuple);
531 printk(KERN_INFO PFX "This is indicative of a broken "
532 "BIOS.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700533
534 return -EINVAL;
535 }
536 p++;
537 }
538
539 return -ENODEV;
540}
541
542
Dave Jonesb9e76382009-01-18 00:32:26 -0500543static int powernow_target(struct cpufreq_policy *policy,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700544 unsigned int target_freq,
545 unsigned int relation)
546{
547 unsigned int newstate;
548
Dave Jonesb9e76382009-01-18 00:32:26 -0500549 if (cpufreq_frequency_table_target(policy, powernow_table, target_freq,
550 relation, &newstate))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700551 return -EINVAL;
552
553 change_speed(newstate);
554
555 return 0;
556}
557
558
Dave Jonesb9e76382009-01-18 00:32:26 -0500559static int powernow_verify(struct cpufreq_policy *policy)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700560{
561 return cpufreq_frequency_table_verify(policy, powernow_table);
562}
563
564/*
565 * We use the fact that the bus frequency is somehow
566 * a multiple of 100000/3 khz, then we compute sgtc according
567 * to this multiple.
568 * That way, we match more how AMD thinks all of that work.
569 * We will then get the same kind of behaviour already tested under
570 * the "well-known" other OS.
571 */
572static int __init fixup_sgtc(void)
573{
574 unsigned int sgtc;
575 unsigned int m;
576
577 m = fsb / 3333;
578 if ((m % 10) >= 5)
579 m += 5;
580
581 m /= 10;
582
583 sgtc = 100 * m * latency;
584 sgtc = sgtc / 3;
585 if (sgtc > 0xfffff) {
586 printk(KERN_WARNING PFX "SGTC too large %d\n", sgtc);
587 sgtc = 0xfffff;
588 }
589 return sgtc;
590}
591
592static unsigned int powernow_get(unsigned int cpu)
593{
594 union msr_fidvidstatus fidvidstatus;
595 unsigned int cfid;
596
597 if (cpu)
598 return 0;
Dave Jonesb9e76382009-01-18 00:32:26 -0500599 rdmsrl(MSR_K7_FID_VID_STATUS, fidvidstatus.val);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700600 cfid = fidvidstatus.bits.CFID;
601
Dave Jonesb9e76382009-01-18 00:32:26 -0500602 return fsb * fid_codes[cfid] / 10;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700603}
604
605
Thomas Gleixner0b4b5dd2007-10-17 18:04:40 +0200606static int __init acer_cpufreq_pst(const struct dmi_system_id *d)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700607{
Dave Jonesb9e76382009-01-18 00:32:26 -0500608 printk(KERN_WARNING PFX
609 "%s laptop with broken PST tables in BIOS detected.\n",
610 d->ident);
611 printk(KERN_WARNING PFX
612 "You need to downgrade to 3A21 (09/09/2002), or try a newer "
613 "BIOS than 3A71 (01/20/2003)\n");
614 printk(KERN_WARNING PFX
615 "cpufreq scaling has been disabled as a result of this.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700616 return 0;
617}
618
619/*
620 * Some Athlon laptops have really fucked PST tables.
621 * A BIOS update is all that can save them.
622 * Mention this, and disable cpufreq.
623 */
624static struct dmi_system_id __initdata powernow_dmi_table[] = {
625 {
626 .callback = acer_cpufreq_pst,
627 .ident = "Acer Aspire",
628 .matches = {
629 DMI_MATCH(DMI_SYS_VENDOR, "Insyde Software"),
630 DMI_MATCH(DMI_BIOS_VERSION, "3A71"),
631 },
632 },
633 { }
634};
635
Dave Jonesb9e76382009-01-18 00:32:26 -0500636static int __init powernow_cpu_init(struct cpufreq_policy *policy)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700637{
638 union msr_fidvidstatus fidvidstatus;
639 int result;
640
641 if (policy->cpu != 0)
642 return -ENODEV;
643
Dave Jonesb9e76382009-01-18 00:32:26 -0500644 rdmsrl(MSR_K7_FID_VID_STATUS, fidvidstatus.val);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700645
Dave Jones436fe7b2006-06-05 14:03:50 -0400646 recalibrate_cpu_khz();
Dave Jones91350ed2005-05-31 19:03:45 -0700647
648 fsb = (10 * cpu_khz) / fid_codes[fidvidstatus.bits.CFID];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700649 if (!fsb) {
650 printk(KERN_WARNING PFX "can not determine bus frequency\n");
651 return -EINVAL;
652 }
Dave Jones7eb53d82005-05-31 19:03:42 -0700653 dprintk("FSB: %3dMHz\n", fsb/1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700654
655 if (dmi_check_system(powernow_dmi_table) || acpi_force) {
Dave Jonesb9e76382009-01-18 00:32:26 -0500656 printk(KERN_INFO PFX "PSB/PST known to be broken. "
657 "Trying ACPI instead\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700658 result = powernow_acpi_init();
659 } else {
Dave Jonesb9e76382009-01-18 00:32:26 -0500660 result = powernow_decode_bios(fidvidstatus.bits.MFID,
661 fidvidstatus.bits.SVID);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700662 if (result) {
Dave Jonesb9e76382009-01-18 00:32:26 -0500663 printk(KERN_INFO PFX "Trying ACPI perflib\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700664 maximum_speed = 0;
665 minimum_speed = -1;
666 latency = 0;
667 result = powernow_acpi_init();
668 if (result) {
Dave Jonesb9e76382009-01-18 00:32:26 -0500669 printk(KERN_INFO PFX
670 "ACPI and legacy methods failed\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700671 }
672 } else {
673 /* SGTC use the bus clock as timer */
674 latency = fixup_sgtc();
675 printk(KERN_INFO PFX "SGTC: %d\n", latency);
676 }
677 }
678
679 if (result)
680 return result;
681
Dave Jonesb9e76382009-01-18 00:32:26 -0500682 printk(KERN_INFO PFX "Minimum speed %d MHz. Maximum speed %d MHz.\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700683 minimum_speed/1000, maximum_speed/1000);
684
Dave Jonesb9e76382009-01-18 00:32:26 -0500685 policy->cpuinfo.transition_latency =
686 cpufreq_scale(2000000UL, fsb, latency);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700687
688 policy->cur = powernow_get(0);
689
690 cpufreq_frequency_table_get_attr(powernow_table, policy->cpu);
691
692 return cpufreq_frequency_table_cpuinfo(policy, powernow_table);
693}
694
Dave Jonesb9e76382009-01-18 00:32:26 -0500695static int powernow_cpu_exit(struct cpufreq_policy *policy)
696{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700697 cpufreq_frequency_table_put_attr(policy->cpu);
698
699#ifdef CONFIG_X86_POWERNOW_K7_ACPI
700 if (acpi_processor_perf) {
701 acpi_processor_unregister_performance(acpi_processor_perf, 0);
Rusty Russell2fdf66b2008-12-31 18:08:47 -0800702 free_cpumask_var(acpi_processor_perf->shared_cpu_map);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700703 kfree(acpi_processor_perf);
704 }
705#endif
706
Jesper Juhl4ae66732005-06-25 14:58:48 -0700707 kfree(powernow_table);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700708 return 0;
709}
710
Dave Jonesb9e76382009-01-18 00:32:26 -0500711static struct freq_attr *powernow_table_attr[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700712 &cpufreq_freq_attr_scaling_available_freqs,
713 NULL,
714};
715
Linus Torvalds221dee22007-02-26 14:55:48 -0800716static struct cpufreq_driver powernow_driver = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700717 .verify = powernow_verify,
718 .target = powernow_target,
719 .get = powernow_get,
720 .init = powernow_cpu_init,
721 .exit = powernow_cpu_exit,
722 .name = "powernow-k7",
723 .owner = THIS_MODULE,
724 .attr = powernow_table_attr,
725};
726
Dave Jonesb9e76382009-01-18 00:32:26 -0500727static int __init powernow_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700728{
Dave Jonesb9e76382009-01-18 00:32:26 -0500729 if (check_powernow() == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700730 return -ENODEV;
731 return cpufreq_register_driver(&powernow_driver);
732}
733
734
Dave Jonesb9e76382009-01-18 00:32:26 -0500735static void __exit powernow_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700736{
737 cpufreq_unregister_driver(&powernow_driver);
738}
739
740module_param(acpi_force, int, 0444);
741MODULE_PARM_DESC(acpi_force, "Force ACPI to be used.");
742
Dave Jonesb9e76382009-01-18 00:32:26 -0500743MODULE_AUTHOR("Dave Jones <davej@redhat.com>");
744MODULE_DESCRIPTION("Powernow driver for AMD K7 processors.");
745MODULE_LICENSE("GPL");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700746
747late_initcall(powernow_init);
748module_exit(powernow_exit);
749