blob: d71d9f3723590cd659323e58338ca0c2a7ee9319 [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
Linus Torvalds1da177e2005-04-16 15:20:36 -070071/* divide by 1000 to get VCore voltage in V. */
Dave Jonesbd5ab262007-02-22 19:11:16 -050072static const int mobile_vid_table[32] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -070073 2000, 1950, 1900, 1850, 1800, 1750, 1700, 1650,
74 1600, 1550, 1500, 1450, 1400, 1350, 1300, 0,
75 1275, 1250, 1225, 1200, 1175, 1150, 1125, 1100,
76 1075, 1050, 1025, 1000, 975, 950, 925, 0,
77};
Linus Torvalds1da177e2005-04-16 15:20:36 -070078
79/* divide by 10 to get FID. */
Dave Jonesbd5ab262007-02-22 19:11:16 -050080static const int fid_codes[32] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -070081 110, 115, 120, 125, 50, 55, 60, 65,
82 70, 75, 80, 85, 90, 95, 100, 105,
83 30, 190, 40, 200, 130, 135, 140, 210,
84 150, 225, 160, 165, 170, 180, -1, -1,
85};
86
87/* This parameter is used in order to force ACPI instead of legacy method for
88 * configuration purpose.
89 */
90
91static int acpi_force;
92
93static struct cpufreq_frequency_table *powernow_table;
94
95static unsigned int can_scale_bus;
96static unsigned int can_scale_vid;
Dave Jonesfff78ad2009-01-17 22:28:42 -050097static unsigned int minimum_speed = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -070098static unsigned int maximum_speed;
99static unsigned int number_scales;
100static unsigned int fsb;
101static unsigned int latency;
102static char have_a0;
103
Linus Torvalds1da177e2005-04-16 15:20:36 -0700104static int check_fsb(unsigned int fsbspeed)
105{
106 int delta;
107 unsigned int f = fsb / 1000;
108
109 delta = (fsbspeed > f) ? fsbspeed - f : f - fsbspeed;
Dave Jonesb9e76382009-01-18 00:32:26 -0500110 return delta < 5;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700111}
112
113static int check_powernow(void)
114{
Mike Travis92cb7612007-10-19 20:35:04 +0200115 struct cpuinfo_x86 *c = &cpu_data(0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700116 unsigned int maxei, eax, ebx, ecx, edx;
117
Dave Jonesb9e76382009-01-18 00:32:26 -0500118 if ((c->x86_vendor != X86_VENDOR_AMD) || (c->x86 != 6)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700119#ifdef MODULE
Dave Jonesb9e76382009-01-18 00:32:26 -0500120 printk(KERN_INFO PFX "This module only works with "
121 "AMD K7 CPUs\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700122#endif
123 return 0;
124 }
125
126 /* Get maximum capabilities */
Dave Jonesb9e76382009-01-18 00:32:26 -0500127 maxei = cpuid_eax(0x80000000);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700128 if (maxei < 0x80000007) { /* Any powernow info ? */
129#ifdef MODULE
Dave Jonesb9e76382009-01-18 00:32:26 -0500130 printk(KERN_INFO PFX "No powernow capabilities detected\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700131#endif
132 return 0;
133 }
134
135 if ((c->x86_model == 6) && (c->x86_mask == 0)) {
Dave Jonesb9e76382009-01-18 00:32:26 -0500136 printk(KERN_INFO PFX "K7 660[A0] core detected, "
137 "enabling errata workarounds\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700138 have_a0 = 1;
139 }
140
141 cpuid(0x80000007, &eax, &ebx, &ecx, &edx);
142
143 /* Check we can actually do something before we say anything.*/
144 if (!(edx & (1 << 1 | 1 << 2)))
145 return 0;
146
Dave Jonesb9e76382009-01-18 00:32:26 -0500147 printk(KERN_INFO PFX "PowerNOW! Technology present. Can scale: ");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700148
149 if (edx & 1 << 1) {
Dave Jonesb9e76382009-01-18 00:32:26 -0500150 printk("frequency");
151 can_scale_bus = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700152 }
153
154 if ((edx & (1 << 1 | 1 << 2)) == 0x6)
Dave Jonesb9e76382009-01-18 00:32:26 -0500155 printk(" and ");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700156
157 if (edx & 1 << 2) {
Dave Jonesb9e76382009-01-18 00:32:26 -0500158 printk("voltage");
159 can_scale_vid = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700160 }
161
Dave Jonesb9e76382009-01-18 00:32:26 -0500162 printk(".\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700163 return 1;
164}
165
Dave Jonesd38e73e2009-04-23 13:36:12 -0400166#ifdef CONFIG_X86_POWERNOW_K7_ACPI
Dave Jonesb9e76382009-01-18 00:32:26 -0500167static void invalidate_entry(unsigned int entry)
168{
169 powernow_table[entry].frequency = CPUFREQ_ENTRY_INVALID;
170}
Dave Jonesd38e73e2009-04-23 13:36:12 -0400171#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700172
Dave Jonesb9e76382009-01-18 00:32:26 -0500173static int get_ranges(unsigned char *pst)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700174{
175 unsigned int j;
176 unsigned int speed;
177 u8 fid, vid;
178
Dave Jonesb9e76382009-01-18 00:32:26 -0500179 powernow_table = kzalloc((sizeof(struct cpufreq_frequency_table) *
180 (number_scales + 1)), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700181 if (!powernow_table)
182 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183
Dave Jonesb9e76382009-01-18 00:32:26 -0500184 for (j = 0 ; j < number_scales; j++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700185 fid = *pst++;
186
187 powernow_table[j].frequency = (fsb * fid_codes[fid]) / 10;
188 powernow_table[j].index = fid; /* lower 8 bits */
189
190 speed = powernow_table[j].frequency;
191
Dave Jonesb9e76382009-01-18 00:32:26 -0500192 if ((fid_codes[fid] % 10) == 5) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700193#ifdef CONFIG_X86_POWERNOW_K7_ACPI
194 if (have_a0 == 1)
Dave Jonesb9e76382009-01-18 00:32:26 -0500195 invalidate_entry(j);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700196#endif
197 }
198
199 if (speed < minimum_speed)
200 minimum_speed = speed;
201 if (speed > maximum_speed)
202 maximum_speed = speed;
203
204 vid = *pst++;
205 powernow_table[j].index |= (vid << 8); /* upper 8 bits */
206
Dominik Brodowski2d06d8c2011-03-27 15:04:46 +0200207 pr_debug(" FID: 0x%x (%d.%dx [%dMHz]) "
Dave Jones32ee8c32006-02-28 00:43:23 -0500208 "VID: 0x%x (%d.%03dV)\n", fid, fid_codes[fid] / 10,
209 fid_codes[fid] % 10, speed/1000, vid,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700210 mobile_vid_table[vid]/1000,
211 mobile_vid_table[vid]%1000);
212 }
213 powernow_table[number_scales].frequency = CPUFREQ_TABLE_END;
214 powernow_table[number_scales].index = 0;
215
216 return 0;
217}
218
219
220static void change_FID(int fid)
221{
222 union msr_fidvidctl fidvidctl;
223
Dave Jonesb9e76382009-01-18 00:32:26 -0500224 rdmsrl(MSR_K7_FID_VID_CTL, fidvidctl.val);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700225 if (fidvidctl.bits.FID != fid) {
226 fidvidctl.bits.SGTC = latency;
227 fidvidctl.bits.FID = fid;
228 fidvidctl.bits.VIDC = 0;
229 fidvidctl.bits.FIDC = 1;
Dave Jonesb9e76382009-01-18 00:32:26 -0500230 wrmsrl(MSR_K7_FID_VID_CTL, fidvidctl.val);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700231 }
232}
233
234
235static void change_VID(int vid)
236{
237 union msr_fidvidctl fidvidctl;
238
Dave Jonesb9e76382009-01-18 00:32:26 -0500239 rdmsrl(MSR_K7_FID_VID_CTL, fidvidctl.val);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700240 if (fidvidctl.bits.VID != vid) {
241 fidvidctl.bits.SGTC = latency;
242 fidvidctl.bits.VID = vid;
243 fidvidctl.bits.FIDC = 0;
244 fidvidctl.bits.VIDC = 1;
Dave Jonesb9e76382009-01-18 00:32:26 -0500245 wrmsrl(MSR_K7_FID_VID_CTL, fidvidctl.val);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700246 }
247}
248
249
Dave Jonesb9e76382009-01-18 00:32:26 -0500250static void change_speed(unsigned int index)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700251{
252 u8 fid, vid;
253 struct cpufreq_freqs freqs;
254 union msr_fidvidstatus fidvidstatus;
255 int cfid;
256
257 /* fid are the lower 8 bits of the index we stored into
258 * the cpufreq frequency table in powernow_decode_bios,
259 * vid are the upper 8 bits.
260 */
261
262 fid = powernow_table[index].index & 0xFF;
263 vid = (powernow_table[index].index & 0xFF00) >> 8;
264
265 freqs.cpu = 0;
266
Dave Jonesb9e76382009-01-18 00:32:26 -0500267 rdmsrl(MSR_K7_FID_VID_STATUS, fidvidstatus.val);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700268 cfid = fidvidstatus.bits.CFID;
269 freqs.old = fsb * fid_codes[cfid] / 10;
270
271 freqs.new = powernow_table[index].frequency;
272
273 cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
274
275 /* Now do the magic poking into the MSRs. */
276
277 if (have_a0 == 1) /* A0 errata 5 */
278 local_irq_disable();
279
280 if (freqs.old > freqs.new) {
281 /* Going down, so change FID first */
282 change_FID(fid);
283 change_VID(vid);
284 } else {
285 /* Going up, so change VID first */
286 change_VID(vid);
287 change_FID(fid);
288 }
289
290
291 if (have_a0 == 1)
292 local_irq_enable();
293
294 cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
295}
296
297
298#ifdef CONFIG_X86_POWERNOW_K7_ACPI
299
300static struct acpi_processor_performance *acpi_processor_perf;
301
302static int powernow_acpi_init(void)
303{
304 int i;
305 int retval = 0;
306 union powernow_acpi_control_t pc;
307
308 if (acpi_processor_perf != NULL && powernow_table != NULL) {
309 retval = -EINVAL;
310 goto err0;
311 }
312
Dave Jonesbfdc7082005-10-20 15:16:15 -0700313 acpi_processor_perf = kzalloc(sizeof(struct acpi_processor_performance),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700314 GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700315 if (!acpi_processor_perf) {
316 retval = -ENOMEM;
317 goto err0;
318 }
319
Yinghai Lueaa95842009-06-06 14:51:36 -0700320 if (!zalloc_cpumask_var(&acpi_processor_perf->shared_cpu_map,
Rusty Russell2fdf66b2008-12-31 18:08:47 -0800321 GFP_KERNEL)) {
322 retval = -ENOMEM;
323 goto err05;
324 }
325
Linus Torvalds1da177e2005-04-16 15:20:36 -0700326 if (acpi_processor_register_performance(acpi_processor_perf, 0)) {
327 retval = -EIO;
328 goto err1;
329 }
330
Dave Jonesb9e76382009-01-18 00:32:26 -0500331 if (acpi_processor_perf->control_register.space_id !=
332 ACPI_ADR_SPACE_FIXED_HARDWARE) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333 retval = -ENODEV;
334 goto err2;
335 }
336
Dave Jonesb9e76382009-01-18 00:32:26 -0500337 if (acpi_processor_perf->status_register.space_id !=
338 ACPI_ADR_SPACE_FIXED_HARDWARE) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700339 retval = -ENODEV;
340 goto err2;
341 }
342
343 number_scales = acpi_processor_perf->state_count;
344
345 if (number_scales < 2) {
346 retval = -ENODEV;
347 goto err2;
348 }
349
Dave Jonesb9e76382009-01-18 00:32:26 -0500350 powernow_table = kzalloc((sizeof(struct cpufreq_frequency_table) *
351 (number_scales + 1)), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700352 if (!powernow_table) {
353 retval = -ENOMEM;
354 goto err2;
355 }
356
Linus Torvalds1da177e2005-04-16 15:20:36 -0700357 pc.val = (unsigned long) acpi_processor_perf->states[0].control;
358 for (i = 0; i < number_scales; i++) {
359 u8 fid, vid;
Daniel Drakedc2585e2007-05-02 23:19:05 +0100360 struct acpi_processor_px *state =
361 &acpi_processor_perf->states[i];
362 unsigned int speed, speed_mhz;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700363
Daniel Drakedc2585e2007-05-02 23:19:05 +0100364 pc.val = (unsigned long) state->control;
Dominik Brodowski2d06d8c2011-03-27 15:04:46 +0200365 pr_debug("acpi: P%d: %d MHz %d mW %d uS control %08x SGTC %d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700366 i,
Daniel Drakedc2585e2007-05-02 23:19:05 +0100367 (u32) state->core_frequency,
368 (u32) state->power,
369 (u32) state->transition_latency,
370 (u32) state->control,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700371 pc.bits.sgtc);
372
373 vid = pc.bits.vid;
374 fid = pc.bits.fid;
375
376 powernow_table[i].frequency = fsb * fid_codes[fid] / 10;
377 powernow_table[i].index = fid; /* lower 8 bits */
378 powernow_table[i].index |= (vid << 8); /* upper 8 bits */
379
380 speed = powernow_table[i].frequency;
Daniel Drakedc2585e2007-05-02 23:19:05 +0100381 speed_mhz = speed / 1000;
382
383 /* processor_perflib will multiply the MHz value by 1000 to
384 * get a KHz value (e.g. 1266000). However, powernow-k7 works
385 * with true KHz values (e.g. 1266768). To ensure that all
386 * powernow frequencies are available, we must ensure that
387 * ACPI doesn't restrict them, so we round up the MHz value
388 * to ensure that perflib's computed KHz value is greater than
389 * or equal to powernow's KHz value.
390 */
391 if (speed % 1000 > 0)
392 speed_mhz++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700393
Dave Jonesb9e76382009-01-18 00:32:26 -0500394 if ((fid_codes[fid] % 10) == 5) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700395 if (have_a0 == 1)
Dave Jonesb9e76382009-01-18 00:32:26 -0500396 invalidate_entry(i);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700397 }
398
Dominik Brodowski2d06d8c2011-03-27 15:04:46 +0200399 pr_debug(" FID: 0x%x (%d.%dx [%dMHz]) "
Dave Jones32ee8c32006-02-28 00:43:23 -0500400 "VID: 0x%x (%d.%03dV)\n", fid, fid_codes[fid] / 10,
Daniel Drakedc2585e2007-05-02 23:19:05 +0100401 fid_codes[fid] % 10, speed_mhz, vid,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700402 mobile_vid_table[vid]/1000,
403 mobile_vid_table[vid]%1000);
404
Daniel Drakedc2585e2007-05-02 23:19:05 +0100405 if (state->core_frequency != speed_mhz) {
406 state->core_frequency = speed_mhz;
Dominik Brodowski2d06d8c2011-03-27 15:04:46 +0200407 pr_debug(" Corrected ACPI frequency to %d\n",
Daniel Drakedc2585e2007-05-02 23:19:05 +0100408 speed_mhz);
409 }
410
Linus Torvalds1da177e2005-04-16 15:20:36 -0700411 if (latency < pc.bits.sgtc)
412 latency = pc.bits.sgtc;
413
414 if (speed < minimum_speed)
415 minimum_speed = speed;
416 if (speed > maximum_speed)
417 maximum_speed = speed;
418 }
419
420 powernow_table[i].frequency = CPUFREQ_TABLE_END;
421 powernow_table[i].index = 0;
422
423 /* notify BIOS that we exist */
424 acpi_processor_notify_smm(THIS_MODULE);
425
426 return 0;
427
428err2:
429 acpi_processor_unregister_performance(acpi_processor_perf, 0);
430err1:
Rusty Russell2fdf66b2008-12-31 18:08:47 -0800431 free_cpumask_var(acpi_processor_perf->shared_cpu_map);
432err05:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700433 kfree(acpi_processor_perf);
434err0:
Dave Jonesb9e76382009-01-18 00:32:26 -0500435 printk(KERN_WARNING PFX "ACPI perflib can not be used on "
436 "this platform\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700437 acpi_processor_perf = NULL;
438 return retval;
439}
440#else
441static int powernow_acpi_init(void)
442{
443 printk(KERN_INFO PFX "no support for ACPI processor found."
444 " Please recompile your kernel with ACPI processor\n");
445 return -EINVAL;
446}
447#endif
448
Dave Jonesb9e76382009-01-18 00:32:26 -0500449static void print_pst_entry(struct pst_s *pst, unsigned int j)
450{
Dominik Brodowski2d06d8c2011-03-27 15:04:46 +0200451 pr_debug("PST:%d (@%p)\n", j, pst);
452 pr_debug(" cpuid: 0x%x fsb: %d maxFID: 0x%x startvid: 0x%x\n",
Dave Jonesb9e76382009-01-18 00:32:26 -0500453 pst->cpuid, pst->fsbspeed, pst->maxfid, pst->startvid);
454}
455
456static int powernow_decode_bios(int maxfid, int startvid)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700457{
458 struct psb_s *psb;
459 struct pst_s *pst;
460 unsigned int i, j;
461 unsigned char *p;
462 unsigned int etuple;
463 unsigned int ret;
464
465 etuple = cpuid_eax(0x80000001);
466
Dave Jonesb9e76382009-01-18 00:32:26 -0500467 for (i = 0xC0000; i < 0xffff0 ; i += 16) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700468
469 p = phys_to_virt(i);
470
Dave Jonesb9e76382009-01-18 00:32:26 -0500471 if (memcmp(p, "AMDK7PNOW!", 10) == 0) {
Dominik Brodowski2d06d8c2011-03-27 15:04:46 +0200472 pr_debug("Found PSB header at %p\n", p);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700473 psb = (struct psb_s *) p;
Dominik Brodowski2d06d8c2011-03-27 15:04:46 +0200474 pr_debug("Table version: 0x%x\n", psb->tableversion);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700475 if (psb->tableversion != 0x12) {
Dave Jonesb9e76382009-01-18 00:32:26 -0500476 printk(KERN_INFO PFX "Sorry, only v1.2 tables"
477 " supported right now\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700478 return -ENODEV;
479 }
480
Dominik Brodowski2d06d8c2011-03-27 15:04:46 +0200481 pr_debug("Flags: 0x%x\n", psb->flags);
Dave Jonesb9e76382009-01-18 00:32:26 -0500482 if ((psb->flags & 1) == 0)
Dominik Brodowski2d06d8c2011-03-27 15:04:46 +0200483 pr_debug("Mobile voltage regulator\n");
Dave Jonesb9e76382009-01-18 00:32:26 -0500484 else
Dominik Brodowski2d06d8c2011-03-27 15:04:46 +0200485 pr_debug("Desktop voltage regulator\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700486
487 latency = psb->settlingtime;
488 if (latency < 100) {
Dave Jonesb9e76382009-01-18 00:32:26 -0500489 printk(KERN_INFO PFX "BIOS set settling time "
490 "to %d microseconds. "
491 "Should be at least 100. "
492 "Correcting.\n", latency);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700493 latency = 100;
494 }
Dominik Brodowski2d06d8c2011-03-27 15:04:46 +0200495 pr_debug("Settling Time: %d microseconds.\n",
Dave Jonesb9e76382009-01-18 00:32:26 -0500496 psb->settlingtime);
Dominik Brodowski2d06d8c2011-03-27 15:04:46 +0200497 pr_debug("Has %d PST tables. (Only dumping ones "
Dave Jonesb9e76382009-01-18 00:32:26 -0500498 "relevant to this CPU).\n",
499 psb->numpst);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500
Dave Jonesb9e76382009-01-18 00:32:26 -0500501 p += sizeof(struct psb_s);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700502
503 pst = (struct pst_s *) p;
504
Dave Jonesb9e76382009-01-18 00:32:26 -0500505 for (j = 0; j < psb->numpst; j++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700506 pst = (struct pst_s *) p;
507 number_scales = pst->numpstates;
508
Dave Jonesb9e76382009-01-18 00:32:26 -0500509 if ((etuple == pst->cpuid) &&
510 check_fsb(pst->fsbspeed) &&
511 (maxfid == pst->maxfid) &&
512 (startvid == pst->startvid)) {
513 print_pst_entry(pst, j);
514 p = (char *)pst + sizeof(struct pst_s);
515 ret = get_ranges(p);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700516 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700517 } else {
Dave Jones8cbe0162006-05-30 17:26:08 -0400518 unsigned int k;
Dave Jonesb9e76382009-01-18 00:32:26 -0500519 p = (char *)pst + sizeof(struct pst_s);
520 for (k = 0; k < number_scales; k++)
521 p += 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700522 }
523 }
Dave Jonesb9e76382009-01-18 00:32:26 -0500524 printk(KERN_INFO PFX "No PST tables match this cpuid "
525 "(0x%x)\n", etuple);
526 printk(KERN_INFO PFX "This is indicative of a broken "
527 "BIOS.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700528
529 return -EINVAL;
530 }
531 p++;
532 }
533
534 return -ENODEV;
535}
536
537
Dave Jonesb9e76382009-01-18 00:32:26 -0500538static int powernow_target(struct cpufreq_policy *policy,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700539 unsigned int target_freq,
540 unsigned int relation)
541{
542 unsigned int newstate;
543
Dave Jonesb9e76382009-01-18 00:32:26 -0500544 if (cpufreq_frequency_table_target(policy, powernow_table, target_freq,
545 relation, &newstate))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700546 return -EINVAL;
547
548 change_speed(newstate);
549
550 return 0;
551}
552
553
Dave Jonesb9e76382009-01-18 00:32:26 -0500554static int powernow_verify(struct cpufreq_policy *policy)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700555{
556 return cpufreq_frequency_table_verify(policy, powernow_table);
557}
558
559/*
560 * We use the fact that the bus frequency is somehow
561 * a multiple of 100000/3 khz, then we compute sgtc according
562 * to this multiple.
563 * That way, we match more how AMD thinks all of that work.
564 * We will then get the same kind of behaviour already tested under
565 * the "well-known" other OS.
566 */
Holger Freyther307069c2010-07-19 03:29:16 +0800567static int __cpuinit fixup_sgtc(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700568{
569 unsigned int sgtc;
570 unsigned int m;
571
572 m = fsb / 3333;
573 if ((m % 10) >= 5)
574 m += 5;
575
576 m /= 10;
577
578 sgtc = 100 * m * latency;
579 sgtc = sgtc / 3;
580 if (sgtc > 0xfffff) {
581 printk(KERN_WARNING PFX "SGTC too large %d\n", sgtc);
582 sgtc = 0xfffff;
583 }
584 return sgtc;
585}
586
587static unsigned int powernow_get(unsigned int cpu)
588{
589 union msr_fidvidstatus fidvidstatus;
590 unsigned int cfid;
591
592 if (cpu)
593 return 0;
Dave Jonesb9e76382009-01-18 00:32:26 -0500594 rdmsrl(MSR_K7_FID_VID_STATUS, fidvidstatus.val);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700595 cfid = fidvidstatus.bits.CFID;
596
Dave Jonesb9e76382009-01-18 00:32:26 -0500597 return fsb * fid_codes[cfid] / 10;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700598}
599
600
Holger Freyther307069c2010-07-19 03:29:16 +0800601static int __cpuinit acer_cpufreq_pst(const struct dmi_system_id *d)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700602{
Dave Jonesb9e76382009-01-18 00:32:26 -0500603 printk(KERN_WARNING PFX
604 "%s laptop with broken PST tables in BIOS detected.\n",
605 d->ident);
606 printk(KERN_WARNING PFX
607 "You need to downgrade to 3A21 (09/09/2002), or try a newer "
608 "BIOS than 3A71 (01/20/2003)\n");
609 printk(KERN_WARNING PFX
610 "cpufreq scaling has been disabled as a result of this.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700611 return 0;
612}
613
614/*
615 * Some Athlon laptops have really fucked PST tables.
616 * A BIOS update is all that can save them.
617 * Mention this, and disable cpufreq.
618 */
Holger Freyther307069c2010-07-19 03:29:16 +0800619static struct dmi_system_id __cpuinitdata powernow_dmi_table[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700620 {
621 .callback = acer_cpufreq_pst,
622 .ident = "Acer Aspire",
623 .matches = {
624 DMI_MATCH(DMI_SYS_VENDOR, "Insyde Software"),
625 DMI_MATCH(DMI_BIOS_VERSION, "3A71"),
626 },
627 },
628 { }
629};
630
Holger Freyther307069c2010-07-19 03:29:16 +0800631static int __cpuinit powernow_cpu_init(struct cpufreq_policy *policy)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700632{
633 union msr_fidvidstatus fidvidstatus;
634 int result;
635
636 if (policy->cpu != 0)
637 return -ENODEV;
638
Dave Jonesb9e76382009-01-18 00:32:26 -0500639 rdmsrl(MSR_K7_FID_VID_STATUS, fidvidstatus.val);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700640
Dave Jones436fe7b2006-06-05 14:03:50 -0400641 recalibrate_cpu_khz();
Dave Jones91350ed2005-05-31 19:03:45 -0700642
643 fsb = (10 * cpu_khz) / fid_codes[fidvidstatus.bits.CFID];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700644 if (!fsb) {
645 printk(KERN_WARNING PFX "can not determine bus frequency\n");
646 return -EINVAL;
647 }
Dominik Brodowski2d06d8c2011-03-27 15:04:46 +0200648 pr_debug("FSB: %3dMHz\n", fsb/1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700649
650 if (dmi_check_system(powernow_dmi_table) || acpi_force) {
Dave Jonesb9e76382009-01-18 00:32:26 -0500651 printk(KERN_INFO PFX "PSB/PST known to be broken. "
652 "Trying ACPI instead\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700653 result = powernow_acpi_init();
654 } else {
Dave Jonesb9e76382009-01-18 00:32:26 -0500655 result = powernow_decode_bios(fidvidstatus.bits.MFID,
656 fidvidstatus.bits.SVID);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700657 if (result) {
Dave Jonesb9e76382009-01-18 00:32:26 -0500658 printk(KERN_INFO PFX "Trying ACPI perflib\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700659 maximum_speed = 0;
660 minimum_speed = -1;
661 latency = 0;
662 result = powernow_acpi_init();
663 if (result) {
Dave Jonesb9e76382009-01-18 00:32:26 -0500664 printk(KERN_INFO PFX
665 "ACPI and legacy methods failed\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700666 }
667 } else {
668 /* SGTC use the bus clock as timer */
669 latency = fixup_sgtc();
670 printk(KERN_INFO PFX "SGTC: %d\n", latency);
671 }
672 }
673
674 if (result)
675 return result;
676
Dave Jonesb9e76382009-01-18 00:32:26 -0500677 printk(KERN_INFO PFX "Minimum speed %d MHz. Maximum speed %d MHz.\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700678 minimum_speed/1000, maximum_speed/1000);
679
Dave Jonesb9e76382009-01-18 00:32:26 -0500680 policy->cpuinfo.transition_latency =
681 cpufreq_scale(2000000UL, fsb, latency);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700682
683 policy->cur = powernow_get(0);
684
685 cpufreq_frequency_table_get_attr(powernow_table, policy->cpu);
686
687 return cpufreq_frequency_table_cpuinfo(policy, powernow_table);
688}
689
Dave Jonesb9e76382009-01-18 00:32:26 -0500690static int powernow_cpu_exit(struct cpufreq_policy *policy)
691{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700692 cpufreq_frequency_table_put_attr(policy->cpu);
693
694#ifdef CONFIG_X86_POWERNOW_K7_ACPI
695 if (acpi_processor_perf) {
696 acpi_processor_unregister_performance(acpi_processor_perf, 0);
Rusty Russell2fdf66b2008-12-31 18:08:47 -0800697 free_cpumask_var(acpi_processor_perf->shared_cpu_map);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700698 kfree(acpi_processor_perf);
699 }
700#endif
701
Jesper Juhl4ae66732005-06-25 14:58:48 -0700702 kfree(powernow_table);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700703 return 0;
704}
705
Dave Jonesb9e76382009-01-18 00:32:26 -0500706static struct freq_attr *powernow_table_attr[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700707 &cpufreq_freq_attr_scaling_available_freqs,
708 NULL,
709};
710
Linus Torvalds221dee22007-02-26 14:55:48 -0800711static struct cpufreq_driver powernow_driver = {
Thomas Renningere2f74f32009-11-19 12:31:01 +0100712 .verify = powernow_verify,
713 .target = powernow_target,
714 .get = powernow_get,
715#ifdef CONFIG_X86_POWERNOW_K7_ACPI
716 .bios_limit = acpi_processor_get_bios_limit,
717#endif
718 .init = powernow_cpu_init,
719 .exit = powernow_cpu_exit,
720 .name = "powernow-k7",
721 .owner = THIS_MODULE,
722 .attr = powernow_table_attr,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700723};
724
Dave Jonesb9e76382009-01-18 00:32:26 -0500725static int __init powernow_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700726{
Dave Jonesb9e76382009-01-18 00:32:26 -0500727 if (check_powernow() == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700728 return -ENODEV;
729 return cpufreq_register_driver(&powernow_driver);
730}
731
732
Dave Jonesb9e76382009-01-18 00:32:26 -0500733static void __exit powernow_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700734{
735 cpufreq_unregister_driver(&powernow_driver);
736}
737
738module_param(acpi_force, int, 0444);
739MODULE_PARM_DESC(acpi_force, "Force ACPI to be used.");
740
Dave Jonesb9e76382009-01-18 00:32:26 -0500741MODULE_AUTHOR("Dave Jones <davej@redhat.com>");
742MODULE_DESCRIPTION("Powernow driver for AMD K7 processors.");
743MODULE_LICENSE("GPL");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700744
745late_initcall(powernow_init);
746module_exit(powernow_exit);
747