Subversion Repositories shark

Compare Revisions

Ignore whitespace Rev 769 → Rev 770

/shark/trunk/drivers/cpu/include/drivers/shark_cpu26.h
49,6 → 49,7
int CPU26_get_cur_frequency(void);
int CPU26_get_min_frequency(void);
int CPU26_get_max_frequency(void);
int CPU26_get_frequencys(char *buf);
int CPU26_get_latency(void);
 
#endif
/shark/trunk/drivers/cpu/cpufreq/powernow-k6.c
165,6 → 165,9
policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
policy->cur = busfreq * max_multiplier;
 
/* Added by Nino */
cpufreq_frequency_table_get_attr(&clock_ratio[0], policy->cpu);
 
return cpufreq_frequency_table_cpuinfo(policy, &clock_ratio[0]);
}
 
176,6 → 179,10
if (i==max_multiplier)
powernow_k6_set_state(i);
}
 
/* Added by Nino */
cpufreq_frequency_table_put_attr(policy->cpu);
 
return 0;
}
 
/shark/trunk/drivers/cpu/cpufreq/powernow-k7.c
392,6 → 392,9
policy->cpuinfo.transition_latency = latency;
policy->cur = maximum_speed;
 
/* Added by Nino */
cpufreq_frequency_table_get_attr(powernow_table, policy->cpu);
 
return cpufreq_frequency_table_cpuinfo(policy, powernow_table);
}
 
/shark/trunk/drivers/cpu/cpufreq/powernow-k8.c
1,1001 → 1,826
/*
* (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
*
* 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
* Licensed under the terms of the GNU GPL License version 2.
* Based upon datasheets & sample CPUs kindly provided by AMD.
*
* 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
*
*/
 
#include <linuxcomp.h>
 
#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 DEBUG
 
#ifdef DEBUG
#define dprintk(msg...) printk(msg)
#else
#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 currvid; /* keep track of the current fid / vid */
static u32 currfid;
 
/*
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)
{
return 800 + (fid * 100);
}
 
/* Return a fid matching an input frequency in MHz */
static u32 find_fid_from_freq(u32 freq)
{
return (freq - 800) / 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;
}
}
 
/* 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. */
static inline int pending_bit_stuck(void)
{
u32 lo;
u32 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(void)
{
u32 lo;
u32 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);
}
 
currvid = hi & MSR_S_HI_CURRENT_VID;
currfid = lo & MSR_S_LO_CURRENT_FID;
 
return 0;
}
 
/* the isochronous relief time */
static inline void count_off_irt(void)
{
udelay((1 << irt) * 10);
return;
}
 
/* the voltage stabalization time */
static inline void count_off_vst(void)
{
udelay(vstable * VST_UNITS_20US);
return;
}
 
/* write the new fid value along with the other control fields to the msr */
static int write_new_fid(u32 fid)
{
u32 lo;
u32 savevid = currvid;
 
if ((fid & INVALID_FID_MASK) || (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;
 
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",
savevid, currvid);
return 1;
}
 
if (fid != currfid) {
printk(KERN_ERR PFX
"fid transition failed, fid %x, currfid %x\n",
fid, currfid);
return 1;
}
 
return 0;
}
 
/* Write a new vid to the hardware */
static int write_new_vid(u32 vid)
{
u32 lo;
u32 savefid = currfid;
 
if ((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;
 
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()) {
return 1;
}
 
if (savefid != currfid) {
printk(KERN_ERR PFX
"fid changed on vid transition, save %x currfid %x\n",
savefid, currfid);
return 1;
}
 
if (vid != currvid) {
printk(KERN_ERR PFX
"vid transition failed, vid %x, currvid %x\n",
vid, 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(u32 reqvid, u32 step)
{
if ((currvid - reqvid) > step)
reqvid = currvid - step;
 
if (write_new_vid(reqvid))
return 1;
 
count_off_vst();
 
return 0;
}
 
/* Change the fid and vid, by the 3 phases. */
static inline int transition_fid_vid(u32 reqfid, u32 reqvid)
{
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",
reqfid, reqvid, currfid, currvid);
return 1;
}
 
dprintk(KERN_INFO PFX
"transitioned: new fid 0x%x, vid 0x%x\n", currfid, currvid);
 
return 0;
}
 
/* Phase 1 - core voltage transition ... setup appropriate voltage for the */
/* fid transition. */
static inline int core_voltage_pre_transition(u32 reqvid)
{
u32 rvosteps = rvo;
u32 savefid = 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))
return 1;
}
 
while (rvosteps > 0) {
if (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))
return 1;
rvosteps--;
}
}
 
if (query_current_values_with_pending_wait())
return 1;
 
if (savefid != currfid) {
printk(KERN_ERR PFX "ph1 err, currfid changed 0x%x\n", currfid);
return 1;
}
 
dprintk(KERN_DEBUG PFX "ph1 complete, currfid 0x%x, currvid 0x%x\n",
currfid, currvid);
 
return 0;
}
 
/* Phase 2 - core frequency transition */
static inline int core_frequency_transition(u32 reqfid)
{
u32 vcoreqfid;
u32 vcocurrfid;
u32 vcofiddiff;
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",
reqfid, currfid);
return 1;
}
 
if (currfid == reqfid) {
printk(KERN_ERR PFX "ph2 null fid transition 0x%x\n", currfid);
return 0;
}
 
dprintk(KERN_DEBUG PFX
"ph2 starting, currfid 0x%x, currvid 0x%x, reqfid 0x%x\n",
currfid, currvid, reqfid);
 
vcoreqfid = convert_fid_to_vco_fid(reqfid);
vcocurrfid = convert_fid_to_vco_fid(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)) {
return 1;
}
} else {
if (write_new_fid
(2 + convert_fid_to_vco_fid(currfid))) {
return 1;
}
}
} else {
if (write_new_fid(currfid - 2))
return 1;
}
 
vcocurrfid = convert_fid_to_vco_fid(currfid);
vcofiddiff = vcocurrfid > vcoreqfid ? vcocurrfid - vcoreqfid
: vcoreqfid - vcocurrfid;
}
 
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",
currfid, reqfid);
return 1;
}
 
if (savevid != currvid) {
printk(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",
currfid, currvid);
 
return 0;
}
 
/* Phase 3 - core voltage transition flow ... jump to the final vid. */
static inline int core_voltage_post_transition(u32 reqvid)
{
u32 savefid = currfid;
u32 savereqvid = reqvid;
 
dprintk(KERN_DEBUG PFX "ph3 starting, currfid 0x%x, currvid 0x%x\n",
currfid, currvid);
 
if (reqvid != currvid) {
if (write_new_vid(reqvid))
return 1;
 
if (savefid != currfid) {
printk(KERN_ERR PFX
"ph3: bad fid change, save %x, curr %x\n",
savefid, currfid);
return 1;
}
 
if (currvid != reqvid) {
printk(KERN_ERR PFX
"ph3: failed vid transition\n, req %x, curr %x",
reqvid, currvid);
return 1;
}
}
 
if (query_current_values_with_pending_wait())
return 1;
 
if (savereqvid != currvid) {
dprintk(KERN_ERR PFX "ph3 failed, currvid 0x%x\n", currvid);
return 1;
}
 
if (savefid != currfid) {
dprintk(KERN_ERR PFX "ph3 failed, currfid changed 0x%x\n",
currfid);
return 1;
}
 
dprintk(KERN_DEBUG PFX "ph3 complete, currfid 0x%x, currvid 0x%x\n",
currfid, currvid);
 
return 0;
}
 
static inline int check_supported_cpu(void)
{
struct cpuinfo_x86 *c = &new_cpu_data;
u32 eax, ebx, ecx, edx;
 
if (num_online_cpus() != 1) {
dprintk(KERN_INFO PFX "multiprocessor systems not supported\n");
return 0;
}
 
if (c->x86_vendor != X86_VENDOR_AMD) {
dprintk(KERN_INFO PFX "Not an AMD processor\n");
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);
return 0;
} else {
dprintk(KERN_INFO PFX "AMD Athlon 64 or AMD Opteron processor found\n");
}
 
eax = cpuid_eax(CPUID_GET_MAX_CAPABILITIES);
if (eax < CPUID_FREQ_VOLT_CAPABILITIES) {
dprintk(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) {
dprintk(KERN_INFO PFX "Power state transitions not supported\n");
return 0;
}
 
printk(KERN_INFO PFX "Found AMD Athlon 64 / Opteron processor "
"supporting p-state transitions\n");
 
return 1;
}
 
/* 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;
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;
}
 
vstable = psb->voltagestabilizationtime;
printk(KERN_INFO PFX "voltage stable time: %d (units 20us)\n",
vstable);
 
dprintk(KERN_DEBUG PFX "flags2: 0x%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);
 
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);
 
plllock = psb->plllocktime;
printk(KERN_INFO PFX "pll lock time: 0x%x\n", plllock);
 
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);
if (numps < 2) {
printk(KERN_ERR BFX "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);
}
 
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");
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);
 
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;
}
 
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;
}
 
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))
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;
}
 
/* 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;
 
freq = (freq / MIN_FREQ_RESOLUTION) * MIN_FREQ_RESOLUTION;
 
if (freq < MIN_FREQ)
freq = MIN_FREQ;
else if (freq > MAX_FREQ)
freq = MAX_FREQ;
 
return find_fid_from_freq(freq);
}
 
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;
 
dprintk(KERN_DEBUG PFX "find match: freq %d MHz, min %d, max %d\n",
*ptargfreq, *pmin, *pmax);
 
/* 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;
 
/* Find appropriate PST index for the minimim fid */
for (i = 0; i < (int) availpstates; i++) {
if (minfid >= ppst[i].fid)
minidx = i;
}
 
/* Find appropriate PST index for the maximum fid */
for (i = availpstates - 1; i >= 0; i--) {
if (maxfid <= ppst[i].fid)
maxidx = i;
}
 
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");
return 1;
}
 
*pmin = find_freq_from_fid(minfid);
*pmax = find_freq_from_fid(maxfid);
*ptargfreq = find_freq_from_fid(ppst[targidx].fid);
 
if (pfid)
*pfid = ppst[targidx].fid;
if (pvid)
*pvid = ppst[targidx].vid;
 
return 0;
}
 
/* Take a frequency, and issue the fid/vid transition command */
static inline int transition_frequency(u32 * preq, u32 * pmin, u32 * pmax, u32 searchup)
{
u32 fid;
u32 vid;
int res;
struct cpufreq_freqs freqs;
 
if (find_match(preq, pmin, pmax, searchup, &fid, &vid))
return 1;
 
dprintk(KERN_DEBUG PFX "table matched fid 0x%x, giving vid 0x%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",
fid, vid);
return 0;
}
 
if ((fid < HI_FID_TABLE_BOTTOM) && (currfid < HI_FID_TABLE_BOTTOM)) {
printk(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);
 
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);
 
res = transition_fid_vid(fid, vid);
 
freqs.new = find_freq_from_fid(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)
{
u32 checkfid = currfid;
u32 checkvid = currvid;
u32 reqfreq = targfreq / 1000;
u32 minfreq = pol->min / 1000;
u32 maxfreq = pol->max / 1000;
 
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");
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())
return -EIO;
 
dprintk(KERN_DEBUG PFX "targ: curr fid 0x%x, vid 0x%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",
checkfid, currfid, checkvid, currvid);
}
 
if (transition_frequency(&reqfreq, &minfreq, &maxfreq,
relation ==
CPUFREQ_RELATION_H ? SEARCH_UP : SEARCH_DOWN))
{
printk(KERN_ERR PFX "transition frequency failed\n");
return 1;
}
 
pol->cur = 1000 * find_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)
{
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");
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;
}
 
#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)
{
if (pol->cpu != 0) {
printk(KERN_ERR PFX "init not cpu 0\n");
return -ENODEV;
}
 
pol->governor = 0; //!!!CPUFREQ_DEFAULT_GOVERNOR;
 
/* Take a crude guess here. */
pol->cpuinfo.transition_latency = ((rvo + 8) * vstable * VST_UNITS_20US)
+ (3 * (1 << irt) * 10);
 
if (query_current_values_with_pending_wait())
return -EIO;
 
pol->cur = 1000 * find_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);
 
printk(KERN_INFO PFX "cpu_init done, current fid 0x%x, vid 0x%x\n",
currfid, currvid);
 
return 0;
}
 
/* driver entry point for init */
/*static*/ int __init powernowk8_init(void)
{
int rc;
 
dprintk(KERN_INFO PFX VERSION "\n");
 
if (check_supported_cpu() == 0)
return -ENODEV;
 
rc = find_psb_table();
if (rc)
return rc;
 
if (pending_bit_stuck()) {
printk(KERN_ERR PFX "powernowk8_init fail, change pending bit set\n");
kfree(ppst);
return -EIO;
}
 
return cpufreq_register_driver(&cpufreq_amd64_driver);
}
 
/* driver entry point for term */
/*static*/ void __exit powernowk8_exit(void)
{
dprintk(KERN_INFO PFX "powernowk8_exit\n");
 
cpufreq_unregister_driver(&cpufreq_amd64_driver);
kfree(ppst);
}
 
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);
/*
* (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.ork.uk> on behalf of SuSE Labs
* 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
*/
 
//#define DEBUG
 
#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 VERSION "version 1.00.12 - February 29, 2004"
#include "powernow-k8.h"
 
#ifdef DEBUG
#define dprintk(msg...) printk(msg)
#else
#define dprintk(msg...) do { } while(0)
#endif
 
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;
 
 
/* 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 KHz, given an input fid */
static inline u32 find_khz_freq_from_fid(u32 fid)
{
return 1000 * (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;
u32 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(void)
{
u32 lo;
u32 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);
}
 
currvid = hi & MSR_S_HI_CURRENT_VID;
currfid = lo & MSR_S_LO_CURRENT_FID;
 
return 0;
}
 
/* the isochronous relief time */
static inline void count_off_irt(void)
{
udelay((1 << irt) * 10);
return;
}
 
/* the voltage stabalization time */
static inline void count_off_vst(void)
{
udelay(vstable * VST_UNITS_20US);
return;
}
 
/* write the new fid value along with the other control fields to the msr */
static int write_new_fid(u32 fid)
{
u32 lo;
u32 savevid = currvid;
 
if ((fid & INVALID_FID_MASK) || (currvid & INVALID_VID_MASK)) {
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) {
dprintk(KERN_ERR PFX "vid change on fid trans, old %x, new %x\n",
savevid, currvid);
return 1;
}
 
if (fid != currfid) {
dprintk(KERN_ERR PFX "fid trans failed, fid %x, curr %x\n", fid,
currfid);
return 1;
}
 
return 0;
}
 
/* Write a new vid to the hardware */
static int write_new_vid(u32 vid)
{
u32 lo;
u32 savefid = currfid;
 
if ((currfid & INVALID_FID_MASK) || (vid & INVALID_VID_MASK)) {
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())
return 1;
 
if (savefid != currfid) {
dprintk(KERN_ERR PFX "fid changed on vid trans, old %x new %x\n",
savefid, currfid);
return 1;
}
 
if (vid != currvid) {
dprintk(KERN_ERR PFX "vid trans failed, vid %x, curr %x\n", vid,
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(u32 reqvid, u32 step)
{
if ((currvid - reqvid) > step)
reqvid = currvid - step;
if (write_new_vid(reqvid))
return 1;
count_off_vst();
return 0;
}
 
/* Change the fid and vid, by the 3 phases. */
static inline int transition_fid_vid(u32 reqfid, u32 reqvid)
{
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)) {
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 %x, vid %x\n", currfid, currvid);
return 0;
}
 
/* Phase 1 - core voltage transition ... setup voltage */
static inline int core_voltage_pre_transition(u32 reqvid)
{
u32 rvosteps = rvo;
u32 savefid = currfid;
 
dprintk(KERN_DEBUG PFX
"ph1: start, currfid %x, currvid %x, reqvid %x, rvo %x\n",
currfid, currvid, reqvid, rvo);
 
while (currvid > reqvid) {
dprintk(KERN_DEBUG PFX "ph1: curr %x, req vid %x\n",
currvid, reqvid);
if (decrease_vid_code_by_step(reqvid, vidmvs))
return 1;
}
 
while (rvosteps > 0) {
if (currvid == 0) {
rvosteps = 0;
} else {
dprintk(KERN_DEBUG PFX
"ph1: changing vid for rvo, req %x\n",
currvid - 1);
if (decrease_vid_code_by_step(currvid - 1, 1))
return 1;
rvosteps--;
}
}
 
if (query_current_values_with_pending_wait())
return 1;
 
if (savefid != currfid) {
dprintk(KERN_ERR PFX "ph1: err, currfid changed %x\n", currfid);
return 1;
}
 
dprintk(KERN_DEBUG PFX "ph1: complete, currfid %x, currvid %x\n",
currfid, currvid);
 
return 0;
}
 
/* Phase 2 - core frequency transition */
static inline int core_frequency_transition(u32 reqfid)
{
u32 vcoreqfid;
u32 vcocurrfid;
u32 vcofiddiff;
u32 savevid = currvid;
 
if ((reqfid < HI_FID_TABLE_BOTTOM) && (currfid < HI_FID_TABLE_BOTTOM)) {
dprintk(KERN_ERR PFX "ph2: illegal lo-lo transition %x %x\n",
reqfid, currfid);
return 1;
}
 
if (currfid == reqfid) {
dprintk(KERN_ERR PFX "ph2: null fid transition %x\n", currfid);
return 0;
}
 
dprintk(KERN_DEBUG PFX
"ph2: starting, currfid %x, currvid %x, reqfid %x\n",
currfid, currvid, reqfid);
 
vcoreqfid = convert_fid_to_vco_fid(reqfid);
vcocurrfid = convert_fid_to_vco_fid(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)) {
return 1;
}
} else {
if (write_new_fid
(2 + convert_fid_to_vco_fid(currfid))) {
return 1;
}
}
} else {
if (write_new_fid(currfid - 2))
return 1;
}
 
vcocurrfid = convert_fid_to_vco_fid(currfid);
vcofiddiff = vcocurrfid > vcoreqfid ? vcocurrfid - vcoreqfid
: vcoreqfid - vcocurrfid;
}
 
if (write_new_fid(reqfid))
return 1;
if (query_current_values_with_pending_wait())
return 1;
 
if (currfid != reqfid) {
dprintk(KERN_ERR PFX
"ph2: mismatch, failed fid trans, curr %x, req %x\n",
currfid, reqfid);
return 1;
}
 
if (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 %x, currvid %x\n",
currfid, currvid);
 
return 0;
}
 
/* Phase 3 - core voltage transition flow ... jump to the final vid. */
static inline int core_voltage_post_transition(u32 reqvid)
{
u32 savefid = currfid;
u32 savereqvid = reqvid;
 
dprintk(KERN_DEBUG PFX "ph3: starting, currfid %x, currvid %x\n",
currfid, currvid);
 
if (reqvid != currvid) {
if (write_new_vid(reqvid))
return 1;
 
if (savefid != currfid) {
dprintk(KERN_ERR PFX
"ph3: bad fid change, save %x, curr %x\n",
savefid, currfid);
return 1;
}
 
if (currvid != reqvid) {
dprintk(KERN_ERR PFX
"ph3: failed vid transition\n, req %x, curr %x",
reqvid, currvid);
return 1;
}
}
 
if (query_current_values_with_pending_wait())
return 1;
 
if (savereqvid != currvid) {
dprintk(KERN_ERR PFX "ph3: failed, currvid %x\n", currvid);
return 1;
}
 
if (savefid != currfid) {
dprintk(KERN_ERR PFX "ph3: failed, currfid changed %x\n",
currfid);
return 1;
}
 
dprintk(KERN_DEBUG PFX "ph3: complete, currfid %x, currvid %x\n",
currfid, currvid);
return 0;
}
 
static inline int check_supported_cpu(void)
{
struct cpuinfo_x86 *c = &new_cpu_data;
u32 eax, ebx, ecx, edx;
 
if (num_online_cpus() != 1) {
dprintk(KERN_INFO PFX "multiprocessor systems not supported\n");
return 0;
}
 
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)) {
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");
}
 
eax = cpuid_eax(CPUID_GET_MAX_CAPABILITIES);
if (eax < CPUID_FREQ_VOLT_CAPABILITIES) {
dprintk(KERN_INFO PFX "No freq change capabilities\n");
return 0;
}
 
cpuid(CPUID_FREQ_VOLT_CAPABILITIES, &eax, &ebx, &ecx, &edx);
if ((edx & P_STATE_TRANSITION_CAPABLE) != P_STATE_TRANSITION_CAPABLE) {
dprintk(KERN_INFO PFX "Power state transitions not supported\n");
return 0;
}
 
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 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 %p\n", psb);
dprintk(KERN_DEBUG PFX "table version: %x\n",
psb->tableversion);
if (psb->tableversion != PSB_VERSION_1_4) {
dprintk(KERN_INFO PFX "PSB table is not v1.4\n");
return -ENODEV;
}
 
dprintk(KERN_DEBUG PFX "flags: %x\n", psb->flags1);
if (psb->flags1) {
dprintk(KERN_ERR PFX "unknown flags\n");
return -ENODEV;
}
 
vstable = psb->voltagestabilizationtime;
dprintk(KERN_INFO PFX "voltage stabilization time: %d(*20us)\n",
vstable);
 
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;
if (batps)
dprintk(KERN_INFO PFX "only %d pstates on battery\n",
batps );
 
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) {
dprintk(KERN_ERR PFX "numpst must be 1\n");
return -ENODEV;
}
 
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;
 
numps = psb->numpstates;
dprintk(KERN_INFO PFX "numpstates: %x\n", numps);
if (numps < 2) {
dprintk(KERN_ERR PFX "no p states to transition\n");
return -ENODEV;
}
 
pst = (struct pst_s *)(psb + 1);
if (check_pst_table(pst, maxvid))
return -EINVAL;
 
ftbl = kmalloc((sizeof(struct cpufreq_frequency_table)
* (numps + 1)), GFP_KERNEL);
if (!ftbl) {
dprintk(KERN_ERR PFX "ftbl memory alloc failure\n");
return -ENOMEM;
}
 
for (j = 0; j < numps; j++) {
dprintk(KERN_INFO PFX " %d : fid %x, vid %x\n", j,
pst[j].fid, pst[j].vid);
 
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;
 
if (query_current_values_with_pending_wait()) {
kfree(ftbl);
return 1;
}
dprintk(KERN_INFO PFX "cfid %x, cvid %x\n", currfid, currvid);
 
for (j = 0; j < numps; j++)
if ((pst[j].fid == currfid) && (pst[j].vid == currvid))
return (0);
dprintk(KERN_ERR PFX "currfid/vid do not match PST, ignoring\n");
return 0;
}
 
dprintk(KERN_ERR PFX "BIOS error - no PSB\n");
 
#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;
 
ftbl = kmalloc((sizeof(struct cpufreq_frequency_table)
* (numps + 1)), GFP_KERNEL);
if (!ftbl)
return -ENOMEM;
 
ftbl[0].index = 0x00; /* 800 MHz */
ftbl[0].index |= 0x12 << 8; /* 1.100v */
 
ftbl[0].frequency = find_khz_freq_from_fid( ftbl[0].index & 0x0f );
 
ftbl[1].index = 0x0a; /* 1.8 GHz */
ftbl[1].index |= 0x03 << 8; /* 1.475v */
 
ftbl[1].frequency = find_khz_freq_from_fid( ftbl[1].index & 0x0f );
 
ftbl[2].index = 0x0c; /* 2.0 GHz */
ftbl[2].index |= 0x02 << 8; /* 1.500v */
 
ftbl[2].frequency = find_khz_freq_from_fid( ftbl[2].index & 0x0f );
 
ftbl[numps].frequency = CPUFREQ_TABLE_END;
ftbl[numps].index = 0;
 
if (query_current_values_with_pending_wait()) {
kfree(ftbl);
return 1;
}
dprintk(KERN_INFO PFX "currfid %x, currvid %x\n",
currfid, currvid);
 
return 0;
#endif
 
return -ENODEV;
}
 
/* Take a frequency, and issue the fid/vid transition command */
static inline int transition_frequency(unsigned int index)
{
u32 fid;
u32 vid;
int res;
struct cpufreq_freqs freqs;
 
dprintk(KERN_DEBUG PFX "transition to index %u\n", 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 = 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 curr (fid %x, vid %x)\n",
fid, vid);
return 0;
}
 
if ((fid < HI_FID_TABLE_BOTTOM) && (currfid < HI_FID_TABLE_BOTTOM)) {
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 %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);
 
res = transition_fid_vid(fid, vid);
 
freqs.new = find_freq_from_fid(currfid);
cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
 
return res;
}
 
/* Driver entry point to switch to the target frequency */
static int
drv_target(struct cpufreq_policy *pol, unsigned targfreq, unsigned relation)
{
u32 checkfid = currfid;
u32 checkvid = currvid;
unsigned int newstate;
 
if (pending_bit_stuck()) {
dprintk(KERN_ERR PFX "failing targ, 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())
return -EIO;
dprintk(KERN_DEBUG PFX "targ: curr fid %x, vid %x\n",
currfid, currvid);
 
if ((checkvid != currvid) || (checkfid != currfid)) {
dprintk(KERN_ERR PFX "out of sync, fid %x %x, vid %x %x\n",
checkfid, currfid, checkvid, currvid);
}
 
if (cpufreq_frequency_table_target(pol, ftbl, targfreq, relation,
&newstate))
return -EINVAL;
if (transition_frequency(newstate))
{
dprintk(KERN_ERR PFX "transition frequency failed\n");
return 1;
}
 
pol->cur = find_khz_freq_from_fid(currfid);
return 0;
}
 
/* Driver entry point to verify the policy and range of frequencies */
static int drv_verify(struct cpufreq_policy *pol)
{
if (pending_bit_stuck()) {
dprintk(KERN_ERR PFX "failing verify, change pending bit set\n");
return -EIO;
}
 
return cpufreq_frequency_table_verify(pol, ftbl);
}
 
/* per CPU init entry point to the driver */
static int __init
drv_cpu_init(struct cpufreq_policy *pol)
{
if (pol->cpu != 0) {
dprintk(KERN_ERR PFX "init - cpu 0\n");
return -ENODEV;
}
 
pol->governor = 0; //!!! CPUFREQ_DEFAULT_GOVERNOR;
 
/* Take a crude guess here. */
pol->cpuinfo.transition_latency = ((rvo + 8) * vstable * VST_UNITS_20US)
+ (3 * (1 << irt) * 10);
 
if (query_current_values_with_pending_wait())
return -EIO;
 
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 */
if (cpufreq_frequency_table_cpuinfo(pol, ftbl)) {
dprintk(KERN_ERR PFX "invalid ftbl\n");
kfree(ftbl);
return -EINVAL;
}
 
/* 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)
{
int rc;
 
dprintk(KERN_INFO PFX VERSION "\n");
 
if (check_supported_cpu() == 0)
return -ENODEV;
 
rc = find_psb_table();
if (rc)
return rc;
 
if (pending_bit_stuck()) {
dprintk(KERN_ERR PFX "failing init, change pending bit set\n");
return -EIO;
}
 
return cpufreq_register_driver(&cpufreq_amd64_driver);
}
 
/* 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);
/shark/trunk/drivers/cpu/cpufreq/freq_table.c
151,7 → 151,7
/**
* show_scaling_governor - show the current policy for the specified CPU
*/
static ssize_t show_available_freqs (struct cpufreq_policy *policy, char *buf)
/*static*/ ssize_t show_available_freqs (struct cpufreq_policy *policy, char *buf)
{
unsigned int i = 0;
unsigned int cpu = policy->cpu;
/shark/trunk/drivers/cpu/cpufreq/cpufreq.c
40,7 → 40,7
 
//extern void kern_scale_timer(unsigned int old_f, unsigned int new_f);
extern void ll_scale_advtimer(unsigned int old_f, unsigned int new_f);
 
extern ssize_t show_available_freqs (struct cpufreq_policy *policy, char *buf);
/**
* The "cpufreq driver" - the arch- or hardware-dependend low
* level driver of CPUFreq support, and its spinlock. This lock
55,31 → 55,36
* USER *
*********************************************************************/
 
int cpufreq_target(unsigned int target_freq, unsigned int relation)
inline int cpufreq_target(unsigned int target_freq, unsigned int relation)
{
return cpufreq_driver_target(cpufreq_cpu_data, target_freq, relation);
}
 
int cpufreq_get_cur_freq(void)
inline int cpufreq_get_cur_freq(void)
{
return cpufreq_cpu_data->cur;
}
 
int cpufreq_get_min_freq(void)
inline int cpufreq_get_min_freq(void)
{
return cpufreq_cpu_data->min;
}
 
int cpufreq_get_max_freq(void)
inline int cpufreq_get_max_freq(void)
{
return cpufreq_cpu_data->max;
}
 
int cpufreq_get_latency(void)
inline int cpufreq_get_latency(void)
{
return cpufreq_cpu_data->cpuinfo.transition_latency;
}
 
inline int cpufreq_show_available_freqs(char *buf)
{
return show_available_freqs(cpufreq_cpu_data, buf);
}
 
/*********************************************************************
* GOVERNOR *
*********************************************************************/
/shark/trunk/drivers/cpu/cpufreq/powernow-k8.h
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
20,7 → 20,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
 
46,9 → 45,8
#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_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
 
55,33 → 53,25
#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_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_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.
*/
* 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
111,7 → 101,3
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 int powernowk8_verify(struct cpufreq_policy *pol);
static int powernowk8_target(struct cpufreq_policy *pol, unsigned targfreq,
unsigned relation);
static int __init powernowk8_cpu_init(struct cpufreq_policy *pol);
/shark/trunk/drivers/cpu/cpufreq/speedstep-centrino.c
209,7 → 209,10
 
dprintk(KERN_INFO PFX "centrino_cpu_init: policy=%d cur=%dkHz\n",
policy->policy, policy->cur);
 
/* Added by Nino */
cpufreq_frequency_table_get_attr(centrino_model->op_points, policy->cpu);
 
return cpufreq_frequency_table_cpuinfo(policy, centrino_model->op_points);
}
 
/shark/trunk/drivers/cpu/cpufreq/speedstep-ich.c
305,6 → 305,9
policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
policy->cur = speed;
 
/* Added by Nino */
cpufreq_frequency_table_get_attr(&speedstep_freqs[0], policy->cpu);
 
return cpufreq_frequency_table_cpuinfo(policy, &speedstep_freqs[0]);
}
 
/shark/trunk/drivers/cpu/shark/shark_cpu.c
57,33 → 57,39
extern int cpufreq_get_min_freq(void);
extern int cpufreq_get_max_freq(void);
extern int cpufreq_get_latency(void);
extern int cpufreq_show_available_freqs(char *buf);
 
static int cpu_installed = FALSE;
static int dvs_installed = DVS_NONE;
 
/* DVS user function */
int CPU26_set_frequency(unsigned int target_freq, unsigned int relation)
inline int CPU26_set_frequency(unsigned int target_freq, unsigned int relation)
{
return cpufreq_target(target_freq, relation);
}
 
int CPU26_get_cur_frequency()
inline int CPU26_get_cur_frequency()
{
return cpufreq_get_cur_freq();
}
 
int CPU26_get_min_frequency()
inline int CPU26_get_min_frequency()
{
return cpufreq_get_min_freq();
}
 
int CPU26_get_max_frequency()
inline int CPU26_get_max_frequency()
{
return cpufreq_get_max_freq();
}
 
int CPU26_get_latency()
inline int CPU26_get_frequencys(char *buf)
{
return cpufreq_show_available_freqs (buf);
}
 
inline int CPU26_get_latency()
{
return cpufreq_get_latency();
}
 
208,12 → 214,12
}
 
/* Init the Linux CPU Layer */
int CPU26_installed(void)
inline int CPU26_installed(void)
{
return cpu_installed;
}
 
void CPU26_showinfo(void)
inline void CPU26_showinfo(void)
{
print_cpu_info_0();
}
236,7 → 242,7
return ret;
}
 
int CPU26_close(void)
inline int CPU26_close(void)
{
if (cpu_installed == TRUE) {
return 0;