blob: cf7e1ee005a24c3d964dc6f8f1fd43c7b2fc4a33 [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>
Andi Kleenfa8031a2012-01-26 00:09:12 +010031#include <asm/cpu_device_id.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070032
33#ifdef CONFIG_X86_POWERNOW_K7_ACPI
34#include <linux/acpi.h>
35#include <acpi/processor.h>
36#endif
37
38#include "powernow-k7.h"
39
40#define PFX "powernow: "
41
42
43struct psb_s {
44 u8 signature[10];
45 u8 tableversion;
46 u8 flags;
47 u16 settlingtime;
48 u8 reserved1;
49 u8 numpst;
50};
51
52struct pst_s {
53 u32 cpuid;
54 u8 fsbspeed;
55 u8 maxfid;
56 u8 startvid;
57 u8 numpstates;
58};
59
60#ifdef CONFIG_X86_POWERNOW_K7_ACPI
61union powernow_acpi_control_t {
62 struct {
63 unsigned long fid:5,
Dave Jonesb9e76382009-01-18 00:32:26 -050064 vid:5,
65 sgtc:20,
66 res1:2;
Linus Torvalds1da177e2005-04-16 15:20:36 -070067 } bits;
68 unsigned long val;
69};
70#endif
71
Linus Torvalds1da177e2005-04-16 15:20:36 -070072/* 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};
Linus Torvalds1da177e2005-04-16 15:20:36 -070079
80/* divide by 10 to get FID. */
Dave Jonesbd5ab262007-02-22 19:11:16 -050081static const int fid_codes[32] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -070082 110, 115, 120, 125, 50, 55, 60, 65,
83 70, 75, 80, 85, 90, 95, 100, 105,
84 30, 190, 40, 200, 130, 135, 140, 210,
85 150, 225, 160, 165, 170, 180, -1, -1,
86};
87
88/* This parameter is used in order to force ACPI instead of legacy method for
89 * configuration purpose.
90 */
91
92static int acpi_force;
93
94static struct cpufreq_frequency_table *powernow_table;
95
96static unsigned int can_scale_bus;
97static unsigned int can_scale_vid;
Dave Jonesfff78ad2009-01-17 22:28:42 -050098static unsigned int minimum_speed = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -070099static unsigned int maximum_speed;
100static unsigned int number_scales;
101static unsigned int fsb;
102static unsigned int latency;
103static char have_a0;
104
Linus Torvalds1da177e2005-04-16 15:20:36 -0700105static int check_fsb(unsigned int fsbspeed)
106{
107 int delta;
108 unsigned int f = fsb / 1000;
109
110 delta = (fsbspeed > f) ? fsbspeed - f : f - fsbspeed;
Dave Jonesb9e76382009-01-18 00:32:26 -0500111 return delta < 5;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700112}
113
Andi Kleenfa8031a2012-01-26 00:09:12 +0100114static const struct x86_cpu_id powernow_k7_cpuids[] = {
Ben Hutchings30bcfff2012-02-11 22:58:14 +0000115 { X86_VENDOR_AMD, 6, },
Andi Kleenfa8031a2012-01-26 00:09:12 +0100116 {}
117};
118MODULE_DEVICE_TABLE(x86cpu, powernow_k7_cpuids);
119
Linus Torvalds1da177e2005-04-16 15:20:36 -0700120static int check_powernow(void)
121{
Mike Travis92cb7612007-10-19 20:35:04 +0200122 struct cpuinfo_x86 *c = &cpu_data(0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700123 unsigned int maxei, eax, ebx, ecx, edx;
124
Andi Kleenfa8031a2012-01-26 00:09:12 +0100125 if (!x86_match_cpu(powernow_k7_cpuids))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700126 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127
128 /* Get maximum capabilities */
Dave Jonesb9e76382009-01-18 00:32:26 -0500129 maxei = cpuid_eax(0x80000000);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700130 if (maxei < 0x80000007) { /* Any powernow info ? */
131#ifdef MODULE
Dave Jonesb9e76382009-01-18 00:32:26 -0500132 printk(KERN_INFO PFX "No powernow capabilities detected\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700133#endif
134 return 0;
135 }
136
137 if ((c->x86_model == 6) && (c->x86_mask == 0)) {
Dave Jonesb9e76382009-01-18 00:32:26 -0500138 printk(KERN_INFO PFX "K7 660[A0] core detected, "
139 "enabling errata workarounds\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700140 have_a0 = 1;
141 }
142
143 cpuid(0x80000007, &eax, &ebx, &ecx, &edx);
144
145 /* Check we can actually do something before we say anything.*/
146 if (!(edx & (1 << 1 | 1 << 2)))
147 return 0;
148
Dave Jonesb9e76382009-01-18 00:32:26 -0500149 printk(KERN_INFO PFX "PowerNOW! Technology present. Can scale: ");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150
151 if (edx & 1 << 1) {
Dave Jonesb9e76382009-01-18 00:32:26 -0500152 printk("frequency");
153 can_scale_bus = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700154 }
155
156 if ((edx & (1 << 1 | 1 << 2)) == 0x6)
Dave Jonesb9e76382009-01-18 00:32:26 -0500157 printk(" and ");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700158
159 if (edx & 1 << 2) {
Dave Jonesb9e76382009-01-18 00:32:26 -0500160 printk("voltage");
161 can_scale_vid = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700162 }
163
Dave Jonesb9e76382009-01-18 00:32:26 -0500164 printk(".\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700165 return 1;
166}
167
Dave Jonesd38e73e2009-04-23 13:36:12 -0400168#ifdef CONFIG_X86_POWERNOW_K7_ACPI
Dave Jonesb9e76382009-01-18 00:32:26 -0500169static void invalidate_entry(unsigned int entry)
170{
171 powernow_table[entry].frequency = CPUFREQ_ENTRY_INVALID;
172}
Dave Jonesd38e73e2009-04-23 13:36:12 -0400173#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700174
Dave Jonesb9e76382009-01-18 00:32:26 -0500175static int get_ranges(unsigned char *pst)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700176{
177 unsigned int j;
178 unsigned int speed;
179 u8 fid, vid;
180
Dave Jonesb9e76382009-01-18 00:32:26 -0500181 powernow_table = kzalloc((sizeof(struct cpufreq_frequency_table) *
182 (number_scales + 1)), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183 if (!powernow_table)
184 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700185
Dave Jonesb9e76382009-01-18 00:32:26 -0500186 for (j = 0 ; j < number_scales; j++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700187 fid = *pst++;
188
189 powernow_table[j].frequency = (fsb * fid_codes[fid]) / 10;
190 powernow_table[j].index = fid; /* lower 8 bits */
191
192 speed = powernow_table[j].frequency;
193
Dave Jonesb9e76382009-01-18 00:32:26 -0500194 if ((fid_codes[fid] % 10) == 5) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700195#ifdef CONFIG_X86_POWERNOW_K7_ACPI
196 if (have_a0 == 1)
Dave Jonesb9e76382009-01-18 00:32:26 -0500197 invalidate_entry(j);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700198#endif
199 }
200
201 if (speed < minimum_speed)
202 minimum_speed = speed;
203 if (speed > maximum_speed)
204 maximum_speed = speed;
205
206 vid = *pst++;
207 powernow_table[j].index |= (vid << 8); /* upper 8 bits */
208
Dominik Brodowski2d06d8c2011-03-27 15:04:46 +0200209 pr_debug(" FID: 0x%x (%d.%dx [%dMHz]) "
Dave Jones32ee8c32006-02-28 00:43:23 -0500210 "VID: 0x%x (%d.%03dV)\n", fid, fid_codes[fid] / 10,
211 fid_codes[fid] % 10, speed/1000, vid,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700212 mobile_vid_table[vid]/1000,
213 mobile_vid_table[vid]%1000);
214 }
215 powernow_table[number_scales].frequency = CPUFREQ_TABLE_END;
216 powernow_table[number_scales].index = 0;
217
218 return 0;
219}
220
221
222static void change_FID(int fid)
223{
224 union msr_fidvidctl fidvidctl;
225
Dave Jonesb9e76382009-01-18 00:32:26 -0500226 rdmsrl(MSR_K7_FID_VID_CTL, fidvidctl.val);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700227 if (fidvidctl.bits.FID != fid) {
228 fidvidctl.bits.SGTC = latency;
229 fidvidctl.bits.FID = fid;
230 fidvidctl.bits.VIDC = 0;
231 fidvidctl.bits.FIDC = 1;
Dave Jonesb9e76382009-01-18 00:32:26 -0500232 wrmsrl(MSR_K7_FID_VID_CTL, fidvidctl.val);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700233 }
234}
235
236
237static void change_VID(int vid)
238{
239 union msr_fidvidctl fidvidctl;
240
Dave Jonesb9e76382009-01-18 00:32:26 -0500241 rdmsrl(MSR_K7_FID_VID_CTL, fidvidctl.val);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700242 if (fidvidctl.bits.VID != vid) {
243 fidvidctl.bits.SGTC = latency;
244 fidvidctl.bits.VID = vid;
245 fidvidctl.bits.FIDC = 0;
246 fidvidctl.bits.VIDC = 1;
Dave Jonesb9e76382009-01-18 00:32:26 -0500247 wrmsrl(MSR_K7_FID_VID_CTL, fidvidctl.val);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248 }
249}
250
251
Dave Jonesb9e76382009-01-18 00:32:26 -0500252static void change_speed(unsigned int index)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700253{
254 u8 fid, vid;
255 struct cpufreq_freqs freqs;
256 union msr_fidvidstatus fidvidstatus;
257 int cfid;
258
259 /* fid are the lower 8 bits of the index we stored into
260 * the cpufreq frequency table in powernow_decode_bios,
261 * vid are the upper 8 bits.
262 */
263
264 fid = powernow_table[index].index & 0xFF;
265 vid = (powernow_table[index].index & 0xFF00) >> 8;
266
267 freqs.cpu = 0;
268
Dave Jonesb9e76382009-01-18 00:32:26 -0500269 rdmsrl(MSR_K7_FID_VID_STATUS, fidvidstatus.val);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700270 cfid = fidvidstatus.bits.CFID;
271 freqs.old = fsb * fid_codes[cfid] / 10;
272
273 freqs.new = powernow_table[index].frequency;
274
275 cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
276
277 /* Now do the magic poking into the MSRs. */
278
279 if (have_a0 == 1) /* A0 errata 5 */
280 local_irq_disable();
281
282 if (freqs.old > freqs.new) {
283 /* Going down, so change FID first */
284 change_FID(fid);
285 change_VID(vid);
286 } else {
287 /* Going up, so change VID first */
288 change_VID(vid);
289 change_FID(fid);
290 }
291
292
293 if (have_a0 == 1)
294 local_irq_enable();
295
296 cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
297}
298
299
300#ifdef CONFIG_X86_POWERNOW_K7_ACPI
301
302static struct acpi_processor_performance *acpi_processor_perf;
303
304static int powernow_acpi_init(void)
305{
306 int i;
307 int retval = 0;
308 union powernow_acpi_control_t pc;
309
310 if (acpi_processor_perf != NULL && powernow_table != NULL) {
311 retval = -EINVAL;
312 goto err0;
313 }
314
Dave Jonesbfdc7082005-10-20 15:16:15 -0700315 acpi_processor_perf = kzalloc(sizeof(struct acpi_processor_performance),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700316 GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700317 if (!acpi_processor_perf) {
318 retval = -ENOMEM;
319 goto err0;
320 }
321
Yinghai Lueaa95842009-06-06 14:51:36 -0700322 if (!zalloc_cpumask_var(&acpi_processor_perf->shared_cpu_map,
Rusty Russell2fdf66b2008-12-31 18:08:47 -0800323 GFP_KERNEL)) {
324 retval = -ENOMEM;
325 goto err05;
326 }
327
Linus Torvalds1da177e2005-04-16 15:20:36 -0700328 if (acpi_processor_register_performance(acpi_processor_perf, 0)) {
329 retval = -EIO;
330 goto err1;
331 }
332
Dave Jonesb9e76382009-01-18 00:32:26 -0500333 if (acpi_processor_perf->control_register.space_id !=
334 ACPI_ADR_SPACE_FIXED_HARDWARE) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700335 retval = -ENODEV;
336 goto err2;
337 }
338
Dave Jonesb9e76382009-01-18 00:32:26 -0500339 if (acpi_processor_perf->status_register.space_id !=
340 ACPI_ADR_SPACE_FIXED_HARDWARE) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700341 retval = -ENODEV;
342 goto err2;
343 }
344
345 number_scales = acpi_processor_perf->state_count;
346
347 if (number_scales < 2) {
348 retval = -ENODEV;
349 goto err2;
350 }
351
Dave Jonesb9e76382009-01-18 00:32:26 -0500352 powernow_table = kzalloc((sizeof(struct cpufreq_frequency_table) *
353 (number_scales + 1)), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700354 if (!powernow_table) {
355 retval = -ENOMEM;
356 goto err2;
357 }
358
Linus Torvalds1da177e2005-04-16 15:20:36 -0700359 pc.val = (unsigned long) acpi_processor_perf->states[0].control;
360 for (i = 0; i < number_scales; i++) {
361 u8 fid, vid;
Daniel Drakedc2585e2007-05-02 23:19:05 +0100362 struct acpi_processor_px *state =
363 &acpi_processor_perf->states[i];
364 unsigned int speed, speed_mhz;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700365
Daniel Drakedc2585e2007-05-02 23:19:05 +0100366 pc.val = (unsigned long) state->control;
Dominik Brodowski2d06d8c2011-03-27 15:04:46 +0200367 pr_debug("acpi: P%d: %d MHz %d mW %d uS control %08x SGTC %d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700368 i,
Daniel Drakedc2585e2007-05-02 23:19:05 +0100369 (u32) state->core_frequency,
370 (u32) state->power,
371 (u32) state->transition_latency,
372 (u32) state->control,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700373 pc.bits.sgtc);
374
375 vid = pc.bits.vid;
376 fid = pc.bits.fid;
377
378 powernow_table[i].frequency = fsb * fid_codes[fid] / 10;
379 powernow_table[i].index = fid; /* lower 8 bits */
380 powernow_table[i].index |= (vid << 8); /* upper 8 bits */
381
382 speed = powernow_table[i].frequency;
Daniel Drakedc2585e2007-05-02 23:19:05 +0100383 speed_mhz = speed / 1000;
384
385 /* processor_perflib will multiply the MHz value by 1000 to
386 * get a KHz value (e.g. 1266000). However, powernow-k7 works
387 * with true KHz values (e.g. 1266768). To ensure that all
388 * powernow frequencies are available, we must ensure that
389 * ACPI doesn't restrict them, so we round up the MHz value
390 * to ensure that perflib's computed KHz value is greater than
391 * or equal to powernow's KHz value.
392 */
393 if (speed % 1000 > 0)
394 speed_mhz++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700395
Dave Jonesb9e76382009-01-18 00:32:26 -0500396 if ((fid_codes[fid] % 10) == 5) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700397 if (have_a0 == 1)
Dave Jonesb9e76382009-01-18 00:32:26 -0500398 invalidate_entry(i);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700399 }
400
Dominik Brodowski2d06d8c2011-03-27 15:04:46 +0200401 pr_debug(" FID: 0x%x (%d.%dx [%dMHz]) "
Dave Jones32ee8c32006-02-28 00:43:23 -0500402 "VID: 0x%x (%d.%03dV)\n", fid, fid_codes[fid] / 10,
Daniel Drakedc2585e2007-05-02 23:19:05 +0100403 fid_codes[fid] % 10, speed_mhz, vid,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700404 mobile_vid_table[vid]/1000,
405 mobile_vid_table[vid]%1000);
406
Daniel Drakedc2585e2007-05-02 23:19:05 +0100407 if (state->core_frequency != speed_mhz) {
408 state->core_frequency = speed_mhz;
Dominik Brodowski2d06d8c2011-03-27 15:04:46 +0200409 pr_debug(" Corrected ACPI frequency to %d\n",
Daniel Drakedc2585e2007-05-02 23:19:05 +0100410 speed_mhz);
411 }
412
Linus Torvalds1da177e2005-04-16 15:20:36 -0700413 if (latency < pc.bits.sgtc)
414 latency = pc.bits.sgtc;
415
416 if (speed < minimum_speed)
417 minimum_speed = speed;
418 if (speed > maximum_speed)
419 maximum_speed = speed;
420 }
421
422 powernow_table[i].frequency = CPUFREQ_TABLE_END;
423 powernow_table[i].index = 0;
424
425 /* notify BIOS that we exist */
426 acpi_processor_notify_smm(THIS_MODULE);
427
428 return 0;
429
430err2:
431 acpi_processor_unregister_performance(acpi_processor_perf, 0);
432err1:
Rusty Russell2fdf66b2008-12-31 18:08:47 -0800433 free_cpumask_var(acpi_processor_perf->shared_cpu_map);
434err05:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700435 kfree(acpi_processor_perf);
436err0:
Dave Jonesb9e76382009-01-18 00:32:26 -0500437 printk(KERN_WARNING PFX "ACPI perflib can not be used on "
438 "this platform\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700439 acpi_processor_perf = NULL;
440 return retval;
441}
442#else
443static int powernow_acpi_init(void)
444{
445 printk(KERN_INFO PFX "no support for ACPI processor found."
446 " Please recompile your kernel with ACPI processor\n");
447 return -EINVAL;
448}
449#endif
450
Dave Jonesb9e76382009-01-18 00:32:26 -0500451static void print_pst_entry(struct pst_s *pst, unsigned int j)
452{
Dominik Brodowski2d06d8c2011-03-27 15:04:46 +0200453 pr_debug("PST:%d (@%p)\n", j, pst);
454 pr_debug(" cpuid: 0x%x fsb: %d maxFID: 0x%x startvid: 0x%x\n",
Dave Jonesb9e76382009-01-18 00:32:26 -0500455 pst->cpuid, pst->fsbspeed, pst->maxfid, pst->startvid);
456}
457
458static int powernow_decode_bios(int maxfid, int startvid)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700459{
460 struct psb_s *psb;
461 struct pst_s *pst;
462 unsigned int i, j;
463 unsigned char *p;
464 unsigned int etuple;
465 unsigned int ret;
466
467 etuple = cpuid_eax(0x80000001);
468
Dave Jonesb9e76382009-01-18 00:32:26 -0500469 for (i = 0xC0000; i < 0xffff0 ; i += 16) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700470
471 p = phys_to_virt(i);
472
Dave Jonesb9e76382009-01-18 00:32:26 -0500473 if (memcmp(p, "AMDK7PNOW!", 10) == 0) {
Dominik Brodowski2d06d8c2011-03-27 15:04:46 +0200474 pr_debug("Found PSB header at %p\n", p);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700475 psb = (struct psb_s *) p;
Dominik Brodowski2d06d8c2011-03-27 15:04:46 +0200476 pr_debug("Table version: 0x%x\n", psb->tableversion);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700477 if (psb->tableversion != 0x12) {
Dave Jonesb9e76382009-01-18 00:32:26 -0500478 printk(KERN_INFO PFX "Sorry, only v1.2 tables"
479 " supported right now\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700480 return -ENODEV;
481 }
482
Dominik Brodowski2d06d8c2011-03-27 15:04:46 +0200483 pr_debug("Flags: 0x%x\n", psb->flags);
Dave Jonesb9e76382009-01-18 00:32:26 -0500484 if ((psb->flags & 1) == 0)
Dominik Brodowski2d06d8c2011-03-27 15:04:46 +0200485 pr_debug("Mobile voltage regulator\n");
Dave Jonesb9e76382009-01-18 00:32:26 -0500486 else
Dominik Brodowski2d06d8c2011-03-27 15:04:46 +0200487 pr_debug("Desktop voltage regulator\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700488
489 latency = psb->settlingtime;
490 if (latency < 100) {
Dave Jonesb9e76382009-01-18 00:32:26 -0500491 printk(KERN_INFO PFX "BIOS set settling time "
492 "to %d microseconds. "
493 "Should be at least 100. "
494 "Correcting.\n", latency);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700495 latency = 100;
496 }
Dominik Brodowski2d06d8c2011-03-27 15:04:46 +0200497 pr_debug("Settling Time: %d microseconds.\n",
Dave Jonesb9e76382009-01-18 00:32:26 -0500498 psb->settlingtime);
Dominik Brodowski2d06d8c2011-03-27 15:04:46 +0200499 pr_debug("Has %d PST tables. (Only dumping ones "
Dave Jonesb9e76382009-01-18 00:32:26 -0500500 "relevant to this CPU).\n",
501 psb->numpst);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700502
Dave Jonesb9e76382009-01-18 00:32:26 -0500503 p += sizeof(struct psb_s);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700504
505 pst = (struct pst_s *) p;
506
Dave Jonesb9e76382009-01-18 00:32:26 -0500507 for (j = 0; j < psb->numpst; j++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700508 pst = (struct pst_s *) p;
509 number_scales = pst->numpstates;
510
Dave Jonesb9e76382009-01-18 00:32:26 -0500511 if ((etuple == pst->cpuid) &&
512 check_fsb(pst->fsbspeed) &&
513 (maxfid == pst->maxfid) &&
514 (startvid == pst->startvid)) {
515 print_pst_entry(pst, j);
516 p = (char *)pst + sizeof(struct pst_s);
517 ret = get_ranges(p);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700519 } else {
Dave Jones8cbe0162006-05-30 17:26:08 -0400520 unsigned int k;
Dave Jonesb9e76382009-01-18 00:32:26 -0500521 p = (char *)pst + sizeof(struct pst_s);
522 for (k = 0; k < number_scales; k++)
523 p += 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700524 }
525 }
Dave Jonesb9e76382009-01-18 00:32:26 -0500526 printk(KERN_INFO PFX "No PST tables match this cpuid "
527 "(0x%x)\n", etuple);
528 printk(KERN_INFO PFX "This is indicative of a broken "
529 "BIOS.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700530
531 return -EINVAL;
532 }
533 p++;
534 }
535
536 return -ENODEV;
537}
538
539
Dave Jonesb9e76382009-01-18 00:32:26 -0500540static int powernow_target(struct cpufreq_policy *policy,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700541 unsigned int target_freq,
542 unsigned int relation)
543{
544 unsigned int newstate;
545
Dave Jonesb9e76382009-01-18 00:32:26 -0500546 if (cpufreq_frequency_table_target(policy, powernow_table, target_freq,
547 relation, &newstate))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700548 return -EINVAL;
549
550 change_speed(newstate);
551
552 return 0;
553}
554
555
Dave Jonesb9e76382009-01-18 00:32:26 -0500556static int powernow_verify(struct cpufreq_policy *policy)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700557{
558 return cpufreq_frequency_table_verify(policy, powernow_table);
559}
560
561/*
562 * We use the fact that the bus frequency is somehow
563 * a multiple of 100000/3 khz, then we compute sgtc according
564 * to this multiple.
565 * That way, we match more how AMD thinks all of that work.
566 * We will then get the same kind of behaviour already tested under
567 * the "well-known" other OS.
568 */
Holger Freyther307069c2010-07-19 03:29:16 +0800569static int __cpuinit fixup_sgtc(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700570{
571 unsigned int sgtc;
572 unsigned int m;
573
574 m = fsb / 3333;
575 if ((m % 10) >= 5)
576 m += 5;
577
578 m /= 10;
579
580 sgtc = 100 * m * latency;
581 sgtc = sgtc / 3;
582 if (sgtc > 0xfffff) {
583 printk(KERN_WARNING PFX "SGTC too large %d\n", sgtc);
584 sgtc = 0xfffff;
585 }
586 return sgtc;
587}
588
589static unsigned int powernow_get(unsigned int cpu)
590{
591 union msr_fidvidstatus fidvidstatus;
592 unsigned int cfid;
593
594 if (cpu)
595 return 0;
Dave Jonesb9e76382009-01-18 00:32:26 -0500596 rdmsrl(MSR_K7_FID_VID_STATUS, fidvidstatus.val);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700597 cfid = fidvidstatus.bits.CFID;
598
Dave Jonesb9e76382009-01-18 00:32:26 -0500599 return fsb * fid_codes[cfid] / 10;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700600}
601
602
Holger Freyther307069c2010-07-19 03:29:16 +0800603static int __cpuinit acer_cpufreq_pst(const struct dmi_system_id *d)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700604{
Dave Jonesb9e76382009-01-18 00:32:26 -0500605 printk(KERN_WARNING PFX
606 "%s laptop with broken PST tables in BIOS detected.\n",
607 d->ident);
608 printk(KERN_WARNING PFX
609 "You need to downgrade to 3A21 (09/09/2002), or try a newer "
610 "BIOS than 3A71 (01/20/2003)\n");
611 printk(KERN_WARNING PFX
612 "cpufreq scaling has been disabled as a result of this.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700613 return 0;
614}
615
616/*
617 * Some Athlon laptops have really fucked PST tables.
618 * A BIOS update is all that can save them.
619 * Mention this, and disable cpufreq.
620 */
Holger Freyther307069c2010-07-19 03:29:16 +0800621static struct dmi_system_id __cpuinitdata powernow_dmi_table[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700622 {
623 .callback = acer_cpufreq_pst,
624 .ident = "Acer Aspire",
625 .matches = {
626 DMI_MATCH(DMI_SYS_VENDOR, "Insyde Software"),
627 DMI_MATCH(DMI_BIOS_VERSION, "3A71"),
628 },
629 },
630 { }
631};
632
Holger Freyther307069c2010-07-19 03:29:16 +0800633static int __cpuinit powernow_cpu_init(struct cpufreq_policy *policy)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700634{
635 union msr_fidvidstatus fidvidstatus;
636 int result;
637
638 if (policy->cpu != 0)
639 return -ENODEV;
640
Dave Jonesb9e76382009-01-18 00:32:26 -0500641 rdmsrl(MSR_K7_FID_VID_STATUS, fidvidstatus.val);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700642
Dave Jones436fe7b2006-06-05 14:03:50 -0400643 recalibrate_cpu_khz();
Dave Jones91350ed2005-05-31 19:03:45 -0700644
645 fsb = (10 * cpu_khz) / fid_codes[fidvidstatus.bits.CFID];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700646 if (!fsb) {
647 printk(KERN_WARNING PFX "can not determine bus frequency\n");
648 return -EINVAL;
649 }
Dominik Brodowski2d06d8c2011-03-27 15:04:46 +0200650 pr_debug("FSB: %3dMHz\n", fsb/1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700651
652 if (dmi_check_system(powernow_dmi_table) || acpi_force) {
Dave Jonesb9e76382009-01-18 00:32:26 -0500653 printk(KERN_INFO PFX "PSB/PST known to be broken. "
654 "Trying ACPI instead\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700655 result = powernow_acpi_init();
656 } else {
Dave Jonesb9e76382009-01-18 00:32:26 -0500657 result = powernow_decode_bios(fidvidstatus.bits.MFID,
658 fidvidstatus.bits.SVID);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700659 if (result) {
Dave Jonesb9e76382009-01-18 00:32:26 -0500660 printk(KERN_INFO PFX "Trying ACPI perflib\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700661 maximum_speed = 0;
662 minimum_speed = -1;
663 latency = 0;
664 result = powernow_acpi_init();
665 if (result) {
Dave Jonesb9e76382009-01-18 00:32:26 -0500666 printk(KERN_INFO PFX
667 "ACPI and legacy methods failed\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700668 }
669 } else {
670 /* SGTC use the bus clock as timer */
671 latency = fixup_sgtc();
672 printk(KERN_INFO PFX "SGTC: %d\n", latency);
673 }
674 }
675
676 if (result)
677 return result;
678
Dave Jonesb9e76382009-01-18 00:32:26 -0500679 printk(KERN_INFO PFX "Minimum speed %d MHz. Maximum speed %d MHz.\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700680 minimum_speed/1000, maximum_speed/1000);
681
Dave Jonesb9e76382009-01-18 00:32:26 -0500682 policy->cpuinfo.transition_latency =
683 cpufreq_scale(2000000UL, fsb, latency);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700684
685 policy->cur = powernow_get(0);
686
687 cpufreq_frequency_table_get_attr(powernow_table, policy->cpu);
688
689 return cpufreq_frequency_table_cpuinfo(policy, powernow_table);
690}
691
Dave Jonesb9e76382009-01-18 00:32:26 -0500692static int powernow_cpu_exit(struct cpufreq_policy *policy)
693{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700694 cpufreq_frequency_table_put_attr(policy->cpu);
695
696#ifdef CONFIG_X86_POWERNOW_K7_ACPI
697 if (acpi_processor_perf) {
698 acpi_processor_unregister_performance(acpi_processor_perf, 0);
Rusty Russell2fdf66b2008-12-31 18:08:47 -0800699 free_cpumask_var(acpi_processor_perf->shared_cpu_map);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700700 kfree(acpi_processor_perf);
701 }
702#endif
703
Jesper Juhl4ae66732005-06-25 14:58:48 -0700704 kfree(powernow_table);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700705 return 0;
706}
707
Dave Jonesb9e76382009-01-18 00:32:26 -0500708static struct freq_attr *powernow_table_attr[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700709 &cpufreq_freq_attr_scaling_available_freqs,
710 NULL,
711};
712
Linus Torvalds221dee22007-02-26 14:55:48 -0800713static struct cpufreq_driver powernow_driver = {
Thomas Renningere2f74f32009-11-19 12:31:01 +0100714 .verify = powernow_verify,
715 .target = powernow_target,
716 .get = powernow_get,
717#ifdef CONFIG_X86_POWERNOW_K7_ACPI
718 .bios_limit = acpi_processor_get_bios_limit,
719#endif
720 .init = powernow_cpu_init,
721 .exit = powernow_cpu_exit,
722 .name = "powernow-k7",
723 .owner = THIS_MODULE,
724 .attr = powernow_table_attr,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700725};
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