[CPUFREQ] architectural pstate driver for powernow-k8

This patch should apply cleanly to the 2.6.23-git7 kernel.  It changes the
powernow-k8 driver code that deals with 3rd generation Opteron, Phenom,
and later processors to match the architectural pstate driver described
in the AMD64 Architecture Programmer's Manual Volume 2 Chapter 18.  The
initial implementation of the hardware pstate driver for PowerNow!
used some processor-version specific features, and would not be
maintainable in the long term as the processor features changed.
This architectural driver should work on all future AMD processors.
 
Signed-off-by: Mark Langsdorf <mark.langsdorf@amd.com>
Signed-off-by: Andreas Herrmann <andreas.herrmann3@amd.com>
Signed-off-by: Dave Jones <davej@redhat.com>
This commit is contained in:
Mark Langsdorf 2007-10-17 16:52:08 -05:00 committed by Dave Jones
parent 63bd8c48e0
commit c5829cd07e
2 changed files with 29 additions and 81 deletions

View File

@ -46,7 +46,7 @@
#define PFX "powernow-k8: "
#define BFX PFX "BIOS error: "
#define VERSION "version 2.00.00"
#define VERSION "version 2.20.00"
#include "powernow-k8.h"
/* serialize freq changes */
@ -73,33 +73,11 @@ static u32 find_khz_freq_from_fid(u32 fid)
return 1000 * find_freq_from_fid(fid);
}
/* Return a frequency in MHz, given an input fid and did */
static u32 find_freq_from_fiddid(u32 fid, u32 did)
static u32 find_khz_freq_from_pstate(struct cpufreq_frequency_table *data, u32 pstate)
{
if (current_cpu_data.x86 == 0x10)
return 100 * (fid + 0x10) >> did;
else
return 100 * (fid + 0x8) >> did;
return data[pstate].frequency;
}
static u32 find_khz_freq_from_fiddid(u32 fid, u32 did)
{
return 1000 * find_freq_from_fiddid(fid, did);
}
static u32 find_fid_from_pstate(u32 pstate)
{
u32 hi, lo;
rdmsr(MSR_PSTATE_DEF_BASE + pstate, lo, hi);
return lo & HW_PSTATE_FID_MASK;
}
static u32 find_did_from_pstate(u32 pstate)
{
u32 hi, lo;
rdmsr(MSR_PSTATE_DEF_BASE + pstate, lo, hi);
return (lo & HW_PSTATE_DID_MASK) >> HW_PSTATE_DID_SHIFT;
}
/* Return the vco fid for an input fid
*
@ -142,9 +120,7 @@ static int query_current_values_with_pending_wait(struct powernow_k8_data *data)
if (cpu_family == CPU_HW_PSTATE) {
rdmsr(MSR_PSTATE_STATUS, lo, hi);
i = lo & HW_PSTATE_MASK;
rdmsr(MSR_PSTATE_DEF_BASE + i, lo, hi);
data->currfid = lo & HW_PSTATE_FID_MASK;
data->currdid = (lo & HW_PSTATE_DID_MASK) >> HW_PSTATE_DID_SHIFT;
data->currpstate = i;
return 0;
}
do {
@ -295,7 +271,7 @@ static int decrease_vid_code_by_step(struct powernow_k8_data *data, u32 reqvid,
static int transition_pstate(struct powernow_k8_data *data, u32 pstate)
{
wrmsr(MSR_PSTATE_CTRL, pstate, 0);
data->currfid = find_fid_from_pstate(pstate);
data->currpstate = pstate;
return 0;
}
@ -845,17 +821,20 @@ static int powernow_k8_cpu_init_acpi(struct powernow_k8_data *data)
static int fill_powernow_table_pstate(struct powernow_k8_data *data, struct cpufreq_frequency_table *powernow_table)
{
int i;
u32 hi = 0, lo = 0;
rdmsr(MSR_PSTATE_CUR_LIMIT, hi, lo);
data->max_hw_pstate = (hi & HW_PSTATE_MAX_MASK) >> HW_PSTATE_MAX_SHIFT;
for (i = 0; i < data->acpi_data.state_count; i++) {
u32 index;
u32 hi = 0, lo = 0;
u32 fid;
u32 did;
index = data->acpi_data.states[i].control & HW_PSTATE_MASK;
if (index > MAX_HW_PSTATE) {
if (index > data->max_hw_pstate) {
printk(KERN_ERR PFX "invalid pstate %d - bad value %d.\n", i, index);
printk(KERN_ERR PFX "Please report to BIOS manufacturer\n");
powernow_table[i].frequency = CPUFREQ_ENTRY_INVALID;
continue;
}
rdmsr(MSR_PSTATE_DEF_BASE + index, lo, hi);
if (!(hi & HW_PSTATE_VALID_MASK)) {
@ -864,22 +843,9 @@ static int fill_powernow_table_pstate(struct powernow_k8_data *data, struct cpuf
continue;
}
fid = lo & HW_PSTATE_FID_MASK;
did = (lo & HW_PSTATE_DID_MASK) >> HW_PSTATE_DID_SHIFT;
powernow_table[i].index = index;
dprintk(" %d : fid 0x%x, did 0x%x\n", index, fid, did);
powernow_table[i].index = index | (fid << HW_FID_INDEX_SHIFT) | (did << HW_DID_INDEX_SHIFT);
powernow_table[i].frequency = find_khz_freq_from_fiddid(fid, did);
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[i].frequency = data->acpi_data.states[i].core_frequency * 1000;
}
return 0;
}
@ -1020,22 +986,18 @@ static int transition_frequency_fidvid(struct powernow_k8_data *data, unsigned i
/* Take a frequency, and issue the hardware pstate transition command */
static int transition_frequency_pstate(struct powernow_k8_data *data, unsigned int index)
{
u32 fid = 0;
u32 did = 0;
u32 pstate = 0;
int res, i;
struct cpufreq_freqs freqs;
dprintk("cpu %d transition to index %u\n", smp_processor_id(), index);
/* get fid did for hardware pstate transition */
/* get MSR index for hardware pstate transition */
pstate = index & HW_PSTATE_MASK;
if (pstate > MAX_HW_PSTATE)
if (pstate > data->max_hw_pstate)
return 0;
fid = (index & HW_FID_INDEX_MASK) >> HW_FID_INDEX_SHIFT;
did = (index & HW_DID_INDEX_MASK) >> HW_DID_INDEX_SHIFT;
freqs.old = find_khz_freq_from_fiddid(data->currfid, data->currdid);
freqs.new = find_khz_freq_from_fiddid(fid, did);
freqs.old = find_khz_freq_from_pstate(data->powernow_table, data->currpstate);
freqs.new = find_khz_freq_from_pstate(data->powernow_table, pstate);
for_each_cpu_mask(i, *(data->available_cores)) {
freqs.cpu = i;
@ -1043,9 +1005,7 @@ static int transition_frequency_pstate(struct powernow_k8_data *data, unsigned i
}
res = transition_pstate(data, pstate);
data->currfid = find_fid_from_pstate(pstate);
data->currdid = find_did_from_pstate(pstate);
freqs.new = find_khz_freq_from_fiddid(data->currfid, data->currdid);
freqs.new = find_khz_freq_from_pstate(data->powernow_table, pstate);
for_each_cpu_mask(i, *(data->available_cores)) {
freqs.cpu = i;
@ -1090,10 +1050,7 @@ static int powernowk8_target(struct cpufreq_policy *pol, unsigned targfreq, unsi
if (query_current_values_with_pending_wait(data))
goto err_out;
if (cpu_family == CPU_HW_PSTATE)
dprintk("targ: curr fid 0x%x, did 0x%x\n",
data->currfid, data->currdid);
else {
if (cpu_family != CPU_HW_PSTATE) {
dprintk("targ: curr fid 0x%x, vid 0x%x\n",
data->currfid, data->currvid);
@ -1124,7 +1081,7 @@ static int powernowk8_target(struct cpufreq_policy *pol, unsigned targfreq, unsi
mutex_unlock(&fidvid_mutex);
if (cpu_family == CPU_HW_PSTATE)
pol->cur = find_khz_freq_from_fiddid(data->currfid, data->currdid);
pol->cur = find_khz_freq_from_pstate(data->powernow_table, newstate);
else
pol->cur = find_khz_freq_from_fid(data->currfid);
ret = 0;
@ -1223,7 +1180,7 @@ static int __cpuinit powernowk8_cpu_init(struct cpufreq_policy *pol)
+ (3 * (1 << data->irt) * 10)) * 1000;
if (cpu_family == CPU_HW_PSTATE)
pol->cur = find_khz_freq_from_fiddid(data->currfid, data->currdid);
pol->cur = find_khz_freq_from_pstate(data->powernow_table, data->currpstate);
else
pol->cur = find_khz_freq_from_fid(data->currfid);
dprintk("policy current frequency %d kHz\n", pol->cur);
@ -1240,8 +1197,7 @@ static int __cpuinit powernowk8_cpu_init(struct cpufreq_policy *pol)
cpufreq_frequency_table_get_attr(data->powernow_table, pol->cpu);
if (cpu_family == CPU_HW_PSTATE)
dprintk("cpu_init done, current fid 0x%x, did 0x%x\n",
data->currfid, data->currdid);
dprintk("cpu_init done, current pstate 0x%x\n", data->currpstate);
else
dprintk("cpu_init done, current fid 0x%x, vid 0x%x\n",
data->currfid, data->currvid);
@ -1297,7 +1253,7 @@ static unsigned int powernowk8_get (unsigned int cpu)
goto out;
if (cpu_family == CPU_HW_PSTATE)
khz = find_khz_freq_from_fiddid(data->currfid, data->currdid);
khz = find_khz_freq_from_pstate(data->powernow_table, data->currpstate);
else
khz = find_khz_freq_from_fid(data->currfid);

View File

@ -10,6 +10,7 @@ struct powernow_k8_data {
u32 numps; /* number of p-states */
u32 batps; /* number of p-states supported on battery */
u32 max_hw_pstate; /* maximum legal hardware pstate */
/* these values are constant when the PSB is used to determine
* vid/fid pairings, but are modified during the ->target() call
@ -21,8 +22,8 @@ struct powernow_k8_data {
u32 plllock; /* pll lock time, units 1 us */
u32 exttype; /* extended interface = 1 */
/* keep track of the current fid / vid or did */
u32 currvid, currfid, currdid;
/* keep track of the current fid / vid or pstate */
u32 currvid, currfid, currpstate;
/* 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.
@ -87,23 +88,14 @@ struct powernow_k8_data {
/* Hardware Pstate _PSS and MSR definitions */
#define USE_HW_PSTATE 0x00000080
#define HW_PSTATE_FID_MASK 0x0000003f
#define HW_PSTATE_DID_MASK 0x000001c0
#define HW_PSTATE_DID_SHIFT 6
#define HW_PSTATE_MASK 0x00000007
#define HW_PSTATE_VALID_MASK 0x80000000
#define HW_FID_INDEX_SHIFT 8
#define HW_FID_INDEX_MASK 0x0000ff00
#define HW_DID_INDEX_SHIFT 16
#define HW_DID_INDEX_MASK 0x00ff0000
#define HW_WATTS_MASK 0xff
#define HW_PWR_DVR_MASK 0x300
#define HW_PWR_DVR_SHIFT 8
#define HW_PWR_MAX_MULT 3
#define MAX_HW_PSTATE 8 /* hw pstate supports up to 8 */
#define HW_PSTATE_MAX_MASK 0x000000f0
#define HW_PSTATE_MAX_SHIFT 4
#define MSR_PSTATE_DEF_BASE 0xc0010064 /* base of Pstate MSRs */
#define MSR_PSTATE_STATUS 0xc0010063 /* Pstate Status MSR */
#define MSR_PSTATE_CTRL 0xc0010062 /* Pstate control MSR */
#define MSR_PSTATE_CUR_LIMIT 0xc0010061 /* pstate current limit MSR */
/* define the two driver architectures */
#define CPU_OPTERON 0