* powernow-k8: support acpi
[not found] ` <20040321185417.GA7969@dominikbrodowski.de>
@ 2004-03-26 12:29 ` Pavel Machek
0 siblings, 0 replies; 21+ messages in thread
From: Pavel Machek @ 2004-03-26 12:29 UTC (permalink / raw)
To: Dominik Brodowski
Cc: Andrew Morton, Cpufreq mailing list, davej, paul.devriendt,
mark.langsdorf, kernel list
Hi!
> Other than that, I'd strongly appreciate pushing the change "upstream" to
> Dave Jones / Andrew Morton / Linus Torvalds
Here it is.
This is new version of powernow-k8 driver. It adds SMP support, and
support for getting tables through ACPI. (ACPI support is really
important, because many machines have broken "legacy" tables). Please
apply,
Pavel
--- clean/arch/i386/kernel/cpu/cpufreq/powernow-k8.c 2004-03-11 18:10:38.000000000 +0100
+++ linux-pn/arch/i386/kernel/cpu/cpufreq/powernow-k8.c 2004-03-26 13:23:08.000000000 +0100
@@ -1,22 +1,24 @@
/*
- * (c) 2003 Advanced Micro Devices, Inc.
+ * (c) 2003, 2004 Advanced Micro Devices, Inc.
* Your use of this code is subject to the terms and conditions of the
- * GNU general public license version 2. See "../../../COPYING" or
+ * GNU general public license version 2. See "COPYING" or
* http://www.gnu.org/licenses/gpl.html
*
* Support : paul.devriendt@amd.com
*
* Based on the powernow-k7.c module written by Dave Jones.
- * (C) 2003 Dave Jones <davej@codemonkey.ork.uk> on behalf of SuSE Labs
+ * (C) 2003 Dave Jones <davej@codemonkey.org.uk> on behalf of SuSE Labs
* (C) 2004 Dominik Brodowski <linux@brodo.de>
* (C) 2004 Pavel Machek <pavel@suse.cz>
* Licensed under the terms of the GNU GPL License version 2.
* Based upon datasheets & sample CPUs kindly provided by AMD.
*
+ * Valuable input gratefully received from Dave Jones, Pavel Machek, Dominik
+ * Brodowski, and others.
+ *
* Processor information obtained from Chapter 9 (Power and Thermal Management)
* of the "BIOS and Kernel Developer's Guide for the AMD Athlon 64 and AMD
- * Opteron Processors", revision 3.03, available for download from www.amd.com
- *
+ * Opteron Processors" available for download from www.amd.com
*/
#include <linux/kernel.h>
@@ -31,55 +33,47 @@
#include <asm/io.h>
#include <asm/delay.h>
+#ifdef CONFIG_ACPI
+#define CONFIG_X86_POWERNOW_K8_ACPI
+#endif
+#ifdef CONFIG_X86_POWERNOW_K8_ACPI
+
+#include <linux/acpi.h>
+#include <acpi/processor.h>
+
+#endif /* CONFIG_X86_POWERNOW_K8_ACPI */
+
+
#define PFX "powernow-k8: "
-#define BFX PFX "BIOS error: "
-#define VERSION "version 1.00.08a"
+#define VERSION "version 1.20.08b - March 20, 2004"
#include "powernow-k8.h"
-static u32 vstable; /* voltage stabalization time, from PSB, units 20 us */
-static u32 plllock; /* pll lock time, from PSB, units 1 us */
-static u32 numps; /* number of p-states, from PSB */
-static u32 rvo; /* ramp voltage offset, from PSB */
-static u32 irt; /* isochronous relief time, from PSB */
-static u32 vidmvs; /* usable value calculated from mvs, from PSB */
-static u32 currvid; /* keep track of the current fid / vid */
-static u32 currfid;
+/* serialize freq changes */
+static DECLARE_MUTEX(fidvid_sem);
-static struct cpufreq_frequency_table *powernow_table;
+static struct powernow_k8_data *powernow_data[NR_CPUS];
-/*
-The PSB table supplied by BIOS allows for the definition of the number of
-p-states that can be used when running on a/c, and the number of p-states
-that can be used when running on battery. This allows laptop manufacturers
-to force the system to save power when running from battery. The relationship
-is :
- 1 <= number_of_battery_p_states <= maximum_number_of_p_states
-
-This driver does NOT have the support in it to detect transitions from
-a/c power to battery power, and thus trigger the transition to a lower
-p-state if required. This is because I need ACPI and the 2.6 kernel to do
-this, and this is a 2.4 kernel driver. Check back for a new improved driver
-for the 2.6 kernel soon.
-
-This code therefore assumes it is on battery at all times, and thus
-restricts performance to number_of_battery_p_states. For desktops,
- number_of_battery_p_states == maximum_number_of_pstates,
-so this is not actually a restriction.
-*/
-static u32 batps; /* limit on the number of p states when on battery */
- /* - set by BIOS in the PSB/PST */
+/* Return a frequency in MHz, given an input fid */
+static inline u32 find_freq_from_fid(u32 fid)
+{
+ return 800 + (fid * 100);
+}
- /* Return a frequency in MHz, given an input fid */
-static u32 find_freq_from_fid(u32 fid)
+/* Return a frequency in KHz, given an input fid */
+static inline u32 find_khz_freq_from_fid(u32 fid)
{
- return 800 + (fid * 100);
+ return 1000 * find_freq_from_fid(fid);
}
+/* Return a voltage in miliVolts, given an input vid */
+static inline u32 find_milivolts_from_vid(struct powernow_k8_data *data, u32 vid)
+{
+ return 1550-vid*25;
+}
/* Return the vco fid for an input fid */
-static u32
-convert_fid_to_vco_fid(u32 fid)
+static u32 convert_fid_to_vco_fid(u32 fid)
{
if (fid < HI_FID_TABLE_BOTTOM) {
return 8 + (2 * fid);
@@ -89,11 +83,10 @@
}
/*
- * Return 1 if the pending bit is set. Unless we are actually just told the
- * processor to transition a state, seeing this bit set is really bad news.
+ * Return 1 if the pending bit is set. Unless we just instructed the processor
+ * to transition to a new state, seeing this bit set is really bad news.
*/
-static inline int
-pending_bit_stuck(void)
+static inline int pending_bit_stuck(void)
{
u32 lo, hi;
@@ -102,11 +95,10 @@
}
/*
- * Update the global current fid / vid values from the status msr. Returns 1
- * on error.
+ * Update the global current fid / vid values from the status msr. Returns
+ * 1 on error.
*/
-static int
-query_current_values_with_pending_wait(void)
+static int query_current_values_with_pending_wait(struct powernow_k8_data *data)
{
u32 lo, hi;
u32 i = 0;
@@ -120,63 +112,69 @@
rdmsr(MSR_FIDVID_STATUS, lo, hi);
}
- currvid = hi & MSR_S_HI_CURRENT_VID;
- currfid = lo & MSR_S_LO_CURRENT_FID;
+ data->currvid = hi & MSR_S_HI_CURRENT_VID;
+ data->currfid = lo & MSR_S_LO_CURRENT_FID;
return 0;
}
/* the isochronous relief time */
-static inline void
-count_off_irt(void)
+static inline void count_off_irt(struct powernow_k8_data *data)
{
- udelay((1 << irt) * 10);
+ udelay((1 << data->irt) * 10);
return;
}
-/* the voltage stabalization time */
-static inline void
-count_off_vst(void)
+/* the voltage stabilization time */
+static inline void count_off_vst(struct powernow_k8_data *data)
{
- udelay(vstable * VST_UNITS_20US);
+ udelay(data->vstable * VST_UNITS_20US);
return;
}
+/* need to init the control msr to a safe value (for each cpu) */
+static void fidvid_msr_init(void)
+{
+ u32 lo, hi;
+ u8 fid, vid;
+
+ rdmsr(MSR_FIDVID_STATUS, lo, hi);
+ vid = hi & MSR_S_HI_CURRENT_VID;
+ fid = lo & MSR_S_LO_CURRENT_FID;
+ lo = fid | (vid << MSR_C_LO_VID_SHIFT);
+ hi = MSR_C_HI_STP_GNT_BENIGN;
+ dprintk(PFX "cpu%d, init lo %x, hi %x\n", smp_processor_id(), lo, hi);
+ wrmsr(MSR_FIDVID_CTL, lo, hi);
+}
+
/* write the new fid value along with the other control fields to the msr */
-static int
-write_new_fid(u32 fid)
+static int write_new_fid(struct powernow_k8_data *data, u32 fid)
{
u32 lo;
- u32 savevid = currvid;
+ u32 savevid = data->currvid;
- if ((fid & INVALID_FID_MASK) || (currvid & INVALID_VID_MASK)) {
+ if ((fid & INVALID_FID_MASK) || (data->currvid & INVALID_VID_MASK)) {
printk(KERN_ERR PFX "internal error - overflow on fid write\n");
return 1;
}
- lo = fid | (currvid << MSR_C_LO_VID_SHIFT) | MSR_C_LO_INIT_FID_VID;
-
+ lo = fid | (data->currvid << MSR_C_LO_VID_SHIFT) | MSR_C_LO_INIT_FID_VID;
dprintk(KERN_DEBUG PFX "writing fid %x, lo %x, hi %x\n",
- fid, lo, plllock * PLL_LOCK_CONVERSION);
-
- wrmsr(MSR_FIDVID_CTL, lo, plllock * PLL_LOCK_CONVERSION);
-
- if (query_current_values_with_pending_wait())
+ fid, lo, data->plllock * PLL_LOCK_CONVERSION);
+ wrmsr(MSR_FIDVID_CTL, lo, data->plllock * PLL_LOCK_CONVERSION);
+ if (query_current_values_with_pending_wait(data))
return 1;
+ count_off_irt(data);
- count_off_irt();
-
- if (savevid != currvid) {
- printk(KERN_ERR PFX
- "vid changed on fid transition, save %x, currvid %x\n",
- savevid, currvid);
+ if (savevid != data->currvid) {
+ printk(KERN_ERR PFX "vid change on fid trans, old %x, new %x\n",
+ savevid, data->currvid);
return 1;
}
- if (fid != currfid) {
- printk(KERN_ERR PFX
- "fid transition failed, fid %x, currfid %x\n",
- fid, currfid);
+ if (fid != data->currfid) {
+ printk(KERN_ERR PFX "fid trans failed, fid %x, curr %x\n", fid,
+ data->currfid);
return 1;
}
@@ -184,40 +182,33 @@
}
/* Write a new vid to the hardware */
-static int
-write_new_vid(u32 vid)
+static int write_new_vid(struct powernow_k8_data *data, u32 vid)
{
u32 lo;
- u32 savefid = currfid;
+ u32 savefid = data->currfid;
- if ((currfid & INVALID_FID_MASK) || (vid & INVALID_VID_MASK)) {
+ if ((data->currfid & INVALID_FID_MASK) || (vid & INVALID_VID_MASK)) {
printk(KERN_ERR PFX "internal error - overflow on vid write\n");
return 1;
}
- lo = currfid | (vid << MSR_C_LO_VID_SHIFT) | MSR_C_LO_INIT_FID_VID;
-
+ lo = data->currfid | (vid << MSR_C_LO_VID_SHIFT) | MSR_C_LO_INIT_FID_VID;
dprintk(KERN_DEBUG PFX "writing vid %x, lo %x, hi %x\n",
vid, lo, STOP_GRANT_5NS);
-
wrmsr(MSR_FIDVID_CTL, lo, STOP_GRANT_5NS);
-
- if (query_current_values_with_pending_wait()) {
+ if (query_current_values_with_pending_wait(data))
return 1;
- }
- if (savefid != currfid) {
- printk(KERN_ERR PFX
- "fid changed on vid transition, save %x currfid %x\n",
- savefid, currfid);
+ if (savefid != data->currfid) {
+ printk(KERN_ERR PFX "fid changed on vid trans, old %x new %x\n",
+ savefid, data->currfid);
return 1;
}
- if (vid != currvid) {
- printk(KERN_ERR PFX
- "vid transition failed, vid %x, currvid %x\n",
- vid, currvid);
- return 1;
+ if (vid != data->currvid) {
+ printk(KERN_ERR PFX "vid trans failed, vid %x, curr %x\n", vid,
+ data->currvid);
+ return 1;
}
return 0;
@@ -228,300 +219,279 @@
* Decreasing vid codes represent increasing voltages:
* vid of 0 is 1.550V, vid of 0x1e is 0.800V, vid of 0x1f is off.
*/
-static int
-decrease_vid_code_by_step(u32 reqvid, u32 step)
+static int decrease_vid_code_by_step(struct powernow_k8_data *data, u32 reqvid, u32 step)
{
- if ((currvid - reqvid) > step)
- reqvid = currvid - step;
-
- if (write_new_vid(reqvid))
+ if ((data->currvid - reqvid) > step)
+ reqvid = data->currvid - step;
+ if (write_new_vid(data, reqvid))
return 1;
-
- count_off_vst();
-
+ count_off_vst(data);
return 0;
}
/* Change the fid and vid, by the 3 phases. */
-static inline int
-transition_fid_vid(u32 reqfid, u32 reqvid)
+static inline int transition_fid_vid(struct powernow_k8_data *data, u32 reqfid, u32 reqvid)
{
- if (core_voltage_pre_transition(reqvid))
+ if (core_voltage_pre_transition(data, reqvid))
return 1;
-
- if (core_frequency_transition(reqfid))
+ if (core_frequency_transition(data, reqfid))
return 1;
-
- if (core_voltage_post_transition(reqvid))
+ if (core_voltage_post_transition(data, reqvid))
return 1;
-
- if (query_current_values_with_pending_wait())
+ if (query_current_values_with_pending_wait(data))
return 1;
- if ((reqfid != currfid) || (reqvid != currvid)) {
- printk(KERN_ERR PFX "failed: req 0x%x 0x%x, curr 0x%x 0x%x\n",
- reqfid, reqvid, currfid, currvid);
+ if ((reqfid != data->currfid) || (reqvid != data->currvid)) {
+ printk(KERN_ERR PFX "failed (cpu%d): req %x %x, curr %x %x\n",
+ smp_processor_id(),
+ reqfid, reqvid, data->currfid, data->currvid);
return 1;
}
- dprintk(KERN_INFO PFX
- "transitioned: new fid 0x%x, vid 0x%x\n", currfid, currvid);
-
+ dprintk(KERN_INFO PFX "transitioned (cpu%d): new fid %x, vid %x\n",
+ smp_processor_id(), data->currfid, data->currvid);
return 0;
}
-/*
- * Phase 1 - core voltage transition ... setup appropriate voltage for the
- * fid transition.
- */
-static inline int
-core_voltage_pre_transition(u32 reqvid)
+/* Phase 1 - core voltage transition ... setup voltage */
+static inline int core_voltage_pre_transition(struct powernow_k8_data *data, u32 reqvid)
{
- u32 rvosteps = rvo;
- u32 savefid = currfid;
+ u32 rvosteps = data->rvo;
+ u32 savefid = data->currfid;
dprintk(KERN_DEBUG PFX
- "ph1: start, currfid 0x%x, currvid 0x%x, reqvid 0x%x, rvo %x\n",
- currfid, currvid, reqvid, rvo);
-
- while (currvid > reqvid) {
- dprintk(KERN_DEBUG PFX "ph1: curr 0x%x, requesting vid 0x%x\n",
- currvid, reqvid);
- if (decrease_vid_code_by_step(reqvid, vidmvs))
+ "ph1 (cpu%d): start, currfid %x, currvid %x, reqvid %x, rvo %x\n",
+ smp_processor_id(),
+ data->currfid, data->currvid, reqvid, data->rvo);
+
+ while (data->currvid > reqvid) {
+ dprintk(KERN_DEBUG PFX "ph1: curr %x, req vid %x\n",
+ data->currvid, reqvid);
+ if (decrease_vid_code_by_step(data, reqvid, data->vidmvs))
return 1;
}
while (rvosteps > 0) {
- if (currvid == 0) {
+ if (data->currvid == 0) {
rvosteps = 0;
} else {
dprintk(KERN_DEBUG PFX
- "ph1: changing vid for rvo, requesting 0x%x\n",
- currvid - 1);
- if (decrease_vid_code_by_step(currvid - 1, 1))
+ "ph1: changing vid for rvo, req %x\n",
+ data->currvid - 1);
+ if (decrease_vid_code_by_step(data, data->currvid - 1, 1))
return 1;
rvosteps--;
}
}
- if (query_current_values_with_pending_wait())
+ if (query_current_values_with_pending_wait(data))
return 1;
- if (savefid != currfid) {
- printk(KERN_ERR PFX "ph1 err, currfid changed 0x%x\n", currfid);
+ if (savefid != data->currfid) {
+ printk(KERN_ERR PFX "ph1: err, currfid changed %x\n", data->currfid);
return 1;
}
- dprintk(KERN_DEBUG PFX "ph1 complete, currfid 0x%x, currvid 0x%x\n",
- currfid, currvid);
+ dprintk(KERN_DEBUG PFX "ph1: complete, currfid %x, currvid %x\n",
+ data->currfid, data->currvid);
return 0;
}
/* Phase 2 - core frequency transition */
-static inline int
-core_frequency_transition(u32 reqfid)
+static inline int core_frequency_transition(struct powernow_k8_data *data, u32 reqfid)
{
u32 vcoreqfid;
u32 vcocurrfid;
u32 vcofiddiff;
- u32 savevid = currvid;
+ u32 savevid = data->currvid;
- if ((reqfid < HI_FID_TABLE_BOTTOM) && (currfid < HI_FID_TABLE_BOTTOM)) {
- printk(KERN_ERR PFX "ph2 illegal lo-lo transition 0x%x 0x%x\n",
- reqfid, currfid);
+ if ((reqfid < HI_FID_TABLE_BOTTOM) && (data->currfid < HI_FID_TABLE_BOTTOM)) {
+ printk(KERN_ERR PFX "ph2: illegal lo-lo transition %x %x\n",
+ reqfid, data->currfid);
return 1;
}
- if (currfid == reqfid) {
- printk(KERN_ERR PFX "ph2 null fid transition 0x%x\n", currfid);
+ if (data->currfid == reqfid) {
+ printk(KERN_ERR PFX "ph2: null fid transition %x\n", data->currfid);
return 0;
}
dprintk(KERN_DEBUG PFX
- "ph2 starting, currfid 0x%x, currvid 0x%x, reqfid 0x%x\n",
- currfid, currvid, reqfid);
+ "ph2 (cpu%d): starting, currfid %x, currvid %x, reqfid %x\n",
+ smp_processor_id(),
+ data->currfid, data->currvid, reqfid);
vcoreqfid = convert_fid_to_vco_fid(reqfid);
- vcocurrfid = convert_fid_to_vco_fid(currfid);
+ vcocurrfid = convert_fid_to_vco_fid(data->currfid);
vcofiddiff = vcocurrfid > vcoreqfid ? vcocurrfid - vcoreqfid
: vcoreqfid - vcocurrfid;
while (vcofiddiff > 2) {
- if (reqfid > currfid) {
- if (currfid > LO_FID_TABLE_TOP) {
- if (write_new_fid(currfid + 2)) {
+ if (reqfid > data->currfid) {
+ if (data->currfid > LO_FID_TABLE_TOP) {
+ if (write_new_fid(data, data->currfid + 2)) {
return 1;
}
} else {
if (write_new_fid
- (2 + convert_fid_to_vco_fid(currfid))) {
+ (data, 2 + convert_fid_to_vco_fid(data->currfid))) {
return 1;
}
}
} else {
- if (write_new_fid(currfid - 2))
+ if (write_new_fid(data, data->currfid - 2))
return 1;
}
- vcocurrfid = convert_fid_to_vco_fid(currfid);
+ vcocurrfid = convert_fid_to_vco_fid(data->currfid);
vcofiddiff = vcocurrfid > vcoreqfid ? vcocurrfid - vcoreqfid
: vcoreqfid - vcocurrfid;
}
- if (write_new_fid(reqfid))
+ if (write_new_fid(data, reqfid))
return 1;
-
- if (query_current_values_with_pending_wait())
+ if (query_current_values_with_pending_wait(data))
return 1;
- if (currfid != reqfid) {
+ if (data->currfid != reqfid) {
printk(KERN_ERR PFX
- "ph2 mismatch, failed fid transition, curr %x, req %x\n",
- currfid, reqfid);
+ "ph2: mismatch, failed fid transition, curr %x, req %x\n",
+ data->currfid, reqfid);
return 1;
}
- if (savevid != currvid) {
- printk(KERN_ERR PFX
- "ph2 vid changed, save %x, curr %x\n", savevid,
- currvid);
+ if (savevid != data->currvid) {
+ printk(KERN_ERR PFX "ph2: vid changed, save %x, curr %x\n",
+ savevid, data->currvid);
return 1;
}
- dprintk(KERN_DEBUG PFX "ph2 complete, currfid 0x%x, currvid 0x%x\n",
- currfid, currvid);
+ dprintk(KERN_DEBUG PFX "ph2: complete, currfid %x, currvid %x\n",
+ data->currfid, data->currvid);
return 0;
}
/* Phase 3 - core voltage transition flow ... jump to the final vid. */
-static inline int
-core_voltage_post_transition(u32 reqvid)
+static inline int core_voltage_post_transition(struct powernow_k8_data *data, u32 reqvid)
{
- u32 savefid = currfid;
+ u32 savefid = data->currfid;
u32 savereqvid = reqvid;
- dprintk(KERN_DEBUG PFX "ph3 starting, currfid 0x%x, currvid 0x%x\n",
- currfid, currvid);
+ dprintk(KERN_DEBUG PFX "ph3 (cpu%d): starting, currfid %x, currvid %x\n",
+ smp_processor_id(),
+ data->currfid, data->currvid);
- if (reqvid != currvid) {
- if (write_new_vid(reqvid))
+ if (reqvid != data->currvid) {
+ if (write_new_vid(data, reqvid))
return 1;
- if (savefid != currfid) {
+ if (savefid != data->currfid) {
printk(KERN_ERR PFX
"ph3: bad fid change, save %x, curr %x\n",
- savefid, currfid);
+ savefid, data->currfid);
return 1;
}
- if (currvid != reqvid) {
+ if (data->currvid != reqvid) {
printk(KERN_ERR PFX
"ph3: failed vid transition\n, req %x, curr %x",
- reqvid, currvid);
+ reqvid, data->currvid);
return 1;
}
}
- if (query_current_values_with_pending_wait())
+ if (query_current_values_with_pending_wait(data))
return 1;
- if (savereqvid != currvid) {
- dprintk(KERN_ERR PFX "ph3 failed, currvid 0x%x\n", currvid);
+ if (savereqvid != data->currvid) {
+ printk(KERN_ERR PFX "ph3: failed, currvid %x\n", data->currvid);
return 1;
}
- if (savefid != currfid) {
- dprintk(KERN_ERR PFX "ph3 failed, currfid changed 0x%x\n",
- currfid);
+ if (savefid != data->currfid) {
+ printk(KERN_ERR PFX "ph3: failed, currfid changed %x\n",
+ data->currfid);
return 1;
}
- dprintk(KERN_DEBUG PFX "ph3 complete, currfid 0x%x, currvid 0x%x\n",
- currfid, currvid);
-
+ dprintk(KERN_DEBUG PFX "ph3: complete, currfid %x, currvid %x\n",
+ data->currfid, data->currvid);
return 0;
}
-static inline int
-check_supported_cpu(void)
+static inline int check_supported_cpu(unsigned int cpu)
{
- struct cpuinfo_x86 *c = cpu_data;
+ cpumask_t oldmask = CPU_MASK_ALL;
u32 eax, ebx, ecx, edx;
+ unsigned int rc = 0;
- if (num_online_cpus() != 1) {
- printk(KERN_INFO PFX "multiprocessor systems not supported\n");
- return 0;
- }
+ oldmask = current->cpus_allowed;
+ set_cpus_allowed(current, cpumask_of_cpu(cpu));
+ schedule();
- if (c->x86_vendor != X86_VENDOR_AMD) {
-#ifdef MODULE
- printk(KERN_INFO PFX "Not an AMD processor\n");
-#endif
- return 0;
+ if (smp_processor_id() != cpu) {
+ printk(KERN_ERR "limiting to cpu %u failed\n", cpu);
+ goto out;
}
+ if (current_cpu_data.x86_vendor != X86_VENDOR_AMD)
+ goto out;
+
eax = cpuid_eax(CPUID_PROCESSOR_SIGNATURE);
if ((eax & CPUID_XFAM_MOD) == ATHLON64_XFAM_MOD) {
dprintk(KERN_DEBUG PFX "AMD Althon 64 Processor found\n");
- if ((eax & CPUID_F1_STEP) < ATHLON64_REV_C0) {
- printk(KERN_INFO PFX "Revision C0 or better "
- "AMD Athlon 64 processor required\n");
- return 0;
- }
} else if ((eax & CPUID_XFAM_MOD) == OPTERON_XFAM_MOD) {
dprintk(KERN_DEBUG PFX "AMD Opteron Processor found\n");
} else {
printk(KERN_INFO PFX
"AMD Athlon 64 or AMD Opteron processor required\n");
- return 0;
+ goto out;
}
eax = cpuid_eax(CPUID_GET_MAX_CAPABILITIES);
if (eax < CPUID_FREQ_VOLT_CAPABILITIES) {
- printk(KERN_INFO PFX
- "No frequency change capabilities detected\n");
- return 0;
+ printk(KERN_INFO PFX "No freq change capabilities\n");
+ goto out;
}
cpuid(CPUID_FREQ_VOLT_CAPABILITIES, &eax, &ebx, &ecx, &edx);
if ((edx & P_STATE_TRANSITION_CAPABLE) != P_STATE_TRANSITION_CAPABLE) {
printk(KERN_INFO PFX "Power state transitions not supported\n");
- return 0;
+ goto out;
}
- printk(KERN_INFO PFX "Found AMD64 processor supporting PowerNow (" VERSION ")\n");
- return 1;
+ rc = 1;
+
+ out:
+ set_cpus_allowed(current, oldmask);
+ schedule();
+ return rc;
}
-static int check_pst_table(struct pst_s *pst, u8 maxvid)
+static int check_pst_table(struct powernow_k8_data *data, struct pst_s *pst, u8 maxvid)
{
unsigned int j;
- u8 lastfid = 0xFF;
+ u8 lastfid = 0xff;
- for (j = 0; j < numps; j++) {
+ for (j = 0; j < data->numps; j++) {
if (pst[j].vid > LEAST_VID) {
- printk(KERN_ERR PFX "vid %d invalid : 0x%x\n", j, pst[j].vid);
+ printk(KERN_ERR PFX "vid %d bad: %x\n", j, pst[j].vid);
return -EINVAL;
}
- if (pst[j].vid < rvo) { /* vid + rvo >= 0 */
- printk(KERN_ERR PFX
- "BIOS error - 0 vid exceeded with pstate %d\n",
- j);
+ if (pst[j].vid < data->rvo) { /* vid + rvo >= 0 */
+ printk(KERN_ERR PFX "0 vid exceeded with pst %d\n", j);
return -ENODEV;
}
- if (pst[j].vid < maxvid + rvo) { /* vid + rvo >= maxvid */
- printk(KERN_ERR PFX
- "BIOS error - maxvid exceeded with pstate %d\n",
- j);
+ if (pst[j].vid < maxvid + data->rvo) { /* vid + rvo >= maxvid */
+ printk(KERN_ERR PFX "maxvid exceeded with pst %d\n", j);
return -ENODEV;
}
if ((pst[j].fid > MAX_FID)
|| (pst[j].fid & 1)
|| (j && (pst[j].fid < HI_FID_TABLE_BOTTOM))) {
- /* Only first fid is allowed to be in "low" range */
- printk(KERN_ERR PFX "fid %d invalid : 0x%x\n", j, pst[j].fid);
+ printk(KERN_ERR PFX "fid %d bad: %x\n", j, pst[j].fid);
return -EINVAL;
}
if (pst[j].fid < lastfid)
@@ -531,20 +501,87 @@
printk(KERN_ERR PFX "lastfid invalid\n");
return -EINVAL;
}
- if (lastfid > LO_FID_TABLE_TOP) {
- printk(KERN_INFO PFX "first fid not from lo freq table\n");
+ if (lastfid > LO_FID_TABLE_TOP)
+ printk(KERN_INFO PFX "first fid not from lo freq table\n");
+
+ return 0;
+}
+
+static void print_basics(struct powernow_k8_data *data)
+{
+ int j;
+ for (j = 0; j < data->numps; j++) {
+ printk(KERN_INFO PFX " %d : fid %x (%d MHz), vid %x (%d mV)\n", j,
+ data->powernow_table[j].index & 0xff,
+ data->powernow_table[j].frequency/1000,
+ data->powernow_table[j].index >> 8,
+ find_milivolts_from_vid(data, data->powernow_table[j].index >> 8));
+ }
+ if (data->batps)
+ printk(KERN_INFO PFX "Only %d pstates on battery\n", data->batps);
+}
+
+static inline int fill_powernow_table(struct powernow_k8_data *data, struct pst_s *pst, u8 maxvid)
+{
+ struct cpufreq_frequency_table *powernow_table;
+ unsigned int j;
+
+ if (data->batps) { /* use ACPI support to get full speed on mains power */
+ printk(KERN_WARNING PFX "Only %d pstates usable (use ACPI driver for full range\n", data->batps);
+ data->numps = data->batps;
}
+ for ( j=1; j<data->numps; j++ )
+ if (pst[j-1].fid >= pst[j].fid) {
+ printk(KERN_ERR PFX "PST out of sequence\n");
+ return -EINVAL;
+ }
+
+ if (data->numps < 2) {
+ printk(KERN_ERR PFX "no p states to transition\n");
+ return -ENODEV;
+ }
+
+ if (check_pst_table(data, pst, maxvid))
+ return -EINVAL;
+
+ powernow_table = kmalloc((sizeof(struct cpufreq_frequency_table)
+ * (data->numps + 1)), GFP_KERNEL);
+ if (!powernow_table) {
+ printk(KERN_ERR PFX "powernow_table memory alloc failure\n");
+ return -ENOMEM;
+ }
+
+ for (j = 0; j < data->numps; j++) {
+ powernow_table[j].index = pst[j].fid; /* lower 8 bits */
+ powernow_table[j].index |= (pst[j].vid << 8); /* upper 8 bits */
+ powernow_table[j].frequency = find_khz_freq_from_fid(pst[j].fid);
+ }
+ powernow_table[data->numps].frequency = CPUFREQ_TABLE_END;
+ powernow_table[data->numps].index = 0;
+
+ if (query_current_values_with_pending_wait(data)) {
+ kfree(powernow_table);
+ return -EIO;
+ }
+
+ dprintk(KERN_INFO PFX "cfid %x, cvid %x\n", data->currfid, data->currvid);
+ data->powernow_table = powernow_table;
+ print_basics(data);
+
+ for (j = 0; j < data->numps; j++)
+ if ((pst[j].fid==data->currfid) && (pst[j].vid==data->currvid))
+ return 0;
+
+ dprintk(KERN_ERR PFX "currfid/vid do not match PST, ignoring\n");
return 0;
}
/* Find and validate the PSB/PST table in BIOS. */
-static inline int
-find_psb_table(void)
+static inline int find_psb_table(struct powernow_k8_data *data)
{
struct psb_s *psb;
- struct pst_s *pst;
- unsigned int i, j;
+ unsigned int i;
u32 mvs;
u8 maxvid;
@@ -556,276 +593,427 @@
if (memcmp(psb, PSB_ID_STRING, PSB_ID_STRING_LEN) != 0)
continue;
- dprintk(KERN_DEBUG PFX "found PSB header at 0x%p\n", psb);
-
- dprintk(KERN_DEBUG PFX "table vers: 0x%x\n", psb->tableversion);
+ dprintk(KERN_DEBUG PFX "found PSB header at %p\n", psb);
+ dprintk(KERN_DEBUG PFX "table version: %x\n",
+ psb->tableversion);
if (psb->tableversion != PSB_VERSION_1_4) {
- printk(KERN_INFO BFX "PSB table is not v1.4\n");
+ printk(KERN_INFO PFX "PSB table is not v1.4\n");
return -ENODEV;
}
- dprintk(KERN_DEBUG PFX "flags: 0x%x\n", psb->flags1);
+ dprintk(KERN_DEBUG PFX "flags: %x\n", psb->flags1);
if (psb->flags1) {
- printk(KERN_ERR BFX "unknown flags\n");
+ printk(KERN_ERR PFX "unknown flags\n");
return -ENODEV;
}
- vstable = psb->voltagestabilizationtime;
- dprintk(KERN_DEBUG PFX "flags2: 0x%x\n", psb->flags2);
- rvo = psb->flags2 & 3;
- irt = ((psb->flags2) >> 2) & 3;
+ data->vstable = psb->voltagestabilizationtime;
+ dprintk(KERN_INFO PFX "voltage stabilization time: %d(*20us)\n", data->vstable);
+
+ dprintk(KERN_DEBUG PFX "flags2: %x\n", psb->flags2);
+
+ data->rvo = psb->flags2 & 3;
+ data->irt = ((psb->flags2) >> 2) & 3;
mvs = ((psb->flags2) >> 4) & 3;
- vidmvs = 1 << mvs;
- batps = ((psb->flags2) >> 6) & 3;
+ data->vidmvs = 1 << mvs;
+ data->batps = ((psb->flags2) >> 6) & 3;
- printk(KERN_INFO PFX "voltage stable in %d usec", vstable * 20);
- if (batps)
- printk(", only %d lowest states on battery", batps);
- printk(", ramp voltage offset: %d", rvo);
- printk(", isochronous relief time: %d", irt);
- printk(", maximum voltage step: %d\n", mvs);
+ dprintk(KERN_INFO PFX "ramp voltage offset: %d\n", data->rvo);
+ dprintk(KERN_INFO PFX "isochronous relief time: %d\n", data->irt);
+ dprintk(KERN_INFO PFX "maximum voltage step: %d - %x\n",
+ mvs, data->vidmvs);
dprintk(KERN_DEBUG PFX "numpst: 0x%x\n", psb->numpst);
if (psb->numpst != 1) {
- printk(KERN_ERR BFX "numpst must be 1\n");
+ printk(KERN_ERR PFX "numpst must be 1\n");
return -ENODEV;
}
- dprintk(KERN_DEBUG PFX "cpuid: 0x%x\n", psb->cpuid);
-
- plllock = psb->plllocktime;
- printk(KERN_INFO PFX "pll lock time: 0x%x, ", plllock);
-
+ data->plllock = psb->plllocktime;
+ dprintk(KERN_INFO PFX "plllocktime: %x (units 1us)\n",
+ psb->plllocktime);
+ dprintk(KERN_INFO PFX "maxfid: %x\n", psb->maxfid);
+ dprintk(KERN_INFO PFX "maxvid: %x\n", psb->maxvid);
maxvid = psb->maxvid;
- printk("maxfid 0x%x (%d MHz), maxvid 0x%x\n",
- psb->maxfid, find_freq_from_fid(psb->maxfid), maxvid);
- numps = psb->numpstates;
- if (numps < 2) {
- printk(KERN_ERR BFX "no p states to transition\n");
- return -ENODEV;
- }
+ data->numps = psb->numpstates;
+ dprintk(KERN_INFO PFX "numpstates: %x\n", data->numps);
+ return fill_powernow_table(data, (struct pst_s *)(psb+1), maxvid);
+ }
- if (batps == 0) {
- batps = numps;
- } else if (batps > numps) {
- printk(KERN_ERR BFX "batterypstates > numpstates\n");
- batps = numps;
- } else {
- printk(KERN_ERR PFX
- "Restricting operation to %d p-states\n", batps);
- printk(KERN_ERR PFX
- "Check for an updated driver to access all "
- "%d p-states\n", numps);
- }
+ /*
+ * If you see this message, complain to BIOS manufacturer. If
+ * he tells you "we do not support Linux" or some similar
+ * nonsense, remember that Windows 2000 uses the same legacy
+ * mechanism that the old Linux PSB driver uses. Tell them it
+ * is broken with Windows 2000.
+ *
+ * The reference to the AMD documentation is chapter 9 in the
+ * BIOS and Kernel Developer's Guide, which is available on
+ * www.amd.com
+ */
+ printk(KERN_ERR PFX "BIOS error - no PSB\n");
+ return -ENODEV;
+}
- if (numps <= 1) {
- printk(KERN_ERR PFX "only 1 p-state to transition\n");
- return -ENODEV;
- }
- pst = (struct pst_s *) (psb + 1);
- if (check_pst_table(pst, maxvid))
- return -EINVAL;
+#ifdef CONFIG_X86_POWERNOW_K8_ACPI
- powernow_table = kmalloc((sizeof(struct cpufreq_frequency_table) * (numps + 1)), GFP_KERNEL);
- if (!powernow_table) {
- printk(KERN_ERR PFX "powernow_table memory alloc failure\n");
- return -ENOMEM;
- }
+static inline void powernow_k8_acpi_pst_values(struct powernow_k8_data *data, unsigned int index)
+{
+ if (!data->acpi_data.state_count)
+ return;
- for (j = 0; j < psb->numpstates; j++) {
- powernow_table[j].index = pst[j].fid; /* lower 8 bits */
- powernow_table[j].index |= (pst[j].vid << 8); /* upper 8 bits */
- }
+ data->irt = (data->acpi_data.states[index].control >> IRT_SHIFT) & IRT_MASK;
+ data->rvo = (data->acpi_data.states[index].control >> RVO_SHIFT) & RVO_MASK;
+ data->plllock = (data->acpi_data.states[index].control >> PLL_L_SHIFT) & PLL_L_MASK;
+ data->vidmvs = 1 << ((data->acpi_data.states[index].control >> MVS_SHIFT) & MVS_MASK);
+ data->vstable = (data->acpi_data.states[index].control >> VST_SHIFT) & VST_MASK;
+}
- /* If you want to override your frequency tables, this
- is right place. */
+static int powernow_k8_cpu_init_acpi(struct powernow_k8_data *data)
+{
+ int i;
+ int cntlofreq = 0;
+ struct cpufreq_frequency_table *powernow_table;
- for (j = 0; j < numps; j++) {
- powernow_table[j].frequency = find_freq_from_fid(powernow_table[j].index & 0xff)*1000;
- printk(KERN_INFO PFX " %d : fid 0x%x (%d MHz), vid 0x%x\n", j,
- powernow_table[j].index & 0xff,
- powernow_table[j].frequency/1000,
- powernow_table[j].index >> 8);
+ if (acpi_processor_register_performance(&data->acpi_data, data->cpu)) {
+ dprintk(KERN_DEBUG PFX "register performance failed\n");
+ return -EIO;
+ }
+
+ /* verify the data contained in the ACPI structures */
+ if (data->acpi_data.state_count <= 1) {
+ dprintk(KERN_DEBUG PFX "No ACPI P-States\n");
+ goto err_out;
+ }
+
+ if ((data->acpi_data.control_register.space_id != ACPI_ADR_SPACE_FIXED_HARDWARE) ||
+ (data->acpi_data.status_register.space_id != ACPI_ADR_SPACE_FIXED_HARDWARE)) {
+ dprintk(KERN_DEBUG PFX "Invalid control/status registers\n");
+ goto err_out;
+ }
+
+ /* fill in data->powernow_table */
+ powernow_table = kmalloc((sizeof(struct cpufreq_frequency_table)
+ * (data->acpi_data.state_count + 1)), GFP_KERNEL);
+ if (!powernow_table) {
+ dprintk(KERN_ERR PFX "powernow_table memory alloc failure\n");
+ goto err_out;
+ }
+
+ for (i = 0; i < data->acpi_data.state_count; i++) {
+ u32 fid = data->acpi_data.states[i].control & FID_MASK;
+ u32 vid = (data->acpi_data.states[i].control >> VID_SHIFT) & VID_MASK;
+
+ dprintk(KERN_INFO PFX " %d : fid %x, vid %x\n", i, fid, vid);
+
+ powernow_table[i].index = fid; /* lower 8 bits */
+ powernow_table[i].index |= (vid << 8); /* upper 8 bits */
+ powernow_table[i].frequency = find_khz_freq_from_fid(fid);
+
+ /* verify frequency is OK */
+ if ((powernow_table[i].frequency > (MAX_FREQ * 1000)) ||
+ (powernow_table[i].frequency < (MIN_FREQ * 1000))) {
+ dprintk(KERN_INFO PFX "invalid freq %u kHz\n", powernow_table[i].frequency);
+ powernow_table[i].frequency = CPUFREQ_ENTRY_INVALID;
+ continue;
}
- powernow_table[numps].frequency = CPUFREQ_TABLE_END;
- powernow_table[numps].index = 0;
+ /* verify only 1 entry from the lo frequency table */
+ if ((fid < HI_FID_TABLE_BOTTOM) && (cntlofreq++)) {
+ printk(KERN_ERR PFX "Too many lo freq table entries\n");
+ goto err_out;
+ }
- if (query_current_values_with_pending_wait()) {
- kfree(powernow_table);
- return -EIO;
+ if (powernow_table[i].frequency != (data->acpi_data.states[i].core_frequency * 1000)) {
+ printk(KERN_INFO PFX "invalid freq entries %u kHz vs. %u kHz\n",
+ powernow_table[i].frequency,
+ (unsigned int) (data->acpi_data.states[i].core_frequency * 1000));
+ powernow_table[i].frequency = CPUFREQ_ENTRY_INVALID;
+ continue;
}
+ }
+ powernow_table[data->acpi_data.state_count].frequency = CPUFREQ_TABLE_END;
+ powernow_table[data->acpi_data.state_count].index = 0;
+ data->powernow_table = powernow_table;
+
+ /* fill in data */
+ data->numps = data->acpi_data.state_count;
+ print_basics(data);
- printk(KERN_INFO PFX "currfid 0x%x (%d MHz), currvid 0x%x\n",
- currfid, find_freq_from_fid(currfid), currvid);
+ powernow_k8_acpi_pst_values(data, 0);
- for (j = 0; j < numps; j++)
- if ((pst[j].fid==currfid) && (pst[j].vid==currvid))
- return 0;
+ return 0;
- printk(KERN_ERR BFX "currfid/vid do not match PST, ignoring\n");
- return 0;
- }
+ err_out:
+ acpi_processor_unregister_performance(&data->acpi_data, data->cpu);
+
+ /* data->acpi_data.state_count informs us at ->exit() whether ACPI was used */
+ data->acpi_data.state_count = 0;
- printk(KERN_ERR BFX "no PSB\n");
return -ENODEV;
}
+static void powernow_k8_cpu_exit_acpi(struct powernow_k8_data *data)
+{
+ if (data->acpi_data.state_count)
+ acpi_processor_unregister_performance(&data->acpi_data, data->cpu);
+}
+
+#else
+static inline int powernow_k8_cpu_init_acpi(struct powernow_k8_data *data) { return -ENODEV; }
+static inline void powernow_k8_cpu_exit_acpi(struct powernow_k8_data *data) { return; }
+static inline void powernow_k8_acpi_pst_values(struct powernow_k8_data *data, unsigned int index) { return; }
+#endif /* CONFIG_X86_POWERNOW_K8_ACPI */
+
+
/* Take a frequency, and issue the fid/vid transition command */
-static inline int
-transition_frequency(unsigned int index)
+static inline int transition_frequency(struct powernow_k8_data *data, unsigned int index)
{
u32 fid;
u32 vid;
int res;
struct cpufreq_freqs freqs;
+ dprintk(KERN_DEBUG PFX "cpu %d transition to index %u\n",
+ smp_processor_id(), index );
+
/* fid are the lower 8 bits of the index we stored into
* the cpufreq frequency table in find_psb_table, vid are
* the upper 8 bits.
*/
- fid = powernow_table[index].index & 0xFF;
- vid = (powernow_table[index].index & 0xFF00) >> 8;
+ fid = data->powernow_table[index].index & 0xFF;
+ vid = (data->powernow_table[index].index & 0xFF00) >> 8;
- dprintk(KERN_DEBUG PFX "table matched fid 0x%x, giving vid 0x%x\n",
- fid, vid);
+ dprintk(KERN_DEBUG PFX "matched fid %x, giving vid %x\n", fid, vid);
- if (query_current_values_with_pending_wait())
+ if (query_current_values_with_pending_wait(data))
return 1;
- if ((currvid == vid) && (currfid == fid)) {
- dprintk(KERN_DEBUG PFX
- "target matches current values (fid 0x%x, vid 0x%x)\n",
+ if ((data->currvid == vid) && (data->currfid == fid)) {
+ dprintk(KERN_DEBUG PFX "target matches curr (fid %x, vid %x)\n",
fid, vid);
return 0;
}
- if ((fid < HI_FID_TABLE_BOTTOM) && (currfid < HI_FID_TABLE_BOTTOM)) {
+ if ((fid < HI_FID_TABLE_BOTTOM) && (data->currfid < HI_FID_TABLE_BOTTOM)) {
printk(KERN_ERR PFX
"ignoring illegal change in lo freq table-%x to %x\n",
- currfid, fid);
+ data->currfid, fid);
return 1;
}
- dprintk(KERN_DEBUG PFX "changing to fid 0x%x, vid 0x%x\n", fid, vid);
-
- freqs.cpu = 0; /* only true because SMP not supported */
+ dprintk(KERN_DEBUG PFX "cpu %d, changing to fid %x, vid %x\n",
+ smp_processor_id(), fid, vid);
+ freqs.cpu = data->cpu;
- freqs.old = find_freq_from_fid(currfid);
- freqs.new = find_freq_from_fid(fid);
+ freqs.old = find_khz_freq_from_fid(data->currfid);
+ freqs.new = find_khz_freq_from_fid(fid);
cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
- res = transition_fid_vid(fid, vid);
+ down(&fidvid_sem);
+ res = transition_fid_vid(data, fid, vid);
+ up(&fidvid_sem);
- freqs.new = find_freq_from_fid(currfid);
+ freqs.new = find_khz_freq_from_fid(data->currfid);
cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
return res;
}
/* Driver entry point to switch to the target frequency */
-static int
-powernowk8_target(struct cpufreq_policy *pol, unsigned targfreq, unsigned relation)
+static int powernowk8_target(struct cpufreq_policy *pol, unsigned targfreq, unsigned relation)
{
- u32 checkfid = currfid;
- u32 checkvid = currvid;
+ cpumask_t oldmask = CPU_MASK_ALL;
+ struct powernow_k8_data *data = powernow_data[pol->cpu];
+ u32 checkfid = data->currfid;
+ u32 checkvid = data->currvid;
unsigned int newstate;
+ int ret = -EIO;
+
+ /* only run on specific CPU from here on */
+ oldmask = current->cpus_allowed;
+ set_cpus_allowed(current, cpumask_of_cpu(pol->cpu));
+ schedule();
+
+ if (smp_processor_id() != pol->cpu) {
+ printk(KERN_ERR "limiting to cpu %u failed\n", pol->cpu);
+ goto err_out;
+ }
+
+ /* from this point, do not exit without restoring preempt and cpu */
+ preempt_disable();
if (pending_bit_stuck()) {
- printk(KERN_ERR PFX "drv targ fail: change pending bit set\n");
- return -EIO;
+ printk(KERN_ERR PFX "failing targ, change pending bit set\n");
+ goto err_out;
}
- dprintk(KERN_DEBUG PFX "targ: %d kHz, min %d, max %d, relation %d\n",
- targfreq, pol->min, pol->max, relation);
+ dprintk(KERN_DEBUG PFX "targ: cpu %d, %d kHz, min %d, max %d, relation %d\n",
+ pol->cpu, targfreq, pol->min, pol->max, relation);
- if (query_current_values_with_pending_wait())
- return -EIO;
+ if (query_current_values_with_pending_wait(data)) {
+ ret = -EIO;
+ goto err_out;
+ }
- dprintk(KERN_DEBUG PFX "targ: curr fid 0x%x, vid 0x%x\n",
- currfid, currvid);
+ dprintk(KERN_DEBUG PFX "targ: curr fid %x, vid %x\n",
+ data->currfid, data->currvid);
- if ((checkvid != currvid) || (checkfid != currfid)) {
- printk(KERN_ERR PFX
- "error - out of sync, fid 0x%x 0x%x, vid 0x%x 0x%x\n",
- checkfid, currfid, checkvid, currvid);
+ if ((checkvid != data->currvid) || (checkfid != data->currfid)) {
+ printk(KERN_ERR PFX "out of sync, fid %x %x, vid %x %x\n",
+ checkfid, data->currfid, checkvid, data->currvid);
}
- if (cpufreq_frequency_table_target(pol, powernow_table, targfreq, relation, &newstate))
- return -EINVAL;
-
- if (transition_frequency(newstate))
+ if (cpufreq_frequency_table_target(pol, data->powernow_table, targfreq, relation, &newstate))
+ goto err_out;
+
+ powernow_k8_acpi_pst_values(data, newstate);
+
+ if (transition_frequency(data, newstate))
{
printk(KERN_ERR PFX "transition frequency failed\n");
- return 1;
+ ret = 1;
+ goto err_out;
}
- pol->cur = 1000 * find_freq_from_fid(currfid);
+ pol->cur = find_khz_freq_from_fid(data->currfid);
+ ret = 0;
- return 0;
+ err_out:
+ preempt_enable_no_resched();
+ set_cpus_allowed(current, oldmask);
+ schedule();
+
+ return ret;
}
/* Driver entry point to verify the policy and range of frequencies */
-static int
-powernowk8_verify(struct cpufreq_policy *pol)
+static int powernowk8_verify(struct cpufreq_policy *pol)
{
- if (pending_bit_stuck()) {
- printk(KERN_ERR PFX "failing verify, change pending bit set\n");
- return -EIO;
- }
+ struct powernow_k8_data *data = powernow_data[pol->cpu];
- return cpufreq_frequency_table_verify(pol, powernow_table);
+ return cpufreq_frequency_table_verify(pol, data->powernow_table);
}
/* per CPU init entry point to the driver */
-static int __init
-powernowk8_cpu_init(struct cpufreq_policy *pol)
+static int __init powernowk8_cpu_init(struct cpufreq_policy *pol)
{
- if (pol->cpu != 0) {
- printk(KERN_ERR PFX "init not cpu 0\n");
+ cpumask_t oldmask = CPU_MASK_ALL;
+ int rc;
+ struct powernow_k8_data *data;
+
+ if (!check_supported_cpu(pol->cpu))
return -ENODEV;
+
+ data = kmalloc(sizeof(struct powernow_k8_data), GFP_KERNEL);
+ if (!data) {
+ printk(KERN_ERR PFX "unable to alloc powernow_k8_data");
+ return -ENOMEM;
+ }
+ memset(data,0,sizeof(struct powernow_k8_data));
+
+ data->cpu = pol->cpu;
+
+ if (powernow_k8_cpu_init_acpi(data)) {
+ /*
+ * Use the PSB BIOS structure. This is only availabe on
+ * an UP version, and is deprecated by AMD.
+ */
+
+ if (pol->cpu != 0) {
+ printk(KERN_ERR PFX "init - cpu 0\n");
+ kfree(data);
+ return -ENODEV;
+ }
+
+ if ((num_online_cpus() != 1) || (num_possible_cpus() != 1)) {
+ printk(KERN_INFO PFX "MP systems not supported by PSB BIOS structure\n");
+ kfree(data);
+ return 0;
+ }
+
+ rc = find_psb_table(data);
+ if (rc) {
+ kfree(data);
+ return -ENODEV;
+ }
}
+ /* only run on specific CPU from here on */
+ oldmask = current->cpus_allowed;
+ set_cpus_allowed(current, cpumask_of_cpu(pol->cpu));
+ schedule();
+
+ if (smp_processor_id() != pol->cpu) {
+ printk(KERN_ERR "limiting to cpu %u failed\n", pol->cpu);
+ goto err_out;
+ }
+
+ if (pending_bit_stuck()) {
+ printk(KERN_ERR PFX "failing init, change pending bit set\n");
+ goto err_out;
+ }
+
+ if (query_current_values_with_pending_wait(data)) {
+ goto err_out;
+ }
+
+ fidvid_msr_init();
+
+
+ /* run on any CPU again */
+ set_cpus_allowed(current, oldmask);
+ schedule();
+
pol->governor = CPUFREQ_DEFAULT_GOVERNOR;
/* Take a crude guess here.
- * That guess was in microseconds, so multply with 1000 */
- pol->cpuinfo.transition_latency = (((rvo + 8) * vstable * VST_UNITS_20US)
- + (3 * (1 << irt) * 10)) * 1000;
-
- if (query_current_values_with_pending_wait())
- return -EIO;
+ * That guess was in microseconds, so multiply with 1000 */
+ pol->cpuinfo.transition_latency = (((data->rvo + 8) * data->vstable * VST_UNITS_20US)
+ + (3 * (1 << data->irt) * 10)) * 1000;
- pol->cur = 1000 * find_freq_from_fid(currfid);
+ pol->cur = find_khz_freq_from_fid(data->currfid);
dprintk(KERN_DEBUG PFX "policy current frequency %d kHz\n", pol->cur);
/* min/max the cpu is capable of */
- if (cpufreq_frequency_table_cpuinfo(pol, powernow_table)) {
+ if (cpufreq_frequency_table_cpuinfo(pol, data->powernow_table)) {
printk(KERN_ERR PFX "invalid powernow_table\n");
- kfree(powernow_table);
+ kfree(data->powernow_table);
+ kfree(data);
return -EINVAL;
}
+ cpufreq_frequency_table_get_attr(data->powernow_table, pol->cpu);
- cpufreq_frequency_table_get_attr(powernow_table, pol->cpu);
+ dprintk(KERN_INFO PFX "init, curr fid %x vid %x\n", data->currfid, data->currvid);
- printk(KERN_INFO PFX "cpu_init done, current fid 0x%x, vid 0x%x\n",
- currfid, currvid);
+ powernow_data[pol->cpu] = data;
return 0;
+
+ err_out:
+ set_cpus_allowed(current, oldmask);
+ schedule();
+
+ kfree(data);
+ return -ENODEV;
}
static int __exit powernowk8_cpu_exit (struct cpufreq_policy *pol)
{
- if (pol->cpu != 0)
+ struct powernow_k8_data *data = powernow_data[pol->cpu];
+
+ if (!data)
return -EINVAL;
+ powernow_k8_cpu_exit_acpi(data);
+
cpufreq_frequency_table_put_attr(pol->cpu);
- if (powernow_table)
- kfree(powernow_table);
+ kfree(data->powernow_table);
+ kfree(data);
return 0;
}
@@ -845,33 +1033,31 @@
.attr = powernow_k8_attr,
};
-
/* driver entry point for init */
-static int __init
-powernowk8_init(void)
+static int __init powernowk8_init(void)
{
- int rc;
-
- if (check_supported_cpu() == 0)
- return -ENODEV;
+ unsigned int i, supported_cpus = 0;
- rc = find_psb_table();
- if (rc)
- return rc;
+ for (i=0; i<NR_CPUS; i++) {
+ if (!cpu_online(i))
+ continue;
+ if (check_supported_cpu(i))
+ supported_cpus++;
+ }
- if (pending_bit_stuck()) {
- printk(KERN_ERR PFX "powernowk8_init fail, change pending bit set\n");
- return -EIO;
+ if (supported_cpus == num_online_cpus()) {
+ printk(KERN_INFO PFX "Found %d AMD Athlon 64 / Opteron processors (" VERSION ")\n",
+ supported_cpus);
+ return cpufreq_register_driver(&cpufreq_amd64_driver);
}
- return cpufreq_register_driver(&cpufreq_amd64_driver);
+ return -ENODEV;
}
/* driver entry point for term */
-static void __exit
-powernowk8_exit(void)
+static void __exit powernowk8_exit(void)
{
- dprintk(KERN_INFO PFX "powernowk8_exit\n");
+ dprintk(KERN_INFO PFX "exit\n");
cpufreq_unregister_driver(&cpufreq_amd64_driver);
}
@@ -880,5 +1066,5 @@
MODULE_DESCRIPTION("AMD Athlon 64 and Opteron processor frequency driver.");
MODULE_LICENSE("GPL");
-module_init(powernowk8_init);
+late_initcall(powernowk8_init);
module_exit(powernowk8_exit);
--- clean/arch/i386/kernel/cpu/cpufreq/powernow-k8.h 2004-02-20 12:29:10.000000000 +0100
+++ linux-pn/arch/i386/kernel/cpu/cpufreq/powernow-k8.h 2004-03-26 12:44:27.000000000 +0100
@@ -1,20 +1,46 @@
/*
- * (c) 2003 Advanced Micro Devices, Inc.
+ * (c) 2003, 2004 Advanced Micro Devices, Inc.
* Your use of this code is subject to the terms and conditions of the
- * GNU general public license version 2. See "../../../COPYING" or
+ * GNU general public license version 2. See "COPYING" or
* http://www.gnu.org/licenses/gpl.html
*/
+struct powernow_k8_data {
+ unsigned int cpu;
+
+ u32 numps; /* number of p-states */
+ u32 batps; /* number of p-states supported on battery */
+
+ /* these values are constant when the PSB is used to determine
+ * vid/fid pairings, but are modified during the ->target() call
+ * when ACPI is used */
+ u32 rvo; /* ramp voltage offset */
+ u32 irt; /* isochronous relief time */
+ u32 vidmvs; /* usable value calculated from mvs */
+ u32 vstable; /* voltage stabilization time, units 20 us */
+ u32 plllock; /* pll lock time, units 1 us */
+
+ /* keep track of the current fid / vid */
+ u32 currvid;
+ u32 currfid;
+
+ /* the powernow_table includes all frequency and vid/fid pairings:
+ * fid are the lower 8 bits of the index, vid are the upper 8 bits.
+ * frequency is in kHz */
+ struct cpufreq_frequency_table *powernow_table;
+
+#ifdef CONFIG_X86_POWERNOW_K8_ACPI
+ /* the acpi table needs to be kept. it's only available if ACPI was
+ * used to determine valid frequency/vid/fid states */
+ struct acpi_processor_performance acpi_data;
+#endif
+};
+
/* processor's cpuid instruction support */
-#define CPUID_PROCESSOR_SIGNATURE 1 /* function 1 */
-#define CPUID_F1_FAM 0x00000f00 /* family mask */
-#define CPUID_F1_XFAM 0x0ff00000 /* extended family mask */
-#define CPUID_F1_MOD 0x000000f0 /* model mask */
-#define CPUID_F1_STEP 0x0000000f /* stepping level mask */
-#define CPUID_XFAM_MOD 0x0ff00ff0 /* xtended fam, fam + model */
-#define ATHLON64_XFAM_MOD 0x00000f40 /* xtended fam, fam + model */
-#define OPTERON_XFAM_MOD 0x00000f50 /* xtended fam, fam + model */
-#define ATHLON64_REV_C0 8
+#define CPUID_PROCESSOR_SIGNATURE 1 /* function 1 */
+#define CPUID_XFAM_MOD 0x0ff00ff0 /* extended fam, fam + model */
+#define ATHLON64_XFAM_MOD 0x00000f40 /* extended fam, fam + model */
+#define OPTERON_XFAM_MOD 0x00000f50 /* extended fam, fam + model */
#define CPUID_GET_MAX_CAPABILITIES 0x80000000
#define CPUID_FREQ_VOLT_CAPABILITIES 0x80000007
#define P_STATE_TRANSITION_CAPABLE 6
@@ -23,7 +49,6 @@
/* writes (wrmsr - opcode 0f 30), the register number is placed in ecx, and */
/* the value to write is placed in edx:eax. For reads (rdmsr - opcode 0f 32), */
/* the register number is placed in ecx, and the data is returned in edx:eax. */
-
#define MSR_FIDVID_CTL 0xc0010041
#define MSR_FIDVID_STATUS 0xc0010042
@@ -47,10 +72,24 @@
#define MSR_S_HI_MAX_WORKING_VID 0x001f0000
#define MSR_S_HI_START_VID 0x00001f00
#define MSR_S_HI_CURRENT_VID 0x0000001f
+#define MSR_C_HI_STP_GNT_BENIGN 0x00000001
+
+/*
+ There are restrictions frequencies have to follow:
+ - only 1 entry in the low fid table ( <=1.4GHz )
+ - lowest entry in the high fid table must be >= 2 * the
+ entry in the low fid table
+ - lowest entry in the high fid table must be a <= 200MHz +
+ 2 * the entry in the low fid table
+ - the parts can only step at 200 MHz intervals, so 1.9 GHz is
+ never valid
+ - lowest frequency must be >= interprocessor hypertransport link
+ speed (only applies to MP systems obviously)
+ */
/* fids (frequency identifiers) are arranged in 2 tables - lo and hi */
-#define LO_FID_TABLE_TOP 6
-#define HI_FID_TABLE_BOTTOM 8
+#define LO_FID_TABLE_TOP 6 /* fid values marking the boundary */
+#define HI_FID_TABLE_BOTTOM 8 /* between the low and high tables */
#define LO_VCOFREQ_TABLE_TOP 1400 /* corresponding vco frequency values */
#define HI_VCOFREQ_TABLE_BOTTOM 1600
@@ -58,33 +97,44 @@
#define MIN_FREQ_RESOLUTION 200 /* fids jump by 2 matching freq jumps by 200 */
#define MAX_FID 0x2a /* Spec only gives FID values as far as 5 GHz */
-
#define LEAST_VID 0x1e /* Lowest (numerically highest) useful vid value */
-
#define MIN_FREQ 800 /* Min and max freqs, per spec */
#define MAX_FREQ 5000
-#define INVALID_FID_MASK 0xffffffc1 /* not a valid fid if these bits are set */
-
-#define INVALID_VID_MASK 0xffffffe0 /* not a valid vid if these bits are set */
+#define INVALID_FID_MASK 0xffffffc1 /* not a valid fid if these bits are set */
+#define INVALID_VID_MASK 0xffffffe0 /* not a valid vid if these bits are set */
#define STOP_GRANT_5NS 1 /* min poss memory access latency for voltage change */
-
#define PLL_LOCK_CONVERSION (1000/5) /* ms to ns, then divide by clock period */
-
#define MAXIMUM_VID_STEPS 1 /* Current cpus only allow a single step of 25mV */
+#define VST_UNITS_20US 20 /* Voltage Stabilization Time is in units of 20us */
-#define VST_UNITS_20US 20 /* Voltage Stabalization Time is in units of 20us */
+/*
+ * Most values of interest are enocoded in a single field of the _PSS
+ * entries: the "control" value.
+ */
+#define IRT_SHIFT 30
+#define RVO_SHIFT 28
+#define PLL_L_SHIFT 20
+#define MVS_SHIFT 18
+#define VST_SHIFT 11
+#define VID_SHIFT 6
+#define IRT_MASK 3
+#define RVO_MASK 3
+#define PLL_L_MASK 0x7f
+#define MVS_MASK 3
+#define VST_MASK 0x7f
+#define VID_MASK 0x1f
+#define FID_MASK 0x3f
+
/*
-Version 1.4 of the PSB table. This table is constructed by BIOS and is
-to tell the OS's power management driver which VIDs and FIDs are
-supported by this particular processor. This information is obtained from
-the data sheets for each processor model by the system vendor and
-incorporated into the BIOS.
-If the data in the PSB / PST is wrong, then this driver will program the
-wrong values into hardware, which is very likely to lead to a crash.
-*/
+ * Version 1.4 of the PSB table. This table is constructed by BIOS and is
+ * to tell the OS's power management driver which VIDs and FIDs are
+ * supported by this particular processor.
+ * If the data in the PSB / PST is wrong, then this driver will program the
+ * wrong values into hardware, which is very likely to lead to a crash.
+ */
#define PSB_ID_STRING "AMDK7PNOW!"
#define PSB_ID_STRING_LEN 10
@@ -117,6 +167,7 @@
#define dprintk(msg...) do { } while(0)
#endif
-static inline int core_voltage_pre_transition(u32 reqvid);
-static inline int core_voltage_post_transition(u32 reqvid);
-static inline int core_frequency_transition(u32 reqfid);
+static inline int core_voltage_pre_transition(struct powernow_k8_data *data, u32 reqvid);
+static inline int core_voltage_post_transition(struct powernow_k8_data *data, u32 reqvid);
+static inline int core_frequency_transition(struct powernow_k8_data *data, u32 reqfid);
+static inline void powernow_k8_acpi_pst_values(struct powernow_k8_data *data, unsigned int index);
--
When do you have a heart between your knees?
[Johanka's followup: and *two* hearts?]
^ permalink raw reply [flat|nested] 21+ messages in thread
* powernow-k8: support acpi
@ 2004-03-26 12:29 ` Pavel Machek
0 siblings, 0 replies; 21+ messages in thread
From: Pavel Machek @ 2004-03-26 12:29 UTC (permalink / raw)
To: Dominik Brodowski
Cc: paul.devriendt, mark.langsdorf, Cpufreq mailing list, kernel list,
Andrew Morton, davej
Hi!
> Other than that, I'd strongly appreciate pushing the change "upstream" to
> Dave Jones / Andrew Morton / Linus Torvalds
Here it is.
This is new version of powernow-k8 driver. It adds SMP support, and
support for getting tables through ACPI. (ACPI support is really
important, because many machines have broken "legacy" tables). Please
apply,
Pavel
--- clean/arch/i386/kernel/cpu/cpufreq/powernow-k8.c 2004-03-11 18:10:38.000000000 +0100
+++ linux-pn/arch/i386/kernel/cpu/cpufreq/powernow-k8.c 2004-03-26 13:23:08.000000000 +0100
@@ -1,22 +1,24 @@
/*
- * (c) 2003 Advanced Micro Devices, Inc.
+ * (c) 2003, 2004 Advanced Micro Devices, Inc.
* Your use of this code is subject to the terms and conditions of the
- * GNU general public license version 2. See "../../../COPYING" or
+ * GNU general public license version 2. See "COPYING" or
* http://www.gnu.org/licenses/gpl.html
*
* Support : paul.devriendt@amd.com
*
* Based on the powernow-k7.c module written by Dave Jones.
- * (C) 2003 Dave Jones <davej@codemonkey.ork.uk> on behalf of SuSE Labs
+ * (C) 2003 Dave Jones <davej@codemonkey.org.uk> on behalf of SuSE Labs
* (C) 2004 Dominik Brodowski <linux@brodo.de>
* (C) 2004 Pavel Machek <pavel@suse.cz>
* Licensed under the terms of the GNU GPL License version 2.
* Based upon datasheets & sample CPUs kindly provided by AMD.
*
+ * Valuable input gratefully received from Dave Jones, Pavel Machek, Dominik
+ * Brodowski, and others.
+ *
* Processor information obtained from Chapter 9 (Power and Thermal Management)
* of the "BIOS and Kernel Developer's Guide for the AMD Athlon 64 and AMD
- * Opteron Processors", revision 3.03, available for download from www.amd.com
- *
+ * Opteron Processors" available for download from www.amd.com
*/
#include <linux/kernel.h>
@@ -31,55 +33,47 @@
#include <asm/io.h>
#include <asm/delay.h>
+#ifdef CONFIG_ACPI
+#define CONFIG_X86_POWERNOW_K8_ACPI
+#endif
+#ifdef CONFIG_X86_POWERNOW_K8_ACPI
+
+#include <linux/acpi.h>
+#include <acpi/processor.h>
+
+#endif /* CONFIG_X86_POWERNOW_K8_ACPI */
+
+
#define PFX "powernow-k8: "
-#define BFX PFX "BIOS error: "
-#define VERSION "version 1.00.08a"
+#define VERSION "version 1.20.08b - March 20, 2004"
#include "powernow-k8.h"
-static u32 vstable; /* voltage stabalization time, from PSB, units 20 us */
-static u32 plllock; /* pll lock time, from PSB, units 1 us */
-static u32 numps; /* number of p-states, from PSB */
-static u32 rvo; /* ramp voltage offset, from PSB */
-static u32 irt; /* isochronous relief time, from PSB */
-static u32 vidmvs; /* usable value calculated from mvs, from PSB */
-static u32 currvid; /* keep track of the current fid / vid */
-static u32 currfid;
+/* serialize freq changes */
+static DECLARE_MUTEX(fidvid_sem);
-static struct cpufreq_frequency_table *powernow_table;
+static struct powernow_k8_data *powernow_data[NR_CPUS];
-/*
-The PSB table supplied by BIOS allows for the definition of the number of
-p-states that can be used when running on a/c, and the number of p-states
-that can be used when running on battery. This allows laptop manufacturers
-to force the system to save power when running from battery. The relationship
-is :
- 1 <= number_of_battery_p_states <= maximum_number_of_p_states
-
-This driver does NOT have the support in it to detect transitions from
-a/c power to battery power, and thus trigger the transition to a lower
-p-state if required. This is because I need ACPI and the 2.6 kernel to do
-this, and this is a 2.4 kernel driver. Check back for a new improved driver
-for the 2.6 kernel soon.
-
-This code therefore assumes it is on battery at all times, and thus
-restricts performance to number_of_battery_p_states. For desktops,
- number_of_battery_p_states == maximum_number_of_pstates,
-so this is not actually a restriction.
-*/
-static u32 batps; /* limit on the number of p states when on battery */
- /* - set by BIOS in the PSB/PST */
+/* Return a frequency in MHz, given an input fid */
+static inline u32 find_freq_from_fid(u32 fid)
+{
+ return 800 + (fid * 100);
+}
- /* Return a frequency in MHz, given an input fid */
-static u32 find_freq_from_fid(u32 fid)
+/* Return a frequency in KHz, given an input fid */
+static inline u32 find_khz_freq_from_fid(u32 fid)
{
- return 800 + (fid * 100);
+ return 1000 * find_freq_from_fid(fid);
}
+/* Return a voltage in miliVolts, given an input vid */
+static inline u32 find_milivolts_from_vid(struct powernow_k8_data *data, u32 vid)
+{
+ return 1550-vid*25;
+}
/* Return the vco fid for an input fid */
-static u32
-convert_fid_to_vco_fid(u32 fid)
+static u32 convert_fid_to_vco_fid(u32 fid)
{
if (fid < HI_FID_TABLE_BOTTOM) {
return 8 + (2 * fid);
@@ -89,11 +83,10 @@
}
/*
- * Return 1 if the pending bit is set. Unless we are actually just told the
- * processor to transition a state, seeing this bit set is really bad news.
+ * Return 1 if the pending bit is set. Unless we just instructed the processor
+ * to transition to a new state, seeing this bit set is really bad news.
*/
-static inline int
-pending_bit_stuck(void)
+static inline int pending_bit_stuck(void)
{
u32 lo, hi;
@@ -102,11 +95,10 @@
}
/*
- * Update the global current fid / vid values from the status msr. Returns 1
- * on error.
+ * Update the global current fid / vid values from the status msr. Returns
+ * 1 on error.
*/
-static int
-query_current_values_with_pending_wait(void)
+static int query_current_values_with_pending_wait(struct powernow_k8_data *data)
{
u32 lo, hi;
u32 i = 0;
@@ -120,63 +112,69 @@
rdmsr(MSR_FIDVID_STATUS, lo, hi);
}
- currvid = hi & MSR_S_HI_CURRENT_VID;
- currfid = lo & MSR_S_LO_CURRENT_FID;
+ data->currvid = hi & MSR_S_HI_CURRENT_VID;
+ data->currfid = lo & MSR_S_LO_CURRENT_FID;
return 0;
}
/* the isochronous relief time */
-static inline void
-count_off_irt(void)
+static inline void count_off_irt(struct powernow_k8_data *data)
{
- udelay((1 << irt) * 10);
+ udelay((1 << data->irt) * 10);
return;
}
-/* the voltage stabalization time */
-static inline void
-count_off_vst(void)
+/* the voltage stabilization time */
+static inline void count_off_vst(struct powernow_k8_data *data)
{
- udelay(vstable * VST_UNITS_20US);
+ udelay(data->vstable * VST_UNITS_20US);
return;
}
+/* need to init the control msr to a safe value (for each cpu) */
+static void fidvid_msr_init(void)
+{
+ u32 lo, hi;
+ u8 fid, vid;
+
+ rdmsr(MSR_FIDVID_STATUS, lo, hi);
+ vid = hi & MSR_S_HI_CURRENT_VID;
+ fid = lo & MSR_S_LO_CURRENT_FID;
+ lo = fid | (vid << MSR_C_LO_VID_SHIFT);
+ hi = MSR_C_HI_STP_GNT_BENIGN;
+ dprintk(PFX "cpu%d, init lo %x, hi %x\n", smp_processor_id(), lo, hi);
+ wrmsr(MSR_FIDVID_CTL, lo, hi);
+}
+
/* write the new fid value along with the other control fields to the msr */
-static int
-write_new_fid(u32 fid)
+static int write_new_fid(struct powernow_k8_data *data, u32 fid)
{
u32 lo;
- u32 savevid = currvid;
+ u32 savevid = data->currvid;
- if ((fid & INVALID_FID_MASK) || (currvid & INVALID_VID_MASK)) {
+ if ((fid & INVALID_FID_MASK) || (data->currvid & INVALID_VID_MASK)) {
printk(KERN_ERR PFX "internal error - overflow on fid write\n");
return 1;
}
- lo = fid | (currvid << MSR_C_LO_VID_SHIFT) | MSR_C_LO_INIT_FID_VID;
-
+ lo = fid | (data->currvid << MSR_C_LO_VID_SHIFT) | MSR_C_LO_INIT_FID_VID;
dprintk(KERN_DEBUG PFX "writing fid %x, lo %x, hi %x\n",
- fid, lo, plllock * PLL_LOCK_CONVERSION);
-
- wrmsr(MSR_FIDVID_CTL, lo, plllock * PLL_LOCK_CONVERSION);
-
- if (query_current_values_with_pending_wait())
+ fid, lo, data->plllock * PLL_LOCK_CONVERSION);
+ wrmsr(MSR_FIDVID_CTL, lo, data->plllock * PLL_LOCK_CONVERSION);
+ if (query_current_values_with_pending_wait(data))
return 1;
+ count_off_irt(data);
- count_off_irt();
-
- if (savevid != currvid) {
- printk(KERN_ERR PFX
- "vid changed on fid transition, save %x, currvid %x\n",
- savevid, currvid);
+ if (savevid != data->currvid) {
+ printk(KERN_ERR PFX "vid change on fid trans, old %x, new %x\n",
+ savevid, data->currvid);
return 1;
}
- if (fid != currfid) {
- printk(KERN_ERR PFX
- "fid transition failed, fid %x, currfid %x\n",
- fid, currfid);
+ if (fid != data->currfid) {
+ printk(KERN_ERR PFX "fid trans failed, fid %x, curr %x\n", fid,
+ data->currfid);
return 1;
}
@@ -184,40 +182,33 @@
}
/* Write a new vid to the hardware */
-static int
-write_new_vid(u32 vid)
+static int write_new_vid(struct powernow_k8_data *data, u32 vid)
{
u32 lo;
- u32 savefid = currfid;
+ u32 savefid = data->currfid;
- if ((currfid & INVALID_FID_MASK) || (vid & INVALID_VID_MASK)) {
+ if ((data->currfid & INVALID_FID_MASK) || (vid & INVALID_VID_MASK)) {
printk(KERN_ERR PFX "internal error - overflow on vid write\n");
return 1;
}
- lo = currfid | (vid << MSR_C_LO_VID_SHIFT) | MSR_C_LO_INIT_FID_VID;
-
+ lo = data->currfid | (vid << MSR_C_LO_VID_SHIFT) | MSR_C_LO_INIT_FID_VID;
dprintk(KERN_DEBUG PFX "writing vid %x, lo %x, hi %x\n",
vid, lo, STOP_GRANT_5NS);
-
wrmsr(MSR_FIDVID_CTL, lo, STOP_GRANT_5NS);
-
- if (query_current_values_with_pending_wait()) {
+ if (query_current_values_with_pending_wait(data))
return 1;
- }
- if (savefid != currfid) {
- printk(KERN_ERR PFX
- "fid changed on vid transition, save %x currfid %x\n",
- savefid, currfid);
+ if (savefid != data->currfid) {
+ printk(KERN_ERR PFX "fid changed on vid trans, old %x new %x\n",
+ savefid, data->currfid);
return 1;
}
- if (vid != currvid) {
- printk(KERN_ERR PFX
- "vid transition failed, vid %x, currvid %x\n",
- vid, currvid);
- return 1;
+ if (vid != data->currvid) {
+ printk(KERN_ERR PFX "vid trans failed, vid %x, curr %x\n", vid,
+ data->currvid);
+ return 1;
}
return 0;
@@ -228,300 +219,279 @@
* Decreasing vid codes represent increasing voltages:
* vid of 0 is 1.550V, vid of 0x1e is 0.800V, vid of 0x1f is off.
*/
-static int
-decrease_vid_code_by_step(u32 reqvid, u32 step)
+static int decrease_vid_code_by_step(struct powernow_k8_data *data, u32 reqvid, u32 step)
{
- if ((currvid - reqvid) > step)
- reqvid = currvid - step;
-
- if (write_new_vid(reqvid))
+ if ((data->currvid - reqvid) > step)
+ reqvid = data->currvid - step;
+ if (write_new_vid(data, reqvid))
return 1;
-
- count_off_vst();
-
+ count_off_vst(data);
return 0;
}
/* Change the fid and vid, by the 3 phases. */
-static inline int
-transition_fid_vid(u32 reqfid, u32 reqvid)
+static inline int transition_fid_vid(struct powernow_k8_data *data, u32 reqfid, u32 reqvid)
{
- if (core_voltage_pre_transition(reqvid))
+ if (core_voltage_pre_transition(data, reqvid))
return 1;
-
- if (core_frequency_transition(reqfid))
+ if (core_frequency_transition(data, reqfid))
return 1;
-
- if (core_voltage_post_transition(reqvid))
+ if (core_voltage_post_transition(data, reqvid))
return 1;
-
- if (query_current_values_with_pending_wait())
+ if (query_current_values_with_pending_wait(data))
return 1;
- if ((reqfid != currfid) || (reqvid != currvid)) {
- printk(KERN_ERR PFX "failed: req 0x%x 0x%x, curr 0x%x 0x%x\n",
- reqfid, reqvid, currfid, currvid);
+ if ((reqfid != data->currfid) || (reqvid != data->currvid)) {
+ printk(KERN_ERR PFX "failed (cpu%d): req %x %x, curr %x %x\n",
+ smp_processor_id(),
+ reqfid, reqvid, data->currfid, data->currvid);
return 1;
}
- dprintk(KERN_INFO PFX
- "transitioned: new fid 0x%x, vid 0x%x\n", currfid, currvid);
-
+ dprintk(KERN_INFO PFX "transitioned (cpu%d): new fid %x, vid %x\n",
+ smp_processor_id(), data->currfid, data->currvid);
return 0;
}
-/*
- * Phase 1 - core voltage transition ... setup appropriate voltage for the
- * fid transition.
- */
-static inline int
-core_voltage_pre_transition(u32 reqvid)
+/* Phase 1 - core voltage transition ... setup voltage */
+static inline int core_voltage_pre_transition(struct powernow_k8_data *data, u32 reqvid)
{
- u32 rvosteps = rvo;
- u32 savefid = currfid;
+ u32 rvosteps = data->rvo;
+ u32 savefid = data->currfid;
dprintk(KERN_DEBUG PFX
- "ph1: start, currfid 0x%x, currvid 0x%x, reqvid 0x%x, rvo %x\n",
- currfid, currvid, reqvid, rvo);
-
- while (currvid > reqvid) {
- dprintk(KERN_DEBUG PFX "ph1: curr 0x%x, requesting vid 0x%x\n",
- currvid, reqvid);
- if (decrease_vid_code_by_step(reqvid, vidmvs))
+ "ph1 (cpu%d): start, currfid %x, currvid %x, reqvid %x, rvo %x\n",
+ smp_processor_id(),
+ data->currfid, data->currvid, reqvid, data->rvo);
+
+ while (data->currvid > reqvid) {
+ dprintk(KERN_DEBUG PFX "ph1: curr %x, req vid %x\n",
+ data->currvid, reqvid);
+ if (decrease_vid_code_by_step(data, reqvid, data->vidmvs))
return 1;
}
while (rvosteps > 0) {
- if (currvid == 0) {
+ if (data->currvid == 0) {
rvosteps = 0;
} else {
dprintk(KERN_DEBUG PFX
- "ph1: changing vid for rvo, requesting 0x%x\n",
- currvid - 1);
- if (decrease_vid_code_by_step(currvid - 1, 1))
+ "ph1: changing vid for rvo, req %x\n",
+ data->currvid - 1);
+ if (decrease_vid_code_by_step(data, data->currvid - 1, 1))
return 1;
rvosteps--;
}
}
- if (query_current_values_with_pending_wait())
+ if (query_current_values_with_pending_wait(data))
return 1;
- if (savefid != currfid) {
- printk(KERN_ERR PFX "ph1 err, currfid changed 0x%x\n", currfid);
+ if (savefid != data->currfid) {
+ printk(KERN_ERR PFX "ph1: err, currfid changed %x\n", data->currfid);
return 1;
}
- dprintk(KERN_DEBUG PFX "ph1 complete, currfid 0x%x, currvid 0x%x\n",
- currfid, currvid);
+ dprintk(KERN_DEBUG PFX "ph1: complete, currfid %x, currvid %x\n",
+ data->currfid, data->currvid);
return 0;
}
/* Phase 2 - core frequency transition */
-static inline int
-core_frequency_transition(u32 reqfid)
+static inline int core_frequency_transition(struct powernow_k8_data *data, u32 reqfid)
{
u32 vcoreqfid;
u32 vcocurrfid;
u32 vcofiddiff;
- u32 savevid = currvid;
+ u32 savevid = data->currvid;
- if ((reqfid < HI_FID_TABLE_BOTTOM) && (currfid < HI_FID_TABLE_BOTTOM)) {
- printk(KERN_ERR PFX "ph2 illegal lo-lo transition 0x%x 0x%x\n",
- reqfid, currfid);
+ if ((reqfid < HI_FID_TABLE_BOTTOM) && (data->currfid < HI_FID_TABLE_BOTTOM)) {
+ printk(KERN_ERR PFX "ph2: illegal lo-lo transition %x %x\n",
+ reqfid, data->currfid);
return 1;
}
- if (currfid == reqfid) {
- printk(KERN_ERR PFX "ph2 null fid transition 0x%x\n", currfid);
+ if (data->currfid == reqfid) {
+ printk(KERN_ERR PFX "ph2: null fid transition %x\n", data->currfid);
return 0;
}
dprintk(KERN_DEBUG PFX
- "ph2 starting, currfid 0x%x, currvid 0x%x, reqfid 0x%x\n",
- currfid, currvid, reqfid);
+ "ph2 (cpu%d): starting, currfid %x, currvid %x, reqfid %x\n",
+ smp_processor_id(),
+ data->currfid, data->currvid, reqfid);
vcoreqfid = convert_fid_to_vco_fid(reqfid);
- vcocurrfid = convert_fid_to_vco_fid(currfid);
+ vcocurrfid = convert_fid_to_vco_fid(data->currfid);
vcofiddiff = vcocurrfid > vcoreqfid ? vcocurrfid - vcoreqfid
: vcoreqfid - vcocurrfid;
while (vcofiddiff > 2) {
- if (reqfid > currfid) {
- if (currfid > LO_FID_TABLE_TOP) {
- if (write_new_fid(currfid + 2)) {
+ if (reqfid > data->currfid) {
+ if (data->currfid > LO_FID_TABLE_TOP) {
+ if (write_new_fid(data, data->currfid + 2)) {
return 1;
}
} else {
if (write_new_fid
- (2 + convert_fid_to_vco_fid(currfid))) {
+ (data, 2 + convert_fid_to_vco_fid(data->currfid))) {
return 1;
}
}
} else {
- if (write_new_fid(currfid - 2))
+ if (write_new_fid(data, data->currfid - 2))
return 1;
}
- vcocurrfid = convert_fid_to_vco_fid(currfid);
+ vcocurrfid = convert_fid_to_vco_fid(data->currfid);
vcofiddiff = vcocurrfid > vcoreqfid ? vcocurrfid - vcoreqfid
: vcoreqfid - vcocurrfid;
}
- if (write_new_fid(reqfid))
+ if (write_new_fid(data, reqfid))
return 1;
-
- if (query_current_values_with_pending_wait())
+ if (query_current_values_with_pending_wait(data))
return 1;
- if (currfid != reqfid) {
+ if (data->currfid != reqfid) {
printk(KERN_ERR PFX
- "ph2 mismatch, failed fid transition, curr %x, req %x\n",
- currfid, reqfid);
+ "ph2: mismatch, failed fid transition, curr %x, req %x\n",
+ data->currfid, reqfid);
return 1;
}
- if (savevid != currvid) {
- printk(KERN_ERR PFX
- "ph2 vid changed, save %x, curr %x\n", savevid,
- currvid);
+ if (savevid != data->currvid) {
+ printk(KERN_ERR PFX "ph2: vid changed, save %x, curr %x\n",
+ savevid, data->currvid);
return 1;
}
- dprintk(KERN_DEBUG PFX "ph2 complete, currfid 0x%x, currvid 0x%x\n",
- currfid, currvid);
+ dprintk(KERN_DEBUG PFX "ph2: complete, currfid %x, currvid %x\n",
+ data->currfid, data->currvid);
return 0;
}
/* Phase 3 - core voltage transition flow ... jump to the final vid. */
-static inline int
-core_voltage_post_transition(u32 reqvid)
+static inline int core_voltage_post_transition(struct powernow_k8_data *data, u32 reqvid)
{
- u32 savefid = currfid;
+ u32 savefid = data->currfid;
u32 savereqvid = reqvid;
- dprintk(KERN_DEBUG PFX "ph3 starting, currfid 0x%x, currvid 0x%x\n",
- currfid, currvid);
+ dprintk(KERN_DEBUG PFX "ph3 (cpu%d): starting, currfid %x, currvid %x\n",
+ smp_processor_id(),
+ data->currfid, data->currvid);
- if (reqvid != currvid) {
- if (write_new_vid(reqvid))
+ if (reqvid != data->currvid) {
+ if (write_new_vid(data, reqvid))
return 1;
- if (savefid != currfid) {
+ if (savefid != data->currfid) {
printk(KERN_ERR PFX
"ph3: bad fid change, save %x, curr %x\n",
- savefid, currfid);
+ savefid, data->currfid);
return 1;
}
- if (currvid != reqvid) {
+ if (data->currvid != reqvid) {
printk(KERN_ERR PFX
"ph3: failed vid transition\n, req %x, curr %x",
- reqvid, currvid);
+ reqvid, data->currvid);
return 1;
}
}
- if (query_current_values_with_pending_wait())
+ if (query_current_values_with_pending_wait(data))
return 1;
- if (savereqvid != currvid) {
- dprintk(KERN_ERR PFX "ph3 failed, currvid 0x%x\n", currvid);
+ if (savereqvid != data->currvid) {
+ printk(KERN_ERR PFX "ph3: failed, currvid %x\n", data->currvid);
return 1;
}
- if (savefid != currfid) {
- dprintk(KERN_ERR PFX "ph3 failed, currfid changed 0x%x\n",
- currfid);
+ if (savefid != data->currfid) {
+ printk(KERN_ERR PFX "ph3: failed, currfid changed %x\n",
+ data->currfid);
return 1;
}
- dprintk(KERN_DEBUG PFX "ph3 complete, currfid 0x%x, currvid 0x%x\n",
- currfid, currvid);
-
+ dprintk(KERN_DEBUG PFX "ph3: complete, currfid %x, currvid %x\n",
+ data->currfid, data->currvid);
return 0;
}
-static inline int
-check_supported_cpu(void)
+static inline int check_supported_cpu(unsigned int cpu)
{
- struct cpuinfo_x86 *c = cpu_data;
+ cpumask_t oldmask = CPU_MASK_ALL;
u32 eax, ebx, ecx, edx;
+ unsigned int rc = 0;
- if (num_online_cpus() != 1) {
- printk(KERN_INFO PFX "multiprocessor systems not supported\n");
- return 0;
- }
+ oldmask = current->cpus_allowed;
+ set_cpus_allowed(current, cpumask_of_cpu(cpu));
+ schedule();
- if (c->x86_vendor != X86_VENDOR_AMD) {
-#ifdef MODULE
- printk(KERN_INFO PFX "Not an AMD processor\n");
-#endif
- return 0;
+ if (smp_processor_id() != cpu) {
+ printk(KERN_ERR "limiting to cpu %u failed\n", cpu);
+ goto out;
}
+ if (current_cpu_data.x86_vendor != X86_VENDOR_AMD)
+ goto out;
+
eax = cpuid_eax(CPUID_PROCESSOR_SIGNATURE);
if ((eax & CPUID_XFAM_MOD) == ATHLON64_XFAM_MOD) {
dprintk(KERN_DEBUG PFX "AMD Althon 64 Processor found\n");
- if ((eax & CPUID_F1_STEP) < ATHLON64_REV_C0) {
- printk(KERN_INFO PFX "Revision C0 or better "
- "AMD Athlon 64 processor required\n");
- return 0;
- }
} else if ((eax & CPUID_XFAM_MOD) == OPTERON_XFAM_MOD) {
dprintk(KERN_DEBUG PFX "AMD Opteron Processor found\n");
} else {
printk(KERN_INFO PFX
"AMD Athlon 64 or AMD Opteron processor required\n");
- return 0;
+ goto out;
}
eax = cpuid_eax(CPUID_GET_MAX_CAPABILITIES);
if (eax < CPUID_FREQ_VOLT_CAPABILITIES) {
- printk(KERN_INFO PFX
- "No frequency change capabilities detected\n");
- return 0;
+ printk(KERN_INFO PFX "No freq change capabilities\n");
+ goto out;
}
cpuid(CPUID_FREQ_VOLT_CAPABILITIES, &eax, &ebx, &ecx, &edx);
if ((edx & P_STATE_TRANSITION_CAPABLE) != P_STATE_TRANSITION_CAPABLE) {
printk(KERN_INFO PFX "Power state transitions not supported\n");
- return 0;
+ goto out;
}
- printk(KERN_INFO PFX "Found AMD64 processor supporting PowerNow (" VERSION ")\n");
- return 1;
+ rc = 1;
+
+ out:
+ set_cpus_allowed(current, oldmask);
+ schedule();
+ return rc;
}
-static int check_pst_table(struct pst_s *pst, u8 maxvid)
+static int check_pst_table(struct powernow_k8_data *data, struct pst_s *pst, u8 maxvid)
{
unsigned int j;
- u8 lastfid = 0xFF;
+ u8 lastfid = 0xff;
- for (j = 0; j < numps; j++) {
+ for (j = 0; j < data->numps; j++) {
if (pst[j].vid > LEAST_VID) {
- printk(KERN_ERR PFX "vid %d invalid : 0x%x\n", j, pst[j].vid);
+ printk(KERN_ERR PFX "vid %d bad: %x\n", j, pst[j].vid);
return -EINVAL;
}
- if (pst[j].vid < rvo) { /* vid + rvo >= 0 */
- printk(KERN_ERR PFX
- "BIOS error - 0 vid exceeded with pstate %d\n",
- j);
+ if (pst[j].vid < data->rvo) { /* vid + rvo >= 0 */
+ printk(KERN_ERR PFX "0 vid exceeded with pst %d\n", j);
return -ENODEV;
}
- if (pst[j].vid < maxvid + rvo) { /* vid + rvo >= maxvid */
- printk(KERN_ERR PFX
- "BIOS error - maxvid exceeded with pstate %d\n",
- j);
+ if (pst[j].vid < maxvid + data->rvo) { /* vid + rvo >= maxvid */
+ printk(KERN_ERR PFX "maxvid exceeded with pst %d\n", j);
return -ENODEV;
}
if ((pst[j].fid > MAX_FID)
|| (pst[j].fid & 1)
|| (j && (pst[j].fid < HI_FID_TABLE_BOTTOM))) {
- /* Only first fid is allowed to be in "low" range */
- printk(KERN_ERR PFX "fid %d invalid : 0x%x\n", j, pst[j].fid);
+ printk(KERN_ERR PFX "fid %d bad: %x\n", j, pst[j].fid);
return -EINVAL;
}
if (pst[j].fid < lastfid)
@@ -531,20 +501,87 @@
printk(KERN_ERR PFX "lastfid invalid\n");
return -EINVAL;
}
- if (lastfid > LO_FID_TABLE_TOP) {
- printk(KERN_INFO PFX "first fid not from lo freq table\n");
+ if (lastfid > LO_FID_TABLE_TOP)
+ printk(KERN_INFO PFX "first fid not from lo freq table\n");
+
+ return 0;
+}
+
+static void print_basics(struct powernow_k8_data *data)
+{
+ int j;
+ for (j = 0; j < data->numps; j++) {
+ printk(KERN_INFO PFX " %d : fid %x (%d MHz), vid %x (%d mV)\n", j,
+ data->powernow_table[j].index & 0xff,
+ data->powernow_table[j].frequency/1000,
+ data->powernow_table[j].index >> 8,
+ find_milivolts_from_vid(data, data->powernow_table[j].index >> 8));
+ }
+ if (data->batps)
+ printk(KERN_INFO PFX "Only %d pstates on battery\n", data->batps);
+}
+
+static inline int fill_powernow_table(struct powernow_k8_data *data, struct pst_s *pst, u8 maxvid)
+{
+ struct cpufreq_frequency_table *powernow_table;
+ unsigned int j;
+
+ if (data->batps) { /* use ACPI support to get full speed on mains power */
+ printk(KERN_WARNING PFX "Only %d pstates usable (use ACPI driver for full range\n", data->batps);
+ data->numps = data->batps;
}
+ for ( j=1; j<data->numps; j++ )
+ if (pst[j-1].fid >= pst[j].fid) {
+ printk(KERN_ERR PFX "PST out of sequence\n");
+ return -EINVAL;
+ }
+
+ if (data->numps < 2) {
+ printk(KERN_ERR PFX "no p states to transition\n");
+ return -ENODEV;
+ }
+
+ if (check_pst_table(data, pst, maxvid))
+ return -EINVAL;
+
+ powernow_table = kmalloc((sizeof(struct cpufreq_frequency_table)
+ * (data->numps + 1)), GFP_KERNEL);
+ if (!powernow_table) {
+ printk(KERN_ERR PFX "powernow_table memory alloc failure\n");
+ return -ENOMEM;
+ }
+
+ for (j = 0; j < data->numps; j++) {
+ powernow_table[j].index = pst[j].fid; /* lower 8 bits */
+ powernow_table[j].index |= (pst[j].vid << 8); /* upper 8 bits */
+ powernow_table[j].frequency = find_khz_freq_from_fid(pst[j].fid);
+ }
+ powernow_table[data->numps].frequency = CPUFREQ_TABLE_END;
+ powernow_table[data->numps].index = 0;
+
+ if (query_current_values_with_pending_wait(data)) {
+ kfree(powernow_table);
+ return -EIO;
+ }
+
+ dprintk(KERN_INFO PFX "cfid %x, cvid %x\n", data->currfid, data->currvid);
+ data->powernow_table = powernow_table;
+ print_basics(data);
+
+ for (j = 0; j < data->numps; j++)
+ if ((pst[j].fid==data->currfid) && (pst[j].vid==data->currvid))
+ return 0;
+
+ dprintk(KERN_ERR PFX "currfid/vid do not match PST, ignoring\n");
return 0;
}
/* Find and validate the PSB/PST table in BIOS. */
-static inline int
-find_psb_table(void)
+static inline int find_psb_table(struct powernow_k8_data *data)
{
struct psb_s *psb;
- struct pst_s *pst;
- unsigned int i, j;
+ unsigned int i;
u32 mvs;
u8 maxvid;
@@ -556,276 +593,427 @@
if (memcmp(psb, PSB_ID_STRING, PSB_ID_STRING_LEN) != 0)
continue;
- dprintk(KERN_DEBUG PFX "found PSB header at 0x%p\n", psb);
-
- dprintk(KERN_DEBUG PFX "table vers: 0x%x\n", psb->tableversion);
+ dprintk(KERN_DEBUG PFX "found PSB header at %p\n", psb);
+ dprintk(KERN_DEBUG PFX "table version: %x\n",
+ psb->tableversion);
if (psb->tableversion != PSB_VERSION_1_4) {
- printk(KERN_INFO BFX "PSB table is not v1.4\n");
+ printk(KERN_INFO PFX "PSB table is not v1.4\n");
return -ENODEV;
}
- dprintk(KERN_DEBUG PFX "flags: 0x%x\n", psb->flags1);
+ dprintk(KERN_DEBUG PFX "flags: %x\n", psb->flags1);
if (psb->flags1) {
- printk(KERN_ERR BFX "unknown flags\n");
+ printk(KERN_ERR PFX "unknown flags\n");
return -ENODEV;
}
- vstable = psb->voltagestabilizationtime;
- dprintk(KERN_DEBUG PFX "flags2: 0x%x\n", psb->flags2);
- rvo = psb->flags2 & 3;
- irt = ((psb->flags2) >> 2) & 3;
+ data->vstable = psb->voltagestabilizationtime;
+ dprintk(KERN_INFO PFX "voltage stabilization time: %d(*20us)\n", data->vstable);
+
+ dprintk(KERN_DEBUG PFX "flags2: %x\n", psb->flags2);
+
+ data->rvo = psb->flags2 & 3;
+ data->irt = ((psb->flags2) >> 2) & 3;
mvs = ((psb->flags2) >> 4) & 3;
- vidmvs = 1 << mvs;
- batps = ((psb->flags2) >> 6) & 3;
+ data->vidmvs = 1 << mvs;
+ data->batps = ((psb->flags2) >> 6) & 3;
- printk(KERN_INFO PFX "voltage stable in %d usec", vstable * 20);
- if (batps)
- printk(", only %d lowest states on battery", batps);
- printk(", ramp voltage offset: %d", rvo);
- printk(", isochronous relief time: %d", irt);
- printk(", maximum voltage step: %d\n", mvs);
+ dprintk(KERN_INFO PFX "ramp voltage offset: %d\n", data->rvo);
+ dprintk(KERN_INFO PFX "isochronous relief time: %d\n", data->irt);
+ dprintk(KERN_INFO PFX "maximum voltage step: %d - %x\n",
+ mvs, data->vidmvs);
dprintk(KERN_DEBUG PFX "numpst: 0x%x\n", psb->numpst);
if (psb->numpst != 1) {
- printk(KERN_ERR BFX "numpst must be 1\n");
+ printk(KERN_ERR PFX "numpst must be 1\n");
return -ENODEV;
}
- dprintk(KERN_DEBUG PFX "cpuid: 0x%x\n", psb->cpuid);
-
- plllock = psb->plllocktime;
- printk(KERN_INFO PFX "pll lock time: 0x%x, ", plllock);
-
+ data->plllock = psb->plllocktime;
+ dprintk(KERN_INFO PFX "plllocktime: %x (units 1us)\n",
+ psb->plllocktime);
+ dprintk(KERN_INFO PFX "maxfid: %x\n", psb->maxfid);
+ dprintk(KERN_INFO PFX "maxvid: %x\n", psb->maxvid);
maxvid = psb->maxvid;
- printk("maxfid 0x%x (%d MHz), maxvid 0x%x\n",
- psb->maxfid, find_freq_from_fid(psb->maxfid), maxvid);
- numps = psb->numpstates;
- if (numps < 2) {
- printk(KERN_ERR BFX "no p states to transition\n");
- return -ENODEV;
- }
+ data->numps = psb->numpstates;
+ dprintk(KERN_INFO PFX "numpstates: %x\n", data->numps);
+ return fill_powernow_table(data, (struct pst_s *)(psb+1), maxvid);
+ }
- if (batps == 0) {
- batps = numps;
- } else if (batps > numps) {
- printk(KERN_ERR BFX "batterypstates > numpstates\n");
- batps = numps;
- } else {
- printk(KERN_ERR PFX
- "Restricting operation to %d p-states\n", batps);
- printk(KERN_ERR PFX
- "Check for an updated driver to access all "
- "%d p-states\n", numps);
- }
+ /*
+ * If you see this message, complain to BIOS manufacturer. If
+ * he tells you "we do not support Linux" or some similar
+ * nonsense, remember that Windows 2000 uses the same legacy
+ * mechanism that the old Linux PSB driver uses. Tell them it
+ * is broken with Windows 2000.
+ *
+ * The reference to the AMD documentation is chapter 9 in the
+ * BIOS and Kernel Developer's Guide, which is available on
+ * www.amd.com
+ */
+ printk(KERN_ERR PFX "BIOS error - no PSB\n");
+ return -ENODEV;
+}
- if (numps <= 1) {
- printk(KERN_ERR PFX "only 1 p-state to transition\n");
- return -ENODEV;
- }
- pst = (struct pst_s *) (psb + 1);
- if (check_pst_table(pst, maxvid))
- return -EINVAL;
+#ifdef CONFIG_X86_POWERNOW_K8_ACPI
- powernow_table = kmalloc((sizeof(struct cpufreq_frequency_table) * (numps + 1)), GFP_KERNEL);
- if (!powernow_table) {
- printk(KERN_ERR PFX "powernow_table memory alloc failure\n");
- return -ENOMEM;
- }
+static inline void powernow_k8_acpi_pst_values(struct powernow_k8_data *data, unsigned int index)
+{
+ if (!data->acpi_data.state_count)
+ return;
- for (j = 0; j < psb->numpstates; j++) {
- powernow_table[j].index = pst[j].fid; /* lower 8 bits */
- powernow_table[j].index |= (pst[j].vid << 8); /* upper 8 bits */
- }
+ data->irt = (data->acpi_data.states[index].control >> IRT_SHIFT) & IRT_MASK;
+ data->rvo = (data->acpi_data.states[index].control >> RVO_SHIFT) & RVO_MASK;
+ data->plllock = (data->acpi_data.states[index].control >> PLL_L_SHIFT) & PLL_L_MASK;
+ data->vidmvs = 1 << ((data->acpi_data.states[index].control >> MVS_SHIFT) & MVS_MASK);
+ data->vstable = (data->acpi_data.states[index].control >> VST_SHIFT) & VST_MASK;
+}
- /* If you want to override your frequency tables, this
- is right place. */
+static int powernow_k8_cpu_init_acpi(struct powernow_k8_data *data)
+{
+ int i;
+ int cntlofreq = 0;
+ struct cpufreq_frequency_table *powernow_table;
- for (j = 0; j < numps; j++) {
- powernow_table[j].frequency = find_freq_from_fid(powernow_table[j].index & 0xff)*1000;
- printk(KERN_INFO PFX " %d : fid 0x%x (%d MHz), vid 0x%x\n", j,
- powernow_table[j].index & 0xff,
- powernow_table[j].frequency/1000,
- powernow_table[j].index >> 8);
+ if (acpi_processor_register_performance(&data->acpi_data, data->cpu)) {
+ dprintk(KERN_DEBUG PFX "register performance failed\n");
+ return -EIO;
+ }
+
+ /* verify the data contained in the ACPI structures */
+ if (data->acpi_data.state_count <= 1) {
+ dprintk(KERN_DEBUG PFX "No ACPI P-States\n");
+ goto err_out;
+ }
+
+ if ((data->acpi_data.control_register.space_id != ACPI_ADR_SPACE_FIXED_HARDWARE) ||
+ (data->acpi_data.status_register.space_id != ACPI_ADR_SPACE_FIXED_HARDWARE)) {
+ dprintk(KERN_DEBUG PFX "Invalid control/status registers\n");
+ goto err_out;
+ }
+
+ /* fill in data->powernow_table */
+ powernow_table = kmalloc((sizeof(struct cpufreq_frequency_table)
+ * (data->acpi_data.state_count + 1)), GFP_KERNEL);
+ if (!powernow_table) {
+ dprintk(KERN_ERR PFX "powernow_table memory alloc failure\n");
+ goto err_out;
+ }
+
+ for (i = 0; i < data->acpi_data.state_count; i++) {
+ u32 fid = data->acpi_data.states[i].control & FID_MASK;
+ u32 vid = (data->acpi_data.states[i].control >> VID_SHIFT) & VID_MASK;
+
+ dprintk(KERN_INFO PFX " %d : fid %x, vid %x\n", i, fid, vid);
+
+ powernow_table[i].index = fid; /* lower 8 bits */
+ powernow_table[i].index |= (vid << 8); /* upper 8 bits */
+ powernow_table[i].frequency = find_khz_freq_from_fid(fid);
+
+ /* verify frequency is OK */
+ if ((powernow_table[i].frequency > (MAX_FREQ * 1000)) ||
+ (powernow_table[i].frequency < (MIN_FREQ * 1000))) {
+ dprintk(KERN_INFO PFX "invalid freq %u kHz\n", powernow_table[i].frequency);
+ powernow_table[i].frequency = CPUFREQ_ENTRY_INVALID;
+ continue;
}
- powernow_table[numps].frequency = CPUFREQ_TABLE_END;
- powernow_table[numps].index = 0;
+ /* verify only 1 entry from the lo frequency table */
+ if ((fid < HI_FID_TABLE_BOTTOM) && (cntlofreq++)) {
+ printk(KERN_ERR PFX "Too many lo freq table entries\n");
+ goto err_out;
+ }
- if (query_current_values_with_pending_wait()) {
- kfree(powernow_table);
- return -EIO;
+ if (powernow_table[i].frequency != (data->acpi_data.states[i].core_frequency * 1000)) {
+ printk(KERN_INFO PFX "invalid freq entries %u kHz vs. %u kHz\n",
+ powernow_table[i].frequency,
+ (unsigned int) (data->acpi_data.states[i].core_frequency * 1000));
+ powernow_table[i].frequency = CPUFREQ_ENTRY_INVALID;
+ continue;
}
+ }
+ powernow_table[data->acpi_data.state_count].frequency = CPUFREQ_TABLE_END;
+ powernow_table[data->acpi_data.state_count].index = 0;
+ data->powernow_table = powernow_table;
+
+ /* fill in data */
+ data->numps = data->acpi_data.state_count;
+ print_basics(data);
- printk(KERN_INFO PFX "currfid 0x%x (%d MHz), currvid 0x%x\n",
- currfid, find_freq_from_fid(currfid), currvid);
+ powernow_k8_acpi_pst_values(data, 0);
- for (j = 0; j < numps; j++)
- if ((pst[j].fid==currfid) && (pst[j].vid==currvid))
- return 0;
+ return 0;
- printk(KERN_ERR BFX "currfid/vid do not match PST, ignoring\n");
- return 0;
- }
+ err_out:
+ acpi_processor_unregister_performance(&data->acpi_data, data->cpu);
+
+ /* data->acpi_data.state_count informs us at ->exit() whether ACPI was used */
+ data->acpi_data.state_count = 0;
- printk(KERN_ERR BFX "no PSB\n");
return -ENODEV;
}
+static void powernow_k8_cpu_exit_acpi(struct powernow_k8_data *data)
+{
+ if (data->acpi_data.state_count)
+ acpi_processor_unregister_performance(&data->acpi_data, data->cpu);
+}
+
+#else
+static inline int powernow_k8_cpu_init_acpi(struct powernow_k8_data *data) { return -ENODEV; }
+static inline void powernow_k8_cpu_exit_acpi(struct powernow_k8_data *data) { return; }
+static inline void powernow_k8_acpi_pst_values(struct powernow_k8_data *data, unsigned int index) { return; }
+#endif /* CONFIG_X86_POWERNOW_K8_ACPI */
+
+
/* Take a frequency, and issue the fid/vid transition command */
-static inline int
-transition_frequency(unsigned int index)
+static inline int transition_frequency(struct powernow_k8_data *data, unsigned int index)
{
u32 fid;
u32 vid;
int res;
struct cpufreq_freqs freqs;
+ dprintk(KERN_DEBUG PFX "cpu %d transition to index %u\n",
+ smp_processor_id(), index );
+
/* fid are the lower 8 bits of the index we stored into
* the cpufreq frequency table in find_psb_table, vid are
* the upper 8 bits.
*/
- fid = powernow_table[index].index & 0xFF;
- vid = (powernow_table[index].index & 0xFF00) >> 8;
+ fid = data->powernow_table[index].index & 0xFF;
+ vid = (data->powernow_table[index].index & 0xFF00) >> 8;
- dprintk(KERN_DEBUG PFX "table matched fid 0x%x, giving vid 0x%x\n",
- fid, vid);
+ dprintk(KERN_DEBUG PFX "matched fid %x, giving vid %x\n", fid, vid);
- if (query_current_values_with_pending_wait())
+ if (query_current_values_with_pending_wait(data))
return 1;
- if ((currvid == vid) && (currfid == fid)) {
- dprintk(KERN_DEBUG PFX
- "target matches current values (fid 0x%x, vid 0x%x)\n",
+ if ((data->currvid == vid) && (data->currfid == fid)) {
+ dprintk(KERN_DEBUG PFX "target matches curr (fid %x, vid %x)\n",
fid, vid);
return 0;
}
- if ((fid < HI_FID_TABLE_BOTTOM) && (currfid < HI_FID_TABLE_BOTTOM)) {
+ if ((fid < HI_FID_TABLE_BOTTOM) && (data->currfid < HI_FID_TABLE_BOTTOM)) {
printk(KERN_ERR PFX
"ignoring illegal change in lo freq table-%x to %x\n",
- currfid, fid);
+ data->currfid, fid);
return 1;
}
- dprintk(KERN_DEBUG PFX "changing to fid 0x%x, vid 0x%x\n", fid, vid);
-
- freqs.cpu = 0; /* only true because SMP not supported */
+ dprintk(KERN_DEBUG PFX "cpu %d, changing to fid %x, vid %x\n",
+ smp_processor_id(), fid, vid);
+ freqs.cpu = data->cpu;
- freqs.old = find_freq_from_fid(currfid);
- freqs.new = find_freq_from_fid(fid);
+ freqs.old = find_khz_freq_from_fid(data->currfid);
+ freqs.new = find_khz_freq_from_fid(fid);
cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
- res = transition_fid_vid(fid, vid);
+ down(&fidvid_sem);
+ res = transition_fid_vid(data, fid, vid);
+ up(&fidvid_sem);
- freqs.new = find_freq_from_fid(currfid);
+ freqs.new = find_khz_freq_from_fid(data->currfid);
cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
return res;
}
/* Driver entry point to switch to the target frequency */
-static int
-powernowk8_target(struct cpufreq_policy *pol, unsigned targfreq, unsigned relation)
+static int powernowk8_target(struct cpufreq_policy *pol, unsigned targfreq, unsigned relation)
{
- u32 checkfid = currfid;
- u32 checkvid = currvid;
+ cpumask_t oldmask = CPU_MASK_ALL;
+ struct powernow_k8_data *data = powernow_data[pol->cpu];
+ u32 checkfid = data->currfid;
+ u32 checkvid = data->currvid;
unsigned int newstate;
+ int ret = -EIO;
+
+ /* only run on specific CPU from here on */
+ oldmask = current->cpus_allowed;
+ set_cpus_allowed(current, cpumask_of_cpu(pol->cpu));
+ schedule();
+
+ if (smp_processor_id() != pol->cpu) {
+ printk(KERN_ERR "limiting to cpu %u failed\n", pol->cpu);
+ goto err_out;
+ }
+
+ /* from this point, do not exit without restoring preempt and cpu */
+ preempt_disable();
if (pending_bit_stuck()) {
- printk(KERN_ERR PFX "drv targ fail: change pending bit set\n");
- return -EIO;
+ printk(KERN_ERR PFX "failing targ, change pending bit set\n");
+ goto err_out;
}
- dprintk(KERN_DEBUG PFX "targ: %d kHz, min %d, max %d, relation %d\n",
- targfreq, pol->min, pol->max, relation);
+ dprintk(KERN_DEBUG PFX "targ: cpu %d, %d kHz, min %d, max %d, relation %d\n",
+ pol->cpu, targfreq, pol->min, pol->max, relation);
- if (query_current_values_with_pending_wait())
- return -EIO;
+ if (query_current_values_with_pending_wait(data)) {
+ ret = -EIO;
+ goto err_out;
+ }
- dprintk(KERN_DEBUG PFX "targ: curr fid 0x%x, vid 0x%x\n",
- currfid, currvid);
+ dprintk(KERN_DEBUG PFX "targ: curr fid %x, vid %x\n",
+ data->currfid, data->currvid);
- if ((checkvid != currvid) || (checkfid != currfid)) {
- printk(KERN_ERR PFX
- "error - out of sync, fid 0x%x 0x%x, vid 0x%x 0x%x\n",
- checkfid, currfid, checkvid, currvid);
+ if ((checkvid != data->currvid) || (checkfid != data->currfid)) {
+ printk(KERN_ERR PFX "out of sync, fid %x %x, vid %x %x\n",
+ checkfid, data->currfid, checkvid, data->currvid);
}
- if (cpufreq_frequency_table_target(pol, powernow_table, targfreq, relation, &newstate))
- return -EINVAL;
-
- if (transition_frequency(newstate))
+ if (cpufreq_frequency_table_target(pol, data->powernow_table, targfreq, relation, &newstate))
+ goto err_out;
+
+ powernow_k8_acpi_pst_values(data, newstate);
+
+ if (transition_frequency(data, newstate))
{
printk(KERN_ERR PFX "transition frequency failed\n");
- return 1;
+ ret = 1;
+ goto err_out;
}
- pol->cur = 1000 * find_freq_from_fid(currfid);
+ pol->cur = find_khz_freq_from_fid(data->currfid);
+ ret = 0;
- return 0;
+ err_out:
+ preempt_enable_no_resched();
+ set_cpus_allowed(current, oldmask);
+ schedule();
+
+ return ret;
}
/* Driver entry point to verify the policy and range of frequencies */
-static int
-powernowk8_verify(struct cpufreq_policy *pol)
+static int powernowk8_verify(struct cpufreq_policy *pol)
{
- if (pending_bit_stuck()) {
- printk(KERN_ERR PFX "failing verify, change pending bit set\n");
- return -EIO;
- }
+ struct powernow_k8_data *data = powernow_data[pol->cpu];
- return cpufreq_frequency_table_verify(pol, powernow_table);
+ return cpufreq_frequency_table_verify(pol, data->powernow_table);
}
/* per CPU init entry point to the driver */
-static int __init
-powernowk8_cpu_init(struct cpufreq_policy *pol)
+static int __init powernowk8_cpu_init(struct cpufreq_policy *pol)
{
- if (pol->cpu != 0) {
- printk(KERN_ERR PFX "init not cpu 0\n");
+ cpumask_t oldmask = CPU_MASK_ALL;
+ int rc;
+ struct powernow_k8_data *data;
+
+ if (!check_supported_cpu(pol->cpu))
return -ENODEV;
+
+ data = kmalloc(sizeof(struct powernow_k8_data), GFP_KERNEL);
+ if (!data) {
+ printk(KERN_ERR PFX "unable to alloc powernow_k8_data");
+ return -ENOMEM;
+ }
+ memset(data,0,sizeof(struct powernow_k8_data));
+
+ data->cpu = pol->cpu;
+
+ if (powernow_k8_cpu_init_acpi(data)) {
+ /*
+ * Use the PSB BIOS structure. This is only availabe on
+ * an UP version, and is deprecated by AMD.
+ */
+
+ if (pol->cpu != 0) {
+ printk(KERN_ERR PFX "init - cpu 0\n");
+ kfree(data);
+ return -ENODEV;
+ }
+
+ if ((num_online_cpus() != 1) || (num_possible_cpus() != 1)) {
+ printk(KERN_INFO PFX "MP systems not supported by PSB BIOS structure\n");
+ kfree(data);
+ return 0;
+ }
+
+ rc = find_psb_table(data);
+ if (rc) {
+ kfree(data);
+ return -ENODEV;
+ }
}
+ /* only run on specific CPU from here on */
+ oldmask = current->cpus_allowed;
+ set_cpus_allowed(current, cpumask_of_cpu(pol->cpu));
+ schedule();
+
+ if (smp_processor_id() != pol->cpu) {
+ printk(KERN_ERR "limiting to cpu %u failed\n", pol->cpu);
+ goto err_out;
+ }
+
+ if (pending_bit_stuck()) {
+ printk(KERN_ERR PFX "failing init, change pending bit set\n");
+ goto err_out;
+ }
+
+ if (query_current_values_with_pending_wait(data)) {
+ goto err_out;
+ }
+
+ fidvid_msr_init();
+
+
+ /* run on any CPU again */
+ set_cpus_allowed(current, oldmask);
+ schedule();
+
pol->governor = CPUFREQ_DEFAULT_GOVERNOR;
/* Take a crude guess here.
- * That guess was in microseconds, so multply with 1000 */
- pol->cpuinfo.transition_latency = (((rvo + 8) * vstable * VST_UNITS_20US)
- + (3 * (1 << irt) * 10)) * 1000;
-
- if (query_current_values_with_pending_wait())
- return -EIO;
+ * That guess was in microseconds, so multiply with 1000 */
+ pol->cpuinfo.transition_latency = (((data->rvo + 8) * data->vstable * VST_UNITS_20US)
+ + (3 * (1 << data->irt) * 10)) * 1000;
- pol->cur = 1000 * find_freq_from_fid(currfid);
+ pol->cur = find_khz_freq_from_fid(data->currfid);
dprintk(KERN_DEBUG PFX "policy current frequency %d kHz\n", pol->cur);
/* min/max the cpu is capable of */
- if (cpufreq_frequency_table_cpuinfo(pol, powernow_table)) {
+ if (cpufreq_frequency_table_cpuinfo(pol, data->powernow_table)) {
printk(KERN_ERR PFX "invalid powernow_table\n");
- kfree(powernow_table);
+ kfree(data->powernow_table);
+ kfree(data);
return -EINVAL;
}
+ cpufreq_frequency_table_get_attr(data->powernow_table, pol->cpu);
- cpufreq_frequency_table_get_attr(powernow_table, pol->cpu);
+ dprintk(KERN_INFO PFX "init, curr fid %x vid %x\n", data->currfid, data->currvid);
- printk(KERN_INFO PFX "cpu_init done, current fid 0x%x, vid 0x%x\n",
- currfid, currvid);
+ powernow_data[pol->cpu] = data;
return 0;
+
+ err_out:
+ set_cpus_allowed(current, oldmask);
+ schedule();
+
+ kfree(data);
+ return -ENODEV;
}
static int __exit powernowk8_cpu_exit (struct cpufreq_policy *pol)
{
- if (pol->cpu != 0)
+ struct powernow_k8_data *data = powernow_data[pol->cpu];
+
+ if (!data)
return -EINVAL;
+ powernow_k8_cpu_exit_acpi(data);
+
cpufreq_frequency_table_put_attr(pol->cpu);
- if (powernow_table)
- kfree(powernow_table);
+ kfree(data->powernow_table);
+ kfree(data);
return 0;
}
@@ -845,33 +1033,31 @@
.attr = powernow_k8_attr,
};
-
/* driver entry point for init */
-static int __init
-powernowk8_init(void)
+static int __init powernowk8_init(void)
{
- int rc;
-
- if (check_supported_cpu() == 0)
- return -ENODEV;
+ unsigned int i, supported_cpus = 0;
- rc = find_psb_table();
- if (rc)
- return rc;
+ for (i=0; i<NR_CPUS; i++) {
+ if (!cpu_online(i))
+ continue;
+ if (check_supported_cpu(i))
+ supported_cpus++;
+ }
- if (pending_bit_stuck()) {
- printk(KERN_ERR PFX "powernowk8_init fail, change pending bit set\n");
- return -EIO;
+ if (supported_cpus == num_online_cpus()) {
+ printk(KERN_INFO PFX "Found %d AMD Athlon 64 / Opteron processors (" VERSION ")\n",
+ supported_cpus);
+ return cpufreq_register_driver(&cpufreq_amd64_driver);
}
- return cpufreq_register_driver(&cpufreq_amd64_driver);
+ return -ENODEV;
}
/* driver entry point for term */
-static void __exit
-powernowk8_exit(void)
+static void __exit powernowk8_exit(void)
{
- dprintk(KERN_INFO PFX "powernowk8_exit\n");
+ dprintk(KERN_INFO PFX "exit\n");
cpufreq_unregister_driver(&cpufreq_amd64_driver);
}
@@ -880,5 +1066,5 @@
MODULE_DESCRIPTION("AMD Athlon 64 and Opteron processor frequency driver.");
MODULE_LICENSE("GPL");
-module_init(powernowk8_init);
+late_initcall(powernowk8_init);
module_exit(powernowk8_exit);
--- clean/arch/i386/kernel/cpu/cpufreq/powernow-k8.h 2004-02-20 12:29:10.000000000 +0100
+++ linux-pn/arch/i386/kernel/cpu/cpufreq/powernow-k8.h 2004-03-26 12:44:27.000000000 +0100
@@ -1,20 +1,46 @@
/*
- * (c) 2003 Advanced Micro Devices, Inc.
+ * (c) 2003, 2004 Advanced Micro Devices, Inc.
* Your use of this code is subject to the terms and conditions of the
- * GNU general public license version 2. See "../../../COPYING" or
+ * GNU general public license version 2. See "COPYING" or
* http://www.gnu.org/licenses/gpl.html
*/
+struct powernow_k8_data {
+ unsigned int cpu;
+
+ u32 numps; /* number of p-states */
+ u32 batps; /* number of p-states supported on battery */
+
+ /* these values are constant when the PSB is used to determine
+ * vid/fid pairings, but are modified during the ->target() call
+ * when ACPI is used */
+ u32 rvo; /* ramp voltage offset */
+ u32 irt; /* isochronous relief time */
+ u32 vidmvs; /* usable value calculated from mvs */
+ u32 vstable; /* voltage stabilization time, units 20 us */
+ u32 plllock; /* pll lock time, units 1 us */
+
+ /* keep track of the current fid / vid */
+ u32 currvid;
+ u32 currfid;
+
+ /* the powernow_table includes all frequency and vid/fid pairings:
+ * fid are the lower 8 bits of the index, vid are the upper 8 bits.
+ * frequency is in kHz */
+ struct cpufreq_frequency_table *powernow_table;
+
+#ifdef CONFIG_X86_POWERNOW_K8_ACPI
+ /* the acpi table needs to be kept. it's only available if ACPI was
+ * used to determine valid frequency/vid/fid states */
+ struct acpi_processor_performance acpi_data;
+#endif
+};
+
/* processor's cpuid instruction support */
-#define CPUID_PROCESSOR_SIGNATURE 1 /* function 1 */
-#define CPUID_F1_FAM 0x00000f00 /* family mask */
-#define CPUID_F1_XFAM 0x0ff00000 /* extended family mask */
-#define CPUID_F1_MOD 0x000000f0 /* model mask */
-#define CPUID_F1_STEP 0x0000000f /* stepping level mask */
-#define CPUID_XFAM_MOD 0x0ff00ff0 /* xtended fam, fam + model */
-#define ATHLON64_XFAM_MOD 0x00000f40 /* xtended fam, fam + model */
-#define OPTERON_XFAM_MOD 0x00000f50 /* xtended fam, fam + model */
-#define ATHLON64_REV_C0 8
+#define CPUID_PROCESSOR_SIGNATURE 1 /* function 1 */
+#define CPUID_XFAM_MOD 0x0ff00ff0 /* extended fam, fam + model */
+#define ATHLON64_XFAM_MOD 0x00000f40 /* extended fam, fam + model */
+#define OPTERON_XFAM_MOD 0x00000f50 /* extended fam, fam + model */
#define CPUID_GET_MAX_CAPABILITIES 0x80000000
#define CPUID_FREQ_VOLT_CAPABILITIES 0x80000007
#define P_STATE_TRANSITION_CAPABLE 6
@@ -23,7 +49,6 @@
/* writes (wrmsr - opcode 0f 30), the register number is placed in ecx, and */
/* the value to write is placed in edx:eax. For reads (rdmsr - opcode 0f 32), */
/* the register number is placed in ecx, and the data is returned in edx:eax. */
-
#define MSR_FIDVID_CTL 0xc0010041
#define MSR_FIDVID_STATUS 0xc0010042
@@ -47,10 +72,24 @@
#define MSR_S_HI_MAX_WORKING_VID 0x001f0000
#define MSR_S_HI_START_VID 0x00001f00
#define MSR_S_HI_CURRENT_VID 0x0000001f
+#define MSR_C_HI_STP_GNT_BENIGN 0x00000001
+
+/*
+ There are restrictions frequencies have to follow:
+ - only 1 entry in the low fid table ( <=1.4GHz )
+ - lowest entry in the high fid table must be >= 2 * the
+ entry in the low fid table
+ - lowest entry in the high fid table must be a <= 200MHz +
+ 2 * the entry in the low fid table
+ - the parts can only step at 200 MHz intervals, so 1.9 GHz is
+ never valid
+ - lowest frequency must be >= interprocessor hypertransport link
+ speed (only applies to MP systems obviously)
+ */
/* fids (frequency identifiers) are arranged in 2 tables - lo and hi */
-#define LO_FID_TABLE_TOP 6
-#define HI_FID_TABLE_BOTTOM 8
+#define LO_FID_TABLE_TOP 6 /* fid values marking the boundary */
+#define HI_FID_TABLE_BOTTOM 8 /* between the low and high tables */
#define LO_VCOFREQ_TABLE_TOP 1400 /* corresponding vco frequency values */
#define HI_VCOFREQ_TABLE_BOTTOM 1600
@@ -58,33 +97,44 @@
#define MIN_FREQ_RESOLUTION 200 /* fids jump by 2 matching freq jumps by 200 */
#define MAX_FID 0x2a /* Spec only gives FID values as far as 5 GHz */
-
#define LEAST_VID 0x1e /* Lowest (numerically highest) useful vid value */
-
#define MIN_FREQ 800 /* Min and max freqs, per spec */
#define MAX_FREQ 5000
-#define INVALID_FID_MASK 0xffffffc1 /* not a valid fid if these bits are set */
-
-#define INVALID_VID_MASK 0xffffffe0 /* not a valid vid if these bits are set */
+#define INVALID_FID_MASK 0xffffffc1 /* not a valid fid if these bits are set */
+#define INVALID_VID_MASK 0xffffffe0 /* not a valid vid if these bits are set */
#define STOP_GRANT_5NS 1 /* min poss memory access latency for voltage change */
-
#define PLL_LOCK_CONVERSION (1000/5) /* ms to ns, then divide by clock period */
-
#define MAXIMUM_VID_STEPS 1 /* Current cpus only allow a single step of 25mV */
+#define VST_UNITS_20US 20 /* Voltage Stabilization Time is in units of 20us */
-#define VST_UNITS_20US 20 /* Voltage Stabalization Time is in units of 20us */
+/*
+ * Most values of interest are enocoded in a single field of the _PSS
+ * entries: the "control" value.
+ */
+#define IRT_SHIFT 30
+#define RVO_SHIFT 28
+#define PLL_L_SHIFT 20
+#define MVS_SHIFT 18
+#define VST_SHIFT 11
+#define VID_SHIFT 6
+#define IRT_MASK 3
+#define RVO_MASK 3
+#define PLL_L_MASK 0x7f
+#define MVS_MASK 3
+#define VST_MASK 0x7f
+#define VID_MASK 0x1f
+#define FID_MASK 0x3f
+
/*
-Version 1.4 of the PSB table. This table is constructed by BIOS and is
-to tell the OS's power management driver which VIDs and FIDs are
-supported by this particular processor. This information is obtained from
-the data sheets for each processor model by the system vendor and
-incorporated into the BIOS.
-If the data in the PSB / PST is wrong, then this driver will program the
-wrong values into hardware, which is very likely to lead to a crash.
-*/
+ * Version 1.4 of the PSB table. This table is constructed by BIOS and is
+ * to tell the OS's power management driver which VIDs and FIDs are
+ * supported by this particular processor.
+ * If the data in the PSB / PST is wrong, then this driver will program the
+ * wrong values into hardware, which is very likely to lead to a crash.
+ */
#define PSB_ID_STRING "AMDK7PNOW!"
#define PSB_ID_STRING_LEN 10
@@ -117,6 +167,7 @@
#define dprintk(msg...) do { } while(0)
#endif
-static inline int core_voltage_pre_transition(u32 reqvid);
-static inline int core_voltage_post_transition(u32 reqvid);
-static inline int core_frequency_transition(u32 reqfid);
+static inline int core_voltage_pre_transition(struct powernow_k8_data *data, u32 reqvid);
+static inline int core_voltage_post_transition(struct powernow_k8_data *data, u32 reqvid);
+static inline int core_frequency_transition(struct powernow_k8_data *data, u32 reqfid);
+static inline void powernow_k8_acpi_pst_values(struct powernow_k8_data *data, unsigned int index);
--
When do you have a heart between your knees?
[Johanka's followup: and *two* hearts?]
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: powernow-k8: support acpi
2004-03-26 12:29 ` Pavel Machek
(?)
@ 2004-03-27 0:36 ` Redeeman
-1 siblings, 0 replies; 21+ messages in thread
From: Redeeman @ 2004-03-27 0:36 UTC (permalink / raw)
To: kernel list
this is great, thanks
On Fri, 2004-03-26 at 13:29, Pavel Machek wrote:
> Hi!
>
> > Other than that, I'd strongly appreciate pushing the change "upstream" to
> > Dave Jones / Andrew Morton / Linus Torvalds
>
> Here it is.
>
> This is new version of powernow-k8 driver. It adds SMP support, and
> support for getting tables through ACPI. (ACPI support is really
> important, because many machines have broken "legacy" tables). Please
> apply,
>
> Pavel
>
> --- clean/arch/i386/kernel/cpu/cpufreq/powernow-k8.c 2004-03-11 18:10:38.000000000 +0100
> +++ linux-pn/arch/i386/kernel/cpu/cpufreq/powernow-k8.c 2004-03-26 13:23:08.000000000 +0100
> @@ -1,22 +1,24 @@
> /*
> - * (c) 2003 Advanced Micro Devices, Inc.
> + * (c) 2003, 2004 Advanced Micro Devices, Inc.
> * Your use of this code is subject to the terms and conditions of the
> - * GNU general public license version 2. See "../../../COPYING" or
> + * GNU general public license version 2. See "COPYING" or
> * http://www.gnu.org/licenses/gpl.html
> *
> * Support : paul.devriendt@amd.com
> *
> * Based on the powernow-k7.c module written by Dave Jones.
> - * (C) 2003 Dave Jones <davej@codemonkey.ork.uk> on behalf of SuSE Labs
> + * (C) 2003 Dave Jones <davej@codemonkey.org.uk> on behalf of SuSE Labs
> * (C) 2004 Dominik Brodowski <linux@brodo.de>
> * (C) 2004 Pavel Machek <pavel@suse.cz>
> * Licensed under the terms of the GNU GPL License version 2.
> * Based upon datasheets & sample CPUs kindly provided by AMD.
> *
> + * Valuable input gratefully received from Dave Jones, Pavel Machek, Dominik
> + * Brodowski, and others.
> + *
> * Processor information obtained from Chapter 9 (Power and Thermal Management)
> * of the "BIOS and Kernel Developer's Guide for the AMD Athlon 64 and AMD
> - * Opteron Processors", revision 3.03, available for download from www.amd.com
> - *
> + * Opteron Processors" available for download from www.amd.com
> */
>
> #include <linux/kernel.h>
> @@ -31,55 +33,47 @@
> #include <asm/io.h>
> #include <asm/delay.h>
>
> +#ifdef CONFIG_ACPI
> +#define CONFIG_X86_POWERNOW_K8_ACPI
> +#endif
> +#ifdef CONFIG_X86_POWERNOW_K8_ACPI
> +
> +#include <linux/acpi.h>
> +#include <acpi/processor.h>
> +
> +#endif /* CONFIG_X86_POWERNOW_K8_ACPI */
> +
> +
> #define PFX "powernow-k8: "
> -#define BFX PFX "BIOS error: "
> -#define VERSION "version 1.00.08a"
> +#define VERSION "version 1.20.08b - March 20, 2004"
> #include "powernow-k8.h"
>
> -static u32 vstable; /* voltage stabalization time, from PSB, units 20 us */
> -static u32 plllock; /* pll lock time, from PSB, units 1 us */
> -static u32 numps; /* number of p-states, from PSB */
> -static u32 rvo; /* ramp voltage offset, from PSB */
> -static u32 irt; /* isochronous relief time, from PSB */
> -static u32 vidmvs; /* usable value calculated from mvs, from PSB */
> -static u32 currvid; /* keep track of the current fid / vid */
> -static u32 currfid;
> +/* serialize freq changes */
> +static DECLARE_MUTEX(fidvid_sem);
>
> -static struct cpufreq_frequency_table *powernow_table;
> +static struct powernow_k8_data *powernow_data[NR_CPUS];
>
> -/*
> -The PSB table supplied by BIOS allows for the definition of the number of
> -p-states that can be used when running on a/c, and the number of p-states
> -that can be used when running on battery. This allows laptop manufacturers
> -to force the system to save power when running from battery. The relationship
> -is :
> - 1 <= number_of_battery_p_states <= maximum_number_of_p_states
> -
> -This driver does NOT have the support in it to detect transitions from
> -a/c power to battery power, and thus trigger the transition to a lower
> -p-state if required. This is because I need ACPI and the 2.6 kernel to do
> -this, and this is a 2.4 kernel driver. Check back for a new improved driver
> -for the 2.6 kernel soon.
> -
> -This code therefore assumes it is on battery at all times, and thus
> -restricts performance to number_of_battery_p_states. For desktops,
> - number_of_battery_p_states == maximum_number_of_pstates,
> -so this is not actually a restriction.
> -*/
>
> -static u32 batps; /* limit on the number of p states when on battery */
> - /* - set by BIOS in the PSB/PST */
> +/* Return a frequency in MHz, given an input fid */
> +static inline u32 find_freq_from_fid(u32 fid)
> +{
> + return 800 + (fid * 100);
> +}
>
> - /* Return a frequency in MHz, given an input fid */
> -static u32 find_freq_from_fid(u32 fid)
> +/* Return a frequency in KHz, given an input fid */
> +static inline u32 find_khz_freq_from_fid(u32 fid)
> {
> - return 800 + (fid * 100);
> + return 1000 * find_freq_from_fid(fid);
> }
>
> +/* Return a voltage in miliVolts, given an input vid */
> +static inline u32 find_milivolts_from_vid(struct powernow_k8_data *data, u32 vid)
> +{
> + return 1550-vid*25;
> +}
>
> /* Return the vco fid for an input fid */
> -static u32
> -convert_fid_to_vco_fid(u32 fid)
> +static u32 convert_fid_to_vco_fid(u32 fid)
> {
> if (fid < HI_FID_TABLE_BOTTOM) {
> return 8 + (2 * fid);
> @@ -89,11 +83,10 @@
> }
>
> /*
> - * Return 1 if the pending bit is set. Unless we are actually just told the
> - * processor to transition a state, seeing this bit set is really bad news.
> + * Return 1 if the pending bit is set. Unless we just instructed the processor
> + * to transition to a new state, seeing this bit set is really bad news.
> */
> -static inline int
> -pending_bit_stuck(void)
> +static inline int pending_bit_stuck(void)
> {
> u32 lo, hi;
>
> @@ -102,11 +95,10 @@
> }
>
> /*
> - * Update the global current fid / vid values from the status msr. Returns 1
> - * on error.
> + * Update the global current fid / vid values from the status msr. Returns
> + * 1 on error.
> */
> -static int
> -query_current_values_with_pending_wait(void)
> +static int query_current_values_with_pending_wait(struct powernow_k8_data *data)
> {
> u32 lo, hi;
> u32 i = 0;
> @@ -120,63 +112,69 @@
> rdmsr(MSR_FIDVID_STATUS, lo, hi);
> }
>
> - currvid = hi & MSR_S_HI_CURRENT_VID;
> - currfid = lo & MSR_S_LO_CURRENT_FID;
> + data->currvid = hi & MSR_S_HI_CURRENT_VID;
> + data->currfid = lo & MSR_S_LO_CURRENT_FID;
>
> return 0;
> }
>
> /* the isochronous relief time */
> -static inline void
> -count_off_irt(void)
> +static inline void count_off_irt(struct powernow_k8_data *data)
> {
> - udelay((1 << irt) * 10);
> + udelay((1 << data->irt) * 10);
> return;
> }
>
> -/* the voltage stabalization time */
> -static inline void
> -count_off_vst(void)
> +/* the voltage stabilization time */
> +static inline void count_off_vst(struct powernow_k8_data *data)
> {
> - udelay(vstable * VST_UNITS_20US);
> + udelay(data->vstable * VST_UNITS_20US);
> return;
> }
>
> +/* need to init the control msr to a safe value (for each cpu) */
> +static void fidvid_msr_init(void)
> +{
> + u32 lo, hi;
> + u8 fid, vid;
> +
> + rdmsr(MSR_FIDVID_STATUS, lo, hi);
> + vid = hi & MSR_S_HI_CURRENT_VID;
> + fid = lo & MSR_S_LO_CURRENT_FID;
> + lo = fid | (vid << MSR_C_LO_VID_SHIFT);
> + hi = MSR_C_HI_STP_GNT_BENIGN;
> + dprintk(PFX "cpu%d, init lo %x, hi %x\n", smp_processor_id(), lo, hi);
> + wrmsr(MSR_FIDVID_CTL, lo, hi);
> +}
> +
> /* write the new fid value along with the other control fields to the msr */
> -static int
> -write_new_fid(u32 fid)
> +static int write_new_fid(struct powernow_k8_data *data, u32 fid)
> {
> u32 lo;
> - u32 savevid = currvid;
> + u32 savevid = data->currvid;
>
> - if ((fid & INVALID_FID_MASK) || (currvid & INVALID_VID_MASK)) {
> + if ((fid & INVALID_FID_MASK) || (data->currvid & INVALID_VID_MASK)) {
> printk(KERN_ERR PFX "internal error - overflow on fid write\n");
> return 1;
> }
>
> - lo = fid | (currvid << MSR_C_LO_VID_SHIFT) | MSR_C_LO_INIT_FID_VID;
> -
> + lo = fid | (data->currvid << MSR_C_LO_VID_SHIFT) | MSR_C_LO_INIT_FID_VID;
> dprintk(KERN_DEBUG PFX "writing fid %x, lo %x, hi %x\n",
> - fid, lo, plllock * PLL_LOCK_CONVERSION);
> -
> - wrmsr(MSR_FIDVID_CTL, lo, plllock * PLL_LOCK_CONVERSION);
> -
> - if (query_current_values_with_pending_wait())
> + fid, lo, data->plllock * PLL_LOCK_CONVERSION);
> + wrmsr(MSR_FIDVID_CTL, lo, data->plllock * PLL_LOCK_CONVERSION);
> + if (query_current_values_with_pending_wait(data))
> return 1;
> + count_off_irt(data);
>
> - count_off_irt();
> -
> - if (savevid != currvid) {
> - printk(KERN_ERR PFX
> - "vid changed on fid transition, save %x, currvid %x\n",
> - savevid, currvid);
> + if (savevid != data->currvid) {
> + printk(KERN_ERR PFX "vid change on fid trans, old %x, new %x\n",
> + savevid, data->currvid);
> return 1;
> }
>
> - if (fid != currfid) {
> - printk(KERN_ERR PFX
> - "fid transition failed, fid %x, currfid %x\n",
> - fid, currfid);
> + if (fid != data->currfid) {
> + printk(KERN_ERR PFX "fid trans failed, fid %x, curr %x\n", fid,
> + data->currfid);
> return 1;
> }
>
> @@ -184,40 +182,33 @@
> }
>
> /* Write a new vid to the hardware */
> -static int
> -write_new_vid(u32 vid)
> +static int write_new_vid(struct powernow_k8_data *data, u32 vid)
> {
> u32 lo;
> - u32 savefid = currfid;
> + u32 savefid = data->currfid;
>
> - if ((currfid & INVALID_FID_MASK) || (vid & INVALID_VID_MASK)) {
> + if ((data->currfid & INVALID_FID_MASK) || (vid & INVALID_VID_MASK)) {
> printk(KERN_ERR PFX "internal error - overflow on vid write\n");
> return 1;
> }
>
> - lo = currfid | (vid << MSR_C_LO_VID_SHIFT) | MSR_C_LO_INIT_FID_VID;
> -
> + lo = data->currfid | (vid << MSR_C_LO_VID_SHIFT) | MSR_C_LO_INIT_FID_VID;
> dprintk(KERN_DEBUG PFX "writing vid %x, lo %x, hi %x\n",
> vid, lo, STOP_GRANT_5NS);
> -
> wrmsr(MSR_FIDVID_CTL, lo, STOP_GRANT_5NS);
> -
> - if (query_current_values_with_pending_wait()) {
> + if (query_current_values_with_pending_wait(data))
> return 1;
> - }
>
> - if (savefid != currfid) {
> - printk(KERN_ERR PFX
> - "fid changed on vid transition, save %x currfid %x\n",
> - savefid, currfid);
> + if (savefid != data->currfid) {
> + printk(KERN_ERR PFX "fid changed on vid trans, old %x new %x\n",
> + savefid, data->currfid);
> return 1;
> }
>
> - if (vid != currvid) {
> - printk(KERN_ERR PFX
> - "vid transition failed, vid %x, currvid %x\n",
> - vid, currvid);
> - return 1;
> + if (vid != data->currvid) {
> + printk(KERN_ERR PFX "vid trans failed, vid %x, curr %x\n", vid,
> + data->currvid);
> + return 1;
> }
>
> return 0;
> @@ -228,300 +219,279 @@
> * Decreasing vid codes represent increasing voltages:
> * vid of 0 is 1.550V, vid of 0x1e is 0.800V, vid of 0x1f is off.
> */
> -static int
> -decrease_vid_code_by_step(u32 reqvid, u32 step)
> +static int decrease_vid_code_by_step(struct powernow_k8_data *data, u32 reqvid, u32 step)
> {
> - if ((currvid - reqvid) > step)
> - reqvid = currvid - step;
> -
> - if (write_new_vid(reqvid))
> + if ((data->currvid - reqvid) > step)
> + reqvid = data->currvid - step;
> + if (write_new_vid(data, reqvid))
> return 1;
> -
> - count_off_vst();
> -
> + count_off_vst(data);
> return 0;
> }
>
> /* Change the fid and vid, by the 3 phases. */
> -static inline int
> -transition_fid_vid(u32 reqfid, u32 reqvid)
> +static inline int transition_fid_vid(struct powernow_k8_data *data, u32 reqfid, u32 reqvid)
> {
> - if (core_voltage_pre_transition(reqvid))
> + if (core_voltage_pre_transition(data, reqvid))
> return 1;
> -
> - if (core_frequency_transition(reqfid))
> + if (core_frequency_transition(data, reqfid))
> return 1;
> -
> - if (core_voltage_post_transition(reqvid))
> + if (core_voltage_post_transition(data, reqvid))
> return 1;
> -
> - if (query_current_values_with_pending_wait())
> + if (query_current_values_with_pending_wait(data))
> return 1;
>
> - if ((reqfid != currfid) || (reqvid != currvid)) {
> - printk(KERN_ERR PFX "failed: req 0x%x 0x%x, curr 0x%x 0x%x\n",
> - reqfid, reqvid, currfid, currvid);
> + if ((reqfid != data->currfid) || (reqvid != data->currvid)) {
> + printk(KERN_ERR PFX "failed (cpu%d): req %x %x, curr %x %x\n",
> + smp_processor_id(),
> + reqfid, reqvid, data->currfid, data->currvid);
> return 1;
> }
>
> - dprintk(KERN_INFO PFX
> - "transitioned: new fid 0x%x, vid 0x%x\n", currfid, currvid);
> -
> + dprintk(KERN_INFO PFX "transitioned (cpu%d): new fid %x, vid %x\n",
> + smp_processor_id(), data->currfid, data->currvid);
> return 0;
> }
>
> -/*
> - * Phase 1 - core voltage transition ... setup appropriate voltage for the
> - * fid transition.
> - */
> -static inline int
> -core_voltage_pre_transition(u32 reqvid)
> +/* Phase 1 - core voltage transition ... setup voltage */
> +static inline int core_voltage_pre_transition(struct powernow_k8_data *data, u32 reqvid)
> {
> - u32 rvosteps = rvo;
> - u32 savefid = currfid;
> + u32 rvosteps = data->rvo;
> + u32 savefid = data->currfid;
>
> dprintk(KERN_DEBUG PFX
> - "ph1: start, currfid 0x%x, currvid 0x%x, reqvid 0x%x, rvo %x\n",
> - currfid, currvid, reqvid, rvo);
> -
> - while (currvid > reqvid) {
> - dprintk(KERN_DEBUG PFX "ph1: curr 0x%x, requesting vid 0x%x\n",
> - currvid, reqvid);
> - if (decrease_vid_code_by_step(reqvid, vidmvs))
> + "ph1 (cpu%d): start, currfid %x, currvid %x, reqvid %x, rvo %x\n",
> + smp_processor_id(),
> + data->currfid, data->currvid, reqvid, data->rvo);
> +
> + while (data->currvid > reqvid) {
> + dprintk(KERN_DEBUG PFX "ph1: curr %x, req vid %x\n",
> + data->currvid, reqvid);
> + if (decrease_vid_code_by_step(data, reqvid, data->vidmvs))
> return 1;
> }
>
> while (rvosteps > 0) {
> - if (currvid == 0) {
> + if (data->currvid == 0) {
> rvosteps = 0;
> } else {
> dprintk(KERN_DEBUG PFX
> - "ph1: changing vid for rvo, requesting 0x%x\n",
> - currvid - 1);
> - if (decrease_vid_code_by_step(currvid - 1, 1))
> + "ph1: changing vid for rvo, req %x\n",
> + data->currvid - 1);
> + if (decrease_vid_code_by_step(data, data->currvid - 1, 1))
> return 1;
> rvosteps--;
> }
> }
>
> - if (query_current_values_with_pending_wait())
> + if (query_current_values_with_pending_wait(data))
> return 1;
>
> - if (savefid != currfid) {
> - printk(KERN_ERR PFX "ph1 err, currfid changed 0x%x\n", currfid);
> + if (savefid != data->currfid) {
> + printk(KERN_ERR PFX "ph1: err, currfid changed %x\n", data->currfid);
> return 1;
> }
>
> - dprintk(KERN_DEBUG PFX "ph1 complete, currfid 0x%x, currvid 0x%x\n",
> - currfid, currvid);
> + dprintk(KERN_DEBUG PFX "ph1: complete, currfid %x, currvid %x\n",
> + data->currfid, data->currvid);
>
> return 0;
> }
>
> /* Phase 2 - core frequency transition */
> -static inline int
> -core_frequency_transition(u32 reqfid)
> +static inline int core_frequency_transition(struct powernow_k8_data *data, u32 reqfid)
> {
> u32 vcoreqfid;
> u32 vcocurrfid;
> u32 vcofiddiff;
> - u32 savevid = currvid;
> + u32 savevid = data->currvid;
>
> - if ((reqfid < HI_FID_TABLE_BOTTOM) && (currfid < HI_FID_TABLE_BOTTOM)) {
> - printk(KERN_ERR PFX "ph2 illegal lo-lo transition 0x%x 0x%x\n",
> - reqfid, currfid);
> + if ((reqfid < HI_FID_TABLE_BOTTOM) && (data->currfid < HI_FID_TABLE_BOTTOM)) {
> + printk(KERN_ERR PFX "ph2: illegal lo-lo transition %x %x\n",
> + reqfid, data->currfid);
> return 1;
> }
>
> - if (currfid == reqfid) {
> - printk(KERN_ERR PFX "ph2 null fid transition 0x%x\n", currfid);
> + if (data->currfid == reqfid) {
> + printk(KERN_ERR PFX "ph2: null fid transition %x\n", data->currfid);
> return 0;
> }
>
> dprintk(KERN_DEBUG PFX
> - "ph2 starting, currfid 0x%x, currvid 0x%x, reqfid 0x%x\n",
> - currfid, currvid, reqfid);
> + "ph2 (cpu%d): starting, currfid %x, currvid %x, reqfid %x\n",
> + smp_processor_id(),
> + data->currfid, data->currvid, reqfid);
>
> vcoreqfid = convert_fid_to_vco_fid(reqfid);
> - vcocurrfid = convert_fid_to_vco_fid(currfid);
> + vcocurrfid = convert_fid_to_vco_fid(data->currfid);
> vcofiddiff = vcocurrfid > vcoreqfid ? vcocurrfid - vcoreqfid
> : vcoreqfid - vcocurrfid;
>
> while (vcofiddiff > 2) {
> - if (reqfid > currfid) {
> - if (currfid > LO_FID_TABLE_TOP) {
> - if (write_new_fid(currfid + 2)) {
> + if (reqfid > data->currfid) {
> + if (data->currfid > LO_FID_TABLE_TOP) {
> + if (write_new_fid(data, data->currfid + 2)) {
> return 1;
> }
> } else {
> if (write_new_fid
> - (2 + convert_fid_to_vco_fid(currfid))) {
> + (data, 2 + convert_fid_to_vco_fid(data->currfid))) {
> return 1;
> }
> }
> } else {
> - if (write_new_fid(currfid - 2))
> + if (write_new_fid(data, data->currfid - 2))
> return 1;
> }
>
> - vcocurrfid = convert_fid_to_vco_fid(currfid);
> + vcocurrfid = convert_fid_to_vco_fid(data->currfid);
> vcofiddiff = vcocurrfid > vcoreqfid ? vcocurrfid - vcoreqfid
> : vcoreqfid - vcocurrfid;
> }
>
> - if (write_new_fid(reqfid))
> + if (write_new_fid(data, reqfid))
> return 1;
> -
> - if (query_current_values_with_pending_wait())
> + if (query_current_values_with_pending_wait(data))
> return 1;
>
> - if (currfid != reqfid) {
> + if (data->currfid != reqfid) {
> printk(KERN_ERR PFX
> - "ph2 mismatch, failed fid transition, curr %x, req %x\n",
> - currfid, reqfid);
> + "ph2: mismatch, failed fid transition, curr %x, req %x\n",
> + data->currfid, reqfid);
> return 1;
> }
>
> - if (savevid != currvid) {
> - printk(KERN_ERR PFX
> - "ph2 vid changed, save %x, curr %x\n", savevid,
> - currvid);
> + if (savevid != data->currvid) {
> + printk(KERN_ERR PFX "ph2: vid changed, save %x, curr %x\n",
> + savevid, data->currvid);
> return 1;
> }
>
> - dprintk(KERN_DEBUG PFX "ph2 complete, currfid 0x%x, currvid 0x%x\n",
> - currfid, currvid);
> + dprintk(KERN_DEBUG PFX "ph2: complete, currfid %x, currvid %x\n",
> + data->currfid, data->currvid);
>
> return 0;
> }
>
> /* Phase 3 - core voltage transition flow ... jump to the final vid. */
> -static inline int
> -core_voltage_post_transition(u32 reqvid)
> +static inline int core_voltage_post_transition(struct powernow_k8_data *data, u32 reqvid)
> {
> - u32 savefid = currfid;
> + u32 savefid = data->currfid;
> u32 savereqvid = reqvid;
>
> - dprintk(KERN_DEBUG PFX "ph3 starting, currfid 0x%x, currvid 0x%x\n",
> - currfid, currvid);
> + dprintk(KERN_DEBUG PFX "ph3 (cpu%d): starting, currfid %x, currvid %x\n",
> + smp_processor_id(),
> + data->currfid, data->currvid);
>
> - if (reqvid != currvid) {
> - if (write_new_vid(reqvid))
> + if (reqvid != data->currvid) {
> + if (write_new_vid(data, reqvid))
> return 1;
>
> - if (savefid != currfid) {
> + if (savefid != data->currfid) {
> printk(KERN_ERR PFX
> "ph3: bad fid change, save %x, curr %x\n",
> - savefid, currfid);
> + savefid, data->currfid);
> return 1;
> }
>
> - if (currvid != reqvid) {
> + if (data->currvid != reqvid) {
> printk(KERN_ERR PFX
> "ph3: failed vid transition\n, req %x, curr %x",
> - reqvid, currvid);
> + reqvid, data->currvid);
> return 1;
> }
> }
>
> - if (query_current_values_with_pending_wait())
> + if (query_current_values_with_pending_wait(data))
> return 1;
>
> - if (savereqvid != currvid) {
> - dprintk(KERN_ERR PFX "ph3 failed, currvid 0x%x\n", currvid);
> + if (savereqvid != data->currvid) {
> + printk(KERN_ERR PFX "ph3: failed, currvid %x\n", data->currvid);
> return 1;
> }
>
> - if (savefid != currfid) {
> - dprintk(KERN_ERR PFX "ph3 failed, currfid changed 0x%x\n",
> - currfid);
> + if (savefid != data->currfid) {
> + printk(KERN_ERR PFX "ph3: failed, currfid changed %x\n",
> + data->currfid);
> return 1;
> }
>
> - dprintk(KERN_DEBUG PFX "ph3 complete, currfid 0x%x, currvid 0x%x\n",
> - currfid, currvid);
> -
> + dprintk(KERN_DEBUG PFX "ph3: complete, currfid %x, currvid %x\n",
> + data->currfid, data->currvid);
> return 0;
> }
>
> -static inline int
> -check_supported_cpu(void)
> +static inline int check_supported_cpu(unsigned int cpu)
> {
> - struct cpuinfo_x86 *c = cpu_data;
> + cpumask_t oldmask = CPU_MASK_ALL;
> u32 eax, ebx, ecx, edx;
> + unsigned int rc = 0;
>
> - if (num_online_cpus() != 1) {
> - printk(KERN_INFO PFX "multiprocessor systems not supported\n");
> - return 0;
> - }
> + oldmask = current->cpus_allowed;
> + set_cpus_allowed(current, cpumask_of_cpu(cpu));
> + schedule();
>
> - if (c->x86_vendor != X86_VENDOR_AMD) {
> -#ifdef MODULE
> - printk(KERN_INFO PFX "Not an AMD processor\n");
> -#endif
> - return 0;
> + if (smp_processor_id() != cpu) {
> + printk(KERN_ERR "limiting to cpu %u failed\n", cpu);
> + goto out;
> }
>
> + if (current_cpu_data.x86_vendor != X86_VENDOR_AMD)
> + goto out;
> +
> eax = cpuid_eax(CPUID_PROCESSOR_SIGNATURE);
> if ((eax & CPUID_XFAM_MOD) == ATHLON64_XFAM_MOD) {
> dprintk(KERN_DEBUG PFX "AMD Althon 64 Processor found\n");
> - if ((eax & CPUID_F1_STEP) < ATHLON64_REV_C0) {
> - printk(KERN_INFO PFX "Revision C0 or better "
> - "AMD Athlon 64 processor required\n");
> - return 0;
> - }
> } else if ((eax & CPUID_XFAM_MOD) == OPTERON_XFAM_MOD) {
> dprintk(KERN_DEBUG PFX "AMD Opteron Processor found\n");
> } else {
> printk(KERN_INFO PFX
> "AMD Athlon 64 or AMD Opteron processor required\n");
> - return 0;
> + goto out;
> }
>
> eax = cpuid_eax(CPUID_GET_MAX_CAPABILITIES);
> if (eax < CPUID_FREQ_VOLT_CAPABILITIES) {
> - printk(KERN_INFO PFX
> - "No frequency change capabilities detected\n");
> - return 0;
> + printk(KERN_INFO PFX "No freq change capabilities\n");
> + goto out;
> }
>
> cpuid(CPUID_FREQ_VOLT_CAPABILITIES, &eax, &ebx, &ecx, &edx);
> if ((edx & P_STATE_TRANSITION_CAPABLE) != P_STATE_TRANSITION_CAPABLE) {
> printk(KERN_INFO PFX "Power state transitions not supported\n");
> - return 0;
> + goto out;
> }
>
> - printk(KERN_INFO PFX "Found AMD64 processor supporting PowerNow (" VERSION ")\n");
> - return 1;
> + rc = 1;
> +
> + out:
> + set_cpus_allowed(current, oldmask);
> + schedule();
> + return rc;
> }
>
> -static int check_pst_table(struct pst_s *pst, u8 maxvid)
> +static int check_pst_table(struct powernow_k8_data *data, struct pst_s *pst, u8 maxvid)
> {
> unsigned int j;
> - u8 lastfid = 0xFF;
> + u8 lastfid = 0xff;
>
> - for (j = 0; j < numps; j++) {
> + for (j = 0; j < data->numps; j++) {
> if (pst[j].vid > LEAST_VID) {
> - printk(KERN_ERR PFX "vid %d invalid : 0x%x\n", j, pst[j].vid);
> + printk(KERN_ERR PFX "vid %d bad: %x\n", j, pst[j].vid);
> return -EINVAL;
> }
> - if (pst[j].vid < rvo) { /* vid + rvo >= 0 */
> - printk(KERN_ERR PFX
> - "BIOS error - 0 vid exceeded with pstate %d\n",
> - j);
> + if (pst[j].vid < data->rvo) { /* vid + rvo >= 0 */
> + printk(KERN_ERR PFX "0 vid exceeded with pst %d\n", j);
> return -ENODEV;
> }
> - if (pst[j].vid < maxvid + rvo) { /* vid + rvo >= maxvid */
> - printk(KERN_ERR PFX
> - "BIOS error - maxvid exceeded with pstate %d\n",
> - j);
> + if (pst[j].vid < maxvid + data->rvo) { /* vid + rvo >= maxvid */
> + printk(KERN_ERR PFX "maxvid exceeded with pst %d\n", j);
> return -ENODEV;
> }
> if ((pst[j].fid > MAX_FID)
> || (pst[j].fid & 1)
> || (j && (pst[j].fid < HI_FID_TABLE_BOTTOM))) {
> - /* Only first fid is allowed to be in "low" range */
> - printk(KERN_ERR PFX "fid %d invalid : 0x%x\n", j, pst[j].fid);
> + printk(KERN_ERR PFX "fid %d bad: %x\n", j, pst[j].fid);
> return -EINVAL;
> }
> if (pst[j].fid < lastfid)
> @@ -531,20 +501,87 @@
> printk(KERN_ERR PFX "lastfid invalid\n");
> return -EINVAL;
> }
> - if (lastfid > LO_FID_TABLE_TOP) {
> - printk(KERN_INFO PFX "first fid not from lo freq table\n");
> + if (lastfid > LO_FID_TABLE_TOP)
> + printk(KERN_INFO PFX "first fid not from lo freq table\n");
> +
> + return 0;
> +}
> +
> +static void print_basics(struct powernow_k8_data *data)
> +{
> + int j;
> + for (j = 0; j < data->numps; j++) {
> + printk(KERN_INFO PFX " %d : fid %x (%d MHz), vid %x (%d mV)\n", j,
> + data->powernow_table[j].index & 0xff,
> + data->powernow_table[j].frequency/1000,
> + data->powernow_table[j].index >> 8,
> + find_milivolts_from_vid(data, data->powernow_table[j].index >> 8));
> + }
> + if (data->batps)
> + printk(KERN_INFO PFX "Only %d pstates on battery\n", data->batps);
> +}
> +
> +static inline int fill_powernow_table(struct powernow_k8_data *data, struct pst_s *pst, u8 maxvid)
> +{
> + struct cpufreq_frequency_table *powernow_table;
> + unsigned int j;
> +
> + if (data->batps) { /* use ACPI support to get full speed on mains power */
> + printk(KERN_WARNING PFX "Only %d pstates usable (use ACPI driver for full range\n", data->batps);
> + data->numps = data->batps;
> }
>
> + for ( j=1; j<data->numps; j++ )
> + if (pst[j-1].fid >= pst[j].fid) {
> + printk(KERN_ERR PFX "PST out of sequence\n");
> + return -EINVAL;
> + }
> +
> + if (data->numps < 2) {
> + printk(KERN_ERR PFX "no p states to transition\n");
> + return -ENODEV;
> + }
> +
> + if (check_pst_table(data, pst, maxvid))
> + return -EINVAL;
> +
> + powernow_table = kmalloc((sizeof(struct cpufreq_frequency_table)
> + * (data->numps + 1)), GFP_KERNEL);
> + if (!powernow_table) {
> + printk(KERN_ERR PFX "powernow_table memory alloc failure\n");
> + return -ENOMEM;
> + }
> +
> + for (j = 0; j < data->numps; j++) {
> + powernow_table[j].index = pst[j].fid; /* lower 8 bits */
> + powernow_table[j].index |= (pst[j].vid << 8); /* upper 8 bits */
> + powernow_table[j].frequency = find_khz_freq_from_fid(pst[j].fid);
> + }
> + powernow_table[data->numps].frequency = CPUFREQ_TABLE_END;
> + powernow_table[data->numps].index = 0;
> +
> + if (query_current_values_with_pending_wait(data)) {
> + kfree(powernow_table);
> + return -EIO;
> + }
> +
> + dprintk(KERN_INFO PFX "cfid %x, cvid %x\n", data->currfid, data->currvid);
> + data->powernow_table = powernow_table;
> + print_basics(data);
> +
> + for (j = 0; j < data->numps; j++)
> + if ((pst[j].fid==data->currfid) && (pst[j].vid==data->currvid))
> + return 0;
> +
> + dprintk(KERN_ERR PFX "currfid/vid do not match PST, ignoring\n");
> return 0;
> }
>
> /* Find and validate the PSB/PST table in BIOS. */
> -static inline int
> -find_psb_table(void)
> +static inline int find_psb_table(struct powernow_k8_data *data)
> {
> struct psb_s *psb;
> - struct pst_s *pst;
> - unsigned int i, j;
> + unsigned int i;
> u32 mvs;
> u8 maxvid;
>
> @@ -556,276 +593,427 @@
> if (memcmp(psb, PSB_ID_STRING, PSB_ID_STRING_LEN) != 0)
> continue;
>
> - dprintk(KERN_DEBUG PFX "found PSB header at 0x%p\n", psb);
> -
> - dprintk(KERN_DEBUG PFX "table vers: 0x%x\n", psb->tableversion);
> + dprintk(KERN_DEBUG PFX "found PSB header at %p\n", psb);
> + dprintk(KERN_DEBUG PFX "table version: %x\n",
> + psb->tableversion);
> if (psb->tableversion != PSB_VERSION_1_4) {
> - printk(KERN_INFO BFX "PSB table is not v1.4\n");
> + printk(KERN_INFO PFX "PSB table is not v1.4\n");
> return -ENODEV;
> }
>
> - dprintk(KERN_DEBUG PFX "flags: 0x%x\n", psb->flags1);
> + dprintk(KERN_DEBUG PFX "flags: %x\n", psb->flags1);
> if (psb->flags1) {
> - printk(KERN_ERR BFX "unknown flags\n");
> + printk(KERN_ERR PFX "unknown flags\n");
> return -ENODEV;
> }
>
> - vstable = psb->voltagestabilizationtime;
> - dprintk(KERN_DEBUG PFX "flags2: 0x%x\n", psb->flags2);
> - rvo = psb->flags2 & 3;
> - irt = ((psb->flags2) >> 2) & 3;
> + data->vstable = psb->voltagestabilizationtime;
> + dprintk(KERN_INFO PFX "voltage stabilization time: %d(*20us)\n", data->vstable);
> +
> + dprintk(KERN_DEBUG PFX "flags2: %x\n", psb->flags2);
> +
> + data->rvo = psb->flags2 & 3;
> + data->irt = ((psb->flags2) >> 2) & 3;
> mvs = ((psb->flags2) >> 4) & 3;
> - vidmvs = 1 << mvs;
> - batps = ((psb->flags2) >> 6) & 3;
> + data->vidmvs = 1 << mvs;
> + data->batps = ((psb->flags2) >> 6) & 3;
>
> - printk(KERN_INFO PFX "voltage stable in %d usec", vstable * 20);
> - if (batps)
> - printk(", only %d lowest states on battery", batps);
> - printk(", ramp voltage offset: %d", rvo);
> - printk(", isochronous relief time: %d", irt);
> - printk(", maximum voltage step: %d\n", mvs);
> + dprintk(KERN_INFO PFX "ramp voltage offset: %d\n", data->rvo);
> + dprintk(KERN_INFO PFX "isochronous relief time: %d\n", data->irt);
> + dprintk(KERN_INFO PFX "maximum voltage step: %d - %x\n",
> + mvs, data->vidmvs);
>
> dprintk(KERN_DEBUG PFX "numpst: 0x%x\n", psb->numpst);
> if (psb->numpst != 1) {
> - printk(KERN_ERR BFX "numpst must be 1\n");
> + printk(KERN_ERR PFX "numpst must be 1\n");
> return -ENODEV;
> }
>
> - dprintk(KERN_DEBUG PFX "cpuid: 0x%x\n", psb->cpuid);
> -
> - plllock = psb->plllocktime;
> - printk(KERN_INFO PFX "pll lock time: 0x%x, ", plllock);
> -
> + data->plllock = psb->plllocktime;
> + dprintk(KERN_INFO PFX "plllocktime: %x (units 1us)\n",
> + psb->plllocktime);
> + dprintk(KERN_INFO PFX "maxfid: %x\n", psb->maxfid);
> + dprintk(KERN_INFO PFX "maxvid: %x\n", psb->maxvid);
> maxvid = psb->maxvid;
> - printk("maxfid 0x%x (%d MHz), maxvid 0x%x\n",
> - psb->maxfid, find_freq_from_fid(psb->maxfid), maxvid);
>
> - numps = psb->numpstates;
> - if (numps < 2) {
> - printk(KERN_ERR BFX "no p states to transition\n");
> - return -ENODEV;
> - }
> + data->numps = psb->numpstates;
> + dprintk(KERN_INFO PFX "numpstates: %x\n", data->numps);
> + return fill_powernow_table(data, (struct pst_s *)(psb+1), maxvid);
> + }
>
> - if (batps == 0) {
> - batps = numps;
> - } else if (batps > numps) {
> - printk(KERN_ERR BFX "batterypstates > numpstates\n");
> - batps = numps;
> - } else {
> - printk(KERN_ERR PFX
> - "Restricting operation to %d p-states\n", batps);
> - printk(KERN_ERR PFX
> - "Check for an updated driver to access all "
> - "%d p-states\n", numps);
> - }
> + /*
> + * If you see this message, complain to BIOS manufacturer. If
> + * he tells you "we do not support Linux" or some similar
> + * nonsense, remember that Windows 2000 uses the same legacy
> + * mechanism that the old Linux PSB driver uses. Tell them it
> + * is broken with Windows 2000.
> + *
> + * The reference to the AMD documentation is chapter 9 in the
> + * BIOS and Kernel Developer's Guide, which is available on
> + * www.amd.com
> + */
> + printk(KERN_ERR PFX "BIOS error - no PSB\n");
> + return -ENODEV;
> +}
>
> - if (numps <= 1) {
> - printk(KERN_ERR PFX "only 1 p-state to transition\n");
> - return -ENODEV;
> - }
>
> - pst = (struct pst_s *) (psb + 1);
> - if (check_pst_table(pst, maxvid))
> - return -EINVAL;
> +#ifdef CONFIG_X86_POWERNOW_K8_ACPI
>
> - powernow_table = kmalloc((sizeof(struct cpufreq_frequency_table) * (numps + 1)), GFP_KERNEL);
> - if (!powernow_table) {
> - printk(KERN_ERR PFX "powernow_table memory alloc failure\n");
> - return -ENOMEM;
> - }
> +static inline void powernow_k8_acpi_pst_values(struct powernow_k8_data *data, unsigned int index)
> +{
> + if (!data->acpi_data.state_count)
> + return;
>
> - for (j = 0; j < psb->numpstates; j++) {
> - powernow_table[j].index = pst[j].fid; /* lower 8 bits */
> - powernow_table[j].index |= (pst[j].vid << 8); /* upper 8 bits */
> - }
> + data->irt = (data->acpi_data.states[index].control >> IRT_SHIFT) & IRT_MASK;
> + data->rvo = (data->acpi_data.states[index].control >> RVO_SHIFT) & RVO_MASK;
> + data->plllock = (data->acpi_data.states[index].control >> PLL_L_SHIFT) & PLL_L_MASK;
> + data->vidmvs = 1 << ((data->acpi_data.states[index].control >> MVS_SHIFT) & MVS_MASK);
> + data->vstable = (data->acpi_data.states[index].control >> VST_SHIFT) & VST_MASK;
> +}
>
> - /* If you want to override your frequency tables, this
> - is right place. */
> +static int powernow_k8_cpu_init_acpi(struct powernow_k8_data *data)
> +{
> + int i;
> + int cntlofreq = 0;
> + struct cpufreq_frequency_table *powernow_table;
>
> - for (j = 0; j < numps; j++) {
> - powernow_table[j].frequency = find_freq_from_fid(powernow_table[j].index & 0xff)*1000;
> - printk(KERN_INFO PFX " %d : fid 0x%x (%d MHz), vid 0x%x\n", j,
> - powernow_table[j].index & 0xff,
> - powernow_table[j].frequency/1000,
> - powernow_table[j].index >> 8);
> + if (acpi_processor_register_performance(&data->acpi_data, data->cpu)) {
> + dprintk(KERN_DEBUG PFX "register performance failed\n");
> + return -EIO;
> + }
> +
> + /* verify the data contained in the ACPI structures */
> + if (data->acpi_data.state_count <= 1) {
> + dprintk(KERN_DEBUG PFX "No ACPI P-States\n");
> + goto err_out;
> + }
> +
> + if ((data->acpi_data.control_register.space_id != ACPI_ADR_SPACE_FIXED_HARDWARE) ||
> + (data->acpi_data.status_register.space_id != ACPI_ADR_SPACE_FIXED_HARDWARE)) {
> + dprintk(KERN_DEBUG PFX "Invalid control/status registers\n");
> + goto err_out;
> + }
> +
> + /* fill in data->powernow_table */
> + powernow_table = kmalloc((sizeof(struct cpufreq_frequency_table)
> + * (data->acpi_data.state_count + 1)), GFP_KERNEL);
> + if (!powernow_table) {
> + dprintk(KERN_ERR PFX "powernow_table memory alloc failure\n");
> + goto err_out;
> + }
> +
> + for (i = 0; i < data->acpi_data.state_count; i++) {
> + u32 fid = data->acpi_data.states[i].control & FID_MASK;
> + u32 vid = (data->acpi_data.states[i].control >> VID_SHIFT) & VID_MASK;
> +
> + dprintk(KERN_INFO PFX " %d : fid %x, vid %x\n", i, fid, vid);
> +
> + powernow_table[i].index = fid; /* lower 8 bits */
> + powernow_table[i].index |= (vid << 8); /* upper 8 bits */
> + powernow_table[i].frequency = find_khz_freq_from_fid(fid);
> +
> + /* verify frequency is OK */
> + if ((powernow_table[i].frequency > (MAX_FREQ * 1000)) ||
> + (powernow_table[i].frequency < (MIN_FREQ * 1000))) {
> + dprintk(KERN_INFO PFX "invalid freq %u kHz\n", powernow_table[i].frequency);
> + powernow_table[i].frequency = CPUFREQ_ENTRY_INVALID;
> + continue;
> }
>
> - powernow_table[numps].frequency = CPUFREQ_TABLE_END;
> - powernow_table[numps].index = 0;
> + /* verify only 1 entry from the lo frequency table */
> + if ((fid < HI_FID_TABLE_BOTTOM) && (cntlofreq++)) {
> + printk(KERN_ERR PFX "Too many lo freq table entries\n");
> + goto err_out;
> + }
>
> - if (query_current_values_with_pending_wait()) {
> - kfree(powernow_table);
> - return -EIO;
> + if (powernow_table[i].frequency != (data->acpi_data.states[i].core_frequency * 1000)) {
> + printk(KERN_INFO PFX "invalid freq entries %u kHz vs. %u kHz\n",
> + powernow_table[i].frequency,
> + (unsigned int) (data->acpi_data.states[i].core_frequency * 1000));
> + powernow_table[i].frequency = CPUFREQ_ENTRY_INVALID;
> + continue;
> }
> + }
> + powernow_table[data->acpi_data.state_count].frequency = CPUFREQ_TABLE_END;
> + powernow_table[data->acpi_data.state_count].index = 0;
> + data->powernow_table = powernow_table;
> +
> + /* fill in data */
> + data->numps = data->acpi_data.state_count;
> + print_basics(data);
>
> - printk(KERN_INFO PFX "currfid 0x%x (%d MHz), currvid 0x%x\n",
> - currfid, find_freq_from_fid(currfid), currvid);
> + powernow_k8_acpi_pst_values(data, 0);
>
> - for (j = 0; j < numps; j++)
> - if ((pst[j].fid==currfid) && (pst[j].vid==currvid))
> - return 0;
> + return 0;
>
> - printk(KERN_ERR BFX "currfid/vid do not match PST, ignoring\n");
> - return 0;
> - }
> + err_out:
> + acpi_processor_unregister_performance(&data->acpi_data, data->cpu);
> +
> + /* data->acpi_data.state_count informs us at ->exit() whether ACPI was used */
> + data->acpi_data.state_count = 0;
>
> - printk(KERN_ERR BFX "no PSB\n");
> return -ENODEV;
> }
>
> +static void powernow_k8_cpu_exit_acpi(struct powernow_k8_data *data)
> +{
> + if (data->acpi_data.state_count)
> + acpi_processor_unregister_performance(&data->acpi_data, data->cpu);
> +}
> +
> +#else
> +static inline int powernow_k8_cpu_init_acpi(struct powernow_k8_data *data) { return -ENODEV; }
> +static inline void powernow_k8_cpu_exit_acpi(struct powernow_k8_data *data) { return; }
> +static inline void powernow_k8_acpi_pst_values(struct powernow_k8_data *data, unsigned int index) { return; }
> +#endif /* CONFIG_X86_POWERNOW_K8_ACPI */
> +
> +
> /* Take a frequency, and issue the fid/vid transition command */
> -static inline int
> -transition_frequency(unsigned int index)
> +static inline int transition_frequency(struct powernow_k8_data *data, unsigned int index)
> {
> u32 fid;
> u32 vid;
> int res;
> struct cpufreq_freqs freqs;
>
> + dprintk(KERN_DEBUG PFX "cpu %d transition to index %u\n",
> + smp_processor_id(), index );
> +
> /* fid are the lower 8 bits of the index we stored into
> * the cpufreq frequency table in find_psb_table, vid are
> * the upper 8 bits.
> */
>
> - fid = powernow_table[index].index & 0xFF;
> - vid = (powernow_table[index].index & 0xFF00) >> 8;
> + fid = data->powernow_table[index].index & 0xFF;
> + vid = (data->powernow_table[index].index & 0xFF00) >> 8;
>
> - dprintk(KERN_DEBUG PFX "table matched fid 0x%x, giving vid 0x%x\n",
> - fid, vid);
> + dprintk(KERN_DEBUG PFX "matched fid %x, giving vid %x\n", fid, vid);
>
> - if (query_current_values_with_pending_wait())
> + if (query_current_values_with_pending_wait(data))
> return 1;
>
> - if ((currvid == vid) && (currfid == fid)) {
> - dprintk(KERN_DEBUG PFX
> - "target matches current values (fid 0x%x, vid 0x%x)\n",
> + if ((data->currvid == vid) && (data->currfid == fid)) {
> + dprintk(KERN_DEBUG PFX "target matches curr (fid %x, vid %x)\n",
> fid, vid);
> return 0;
> }
>
> - if ((fid < HI_FID_TABLE_BOTTOM) && (currfid < HI_FID_TABLE_BOTTOM)) {
> + if ((fid < HI_FID_TABLE_BOTTOM) && (data->currfid < HI_FID_TABLE_BOTTOM)) {
> printk(KERN_ERR PFX
> "ignoring illegal change in lo freq table-%x to %x\n",
> - currfid, fid);
> + data->currfid, fid);
> return 1;
> }
>
> - dprintk(KERN_DEBUG PFX "changing to fid 0x%x, vid 0x%x\n", fid, vid);
> -
> - freqs.cpu = 0; /* only true because SMP not supported */
> + dprintk(KERN_DEBUG PFX "cpu %d, changing to fid %x, vid %x\n",
> + smp_processor_id(), fid, vid);
> + freqs.cpu = data->cpu;
>
> - freqs.old = find_freq_from_fid(currfid);
> - freqs.new = find_freq_from_fid(fid);
> + freqs.old = find_khz_freq_from_fid(data->currfid);
> + freqs.new = find_khz_freq_from_fid(fid);
> cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
>
> - res = transition_fid_vid(fid, vid);
> + down(&fidvid_sem);
> + res = transition_fid_vid(data, fid, vid);
> + up(&fidvid_sem);
>
> - freqs.new = find_freq_from_fid(currfid);
> + freqs.new = find_khz_freq_from_fid(data->currfid);
> cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
>
> return res;
> }
>
> /* Driver entry point to switch to the target frequency */
> -static int
> -powernowk8_target(struct cpufreq_policy *pol, unsigned targfreq, unsigned relation)
> +static int powernowk8_target(struct cpufreq_policy *pol, unsigned targfreq, unsigned relation)
> {
> - u32 checkfid = currfid;
> - u32 checkvid = currvid;
> + cpumask_t oldmask = CPU_MASK_ALL;
> + struct powernow_k8_data *data = powernow_data[pol->cpu];
> + u32 checkfid = data->currfid;
> + u32 checkvid = data->currvid;
> unsigned int newstate;
> + int ret = -EIO;
> +
> + /* only run on specific CPU from here on */
> + oldmask = current->cpus_allowed;
> + set_cpus_allowed(current, cpumask_of_cpu(pol->cpu));
> + schedule();
> +
> + if (smp_processor_id() != pol->cpu) {
> + printk(KERN_ERR "limiting to cpu %u failed\n", pol->cpu);
> + goto err_out;
> + }
> +
> + /* from this point, do not exit without restoring preempt and cpu */
> + preempt_disable();
>
> if (pending_bit_stuck()) {
> - printk(KERN_ERR PFX "drv targ fail: change pending bit set\n");
> - return -EIO;
> + printk(KERN_ERR PFX "failing targ, change pending bit set\n");
> + goto err_out;
> }
>
> - dprintk(KERN_DEBUG PFX "targ: %d kHz, min %d, max %d, relation %d\n",
> - targfreq, pol->min, pol->max, relation);
> + dprintk(KERN_DEBUG PFX "targ: cpu %d, %d kHz, min %d, max %d, relation %d\n",
> + pol->cpu, targfreq, pol->min, pol->max, relation);
>
> - if (query_current_values_with_pending_wait())
> - return -EIO;
> + if (query_current_values_with_pending_wait(data)) {
> + ret = -EIO;
> + goto err_out;
> + }
>
> - dprintk(KERN_DEBUG PFX "targ: curr fid 0x%x, vid 0x%x\n",
> - currfid, currvid);
> + dprintk(KERN_DEBUG PFX "targ: curr fid %x, vid %x\n",
> + data->currfid, data->currvid);
>
> - if ((checkvid != currvid) || (checkfid != currfid)) {
> - printk(KERN_ERR PFX
> - "error - out of sync, fid 0x%x 0x%x, vid 0x%x 0x%x\n",
> - checkfid, currfid, checkvid, currvid);
> + if ((checkvid != data->currvid) || (checkfid != data->currfid)) {
> + printk(KERN_ERR PFX "out of sync, fid %x %x, vid %x %x\n",
> + checkfid, data->currfid, checkvid, data->currvid);
> }
>
> - if (cpufreq_frequency_table_target(pol, powernow_table, targfreq, relation, &newstate))
> - return -EINVAL;
> -
> - if (transition_frequency(newstate))
> + if (cpufreq_frequency_table_target(pol, data->powernow_table, targfreq, relation, &newstate))
> + goto err_out;
> +
> + powernow_k8_acpi_pst_values(data, newstate);
> +
> + if (transition_frequency(data, newstate))
> {
> printk(KERN_ERR PFX "transition frequency failed\n");
> - return 1;
> + ret = 1;
> + goto err_out;
> }
>
> - pol->cur = 1000 * find_freq_from_fid(currfid);
> + pol->cur = find_khz_freq_from_fid(data->currfid);
> + ret = 0;
>
> - return 0;
> + err_out:
> + preempt_enable_no_resched();
> + set_cpus_allowed(current, oldmask);
> + schedule();
> +
> + return ret;
> }
>
> /* Driver entry point to verify the policy and range of frequencies */
> -static int
> -powernowk8_verify(struct cpufreq_policy *pol)
> +static int powernowk8_verify(struct cpufreq_policy *pol)
> {
> - if (pending_bit_stuck()) {
> - printk(KERN_ERR PFX "failing verify, change pending bit set\n");
> - return -EIO;
> - }
> + struct powernow_k8_data *data = powernow_data[pol->cpu];
>
> - return cpufreq_frequency_table_verify(pol, powernow_table);
> + return cpufreq_frequency_table_verify(pol, data->powernow_table);
> }
>
> /* per CPU init entry point to the driver */
> -static int __init
> -powernowk8_cpu_init(struct cpufreq_policy *pol)
> +static int __init powernowk8_cpu_init(struct cpufreq_policy *pol)
> {
> - if (pol->cpu != 0) {
> - printk(KERN_ERR PFX "init not cpu 0\n");
> + cpumask_t oldmask = CPU_MASK_ALL;
> + int rc;
> + struct powernow_k8_data *data;
> +
> + if (!check_supported_cpu(pol->cpu))
> return -ENODEV;
> +
> + data = kmalloc(sizeof(struct powernow_k8_data), GFP_KERNEL);
> + if (!data) {
> + printk(KERN_ERR PFX "unable to alloc powernow_k8_data");
> + return -ENOMEM;
> + }
> + memset(data,0,sizeof(struct powernow_k8_data));
> +
> + data->cpu = pol->cpu;
> +
> + if (powernow_k8_cpu_init_acpi(data)) {
> + /*
> + * Use the PSB BIOS structure. This is only availabe on
> + * an UP version, and is deprecated by AMD.
> + */
> +
> + if (pol->cpu != 0) {
> + printk(KERN_ERR PFX "init - cpu 0\n");
> + kfree(data);
> + return -ENODEV;
> + }
> +
> + if ((num_online_cpus() != 1) || (num_possible_cpus() != 1)) {
> + printk(KERN_INFO PFX "MP systems not supported by PSB BIOS structure\n");
> + kfree(data);
> + return 0;
> + }
> +
> + rc = find_psb_table(data);
> + if (rc) {
> + kfree(data);
> + return -ENODEV;
> + }
> }
>
> + /* only run on specific CPU from here on */
> + oldmask = current->cpus_allowed;
> + set_cpus_allowed(current, cpumask_of_cpu(pol->cpu));
> + schedule();
> +
> + if (smp_processor_id() != pol->cpu) {
> + printk(KERN_ERR "limiting to cpu %u failed\n", pol->cpu);
> + goto err_out;
> + }
> +
> + if (pending_bit_stuck()) {
> + printk(KERN_ERR PFX "failing init, change pending bit set\n");
> + goto err_out;
> + }
> +
> + if (query_current_values_with_pending_wait(data)) {
> + goto err_out;
> + }
> +
> + fidvid_msr_init();
> +
> +
> + /* run on any CPU again */
> + set_cpus_allowed(current, oldmask);
> + schedule();
> +
> pol->governor = CPUFREQ_DEFAULT_GOVERNOR;
>
> /* Take a crude guess here.
> - * That guess was in microseconds, so multply with 1000 */
> - pol->cpuinfo.transition_latency = (((rvo + 8) * vstable * VST_UNITS_20US)
> - + (3 * (1 << irt) * 10)) * 1000;
> -
> - if (query_current_values_with_pending_wait())
> - return -EIO;
> + * That guess was in microseconds, so multiply with 1000 */
> + pol->cpuinfo.transition_latency = (((data->rvo + 8) * data->vstable * VST_UNITS_20US)
> + + (3 * (1 << data->irt) * 10)) * 1000;
>
> - pol->cur = 1000 * find_freq_from_fid(currfid);
> + pol->cur = find_khz_freq_from_fid(data->currfid);
> dprintk(KERN_DEBUG PFX "policy current frequency %d kHz\n", pol->cur);
>
> /* min/max the cpu is capable of */
> - if (cpufreq_frequency_table_cpuinfo(pol, powernow_table)) {
> + if (cpufreq_frequency_table_cpuinfo(pol, data->powernow_table)) {
> printk(KERN_ERR PFX "invalid powernow_table\n");
> - kfree(powernow_table);
> + kfree(data->powernow_table);
> + kfree(data);
> return -EINVAL;
> }
> + cpufreq_frequency_table_get_attr(data->powernow_table, pol->cpu);
>
> - cpufreq_frequency_table_get_attr(powernow_table, pol->cpu);
> + dprintk(KERN_INFO PFX "init, curr fid %x vid %x\n", data->currfid, data->currvid);
>
> - printk(KERN_INFO PFX "cpu_init done, current fid 0x%x, vid 0x%x\n",
> - currfid, currvid);
> + powernow_data[pol->cpu] = data;
>
> return 0;
> +
> + err_out:
> + set_cpus_allowed(current, oldmask);
> + schedule();
> +
> + kfree(data);
> + return -ENODEV;
> }
>
> static int __exit powernowk8_cpu_exit (struct cpufreq_policy *pol)
> {
> - if (pol->cpu != 0)
> + struct powernow_k8_data *data = powernow_data[pol->cpu];
> +
> + if (!data)
> return -EINVAL;
>
> + powernow_k8_cpu_exit_acpi(data);
> +
> cpufreq_frequency_table_put_attr(pol->cpu);
>
> - if (powernow_table)
> - kfree(powernow_table);
> + kfree(data->powernow_table);
> + kfree(data);
>
> return 0;
> }
> @@ -845,33 +1033,31 @@
> .attr = powernow_k8_attr,
> };
>
> -
> /* driver entry point for init */
> -static int __init
> -powernowk8_init(void)
> +static int __init powernowk8_init(void)
> {
> - int rc;
> -
> - if (check_supported_cpu() == 0)
> - return -ENODEV;
> + unsigned int i, supported_cpus = 0;
>
> - rc = find_psb_table();
> - if (rc)
> - return rc;
> + for (i=0; i<NR_CPUS; i++) {
> + if (!cpu_online(i))
> + continue;
> + if (check_supported_cpu(i))
> + supported_cpus++;
> + }
>
> - if (pending_bit_stuck()) {
> - printk(KERN_ERR PFX "powernowk8_init fail, change pending bit set\n");
> - return -EIO;
> + if (supported_cpus == num_online_cpus()) {
> + printk(KERN_INFO PFX "Found %d AMD Athlon 64 / Opteron processors (" VERSION ")\n",
> + supported_cpus);
> + return cpufreq_register_driver(&cpufreq_amd64_driver);
> }
>
> - return cpufreq_register_driver(&cpufreq_amd64_driver);
> + return -ENODEV;
> }
>
> /* driver entry point for term */
> -static void __exit
> -powernowk8_exit(void)
> +static void __exit powernowk8_exit(void)
> {
> - dprintk(KERN_INFO PFX "powernowk8_exit\n");
> + dprintk(KERN_INFO PFX "exit\n");
>
> cpufreq_unregister_driver(&cpufreq_amd64_driver);
> }
> @@ -880,5 +1066,5 @@
> MODULE_DESCRIPTION("AMD Athlon 64 and Opteron processor frequency driver.");
> MODULE_LICENSE("GPL");
>
> -module_init(powernowk8_init);
> +late_initcall(powernowk8_init);
> module_exit(powernowk8_exit);
> --- clean/arch/i386/kernel/cpu/cpufreq/powernow-k8.h 2004-02-20 12:29:10.000000000 +0100
> +++ linux-pn/arch/i386/kernel/cpu/cpufreq/powernow-k8.h 2004-03-26 12:44:27.000000000 +0100
> @@ -1,20 +1,46 @@
> /*
> - * (c) 2003 Advanced Micro Devices, Inc.
> + * (c) 2003, 2004 Advanced Micro Devices, Inc.
> * Your use of this code is subject to the terms and conditions of the
> - * GNU general public license version 2. See "../../../COPYING" or
> + * GNU general public license version 2. See "COPYING" or
> * http://www.gnu.org/licenses/gpl.html
> */
>
> +struct powernow_k8_data {
> + unsigned int cpu;
> +
> + u32 numps; /* number of p-states */
> + u32 batps; /* number of p-states supported on battery */
> +
> + /* these values are constant when the PSB is used to determine
> + * vid/fid pairings, but are modified during the ->target() call
> + * when ACPI is used */
> + u32 rvo; /* ramp voltage offset */
> + u32 irt; /* isochronous relief time */
> + u32 vidmvs; /* usable value calculated from mvs */
> + u32 vstable; /* voltage stabilization time, units 20 us */
> + u32 plllock; /* pll lock time, units 1 us */
> +
> + /* keep track of the current fid / vid */
> + u32 currvid;
> + u32 currfid;
> +
> + /* the powernow_table includes all frequency and vid/fid pairings:
> + * fid are the lower 8 bits of the index, vid are the upper 8 bits.
> + * frequency is in kHz */
> + struct cpufreq_frequency_table *powernow_table;
> +
> +#ifdef CONFIG_X86_POWERNOW_K8_ACPI
> + /* the acpi table needs to be kept. it's only available if ACPI was
> + * used to determine valid frequency/vid/fid states */
> + struct acpi_processor_performance acpi_data;
> +#endif
> +};
> +
> /* processor's cpuid instruction support */
> -#define CPUID_PROCESSOR_SIGNATURE 1 /* function 1 */
> -#define CPUID_F1_FAM 0x00000f00 /* family mask */
> -#define CPUID_F1_XFAM 0x0ff00000 /* extended family mask */
> -#define CPUID_F1_MOD 0x000000f0 /* model mask */
> -#define CPUID_F1_STEP 0x0000000f /* stepping level mask */
> -#define CPUID_XFAM_MOD 0x0ff00ff0 /* xtended fam, fam + model */
> -#define ATHLON64_XFAM_MOD 0x00000f40 /* xtended fam, fam + model */
> -#define OPTERON_XFAM_MOD 0x00000f50 /* xtended fam, fam + model */
> -#define ATHLON64_REV_C0 8
> +#define CPUID_PROCESSOR_SIGNATURE 1 /* function 1 */
> +#define CPUID_XFAM_MOD 0x0ff00ff0 /* extended fam, fam + model */
> +#define ATHLON64_XFAM_MOD 0x00000f40 /* extended fam, fam + model */
> +#define OPTERON_XFAM_MOD 0x00000f50 /* extended fam, fam + model */
> #define CPUID_GET_MAX_CAPABILITIES 0x80000000
> #define CPUID_FREQ_VOLT_CAPABILITIES 0x80000007
> #define P_STATE_TRANSITION_CAPABLE 6
> @@ -23,7 +49,6 @@
> /* writes (wrmsr - opcode 0f 30), the register number is placed in ecx, and */
> /* the value to write is placed in edx:eax. For reads (rdmsr - opcode 0f 32), */
> /* the register number is placed in ecx, and the data is returned in edx:eax. */
> -
> #define MSR_FIDVID_CTL 0xc0010041
> #define MSR_FIDVID_STATUS 0xc0010042
>
> @@ -47,10 +72,24 @@
> #define MSR_S_HI_MAX_WORKING_VID 0x001f0000
> #define MSR_S_HI_START_VID 0x00001f00
> #define MSR_S_HI_CURRENT_VID 0x0000001f
> +#define MSR_C_HI_STP_GNT_BENIGN 0x00000001
> +
> +/*
> + There are restrictions frequencies have to follow:
> + - only 1 entry in the low fid table ( <=1.4GHz )
> + - lowest entry in the high fid table must be >= 2 * the
> + entry in the low fid table
> + - lowest entry in the high fid table must be a <= 200MHz +
> + 2 * the entry in the low fid table
> + - the parts can only step at 200 MHz intervals, so 1.9 GHz is
> + never valid
> + - lowest frequency must be >= interprocessor hypertransport link
> + speed (only applies to MP systems obviously)
> + */
>
> /* fids (frequency identifiers) are arranged in 2 tables - lo and hi */
> -#define LO_FID_TABLE_TOP 6
> -#define HI_FID_TABLE_BOTTOM 8
> +#define LO_FID_TABLE_TOP 6 /* fid values marking the boundary */
> +#define HI_FID_TABLE_BOTTOM 8 /* between the low and high tables */
>
> #define LO_VCOFREQ_TABLE_TOP 1400 /* corresponding vco frequency values */
> #define HI_VCOFREQ_TABLE_BOTTOM 1600
> @@ -58,33 +97,44 @@
> #define MIN_FREQ_RESOLUTION 200 /* fids jump by 2 matching freq jumps by 200 */
>
> #define MAX_FID 0x2a /* Spec only gives FID values as far as 5 GHz */
> -
> #define LEAST_VID 0x1e /* Lowest (numerically highest) useful vid value */
> -
> #define MIN_FREQ 800 /* Min and max freqs, per spec */
> #define MAX_FREQ 5000
>
> -#define INVALID_FID_MASK 0xffffffc1 /* not a valid fid if these bits are set */
> -
> -#define INVALID_VID_MASK 0xffffffe0 /* not a valid vid if these bits are set */
> +#define INVALID_FID_MASK 0xffffffc1 /* not a valid fid if these bits are set */
> +#define INVALID_VID_MASK 0xffffffe0 /* not a valid vid if these bits are set */
>
> #define STOP_GRANT_5NS 1 /* min poss memory access latency for voltage change */
> -
> #define PLL_LOCK_CONVERSION (1000/5) /* ms to ns, then divide by clock period */
> -
> #define MAXIMUM_VID_STEPS 1 /* Current cpus only allow a single step of 25mV */
> +#define VST_UNITS_20US 20 /* Voltage Stabilization Time is in units of 20us */
>
> -#define VST_UNITS_20US 20 /* Voltage Stabalization Time is in units of 20us */
> +/*
> + * Most values of interest are enocoded in a single field of the _PSS
> + * entries: the "control" value.
> + */
>
> +#define IRT_SHIFT 30
> +#define RVO_SHIFT 28
> +#define PLL_L_SHIFT 20
> +#define MVS_SHIFT 18
> +#define VST_SHIFT 11
> +#define VID_SHIFT 6
> +#define IRT_MASK 3
> +#define RVO_MASK 3
> +#define PLL_L_MASK 0x7f
> +#define MVS_MASK 3
> +#define VST_MASK 0x7f
> +#define VID_MASK 0x1f
> +#define FID_MASK 0x3f
> +
> /*
> -Version 1.4 of the PSB table. This table is constructed by BIOS and is
> -to tell the OS's power management driver which VIDs and FIDs are
> -supported by this particular processor. This information is obtained from
> -the data sheets for each processor model by the system vendor and
> -incorporated into the BIOS.
> -If the data in the PSB / PST is wrong, then this driver will program the
> -wrong values into hardware, which is very likely to lead to a crash.
> -*/
> + * Version 1.4 of the PSB table. This table is constructed by BIOS and is
> + * to tell the OS's power management driver which VIDs and FIDs are
> + * supported by this particular processor.
> + * If the data in the PSB / PST is wrong, then this driver will program the
> + * wrong values into hardware, which is very likely to lead to a crash.
> + */
>
> #define PSB_ID_STRING "AMDK7PNOW!"
> #define PSB_ID_STRING_LEN 10
> @@ -117,6 +167,7 @@
> #define dprintk(msg...) do { } while(0)
> #endif
>
> -static inline int core_voltage_pre_transition(u32 reqvid);
> -static inline int core_voltage_post_transition(u32 reqvid);
> -static inline int core_frequency_transition(u32 reqfid);
> +static inline int core_voltage_pre_transition(struct powernow_k8_data *data, u32 reqvid);
> +static inline int core_voltage_post_transition(struct powernow_k8_data *data, u32 reqvid);
> +static inline int core_frequency_transition(struct powernow_k8_data *data, u32 reqfid);
> +static inline void powernow_k8_acpi_pst_values(struct powernow_k8_data *data, unsigned int index);
--
Regards, Redeeman
redeeman@metanurb.dk
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: powernow-k8: support acpi
2004-03-26 12:29 ` Pavel Machek
(?)
(?)
@ 2004-04-01 23:56 ` Dave Jones
2004-04-02 8:30 ` Pavel Machek
2004-04-02 16:24 ` Bruno Ducrot
-1 siblings, 2 replies; 21+ messages in thread
From: Dave Jones @ 2004-04-01 23:56 UTC (permalink / raw)
To: Pavel Machek
Cc: Andrew Morton, Bruno Ducrot, mark.langsdorf, Cpufreq mailing list,
paul.devriendt, Dominik Brodowski
On Fri, Mar 26, 2004 at 01:29:32PM +0100, Pavel Machek wrote:
> This is new version of powernow-k8 driver. It adds SMP support, and
> support for getting tables through ACPI. (ACPI support is really
> important, because many machines have broken "legacy" tables). Please
> apply,
First off, apologies for sitting on this for so long.
I just made a first-pass attempt at merging this. Most of
(maybe all?) the simpler bits are in (whitespace changes, typos etc).
I'll push that out to bkbits soon, so the powernow-k8 diff in the
next -mm that Andrew builds should be slightly smaller at least.
The ACPI stuff I'm pondering.
Bruno, how does this stuff look to you? Your acpi perflib stuff is
already in the pending pool (see the diff in the current -mm tree at
http://www.kernel.org/pub/linux/kernel/people/akpm/patches/2.6/2.6.5-rc3/2.6.5-rc3-mm4/broken-out/bk-cpufreq.patch)
Would this driver be better off using that ?
> @@ -31,55 +33,47 @@
> #include <asm/io.h>
> #include <asm/delay.h>
>
> +#ifdef CONFIG_ACPI
> +#define CONFIG_X86_POWERNOW_K8_ACPI
> +#endif
> +#ifdef CONFIG_X86_POWERNOW_K8_ACPI
> +
> +#include <linux/acpi.h>
> +#include <acpi/processor.h>
> +
> +#endif /* CONFIG_X86_POWERNOW_K8_ACPI */
This bit just looks *wrong* to me. This looks like something
that really should be sorted out in Kconfig.
The rest of the ACPI changes, I'd really like to get Bruno Ducrot's opinion
on.
Dave
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: powernow-k8: support acpi
2004-04-01 23:56 ` Dave Jones
@ 2004-04-02 8:30 ` Pavel Machek
2004-04-02 9:02 ` Dave Jones
2004-04-02 11:13 ` Dave Jones
2004-04-02 16:24 ` Bruno Ducrot
1 sibling, 2 replies; 21+ messages in thread
From: Pavel Machek @ 2004-04-02 8:30 UTC (permalink / raw)
To: Dave Jones
Cc: Andrew Morton, Bruno Ducrot, mark.langsdorf, Cpufreq mailing list,
paul.devriendt, Dominik Brodowski
Hi!
> > This is new version of powernow-k8 driver. It adds SMP support, and
> > support for getting tables through ACPI. (ACPI support is really
> > important, because many machines have broken "legacy" tables). Please
> > apply,
>
> First off, apologies for sitting on this for so long.
> I just made a first-pass attempt at merging this. Most of
> (maybe all?) the simpler bits are in (whitespace changes, typos etc).
> I'll push that out to bkbits soon, so the powernow-k8 diff in the
> next -mm that Andrew builds should be slightly smaller at least.
Adding of data->, too? Could you mail me your latest powernow-k8.[ch]?
> The ACPI stuff I'm pondering.
> Bruno, how does this stuff look to you? Your acpi perflib stuff is
> already in the pending pool (see the diff in the current -mm tree at
> http://www.kernel.org/pub/linux/kernel/people/akpm/patches/2.6/2.6.5-rc3/2.6.5-rc3-mm4/broken-out/bk-cpufreq.patch)
> Would this driver be better off using that ?
It already uses some kind of library as its *way* smaller than it used
to be...
> > @@ -31,55 +33,47 @@
> > #include <asm/io.h>
> > #include <asm/delay.h>
> >
> > +#ifdef CONFIG_ACPI
> > +#define CONFIG_X86_POWERNOW_K8_ACPI
> > +#endif
> > +#ifdef CONFIG_X86_POWERNOW_K8_ACPI
> > +
> > +#include <linux/acpi.h>
> > +#include <acpi/processor.h>
> > +
> > +#endif /* CONFIG_X86_POWERNOW_K8_ACPI */
>
> This bit just looks *wrong* to me. This looks like something
> that really should be sorted out in Kconfig.
No problem... Or perhaps just CONFIG_X86_POWERNOW_K8_ACPI should be
replaced with CONFIG_ACPI in the driver?
Pavel
--
When do you have a heart between your knees?
[Johanka's followup: and *two* hearts?]
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: powernow-k8: support acpi
2004-04-02 8:30 ` Pavel Machek
@ 2004-04-02 9:02 ` Dave Jones
2004-04-02 11:13 ` Dave Jones
1 sibling, 0 replies; 21+ messages in thread
From: Dave Jones @ 2004-04-02 9:02 UTC (permalink / raw)
To: Pavel Machek
Cc: Andrew Morton, Bruno Ducrot, mark.langsdorf, Cpufreq mailing list,
paul.devriendt, Dominik Brodowski
On Fri, Apr 02, 2004 at 10:30:38AM +0200, Pavel Machek wrote:
> > First off, apologies for sitting on this for so long.
> > I just made a first-pass attempt at merging this. Most of
> > (maybe all?) the simpler bits are in (whitespace changes, typos etc).
> > I'll push that out to bkbits soon, so the powernow-k8 diff in the
> > next -mm that Andrew builds should be slightly smaller at least.
>
> Adding of data->, too? Could you mail me your latest powernow-k8.[ch]?
I'll look at that today. (Honest, as soon as I'm done with mail 8-)
> > > +#endif /* CONFIG_X86_POWERNOW_K8_ACPI */
> >
> > This bit just looks *wrong* to me. This looks like something
> > that really should be sorted out in Kconfig.
>
> No problem... Or perhaps just CONFIG_X86_POWERNOW_K8_ACPI should be
> replaced with CONFIG_ACPI in the driver?
Hmm, possibly. Lets see Bruno's response first.
Dave
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: powernow-k8: support acpi
2004-04-02 8:30 ` Pavel Machek
2004-04-02 9:02 ` Dave Jones
@ 2004-04-02 11:13 ` Dave Jones
1 sibling, 0 replies; 21+ messages in thread
From: Dave Jones @ 2004-04-02 11:13 UTC (permalink / raw)
To: Pavel Machek
Cc: Andrew Morton, Bruno Ducrot, mark.langsdorf, Cpufreq mailing list,
paul.devriendt, Dominik Brodowski
[-- Attachment #1: Type: text/plain, Size: 1015 bytes --]
On Fri, Apr 02, 2004 at 10:30:38AM +0200, Pavel Machek wrote:
> Adding of data->, too? Could you mail me your latest powernow-k8.[ch]?
Ok, I think that was the bulk of it done. The file attached is the
current state of play in the cpufreq bk tree. It has the data->
stuff merged too. It compiles, but I'm not convinced it'll run as
some of the chunks are still missing.
This should get us a lot closer though. The next stage of the merge
should be a lot simpler.
Want to send me another diff vs this, and we'll see where we stand?
The biggest outstanding chunk should be the acpi stuff, which I'm
still undecided on, but we'll worry about that after any more
trivial bits get merged.
I did make a small number of additional edits. Whitespace & removal
of unnecessary inline's. Other than that, we should be pretty close.
Andrew, the additional powernow-k8 diff in your tree stands no
chance of applying with this stuff merged. I'd suggest to just drop it.
We'll soon resolve the conflicts anyway.
Dave
[-- Attachment #2: powernow-k8.c --]
[-- Type: text/plain, Size: 23872 bytes --]
/*
* (c) 2003, 2004 Advanced Micro Devices, Inc.
* Your use of this code is subject to the terms and conditions of the
* GNU general public license version 2. See "COPYING" or
* http://www.gnu.org/licenses/gpl.html
*
* Support : paul.devriendt@amd.com
*
* Based on the powernow-k7.c module written by Dave Jones.
* (C) 2003 Dave Jones <davej@codemonkey.org.uk> on behalf of SuSE Labs
* (C) 2004 Dominik Brodowski <linux@brodo.de>
* (C) 2004 Pavel Machek <pavel@suse.cz>
* Licensed under the terms of the GNU GPL License version 2.
* Based upon datasheets & sample CPUs kindly provided by AMD.
*
* Valuable input gratefully received from Dave Jones, Pavel Machek,
* Dominik Brodowski, and others.
* Processor information obtained from Chapter 9 (Power and Thermal Management)
* of the "BIOS and Kernel Developer's Guide for the AMD Athlon 64 and AMD
* Opteron Processors" available for download from www.amd.com
*/
#include <linux/kernel.h>
#include <linux/smp.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/cpufreq.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <asm/msr.h>
#include <asm/io.h>
#include <asm/delay.h>
#define PFX "powernow-k8: "
#define BFX PFX "BIOS error: "
#define VERSION "version 1.00.08a"
#include "powernow-k8.h"
static struct powernow_k8_data *powernow_data[NR_CPUS];
/*
The PSB table supplied by BIOS allows for the definition of the number of
p-states that can be used when running on a/c, and the number of p-states
that can be used when running on battery. This allows laptop manufacturers
to force the system to save power when running from battery. The relationship
is :
1 <= number_of_battery_p_states <= maximum_number_of_p_states
This driver does NOT have the support in it to detect transitions from
a/c power to battery power, and thus trigger the transition to a lower
p-state if required. This is because I need ACPI and the 2.6 kernel to do
this, and this is a 2.4 kernel driver. Check back for a new improved driver
for the 2.6 kernel soon.
This code therefore assumes it is on battery at all times, and thus
restricts performance to number_of_battery_p_states. For desktops,
number_of_battery_p_states == maximum_number_of_pstates,
so this is not actually a restriction.
*/
static u32 batps; /* limit on the number of p states when on battery */
/* - set by BIOS in the PSB/PST */
/* Return a frequency in MHz, given an input fid */
static u32 find_freq_from_fid(u32 fid)
{
return 800 + (fid * 100);
}
/* Return the vco fid for an input fid */
static u32 convert_fid_to_vco_fid(u32 fid)
{
if (fid < HI_FID_TABLE_BOTTOM) {
return 8 + (2 * fid);
} else {
return fid;
}
}
/*
* Return 1 if the pending bit is set. Unless we just instructed the processor
* to transition to a new state, seeing this bit set is really bad news.
*/
static inline int pending_bit_stuck(void)
{
u32 lo, hi;
rdmsr(MSR_FIDVID_STATUS, lo, hi);
return lo & MSR_S_LO_CHANGE_PENDING ? 1 : 0;
}
/*
* Update the global current fid / vid values from the status msr.
* Returns 1 on error.
*/
static int query_current_values_with_pending_wait(struct powernow_k8_data *data)
{
u32 lo, hi;
u32 i = 0;
lo = MSR_S_LO_CHANGE_PENDING;
while (lo & MSR_S_LO_CHANGE_PENDING) {
if (i++ > 0x1000000) {
printk(KERN_ERR PFX "detected change pending stuck\n");
return 1;
}
rdmsr(MSR_FIDVID_STATUS, lo, hi);
}
data->currvid = hi & MSR_S_HI_CURRENT_VID;
data->currfid = lo & MSR_S_LO_CURRENT_FID;
return 0;
}
/* the isochronous relief time */
static inline void count_off_irt(struct powernow_k8_data *data)
{
udelay((1 << data->irt) * 10);
return;
}
/* the voltage stabalization time */
static inline void count_off_vst(struct powernow_k8_data *data)
{
udelay(data->vstable * VST_UNITS_20US);
return;
}
/* write the new fid value along with the other control fields to the msr */
static int write_new_fid(struct powernow_k8_data *data, u32 fid)
{
u32 lo;
u32 savevid = data->currvid;
if ((fid & INVALID_FID_MASK) || (data->currvid & INVALID_VID_MASK)) {
printk(KERN_ERR PFX "internal error - overflow on fid write\n");
return 1;
}
lo = fid | (data->currvid << MSR_C_LO_VID_SHIFT) | MSR_C_LO_INIT_FID_VID;
dprintk(KERN_DEBUG PFX "writing fid %x, lo %x, hi %x\n",
fid, lo, data->plllock * PLL_LOCK_CONVERSION);
wrmsr(MSR_FIDVID_CTL, lo, data->plllock * PLL_LOCK_CONVERSION);
if (query_current_values_with_pending_wait(data))
return 1;
count_off_irt(data);
if (savevid != data->currvid) {
printk(KERN_ERR PFX "vid change on fid trans, old %x, new %x\n",
savevid, data->currvid);
return 1;
}
if (fid != data->currfid) {
printk(KERN_ERR PFX "fid trans failed, fid %x, curr %x\n", fid,
data->currfid);
return 1;
}
return 0;
}
/* Write a new vid to the hardware */
static int write_new_vid(struct powernow_k8_data *data, u32 vid)
{
u32 lo;
u32 savefid = data->currfid;
if ((data->currfid & INVALID_FID_MASK) || (vid & INVALID_VID_MASK)) {
printk(KERN_ERR PFX "internal error - overflow on vid write\n");
return 1;
}
lo = data->currfid | (vid << MSR_C_LO_VID_SHIFT) | MSR_C_LO_INIT_FID_VID;
dprintk(KERN_DEBUG PFX "writing vid %x, lo %x, hi %x\n",
vid, lo, STOP_GRANT_5NS);
wrmsr(MSR_FIDVID_CTL, lo, STOP_GRANT_5NS);
if (query_current_values_with_pending_wait(data))
return 1;
if (savefid != data->currfid) {
printk(KERN_ERR PFX "fid changed on vid trans, old %x new %x\n",
savefid, data->currfid);
return 1;
}
if (vid != data->currvid) {
printk(KERN_ERR PFX "vid trans failed, vid %x, curr %x\n", vid,
data->currvid);
return 1;
}
return 0;
}
/*
* Reduce the vid by the max of step or reqvid.
* Decreasing vid codes represent increasing voltages:
* vid of 0 is 1.550V, vid of 0x1e is 0.800V, vid of 0x1f is off.
*/
static int decrease_vid_code_by_step(struct powernow_k8_data *data, u32 reqvid, u32 step)
{
if ((data->currvid - reqvid) > step)
reqvid = data->currvid - step;
if (write_new_vid(data, reqvid))
return 1;
count_off_vst(data);
return 0;
}
/* Change the fid and vid, by the 3 phases. */
static int transition_fid_vid(struct powernow_k8_data *data, u32 reqfid, u32 reqvid)
{
if (core_voltage_pre_transition(data, reqvid))
return 1;
if (core_frequency_transition(data, reqfid))
return 1;
if (core_voltage_post_transition(data, reqvid))
return 1;
if (query_current_values_with_pending_wait(data))
return 1;
if ((reqfid != data->currfid) || (reqvid != data->currvid)) {
printk(KERN_ERR PFX "failed (cpu%d): req 0x%x 0x%x, curr 0x%x 0x%x\n",
smp_processor_id(),
reqfid, reqvid, data->currfid, data->currvid);
return 1;
}
dprintk(KERN_INFO PFX "transitioned (cpu%d): new fid 0x%x, vid 0x%x\n",
smp_processor_id(), data->currfid, data->currvid);
return 0;
}
/* Phase 1 - core voltage transition ... setup voltage */
static int core_voltage_pre_transition(struct powernow_k8_data *data, u32 reqvid)
{
u32 rvosteps = data->rvo;
u32 savefid = data->currfid;
dprintk(KERN_DEBUG PFX
"ph1 (cpu%d): start, currfid 0x%x, currvid 0x%x, reqvid 0x%x, rvo 0x%x\n",
smp_processor_id();
data->currfid, data->currvid, reqvid, data->rvo);
while (data->currvid > reqvid) {
dprintk(KERN_DEBUG PFX "ph1: curr 0x%x, req vid 0x%x\n",
data->currvid, reqvid);
if (decrease_vid_code_by_step(data, reqvid, data->vidmvs))
return 1;
}
while (rvosteps > 0) {
if (data->currvid == 0) {
rvosteps = 0;
} else {
dprintk(KERN_DEBUG PFX
"ph1: changing vid for rvo, req 0x%x\n",
data->currvid - 1);
if (decrease_vid_code_by_step(data, data->currvid - 1, 1))
return 1;
rvosteps--;
}
}
if (query_current_values_with_pending_wait(data))
return 1;
if (savefid != data->currfid) {
printk(KERN_ERR PFX "ph1 err, currfid changed 0x%x\n", data->currfid);
return 1;
}
dprintk(KERN_DEBUG PFX "ph1 complete, currfid 0x%x, currvid 0x%x\n",
data->currfid, data->currvid);
return 0;
}
/* Phase 2 - core frequency transition */
static int core_frequency_transition(struct powernow_k8_data *data, u32 reqfid)
{
u32 vcoreqfid;
u32 vcocurrfid;
u32 vcofiddiff;
u32 savevid = data->currvid;
if ((reqfid < HI_FID_TABLE_BOTTOM) && (data->currfid < HI_FID_TABLE_BOTTOM)) {
printk(KERN_ERR PFX "ph2: illegal lo-lo transition 0x%x 0x%x\n",
reqfid, data->currfid);
return 1;
}
if (data->currfid == reqfid) {
printk(KERN_ERR PFX "ph2 null fid transition 0x%x\n", data->currfid);
return 0;
}
dprintk(KERN_DEBUG PFX
"ph2 (cpu%d): starting, currfid 0x%x, currvid 0x%x, reqfid 0x%x\n",
smp_processor_id(),
data->currfid, data->currvid, reqfid);
vcoreqfid = convert_fid_to_vco_fid(reqfid);
vcocurrfid = convert_fid_to_vco_fid(data->currfid);
vcofiddiff = vcocurrfid > vcoreqfid ? vcocurrfid - vcoreqfid
: vcoreqfid - vcocurrfid;
while (vcofiddiff > 2) {
if (reqfid > data->currfid) {
if (data->currfid > LO_FID_TABLE_TOP) {
if (write_new_fid(data, data->currfid + 2)) {
return 1;
}
} else {
if (write_new_fid
(data, 2 + convert_fid_to_vco_fid(data->currfid))) {
return 1;
}
}
} else {
if (write_new_fid(data, data->currfid - 2))
return 1;
}
vcocurrfid = convert_fid_to_vco_fid(data->currfid);
vcofiddiff = vcocurrfid > vcoreqfid ? vcocurrfid - vcoreqfid
: vcoreqfid - vcocurrfid;
}
if (write_new_fid(data, reqfid))
return 1;
if (query_current_values_with_pending_wait(data))
return 1;
if (data->currfid != reqfid) {
printk(KERN_ERR PFX
"ph2: mismatch, failed fid transition, curr 0x%x, req 0x%x\n",
data->currfid, reqfid);
return 1;
}
if (savevid != data->currvid) {
printk(KERN_ERR PFX "ph2: vid changed, save %x, curr %x\n",
savevid, data->currvid);
return 1;
}
dprintk(KERN_DEBUG PFX "ph2 complete, currfid 0x%x, currvid 0x%x\n",
data->currfid, data->currvid);
return 0;
}
/* Phase 3 - core voltage transition flow ... jump to the final vid. */
static int core_voltage_post_transition(struct powernow_k8_data *data, u32 reqvid)
{
u32 savefid = data->currfid;
u32 savereqvid = reqvid;
dprintk(KERN_DEBUG PFX "ph3 (cpu%d): starting, currfid 0x%x, currvid 0x%x\n",
smp_processor_id(),
data->currfid, data->currvid);
if (reqvid != data->currvid) {
if (write_new_vid(data, reqvid))
return 1;
if (savefid != data->currfid) {
printk(KERN_ERR PFX
"ph3: bad fid change, save %x, curr %x\n",
savefid, data->currfid);
return 1;
}
if (data->currvid != reqvid) {
printk(KERN_ERR PFX
"ph3: failed vid transition\n, req %x, curr %x",
reqvid, data->currvid);
return 1;
}
}
if (query_current_values_with_pending_wait(data))
return 1;
if (savereqvid != data->currvid) {
dprintk(KERN_ERR PFX "ph3 failed, currvid 0x%x\n", data->currvid);
return 1;
}
if (savefid != data->currfid) {
dprintk(KERN_ERR PFX "ph3 failed, currfid changed 0x%x\n",
data->currfid);
return 1;
}
dprintk(KERN_DEBUG PFX "ph3 complete, currfid 0x%x, currvid 0x%x\n",
data->currfid, data->currvid);
return 0;
}
static int check_supported_cpu(unsigned int cpu)
{
struct cpuinfo_x86 *c = cpu_data;
u32 eax, ebx, ecx, edx;
if (num_online_cpus() != 1) {
printk(KERN_INFO PFX "multiprocessor systems not supported\n");
return 0;
}
if (c->x86_vendor != X86_VENDOR_AMD) {
#ifdef MODULE
printk(KERN_INFO PFX "Not an AMD processor\n");
#endif
return 0;
}
eax = cpuid_eax(CPUID_PROCESSOR_SIGNATURE);
if ((eax & CPUID_XFAM_MOD) == ATHLON64_XFAM_MOD) {
dprintk(KERN_DEBUG PFX "AMD Althon 64 Processor found\n");
if ((eax & CPUID_F1_STEP) < ATHLON64_REV_C0) {
printk(KERN_INFO PFX "Revision C0 or better "
"AMD Athlon 64 processor required\n");
return 0;
}
} else if ((eax & CPUID_XFAM_MOD) == OPTERON_XFAM_MOD) {
dprintk(KERN_DEBUG PFX "AMD Opteron Processor found\n");
} else {
printk(KERN_INFO PFX
"AMD Athlon 64 or AMD Opteron processor required\n");
return 0;
}
eax = cpuid_eax(CPUID_GET_MAX_CAPABILITIES);
if (eax < CPUID_FREQ_VOLT_CAPABILITIES) {
printk(KERN_INFO PFX
"No frequency change capabilities detected\n");
return 0;
}
cpuid(CPUID_FREQ_VOLT_CAPABILITIES, &eax, &ebx, &ecx, &edx);
if ((edx & P_STATE_TRANSITION_CAPABLE) != P_STATE_TRANSITION_CAPABLE) {
printk(KERN_INFO PFX "Power state transitions not supported\n");
return 0;
}
printk(KERN_INFO PFX "Found AMD64 processor supporting PowerNow (" VERSION ")\n");
return 1;
}
static int check_pst_table(struct powernow_k8_data *data, struct pst_s *pst, u8 maxvid)
{
unsigned int j;
u8 lastfid = 0xFF;
for (j = 0; j < data->numps; j++) {
if (pst[j].vid > LEAST_VID) {
printk(KERN_ERR PFX "vid %d invalid : 0x%x\n", j, pst[j].vid);
return -EINVAL;
}
if (pst[j].vid < data->rvo) { /* vid + rvo >= 0 */
printk(KERN_ERR PFX
"BIOS error - 0 vid exceeded with pstate %d\n",
j);
return -ENODEV;
}
if (pst[j].vid < maxvid + data->rvo) { /* vid + rvo >= maxvid */
printk(KERN_ERR PFX
"BIOS error - maxvid exceeded with pstate %d\n",
j);
return -ENODEV;
}
if ((pst[j].fid > MAX_FID)
|| (pst[j].fid & 1)
|| (j && (pst[j].fid < HI_FID_TABLE_BOTTOM))) {
/* Only first fid is allowed to be in "low" range */
printk(KERN_ERR PFX "fid %d invalid : 0x%x\n", j, pst[j].fid);
return -EINVAL;
}
if (pst[j].fid < lastfid)
lastfid = pst[j].fid;
}
if (lastfid & 1) {
printk(KERN_ERR PFX "lastfid invalid\n");
return -EINVAL;
}
if (lastfid > LO_FID_TABLE_TOP) {
printk(KERN_INFO PFX "first fid not from lo freq table\n");
}
return 0;
}
/* Find and validate the PSB/PST table in BIOS. */
static int find_psb_table(struct powernow_k8_data *data)
{
struct cpufreq_frequency_table *powernow_table;
struct psb_s *psb;
struct pst_s *pst;
unsigned int i, j;
u32 mvs;
u8 maxvid;
for (i = 0xc0000; i < 0xffff0; i += 0x10) {
/* Scan BIOS looking for the signature. */
/* It can not be at ffff0 - it is too big. */
psb = phys_to_virt(i);
if (memcmp(psb, PSB_ID_STRING, PSB_ID_STRING_LEN) != 0)
continue;
dprintk(KERN_DEBUG PFX "found PSB header at 0x%p\n", psb);
dprintk(KERN_DEBUG PFX "table vers: 0x%x\n", psb->tableversion);
if (psb->tableversion != PSB_VERSION_1_4) {
printk(KERN_INFO BFX "PSB table is not v1.4\n");
return -ENODEV;
}
dprintk(KERN_DEBUG PFX "flags: 0x%x\n", psb->flags1);
if (psb->flags1) {
printk(KERN_ERR BFX "unknown flags\n");
return -ENODEV;
}
data->vstable = psb->voltagestabilizationtime;
dprintk(KERN_INFO PFX "voltage stabilization time: %d(*20us)\n", data->vstable);
dprintk(KERN_DEBUG PFX "flags2: 0x%x\n", psb->flags2);
data->rvo = psb->flags2 & 3;
data->irt = ((psb->flags2) >> 2) & 3;
mvs = ((psb->flags2) >> 4) & 3;
data->vidmvs = 1 << mvs;
data->batps = ((psb->flags2) >> 6) & 3;
printk(KERN_INFO PFX "voltage stable in %d usec", data->vstable * 20);
if (data->batps)
printk(", only %d lowest states on battery", batps);
printk(", ramp voltage offset: %d", data->rvo);
printk(", isochronous relief time: %d", data->irt);
printk(", maximum voltage step: %d\n", mvs);
dprintk(KERN_DEBUG PFX "numpst: 0x%x\n", psb->numpst);
if (psb->numpst != 1) {
printk(KERN_ERR BFX "numpst must be 1\n");
return -ENODEV;
}
dprintk(KERN_DEBUG PFX "cpuid: 0x%x\n", psb->cpuid);
data->plllock = psb->plllocktime;
printk(KERN_INFO PFX "pll lock time: 0x%x, ", data->plllock);
maxvid = psb->maxvid;
printk("maxfid 0x%x (%d MHz), maxvid 0x%x\n",
psb->maxfid, find_freq_from_fid(psb->maxfid), maxvid);
data->numps = psb->numpstates;
if (data->numps < 2) {
printk(KERN_ERR BFX "no p states to transition\n");
return -ENODEV;
}
if (batps == 0) {
batps = data->numps;
} else if (batps > data->numps) {
printk(KERN_ERR BFX "batterypstates > numpstates\n");
batps = data->numps;
} else {
printk(KERN_ERR PFX
"Restricting operation to %d p-states\n", batps);
printk(KERN_ERR PFX
"Check for an updated driver to access all "
"%d p-states\n", data->numps);
}
if (data->numps <= 1) {
printk(KERN_ERR PFX "only 1 p-state to transition\n");
return -ENODEV;
}
pst = (struct pst_s *) (psb + 1);
if (check_pst_table(data, pst, maxvid))
return -EINVAL;
powernow_table = kmalloc((sizeof(struct cpufreq_frequency_table) * (data->numps + 1)), GFP_KERNEL);
if (!powernow_table) {
printk(KERN_ERR PFX "powernow_table memory alloc failure\n");
return -ENOMEM;
}
for (j = 0; j < psb->numpstates; j++) {
powernow_table[j].index = pst[j].fid; /* lower 8 bits */
powernow_table[j].index |= (pst[j].vid << 8); /* upper 8 bits */
}
/* If you want to override your frequency tables, this
is right place. */
for (j = 0; j < data->numps; j++) {
powernow_table[j].frequency = find_freq_from_fid(powernow_table[j].index & 0xff)*1000;
printk(KERN_INFO PFX " %d : fid 0x%x (%d MHz), vid 0x%x\n", j,
powernow_table[j].index & 0xff,
powernow_table[j].frequency/1000,
powernow_table[j].index >> 8);
}
powernow_table[data->numps].frequency = CPUFREQ_TABLE_END;
powernow_table[data->numps].index = 0;
if (query_current_values_with_pending_wait(data)) {
kfree(powernow_table);
return -EIO;
}
printk(KERN_INFO PFX "currfid 0x%x (%d MHz), currvid 0x%x\n",
data->currfid, find_freq_from_fid(data->currfid), data->currvid);
for (j = 0; j < data->numps; j++)
if ((pst[j].fid==data->currfid) && (pst[j].vid==data->currvid))
return 0;
printk(KERN_ERR BFX "currfid/vid do not match PST, ignoring\n");
return 0;
}
printk(KERN_ERR BFX "no PSB\n");
return -ENODEV;
}
/* Take a frequency, and issue the fid/vid transition command */
static int transition_frequency(struct powernow_k8_data *data, unsigned int index)
{
u32 fid;
u32 vid;
int res;
struct cpufreq_freqs freqs;
/* fid are the lower 8 bits of the index we stored into
* the cpufreq frequency table in find_psb_table, vid are
* the upper 8 bits.
*/
fid = data->powernow_table[index].index & 0xFF;
vid = (data->powernow_table[index].index & 0xFF00) >> 8;
dprintk(KERN_DEBUG PFX "table matched fid 0x%x, giving vid 0x%x\n",
fid, vid);
if (query_current_values_with_pending_wait(data))
return 1;
if ((data->currvid == vid) && (data->currfid == fid)) {
dprintk(KERN_DEBUG PFX
"target matches current values (fid 0x%x, vid 0x%x)\n",
fid, vid);
return 0;
}
if ((fid < HI_FID_TABLE_BOTTOM) && (data->currfid < HI_FID_TABLE_BOTTOM)) {
printk(KERN_ERR PFX
"ignoring illegal change in lo freq table-%x to %x\n",
data->currfid, fid);
return 1;
}
dprintk(KERN_DEBUG PFX "changing to fid 0x%x, vid 0x%x\n", fid, vid);
freqs.cpu = data->cpu;
freqs.old = find_freq_from_fid(data->currfid);
freqs.new = find_freq_from_fid(fid);
cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
res = transition_fid_vid(data, fid, vid);
freqs.new = find_freq_from_fid(data->currfid);
cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
return res;
}
/* Driver entry point to switch to the target frequency */
static int powernowk8_target(struct cpufreq_policy *pol, unsigned targfreq, unsigned relation)
{
struct powernow_k8_data *data = powernow_data[pol->cpu];
u32 checkfid = data->currfid;
u32 checkvid = data->currvid;
unsigned int newstate;
if (pending_bit_stuck()) {
printk(KERN_ERR PFX "drv targ fail: change pending bit set\n");
return -EIO;
}
dprintk(KERN_DEBUG PFX "targ: %d kHz, min %d, max %d, relation %d\n",
targfreq, pol->min, pol->max, relation);
if (query_current_values_with_pending_wait(data))
return -EIO;
dprintk(KERN_DEBUG PFX "targ: curr fid 0x%x, vid 0x%x\n",
data->currfid, data->currvid);
if ((checkvid != data->currvid) || (checkfid != data->currfid)) {
printk(KERN_ERR PFX
"error - out of sync, fid 0x%x 0x%x, vid 0x%x 0x%x\n",
checkfid, data->currfid, checkvid, data->currvid);
}
if (cpufreq_frequency_table_target(pol, data->powernow_table, targfreq, relation, &newstate))
return -EINVAL;
if (transition_frequency(data, newstate)) {
printk(KERN_ERR PFX "transition frequency failed\n");
return 1;
}
pol->cur = 1000 * find_freq_from_fid(data->currfid);
return 0;
}
/* Driver entry point to verify the policy and range of frequencies */
static int powernowk8_verify(struct cpufreq_policy *pol)
{
struct powernow_k8_data *data = powernow_data[pol->cpu];
if (pending_bit_stuck()) {
printk(KERN_ERR PFX "failing verify, change pending bit set\n");
return -EIO;
}
return cpufreq_frequency_table_verify(pol, data->powernow_table);
}
/* per CPU init entry point to the driver */
static int __init powernowk8_cpu_init(struct cpufreq_policy *pol)
{
struct powernow_k8_data *data;
int rc;
data = kmalloc(sizeof(struct powernow_k8_data), GFP_KERNEL);
if (!data) {
printk(KERN_ERR PFX "unable to alloc powernow_k8_data");
return -ENOMEM;
}
memset(data,0,sizeof(struct powernow_k8_data));
data->cpu = pol->cpu;
if (pol->cpu != 0) {
printk(KERN_ERR PFX "init not cpu 0\n");
kfree(data);
return -ENODEV;
}
pol->governor = CPUFREQ_DEFAULT_GOVERNOR;
rc = find_psb_table(data);
if (rc) {
kfree(data);
return -ENODEV;
}
if (pending_bit_stuck()) {
printk(KERN_ERR PFX "failing init, change pending bit set\n");
goto err_out;
}
/* Take a crude guess here.
* That guess was in microseconds, so multply with 1000 */
pol->cpuinfo.transition_latency = (((data->rvo + 8) * data->vstable * VST_UNITS_20US)
+ (3 * (1 << data->irt) * 10)) * 1000;
if (query_current_values_with_pending_wait(data))
return -EIO;
pol->cur = 1000 * find_freq_from_fid(data->currfid);
dprintk(KERN_DEBUG PFX "policy current frequency %d kHz\n", pol->cur);
/* min/max the cpu is capable of */
if (cpufreq_frequency_table_cpuinfo(pol, data->powernow_table)) {
printk(KERN_ERR PFX "invalid powernow_table\n");
kfree(data->powernow_table);
kfree(data);
return -EINVAL;
}
cpufreq_frequency_table_get_attr(data->powernow_table, pol->cpu);
printk(KERN_INFO PFX "cpu_init done, current fid 0x%x, vid 0x%x\n",
data->currfid, data->currvid);
powernow_data[pol->cpu] = data;
return 0;
err_out:
kfree(data);
return -ENODEV;
}
static int __exit powernowk8_cpu_exit (struct cpufreq_policy *pol)
{
struct powernow_k8_data *data = powernow_data[pol->cpu];
if (!data)
return -EINVAL;
cpufreq_frequency_table_put_attr(pol->cpu);
kfree(data->powernow_table);
kfree(data);
return 0;
}
static struct freq_attr* powernow_k8_attr[] = {
&cpufreq_freq_attr_scaling_available_freqs,
NULL,
};
static struct cpufreq_driver cpufreq_amd64_driver = {
.verify = powernowk8_verify,
.target = powernowk8_target,
.init = powernowk8_cpu_init,
.exit = powernowk8_cpu_exit,
.name = "powernow-k8",
.owner = THIS_MODULE,
.attr = powernow_k8_attr,
};
/* driver entry point for init */
static int __init powernowk8_init(void)
{
unsigned int i, supported_cpus = 0;
for (i=0; i<NR_CPUS; i++) {
if (!cpu_online(i))
continue;
if (check_supported_cpu(i))
supported_cpus++;
}
if (supported_cpus == num_online_cpus()) {
printk(KERN_INFO PFX "Found %d AMD Athlon 64 / Opteron processors (" VERSION ")\n",
supported_cpus);
return cpufreq_register_driver(&cpufreq_amd64_driver);
}
return -ENODEV;
}
/* driver entry point for term */
static void __exit powernowk8_exit(void)
{
dprintk(KERN_INFO PFX "exit\n");
cpufreq_unregister_driver(&cpufreq_amd64_driver);
}
MODULE_AUTHOR("Paul Devriendt <paul.devriendt@amd.com>");
MODULE_DESCRIPTION("AMD Athlon 64 and Opteron processor frequency driver.");
MODULE_LICENSE("GPL");
module_init(powernowk8_init);
module_exit(powernowk8_exit);
[-- Attachment #3: powernow-k8.h --]
[-- Type: text/plain, Size: 5479 bytes --]
/*
* (c) 2003 Advanced Micro Devices, Inc.
* Your use of this code is subject to the terms and conditions of the
* GNU general public license version 2. See "../../../COPYING" or
* http://www.gnu.org/licenses/gpl.html
*/
struct powernow_k8_data {
unsigned int cpu;
u32 numps; /* number of p-states */
u32 batps; /* number of p-states supported on battery */
/* these values are constant when the PSB is used to determine
* vid/fid pairings, but are modified during the ->target() call
* when ACPI is used */
u32 rvo; /* ramp voltage offset */
u32 irt; /* isochronous relief time */
u32 vidmvs; /* usable value calculated from mvs */
u32 vstable; /* voltage stabilization time, units 20 us */
u32 plllock; /* pll lock time, units 1 us */
/* keep track of the current fid / vid */
u32 currvid;
u32 currfid;
/* the powernow_table includes all frequency and vid/fid pairings:
* fid are the lower 8 bits of the index, vid are the upper 8 bits.
* frequency is in kHz */
struct cpufreq_frequency_table *powernow_table;
};
/* processor's cpuid instruction support */
#define CPUID_PROCESSOR_SIGNATURE 1 /* function 1 */
#define CPUID_F1_FAM 0x00000f00 /* family mask */
#define CPUID_F1_XFAM 0x0ff00000 /* extended family mask */
#define CPUID_F1_MOD 0x000000f0 /* model mask */
#define CPUID_F1_STEP 0x0000000f /* stepping level mask */
#define CPUID_XFAM_MOD 0x0ff00ff0 /* xtended fam, fam + model */
#define ATHLON64_XFAM_MOD 0x00000f40 /* xtended fam, fam + model */
#define OPTERON_XFAM_MOD 0x00000f50 /* xtended fam, fam + model */
#define ATHLON64_REV_C0 8
#define CPUID_GET_MAX_CAPABILITIES 0x80000000
#define CPUID_FREQ_VOLT_CAPABILITIES 0x80000007
#define P_STATE_TRANSITION_CAPABLE 6
/* Model Specific Registers for p-state transitions. MSRs are 64-bit. For */
/* writes (wrmsr - opcode 0f 30), the register number is placed in ecx, and */
/* the value to write is placed in edx:eax. For reads (rdmsr - opcode 0f 32), */
/* the register number is placed in ecx, and the data is returned in edx:eax. */
#define MSR_FIDVID_CTL 0xc0010041
#define MSR_FIDVID_STATUS 0xc0010042
/* Field definitions within the FID VID Low Control MSR : */
#define MSR_C_LO_INIT_FID_VID 0x00010000
#define MSR_C_LO_NEW_VID 0x00001f00
#define MSR_C_LO_NEW_FID 0x0000002f
#define MSR_C_LO_VID_SHIFT 8
/* Field definitions within the FID VID High Control MSR : */
#define MSR_C_HI_STP_GNT_TO 0x000fffff
/* Field definitions within the FID VID Low Status MSR : */
#define MSR_S_LO_CHANGE_PENDING 0x80000000 /* cleared when completed */
#define MSR_S_LO_MAX_RAMP_VID 0x1f000000
#define MSR_S_LO_MAX_FID 0x003f0000
#define MSR_S_LO_START_FID 0x00003f00
#define MSR_S_LO_CURRENT_FID 0x0000003f
/* Field definitions within the FID VID High Status MSR : */
#define MSR_S_HI_MAX_WORKING_VID 0x001f0000
#define MSR_S_HI_START_VID 0x00001f00
#define MSR_S_HI_CURRENT_VID 0x0000001f
/* fids (frequency identifiers) are arranged in 2 tables - lo and hi */
#define LO_FID_TABLE_TOP 6
#define HI_FID_TABLE_BOTTOM 8
#define LO_VCOFREQ_TABLE_TOP 1400 /* corresponding vco frequency values */
#define HI_VCOFREQ_TABLE_BOTTOM 1600
#define MIN_FREQ_RESOLUTION 200 /* fids jump by 2 matching freq jumps by 200 */
#define MAX_FID 0x2a /* Spec only gives FID values as far as 5 GHz */
#define LEAST_VID 0x1e /* Lowest (numerically highest) useful vid value */
#define MIN_FREQ 800 /* Min and max freqs, per spec */
#define MAX_FREQ 5000
#define INVALID_FID_MASK 0xffffffc1 /* not a valid fid if these bits are set */
#define INVALID_VID_MASK 0xffffffe0 /* not a valid vid if these bits are set */
#define STOP_GRANT_5NS 1 /* min poss memory access latency for voltage change */
#define PLL_LOCK_CONVERSION (1000/5) /* ms to ns, then divide by clock period */
#define MAXIMUM_VID_STEPS 1 /* Current cpus only allow a single step of 25mV */
#define VST_UNITS_20US 20 /* Voltage Stabalization Time is in units of 20us */
/*
Version 1.4 of the PSB table. This table is constructed by BIOS and is
to tell the OS's power management driver which VIDs and FIDs are
supported by this particular processor. This information is obtained from
the data sheets for each processor model by the system vendor and
incorporated into the BIOS.
If the data in the PSB / PST is wrong, then this driver will program the
wrong values into hardware, which is very likely to lead to a crash.
*/
#define PSB_ID_STRING "AMDK7PNOW!"
#define PSB_ID_STRING_LEN 10
#define PSB_VERSION_1_4 0x14
struct psb_s {
u8 signature[10];
u8 tableversion;
u8 flags1;
u16 voltagestabilizationtime;
u8 flags2;
u8 numpst;
u32 cpuid;
u8 plllocktime;
u8 maxfid;
u8 maxvid;
u8 numpstates;
};
/* Pairs of fid/vid values are appended to the version 1.4 PSB table. */
struct pst_s {
u8 fid;
u8 vid;
};
#ifdef DEBUG
#define dprintk(msg...) printk(msg)
#else
#define dprintk(msg...) do { } while(0)
#endif
static inline int core_voltage_pre_transition(struct powernow_k8_data *data, u32 reqvid);
static inline int core_voltage_post_transition(struct powernow_k8_data *data, u32 reqvid);
static inline int core_frequency_transition(struct powernow_k8_data *data, u32 reqfid);
[-- Attachment #4: Type: text/plain, Size: 143 bytes --]
_______________________________________________
Cpufreq mailing list
Cpufreq@www.linux.org.uk
http://www.linux.org.uk/mailman/listinfo/cpufreq
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: powernow-k8: support acpi
2004-04-01 23:56 ` Dave Jones
2004-04-02 8:30 ` Pavel Machek
@ 2004-04-02 16:24 ` Bruno Ducrot
2004-04-02 16:39 ` Dave Jones
2004-04-02 16:39 ` Dominik Brodowski
1 sibling, 2 replies; 21+ messages in thread
From: Bruno Ducrot @ 2004-04-02 16:24 UTC (permalink / raw)
To: Dave Jones
Cc: Andrew Morton, mark.langsdorf, Cpufreq mailing list, Pavel Machek,
paul.devriendt, Dominik Brodowski
On Fri, Apr 02, 2004 at 12:56:35AM +0100, Dave Jones wrote:
> On Fri, Mar 26, 2004 at 01:29:32PM +0100, Pavel Machek wrote:
> > This is new version of powernow-k8 driver. It adds SMP support, and
> > support for getting tables through ACPI. (ACPI support is really
> > important, because many machines have broken "legacy" tables). Please
> > apply,
>
> First off, apologies for sitting on this for so long.
> I just made a first-pass attempt at merging this. Most of
> (maybe all?) the simpler bits are in (whitespace changes, typos etc).
> I'll push that out to bkbits soon, so the powernow-k8 diff in the
> next -mm that Andrew builds should be slightly smaller at least.
>
> The ACPI stuff I'm pondering.
> Bruno, how does this stuff look to you? Your acpi perflib stuff is
> already in the pending pool (see the diff in the current -mm tree at
> http://www.kernel.org/pub/linux/kernel/people/akpm/patches/2.6/2.6.5-rc3/2.6.5-rc3-mm4/broken-out/bk-cpufreq.patch)
This is Dominik work. Not mine.
> Would this driver be better off using that ?
At first, yes, it's needed anyway for the SMP case if I'm correct (there is
a flaw in how the v1.4 psb header have been changed wrt 1.2 that make that
impossible apparently).
But I can't say for sure which have to be favored: legacy or ACPI.
I don't have enough dump of ACPI tables and PSB/PST from machines shipped
with AMD64.
--
Bruno Ducrot
-- Which is worse: ignorance or apathy?
-- Don't know. Don't care.
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: powernow-k8: support acpi
2004-04-02 16:24 ` Bruno Ducrot
@ 2004-04-02 16:39 ` Dave Jones
2004-04-02 17:09 ` Pavel Machek
2004-04-02 16:39 ` Dominik Brodowski
1 sibling, 1 reply; 21+ messages in thread
From: Dave Jones @ 2004-04-02 16:39 UTC (permalink / raw)
To: Bruno Ducrot
Cc: Andrew Morton, mark.langsdorf, Cpufreq mailing list, Pavel Machek,
paul.devriendt, Dominik Brodowski
On Fri, Apr 02, 2004 at 06:24:47PM +0200, Bruno Ducrot wrote:
> > http://www.kernel.org/pub/linux/kernel/people/akpm/patches/2.6/2.6.5-rc3/2.6.5-rc3-mm4/broken-out/bk-cpufreq.patch)
> This is Dominik work. Not mine.
My apologies.. I got my wires crossed.
> > Would this driver be better off using that ?
>
> At first, yes, it's needed anyway for the SMP case if I'm correct (there is
> a flaw in how the v1.4 psb header have been changed wrt 1.2 that make that
> impossible apparently).
>
> But I can't say for sure which have to be favored: legacy or ACPI.
> I don't have enough dump of ACPI tables and PSB/PST from machines shipped
> with AMD64.
Ok. I think for the time being we'll roll with what Pavel has come up with.
We can always improve on it later, whichever way we decide to turn.
I'm about to disappear for a day or two. I'll look into integrating whats
outstanding on Sunday. Pavel, if you have time between now and then
to do a diff of whats remaining between your tree and the driver I posted
this afternoon, that would make my life a little easier.
thanks,
Dave
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: powernow-k8: support acpi
2004-04-02 16:24 ` Bruno Ducrot
2004-04-02 16:39 ` Dave Jones
@ 2004-04-02 16:39 ` Dominik Brodowski
2004-04-02 16:46 ` Dominik Brodowski
1 sibling, 1 reply; 21+ messages in thread
From: Dominik Brodowski @ 2004-04-02 16:39 UTC (permalink / raw)
To: Bruno Ducrot
Cc: Andrew Morton, mark.langsdorf, Cpufreq mailing list, Pavel Machek,
Dave Jones, paul.devriendt
[-- Attachment #1.1: Type: text/plain, Size: 299 bytes --]
On Fri, Apr 02, 2004 at 06:24:47PM +0200, Bruno Ducrot wrote:
> But I can't say for sure which have to be favored: legacy or ACPI.
> I don't have enough dump of ACPI tables and PSB/PST from machines shipped
> with AMD64.
AFAIK, AMD will deprecate PSB/PST soon, so ACPI should be favoured.
Dominik
[-- Attachment #1.2: Type: application/pgp-signature, Size: 189 bytes --]
[-- Attachment #2: Type: text/plain, Size: 143 bytes --]
_______________________________________________
Cpufreq mailing list
Cpufreq@www.linux.org.uk
http://www.linux.org.uk/mailman/listinfo/cpufreq
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: powernow-k8: support acpi
2004-04-02 16:39 ` Dominik Brodowski
@ 2004-04-02 16:46 ` Dominik Brodowski
0 siblings, 0 replies; 21+ messages in thread
From: Dominik Brodowski @ 2004-04-02 16:46 UTC (permalink / raw)
To: Bruno Ducrot, Dave Jones, Andrew Morton, mark.langsdorf,
Cpufreq mailing list, Pavel Machek, paul.devriendt
[-- Attachment #1.1: Type: text/plain, Size: 566 bytes --]
On Fri, Apr 02, 2004 at 06:39:55PM +0200, Dominik Brodowski wrote:
> On Fri, Apr 02, 2004 at 06:24:47PM +0200, Bruno Ducrot wrote:
> > But I can't say for sure which have to be favored: legacy or ACPI.
> > I don't have enough dump of ACPI tables and PSB/PST from machines shipped
> > with AMD64.
>
> AFAIK, AMD will deprecate PSB/PST soon, so ACPI should be favoured.
Another reason is that with PSB/PST the system might not run at highest
possible frequency, even when connected to AC-power [batps]. So ACPI should
really be favored, IMHO.
Dominik
[-- Attachment #1.2: Type: application/pgp-signature, Size: 189 bytes --]
[-- Attachment #2: Type: text/plain, Size: 143 bytes --]
_______________________________________________
Cpufreq mailing list
Cpufreq@www.linux.org.uk
http://www.linux.org.uk/mailman/listinfo/cpufreq
^ permalink raw reply [flat|nested] 21+ messages in thread
* RE: powernow-k8: support acpi
@ 2004-04-02 16:59 paul.devriendt
2004-04-02 18:40 ` Bruno Ducrot
0 siblings, 1 reply; 21+ messages in thread
From: paul.devriendt @ 2004-04-02 16:59 UTC (permalink / raw)
To: ducrot, davej; +Cc: akpm, linux, mark.langsdorf, cpufreq, pavel
> But I can't say for sure which have to be favored: legacy or ACPI.
> I don't have enough dump of ACPI tables and PSB/PST from
> machines shipped
> with AMD64.
>
> Bruno Ducrot
ACPI please ....
1. It seems that system manufacturers are testing with Windows XP
only before shipping their machines. Windows XP uses ACPI, so that
is the mechanism that is most likely to have accurate pstate data.
2. We are seeing a lot of broken PSB/PSTs.
3. The PSB/PST does not support SMP in its current form. ACPI does.
4. Current plan is to not have PSB/PST at all for future processors.
5. You need the ACPI support for the _PPC object which gives battery
and thermal control.
The downside to making the ACPI favored is that it is newer. But,
given the PSB/PST breakages from bad BIOS data, the newer is going
to be more robust.
Paul.
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: powernow-k8: support acpi
2004-04-02 16:39 ` Dave Jones
@ 2004-04-02 17:09 ` Pavel Machek
2004-04-06 13:07 ` Dave Jones
0 siblings, 1 reply; 21+ messages in thread
From: Pavel Machek @ 2004-04-02 17:09 UTC (permalink / raw)
To: Dave Jones
Cc: Andrew Morton, Bruno Ducrot, mark.langsdorf, Cpufreq mailing list,
paul.devriendt, Dominik Brodowski
Hi!
> > > Would this driver be better off using that ?
> >
> > At first, yes, it's needed anyway for the SMP case if I'm correct (there is
> > a flaw in how the v1.4 psb header have been changed wrt 1.2 that make that
> > impossible apparently).
> >
> > But I can't say for sure which have to be favored: legacy or ACPI.
> > I don't have enough dump of ACPI tables and PSB/PST from machines shipped
> > with AMD64.
>
> Ok. I think for the time being we'll roll with what Pavel has come up with.
> We can always improve on it later, whichever way we decide to turn.
Yes, that looks best. At least patches should be way smaller from now on.
> I'm about to disappear for a day or two. I'll look into integrating whats
> outstanding on Sunday. Pavel, if you have time between now and then
> to do a diff of whats remaining between your tree and the driver I posted
> this afternoon, that would make my life a little easier.
Here it is: (I was not able to locate your changes, so it is possible
I'm reverting some of those; but few extra inlines should not hurt
that bad :-).
Pavel
--- clean-dj//arch/i386/kernel/cpu/cpufreq/powernow-k8.c 2004-04-02 17:46:20.000000000 +0200
+++ linux-pn/arch/i386/kernel/cpu/cpufreq/powernow-k8.c 2004-04-02 19:05:25.000000000 +0200
@@ -13,8 +13,9 @@
* Licensed under the terms of the GNU GPL License version 2.
* Based upon datasheets & sample CPUs kindly provided by AMD.
*
- * Valuable input gratefully received from Dave Jones, Pavel Machek,
- * Dominik Brodowski, and others.
+ * Valuable input gratefully received from Dave Jones, Pavel Machek, Dominik
+ * Brodowski, and others.
+ *
* Processor information obtained from Chapter 9 (Power and Thermal Management)
* of the "BIOS and Kernel Developer's Guide for the AMD Athlon 64 and AMD
* Opteron Processors" available for download from www.amd.com
@@ -32,42 +33,41 @@
#include <asm/io.h>
#include <asm/delay.h>
+#ifdef CONFIG_X86_POWERNOW_K8_ACPI
+
+#include <linux/acpi.h>
+#include <acpi/processor.h>
+
+#endif /* CONFIG_X86_POWERNOW_K8_ACPI */
+
+
#define PFX "powernow-k8: "
-#define BFX PFX "BIOS error: "
-#define VERSION "version 1.00.08a"
+#define VERSION "version 1.20.08b - March 20, 2004"
#include "powernow-k8.h"
+/* serialize freq changes */
+static DECLARE_MUTEX(fidvid_sem);
+
static struct powernow_k8_data *powernow_data[NR_CPUS];
-/*
-The PSB table supplied by BIOS allows for the definition of the number of
-p-states that can be used when running on a/c, and the number of p-states
-that can be used when running on battery. This allows laptop manufacturers
-to force the system to save power when running from battery. The relationship
-is :
- 1 <= number_of_battery_p_states <= maximum_number_of_p_states
-
-This driver does NOT have the support in it to detect transitions from
-a/c power to battery power, and thus trigger the transition to a lower
-p-state if required. This is because I need ACPI and the 2.6 kernel to do
-this, and this is a 2.4 kernel driver. Check back for a new improved driver
-for the 2.6 kernel soon.
-
-This code therefore assumes it is on battery at all times, and thus
-restricts performance to number_of_battery_p_states. For desktops,
- number_of_battery_p_states == maximum_number_of_pstates,
-so this is not actually a restriction.
-*/
-static u32 batps; /* limit on the number of p states when on battery */
- /* - set by BIOS in the PSB/PST */
+/* Return a frequency in MHz, given an input fid */
+static inline u32 find_freq_from_fid(u32 fid)
+{
+ return 800 + (fid * 100);
+}
- /* Return a frequency in MHz, given an input fid */
-static u32 find_freq_from_fid(u32 fid)
+/* Return a frequency in KHz, given an input fid */
+static inline u32 find_khz_freq_from_fid(u32 fid)
{
- return 800 + (fid * 100);
+ return 1000 * find_freq_from_fid(fid);
}
+/* Return a voltage in miliVolts, given an input vid */
+static inline u32 find_milivolts_from_vid(struct powernow_k8_data *data, u32 vid)
+{
+ return 1550-vid*25;
+}
/* Return the vco fid for an input fid */
static u32 convert_fid_to_vco_fid(u32 fid)
@@ -92,8 +95,8 @@
}
/*
- * Update the global current fid / vid values from the status msr.
- * Returns 1 on error.
+ * Update the global current fid / vid values from the status msr. Returns
+ * 1 on error.
*/
static int query_current_values_with_pending_wait(struct powernow_k8_data *data)
{
@@ -122,13 +125,28 @@
return;
}
-/* the voltage stabalization time */
+/* the voltage stabilization time */
static inline void count_off_vst(struct powernow_k8_data *data)
{
udelay(data->vstable * VST_UNITS_20US);
return;
}
+/* need to init the control msr to a safe value (for each cpu) */
+static void fidvid_msr_init(void)
+{
+ u32 lo, hi;
+ u8 fid, vid;
+
+ rdmsr(MSR_FIDVID_STATUS, lo, hi);
+ vid = hi & MSR_S_HI_CURRENT_VID;
+ fid = lo & MSR_S_LO_CURRENT_FID;
+ lo = fid | (vid << MSR_C_LO_VID_SHIFT);
+ hi = MSR_C_HI_STP_GNT_BENIGN;
+ dprintk(PFX "cpu%d, init lo %x, hi %x\n", smp_processor_id(), lo, hi);
+ wrmsr(MSR_FIDVID_CTL, lo, hi);
+}
+
/* write the new fid value along with the other control fields to the msr */
static int write_new_fid(struct powernow_k8_data *data, u32 fid)
{
@@ -141,15 +159,11 @@
}
lo = fid | (data->currvid << MSR_C_LO_VID_SHIFT) | MSR_C_LO_INIT_FID_VID;
-
dprintk(KERN_DEBUG PFX "writing fid %x, lo %x, hi %x\n",
fid, lo, data->plllock * PLL_LOCK_CONVERSION);
-
wrmsr(MSR_FIDVID_CTL, lo, data->plllock * PLL_LOCK_CONVERSION);
-
if (query_current_values_with_pending_wait(data))
return 1;
-
count_off_irt(data);
if (savevid != data->currvid) {
@@ -160,7 +174,7 @@
if (fid != data->currfid) {
printk(KERN_ERR PFX "fid trans failed, fid %x, curr %x\n", fid,
- data->currfid);
+ data->currfid);
return 1;
}
@@ -179,12 +193,9 @@
}
lo = data->currfid | (vid << MSR_C_LO_VID_SHIFT) | MSR_C_LO_INIT_FID_VID;
-
dprintk(KERN_DEBUG PFX "writing vid %x, lo %x, hi %x\n",
vid, lo, STOP_GRANT_5NS);
-
wrmsr(MSR_FIDVID_CTL, lo, STOP_GRANT_5NS);
-
if (query_current_values_with_pending_wait(data))
return 1;
@@ -196,8 +207,8 @@
if (vid != data->currvid) {
printk(KERN_ERR PFX "vid trans failed, vid %x, curr %x\n", vid,
- data->currvid);
- return 1;
+ data->currvid);
+ return 1;
}
return 0;
@@ -212,56 +223,49 @@
{
if ((data->currvid - reqvid) > step)
reqvid = data->currvid - step;
-
if (write_new_vid(data, reqvid))
return 1;
-
count_off_vst(data);
-
return 0;
}
/* Change the fid and vid, by the 3 phases. */
-static int transition_fid_vid(struct powernow_k8_data *data, u32 reqfid, u32 reqvid)
+static inline int transition_fid_vid(struct powernow_k8_data *data, u32 reqfid, u32 reqvid)
{
if (core_voltage_pre_transition(data, reqvid))
return 1;
-
if (core_frequency_transition(data, reqfid))
return 1;
-
if (core_voltage_post_transition(data, reqvid))
return 1;
-
if (query_current_values_with_pending_wait(data))
return 1;
if ((reqfid != data->currfid) || (reqvid != data->currvid)) {
- printk(KERN_ERR PFX "failed (cpu%d): req 0x%x 0x%x, curr 0x%x 0x%x\n",
- smp_processor_id(),
- reqfid, reqvid, data->currfid, data->currvid);
+ printk(KERN_ERR PFX "failed (cpu%d): req %x %x, curr %x %x\n",
+ smp_processor_id(),
+ reqfid, reqvid, data->currfid, data->currvid);
return 1;
}
- dprintk(KERN_INFO PFX "transitioned (cpu%d): new fid 0x%x, vid 0x%x\n",
+ dprintk(KERN_INFO PFX "transitioned (cpu%d): new fid %x, vid %x\n",
smp_processor_id(), data->currfid, data->currvid);
-
return 0;
}
/* Phase 1 - core voltage transition ... setup voltage */
-static int core_voltage_pre_transition(struct powernow_k8_data *data, u32 reqvid)
+static inline int core_voltage_pre_transition(struct powernow_k8_data *data, u32 reqvid)
{
u32 rvosteps = data->rvo;
u32 savefid = data->currfid;
dprintk(KERN_DEBUG PFX
- "ph1 (cpu%d): start, currfid 0x%x, currvid 0x%x, reqvid 0x%x, rvo 0x%x\n",
- smp_processor_id();
+ "ph1 (cpu%d): start, currfid %x, currvid %x, reqvid %x, rvo %x\n",
+ smp_processor_id(),
data->currfid, data->currvid, reqvid, data->rvo);
while (data->currvid > reqvid) {
- dprintk(KERN_DEBUG PFX "ph1: curr 0x%x, req vid 0x%x\n",
+ dprintk(KERN_DEBUG PFX "ph1: curr %x, req vid %x\n",
data->currvid, reqvid);
if (decrease_vid_code_by_step(data, reqvid, data->vidmvs))
return 1;
@@ -272,7 +276,7 @@
rvosteps = 0;
} else {
dprintk(KERN_DEBUG PFX
- "ph1: changing vid for rvo, req 0x%x\n",
+ "ph1: changing vid for rvo, req %x\n",
data->currvid - 1);
if (decrease_vid_code_by_step(data, data->currvid - 1, 1))
return 1;
@@ -284,18 +288,18 @@
return 1;
if (savefid != data->currfid) {
- printk(KERN_ERR PFX "ph1 err, currfid changed 0x%x\n", data->currfid);
+ printk(KERN_ERR PFX "ph1: err, currfid changed %x\n", data->currfid);
return 1;
}
- dprintk(KERN_DEBUG PFX "ph1 complete, currfid 0x%x, currvid 0x%x\n",
+ dprintk(KERN_DEBUG PFX "ph1: complete, currfid %x, currvid %x\n",
data->currfid, data->currvid);
return 0;
}
/* Phase 2 - core frequency transition */
-static int core_frequency_transition(struct powernow_k8_data *data, u32 reqfid)
+static inline int core_frequency_transition(struct powernow_k8_data *data, u32 reqfid)
{
u32 vcoreqfid;
u32 vcocurrfid;
@@ -303,18 +307,18 @@
u32 savevid = data->currvid;
if ((reqfid < HI_FID_TABLE_BOTTOM) && (data->currfid < HI_FID_TABLE_BOTTOM)) {
- printk(KERN_ERR PFX "ph2: illegal lo-lo transition 0x%x 0x%x\n",
- reqfid, data->currfid);
+ printk(KERN_ERR PFX "ph2: illegal lo-lo transition %x %x\n",
+ reqfid, data->currfid);
return 1;
}
if (data->currfid == reqfid) {
- printk(KERN_ERR PFX "ph2 null fid transition 0x%x\n", data->currfid);
+ printk(KERN_ERR PFX "ph2: null fid transition %x\n", data->currfid);
return 0;
}
dprintk(KERN_DEBUG PFX
- "ph2 (cpu%d): starting, currfid 0x%x, currvid 0x%x, reqfid 0x%x\n",
+ "ph2 (cpu%d): starting, currfid %x, currvid %x, reqfid %x\n",
smp_processor_id(),
data->currfid, data->currvid, reqfid);
@@ -347,36 +351,35 @@
if (write_new_fid(data, reqfid))
return 1;
-
if (query_current_values_with_pending_wait(data))
return 1;
if (data->currfid != reqfid) {
printk(KERN_ERR PFX
- "ph2: mismatch, failed fid transition, curr 0x%x, req 0x%x\n",
- data->currfid, reqfid);
+ "ph2: mismatch, failed fid transition, curr %x, req %x\n",
+ data->currfid, reqfid);
return 1;
}
if (savevid != data->currvid) {
printk(KERN_ERR PFX "ph2: vid changed, save %x, curr %x\n",
- savevid, data->currvid);
+ savevid, data->currvid);
return 1;
}
- dprintk(KERN_DEBUG PFX "ph2 complete, currfid 0x%x, currvid 0x%x\n",
+ dprintk(KERN_DEBUG PFX "ph2: complete, currfid %x, currvid %x\n",
data->currfid, data->currvid);
return 0;
}
/* Phase 3 - core voltage transition flow ... jump to the final vid. */
-static int core_voltage_post_transition(struct powernow_k8_data *data, u32 reqvid)
+static inline int core_voltage_post_transition(struct powernow_k8_data *data, u32 reqvid)
{
u32 savefid = data->currfid;
u32 savereqvid = reqvid;
- dprintk(KERN_DEBUG PFX "ph3 (cpu%d): starting, currfid 0x%x, currvid 0x%x\n",
+ dprintk(KERN_DEBUG PFX "ph3 (cpu%d): starting, currfid %x, currvid %x\n",
smp_processor_id(),
data->currfid, data->currvid);
@@ -403,99 +406,92 @@
return 1;
if (savereqvid != data->currvid) {
- dprintk(KERN_ERR PFX "ph3 failed, currvid 0x%x\n", data->currvid);
+ printk(KERN_ERR PFX "ph3: failed, currvid %x\n", data->currvid);
return 1;
}
if (savefid != data->currfid) {
- dprintk(KERN_ERR PFX "ph3 failed, currfid changed 0x%x\n",
+ printk(KERN_ERR PFX "ph3: failed, currfid changed %x\n",
data->currfid);
return 1;
}
- dprintk(KERN_DEBUG PFX "ph3 complete, currfid 0x%x, currvid 0x%x\n",
+ dprintk(KERN_DEBUG PFX "ph3: complete, currfid %x, currvid %x\n",
data->currfid, data->currvid);
-
return 0;
}
-static int check_supported_cpu(unsigned int cpu)
+static inline int check_supported_cpu(unsigned int cpu)
{
- struct cpuinfo_x86 *c = cpu_data;
+ cpumask_t oldmask = CPU_MASK_ALL;
u32 eax, ebx, ecx, edx;
+ unsigned int rc = 0;
- if (num_online_cpus() != 1) {
- printk(KERN_INFO PFX "multiprocessor systems not supported\n");
- return 0;
- }
+ oldmask = current->cpus_allowed;
+ set_cpus_allowed(current, cpumask_of_cpu(cpu));
+ schedule();
- if (c->x86_vendor != X86_VENDOR_AMD) {
-#ifdef MODULE
- printk(KERN_INFO PFX "Not an AMD processor\n");
-#endif
- return 0;
+ if (smp_processor_id() != cpu) {
+ printk(KERN_ERR "limiting to cpu %u failed\n", cpu);
+ goto out;
}
+ if (current_cpu_data.x86_vendor != X86_VENDOR_AMD)
+ goto out;
+
eax = cpuid_eax(CPUID_PROCESSOR_SIGNATURE);
if ((eax & CPUID_XFAM_MOD) == ATHLON64_XFAM_MOD) {
dprintk(KERN_DEBUG PFX "AMD Althon 64 Processor found\n");
- if ((eax & CPUID_F1_STEP) < ATHLON64_REV_C0) {
- printk(KERN_INFO PFX "Revision C0 or better "
- "AMD Athlon 64 processor required\n");
- return 0;
- }
} else if ((eax & CPUID_XFAM_MOD) == OPTERON_XFAM_MOD) {
dprintk(KERN_DEBUG PFX "AMD Opteron Processor found\n");
} else {
printk(KERN_INFO PFX
"AMD Athlon 64 or AMD Opteron processor required\n");
- return 0;
+ goto out;
}
eax = cpuid_eax(CPUID_GET_MAX_CAPABILITIES);
if (eax < CPUID_FREQ_VOLT_CAPABILITIES) {
- printk(KERN_INFO PFX
- "No frequency change capabilities detected\n");
- return 0;
+ printk(KERN_INFO PFX "No freq change capabilities\n");
+ goto out;
}
cpuid(CPUID_FREQ_VOLT_CAPABILITIES, &eax, &ebx, &ecx, &edx);
if ((edx & P_STATE_TRANSITION_CAPABLE) != P_STATE_TRANSITION_CAPABLE) {
printk(KERN_INFO PFX "Power state transitions not supported\n");
- return 0;
+ goto out;
}
- printk(KERN_INFO PFX "Found AMD64 processor supporting PowerNow (" VERSION ")\n");
- return 1;
+ rc = 1;
+
+ out:
+ set_cpus_allowed(current, oldmask);
+ schedule();
+ return rc;
}
static int check_pst_table(struct powernow_k8_data *data, struct pst_s *pst, u8 maxvid)
{
unsigned int j;
- u8 lastfid = 0xFF;
+ u8 lastfid = 0xff;
for (j = 0; j < data->numps; j++) {
if (pst[j].vid > LEAST_VID) {
- printk(KERN_ERR PFX "vid %d invalid : 0x%x\n", j, pst[j].vid);
+ printk(KERN_ERR PFX "vid %d bad: %x\n", j, pst[j].vid);
return -EINVAL;
}
if (pst[j].vid < data->rvo) { /* vid + rvo >= 0 */
- printk(KERN_ERR PFX
- "BIOS error - 0 vid exceeded with pstate %d\n",
- j);
+ printk(KERN_ERR PFX "0 vid exceeded with pst %d\n", j);
return -ENODEV;
}
- if (pst[j].vid < maxvid + data->rvo) { /* vid + rvo >= maxvid */
- printk(KERN_ERR PFX
- "BIOS error - maxvid exceeded with pstate %d\n",
- j);
+ if (pst[j].vid < maxvid + data->rvo) { /* vid + rvo >= maxvid */
+ printk(KERN_ERR PFX "maxvid exceeded with pst %d\n", j);
return -ENODEV;
}
if ((pst[j].fid > MAX_FID)
|| (pst[j].fid & 1)
|| (j && (pst[j].fid < HI_FID_TABLE_BOTTOM))) {
- /* Only first fid is allowed to be in "low" range */
- printk(KERN_ERR PFX "fid %d invalid : 0x%x\n", j, pst[j].fid);
+ printk(KERN_ERR PFX "fid %d bad: %x\n", j, pst[j].fid);
return -EINVAL;
}
if (pst[j].fid < lastfid)
@@ -505,20 +501,87 @@
printk(KERN_ERR PFX "lastfid invalid\n");
return -EINVAL;
}
- if (lastfid > LO_FID_TABLE_TOP) {
- printk(KERN_INFO PFX "first fid not from lo freq table\n");
+ if (lastfid > LO_FID_TABLE_TOP)
+ printk(KERN_INFO PFX "first fid not from lo freq table\n");
+
+ return 0;
+}
+
+static void print_basics(struct powernow_k8_data *data)
+{
+ int j;
+ for (j = 0; j < data->numps; j++) {
+ printk(KERN_INFO PFX " %d : fid %x (%d MHz), vid %x (%d mV)\n", j,
+ data->powernow_table[j].index & 0xff,
+ data->powernow_table[j].frequency/1000,
+ data->powernow_table[j].index >> 8,
+ find_milivolts_from_vid(data, data->powernow_table[j].index >> 8));
+ }
+ if (data->batps)
+ printk(KERN_INFO PFX "Only %d pstates on battery\n", data->batps);
+}
+
+static inline int fill_powernow_table(struct powernow_k8_data *data, struct pst_s *pst, u8 maxvid)
+{
+ struct cpufreq_frequency_table *powernow_table;
+ unsigned int j;
+
+ if (data->batps) { /* use ACPI support to get full speed on mains power */
+ printk(KERN_WARNING PFX "Only %d pstates usable (use ACPI driver for full range\n", data->batps);
+ data->numps = data->batps;
+ }
+
+ for ( j=1; j<data->numps; j++ )
+ if (pst[j-1].fid >= pst[j].fid) {
+ printk(KERN_ERR PFX "PST out of sequence\n");
+ return -EINVAL;
+ }
+
+ if (data->numps < 2) {
+ printk(KERN_ERR PFX "no p states to transition\n");
+ return -ENODEV;
}
+ if (check_pst_table(data, pst, maxvid))
+ return -EINVAL;
+
+ powernow_table = kmalloc((sizeof(struct cpufreq_frequency_table)
+ * (data->numps + 1)), GFP_KERNEL);
+ if (!powernow_table) {
+ printk(KERN_ERR PFX "powernow_table memory alloc failure\n");
+ return -ENOMEM;
+ }
+
+ for (j = 0; j < data->numps; j++) {
+ powernow_table[j].index = pst[j].fid; /* lower 8 bits */
+ powernow_table[j].index |= (pst[j].vid << 8); /* upper 8 bits */
+ powernow_table[j].frequency = find_khz_freq_from_fid(pst[j].fid);
+ }
+ powernow_table[data->numps].frequency = CPUFREQ_TABLE_END;
+ powernow_table[data->numps].index = 0;
+
+ if (query_current_values_with_pending_wait(data)) {
+ kfree(powernow_table);
+ return -EIO;
+ }
+
+ dprintk(KERN_INFO PFX "cfid %x, cvid %x\n", data->currfid, data->currvid);
+ data->powernow_table = powernow_table;
+ print_basics(data);
+
+ for (j = 0; j < data->numps; j++)
+ if ((pst[j].fid==data->currfid) && (pst[j].vid==data->currvid))
+ return 0;
+
+ dprintk(KERN_ERR PFX "currfid/vid do not match PST, ignoring\n");
return 0;
}
/* Find and validate the PSB/PST table in BIOS. */
-static int find_psb_table(struct powernow_k8_data *data)
+static inline int find_psb_table(struct powernow_k8_data *data)
{
- struct cpufreq_frequency_table *powernow_table;
struct psb_s *psb;
- struct pst_s *pst;
- unsigned int i, j;
+ unsigned int i;
u32 mvs;
u8 maxvid;
@@ -530,133 +593,192 @@
if (memcmp(psb, PSB_ID_STRING, PSB_ID_STRING_LEN) != 0)
continue;
- dprintk(KERN_DEBUG PFX "found PSB header at 0x%p\n", psb);
-
- dprintk(KERN_DEBUG PFX "table vers: 0x%x\n", psb->tableversion);
+ dprintk(KERN_DEBUG PFX "found PSB header at %p\n", psb);
+ dprintk(KERN_DEBUG PFX "table version: %x\n",
+ psb->tableversion);
if (psb->tableversion != PSB_VERSION_1_4) {
- printk(KERN_INFO BFX "PSB table is not v1.4\n");
+ printk(KERN_INFO PFX "PSB table is not v1.4\n");
return -ENODEV;
}
- dprintk(KERN_DEBUG PFX "flags: 0x%x\n", psb->flags1);
+ dprintk(KERN_DEBUG PFX "flags: %x\n", psb->flags1);
if (psb->flags1) {
- printk(KERN_ERR BFX "unknown flags\n");
+ printk(KERN_ERR PFX "unknown flags\n");
return -ENODEV;
}
data->vstable = psb->voltagestabilizationtime;
dprintk(KERN_INFO PFX "voltage stabilization time: %d(*20us)\n", data->vstable);
- dprintk(KERN_DEBUG PFX "flags2: 0x%x\n", psb->flags2);
+ dprintk(KERN_DEBUG PFX "flags2: %x\n", psb->flags2);
+
data->rvo = psb->flags2 & 3;
data->irt = ((psb->flags2) >> 2) & 3;
mvs = ((psb->flags2) >> 4) & 3;
data->vidmvs = 1 << mvs;
data->batps = ((psb->flags2) >> 6) & 3;
- printk(KERN_INFO PFX "voltage stable in %d usec", data->vstable * 20);
- if (data->batps)
- printk(", only %d lowest states on battery", batps);
- printk(", ramp voltage offset: %d", data->rvo);
- printk(", isochronous relief time: %d", data->irt);
- printk(", maximum voltage step: %d\n", mvs);
+ dprintk(KERN_INFO PFX "ramp voltage offset: %d\n", data->rvo);
+ dprintk(KERN_INFO PFX "isochronous relief time: %d\n", data->irt);
+ dprintk(KERN_INFO PFX "maximum voltage step: %d - %x\n",
+ mvs, data->vidmvs);
dprintk(KERN_DEBUG PFX "numpst: 0x%x\n", psb->numpst);
if (psb->numpst != 1) {
- printk(KERN_ERR BFX "numpst must be 1\n");
+ printk(KERN_ERR PFX "numpst must be 1\n");
return -ENODEV;
}
- dprintk(KERN_DEBUG PFX "cpuid: 0x%x\n", psb->cpuid);
-
data->plllock = psb->plllocktime;
- printk(KERN_INFO PFX "pll lock time: 0x%x, ", data->plllock);
-
+ dprintk(KERN_INFO PFX "plllocktime: %x (units 1us)\n",
+ psb->plllocktime);
+ dprintk(KERN_INFO PFX "maxfid: %x\n", psb->maxfid);
+ dprintk(KERN_INFO PFX "maxvid: %x\n", psb->maxvid);
maxvid = psb->maxvid;
- printk("maxfid 0x%x (%d MHz), maxvid 0x%x\n",
- psb->maxfid, find_freq_from_fid(psb->maxfid), maxvid);
data->numps = psb->numpstates;
- if (data->numps < 2) {
- printk(KERN_ERR BFX "no p states to transition\n");
- return -ENODEV;
- }
+ dprintk(KERN_INFO PFX "numpstates: %x\n", data->numps);
+ return fill_powernow_table(data, (struct pst_s *)(psb+1), maxvid);
+ }
- if (batps == 0) {
- batps = data->numps;
- } else if (batps > data->numps) {
- printk(KERN_ERR BFX "batterypstates > numpstates\n");
- batps = data->numps;
- } else {
- printk(KERN_ERR PFX
- "Restricting operation to %d p-states\n", batps);
- printk(KERN_ERR PFX
- "Check for an updated driver to access all "
- "%d p-states\n", data->numps);
- }
+ /*
+ * If you see this message, complain to BIOS manufacturer. If
+ * he tells you "we do not support Linux" or some similar
+ * nonsense, remember that Windows 2000 uses the same legacy
+ * mechanism that the old Linux PSB driver uses. Tell them it
+ * is broken with Windows 2000.
+ *
+ * The reference to the AMD documentation is chapter 9 in the
+ * BIOS and Kernel Developer's Guide, which is available on
+ * www.amd.com
+ */
+ printk(KERN_ERR PFX "BIOS error - no PSB\n");
+ return -ENODEV;
+}
- if (data->numps <= 1) {
- printk(KERN_ERR PFX "only 1 p-state to transition\n");
- return -ENODEV;
- }
- pst = (struct pst_s *) (psb + 1);
- if (check_pst_table(data, pst, maxvid))
- return -EINVAL;
+#ifdef CONFIG_X86_POWERNOW_K8_ACPI
- powernow_table = kmalloc((sizeof(struct cpufreq_frequency_table) * (data->numps + 1)), GFP_KERNEL);
- if (!powernow_table) {
- printk(KERN_ERR PFX "powernow_table memory alloc failure\n");
- return -ENOMEM;
- }
+static inline void powernow_k8_acpi_pst_values(struct powernow_k8_data *data, unsigned int index)
+{
+ if (!data->acpi_data.state_count)
+ return;
- for (j = 0; j < psb->numpstates; j++) {
- powernow_table[j].index = pst[j].fid; /* lower 8 bits */
- powernow_table[j].index |= (pst[j].vid << 8); /* upper 8 bits */
- }
+ data->irt = (data->acpi_data.states[index].control >> IRT_SHIFT) & IRT_MASK;
+ data->rvo = (data->acpi_data.states[index].control >> RVO_SHIFT) & RVO_MASK;
+ data->plllock = (data->acpi_data.states[index].control >> PLL_L_SHIFT) & PLL_L_MASK;
+ data->vidmvs = 1 << ((data->acpi_data.states[index].control >> MVS_SHIFT) & MVS_MASK);
+ data->vstable = (data->acpi_data.states[index].control >> VST_SHIFT) & VST_MASK;
+}
+
+static int powernow_k8_cpu_init_acpi(struct powernow_k8_data *data)
+{
+ int i;
+ int cntlofreq = 0;
+ struct cpufreq_frequency_table *powernow_table;
+
+ if (acpi_processor_register_performance(&data->acpi_data, data->cpu)) {
+ dprintk(KERN_DEBUG PFX "register performance failed\n");
+ return -EIO;
+ }
+
+ /* verify the data contained in the ACPI structures */
+ if (data->acpi_data.state_count <= 1) {
+ dprintk(KERN_DEBUG PFX "No ACPI P-States\n");
+ goto err_out;
+ }
- /* If you want to override your frequency tables, this
- is right place. */
+ if ((data->acpi_data.control_register.space_id != ACPI_ADR_SPACE_FIXED_HARDWARE) ||
+ (data->acpi_data.status_register.space_id != ACPI_ADR_SPACE_FIXED_HARDWARE)) {
+ dprintk(KERN_DEBUG PFX "Invalid control/status registers\n");
+ goto err_out;
+ }
+
+ /* fill in data->powernow_table */
+ powernow_table = kmalloc((sizeof(struct cpufreq_frequency_table)
+ * (data->acpi_data.state_count + 1)), GFP_KERNEL);
+ if (!powernow_table) {
+ dprintk(KERN_ERR PFX "powernow_table memory alloc failure\n");
+ goto err_out;
+ }
- for (j = 0; j < data->numps; j++) {
- powernow_table[j].frequency = find_freq_from_fid(powernow_table[j].index & 0xff)*1000;
- printk(KERN_INFO PFX " %d : fid 0x%x (%d MHz), vid 0x%x\n", j,
- powernow_table[j].index & 0xff,
- powernow_table[j].frequency/1000,
- powernow_table[j].index >> 8);
+ for (i = 0; i < data->acpi_data.state_count; i++) {
+ u32 fid = data->acpi_data.states[i].control & FID_MASK;
+ u32 vid = (data->acpi_data.states[i].control >> VID_SHIFT) & VID_MASK;
+
+ dprintk(KERN_INFO PFX " %d : fid %x, vid %x\n", i, fid, vid);
+
+ powernow_table[i].index = fid; /* lower 8 bits */
+ powernow_table[i].index |= (vid << 8); /* upper 8 bits */
+ powernow_table[i].frequency = find_khz_freq_from_fid(fid);
+
+ /* verify frequency is OK */
+ if ((powernow_table[i].frequency > (MAX_FREQ * 1000)) ||
+ (powernow_table[i].frequency < (MIN_FREQ * 1000))) {
+ dprintk(KERN_INFO PFX "invalid freq %u kHz\n", powernow_table[i].frequency);
+ powernow_table[i].frequency = CPUFREQ_ENTRY_INVALID;
+ continue;
}
- powernow_table[data->numps].frequency = CPUFREQ_TABLE_END;
- powernow_table[data->numps].index = 0;
+ /* verify only 1 entry from the lo frequency table */
+ if ((fid < HI_FID_TABLE_BOTTOM) && (cntlofreq++)) {
+ printk(KERN_ERR PFX "Too many lo freq table entries\n");
+ goto err_out;
+ }
- if (query_current_values_with_pending_wait(data)) {
- kfree(powernow_table);
- return -EIO;
+ if (powernow_table[i].frequency != (data->acpi_data.states[i].core_frequency * 1000)) {
+ printk(KERN_INFO PFX "invalid freq entries %u kHz vs. %u kHz\n",
+ powernow_table[i].frequency,
+ (unsigned int) (data->acpi_data.states[i].core_frequency * 1000));
+ powernow_table[i].frequency = CPUFREQ_ENTRY_INVALID;
+ continue;
}
+ }
+ powernow_table[data->acpi_data.state_count].frequency = CPUFREQ_TABLE_END;
+ powernow_table[data->acpi_data.state_count].index = 0;
+ data->powernow_table = powernow_table;
+
+ /* fill in data */
+ data->numps = data->acpi_data.state_count;
+ print_basics(data);
- printk(KERN_INFO PFX "currfid 0x%x (%d MHz), currvid 0x%x\n",
- data->currfid, find_freq_from_fid(data->currfid), data->currvid);
+ powernow_k8_acpi_pst_values(data, 0);
- for (j = 0; j < data->numps; j++)
- if ((pst[j].fid==data->currfid) && (pst[j].vid==data->currvid))
- return 0;
+ return 0;
- printk(KERN_ERR BFX "currfid/vid do not match PST, ignoring\n");
- return 0;
- }
+ err_out:
+ acpi_processor_unregister_performance(&data->acpi_data, data->cpu);
+
+ /* data->acpi_data.state_count informs us at ->exit() whether ACPI was used */
+ data->acpi_data.state_count = 0;
- printk(KERN_ERR BFX "no PSB\n");
return -ENODEV;
}
+static void powernow_k8_cpu_exit_acpi(struct powernow_k8_data *data)
+{
+ if (data->acpi_data.state_count)
+ acpi_processor_unregister_performance(&data->acpi_data, data->cpu);
+}
+
+#else
+static inline int powernow_k8_cpu_init_acpi(struct powernow_k8_data *data) { return -ENODEV; }
+static inline void powernow_k8_cpu_exit_acpi(struct powernow_k8_data *data) { return; }
+static inline void powernow_k8_acpi_pst_values(struct powernow_k8_data *data, unsigned int index) { return; }
+#endif /* CONFIG_X86_POWERNOW_K8_ACPI */
+
+
/* Take a frequency, and issue the fid/vid transition command */
-static int transition_frequency(struct powernow_k8_data *data, unsigned int index)
+static inline int transition_frequency(struct powernow_k8_data *data, unsigned int index)
{
u32 fid;
u32 vid;
int res;
struct cpufreq_freqs freqs;
+ dprintk(KERN_DEBUG PFX "cpu %d transition to index %u\n",
+ smp_processor_id(), index );
+
/* fid are the lower 8 bits of the index we stored into
* the cpufreq frequency table in find_psb_table, vid are
* the upper 8 bits.
@@ -665,15 +787,13 @@
fid = data->powernow_table[index].index & 0xFF;
vid = (data->powernow_table[index].index & 0xFF00) >> 8;
- dprintk(KERN_DEBUG PFX "table matched fid 0x%x, giving vid 0x%x\n",
- fid, vid);
+ dprintk(KERN_DEBUG PFX "matched fid %x, giving vid %x\n", fid, vid);
if (query_current_values_with_pending_wait(data))
return 1;
if ((data->currvid == vid) && (data->currfid == fid)) {
- dprintk(KERN_DEBUG PFX
- "target matches current values (fid 0x%x, vid 0x%x)\n",
+ dprintk(KERN_DEBUG PFX "target matches curr (fid %x, vid %x)\n",
fid, vid);
return 0;
}
@@ -685,17 +805,19 @@
return 1;
}
- dprintk(KERN_DEBUG PFX "changing to fid 0x%x, vid 0x%x\n", fid, vid);
-
+ dprintk(KERN_DEBUG PFX "cpu %d, changing to fid %x, vid %x\n",
+ smp_processor_id(), fid, vid);
freqs.cpu = data->cpu;
- freqs.old = find_freq_from_fid(data->currfid);
- freqs.new = find_freq_from_fid(fid);
+ freqs.old = find_khz_freq_from_fid(data->currfid);
+ freqs.new = find_khz_freq_from_fid(fid);
cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+ down(&fidvid_sem);
res = transition_fid_vid(data, fid, vid);
+ up(&fidvid_sem);
- freqs.new = find_freq_from_fid(data->currfid);
+ freqs.new = find_khz_freq_from_fid(data->currfid);
cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
return res;
@@ -704,42 +826,68 @@
/* Driver entry point to switch to the target frequency */
static int powernowk8_target(struct cpufreq_policy *pol, unsigned targfreq, unsigned relation)
{
+ cpumask_t oldmask = CPU_MASK_ALL;
struct powernow_k8_data *data = powernow_data[pol->cpu];
u32 checkfid = data->currfid;
u32 checkvid = data->currvid;
unsigned int newstate;
+ int ret = -EIO;
+
+ /* only run on specific CPU from here on */
+ oldmask = current->cpus_allowed;
+ set_cpus_allowed(current, cpumask_of_cpu(pol->cpu));
+ schedule();
+
+ if (smp_processor_id() != pol->cpu) {
+ printk(KERN_ERR "limiting to cpu %u failed\n", pol->cpu);
+ goto err_out;
+ }
+
+ /* from this point, do not exit without restoring preempt and cpu */
+ preempt_disable();
if (pending_bit_stuck()) {
- printk(KERN_ERR PFX "drv targ fail: change pending bit set\n");
- return -EIO;
+ printk(KERN_ERR PFX "failing targ, change pending bit set\n");
+ goto err_out;
}
- dprintk(KERN_DEBUG PFX "targ: %d kHz, min %d, max %d, relation %d\n",
- targfreq, pol->min, pol->max, relation);
+ dprintk(KERN_DEBUG PFX "targ: cpu %d, %d kHz, min %d, max %d, relation %d\n",
+ pol->cpu, targfreq, pol->min, pol->max, relation);
- if (query_current_values_with_pending_wait(data))
- return -EIO;
+ if (query_current_values_with_pending_wait(data)) {
+ ret = -EIO;
+ goto err_out;
+ }
- dprintk(KERN_DEBUG PFX "targ: curr fid 0x%x, vid 0x%x\n",
+ dprintk(KERN_DEBUG PFX "targ: curr fid %x, vid %x\n",
data->currfid, data->currvid);
if ((checkvid != data->currvid) || (checkfid != data->currfid)) {
- printk(KERN_ERR PFX
- "error - out of sync, fid 0x%x 0x%x, vid 0x%x 0x%x\n",
+ printk(KERN_ERR PFX "out of sync, fid %x %x, vid %x %x\n",
checkfid, data->currfid, checkvid, data->currvid);
}
- if (cpufreq_frequency_table_target(pol, data->powernow_table, targfreq, relation, &newstate))
- return -EINVAL;
-
- if (transition_frequency(data, newstate)) {
+ if (cpufreq_frequency_table_target(pol, data->powernow_table, targfreq, relation, &newstate))
+ goto err_out;
+
+ powernow_k8_acpi_pst_values(data, newstate);
+
+ if (transition_frequency(data, newstate))
+ {
printk(KERN_ERR PFX "transition frequency failed\n");
- return 1;
+ ret = 1;
+ goto err_out;
}
- pol->cur = 1000 * find_freq_from_fid(data->currfid);
+ pol->cur = find_khz_freq_from_fid(data->currfid);
+ ret = 0;
- return 0;
+ err_out:
+ preempt_enable_no_resched();
+ set_cpus_allowed(current, oldmask);
+ schedule();
+
+ return ret;
}
/* Driver entry point to verify the policy and range of frequencies */
@@ -747,19 +895,18 @@
{
struct powernow_k8_data *data = powernow_data[pol->cpu];
- if (pending_bit_stuck()) {
- printk(KERN_ERR PFX "failing verify, change pending bit set\n");
- return -EIO;
- }
-
return cpufreq_frequency_table_verify(pol, data->powernow_table);
}
/* per CPU init entry point to the driver */
static int __init powernowk8_cpu_init(struct cpufreq_policy *pol)
{
- struct powernow_k8_data *data;
+ cpumask_t oldmask = CPU_MASK_ALL;
int rc;
+ struct powernow_k8_data *data;
+
+ if (!check_supported_cpu(pol->cpu))
+ return -ENODEV;
data = kmalloc(sizeof(struct powernow_k8_data), GFP_KERNEL);
if (!data) {
@@ -770,18 +917,39 @@
data->cpu = pol->cpu;
- if (pol->cpu != 0) {
- printk(KERN_ERR PFX "init not cpu 0\n");
- kfree(data);
- return -ENODEV;
+ if (powernow_k8_cpu_init_acpi(data)) {
+ /*
+ * Use the PSB BIOS structure. This is only availabe on
+ * an UP version, and is deprecated by AMD.
+ */
+
+ if (pol->cpu != 0) {
+ printk(KERN_ERR PFX "init - cpu 0\n");
+ kfree(data);
+ return -ENODEV;
+ }
+
+ if ((num_online_cpus() != 1) || (num_possible_cpus() != 1)) {
+ printk(KERN_INFO PFX "MP systems not supported by PSB BIOS structure\n");
+ kfree(data);
+ return 0;
+ }
+
+ rc = find_psb_table(data);
+ if (rc) {
+ kfree(data);
+ return -ENODEV;
+ }
}
- pol->governor = CPUFREQ_DEFAULT_GOVERNOR;
+ /* only run on specific CPU from here on */
+ oldmask = current->cpus_allowed;
+ set_cpus_allowed(current, cpumask_of_cpu(pol->cpu));
+ schedule();
- rc = find_psb_table(data);
- if (rc) {
- kfree(data);
- return -ENODEV;
+ if (smp_processor_id() != pol->cpu) {
+ printk(KERN_ERR "limiting to cpu %u failed\n", pol->cpu);
+ goto err_out;
}
if (pending_bit_stuck()) {
@@ -789,15 +957,25 @@
goto err_out;
}
+ if (query_current_values_with_pending_wait(data)) {
+ goto err_out;
+ }
+
+ fidvid_msr_init();
+
+
+ /* run on any CPU again */
+ set_cpus_allowed(current, oldmask);
+ schedule();
+
+ pol->governor = CPUFREQ_DEFAULT_GOVERNOR;
+
/* Take a crude guess here.
- * That guess was in microseconds, so multply with 1000 */
+ * That guess was in microseconds, so multiply with 1000 */
pol->cpuinfo.transition_latency = (((data->rvo + 8) * data->vstable * VST_UNITS_20US)
+ (3 * (1 << data->irt) * 10)) * 1000;
- if (query_current_values_with_pending_wait(data))
- return -EIO;
-
- pol->cur = 1000 * find_freq_from_fid(data->currfid);
+ pol->cur = find_khz_freq_from_fid(data->currfid);
dprintk(KERN_DEBUG PFX "policy current frequency %d kHz\n", pol->cur);
/* min/max the cpu is capable of */
@@ -807,17 +985,18 @@
kfree(data);
return -EINVAL;
}
-
cpufreq_frequency_table_get_attr(data->powernow_table, pol->cpu);
- printk(KERN_INFO PFX "cpu_init done, current fid 0x%x, vid 0x%x\n",
- data->currfid, data->currvid);
+ dprintk(KERN_INFO PFX "init, curr fid %x vid %x\n", data->currfid, data->currvid);
powernow_data[pol->cpu] = data;
return 0;
-err_out:
+ err_out:
+ set_cpus_allowed(current, oldmask);
+ schedule();
+
kfree(data);
return -ENODEV;
}
@@ -829,6 +1008,8 @@
if (!data)
return -EINVAL;
+ powernow_k8_cpu_exit_acpi(data);
+
cpufreq_frequency_table_put_attr(pol->cpu);
kfree(data->powernow_table);
@@ -866,7 +1047,7 @@
if (supported_cpus == num_online_cpus()) {
printk(KERN_INFO PFX "Found %d AMD Athlon 64 / Opteron processors (" VERSION ")\n",
- supported_cpus);
+ supported_cpus);
return cpufreq_register_driver(&cpufreq_amd64_driver);
}
@@ -885,5 +1066,5 @@
MODULE_DESCRIPTION("AMD Athlon 64 and Opteron processor frequency driver.");
MODULE_LICENSE("GPL");
-module_init(powernowk8_init);
+late_initcall(powernowk8_init);
module_exit(powernowk8_exit);
--- clean-dj//arch/i386/kernel/cpu/cpufreq/powernow-k8.h 2004-04-02 18:05:38.000000000 +0200
+++ linux-pn/arch/i386/kernel/cpu/cpufreq/powernow-k8.h 2004-04-02 18:39:13.000000000 +0200
@@ -1,46 +1,46 @@
/*
- * (c) 2003 Advanced Micro Devices, Inc.
+ * (c) 2003, 2004 Advanced Micro Devices, Inc.
* Your use of this code is subject to the terms and conditions of the
- * GNU general public license version 2. See "../../../COPYING" or
+ * GNU general public license version 2. See "COPYING" or
* http://www.gnu.org/licenses/gpl.html
*/
struct powernow_k8_data {
- unsigned int cpu;
+ unsigned int cpu;
- u32 numps; /* number of p-states */
- u32 batps; /* number of p-states supported on battery */
+ u32 numps; /* number of p-states */
+ u32 batps; /* number of p-states supported on battery */
/* these values are constant when the PSB is used to determine
* vid/fid pairings, but are modified during the ->target() call
* when ACPI is used */
- u32 rvo; /* ramp voltage offset */
- u32 irt; /* isochronous relief time */
- u32 vidmvs; /* usable value calculated from mvs */
- u32 vstable; /* voltage stabilization time, units 20 us */
- u32 plllock; /* pll lock time, units 1 us */
+ u32 rvo; /* ramp voltage offset */
+ u32 irt; /* isochronous relief time */
+ u32 vidmvs; /* usable value calculated from mvs */
+ u32 vstable; /* voltage stabilization time, units 20 us */
+ u32 plllock; /* pll lock time, units 1 us */
/* keep track of the current fid / vid */
- u32 currvid;
- u32 currfid;
+ u32 currvid;
+ u32 currfid;
/* the powernow_table includes all frequency and vid/fid pairings:
* fid are the lower 8 bits of the index, vid are the upper 8 bits.
* frequency is in kHz */
- struct cpufreq_frequency_table *powernow_table;
-};
+ struct cpufreq_frequency_table *powernow_table;
+#ifdef CONFIG_X86_POWERNOW_K8_ACPI
+ /* the acpi table needs to be kept. it's only available if ACPI was
+ * used to determine valid frequency/vid/fid states */
+ struct acpi_processor_performance acpi_data;
+#endif
+};
/* processor's cpuid instruction support */
-#define CPUID_PROCESSOR_SIGNATURE 1 /* function 1 */
-#define CPUID_F1_FAM 0x00000f00 /* family mask */
-#define CPUID_F1_XFAM 0x0ff00000 /* extended family mask */
-#define CPUID_F1_MOD 0x000000f0 /* model mask */
-#define CPUID_F1_STEP 0x0000000f /* stepping level mask */
-#define CPUID_XFAM_MOD 0x0ff00ff0 /* xtended fam, fam + model */
-#define ATHLON64_XFAM_MOD 0x00000f40 /* xtended fam, fam + model */
-#define OPTERON_XFAM_MOD 0x00000f50 /* xtended fam, fam + model */
-#define ATHLON64_REV_C0 8
+#define CPUID_PROCESSOR_SIGNATURE 1 /* function 1 */
+#define CPUID_XFAM_MOD 0x0ff00ff0 /* extended fam, fam + model */
+#define ATHLON64_XFAM_MOD 0x00000f40 /* extended fam, fam + model */
+#define OPTERON_XFAM_MOD 0x00000f50 /* extended fam, fam + model */
#define CPUID_GET_MAX_CAPABILITIES 0x80000000
#define CPUID_FREQ_VOLT_CAPABILITIES 0x80000007
#define P_STATE_TRANSITION_CAPABLE 6
@@ -49,7 +49,6 @@
/* writes (wrmsr - opcode 0f 30), the register number is placed in ecx, and */
/* the value to write is placed in edx:eax. For reads (rdmsr - opcode 0f 32), */
/* the register number is placed in ecx, and the data is returned in edx:eax. */
-
#define MSR_FIDVID_CTL 0xc0010041
#define MSR_FIDVID_STATUS 0xc0010042
@@ -73,10 +72,24 @@
#define MSR_S_HI_MAX_WORKING_VID 0x001f0000
#define MSR_S_HI_START_VID 0x00001f00
#define MSR_S_HI_CURRENT_VID 0x0000001f
+#define MSR_C_HI_STP_GNT_BENIGN 0x00000001
+
+/*
+ There are restrictions frequencies have to follow:
+ - only 1 entry in the low fid table ( <=1.4GHz )
+ - lowest entry in the high fid table must be >= 2 * the
+ entry in the low fid table
+ - lowest entry in the high fid table must be a <= 200MHz +
+ 2 * the entry in the low fid table
+ - the parts can only step at 200 MHz intervals, so 1.9 GHz is
+ never valid
+ - lowest frequency must be >= interprocessor hypertransport link
+ speed (only applies to MP systems obviously)
+ */
/* fids (frequency identifiers) are arranged in 2 tables - lo and hi */
-#define LO_FID_TABLE_TOP 6
-#define HI_FID_TABLE_BOTTOM 8
+#define LO_FID_TABLE_TOP 6 /* fid values marking the boundary */
+#define HI_FID_TABLE_BOTTOM 8 /* between the low and high tables */
#define LO_VCOFREQ_TABLE_TOP 1400 /* corresponding vco frequency values */
#define HI_VCOFREQ_TABLE_BOTTOM 1600
@@ -84,33 +97,44 @@
#define MIN_FREQ_RESOLUTION 200 /* fids jump by 2 matching freq jumps by 200 */
#define MAX_FID 0x2a /* Spec only gives FID values as far as 5 GHz */
-
#define LEAST_VID 0x1e /* Lowest (numerically highest) useful vid value */
-
#define MIN_FREQ 800 /* Min and max freqs, per spec */
#define MAX_FREQ 5000
-#define INVALID_FID_MASK 0xffffffc1 /* not a valid fid if these bits are set */
-
-#define INVALID_VID_MASK 0xffffffe0 /* not a valid vid if these bits are set */
+#define INVALID_FID_MASK 0xffffffc1 /* not a valid fid if these bits are set */
+#define INVALID_VID_MASK 0xffffffe0 /* not a valid vid if these bits are set */
#define STOP_GRANT_5NS 1 /* min poss memory access latency for voltage change */
-
#define PLL_LOCK_CONVERSION (1000/5) /* ms to ns, then divide by clock period */
-
#define MAXIMUM_VID_STEPS 1 /* Current cpus only allow a single step of 25mV */
+#define VST_UNITS_20US 20 /* Voltage Stabilization Time is in units of 20us */
-#define VST_UNITS_20US 20 /* Voltage Stabalization Time is in units of 20us */
+/*
+ * Most values of interest are enocoded in a single field of the _PSS
+ * entries: the "control" value.
+ */
+#define IRT_SHIFT 30
+#define RVO_SHIFT 28
+#define PLL_L_SHIFT 20
+#define MVS_SHIFT 18
+#define VST_SHIFT 11
+#define VID_SHIFT 6
+#define IRT_MASK 3
+#define RVO_MASK 3
+#define PLL_L_MASK 0x7f
+#define MVS_MASK 3
+#define VST_MASK 0x7f
+#define VID_MASK 0x1f
+#define FID_MASK 0x3f
+
/*
-Version 1.4 of the PSB table. This table is constructed by BIOS and is
-to tell the OS's power management driver which VIDs and FIDs are
-supported by this particular processor. This information is obtained from
-the data sheets for each processor model by the system vendor and
-incorporated into the BIOS.
-If the data in the PSB / PST is wrong, then this driver will program the
-wrong values into hardware, which is very likely to lead to a crash.
-*/
+ * Version 1.4 of the PSB table. This table is constructed by BIOS and is
+ * to tell the OS's power management driver which VIDs and FIDs are
+ * supported by this particular processor.
+ * If the data in the PSB / PST is wrong, then this driver will program the
+ * wrong values into hardware, which is very likely to lead to a crash.
+ */
#define PSB_ID_STRING "AMDK7PNOW!"
#define PSB_ID_STRING_LEN 10
@@ -146,4 +171,4 @@
static inline int core_voltage_pre_transition(struct powernow_k8_data *data, u32 reqvid);
static inline int core_voltage_post_transition(struct powernow_k8_data *data, u32 reqvid);
static inline int core_frequency_transition(struct powernow_k8_data *data, u32 reqfid);
-
+static inline void powernow_k8_acpi_pst_values(struct powernow_k8_data *data, unsigned int index);
--
When do you have a heart between your knees?
[Johanka's followup: and *two* hearts?]
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: powernow-k8: support acpi
2004-04-02 16:59 paul.devriendt
@ 2004-04-02 18:40 ` Bruno Ducrot
0 siblings, 0 replies; 21+ messages in thread
From: Bruno Ducrot @ 2004-04-02 18:40 UTC (permalink / raw)
To: paul.devriendt; +Cc: akpm, mark.langsdorf, cpufreq, pavel, davej, linux
On Fri, Apr 02, 2004 at 10:59:05AM -0600, paul.devriendt@amd.com wrote:
> > But I can't say for sure which have to be favored: legacy or ACPI.
> > I don't have enough dump of ACPI tables and PSB/PST from
> > machines shipped
> > with AMD64.
> >
> > Bruno Ducrot
>
> ACPI please ....
>
> 1. It seems that system manufacturers are testing with Windows XP
> only before shipping their machines. Windows XP uses ACPI, so that
> is the mechanism that is most likely to have accurate pstate data.
> 2. We are seeing a lot of broken PSB/PSTs.
> 3. The PSB/PST does not support SMP in its current form. ACPI does.
> 4. Current plan is to not have PSB/PST at all for future processors.
> 5. You need the ACPI support for the _PPC object which gives battery
> and thermal control.
>
> The downside to making the ACPI favored is that it is newer. But,
> given the PSB/PST breakages from bad BIOS data, the newer is going
> to be more robust.
>
I totally agree with you. The only problem I see is that it's
possible that AML is so broken that it's not possible to make it
work under Linux without changing a lot of things in the current ACPI
interpreter, and that may be hard to get those changes by ACPI linux
gang.
--
Bruno Ducrot
-- Which is worse: ignorance or apathy?
-- Don't know. Don't care.
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: powernow-k8: support acpi
2004-04-02 17:09 ` Pavel Machek
@ 2004-04-06 13:07 ` Dave Jones
2004-04-06 14:55 ` Dominik Brodowski
2004-04-08 13:11 ` Pavel Machek
0 siblings, 2 replies; 21+ messages in thread
From: Dave Jones @ 2004-04-06 13:07 UTC (permalink / raw)
To: Pavel Machek
Cc: Andrew Morton, Bruno Ducrot, mark.langsdorf, Cpufreq mailing list,
paul.devriendt, Dominik Brodowski
On Fri, Apr 02, 2004 at 07:09:26PM +0200, Pavel Machek wrote:
> > I'm about to disappear for a day or two. I'll look into integrating whats
> > outstanding on Sunday. Pavel, if you have time between now and then
> > to do a diff of whats remaining between your tree and the driver I posted
> > this afternoon, that would make my life a little easier.
>
> Here it is: (I was not able to locate your changes, so it is possible
> I'm reverting some of those; but few extra inlines should not hurt
> that bad :-).
Ok, the merge-from-hell is complete (split up into 23 csets).
The only diffs between your tree and mine should now be trivial
whitespace changes, and some small bits that looked bogus.
You can grab what I have in my tree pending pushing to Linus at
http://www.codemonkey.org.uk/projects/cpufreq/pending.diff
bk users can clone bk://linux-dj.bkbits.net/cpufreq
And you'll also find this pulled into -mm each time Andrew makes a tree.
One thing outstanding that probably does need fixing. Andi Kleen
mentioned on IRC yesterday ..
<f> found a funny bug in the powernow-k8 acpi changes
<f> when acpi is disabled processor.ko won't load, but the new module needs symbols from that
<f> i added a hack to keep it loaded anyways
Be nice to get that fixed, and merged before these changes get pushed to mainline.
Dave
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: powernow-k8: support acpi
2004-04-06 13:07 ` Dave Jones
@ 2004-04-06 14:55 ` Dominik Brodowski
2004-04-06 15:47 ` Dave Jones
2004-04-08 13:11 ` Pavel Machek
1 sibling, 1 reply; 21+ messages in thread
From: Dominik Brodowski @ 2004-04-06 14:55 UTC (permalink / raw)
To: Dave Jones
Cc: Andrew Morton, Bruno Ducrot, mark.langsdorf, Cpufreq mailing list,
Pavel Machek, paul.devriendt
[-- Attachment #1: Type: text/plain, Size: 1774 bytes --]
On Tue, Apr 06, 2004 at 02:07:30PM +0100, Dave Jones wrote:
> On Fri, Apr 02, 2004 at 07:09:26PM +0200, Pavel Machek wrote:
>
> > > I'm about to disappear for a day or two. I'll look into integrating whats
> > > outstanding on Sunday. Pavel, if you have time between now and then
> > > to do a diff of whats remaining between your tree and the driver I posted
> > > this afternoon, that would make my life a little easier.
> >
> > Here it is: (I was not able to locate your changes, so it is possible
> > I'm reverting some of those; but few extra inlines should not hurt
> > that bad :-).
>
> Ok, the merge-from-hell is complete (split up into 23 csets).
> The only diffs between your tree and mine should now be trivial
> whitespace changes, and some small bits that looked bogus.
Just found a small bug which would leave the preempt count negative(!) in
special cases. Patch is attached.
> You can grab what I have in my tree pending pushing to Linus at
> http://www.codemonkey.org.uk/projects/cpufreq/pending.diff
Will
http://www.codemonkey.org.uk/projects/bitkeeper/cpufreq/
be a permanent institution, or is it just available for the moment?
> One thing outstanding that probably does need fixing. Andi Kleen
> mentioned on IRC yesterday ..
>
> <f> found a funny bug in the powernow-k8 acpi changes
> <f> when acpi is disabled processor.ko won't load, but the new module needs symbols from that
> <f> i added a hack to keep it loaded anyways
>
> Be nice to get that fixed, and merged before these changes get pushed to mainline.
Hm. Keeping processor.ko loaded seems to be an ugly hack to me. However, I
can't see a way (yet) how powernow-k8.ko can rely on processor.ko's objects
but not depend on it if these are not neeeded (psb case).
Dominik
[-- Attachment #2: cpufreq-2.6.5-fix-jump --]
[-- Type: text/plain, Size: 669 bytes --]
diff -ruN linux-original/arch/i386/kernel/cpu/cpufreq/powernow-k8.c linux/arch/i386/kernel/cpu/cpufreq/powernow-k8.c
--- linux-original/arch/i386/kernel/cpu/cpufreq/powernow-k8.c 2004-04-06 16:41:19.907636632 +0200
+++ linux/arch/i386/kernel/cpu/cpufreq/powernow-k8.c 2004-04-06 16:50:54.428296216 +0200
@@ -848,7 +848,7 @@
if (smp_processor_id() != pol->cpu) {
printk(KERN_ERR "limiting to cpu %u failed\n", pol->cpu);
- goto err_out;
+ goto sched_out;
}
/* from this point, do not exit without restoring preempt and cpu */
@@ -892,6 +892,7 @@
err_out:
preempt_enable_no_resched();
+sched_out:
set_cpus_allowed(current, oldmask);
schedule();
[-- Attachment #3: Type: text/plain, Size: 143 bytes --]
_______________________________________________
Cpufreq mailing list
Cpufreq@www.linux.org.uk
http://www.linux.org.uk/mailman/listinfo/cpufreq
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: powernow-k8: support acpi
2004-04-06 14:55 ` Dominik Brodowski
@ 2004-04-06 15:47 ` Dave Jones
2004-04-06 16:55 ` Dominik Brodowski
0 siblings, 1 reply; 21+ messages in thread
From: Dave Jones @ 2004-04-06 15:47 UTC (permalink / raw)
To: Pavel Machek, Andrew Morton, Bruno Ducrot, mark.langsdorf,
Cpufreq mailing list, paul.devriendt
On Tue, Apr 06, 2004 at 04:55:02PM +0200, Dominik Brodowski wrote:
> Just found a small bug which would leave the preempt count negative(!) in
> special cases. Patch is attached.
good catch. applied.
> > You can grab what I have in my tree pending pushing to Linus at
> > http://www.codemonkey.org.uk/projects/cpufreq/pending.diff
>
> Will http://www.codemonkey.org.uk/projects/bitkeeper/cpufreq/
> be a permanent institution, or is it just available for the moment?
I set it up this morning to do a snapshot each night along with the
other trees one level up from there. It'll continually keep one weeks
worth of patches.
> > <f> found a funny bug in the powernow-k8 acpi changes
> > <f> when acpi is disabled processor.ko won't load, but the new module needs symbols from that
> > <f> i added a hack to keep it loaded anyways
> >
> > Be nice to get that fixed, and merged before these changes get pushed to mainline.
>
> Hm. Keeping processor.ko loaded seems to be an ugly hack to me. However, I
> can't see a way (yet) how powernow-k8.ko can rely on processor.ko's objects
> but not depend on it if these are not neeeded (psb case).
True, it's a sledgehammer to crack a walnut, but I'm not sure messing with
weak symbols would be any cleaner, and other than that or moving bits out
to a seperate module (double ugh), I don't really have any ideas.
Dave
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: powernow-k8: support acpi
2004-04-06 15:47 ` Dave Jones
@ 2004-04-06 16:55 ` Dominik Brodowski
2004-04-06 17:05 ` Dave Jones
0 siblings, 1 reply; 21+ messages in thread
From: Dominik Brodowski @ 2004-04-06 16:55 UTC (permalink / raw)
To: Dave Jones
Cc: Andrew Morton, Bruno Ducrot, mark.langsdorf, Cpufreq mailing list,
Pavel Machek, paul.devriendt
[-- Attachment #1.1: Type: text/plain, Size: 1579 bytes --]
On Tue, Apr 06, 2004 at 04:47:50PM +0100, Dave Jones wrote:
>
> I set it up this morning to do a snapshot each night along with the
> other trees one level up from there. It'll continually keep one weeks
> worth of patches.
Excellent. Thanks for doing this.
> > > <f> found a funny bug in the powernow-k8 acpi changes
> > > <f> when acpi is disabled processor.ko won't load, but the new module needs symbols from that
> > > <f> i added a hack to keep it loaded anyways
> > >
> > > Be nice to get that fixed, and merged before these changes get pushed to mainline.
> >
> > Hm. Keeping processor.ko loaded seems to be an ugly hack to me. However, I
> > can't see a way (yet) how powernow-k8.ko can rely on processor.ko's objects
> > but not depend on it if these are not neeeded (psb case).
>
> True, it's a sledgehammer to crack a walnut, but I'm not sure messing with
> weak symbols would be any cleaner,
What do you mean with "weak symbols"?
> and other than that or moving bits out
> to a seperate module (double ugh), I don't really have any ideas.
unfortunately, that's currently a no-go, unless you have 2+ weeks of spare
time. I once tried splitting acpi/drivers/processor.c up into several
modules [and export user-information in sysfs], but it was sooo messy
because the ACPI core lacks basic rules of reference counting. Additionally,
such a "new" module would rely on a "basic" ACPI - processor.ko module
again, which should fail to load, IMHO, if acpi is disabled. So it wouldn't
get us anything, I think.
Dominik
[-- Attachment #1.2: Type: application/pgp-signature, Size: 189 bytes --]
[-- Attachment #2: Type: text/plain, Size: 143 bytes --]
_______________________________________________
Cpufreq mailing list
Cpufreq@www.linux.org.uk
http://www.linux.org.uk/mailman/listinfo/cpufreq
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: powernow-k8: support acpi
2004-04-06 16:55 ` Dominik Brodowski
@ 2004-04-06 17:05 ` Dave Jones
2004-04-06 21:57 ` Dominik Brodowski
0 siblings, 1 reply; 21+ messages in thread
From: Dave Jones @ 2004-04-06 17:05 UTC (permalink / raw)
To: Pavel Machek, Andrew Morton, Bruno Ducrot, mark.langsdorf,
Cpufreq mailing list, paul.devriendt
On Tue, Apr 06, 2004 at 06:55:39PM +0200, Dominik Brodowski wrote:
> > True, it's a sledgehammer to crack a walnut, but I'm not sure messing with
> > weak symbols would be any cleaner,
> What do you mean with "weak symbols"?
see for example how cond_syscall is implemented.
Dave
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: powernow-k8: support acpi
2004-04-06 17:05 ` Dave Jones
@ 2004-04-06 21:57 ` Dominik Brodowski
0 siblings, 0 replies; 21+ messages in thread
From: Dominik Brodowski @ 2004-04-06 21:57 UTC (permalink / raw)
To: Dave Jones
Cc: Andrew Morton, Bruno Ducrot, mark.langsdorf, Cpufreq mailing list,
Pavel Machek, paul.devriendt
[-- Attachment #1.1: Type: text/plain, Size: 529 bytes --]
On Tue, Apr 06, 2004 at 06:05:06PM +0100, Dave Jones wrote:
> On Tue, Apr 06, 2004 at 06:55:39PM +0200, Dominik Brodowski wrote:
> > > True, it's a sledgehammer to crack a walnut, but I'm not sure messing with
> > > weak symbols would be any cleaner,
> > What do you mean with "weak symbols"?
>
> see for example how cond_syscall is implemented.
/me still doesn't get the logic behind this, as I don't understand asm
magic... but well, maybe others can and find a _good_ way to get around this
problem.
Dominik
[-- Attachment #1.2: Type: application/pgp-signature, Size: 189 bytes --]
[-- Attachment #2: Type: text/plain, Size: 143 bytes --]
_______________________________________________
Cpufreq mailing list
Cpufreq@www.linux.org.uk
http://www.linux.org.uk/mailman/listinfo/cpufreq
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: powernow-k8: support acpi
2004-04-06 13:07 ` Dave Jones
2004-04-06 14:55 ` Dominik Brodowski
@ 2004-04-08 13:11 ` Pavel Machek
1 sibling, 0 replies; 21+ messages in thread
From: Pavel Machek @ 2004-04-08 13:11 UTC (permalink / raw)
To: Dave Jones
Cc: Andrew Morton, Bruno Ducrot, mark.langsdorf, Cpufreq mailing list,
Pavel Machek, paul.devriendt, Dominik Brodowski
Hi!
> > > I'm about to disappear for a day or two. I'll look into integrating whats
> > > outstanding on Sunday. Pavel, if you have time between now and then
> > > to do a diff of whats remaining between your tree and the driver I posted
> > > this afternoon, that would make my life a little easier.
> >
> > Here it is: (I was not able to locate your changes, so it is possible
> > I'm reverting some of those; but few extra inlines should not hurt
> > that bad :-).
>
> Ok, the merge-from-hell is complete (split up into 23 csets).
> The only diffs between your tree and mine should now be trivial
> whitespace changes, and some small bits that looked bogus.
Nice job! I thought it is impossible to split at all.
> One thing outstanding that probably does need fixing. Andi Kleen
> mentioned on IRC yesterday ..
>
> <f> found a funny bug in the powernow-k8 acpi changes
> <f> when acpi is disabled processor.ko won't load, but the new module needs symbols from that
> <f> i added a hack to keep it loaded anyways
I thought about it... Would it be fair to make ACPI support
in powernow-k8 depend on ACPI_PROCESSOR=y?
We used to have no ACPI support at all, so it still would be strict improvement
(and unlike other solutions it is one-liner).
Pavel
--
64 bytes from 195.113.31.123: icmp_seq=28 ttl=51 time=448769.1 ms
^ permalink raw reply [flat|nested] 21+ messages in thread
end of thread, other threads:[~2004-04-08 13:11 UTC | newest]
Thread overview: 21+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <99F2150714F93F448942F9A9F112634C1163C8D7@txexmtae.amd.com>
[not found] ` <20040321185417.GA7969@dominikbrodowski.de>
2004-03-26 12:29 ` powernow-k8: support acpi Pavel Machek
2004-03-26 12:29 ` Pavel Machek
2004-03-27 0:36 ` Redeeman
2004-04-01 23:56 ` Dave Jones
2004-04-02 8:30 ` Pavel Machek
2004-04-02 9:02 ` Dave Jones
2004-04-02 11:13 ` Dave Jones
2004-04-02 16:24 ` Bruno Ducrot
2004-04-02 16:39 ` Dave Jones
2004-04-02 17:09 ` Pavel Machek
2004-04-06 13:07 ` Dave Jones
2004-04-06 14:55 ` Dominik Brodowski
2004-04-06 15:47 ` Dave Jones
2004-04-06 16:55 ` Dominik Brodowski
2004-04-06 17:05 ` Dave Jones
2004-04-06 21:57 ` Dominik Brodowski
2004-04-08 13:11 ` Pavel Machek
2004-04-02 16:39 ` Dominik Brodowski
2004-04-02 16:46 ` Dominik Brodowski
2004-04-02 16:59 paul.devriendt
2004-04-02 18:40 ` Bruno Ducrot
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.