1,5 → 1,5 |
/* |
* (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 |
* http://www.gnu.org/licenses/gpl.html |
11,13 → 11,15 |
* 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 <linuxcomp.h> |
//#define DEBUG |
|
#include <linux/kernel.h> |
#include <linux/smp.h> |
31,7 → 33,9 |
#include <asm/io.h> |
#include <asm/delay.h> |
|
#define DEBUG |
#define PFX "powernow-k8: " |
#define VERSION "version 1.00.12 - February 29, 2004" |
#include "powernow-k8.h" |
|
#ifdef DEBUG |
#define dprintk(msg...) printk(msg) |
39,71 → 43,28 |
#define dprintk(msg...) do { } while(0) |
#endif |
|
#define PFX "powernow-k8: " |
#define BFX PFX "BIOS error: " |
#define VERSION "version 1.00.08 - September 26, 2003" |
#include "powernow-k8.h" |
|
#ifdef CONFIG_PREEMPT |
#warning this driver has not been tested on a preempt system |
#endif |
|
extern struct cpuinfo_x86 new_cpu_data; |
|
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 */ |
struct pst_s *ppst; /* array of p states, valid for this part */ |
static u32 vstable; /* voltage stabalization time, units 20 us */ |
static u32 plllock; /* pll lock time, units 1 us */ |
static u32 numps; /* number of p-states */ |
static u32 batps; /* number of p-states supported on battery */ |
static u32 rvo; /* ramp voltage offset */ |
static u32 irt; /* isochronous relief time */ |
static u32 vidmvs; /* usable value calculated from mvs */ |
static u32 currvid; /* keep track of the current fid / vid */ |
static u32 currfid; |
static struct cpufreq_frequency_table *ftbl; |
|
/* |
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 */ |
|
static struct cpufreq_driver cpufreq_amd64_driver = { |
.verify = powernowk8_verify, |
.target = powernowk8_target, |
.init = powernowk8_cpu_init, |
.name = "cpufreq-amd64", |
.owner = THIS_MODULE, |
}; |
|
#define SEARCH_UP 1 |
#define SEARCH_DOWN 0 |
|
/* Return a frequency in MHz, given an input fid */ |
u32 find_freq_from_fid(u32 fid) |
static inline u32 find_freq_from_fid(u32 fid) |
{ |
return 800 + (fid * 100); |
} |
|
/* Return a fid matching an input frequency in MHz */ |
static u32 find_fid_from_freq(u32 freq) |
/* Return a frequency in KHz, given an input fid */ |
static inline u32 find_khz_freq_from_fid(u32 fid) |
{ |
return (freq - 800) / 100; |
return 1000 * (800 + (fid * 100)); |
} |
|
/* Return the vco fid for an input fid */ |
116,38 → 77,10 |
} |
} |
|
/* Sort the fid/vid frequency table into ascending order by fid. The spec */ |
/* implies that it will be sorted by BIOS, but, it only implies it, and I */ |
/* prefer not to trust when I can check. */ |
/* Yes, it is a simple bubble sort, but the PST is really small, so the */ |
/* choice of algorithm is pretty irrelevant. */ |
static inline void sort_pst(struct pst_s *ppst, u32 numpstates) |
{ |
u32 i; |
u8 tempfid; |
u8 tempvid; |
int swaps = 1; |
|
while (swaps) { |
swaps = 0; |
for (i = 0; i < (numpstates - 1); i++) { |
if (ppst[i].fid > ppst[i + 1].fid) { |
swaps = 1; |
tempfid = ppst[i].fid; |
tempvid = ppst[i].vid; |
ppst[i].fid = ppst[i + 1].fid; |
ppst[i].vid = ppst[i + 1].vid; |
ppst[i + 1].fid = tempfid; |
ppst[i + 1].vid = tempvid; |
} |
} |
} |
|
return; |
} |
|
/* 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) |
{ |
u32 lo; |
157,8 → 90,10 |
return lo & MSR_S_LO_CHANGE_PENDING ? 1 : 0; |
} |
|
/* 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) |
{ |
u32 lo; |
201,33 → 136,27 |
u32 savevid = currvid; |
|
if ((fid & INVALID_FID_MASK) || (currvid & INVALID_VID_MASK)) { |
printk(KERN_ERR PFX "internal error - overflow on fid write\n"); |
dprintk(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; |
|
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()) |
return 1; |
|
count_off_irt(); |
|
if (savevid != currvid) { |
printk(KERN_ERR PFX |
"vid changed on fid transition, save %x, currvid %x\n", |
dprintk(KERN_ERR PFX "vid change on fid trans, old %x, new %x\n", |
savevid, currvid); |
return 1; |
} |
|
if (fid != currfid) { |
printk(KERN_ERR PFX |
"fid transition failed, fid %x, currfid %x\n", |
fid, currfid); |
dprintk(KERN_ERR PFX "fid trans failed, fid %x, curr %x\n", fid, |
currfid); |
return 1; |
} |
|
241,32 → 170,26 |
u32 savefid = currfid; |
|
if ((currfid & INVALID_FID_MASK) || (vid & INVALID_VID_MASK)) { |
printk(KERN_ERR PFX "internal error - overflow on vid write\n"); |
dprintk(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; |
|
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()) |
return 1; |
} |
|
if (savefid != currfid) { |
printk(KERN_ERR PFX |
"fid changed on vid transition, save %x currfid %x\n", |
dprintk(KERN_ERR PFX "fid changed on vid trans, old %x new %x\n", |
savefid, currfid); |
return 1; |
} |
|
if (vid != currvid) { |
printk(KERN_ERR PFX |
"vid transition failed, vid %x, currvid %x\n", |
vid, currvid); |
dprintk(KERN_ERR PFX "vid trans failed, vid %x, curr %x\n", vid, |
currvid); |
return 1; |
} |
|
273,19 → 196,18 |
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. */ |
/* |
* 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(u32 reqvid, u32 step) |
{ |
if ((currvid - reqvid) > step) |
reqvid = currvid - step; |
|
if (write_new_vid(reqvid)) |
return 1; |
|
count_off_vst(); |
|
return 0; |
} |
|
294,30 → 216,25 |
{ |
if (core_voltage_pre_transition(reqvid)) |
return 1; |
|
if (core_frequency_transition(reqfid)) |
return 1; |
|
if (core_voltage_post_transition(reqvid)) |
return 1; |
|
if (query_current_values_with_pending_wait()) |
return 1; |
|
if ((reqfid != currfid) || (reqvid != currvid)) { |
printk(KERN_ERR PFX "failed: req 0x%x 0x%x, curr 0x%x 0x%x\n", |
dprintk(KERN_ERR PFX "failed: req %x %x, curr %x %x\n", |
reqfid, reqvid, currfid, currvid); |
return 1; |
} |
|
dprintk(KERN_INFO PFX |
"transitioned: new fid 0x%x, vid 0x%x\n", currfid, currvid); |
|
"transitioned: new fid %x, vid %x\n", currfid, currvid); |
return 0; |
} |
|
/* Phase 1 - core voltage transition ... setup appropriate voltage for the */ |
/* fid transition. */ |
/* Phase 1 - core voltage transition ... setup voltage */ |
static inline int core_voltage_pre_transition(u32 reqvid) |
{ |
u32 rvosteps = rvo; |
324,11 → 241,11 |
u32 savefid = currfid; |
|
dprintk(KERN_DEBUG PFX |
"ph1: start, currfid 0x%x, currvid 0x%x, reqvid 0x%x, rvo %x\n", |
"ph1: start, currfid %x, currvid %x, reqvid %x, rvo %x\n", |
currfid, currvid, reqvid, rvo); |
|
while (currvid > reqvid) { |
dprintk(KERN_DEBUG PFX "ph1: curr 0x%x, requesting vid 0x%x\n", |
dprintk(KERN_DEBUG PFX "ph1: curr %x, req vid %x\n", |
currvid, reqvid); |
if (decrease_vid_code_by_step(reqvid, vidmvs)) |
return 1; |
339,7 → 256,7 |
rvosteps = 0; |
} else { |
dprintk(KERN_DEBUG PFX |
"ph1: changing vid for rvo, requesting 0x%x\n", |
"ph1: changing vid for rvo, req %x\n", |
currvid - 1); |
if (decrease_vid_code_by_step(currvid - 1, 1)) |
return 1; |
351,11 → 268,11 |
return 1; |
|
if (savefid != currfid) { |
printk(KERN_ERR PFX "ph1 err, currfid changed 0x%x\n", currfid); |
dprintk(KERN_ERR PFX "ph1: err, currfid changed %x\n", 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", |
currfid, currvid); |
|
return 0; |
370,18 → 287,18 |
u32 savevid = 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", |
dprintk(KERN_ERR PFX "ph2: illegal lo-lo transition %x %x\n", |
reqfid, currfid); |
return 1; |
} |
|
if (currfid == reqfid) { |
printk(KERN_ERR PFX "ph2 null fid transition 0x%x\n", currfid); |
dprintk(KERN_ERR PFX "ph2: null fid transition %x\n", currfid); |
return 0; |
} |
|
dprintk(KERN_DEBUG PFX |
"ph2 starting, currfid 0x%x, currvid 0x%x, reqfid 0x%x\n", |
"ph2: starting, currfid %x, currvid %x, reqfid %x\n", |
currfid, currvid, reqfid); |
|
vcoreqfid = convert_fid_to_vco_fid(reqfid); |
413,25 → 330,23 |
|
if (write_new_fid(reqfid)) |
return 1; |
|
if (query_current_values_with_pending_wait()) |
return 1; |
|
if (currfid != reqfid) { |
printk(KERN_ERR PFX |
"ph2 mismatch, failed fid transition, curr %x, req %x\n", |
dprintk(KERN_ERR PFX |
"ph2: mismatch, failed fid trans, curr %x, req %x\n", |
currfid, reqfid); |
return 1; |
} |
|
if (savevid != currvid) { |
printk(KERN_ERR PFX |
"ph2 vid changed, save %x, curr %x\n", savevid, |
currvid); |
dprintk(KERN_ERR PFX "ph2: vid changed, save %x, curr %x\n", |
savevid, 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", |
currfid, currvid); |
|
return 0; |
443,7 → 358,7 |
u32 savefid = currfid; |
u32 savereqvid = reqvid; |
|
dprintk(KERN_DEBUG PFX "ph3 starting, currfid 0x%x, currvid 0x%x\n", |
dprintk(KERN_DEBUG PFX "ph3: starting, currfid %x, currvid %x\n", |
currfid, currvid); |
|
if (reqvid != currvid) { |
451,7 → 366,7 |
return 1; |
|
if (savefid != currfid) { |
printk(KERN_ERR PFX |
dprintk(KERN_ERR PFX |
"ph3: bad fid change, save %x, curr %x\n", |
savefid, currfid); |
return 1; |
458,7 → 373,7 |
} |
|
if (currvid != reqvid) { |
printk(KERN_ERR PFX |
dprintk(KERN_ERR PFX |
"ph3: failed vid transition\n, req %x, curr %x", |
reqvid, currvid); |
return 1; |
469,19 → 384,18 |
return 1; |
|
if (savereqvid != currvid) { |
dprintk(KERN_ERR PFX "ph3 failed, currvid 0x%x\n", currvid); |
dprintk(KERN_ERR PFX "ph3: failed, currvid %x\n", currvid); |
return 1; |
} |
|
if (savefid != currfid) { |
dprintk(KERN_ERR PFX "ph3 failed, currfid changed 0x%x\n", |
dprintk(KERN_ERR PFX "ph3: failed, currfid changed %x\n", |
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", |
currfid, currvid); |
|
return 0; |
} |
|
495,16 → 409,14 |
return 0; |
} |
|
if (c->x86_vendor != X86_VENDOR_AMD) { |
dprintk(KERN_INFO PFX "Not an AMD processor\n"); |
if (c->x86_vendor != X86_VENDOR_AMD) |
return 0; |
} |
|
eax = cpuid_eax(CPUID_PROCESSOR_SIGNATURE); |
if (((eax & CPUID_USE_XFAM_XMOD) != CPUID_USE_XFAM_XMOD) || |
((eax & CPUID_XFAM) != CPUID_XFAM_K8) || |
((eax & CPUID_XMOD) > CPUID_XMOD_REV_E)) { |
printk(KERN_INFO PFX "Processor cpuid %x not supported\n", eax); |
dprintk(KERN_INFO PFX "Processor cpuid %x not supported\n", eax); |
return 0; |
} else { |
dprintk(KERN_INFO PFX "AMD Athlon 64 or AMD Opteron processor found\n"); |
512,8 → 424,7 |
|
eax = cpuid_eax(CPUID_GET_MAX_CAPABILITIES); |
if (eax < CPUID_FREQ_VOLT_CAPABILITIES) { |
dprintk(KERN_INFO PFX |
"No frequency change capabilities detected\n"); |
dprintk(KERN_INFO PFX "No freq change capabilities\n"); |
return 0; |
} |
|
523,19 → 434,53 |
return 0; |
} |
|
printk(KERN_INFO PFX "Found AMD Athlon 64 / Opteron processor " |
"supporting p-state transitions\n"); |
|
dprintk(KERN_INFO PFX "Found AMD Athlon 64 / Opteron processor\n"); |
return 1; |
} |
|
static int check_pst_table(struct pst_s *pst, u8 maxvid) |
{ |
unsigned int j; |
u8 lastfid = 0xff; |
|
for (j = 0; j < numps; j++) { |
if (pst[j].vid > LEAST_VID) { |
dprintk(KERN_ERR PFX "vid %d bad: %x\n", j, pst[j].vid); |
return -EINVAL; |
} |
if (pst[j].vid < rvo) { /* vid + rvo >= 0 */ |
dprintk(KERN_ERR PFX "0 vid exceeded with pst %d\n", j); |
return -ENODEV; |
} |
if (pst[j].vid < maxvid + rvo) { /* vid + rvo >= maxvid */ |
dprintk(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))) { |
dprintk(KERN_ERR PFX "fid %d bad: %x\n", j, pst[j].fid); |
return -EINVAL; |
} |
if (pst[j].fid < lastfid) |
lastfid = pst[j].fid; |
} |
if (lastfid & 1) { |
dprintk(KERN_ERR PFX "lastfid invalid\n"); |
return -EINVAL; |
} |
if (lastfid > LO_FID_TABLE_TOP) |
dprintk(KERN_INFO PFX "first fid not from lo freq table\n"); |
|
return 0; |
} |
|
/* Find and validate the PSB/PST table in BIOS. */ |
static inline int find_psb_table(void) |
{ |
struct psb_s *psb; |
struct pst_s *pst; |
unsigned i, j; |
u32 lastfid; |
unsigned int i, j; |
u32 mvs; |
u8 maxvid; |
|
547,258 → 492,144 |
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"); |
dprintk(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"); |
dprintk(KERN_ERR PFX "unknown flags\n"); |
return -ENODEV; |
} |
|
vstable = psb->voltagestabilizationtime; |
printk(KERN_INFO PFX "voltage stable time: %d (units 20us)\n", |
dprintk(KERN_INFO PFX "voltage stabilization time: %d(*20us)\n", |
vstable); |
|
dprintk(KERN_DEBUG PFX "flags2: 0x%x\n", psb->flags2); |
dprintk(KERN_DEBUG PFX "flags2: %x\n", psb->flags2); |
rvo = psb->flags2 & 3; |
irt = ((psb->flags2) >> 2) & 3; |
mvs = ((psb->flags2) >> 4) & 3; |
vidmvs = 1 << mvs; |
batps = ((psb->flags2) >> 6) & 3; |
printk(KERN_INFO PFX "p states on battery: %d ", batps); |
switch (batps) { |
case 0: |
printk("- all available\n"); |
break; |
case 1: |
printk("- only the minimum\n"); |
break; |
case 2: |
printk("- only the 2 lowest\n"); |
break; |
case 3: |
printk("- only the 3 lowest\n"); |
break; |
} |
printk(KERN_INFO PFX "ramp voltage offset: %d\n", rvo); |
printk(KERN_INFO PFX "isochronous relief time: %d\n", irt); |
printk(KERN_INFO PFX "maximum voltage step: %d\n", mvs); |
if (batps) |
dprintk(KERN_INFO PFX "only %d pstates on battery\n", |
batps ); |
|
dprintk(KERN_DEBUG PFX "numpst: 0x%x\n", psb->numpst); |
dprintk(KERN_INFO PFX "ramp voltage offset: %d\n", rvo); |
dprintk(KERN_INFO PFX "isochronous relief time: %d\n", irt); |
dprintk(KERN_INFO PFX "maximum voltage step: %d - %x\n", |
mvs, vidmvs); |
|
if (psb->numpst != 1) { |
printk(KERN_ERR BFX "numpst must be 1\n"); |
dprintk(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\n", 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(KERN_INFO PFX "maxfid: 0x%x\n", psb->maxfid); |
printk(KERN_INFO PFX "maxvid: 0x%x\n", maxvid); |
|
numps = psb->numpstates; |
printk(KERN_INFO PFX "numpstates: 0x%x\n", numps); |
dprintk(KERN_INFO PFX "numpstates: %x\n", numps); |
if (numps < 2) { |
printk(KERN_ERR BFX "no p states to transition\n"); |
dprintk(KERN_ERR PFX "no p states to transition\n"); |
return -ENODEV; |
} |
|
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); |
} |
pst = (struct pst_s *)(psb + 1); |
if (check_pst_table(pst, maxvid)) |
return -EINVAL; |
|
if ((numps <= 1) || (batps <= 1)) { |
printk(KERN_ERR PFX "only 1 p-state to transition\n"); |
return -ENODEV; |
} |
|
ppst = kmalloc(sizeof (struct pst_s) * numps, GFP_KERNEL); |
if (!ppst) { |
printk(KERN_ERR PFX "ppst memory alloc failure\n"); |
ftbl = kmalloc((sizeof(struct cpufreq_frequency_table) |
* (numps + 1)), GFP_KERNEL); |
if (!ftbl) { |
dprintk(KERN_ERR PFX "ftbl memory alloc failure\n"); |
return -ENOMEM; |
} |
|
pst = (struct pst_s *) (psb + 1); |
for (j = 0; j < numps; j++) { |
ppst[j].fid = pst[j].fid; |
ppst[j].vid = pst[j].vid; |
printk(KERN_INFO PFX |
" %d : fid 0x%x, vid 0x%x\n", j, |
ppst[j].fid, ppst[j].vid); |
} |
sort_pst(ppst, numps); |
dprintk(KERN_INFO PFX " %d : fid %x, vid %x\n", j, |
pst[j].fid, pst[j].vid); |
|
lastfid = ppst[0].fid; |
if (lastfid > LO_FID_TABLE_TOP) |
printk(KERN_INFO BFX "first fid not in lo freq tbl\n"); |
|
if ((lastfid > MAX_FID) || (lastfid & 1) || (ppst[0].vid > LEAST_VID)) { |
printk(KERN_ERR BFX "first fid/vid bad (0x%x - 0x%x)\n", |
lastfid, ppst[0].vid); |
kfree(ppst); |
return -ENODEV; |
ftbl[j].index = pst[j].fid; /* lower 8 bits */ |
ftbl[j].index |= (pst[j].vid << 8); /* upper 8 bits */ |
ftbl[j].frequency = find_khz_freq_from_fid(pst[j].fid); |
} |
ftbl[numps].frequency = CPUFREQ_TABLE_END; |
ftbl[numps].index = 0; |
|
for (j = 1; j < numps; j++) { |
if ((lastfid >= ppst[j].fid) |
|| (ppst[j].fid & 1) |
|| (ppst[j].fid < HI_FID_TABLE_BOTTOM) |
|| (ppst[j].fid > MAX_FID) |
|| (ppst[j].vid > LEAST_VID)) { |
printk(KERN_ERR BFX |
"invalid fid/vid in pst(%x %x)\n", |
ppst[j].fid, ppst[j].vid); |
kfree(ppst); |
return -ENODEV; |
} |
lastfid = ppst[j].fid; |
} |
|
for (j = 0; j < numps; j++) { |
if (ppst[j].vid < rvo) { /* vid+rvo >= 0 */ |
printk(KERN_ERR BFX |
"0 vid exceeded with pstate %d\n", j); |
return -ENODEV; |
} |
if (ppst[j].vid < maxvid+rvo) { /* vid+rvo >= maxvid */ |
printk(KERN_ERR BFX |
"maxvid exceeded with pstate %d\n", j); |
return -ENODEV; |
} |
} |
|
if (query_current_values_with_pending_wait()) { |
kfree(ppst); |
return -EIO; |
kfree(ftbl); |
return 1; |
} |
dprintk(KERN_INFO PFX "cfid %x, cvid %x\n", currfid, currvid); |
|
printk(KERN_INFO PFX "currfid 0x%x, currvid 0x%x\n", |
currfid, currvid); |
|
for (j = 0; j < numps; j++) |
if ((ppst[j].fid==currfid) && (ppst[j].vid==currvid)) |
if ((pst[j].fid == currfid) && (pst[j].vid == currvid)) |
return (0); |
|
printk(KERN_ERR BFX "currfid/vid do not match PST, ignoring\n"); |
dprintk(KERN_ERR PFX "currfid/vid do not match PST, ignoring\n"); |
return 0; |
} |
|
printk(KERN_ERR BFX "no PSB\n"); |
return -ENODEV; |
} |
dprintk(KERN_ERR PFX "BIOS error - no PSB\n"); |
|
/* Converts a frequency (that might not necessarily be a multiple of 200) */ |
/* to a fid. */ |
static u32 find_closest_fid(u32 freq, int searchup) |
{ |
if (searchup == SEARCH_UP) |
freq += MIN_FREQ_RESOLUTION - 1; |
#if 0 |
/* hack for machines without a PSB - hardcode 2.0/1.8/0.8 GHz */ |
/* use this hack at your own risk */ |
vstable = 5; |
rvo = 2; |
irt = 2; |
mvs = 1; |
vidmvs = 1 << mvs; |
batps = numps = 3; |
plllock = 2; |
|
freq = (freq / MIN_FREQ_RESOLUTION) * MIN_FREQ_RESOLUTION; |
ftbl = kmalloc((sizeof(struct cpufreq_frequency_table) |
* (numps + 1)), GFP_KERNEL); |
if (!ftbl) |
return -ENOMEM; |
|
if (freq < MIN_FREQ) |
freq = MIN_FREQ; |
else if (freq > MAX_FREQ) |
freq = MAX_FREQ; |
ftbl[0].index = 0x00; /* 800 MHz */ |
ftbl[0].index |= 0x12 << 8; /* 1.100v */ |
|
return find_fid_from_freq(freq); |
} |
ftbl[0].frequency = find_khz_freq_from_fid( ftbl[0].index & 0x0f ); |
|
static int find_match(u32 * ptargfreq, u32 * pmin, u32 * pmax, int searchup, u32 * pfid, u32 * pvid) |
{ |
u32 availpstates = batps; |
u32 targfid = find_closest_fid(*ptargfreq, searchup); |
u32 minfid = find_closest_fid(*pmin, SEARCH_DOWN); |
u32 maxfid = find_closest_fid(*pmax, SEARCH_UP); |
u32 minidx = 0; |
u32 maxidx = availpstates - 1; |
u32 targidx = 0xffffffff; |
int i; |
ftbl[1].index = 0x0a; /* 1.8 GHz */ |
ftbl[1].index |= 0x03 << 8; /* 1.475v */ |
|
dprintk(KERN_DEBUG PFX "find match: freq %d MHz, min %d, max %d\n", |
*ptargfreq, *pmin, *pmax); |
ftbl[1].frequency = find_khz_freq_from_fid( ftbl[1].index & 0x0f ); |
|
/* Restrict values to the frequency choices in the PST */ |
if (minfid < ppst[0].fid) |
minfid = ppst[0].fid; |
if (maxfid > ppst[maxidx].fid) |
maxfid = ppst[maxidx].fid; |
ftbl[2].index = 0x0c; /* 2.0 GHz */ |
ftbl[2].index |= 0x02 << 8; /* 1.500v */ |
|
/* Find appropriate PST index for the minimim fid */ |
for (i = 0; i < (int) availpstates; i++) { |
if (minfid >= ppst[i].fid) |
minidx = i; |
} |
ftbl[2].frequency = find_khz_freq_from_fid( ftbl[2].index & 0x0f ); |
|
/* Find appropriate PST index for the maximum fid */ |
for (i = availpstates - 1; i >= 0; i--) { |
if (maxfid <= ppst[i].fid) |
maxidx = i; |
} |
ftbl[numps].frequency = CPUFREQ_TABLE_END; |
ftbl[numps].index = 0; |
|
if (minidx > maxidx) |
maxidx = minidx; |
|
/* Frequency ids are now constrained by limits matching PST entries */ |
minfid = ppst[minidx].fid; |
maxfid = ppst[maxidx].fid; |
|
/* Limit the target frequency to these limits */ |
if (targfid < minfid) |
targfid = minfid; |
else if (targfid > maxfid) |
targfid = maxfid; |
|
/* Find the best target index into the PST, contrained by the range */ |
if (searchup == SEARCH_UP) { |
for (i = maxidx; i >= (int) minidx; i--) { |
if (targfid <= ppst[i].fid) |
targidx = i; |
} |
} else { |
for (i = minidx; i <= (int) maxidx; i++) { |
if (targfid >= ppst[i].fid) |
targidx = i; |
} |
} |
|
if (targidx == 0xffffffff) { |
printk(KERN_ERR PFX "could not find target\n"); |
if (query_current_values_with_pending_wait()) { |
kfree(ftbl); |
return 1; |
} |
dprintk(KERN_INFO PFX "currfid %x, currvid %x\n", |
currfid, currvid); |
|
*pmin = find_freq_from_fid(minfid); |
*pmax = find_freq_from_fid(maxfid); |
*ptargfreq = find_freq_from_fid(ppst[targidx].fid); |
return 0; |
#endif |
|
if (pfid) |
*pfid = ppst[targidx].fid; |
if (pvid) |
*pvid = ppst[targidx].vid; |
|
return 0; |
return -ENODEV; |
} |
|
/* Take a frequency, and issue the fid/vid transition command */ |
static inline int transition_frequency(u32 * preq, u32 * pmin, u32 * pmax, u32 searchup) |
static inline int transition_frequency(unsigned int index) |
{ |
u32 fid; |
u32 vid; |
805,33 → 636,37 |
int res; |
struct cpufreq_freqs freqs; |
|
if (find_match(preq, pmin, pmax, searchup, &fid, &vid)) |
return 1; |
dprintk(KERN_DEBUG PFX "transition to index %u\n", index ); |
|
dprintk(KERN_DEBUG PFX "table matched fid 0x%x, giving vid 0x%x\n", |
fid, vid); |
/* 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 = ftbl[index].index & 0xFF; |
vid = (ftbl[index].index & 0xFF00) >> 8; |
|
dprintk(KERN_DEBUG PFX "matched fid %x, giving vid %x\n", fid, vid); |
|
if (query_current_values_with_pending_wait()) |
return 1; |
|
if ((currvid == vid) && (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; |
} |
|
if ((fid < HI_FID_TABLE_BOTTOM) && (currfid < HI_FID_TABLE_BOTTOM)) { |
printk(KERN_ERR PFX |
dprintk(KERN_ERR PFX |
"ignoring illegal change in lo freq table-%x to %x\n", |
currfid, fid); |
return 1; |
} |
|
dprintk(KERN_DEBUG PFX "changing to fid 0x%x, vid 0x%x\n", fid, vid); |
dprintk(KERN_DEBUG PFX "changing to fid %x, vid %x\n", fid, vid); |
|
freqs.cpu = 0; /* only true because SMP not supported */ |
|
freqs.old = find_freq_from_fid(currfid); |
freqs.new = find_freq_from_fid(fid); |
cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); |
845,21 → 680,15 |
} |
|
/* Driver entry point to switch to the target frequency */ |
static int powernowk8_target(struct cpufreq_policy *pol, unsigned targfreq, unsigned relation) |
static int |
drv_target(struct cpufreq_policy *pol, unsigned targfreq, unsigned relation) |
{ |
u32 checkfid = currfid; |
u32 checkvid = currvid; |
u32 reqfreq = targfreq / 1000; |
u32 minfreq = pol->min / 1000; |
u32 maxfreq = pol->max / 1000; |
unsigned int newstate; |
|
if (ppst == 0) { |
printk(KERN_ERR PFX "targ: ppst 0\n"); |
return -ENODEV; |
} |
|
if (pending_bit_stuck()) { |
printk(KERN_ERR PFX "drv targ fail: change pending bit set\n"); |
dprintk(KERN_ERR PFX "failing targ, change pending bit set\n"); |
return -EIO; |
} |
|
868,72 → 697,45 |
|
if (query_current_values_with_pending_wait()) |
return -EIO; |
|
dprintk(KERN_DEBUG PFX "targ: curr fid 0x%x, vid 0x%x\n", |
dprintk(KERN_DEBUG PFX "targ: curr fid %x, vid %x\n", |
currfid, 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", |
dprintk(KERN_ERR PFX "out of sync, fid %x %x, vid %x %x\n", |
checkfid, currfid, checkvid, currvid); |
} |
|
if (transition_frequency(&reqfreq, &minfreq, &maxfreq, |
relation == |
CPUFREQ_RELATION_H ? SEARCH_UP : SEARCH_DOWN)) |
if (cpufreq_frequency_table_target(pol, ftbl, targfreq, relation, |
&newstate)) |
return -EINVAL; |
|
if (transition_frequency(newstate)) |
{ |
printk(KERN_ERR PFX "transition frequency failed\n"); |
dprintk(KERN_ERR PFX "transition frequency failed\n"); |
return 1; |
} |
|
pol->cur = 1000 * find_freq_from_fid(currfid); |
|
pol->cur = find_khz_freq_from_fid(currfid); |
return 0; |
} |
|
/* Driver entry point to verify the policy and range of frequencies */ |
static int powernowk8_verify(struct cpufreq_policy *pol) |
static int drv_verify(struct cpufreq_policy *pol) |
{ |
u32 min = pol->min / 1000; |
u32 max = pol->max / 1000; |
u32 targ = min; |
int res; |
|
if (ppst == 0) { |
printk(KERN_ERR PFX "verify - ppst 0\n"); |
return -ENODEV; |
} |
|
if (pending_bit_stuck()) { |
printk(KERN_ERR PFX "failing verify, change pending bit set\n"); |
dprintk(KERN_ERR PFX "failing verify, change pending bit set\n"); |
return -EIO; |
} |
|
dprintk(KERN_DEBUG PFX |
"ver: cpu%d, min %d, max %d, cur %d, pol %d\n", pol->cpu, |
pol->min, pol->max, pol->cur, pol->policy); |
|
if (pol->cpu != 0) { |
printk(KERN_ERR PFX "verify - cpu not 0\n"); |
return -ENODEV; |
return cpufreq_frequency_table_verify(pol, ftbl); |
} |
|
#warning pol->policy is in undefined state here |
res = find_match(&targ, &min, &max, |
pol->policy == CPUFREQ_POLICY_POWERSAVE ? |
SEARCH_DOWN : SEARCH_UP, 0, 0); |
if (!res) { |
pol->min = min * 1000; |
pol->max = max * 1000; |
} |
return res; |
} |
|
/* per CPU init entry point to the driver */ |
static int __init powernowk8_cpu_init(struct cpufreq_policy *pol) |
static int __init |
drv_cpu_init(struct cpufreq_policy *pol) |
{ |
if (pol->cpu != 0) { |
printk(KERN_ERR PFX "init not cpu 0\n"); |
dprintk(KERN_ERR PFX "init - cpu 0\n"); |
return -ENODEV; |
} |
|
946,21 → 748,47 |
if (query_current_values_with_pending_wait()) |
return -EIO; |
|
pol->cur = 1000 * find_freq_from_fid(currfid); |
pol->cur = find_khz_freq_from_fid(currfid); |
dprintk(KERN_DEBUG PFX "policy current frequency %d kHz\n", pol->cur); |
|
/* min/max the cpu is capable of */ |
pol->cpuinfo.min_freq = 1000 * find_freq_from_fid(ppst[0].fid); |
pol->cpuinfo.max_freq = 1000 * find_freq_from_fid(ppst[numps-1].fid); |
pol->min = 1000 * find_freq_from_fid(ppst[0].fid); |
pol->max = 1000 * find_freq_from_fid(ppst[batps - 1].fid); |
if (cpufreq_frequency_table_cpuinfo(pol, ftbl)) { |
dprintk(KERN_ERR PFX "invalid ftbl\n"); |
kfree(ftbl); |
return -EINVAL; |
} |
|
printk(KERN_INFO PFX "cpu_init done, current fid 0x%x, vid 0x%x\n", |
currfid, currvid); |
/* Added by Nino */ |
cpufreq_frequency_table_get_attr(ftbl, pol->cpu); |
|
dprintk(KERN_INFO PFX "init, curr fid %x vid %x\n", currfid, currvid); |
return 0; |
} |
|
|
static int __exit drv_cpu_exit (struct cpufreq_policy *pol) |
{ |
if (pol->cpu != 0) |
return -EINVAL; |
|
/* Added by Nino */ |
cpufreq_frequency_table_put_attr(pol->cpu); |
|
kfree(ftbl); |
|
return 0; |
} |
|
static struct cpufreq_driver cpufreq_amd64_driver = { |
.verify = drv_verify, |
.target = drv_target, |
.init = drv_cpu_init, |
.exit = drv_cpu_exit, |
.name = "powernow-k8", |
.owner = THIS_MODULE |
}; |
|
|
/* driver entry point for init */ |
/*static*/ int __init powernowk8_init(void) |
{ |
976,8 → 804,7 |
return rc; |
|
if (pending_bit_stuck()) { |
printk(KERN_ERR PFX "powernowk8_init fail, change pending bit set\n"); |
kfree(ppst); |
dprintk(KERN_ERR PFX "failing init, change pending bit set\n"); |
return -EIO; |
} |
|
987,10 → 814,8 |
/* driver entry point for term */ |
/*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); |
kfree(ppst); |
} |
|
MODULE_AUTHOR("Paul Devriendt <paul.devriendt@amd.com>"); |