[go: nahoru, domu]

11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *  AMD K7 Powernow driver.
3f4432c5caec5fa95ea7eefd00f8e6cee17e2e023Dave Jones *  (C) 2003 Dave Jones on behalf of SuSE Labs.
41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *  (C) 2003-2004 Dave Jones <davej@redhat.com>
51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *  Licensed under the terms of the GNU GPL License version 2.
71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *  Based upon datasheets & sample CPUs kindly provided by AMD.
81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
9b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones * Errata 5:
10b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones *  CPU may fail to execute a FID/VID change in presence of interrupt.
11b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones *  - We cli/sti on stepping A0 CPUs around the FID/VID transition.
12b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones * Errata 15:
13b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones *  CPU with half frequency multipliers may hang upon wakeup from disconnect.
14b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones *  - We disable half multipliers if ACPI is used on A0 stepping CPUs.
151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/kernel.h>
181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h>
191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/moduleparam.h>
201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h>
211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/cpufreq.h>
221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/slab.h>
231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/string.h>
241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/dmi.h>
25b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones#include <linux/timex.h>
26b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones#include <linux/io.h>
271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
28b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones#include <asm/timer.h>		/* Needed for recalibrate_cpu_khz() */
291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/msr.h>
30fa8031aefec0cf7ea6c2387c93610d99d9659aa2Andi Kleen#include <asm/cpu_device_id.h>
311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef CONFIG_X86_POWERNOW_K7_ACPI
331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/acpi.h>
341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <acpi/processor.h>
351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "powernow-k7.h"
381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define PFX "powernow: "
401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstruct psb_s {
431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u8 signature[10];
441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u8 tableversion;
451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u8 flags;
461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u16 settlingtime;
471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u8 reserved1;
481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u8 numpst;
491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstruct pst_s {
521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u32 cpuid;
531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u8 fsbspeed;
541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u8 maxfid;
551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u8 startvid;
561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u8 numpstates;
571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef CONFIG_X86_POWERNOW_K7_ACPI
601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsunion powernow_acpi_control_t {
611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct {
621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		unsigned long fid:5,
63b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones			vid:5,
64b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones			sgtc:20,
65b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones			res1:2;
661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} bits;
671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long val;
681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* divide by 1000 to get VCore voltage in V. */
72bd5ab26a7d0cc834d846fe5dd7291f0aed3be72bDave Jonesstatic const int mobile_vid_table[32] = {
731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    2000, 1950, 1900, 1850, 1800, 1750, 1700, 1650,
741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    1600, 1550, 1500, 1450, 1400, 1350, 1300, 0,
751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    1275, 1250, 1225, 1200, 1175, 1150, 1125, 1100,
761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    1075, 1050, 1025, 1000, 975, 950, 925, 0,
771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* divide by 10 to get FID. */
80bd5ab26a7d0cc834d846fe5dd7291f0aed3be72bDave Jonesstatic const int fid_codes[32] = {
811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    110, 115, 120, 125, 50, 55, 60, 65,
821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    70, 75, 80, 85, 90, 95, 100, 105,
831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    30, 190, 40, 200, 130, 135, 140, 210,
841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    150, 225, 160, 165, 170, 180, -1, -1,
851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* This parameter is used in order to force ACPI instead of legacy method for
881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * configuration purpose.
891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acpi_force;
921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct cpufreq_frequency_table *powernow_table;
941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic unsigned int can_scale_bus;
961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic unsigned int can_scale_vid;
97fff78ad5cee1d6f695103ec590cbd2a9f3c39e8cDave Jonesstatic unsigned int minimum_speed = -1;
981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic unsigned int maximum_speed;
991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic unsigned int number_scales;
1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic unsigned int fsb;
1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic unsigned int latency;
1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic char have_a0;
1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int check_fsb(unsigned int fsbspeed)
1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int delta;
1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int f = fsb / 1000;
1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	delta = (fsbspeed > f) ? fsbspeed - f : f - fsbspeed;
110b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones	return delta < 5;
1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
113fa8031aefec0cf7ea6c2387c93610d99d9659aa2Andi Kleenstatic const struct x86_cpu_id powernow_k7_cpuids[] = {
11430bcfff9bd41db5edab6420d0ae2e435609eb083Ben Hutchings	{ X86_VENDOR_AMD, 6, },
115fa8031aefec0cf7ea6c2387c93610d99d9659aa2Andi Kleen	{}
116fa8031aefec0cf7ea6c2387c93610d99d9659aa2Andi Kleen};
117fa8031aefec0cf7ea6c2387c93610d99d9659aa2Andi KleenMODULE_DEVICE_TABLE(x86cpu, powernow_k7_cpuids);
118fa8031aefec0cf7ea6c2387c93610d99d9659aa2Andi Kleen
1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int check_powernow(void)
1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
12192cb7612aee39642d109b8d935ad265e602c0563Mike Travis	struct cpuinfo_x86 *c = &cpu_data(0);
1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int maxei, eax, ebx, ecx, edx;
1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
124fa8031aefec0cf7ea6c2387c93610d99d9659aa2Andi Kleen	if (!x86_match_cpu(powernow_k7_cpuids))
1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Get maximum capabilities */
128b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones	maxei = cpuid_eax(0x80000000);
1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (maxei < 0x80000007) {	/* Any powernow info ? */
1301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef MODULE
131b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones		printk(KERN_INFO PFX "No powernow capabilities detected\n");
1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
1331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
1341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((c->x86_model == 6) && (c->x86_mask == 0)) {
137b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones		printk(KERN_INFO PFX "K7 660[A0] core detected, "
138b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones				"enabling errata workarounds\n");
1391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		have_a0 = 1;
1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cpuid(0x80000007, &eax, &ebx, &ecx, &edx);
1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Check we can actually do something before we say anything.*/
1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!(edx & (1 << 1 | 1 << 2)))
1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
1471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
148b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones	printk(KERN_INFO PFX "PowerNOW! Technology present. Can scale: ");
1491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (edx & 1 << 1) {
151b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones		printk("frequency");
152b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones		can_scale_bus = 1;
1531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((edx & (1 << 1 | 1 << 2)) == 0x6)
156b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones		printk(" and ");
1571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (edx & 1 << 2) {
159b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones		printk("voltage");
160b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones		can_scale_vid = 1;
1611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
163b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones	printk(".\n");
1641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 1;
1651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
167d38e73e8dad454a5916f446b0d3523c1161ae95aDave Jones#ifdef CONFIG_X86_POWERNOW_K7_ACPI
168b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jonesstatic void invalidate_entry(unsigned int entry)
169b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones{
170b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones	powernow_table[entry].frequency = CPUFREQ_ENTRY_INVALID;
171b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones}
172d38e73e8dad454a5916f446b0d3523c1161ae95aDave Jones#endif
1731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
174b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jonesstatic int get_ranges(unsigned char *pst)
1751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int j;
1771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int speed;
1781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u8 fid, vid;
1791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
180d5b73cd870e2b049ef566aec2791dbf5fd26a7ecViresh Kumar	powernow_table = kzalloc((sizeof(*powernow_table) *
181b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones				(number_scales + 1)), GFP_KERNEL);
1821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!powernow_table)
1831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENOMEM;
1841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
185b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones	for (j = 0 ; j < number_scales; j++) {
1861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		fid = *pst++;
1871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		powernow_table[j].frequency = (fsb * fid_codes[fid]) / 10;
1895070158804b5339c71809f5e673cea1cfacd804dViresh Kumar		powernow_table[j].driver_data = fid; /* lower 8 bits */
1901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		speed = powernow_table[j].frequency;
1921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
193b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones		if ((fid_codes[fid] % 10) == 5) {
1941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef CONFIG_X86_POWERNOW_K7_ACPI
1951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (have_a0 == 1)
196b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones				invalidate_entry(j);
1971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
1991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (speed < minimum_speed)
2011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			minimum_speed = speed;
2021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (speed > maximum_speed)
2031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			maximum_speed = speed;
2041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		vid = *pst++;
2065070158804b5339c71809f5e673cea1cfacd804dViresh Kumar		powernow_table[j].driver_data |= (vid << 8); /* upper 8 bits */
2071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2082d06d8c49afdcc9bb35a85039fa50f0fe35bd40eDominik Brodowski		pr_debug("   FID: 0x%x (%d.%dx [%dMHz])  "
20932ee8c3e470d86588b51dc42ed01e85c5fa0f180Dave Jones			 "VID: 0x%x (%d.%03dV)\n", fid, fid_codes[fid] / 10,
21032ee8c3e470d86588b51dc42ed01e85c5fa0f180Dave Jones			 fid_codes[fid] % 10, speed/1000, vid,
2111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 mobile_vid_table[vid]/1000,
2121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 mobile_vid_table[vid]%1000);
2131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	powernow_table[number_scales].frequency = CPUFREQ_TABLE_END;
2155070158804b5339c71809f5e673cea1cfacd804dViresh Kumar	powernow_table[number_scales].driver_data = 0;
2161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
2181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void change_FID(int fid)
2221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	union msr_fidvidctl fidvidctl;
2241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
225b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones	rdmsrl(MSR_K7_FID_VID_CTL, fidvidctl.val);
2261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (fidvidctl.bits.FID != fid) {
2271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		fidvidctl.bits.SGTC = latency;
2281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		fidvidctl.bits.FID = fid;
2291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		fidvidctl.bits.VIDC = 0;
2301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		fidvidctl.bits.FIDC = 1;
231b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones		wrmsrl(MSR_K7_FID_VID_CTL, fidvidctl.val);
2321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void change_VID(int vid)
2371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	union msr_fidvidctl fidvidctl;
2391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
240b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones	rdmsrl(MSR_K7_FID_VID_CTL, fidvidctl.val);
2411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (fidvidctl.bits.VID != vid) {
2421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		fidvidctl.bits.SGTC = latency;
2431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		fidvidctl.bits.VID = vid;
2441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		fidvidctl.bits.FIDC = 0;
2451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		fidvidctl.bits.VIDC = 1;
246b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones		wrmsrl(MSR_K7_FID_VID_CTL, fidvidctl.val);
2471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2519c0ebcf78fde0ffa348a95a544c6d3f2dac5af65Viresh Kumarstatic int powernow_target(struct cpufreq_policy *policy, unsigned int index)
2521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u8 fid, vid;
2541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct cpufreq_freqs freqs;
2551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	union msr_fidvidstatus fidvidstatus;
2561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int cfid;
2571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* fid are the lower 8 bits of the index we stored into
2591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * the cpufreq frequency table in powernow_decode_bios,
2601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * vid are the upper 8 bits.
2611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
2621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2635070158804b5339c71809f5e673cea1cfacd804dViresh Kumar	fid = powernow_table[index].driver_data & 0xFF;
2645070158804b5339c71809f5e673cea1cfacd804dViresh Kumar	vid = (powernow_table[index].driver_data & 0xFF00) >> 8;
2651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
266b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones	rdmsrl(MSR_K7_FID_VID_STATUS, fidvidstatus.val);
2671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cfid = fidvidstatus.bits.CFID;
2681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	freqs.old = fsb * fid_codes[cfid] / 10;
2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	freqs.new = powernow_table[index].frequency;
2711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Now do the magic poking into the MSRs.  */
2731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (have_a0 == 1)	/* A0 errata 5 */
2751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		local_irq_disable();
2761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (freqs.old > freqs.new) {
2781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* Going down, so change FID first */
2791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		change_FID(fid);
2801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		change_VID(vid);
2811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
2821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* Going up, so change VID first */
2831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		change_VID(vid);
2841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		change_FID(fid);
2851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (have_a0 == 1)
2891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		local_irq_enable();
2901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2919c0ebcf78fde0ffa348a95a544c6d3f2dac5af65Viresh Kumar	return 0;
2921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef CONFIG_X86_POWERNOW_K7_ACPI
2961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct acpi_processor_performance *acpi_processor_perf;
2981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int powernow_acpi_init(void)
3001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i;
3021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int retval = 0;
3031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	union powernow_acpi_control_t pc;
3041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (acpi_processor_perf != NULL && powernow_table != NULL) {
3061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		retval = -EINVAL;
3071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto err0;
3081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
310d5b73cd870e2b049ef566aec2791dbf5fd26a7ecViresh Kumar	acpi_processor_perf = kzalloc(sizeof(*acpi_processor_perf), GFP_KERNEL);
3111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!acpi_processor_perf) {
3121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		retval = -ENOMEM;
3131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto err0;
3141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
316eaa958402ea40851097d051f52ba1bb7a885efe9Yinghai Lu	if (!zalloc_cpumask_var(&acpi_processor_perf->shared_cpu_map,
3172fdf66b491ac706657946442789ec644cc317e1aRusty Russell								GFP_KERNEL)) {
3182fdf66b491ac706657946442789ec644cc317e1aRusty Russell		retval = -ENOMEM;
3192fdf66b491ac706657946442789ec644cc317e1aRusty Russell		goto err05;
3202fdf66b491ac706657946442789ec644cc317e1aRusty Russell	}
3212fdf66b491ac706657946442789ec644cc317e1aRusty Russell
3221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (acpi_processor_register_performance(acpi_processor_perf, 0)) {
3231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		retval = -EIO;
3241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto err1;
3251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
327b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones	if (acpi_processor_perf->control_register.space_id !=
328b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones			ACPI_ADR_SPACE_FIXED_HARDWARE) {
3291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		retval = -ENODEV;
3301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto err2;
3311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
333b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones	if (acpi_processor_perf->status_register.space_id !=
334b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones			ACPI_ADR_SPACE_FIXED_HARDWARE) {
3351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		retval = -ENODEV;
3361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto err2;
3371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	number_scales = acpi_processor_perf->state_count;
3401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (number_scales < 2) {
3421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		retval = -ENODEV;
3431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto err2;
3441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
346d5b73cd870e2b049ef566aec2791dbf5fd26a7ecViresh Kumar	powernow_table = kzalloc((sizeof(*powernow_table) *
347b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones				(number_scales + 1)), GFP_KERNEL);
3481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!powernow_table) {
3491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		retval = -ENOMEM;
3501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto err2;
3511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	pc.val = (unsigned long) acpi_processor_perf->states[0].control;
3541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < number_scales; i++) {
3551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		u8 fid, vid;
356dc2585eb478cfeb45b3d6e235ac7b5918fb859f7Daniel Drake		struct acpi_processor_px *state =
357dc2585eb478cfeb45b3d6e235ac7b5918fb859f7Daniel Drake			&acpi_processor_perf->states[i];
358dc2585eb478cfeb45b3d6e235ac7b5918fb859f7Daniel Drake		unsigned int speed, speed_mhz;
3591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
360dc2585eb478cfeb45b3d6e235ac7b5918fb859f7Daniel Drake		pc.val = (unsigned long) state->control;
3612d06d8c49afdcc9bb35a85039fa50f0fe35bd40eDominik Brodowski		pr_debug("acpi:  P%d: %d MHz %d mW %d uS control %08x SGTC %d\n",
3621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 i,
363dc2585eb478cfeb45b3d6e235ac7b5918fb859f7Daniel Drake			 (u32) state->core_frequency,
364dc2585eb478cfeb45b3d6e235ac7b5918fb859f7Daniel Drake			 (u32) state->power,
365dc2585eb478cfeb45b3d6e235ac7b5918fb859f7Daniel Drake			 (u32) state->transition_latency,
366dc2585eb478cfeb45b3d6e235ac7b5918fb859f7Daniel Drake			 (u32) state->control,
3671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 pc.bits.sgtc);
3681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		vid = pc.bits.vid;
3701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		fid = pc.bits.fid;
3711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		powernow_table[i].frequency = fsb * fid_codes[fid] / 10;
3735070158804b5339c71809f5e673cea1cfacd804dViresh Kumar		powernow_table[i].driver_data = fid; /* lower 8 bits */
3745070158804b5339c71809f5e673cea1cfacd804dViresh Kumar		powernow_table[i].driver_data |= (vid << 8); /* upper 8 bits */
3751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		speed = powernow_table[i].frequency;
377dc2585eb478cfeb45b3d6e235ac7b5918fb859f7Daniel Drake		speed_mhz = speed / 1000;
378dc2585eb478cfeb45b3d6e235ac7b5918fb859f7Daniel Drake
379dc2585eb478cfeb45b3d6e235ac7b5918fb859f7Daniel Drake		/* processor_perflib will multiply the MHz value by 1000 to
380dc2585eb478cfeb45b3d6e235ac7b5918fb859f7Daniel Drake		 * get a KHz value (e.g. 1266000). However, powernow-k7 works
381dc2585eb478cfeb45b3d6e235ac7b5918fb859f7Daniel Drake		 * with true KHz values (e.g. 1266768). To ensure that all
382dc2585eb478cfeb45b3d6e235ac7b5918fb859f7Daniel Drake		 * powernow frequencies are available, we must ensure that
383dc2585eb478cfeb45b3d6e235ac7b5918fb859f7Daniel Drake		 * ACPI doesn't restrict them, so we round up the MHz value
384dc2585eb478cfeb45b3d6e235ac7b5918fb859f7Daniel Drake		 * to ensure that perflib's computed KHz value is greater than
385dc2585eb478cfeb45b3d6e235ac7b5918fb859f7Daniel Drake		 * or equal to powernow's KHz value.
386dc2585eb478cfeb45b3d6e235ac7b5918fb859f7Daniel Drake		 */
387dc2585eb478cfeb45b3d6e235ac7b5918fb859f7Daniel Drake		if (speed % 1000 > 0)
388dc2585eb478cfeb45b3d6e235ac7b5918fb859f7Daniel Drake			speed_mhz++;
3891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
390b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones		if ((fid_codes[fid] % 10) == 5) {
3911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (have_a0 == 1)
392b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones				invalidate_entry(i);
3931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
3941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3952d06d8c49afdcc9bb35a85039fa50f0fe35bd40eDominik Brodowski		pr_debug("   FID: 0x%x (%d.%dx [%dMHz])  "
39632ee8c3e470d86588b51dc42ed01e85c5fa0f180Dave Jones			 "VID: 0x%x (%d.%03dV)\n", fid, fid_codes[fid] / 10,
397dc2585eb478cfeb45b3d6e235ac7b5918fb859f7Daniel Drake			 fid_codes[fid] % 10, speed_mhz, vid,
3981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 mobile_vid_table[vid]/1000,
3991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 mobile_vid_table[vid]%1000);
4001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
401dc2585eb478cfeb45b3d6e235ac7b5918fb859f7Daniel Drake		if (state->core_frequency != speed_mhz) {
402dc2585eb478cfeb45b3d6e235ac7b5918fb859f7Daniel Drake			state->core_frequency = speed_mhz;
4032d06d8c49afdcc9bb35a85039fa50f0fe35bd40eDominik Brodowski			pr_debug("   Corrected ACPI frequency to %d\n",
404dc2585eb478cfeb45b3d6e235ac7b5918fb859f7Daniel Drake				speed_mhz);
405dc2585eb478cfeb45b3d6e235ac7b5918fb859f7Daniel Drake		}
406dc2585eb478cfeb45b3d6e235ac7b5918fb859f7Daniel Drake
4071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (latency < pc.bits.sgtc)
4081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			latency = pc.bits.sgtc;
4091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (speed < minimum_speed)
4111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			minimum_speed = speed;
4121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (speed > maximum_speed)
4131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			maximum_speed = speed;
4141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	powernow_table[i].frequency = CPUFREQ_TABLE_END;
4175070158804b5339c71809f5e673cea1cfacd804dViresh Kumar	powernow_table[i].driver_data = 0;
4181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* notify BIOS that we exist */
4201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acpi_processor_notify_smm(THIS_MODULE);
4211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
4231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldserr2:
4251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acpi_processor_unregister_performance(acpi_processor_perf, 0);
4261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldserr1:
4272fdf66b491ac706657946442789ec644cc317e1aRusty Russell	free_cpumask_var(acpi_processor_perf->shared_cpu_map);
4282fdf66b491ac706657946442789ec644cc317e1aRusty Russellerr05:
4291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(acpi_processor_perf);
4301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldserr0:
431b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones	printk(KERN_WARNING PFX "ACPI perflib can not be used on "
432b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones			"this platform\n");
4331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acpi_processor_perf = NULL;
4341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return retval;
4351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#else
4371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int powernow_acpi_init(void)
4381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk(KERN_INFO PFX "no support for ACPI processor found."
4401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       "  Please recompile your kernel with ACPI processor\n");
4411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -EINVAL;
4421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
4441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
445b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jonesstatic void print_pst_entry(struct pst_s *pst, unsigned int j)
446b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones{
4472d06d8c49afdcc9bb35a85039fa50f0fe35bd40eDominik Brodowski	pr_debug("PST:%d (@%p)\n", j, pst);
4482d06d8c49afdcc9bb35a85039fa50f0fe35bd40eDominik Brodowski	pr_debug(" cpuid: 0x%x  fsb: %d  maxFID: 0x%x  startvid: 0x%x\n",
449b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones		pst->cpuid, pst->fsbspeed, pst->maxfid, pst->startvid);
450b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones}
451b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones
452b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jonesstatic int powernow_decode_bios(int maxfid, int startvid)
4531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct psb_s *psb;
4551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct pst_s *pst;
4561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int i, j;
4571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned char *p;
4581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int etuple;
4591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int ret;
4601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	etuple = cpuid_eax(0x80000001);
4621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
463b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones	for (i = 0xC0000; i < 0xffff0 ; i += 16) {
4641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		p = phys_to_virt(i);
4661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
467b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones		if (memcmp(p, "AMDK7PNOW!",  10) == 0) {
4682d06d8c49afdcc9bb35a85039fa50f0fe35bd40eDominik Brodowski			pr_debug("Found PSB header at %p\n", p);
4691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			psb = (struct psb_s *) p;
4702d06d8c49afdcc9bb35a85039fa50f0fe35bd40eDominik Brodowski			pr_debug("Table version: 0x%x\n", psb->tableversion);
4711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (psb->tableversion != 0x12) {
472b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones				printk(KERN_INFO PFX "Sorry, only v1.2 tables"
473b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones						" supported right now\n");
4741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				return -ENODEV;
4751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
4761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4772d06d8c49afdcc9bb35a85039fa50f0fe35bd40eDominik Brodowski			pr_debug("Flags: 0x%x\n", psb->flags);
478b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones			if ((psb->flags & 1) == 0)
4792d06d8c49afdcc9bb35a85039fa50f0fe35bd40eDominik Brodowski				pr_debug("Mobile voltage regulator\n");
480b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones			else
4812d06d8c49afdcc9bb35a85039fa50f0fe35bd40eDominik Brodowski				pr_debug("Desktop voltage regulator\n");
4821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			latency = psb->settlingtime;
4841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (latency < 100) {
485b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones				printk(KERN_INFO PFX "BIOS set settling time "
486b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones						"to %d microseconds. "
487b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones						"Should be at least 100. "
488b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones						"Correcting.\n", latency);
4891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				latency = 100;
4901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
4912d06d8c49afdcc9bb35a85039fa50f0fe35bd40eDominik Brodowski			pr_debug("Settling Time: %d microseconds.\n",
492b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones					psb->settlingtime);
4932d06d8c49afdcc9bb35a85039fa50f0fe35bd40eDominik Brodowski			pr_debug("Has %d PST tables. (Only dumping ones "
494b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones					"relevant to this CPU).\n",
495b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones					psb->numpst);
4961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
497d5b73cd870e2b049ef566aec2791dbf5fd26a7ecViresh Kumar			p += sizeof(*psb);
4981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			pst = (struct pst_s *) p;
5001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
501b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones			for (j = 0; j < psb->numpst; j++) {
5021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				pst = (struct pst_s *) p;
5031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				number_scales = pst->numpstates;
5041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
505b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones				if ((etuple == pst->cpuid) &&
506b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones				    check_fsb(pst->fsbspeed) &&
507b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones				    (maxfid == pst->maxfid) &&
508b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones				    (startvid == pst->startvid)) {
509b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones					print_pst_entry(pst, j);
510d5b73cd870e2b049ef566aec2791dbf5fd26a7ecViresh Kumar					p = (char *)pst + sizeof(*pst);
511b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones					ret = get_ranges(p);
5121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					return ret;
5131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				} else {
5148cbe0169053ffa185ad349088eb0901946c14a09Dave Jones					unsigned int k;
515d5b73cd870e2b049ef566aec2791dbf5fd26a7ecViresh Kumar					p = (char *)pst + sizeof(*pst);
516b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones					for (k = 0; k < number_scales; k++)
517b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones						p += 2;
5181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				}
5191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
520b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones			printk(KERN_INFO PFX "No PST tables match this cpuid "
521b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones					"(0x%x)\n", etuple);
522b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones			printk(KERN_INFO PFX "This is indicative of a broken "
523b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones					"BIOS.\n");
5241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EINVAL;
5261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
5271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		p++;
5281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
5291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -ENODEV;
5311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
5351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * We use the fact that the bus frequency is somehow
5361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * a multiple of 100000/3 khz, then we compute sgtc according
5371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * to this multiple.
5381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * That way, we match more how AMD thinks all of that work.
5391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * We will then get the same kind of behaviour already tested under
5401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * the "well-known" other OS.
5411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
5422760984f6578d5a462155bb4727766d0c8b68387Paul Gortmakerstatic int fixup_sgtc(void)
5431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int sgtc;
5451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int m;
5461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	m = fsb / 3333;
5481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((m % 10) >= 5)
5491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		m += 5;
5501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	m /= 10;
5521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	sgtc = 100 * m * latency;
5541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	sgtc = sgtc / 3;
5551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (sgtc > 0xfffff) {
5561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(KERN_WARNING PFX "SGTC too large %d\n", sgtc);
5571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		sgtc = 0xfffff;
5581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
5591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return sgtc;
5601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic unsigned int powernow_get(unsigned int cpu)
5631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	union msr_fidvidstatus fidvidstatus;
5651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int cfid;
5661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (cpu)
5681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
569b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones	rdmsrl(MSR_K7_FID_VID_STATUS, fidvidstatus.val);
5701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cfid = fidvidstatus.bits.CFID;
5711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
572b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones	return fsb * fid_codes[cfid] / 10;
5731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5762760984f6578d5a462155bb4727766d0c8b68387Paul Gortmakerstatic int acer_cpufreq_pst(const struct dmi_system_id *d)
5771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
578b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones	printk(KERN_WARNING PFX
579b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones		"%s laptop with broken PST tables in BIOS detected.\n",
580b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones		d->ident);
581b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones	printk(KERN_WARNING PFX
582b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones		"You need to downgrade to 3A21 (09/09/2002), or try a newer "
583b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones		"BIOS than 3A71 (01/20/2003)\n");
584b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones	printk(KERN_WARNING PFX
585b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones		"cpufreq scaling has been disabled as a result of this.\n");
5861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
5871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
5901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Some Athlon laptops have really fucked PST tables.
5911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * A BIOS update is all that can save them.
5921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Mention this, and disable cpufreq.
5931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
5942760984f6578d5a462155bb4727766d0c8b68387Paul Gortmakerstatic struct dmi_system_id powernow_dmi_table[] = {
5951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{
5961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.callback = acer_cpufreq_pst,
5971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.ident = "Acer Aspire",
5981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.matches = {
5991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			DMI_MATCH(DMI_SYS_VENDOR, "Insyde Software"),
6001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			DMI_MATCH(DMI_BIOS_VERSION, "3A71"),
6011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		},
6021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	},
6031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ }
6041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
6051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6062760984f6578d5a462155bb4727766d0c8b68387Paul Gortmakerstatic int powernow_cpu_init(struct cpufreq_policy *policy)
6071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	union msr_fidvidstatus fidvidstatus;
6091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int result;
6101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (policy->cpu != 0)
6121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENODEV;
6131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
614b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones	rdmsrl(MSR_K7_FID_VID_STATUS, fidvidstatus.val);
6151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
616436fe7b8b4a5016ef1fcb32bff77bde84003e15dDave Jones	recalibrate_cpu_khz();
61791350ed49bf3613e243c2e216228cd4ae8f32516Dave Jones
61891350ed49bf3613e243c2e216228cd4ae8f32516Dave Jones	fsb = (10 * cpu_khz) / fid_codes[fidvidstatus.bits.CFID];
6191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!fsb) {
6201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(KERN_WARNING PFX "can not determine bus frequency\n");
6211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
6221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6232d06d8c49afdcc9bb35a85039fa50f0fe35bd40eDominik Brodowski	pr_debug("FSB: %3dMHz\n", fsb/1000);
6241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (dmi_check_system(powernow_dmi_table) || acpi_force) {
626b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones		printk(KERN_INFO PFX "PSB/PST known to be broken.  "
627b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones				"Trying ACPI instead\n");
6281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		result = powernow_acpi_init();
6291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
630b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones		result = powernow_decode_bios(fidvidstatus.bits.MFID,
631b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones				fidvidstatus.bits.SVID);
6321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (result) {
633b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones			printk(KERN_INFO PFX "Trying ACPI perflib\n");
6341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			maximum_speed = 0;
6351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			minimum_speed = -1;
6361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			latency = 0;
6371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			result = powernow_acpi_init();
6381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (result) {
639b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones				printk(KERN_INFO PFX
640b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones					"ACPI and legacy methods failed\n");
6411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
6421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} else {
6431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* SGTC use the bus clock as timer */
6441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			latency = fixup_sgtc();
6451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			printk(KERN_INFO PFX "SGTC: %d\n", latency);
6461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
6471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (result)
6501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return result;
6511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
652b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones	printk(KERN_INFO PFX "Minimum speed %d MHz. Maximum speed %d MHz.\n",
6531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				minimum_speed/1000, maximum_speed/1000);
6541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
655b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones	policy->cpuinfo.transition_latency =
656b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones		cpufreq_scale(2000000UL, fsb, latency);
6571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
658b147405aa8e568750bfa99501c7fa831edef47c9Viresh Kumar	return cpufreq_table_validate_and_show(policy, powernow_table);
6591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
661b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jonesstatic int powernow_cpu_exit(struct cpufreq_policy *policy)
662b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones{
6631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef CONFIG_X86_POWERNOW_K7_ACPI
6641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (acpi_processor_perf) {
6651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		acpi_processor_unregister_performance(acpi_processor_perf, 0);
6662fdf66b491ac706657946442789ec644cc317e1aRusty Russell		free_cpumask_var(acpi_processor_perf->shared_cpu_map);
6671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		kfree(acpi_processor_perf);
6681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
6701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6714ae6673e029d609da7ef4311440d6de501d6967aJesper Juhl	kfree(powernow_table);
6721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
6731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
675221dee285ee38099b82437531bcae9fa9cb64cc4Linus Torvaldsstatic struct cpufreq_driver powernow_driver = {
676d63bd27fe953daa402a108e141c36dcc59c6931cViresh Kumar	.verify		= cpufreq_generic_frequency_table_verify,
6779c0ebcf78fde0ffa348a95a544c6d3f2dac5af65Viresh Kumar	.target_index	= powernow_target,
678e2f74f355e9e2914483db10c05d70e69e0b7ae04Thomas Renninger	.get		= powernow_get,
679e2f74f355e9e2914483db10c05d70e69e0b7ae04Thomas Renninger#ifdef CONFIG_X86_POWERNOW_K7_ACPI
680e2f74f355e9e2914483db10c05d70e69e0b7ae04Thomas Renninger	.bios_limit	= acpi_processor_get_bios_limit,
681e2f74f355e9e2914483db10c05d70e69e0b7ae04Thomas Renninger#endif
682e2f74f355e9e2914483db10c05d70e69e0b7ae04Thomas Renninger	.init		= powernow_cpu_init,
683e2f74f355e9e2914483db10c05d70e69e0b7ae04Thomas Renninger	.exit		= powernow_cpu_exit,
684e2f74f355e9e2914483db10c05d70e69e0b7ae04Thomas Renninger	.name		= "powernow-k7",
685d63bd27fe953daa402a108e141c36dcc59c6931cViresh Kumar	.attr		= cpufreq_generic_attr,
6861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
6871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
688b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jonesstatic int __init powernow_init(void)
6891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
690b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jones	if (check_powernow() == 0)
6911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENODEV;
6921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return cpufreq_register_driver(&powernow_driver);
6931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
696b9e7638a301b1245d4675087a05fa90fb4fa1845Dave Jonesstatic void __exit powernow_exit(void)
6971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cpufreq_unregister_driver(&powernow_driver);
6991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param(acpi_force,  int, 0444);
7021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(acpi_force, "Force ACPI to be used.");
7031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
704b9e7638a301b1245d4675087a05fa90fb4fa1845Dave JonesMODULE_AUTHOR("Dave Jones <davej@redhat.com>");
705b9e7638a301b1245d4675087a05fa90fb4fa1845Dave JonesMODULE_DESCRIPTION("Powernow driver for AMD K7 processors.");
706b9e7638a301b1245d4675087a05fa90fb4fa1845Dave JonesMODULE_LICENSE("GPL");
7071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldslate_initcall(powernow_init);
7091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit(powernow_exit);
7101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
711