blob: 14ce480be8ab2503515cb1d61b8f30b5892d85d2 [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>
Andi Kleenfa8031a2012-01-26 00:09:12 +010030#include <asm/cpu_device_id.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070031
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
Andi Kleenfa8031a2012-01-26 00:09:12 +0100113static const struct x86_cpu_id powernow_k7_cpuids[] = {
Ben Hutchings30bcfff2012-02-11 22:58:14 +0000114 { X86_VENDOR_AMD, 6, },
Andi Kleenfa8031a2012-01-26 00:09:12 +0100115 {}
116};
117MODULE_DEVICE_TABLE(x86cpu, powernow_k7_cpuids);
118
Linus Torvalds1da177e2005-04-16 15:20:36 -0700119static int check_powernow(void)
120{
Mike Travis92cb7612007-10-19 20:35:04 +0200121 struct cpuinfo_x86 *c = &cpu_data(0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700122 unsigned int maxei, eax, ebx, ecx, edx;
123
Andi Kleenfa8031a2012-01-26 00:09:12 +0100124 if (!x86_match_cpu(powernow_k7_cpuids))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700126
127 /* Get maximum capabilities */
Dave Jonesb9e76382009-01-18 00:32:26 -0500128 maxei = cpuid_eax(0x80000000);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700129 if (maxei < 0x80000007) { /* Any powernow info ? */
130#ifdef MODULE
Dave Jonesb9e76382009-01-18 00:32:26 -0500131 printk(KERN_INFO PFX "No powernow capabilities detected\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132#endif
133 return 0;
134 }
135
136 if ((c->x86_model == 6) && (c->x86_mask == 0)) {
Dave Jonesb9e76382009-01-18 00:32:26 -0500137 printk(KERN_INFO PFX "K7 660[A0] core detected, "
138 "enabling errata workarounds\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700139 have_a0 = 1;
140 }
141
142 cpuid(0x80000007, &eax, &ebx, &ecx, &edx);
143
144 /* Check we can actually do something before we say anything.*/
145 if (!(edx & (1 << 1 | 1 << 2)))
146 return 0;
147
Dave Jonesb9e76382009-01-18 00:32:26 -0500148 printk(KERN_INFO PFX "PowerNOW! Technology present. Can scale: ");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700149
150 if (edx & 1 << 1) {
Dave Jonesb9e76382009-01-18 00:32:26 -0500151 printk("frequency");
152 can_scale_bus = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700153 }
154
155 if ((edx & (1 << 1 | 1 << 2)) == 0x6)
Dave Jonesb9e76382009-01-18 00:32:26 -0500156 printk(" and ");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700157
158 if (edx & 1 << 2) {
Dave Jonesb9e76382009-01-18 00:32:26 -0500159 printk("voltage");
160 can_scale_vid = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700161 }
162
Dave Jonesb9e76382009-01-18 00:32:26 -0500163 printk(".\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700164 return 1;
165}
166
Dave Jonesd38e73e2009-04-23 13:36:12 -0400167#ifdef CONFIG_X86_POWERNOW_K7_ACPI
Dave Jonesb9e76382009-01-18 00:32:26 -0500168static void invalidate_entry(unsigned int entry)
169{
170 powernow_table[entry].frequency = CPUFREQ_ENTRY_INVALID;
171}
Dave Jonesd38e73e2009-04-23 13:36:12 -0400172#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173
Dave Jonesb9e76382009-01-18 00:32:26 -0500174static int get_ranges(unsigned char *pst)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700175{
176 unsigned int j;
177 unsigned int speed;
178 u8 fid, vid;
179
Viresh Kumard5b73cd2013-08-06 22:53:06 +0530180 powernow_table = kzalloc((sizeof(*powernow_table) *
Dave Jonesb9e76382009-01-18 00:32:26 -0500181 (number_scales + 1)), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700182 if (!powernow_table)
183 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184
Dave Jonesb9e76382009-01-18 00:32:26 -0500185 for (j = 0 ; j < number_scales; j++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700186 fid = *pst++;
187
188 powernow_table[j].frequency = (fsb * fid_codes[fid]) / 10;
Viresh Kumar50701582013-03-30 16:25:15 +0530189 powernow_table[j].driver_data = fid; /* lower 8 bits */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700190
191 speed = powernow_table[j].frequency;
192
Dave Jonesb9e76382009-01-18 00:32:26 -0500193 if ((fid_codes[fid] % 10) == 5) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700194#ifdef CONFIG_X86_POWERNOW_K7_ACPI
195 if (have_a0 == 1)
Dave Jonesb9e76382009-01-18 00:32:26 -0500196 invalidate_entry(j);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700197#endif
198 }
199
200 if (speed < minimum_speed)
201 minimum_speed = speed;
202 if (speed > maximum_speed)
203 maximum_speed = speed;
204
205 vid = *pst++;
Viresh Kumar50701582013-03-30 16:25:15 +0530206 powernow_table[j].driver_data |= (vid << 8); /* upper 8 bits */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700207
Dominik Brodowski2d06d8c2011-03-27 15:04:46 +0200208 pr_debug(" FID: 0x%x (%d.%dx [%dMHz]) "
Dave Jones32ee8c32006-02-28 00:43:23 -0500209 "VID: 0x%x (%d.%03dV)\n", fid, fid_codes[fid] / 10,
210 fid_codes[fid] % 10, speed/1000, vid,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700211 mobile_vid_table[vid]/1000,
212 mobile_vid_table[vid]%1000);
213 }
214 powernow_table[number_scales].frequency = CPUFREQ_TABLE_END;
Viresh Kumar50701582013-03-30 16:25:15 +0530215 powernow_table[number_scales].driver_data = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700216
217 return 0;
218}
219
220
221static void change_FID(int fid)
222{
223 union msr_fidvidctl fidvidctl;
224
Dave Jonesb9e76382009-01-18 00:32:26 -0500225 rdmsrl(MSR_K7_FID_VID_CTL, fidvidctl.val);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700226 if (fidvidctl.bits.FID != fid) {
227 fidvidctl.bits.SGTC = latency;
228 fidvidctl.bits.FID = fid;
229 fidvidctl.bits.VIDC = 0;
230 fidvidctl.bits.FIDC = 1;
Dave Jonesb9e76382009-01-18 00:32:26 -0500231 wrmsrl(MSR_K7_FID_VID_CTL, fidvidctl.val);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700232 }
233}
234
235
236static void change_VID(int vid)
237{
238 union msr_fidvidctl fidvidctl;
239
Dave Jonesb9e76382009-01-18 00:32:26 -0500240 rdmsrl(MSR_K7_FID_VID_CTL, fidvidctl.val);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700241 if (fidvidctl.bits.VID != vid) {
242 fidvidctl.bits.SGTC = latency;
243 fidvidctl.bits.VID = vid;
244 fidvidctl.bits.FIDC = 0;
245 fidvidctl.bits.VIDC = 1;
Dave Jonesb9e76382009-01-18 00:32:26 -0500246 wrmsrl(MSR_K7_FID_VID_CTL, fidvidctl.val);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700247 }
248}
249
250
Viresh Kumarb43a7ff2013-03-24 11:56:43 +0530251static void change_speed(struct cpufreq_policy *policy, unsigned int index)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700252{
253 u8 fid, vid;
254 struct cpufreq_freqs freqs;
255 union msr_fidvidstatus fidvidstatus;
256 int cfid;
257
258 /* fid are the lower 8 bits of the index we stored into
259 * the cpufreq frequency table in powernow_decode_bios,
260 * vid are the upper 8 bits.
261 */
262
Viresh Kumar50701582013-03-30 16:25:15 +0530263 fid = powernow_table[index].driver_data & 0xFF;
264 vid = (powernow_table[index].driver_data & 0xFF00) >> 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700265
Dave Jonesb9e76382009-01-18 00:32:26 -0500266 rdmsrl(MSR_K7_FID_VID_STATUS, fidvidstatus.val);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700267 cfid = fidvidstatus.bits.CFID;
268 freqs.old = fsb * fid_codes[cfid] / 10;
269
270 freqs.new = powernow_table[index].frequency;
271
Viresh Kumarb43a7ff2013-03-24 11:56:43 +0530272 cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700273
274 /* Now do the magic poking into the MSRs. */
275
276 if (have_a0 == 1) /* A0 errata 5 */
277 local_irq_disable();
278
279 if (freqs.old > freqs.new) {
280 /* Going down, so change FID first */
281 change_FID(fid);
282 change_VID(vid);
283 } else {
284 /* Going up, so change VID first */
285 change_VID(vid);
286 change_FID(fid);
287 }
288
289
290 if (have_a0 == 1)
291 local_irq_enable();
292
Viresh Kumarb43a7ff2013-03-24 11:56:43 +0530293 cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700294}
295
296
297#ifdef CONFIG_X86_POWERNOW_K7_ACPI
298
299static struct acpi_processor_performance *acpi_processor_perf;
300
301static int powernow_acpi_init(void)
302{
303 int i;
304 int retval = 0;
305 union powernow_acpi_control_t pc;
306
307 if (acpi_processor_perf != NULL && powernow_table != NULL) {
308 retval = -EINVAL;
309 goto err0;
310 }
311
Viresh Kumard5b73cd2013-08-06 22:53:06 +0530312 acpi_processor_perf = kzalloc(sizeof(*acpi_processor_perf), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700313 if (!acpi_processor_perf) {
314 retval = -ENOMEM;
315 goto err0;
316 }
317
Yinghai Lueaa95842009-06-06 14:51:36 -0700318 if (!zalloc_cpumask_var(&acpi_processor_perf->shared_cpu_map,
Rusty Russell2fdf66b2008-12-31 18:08:47 -0800319 GFP_KERNEL)) {
320 retval = -ENOMEM;
321 goto err05;
322 }
323
Linus Torvalds1da177e2005-04-16 15:20:36 -0700324 if (acpi_processor_register_performance(acpi_processor_perf, 0)) {
325 retval = -EIO;
326 goto err1;
327 }
328
Dave Jonesb9e76382009-01-18 00:32:26 -0500329 if (acpi_processor_perf->control_register.space_id !=
330 ACPI_ADR_SPACE_FIXED_HARDWARE) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700331 retval = -ENODEV;
332 goto err2;
333 }
334
Dave Jonesb9e76382009-01-18 00:32:26 -0500335 if (acpi_processor_perf->status_register.space_id !=
336 ACPI_ADR_SPACE_FIXED_HARDWARE) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700337 retval = -ENODEV;
338 goto err2;
339 }
340
341 number_scales = acpi_processor_perf->state_count;
342
343 if (number_scales < 2) {
344 retval = -ENODEV;
345 goto err2;
346 }
347
Viresh Kumard5b73cd2013-08-06 22:53:06 +0530348 powernow_table = kzalloc((sizeof(*powernow_table) *
Dave Jonesb9e76382009-01-18 00:32:26 -0500349 (number_scales + 1)), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700350 if (!powernow_table) {
351 retval = -ENOMEM;
352 goto err2;
353 }
354
Linus Torvalds1da177e2005-04-16 15:20:36 -0700355 pc.val = (unsigned long) acpi_processor_perf->states[0].control;
356 for (i = 0; i < number_scales; i++) {
357 u8 fid, vid;
Daniel Drakedc2585e2007-05-02 23:19:05 +0100358 struct acpi_processor_px *state =
359 &acpi_processor_perf->states[i];
360 unsigned int speed, speed_mhz;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700361
Daniel Drakedc2585e2007-05-02 23:19:05 +0100362 pc.val = (unsigned long) state->control;
Dominik Brodowski2d06d8c2011-03-27 15:04:46 +0200363 pr_debug("acpi: P%d: %d MHz %d mW %d uS control %08x SGTC %d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700364 i,
Daniel Drakedc2585e2007-05-02 23:19:05 +0100365 (u32) state->core_frequency,
366 (u32) state->power,
367 (u32) state->transition_latency,
368 (u32) state->control,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700369 pc.bits.sgtc);
370
371 vid = pc.bits.vid;
372 fid = pc.bits.fid;
373
374 powernow_table[i].frequency = fsb * fid_codes[fid] / 10;
Viresh Kumar50701582013-03-30 16:25:15 +0530375 powernow_table[i].driver_data = fid; /* lower 8 bits */
376 powernow_table[i].driver_data |= (vid << 8); /* upper 8 bits */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700377
378 speed = powernow_table[i].frequency;
Daniel Drakedc2585e2007-05-02 23:19:05 +0100379 speed_mhz = speed / 1000;
380
381 /* processor_perflib will multiply the MHz value by 1000 to
382 * get a KHz value (e.g. 1266000). However, powernow-k7 works
383 * with true KHz values (e.g. 1266768). To ensure that all
384 * powernow frequencies are available, we must ensure that
385 * ACPI doesn't restrict them, so we round up the MHz value
386 * to ensure that perflib's computed KHz value is greater than
387 * or equal to powernow's KHz value.
388 */
389 if (speed % 1000 > 0)
390 speed_mhz++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700391
Dave Jonesb9e76382009-01-18 00:32:26 -0500392 if ((fid_codes[fid] % 10) == 5) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700393 if (have_a0 == 1)
Dave Jonesb9e76382009-01-18 00:32:26 -0500394 invalidate_entry(i);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700395 }
396
Dominik Brodowski2d06d8c2011-03-27 15:04:46 +0200397 pr_debug(" FID: 0x%x (%d.%dx [%dMHz]) "
Dave Jones32ee8c32006-02-28 00:43:23 -0500398 "VID: 0x%x (%d.%03dV)\n", fid, fid_codes[fid] / 10,
Daniel Drakedc2585e2007-05-02 23:19:05 +0100399 fid_codes[fid] % 10, speed_mhz, vid,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700400 mobile_vid_table[vid]/1000,
401 mobile_vid_table[vid]%1000);
402
Daniel Drakedc2585e2007-05-02 23:19:05 +0100403 if (state->core_frequency != speed_mhz) {
404 state->core_frequency = speed_mhz;
Dominik Brodowski2d06d8c2011-03-27 15:04:46 +0200405 pr_debug(" Corrected ACPI frequency to %d\n",
Daniel Drakedc2585e2007-05-02 23:19:05 +0100406 speed_mhz);
407 }
408
Linus Torvalds1da177e2005-04-16 15:20:36 -0700409 if (latency < pc.bits.sgtc)
410 latency = pc.bits.sgtc;
411
412 if (speed < minimum_speed)
413 minimum_speed = speed;
414 if (speed > maximum_speed)
415 maximum_speed = speed;
416 }
417
418 powernow_table[i].frequency = CPUFREQ_TABLE_END;
Viresh Kumar50701582013-03-30 16:25:15 +0530419 powernow_table[i].driver_data = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700420
421 /* notify BIOS that we exist */
422 acpi_processor_notify_smm(THIS_MODULE);
423
424 return 0;
425
426err2:
427 acpi_processor_unregister_performance(acpi_processor_perf, 0);
428err1:
Rusty Russell2fdf66b2008-12-31 18:08:47 -0800429 free_cpumask_var(acpi_processor_perf->shared_cpu_map);
430err05:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700431 kfree(acpi_processor_perf);
432err0:
Dave Jonesb9e76382009-01-18 00:32:26 -0500433 printk(KERN_WARNING PFX "ACPI perflib can not be used on "
434 "this platform\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700435 acpi_processor_perf = NULL;
436 return retval;
437}
438#else
439static int powernow_acpi_init(void)
440{
441 printk(KERN_INFO PFX "no support for ACPI processor found."
442 " Please recompile your kernel with ACPI processor\n");
443 return -EINVAL;
444}
445#endif
446
Dave Jonesb9e76382009-01-18 00:32:26 -0500447static void print_pst_entry(struct pst_s *pst, unsigned int j)
448{
Dominik Brodowski2d06d8c2011-03-27 15:04:46 +0200449 pr_debug("PST:%d (@%p)\n", j, pst);
450 pr_debug(" cpuid: 0x%x fsb: %d maxFID: 0x%x startvid: 0x%x\n",
Dave Jonesb9e76382009-01-18 00:32:26 -0500451 pst->cpuid, pst->fsbspeed, pst->maxfid, pst->startvid);
452}
453
454static int powernow_decode_bios(int maxfid, int startvid)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455{
456 struct psb_s *psb;
457 struct pst_s *pst;
458 unsigned int i, j;
459 unsigned char *p;
460 unsigned int etuple;
461 unsigned int ret;
462
463 etuple = cpuid_eax(0x80000001);
464
Dave Jonesb9e76382009-01-18 00:32:26 -0500465 for (i = 0xC0000; i < 0xffff0 ; i += 16) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700466
467 p = phys_to_virt(i);
468
Dave Jonesb9e76382009-01-18 00:32:26 -0500469 if (memcmp(p, "AMDK7PNOW!", 10) == 0) {
Dominik Brodowski2d06d8c2011-03-27 15:04:46 +0200470 pr_debug("Found PSB header at %p\n", p);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700471 psb = (struct psb_s *) p;
Dominik Brodowski2d06d8c2011-03-27 15:04:46 +0200472 pr_debug("Table version: 0x%x\n", psb->tableversion);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700473 if (psb->tableversion != 0x12) {
Dave Jonesb9e76382009-01-18 00:32:26 -0500474 printk(KERN_INFO PFX "Sorry, only v1.2 tables"
475 " supported right now\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700476 return -ENODEV;
477 }
478
Dominik Brodowski2d06d8c2011-03-27 15:04:46 +0200479 pr_debug("Flags: 0x%x\n", psb->flags);
Dave Jonesb9e76382009-01-18 00:32:26 -0500480 if ((psb->flags & 1) == 0)
Dominik Brodowski2d06d8c2011-03-27 15:04:46 +0200481 pr_debug("Mobile voltage regulator\n");
Dave Jonesb9e76382009-01-18 00:32:26 -0500482 else
Dominik Brodowski2d06d8c2011-03-27 15:04:46 +0200483 pr_debug("Desktop voltage regulator\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700484
485 latency = psb->settlingtime;
486 if (latency < 100) {
Dave Jonesb9e76382009-01-18 00:32:26 -0500487 printk(KERN_INFO PFX "BIOS set settling time "
488 "to %d microseconds. "
489 "Should be at least 100. "
490 "Correcting.\n", latency);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700491 latency = 100;
492 }
Dominik Brodowski2d06d8c2011-03-27 15:04:46 +0200493 pr_debug("Settling Time: %d microseconds.\n",
Dave Jonesb9e76382009-01-18 00:32:26 -0500494 psb->settlingtime);
Dominik Brodowski2d06d8c2011-03-27 15:04:46 +0200495 pr_debug("Has %d PST tables. (Only dumping ones "
Dave Jonesb9e76382009-01-18 00:32:26 -0500496 "relevant to this CPU).\n",
497 psb->numpst);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498
Viresh Kumard5b73cd2013-08-06 22:53:06 +0530499 p += sizeof(*psb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500
501 pst = (struct pst_s *) p;
502
Dave Jonesb9e76382009-01-18 00:32:26 -0500503 for (j = 0; j < psb->numpst; j++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700504 pst = (struct pst_s *) p;
505 number_scales = pst->numpstates;
506
Dave Jonesb9e76382009-01-18 00:32:26 -0500507 if ((etuple == pst->cpuid) &&
508 check_fsb(pst->fsbspeed) &&
509 (maxfid == pst->maxfid) &&
510 (startvid == pst->startvid)) {
511 print_pst_entry(pst, j);
Viresh Kumard5b73cd2013-08-06 22:53:06 +0530512 p = (char *)pst + sizeof(*pst);
Dave Jonesb9e76382009-01-18 00:32:26 -0500513 ret = get_ranges(p);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700514 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700515 } else {
Dave Jones8cbe0162006-05-30 17:26:08 -0400516 unsigned int k;
Viresh Kumard5b73cd2013-08-06 22:53:06 +0530517 p = (char *)pst + sizeof(*pst);
Dave Jonesb9e76382009-01-18 00:32:26 -0500518 for (k = 0; k < number_scales; k++)
519 p += 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700520 }
521 }
Dave Jonesb9e76382009-01-18 00:32:26 -0500522 printk(KERN_INFO PFX "No PST tables match this cpuid "
523 "(0x%x)\n", etuple);
524 printk(KERN_INFO PFX "This is indicative of a broken "
525 "BIOS.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700526
527 return -EINVAL;
528 }
529 p++;
530 }
531
532 return -ENODEV;
533}
534
535
Dave Jonesb9e76382009-01-18 00:32:26 -0500536static int powernow_target(struct cpufreq_policy *policy,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700537 unsigned int target_freq,
538 unsigned int relation)
539{
540 unsigned int newstate;
541
Dave Jonesb9e76382009-01-18 00:32:26 -0500542 if (cpufreq_frequency_table_target(policy, powernow_table, target_freq,
543 relation, &newstate))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700544 return -EINVAL;
545
Viresh Kumarb43a7ff2013-03-24 11:56:43 +0530546 change_speed(policy, newstate);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700547
548 return 0;
549}
550
551
Dave Jonesb9e76382009-01-18 00:32:26 -0500552static int powernow_verify(struct cpufreq_policy *policy)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700553{
554 return cpufreq_frequency_table_verify(policy, powernow_table);
555}
556
557/*
558 * We use the fact that the bus frequency is somehow
559 * a multiple of 100000/3 khz, then we compute sgtc according
560 * to this multiple.
561 * That way, we match more how AMD thinks all of that work.
562 * We will then get the same kind of behaviour already tested under
563 * the "well-known" other OS.
564 */
Paul Gortmaker27609842013-06-19 13:54:04 -0400565static int fixup_sgtc(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566{
567 unsigned int sgtc;
568 unsigned int m;
569
570 m = fsb / 3333;
571 if ((m % 10) >= 5)
572 m += 5;
573
574 m /= 10;
575
576 sgtc = 100 * m * latency;
577 sgtc = sgtc / 3;
578 if (sgtc > 0xfffff) {
579 printk(KERN_WARNING PFX "SGTC too large %d\n", sgtc);
580 sgtc = 0xfffff;
581 }
582 return sgtc;
583}
584
585static unsigned int powernow_get(unsigned int cpu)
586{
587 union msr_fidvidstatus fidvidstatus;
588 unsigned int cfid;
589
590 if (cpu)
591 return 0;
Dave Jonesb9e76382009-01-18 00:32:26 -0500592 rdmsrl(MSR_K7_FID_VID_STATUS, fidvidstatus.val);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700593 cfid = fidvidstatus.bits.CFID;
594
Dave Jonesb9e76382009-01-18 00:32:26 -0500595 return fsb * fid_codes[cfid] / 10;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700596}
597
598
Paul Gortmaker27609842013-06-19 13:54:04 -0400599static int acer_cpufreq_pst(const struct dmi_system_id *d)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700600{
Dave Jonesb9e76382009-01-18 00:32:26 -0500601 printk(KERN_WARNING PFX
602 "%s laptop with broken PST tables in BIOS detected.\n",
603 d->ident);
604 printk(KERN_WARNING PFX
605 "You need to downgrade to 3A21 (09/09/2002), or try a newer "
606 "BIOS than 3A71 (01/20/2003)\n");
607 printk(KERN_WARNING PFX
608 "cpufreq scaling has been disabled as a result of this.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700609 return 0;
610}
611
612/*
613 * Some Athlon laptops have really fucked PST tables.
614 * A BIOS update is all that can save them.
615 * Mention this, and disable cpufreq.
616 */
Paul Gortmaker27609842013-06-19 13:54:04 -0400617static struct dmi_system_id powernow_dmi_table[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700618 {
619 .callback = acer_cpufreq_pst,
620 .ident = "Acer Aspire",
621 .matches = {
622 DMI_MATCH(DMI_SYS_VENDOR, "Insyde Software"),
623 DMI_MATCH(DMI_BIOS_VERSION, "3A71"),
624 },
625 },
626 { }
627};
628
Paul Gortmaker27609842013-06-19 13:54:04 -0400629static int powernow_cpu_init(struct cpufreq_policy *policy)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700630{
631 union msr_fidvidstatus fidvidstatus;
632 int result;
633
634 if (policy->cpu != 0)
635 return -ENODEV;
636
Dave Jonesb9e76382009-01-18 00:32:26 -0500637 rdmsrl(MSR_K7_FID_VID_STATUS, fidvidstatus.val);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700638
Dave Jones436fe7b2006-06-05 14:03:50 -0400639 recalibrate_cpu_khz();
Dave Jones91350ed2005-05-31 19:03:45 -0700640
641 fsb = (10 * cpu_khz) / fid_codes[fidvidstatus.bits.CFID];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700642 if (!fsb) {
643 printk(KERN_WARNING PFX "can not determine bus frequency\n");
644 return -EINVAL;
645 }
Dominik Brodowski2d06d8c2011-03-27 15:04:46 +0200646 pr_debug("FSB: %3dMHz\n", fsb/1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700647
648 if (dmi_check_system(powernow_dmi_table) || acpi_force) {
Dave Jonesb9e76382009-01-18 00:32:26 -0500649 printk(KERN_INFO PFX "PSB/PST known to be broken. "
650 "Trying ACPI instead\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700651 result = powernow_acpi_init();
652 } else {
Dave Jonesb9e76382009-01-18 00:32:26 -0500653 result = powernow_decode_bios(fidvidstatus.bits.MFID,
654 fidvidstatus.bits.SVID);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700655 if (result) {
Dave Jonesb9e76382009-01-18 00:32:26 -0500656 printk(KERN_INFO PFX "Trying ACPI perflib\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700657 maximum_speed = 0;
658 minimum_speed = -1;
659 latency = 0;
660 result = powernow_acpi_init();
661 if (result) {
Dave Jonesb9e76382009-01-18 00:32:26 -0500662 printk(KERN_INFO PFX
663 "ACPI and legacy methods failed\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700664 }
665 } else {
666 /* SGTC use the bus clock as timer */
667 latency = fixup_sgtc();
668 printk(KERN_INFO PFX "SGTC: %d\n", latency);
669 }
670 }
671
672 if (result)
673 return result;
674
Dave Jonesb9e76382009-01-18 00:32:26 -0500675 printk(KERN_INFO PFX "Minimum speed %d MHz. Maximum speed %d MHz.\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700676 minimum_speed/1000, maximum_speed/1000);
677
Dave Jonesb9e76382009-01-18 00:32:26 -0500678 policy->cpuinfo.transition_latency =
679 cpufreq_scale(2000000UL, fsb, latency);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700680
681 policy->cur = powernow_get(0);
682
683 cpufreq_frequency_table_get_attr(powernow_table, policy->cpu);
684
685 return cpufreq_frequency_table_cpuinfo(policy, powernow_table);
686}
687
Dave Jonesb9e76382009-01-18 00:32:26 -0500688static int powernow_cpu_exit(struct cpufreq_policy *policy)
689{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700690 cpufreq_frequency_table_put_attr(policy->cpu);
691
692#ifdef CONFIG_X86_POWERNOW_K7_ACPI
693 if (acpi_processor_perf) {
694 acpi_processor_unregister_performance(acpi_processor_perf, 0);
Rusty Russell2fdf66b2008-12-31 18:08:47 -0800695 free_cpumask_var(acpi_processor_perf->shared_cpu_map);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700696 kfree(acpi_processor_perf);
697 }
698#endif
699
Jesper Juhl4ae66732005-06-25 14:58:48 -0700700 kfree(powernow_table);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700701 return 0;
702}
703
Dave Jonesb9e76382009-01-18 00:32:26 -0500704static struct freq_attr *powernow_table_attr[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700705 &cpufreq_freq_attr_scaling_available_freqs,
706 NULL,
707};
708
Linus Torvalds221dee22007-02-26 14:55:48 -0800709static struct cpufreq_driver powernow_driver = {
Thomas Renningere2f74f32009-11-19 12:31:01 +0100710 .verify = powernow_verify,
711 .target = powernow_target,
712 .get = powernow_get,
713#ifdef CONFIG_X86_POWERNOW_K7_ACPI
714 .bios_limit = acpi_processor_get_bios_limit,
715#endif
716 .init = powernow_cpu_init,
717 .exit = powernow_cpu_exit,
718 .name = "powernow-k7",
Thomas Renningere2f74f32009-11-19 12:31:01 +0100719 .attr = powernow_table_attr,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700720};
721
Dave Jonesb9e76382009-01-18 00:32:26 -0500722static int __init powernow_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700723{
Dave Jonesb9e76382009-01-18 00:32:26 -0500724 if (check_powernow() == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700725 return -ENODEV;
726 return cpufreq_register_driver(&powernow_driver);
727}
728
729
Dave Jonesb9e76382009-01-18 00:32:26 -0500730static void __exit powernow_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700731{
732 cpufreq_unregister_driver(&powernow_driver);
733}
734
735module_param(acpi_force, int, 0444);
736MODULE_PARM_DESC(acpi_force, "Force ACPI to be used.");
737
Dave Jonesb9e76382009-01-18 00:32:26 -0500738MODULE_AUTHOR("Dave Jones <davej@redhat.com>");
739MODULE_DESCRIPTION("Powernow driver for AMD K7 processors.");
740MODULE_LICENSE("GPL");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700741
742late_initcall(powernow_init);
743module_exit(powernow_exit);
744