[CPUFREQ] Prepare powernow-k8 for future CPUs.
Forthcoming AMD products will use a different algorithm for transitioning pstates than the current generation Opteron products do. The attached patch allows the powernow-k8 driver to work with those products. Signed-off-by: Mark Langsdorf <mark.langsdorf@amd.com> Signed-off-by: Dave Jones <davej@redhat.com>
This commit is contained in:
parent
911cb74bb9
commit
1f729e0660
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* (c) 2003, 2004, 2005 Advanced Micro Devices, Inc.
|
* (c) 2003-2006 Advanced Micro Devices, Inc.
|
||||||
* Your use of this code is subject to the terms and conditions of the
|
* Your use of this code is subject to the terms and conditions of the
|
||||||
* GNU general public license version 2. See "COPYING" or
|
* GNU general public license version 2. See "COPYING" or
|
||||||
* http://www.gnu.org/licenses/gpl.html
|
* http://www.gnu.org/licenses/gpl.html
|
||||||
@ -14,7 +14,7 @@
|
|||||||
* Based upon datasheets & sample CPUs kindly provided by AMD.
|
* Based upon datasheets & sample CPUs kindly provided by AMD.
|
||||||
*
|
*
|
||||||
* Valuable input gratefully received from Dave Jones, Pavel Machek,
|
* Valuable input gratefully received from Dave Jones, Pavel Machek,
|
||||||
* Dominik Brodowski, and others.
|
* Dominik Brodowski, Jacob Shin, and others.
|
||||||
* Originally developed by Paul Devriendt.
|
* Originally developed by Paul Devriendt.
|
||||||
* Processor information obtained from Chapter 9 (Power and Thermal Management)
|
* 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
|
* of the "BIOS and Kernel Developer's Guide for the AMD Athlon 64 and AMD
|
||||||
@ -46,7 +46,7 @@
|
|||||||
|
|
||||||
#define PFX "powernow-k8: "
|
#define PFX "powernow-k8: "
|
||||||
#define BFX PFX "BIOS error: "
|
#define BFX PFX "BIOS error: "
|
||||||
#define VERSION "version 1.60.2"
|
#define VERSION "version 2.00.00"
|
||||||
#include "powernow-k8.h"
|
#include "powernow-k8.h"
|
||||||
|
|
||||||
/* serialize freq changes */
|
/* serialize freq changes */
|
||||||
@ -54,6 +54,8 @@ static DEFINE_MUTEX(fidvid_mutex);
|
|||||||
|
|
||||||
static struct powernow_k8_data *powernow_data[NR_CPUS];
|
static struct powernow_k8_data *powernow_data[NR_CPUS];
|
||||||
|
|
||||||
|
static int cpu_family = CPU_OPTERON;
|
||||||
|
|
||||||
#ifndef CONFIG_SMP
|
#ifndef CONFIG_SMP
|
||||||
static cpumask_t cpu_core_map[1];
|
static cpumask_t cpu_core_map[1];
|
||||||
#endif
|
#endif
|
||||||
@ -64,16 +66,36 @@ static u32 find_freq_from_fid(u32 fid)
|
|||||||
return 800 + (fid * 100);
|
return 800 + (fid * 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Return a frequency in KHz, given an input fid */
|
/* Return a frequency in KHz, given an input fid */
|
||||||
static u32 find_khz_freq_from_fid(u32 fid)
|
static u32 find_khz_freq_from_fid(u32 fid)
|
||||||
{
|
{
|
||||||
return 1000 * find_freq_from_fid(fid);
|
return 1000 * find_freq_from_fid(fid);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Return a voltage in miliVolts, given an input vid */
|
/* Return a frequency in MHz, given an input fid and did */
|
||||||
static u32 find_millivolts_from_vid(struct powernow_k8_data *data, u32 vid)
|
static u32 find_freq_from_fiddid(u32 fid, u32 did)
|
||||||
{
|
{
|
||||||
return 1550-vid*25;
|
return 100 * (fid + 0x10) >> did;
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
/* Return the vco fid for an input fid
|
||||||
@ -98,6 +120,9 @@ static int pending_bit_stuck(void)
|
|||||||
{
|
{
|
||||||
u32 lo, hi;
|
u32 lo, hi;
|
||||||
|
|
||||||
|
if (cpu_family)
|
||||||
|
return 0;
|
||||||
|
|
||||||
rdmsr(MSR_FIDVID_STATUS, lo, hi);
|
rdmsr(MSR_FIDVID_STATUS, lo, hi);
|
||||||
return lo & MSR_S_LO_CHANGE_PENDING ? 1 : 0;
|
return lo & MSR_S_LO_CHANGE_PENDING ? 1 : 0;
|
||||||
}
|
}
|
||||||
@ -111,6 +136,14 @@ static int query_current_values_with_pending_wait(struct powernow_k8_data *data)
|
|||||||
u32 lo, hi;
|
u32 lo, hi;
|
||||||
u32 i = 0;
|
u32 i = 0;
|
||||||
|
|
||||||
|
if (cpu_family) {
|
||||||
|
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;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
do {
|
do {
|
||||||
if (i++ > 10000) {
|
if (i++ > 10000) {
|
||||||
dprintk("detected change pending stuck\n");
|
dprintk("detected change pending stuck\n");
|
||||||
@ -175,7 +208,7 @@ static int write_new_fid(struct powernow_k8_data *data, u32 fid)
|
|||||||
do {
|
do {
|
||||||
wrmsr(MSR_FIDVID_CTL, lo, data->plllock * PLL_LOCK_CONVERSION);
|
wrmsr(MSR_FIDVID_CTL, lo, data->plllock * PLL_LOCK_CONVERSION);
|
||||||
if (i++ > 100) {
|
if (i++ > 100) {
|
||||||
printk(KERN_ERR PFX "internal error - pending bit very stuck - no further pstate changes possible\n");
|
printk(KERN_ERR PFX "Hardware error - pending bit very stuck - no further pstate changes possible\n");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
} while (query_current_values_with_pending_wait(data));
|
} while (query_current_values_with_pending_wait(data));
|
||||||
@ -255,7 +288,15 @@ static int decrease_vid_code_by_step(struct powernow_k8_data *data, u32 reqvid,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Change the fid and vid, by the 3 phases. */
|
/* Change hardware pstate by single MSR write */
|
||||||
|
static int transition_pstate(struct powernow_k8_data *data, u32 pstate)
|
||||||
|
{
|
||||||
|
wrmsr(MSR_PSTATE_CTRL, pstate, 0);
|
||||||
|
data->currfid = find_fid_from_pstate(pstate);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Change Opteron/Athlon64 fid and vid, by the 3 phases. */
|
||||||
static int transition_fid_vid(struct powernow_k8_data *data, u32 reqfid, u32 reqvid)
|
static int transition_fid_vid(struct powernow_k8_data *data, u32 reqfid, u32 reqvid)
|
||||||
{
|
{
|
||||||
if (core_voltage_pre_transition(data, reqvid))
|
if (core_voltage_pre_transition(data, reqvid))
|
||||||
@ -474,26 +515,35 @@ static int check_supported_cpu(unsigned int cpu)
|
|||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
eax = cpuid_eax(CPUID_PROCESSOR_SIGNATURE);
|
eax = cpuid_eax(CPUID_PROCESSOR_SIGNATURE);
|
||||||
if ((eax & CPUID_XFAM) != CPUID_XFAM_K8)
|
if (((eax & CPUID_XFAM) != CPUID_XFAM_K8) &&
|
||||||
|
((eax & CPUID_XFAM) < CPUID_XFAM_10H))
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
if (((eax & CPUID_USE_XFAM_XMOD) != CPUID_USE_XFAM_XMOD) ||
|
if ((eax & CPUID_XFAM) == CPUID_XFAM_K8) {
|
||||||
((eax & CPUID_XMOD) > CPUID_XMOD_REV_G)) {
|
if (((eax & CPUID_USE_XFAM_XMOD) != CPUID_USE_XFAM_XMOD) ||
|
||||||
printk(KERN_INFO PFX "Processor cpuid %x not supported\n", eax);
|
((eax & CPUID_XMOD) > CPUID_XMOD_REV_G)) {
|
||||||
goto out;
|
printk(KERN_INFO PFX "Processor cpuid %x not supported\n", eax);
|
||||||
}
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
eax = cpuid_eax(CPUID_GET_MAX_CAPABILITIES);
|
eax = cpuid_eax(CPUID_GET_MAX_CAPABILITIES);
|
||||||
if (eax < CPUID_FREQ_VOLT_CAPABILITIES) {
|
if (eax < CPUID_FREQ_VOLT_CAPABILITIES) {
|
||||||
printk(KERN_INFO PFX
|
printk(KERN_INFO PFX
|
||||||
"No frequency change capabilities detected\n");
|
"No frequency change capabilities detected\n");
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
cpuid(CPUID_FREQ_VOLT_CAPABILITIES, &eax, &ebx, &ecx, &edx);
|
cpuid(CPUID_FREQ_VOLT_CAPABILITIES, &eax, &ebx, &ecx, &edx);
|
||||||
if ((edx & P_STATE_TRANSITION_CAPABLE) != P_STATE_TRANSITION_CAPABLE) {
|
if ((edx & P_STATE_TRANSITION_CAPABLE) != P_STATE_TRANSITION_CAPABLE) {
|
||||||
printk(KERN_INFO PFX "Power state transitions not supported\n");
|
printk(KERN_INFO PFX "Power state transitions not supported\n");
|
||||||
goto out;
|
goto out;
|
||||||
|
}
|
||||||
|
} else { /* must be a HW Pstate capable processor */
|
||||||
|
cpuid(CPUID_FREQ_VOLT_CAPABILITIES, &eax, &ebx, &ecx, &edx);
|
||||||
|
if ((edx & USE_HW_PSTATE) == USE_HW_PSTATE)
|
||||||
|
cpu_family = CPU_HW_PSTATE;
|
||||||
|
else
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = 1;
|
rc = 1;
|
||||||
@ -547,12 +597,18 @@ static void print_basics(struct powernow_k8_data *data)
|
|||||||
{
|
{
|
||||||
int j;
|
int j;
|
||||||
for (j = 0; j < data->numps; j++) {
|
for (j = 0; j < data->numps; j++) {
|
||||||
if (data->powernow_table[j].frequency != CPUFREQ_ENTRY_INVALID)
|
if (data->powernow_table[j].frequency != CPUFREQ_ENTRY_INVALID) {
|
||||||
printk(KERN_INFO PFX " %d : fid 0x%x (%d MHz), vid 0x%x (%d mV)\n", j,
|
if (cpu_family) {
|
||||||
|
printk(KERN_INFO PFX " %d : fid 0x%x gid 0x%x (%d MHz)\n", j, (data->powernow_table[j].index & 0xff00) >> 8,
|
||||||
|
(data->powernow_table[j].index & 0xff0000) >> 16,
|
||||||
|
data->powernow_table[j].frequency/1000);
|
||||||
|
} else {
|
||||||
|
printk(KERN_INFO PFX " %d : fid 0x%x (%d MHz), vid 0x%x\n", j,
|
||||||
data->powernow_table[j].index & 0xff,
|
data->powernow_table[j].index & 0xff,
|
||||||
data->powernow_table[j].frequency/1000,
|
data->powernow_table[j].frequency/1000,
|
||||||
data->powernow_table[j].index >> 8,
|
data->powernow_table[j].index >> 8);
|
||||||
find_millivolts_from_vid(data, data->powernow_table[j].index >> 8));
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (data->batps)
|
if (data->batps)
|
||||||
printk(KERN_INFO PFX "Only %d pstates on battery\n", data->batps);
|
printk(KERN_INFO PFX "Only %d pstates on battery\n", data->batps);
|
||||||
@ -702,7 +758,7 @@ static int find_psb_table(struct powernow_k8_data *data)
|
|||||||
#ifdef CONFIG_X86_POWERNOW_K8_ACPI
|
#ifdef CONFIG_X86_POWERNOW_K8_ACPI
|
||||||
static void powernow_k8_acpi_pst_values(struct powernow_k8_data *data, unsigned int index)
|
static void powernow_k8_acpi_pst_values(struct powernow_k8_data *data, unsigned int index)
|
||||||
{
|
{
|
||||||
if (!data->acpi_data.state_count)
|
if (!data->acpi_data.state_count || cpu_family)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
data->irt = (data->acpi_data.states[index].control >> IRT_SHIFT) & IRT_MASK;
|
data->irt = (data->acpi_data.states[index].control >> IRT_SHIFT) & IRT_MASK;
|
||||||
@ -715,9 +771,8 @@ static void powernow_k8_acpi_pst_values(struct powernow_k8_data *data, unsigned
|
|||||||
|
|
||||||
static int powernow_k8_cpu_init_acpi(struct powernow_k8_data *data)
|
static int powernow_k8_cpu_init_acpi(struct powernow_k8_data *data)
|
||||||
{
|
{
|
||||||
int i;
|
|
||||||
int cntlofreq = 0;
|
|
||||||
struct cpufreq_frequency_table *powernow_table;
|
struct cpufreq_frequency_table *powernow_table;
|
||||||
|
int ret_val;
|
||||||
|
|
||||||
if (acpi_processor_register_performance(&data->acpi_data, data->cpu)) {
|
if (acpi_processor_register_performance(&data->acpi_data, data->cpu)) {
|
||||||
dprintk("register performance failed: bad ACPI data\n");
|
dprintk("register performance failed: bad ACPI data\n");
|
||||||
@ -746,6 +801,85 @@ static int powernow_k8_cpu_init_acpi(struct powernow_k8_data *data)
|
|||||||
goto err_out;
|
goto err_out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cpu_family)
|
||||||
|
ret_val = fill_powernow_table_pstate(data, powernow_table);
|
||||||
|
else
|
||||||
|
ret_val = fill_powernow_table_fidvid(data, powernow_table);
|
||||||
|
if (ret_val)
|
||||||
|
goto err_out_mem;
|
||||||
|
|
||||||
|
powernow_table[data->acpi_data.state_count].frequency = CPUFREQ_TABLE_END;
|
||||||
|
powernow_table[data->acpi_data.state_count].index = 0;
|
||||||
|
data->powernow_table = powernow_table;
|
||||||
|
|
||||||
|
/* fill in data */
|
||||||
|
data->numps = data->acpi_data.state_count;
|
||||||
|
print_basics(data);
|
||||||
|
powernow_k8_acpi_pst_values(data, 0);
|
||||||
|
|
||||||
|
/* notify BIOS that we exist */
|
||||||
|
acpi_processor_notify_smm(THIS_MODULE);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err_out_mem:
|
||||||
|
kfree(powernow_table);
|
||||||
|
|
||||||
|
err_out:
|
||||||
|
acpi_processor_unregister_performance(&data->acpi_data, data->cpu);
|
||||||
|
|
||||||
|
/* data->acpi_data.state_count informs us at ->exit() whether ACPI was used */
|
||||||
|
data->acpi_data.state_count = 0;
|
||||||
|
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fill_powernow_table_pstate(struct powernow_k8_data *data, struct cpufreq_frequency_table *powernow_table)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
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) {
|
||||||
|
printk(KERN_ERR PFX "invalid pstate %d - bad value %d.\n", i, index);
|
||||||
|
printk(KERN_ERR PFX "Please report to BIOS manufacturer\n");
|
||||||
|
}
|
||||||
|
rdmsr(MSR_PSTATE_DEF_BASE + index, lo, hi);
|
||||||
|
if (!(hi & HW_PSTATE_VALID_MASK)) {
|
||||||
|
dprintk("invalid pstate %d, ignoring\n", index);
|
||||||
|
powernow_table[i].frequency = CPUFREQ_ENTRY_INVALID;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
fid = lo & HW_PSTATE_FID_MASK;
|
||||||
|
did = (lo & HW_PSTATE_DID_MASK) >> HW_PSTATE_DID_SHIFT;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fill_powernow_table_fidvid(struct powernow_k8_data *data, struct cpufreq_frequency_table *powernow_table)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int cntlofreq = 0;
|
||||||
for (i = 0; i < data->acpi_data.state_count; i++) {
|
for (i = 0; i < data->acpi_data.state_count; i++) {
|
||||||
u32 fid;
|
u32 fid;
|
||||||
u32 vid;
|
u32 vid;
|
||||||
@ -786,7 +920,7 @@ static int powernow_k8_cpu_init_acpi(struct powernow_k8_data *data)
|
|||||||
if ((powernow_table[i].frequency != powernow_table[cntlofreq].frequency) ||
|
if ((powernow_table[i].frequency != powernow_table[cntlofreq].frequency) ||
|
||||||
(powernow_table[i].index != powernow_table[cntlofreq].index)) {
|
(powernow_table[i].index != powernow_table[cntlofreq].index)) {
|
||||||
printk(KERN_ERR PFX "Too many lo freq table entries\n");
|
printk(KERN_ERR PFX "Too many lo freq table entries\n");
|
||||||
goto err_out_mem;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
dprintk("double low frequency table entry, ignoring it.\n");
|
dprintk("double low frequency table entry, ignoring it.\n");
|
||||||
@ -804,31 +938,7 @@ static int powernow_k8_cpu_init_acpi(struct powernow_k8_data *data)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
powernow_table[data->acpi_data.state_count].frequency = CPUFREQ_TABLE_END;
|
|
||||||
powernow_table[data->acpi_data.state_count].index = 0;
|
|
||||||
data->powernow_table = powernow_table;
|
|
||||||
|
|
||||||
/* fill in data */
|
|
||||||
data->numps = data->acpi_data.state_count;
|
|
||||||
print_basics(data);
|
|
||||||
powernow_k8_acpi_pst_values(data, 0);
|
|
||||||
|
|
||||||
/* notify BIOS that we exist */
|
|
||||||
acpi_processor_notify_smm(THIS_MODULE);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_out_mem:
|
|
||||||
kfree(powernow_table);
|
|
||||||
|
|
||||||
err_out:
|
|
||||||
acpi_processor_unregister_performance(&data->acpi_data, data->cpu);
|
|
||||||
|
|
||||||
/* data->acpi_data.state_count informs us at ->exit() whether ACPI was used */
|
|
||||||
data->acpi_data.state_count = 0;
|
|
||||||
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void powernow_k8_cpu_exit_acpi(struct powernow_k8_data *data)
|
static void powernow_k8_cpu_exit_acpi(struct powernow_k8_data *data)
|
||||||
@ -844,20 +954,20 @@ static void powernow_k8_acpi_pst_values(struct powernow_k8_data *data, unsigned
|
|||||||
#endif /* CONFIG_X86_POWERNOW_K8_ACPI */
|
#endif /* CONFIG_X86_POWERNOW_K8_ACPI */
|
||||||
|
|
||||||
/* Take a frequency, and issue the fid/vid transition command */
|
/* Take a frequency, and issue the fid/vid transition command */
|
||||||
static int transition_frequency(struct powernow_k8_data *data, unsigned int index)
|
static int transition_frequency_fidvid(struct powernow_k8_data *data, unsigned int index)
|
||||||
{
|
{
|
||||||
u32 fid;
|
u32 fid = 0;
|
||||||
u32 vid;
|
u32 vid = 0;
|
||||||
int res, i;
|
int res, i;
|
||||||
struct cpufreq_freqs freqs;
|
struct cpufreq_freqs freqs;
|
||||||
|
|
||||||
dprintk("cpu %d transition to index %u\n", smp_processor_id(), index);
|
dprintk("cpu %d transition to index %u\n", smp_processor_id(), index);
|
||||||
|
|
||||||
|
/* fid/vid correctness check for k8 */
|
||||||
/* fid are the lower 8 bits of the index we stored into
|
/* fid are the lower 8 bits of the index we stored into
|
||||||
* the cpufreq frequency table in find_psb_table, vid are
|
* the cpufreq frequency table in find_psb_table, vid
|
||||||
* the upper 8 bits.
|
* are the upper 8 bits.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
fid = data->powernow_table[index].index & 0xFF;
|
fid = data->powernow_table[index].index & 0xFF;
|
||||||
vid = (data->powernow_table[index].index & 0xFF00) >> 8;
|
vid = (data->powernow_table[index].index & 0xFF00) >> 8;
|
||||||
|
|
||||||
@ -881,19 +991,55 @@ static int transition_frequency(struct powernow_k8_data *data, unsigned int inde
|
|||||||
|
|
||||||
dprintk("cpu %d, changing to fid 0x%x, vid 0x%x\n",
|
dprintk("cpu %d, changing to fid 0x%x, vid 0x%x\n",
|
||||||
smp_processor_id(), fid, vid);
|
smp_processor_id(), fid, vid);
|
||||||
|
|
||||||
freqs.cpu = data->cpu;
|
|
||||||
freqs.old = find_khz_freq_from_fid(data->currfid);
|
freqs.old = find_khz_freq_from_fid(data->currfid);
|
||||||
freqs.new = find_khz_freq_from_fid(fid);
|
freqs.new = find_khz_freq_from_fid(fid);
|
||||||
for_each_cpu_mask(i, cpu_core_map[data->cpu]) {
|
|
||||||
|
for_each_cpu_mask(i, *(data->available_cores)) {
|
||||||
freqs.cpu = i;
|
freqs.cpu = i;
|
||||||
cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
|
cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
res = transition_fid_vid(data, fid, vid);
|
res = transition_fid_vid(data, fid, vid);
|
||||||
|
|
||||||
freqs.new = find_khz_freq_from_fid(data->currfid);
|
freqs.new = find_khz_freq_from_fid(data->currfid);
|
||||||
for_each_cpu_mask(i, cpu_core_map[data->cpu]) {
|
|
||||||
|
for_each_cpu_mask(i, *(data->available_cores)) {
|
||||||
|
freqs.cpu = i;
|
||||||
|
cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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 */
|
||||||
|
pstate = index & HW_PSTATE_MASK;
|
||||||
|
if (pstate > 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);
|
||||||
|
|
||||||
|
for_each_cpu_mask(i, *(data->available_cores)) {
|
||||||
|
freqs.cpu = i;
|
||||||
|
cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
for_each_cpu_mask(i, *(data->available_cores)) {
|
||||||
freqs.cpu = i;
|
freqs.cpu = i;
|
||||||
cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
|
cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
|
||||||
}
|
}
|
||||||
@ -936,13 +1082,18 @@ static int powernowk8_target(struct cpufreq_policy *pol, unsigned targfreq, unsi
|
|||||||
if (query_current_values_with_pending_wait(data))
|
if (query_current_values_with_pending_wait(data))
|
||||||
goto err_out;
|
goto err_out;
|
||||||
|
|
||||||
dprintk("targ: curr fid 0x%x, vid 0x%x\n",
|
if (cpu_family)
|
||||||
|
dprintk("targ: curr fid 0x%x, did 0x%x\n",
|
||||||
|
data->currfid, data->currvid);
|
||||||
|
else {
|
||||||
|
dprintk("targ: curr fid 0x%x, vid 0x%x\n",
|
||||||
data->currfid, data->currvid);
|
data->currfid, data->currvid);
|
||||||
|
|
||||||
if ((checkvid != data->currvid) || (checkfid != data->currfid)) {
|
if ((checkvid != data->currvid) || (checkfid != data->currfid)) {
|
||||||
printk(KERN_INFO PFX
|
printk(KERN_INFO PFX
|
||||||
"error - out of sync, fix 0x%x 0x%x, vid 0x%x 0x%x\n",
|
"error - out of sync, fix 0x%x 0x%x, vid 0x%x 0x%x\n",
|
||||||
checkfid, data->currfid, checkvid, data->currvid);
|
checkfid, data->currfid, checkvid, data->currvid);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cpufreq_frequency_table_target(pol, data->powernow_table, targfreq, relation, &newstate))
|
if (cpufreq_frequency_table_target(pol, data->powernow_table, targfreq, relation, &newstate))
|
||||||
@ -952,7 +1103,11 @@ static int powernowk8_target(struct cpufreq_policy *pol, unsigned targfreq, unsi
|
|||||||
|
|
||||||
powernow_k8_acpi_pst_values(data, newstate);
|
powernow_k8_acpi_pst_values(data, newstate);
|
||||||
|
|
||||||
if (transition_frequency(data, newstate)) {
|
if (cpu_family)
|
||||||
|
ret = transition_frequency_pstate(data, newstate);
|
||||||
|
else
|
||||||
|
ret = transition_frequency_fidvid(data, newstate);
|
||||||
|
if (ret) {
|
||||||
printk(KERN_ERR PFX "transition frequency failed\n");
|
printk(KERN_ERR PFX "transition frequency failed\n");
|
||||||
ret = 1;
|
ret = 1;
|
||||||
mutex_unlock(&fidvid_mutex);
|
mutex_unlock(&fidvid_mutex);
|
||||||
@ -960,7 +1115,10 @@ static int powernowk8_target(struct cpufreq_policy *pol, unsigned targfreq, unsi
|
|||||||
}
|
}
|
||||||
mutex_unlock(&fidvid_mutex);
|
mutex_unlock(&fidvid_mutex);
|
||||||
|
|
||||||
pol->cur = find_khz_freq_from_fid(data->currfid);
|
if (cpu_family)
|
||||||
|
pol->cur = find_khz_freq_from_fiddid(data->currfid, data->currdid);
|
||||||
|
else
|
||||||
|
pol->cur = find_khz_freq_from_fid(data->currfid);
|
||||||
ret = 0;
|
ret = 0;
|
||||||
|
|
||||||
err_out:
|
err_out:
|
||||||
@ -1005,14 +1163,13 @@ static int __cpuinit powernowk8_cpu_init(struct cpufreq_policy *pol)
|
|||||||
* Use the PSB BIOS structure. This is only availabe on
|
* Use the PSB BIOS structure. This is only availabe on
|
||||||
* an UP version, and is deprecated by AMD.
|
* an UP version, and is deprecated by AMD.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if ((num_online_cpus() != 1) || (num_possible_cpus() != 1)) {
|
if ((num_online_cpus() != 1) || (num_possible_cpus() != 1)) {
|
||||||
printk(KERN_ERR PFX "MP systems not supported by PSB BIOS structure\n");
|
printk(KERN_ERR PFX "MP systems not supported by PSB BIOS structure\n");
|
||||||
kfree(data);
|
kfree(data);
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
if (pol->cpu != 0) {
|
if (pol->cpu != 0) {
|
||||||
printk(KERN_ERR PFX "init not cpu 0\n");
|
printk(KERN_ERR PFX "No _PSS objects for CPU other than CPU0\n");
|
||||||
kfree(data);
|
kfree(data);
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
@ -1040,20 +1197,28 @@ static int __cpuinit powernowk8_cpu_init(struct cpufreq_policy *pol)
|
|||||||
if (query_current_values_with_pending_wait(data))
|
if (query_current_values_with_pending_wait(data))
|
||||||
goto err_out;
|
goto err_out;
|
||||||
|
|
||||||
fidvid_msr_init();
|
if (!cpu_family)
|
||||||
|
fidvid_msr_init();
|
||||||
|
|
||||||
/* run on any CPU again */
|
/* run on any CPU again */
|
||||||
set_cpus_allowed(current, oldmask);
|
set_cpus_allowed(current, oldmask);
|
||||||
|
|
||||||
pol->governor = CPUFREQ_DEFAULT_GOVERNOR;
|
pol->governor = CPUFREQ_DEFAULT_GOVERNOR;
|
||||||
pol->cpus = cpu_core_map[pol->cpu];
|
if (cpu_family)
|
||||||
|
pol->cpus = cpumask_of_cpu(pol->cpu);
|
||||||
|
else
|
||||||
|
pol->cpus = cpu_core_map[pol->cpu];
|
||||||
|
data->available_cores = &(pol->cpus);
|
||||||
|
|
||||||
/* Take a crude guess here.
|
/* Take a crude guess here.
|
||||||
* That guess was in microseconds, so multiply with 1000 */
|
* That guess was in microseconds, so multiply with 1000 */
|
||||||
pol->cpuinfo.transition_latency = (((data->rvo + 8) * data->vstable * VST_UNITS_20US)
|
pol->cpuinfo.transition_latency = (((data->rvo + 8) * data->vstable * VST_UNITS_20US)
|
||||||
+ (3 * (1 << data->irt) * 10)) * 1000;
|
+ (3 * (1 << data->irt) * 10)) * 1000;
|
||||||
|
|
||||||
pol->cur = find_khz_freq_from_fid(data->currfid);
|
if (cpu_family)
|
||||||
|
pol->cur = find_khz_freq_from_fiddid(data->currfid, data->currdid);
|
||||||
|
else
|
||||||
|
pol->cur = find_khz_freq_from_fid(data->currfid);
|
||||||
dprintk("policy current frequency %d kHz\n", pol->cur);
|
dprintk("policy current frequency %d kHz\n", pol->cur);
|
||||||
|
|
||||||
/* min/max the cpu is capable of */
|
/* min/max the cpu is capable of */
|
||||||
@ -1067,8 +1232,12 @@ static int __cpuinit powernowk8_cpu_init(struct cpufreq_policy *pol)
|
|||||||
|
|
||||||
cpufreq_frequency_table_get_attr(data->powernow_table, pol->cpu);
|
cpufreq_frequency_table_get_attr(data->powernow_table, pol->cpu);
|
||||||
|
|
||||||
printk("cpu_init done, current fid 0x%x, vid 0x%x\n",
|
if (cpu_family)
|
||||||
data->currfid, data->currvid);
|
dprintk("cpu_init done, current fid 0x%x, did 0x%x\n",
|
||||||
|
data->currfid, data->currdid);
|
||||||
|
else
|
||||||
|
dprintk("cpu_init done, current fid 0x%x, vid 0x%x\n",
|
||||||
|
data->currfid, data->currvid);
|
||||||
|
|
||||||
powernow_data[pol->cpu] = data;
|
powernow_data[pol->cpu] = data;
|
||||||
|
|
||||||
@ -1154,8 +1323,9 @@ static int __cpuinit powernowk8_init(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (supported_cpus == num_online_cpus()) {
|
if (supported_cpus == num_online_cpus()) {
|
||||||
printk(KERN_INFO PFX "Found %d AMD Athlon 64 / Opteron "
|
printk(KERN_INFO PFX "Found %d %s "
|
||||||
"processors (" VERSION ")\n", supported_cpus);
|
"processors (" VERSION ")\n", supported_cpus,
|
||||||
|
boot_cpu_data.x86_model_id);
|
||||||
return cpufreq_register_driver(&cpufreq_amd64_driver);
|
return cpufreq_register_driver(&cpufreq_amd64_driver);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* (c) 2003, 2004, 2005 Advanced Micro Devices, Inc.
|
* (c) 2003-2006 Advanced Micro Devices, Inc.
|
||||||
* Your use of this code is subject to the terms and conditions of the
|
* Your use of this code is subject to the terms and conditions of the
|
||||||
* GNU general public license version 2. See "COPYING" or
|
* GNU general public license version 2. See "COPYING" or
|
||||||
* http://www.gnu.org/licenses/gpl.html
|
* http://www.gnu.org/licenses/gpl.html
|
||||||
@ -21,8 +21,8 @@ struct powernow_k8_data {
|
|||||||
u32 plllock; /* pll lock time, units 1 us */
|
u32 plllock; /* pll lock time, units 1 us */
|
||||||
u32 exttype; /* extended interface = 1 */
|
u32 exttype; /* extended interface = 1 */
|
||||||
|
|
||||||
/* keep track of the current fid / vid */
|
/* keep track of the current fid / vid or did */
|
||||||
u32 currvid, currfid;
|
u32 currvid, currfid, currdid;
|
||||||
|
|
||||||
/* the powernow_table includes all frequency and vid/fid pairings:
|
/* 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.
|
* fid are the lower 8 bits of the index, vid are the upper 8 bits.
|
||||||
@ -34,6 +34,10 @@ struct powernow_k8_data {
|
|||||||
* used to determine valid frequency/vid/fid states */
|
* used to determine valid frequency/vid/fid states */
|
||||||
struct acpi_processor_performance acpi_data;
|
struct acpi_processor_performance acpi_data;
|
||||||
#endif
|
#endif
|
||||||
|
/* we need to keep track of associated cores, but let cpufreq
|
||||||
|
* handle hotplug events - so just point at cpufreq pol->cpus
|
||||||
|
* structure */
|
||||||
|
cpumask_t *available_cores;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -43,6 +47,7 @@ struct powernow_k8_data {
|
|||||||
#define CPUID_XFAM_K8 0
|
#define CPUID_XFAM_K8 0
|
||||||
#define CPUID_XMOD 0x000f0000 /* extended model */
|
#define CPUID_XMOD 0x000f0000 /* extended model */
|
||||||
#define CPUID_XMOD_REV_G 0x00060000
|
#define CPUID_XMOD_REV_G 0x00060000
|
||||||
|
#define CPUID_XFAM_10H 0x00100000 /* family 0x10 */
|
||||||
#define CPUID_USE_XFAM_XMOD 0x00000f00
|
#define CPUID_USE_XFAM_XMOD 0x00000f00
|
||||||
#define CPUID_GET_MAX_CAPABILITIES 0x80000000
|
#define CPUID_GET_MAX_CAPABILITIES 0x80000000
|
||||||
#define CPUID_FREQ_VOLT_CAPABILITIES 0x80000007
|
#define CPUID_FREQ_VOLT_CAPABILITIES 0x80000007
|
||||||
@ -79,6 +84,32 @@ struct powernow_k8_data {
|
|||||||
#define MSR_S_HI_CURRENT_VID 0x0000003f
|
#define MSR_S_HI_CURRENT_VID 0x0000003f
|
||||||
#define MSR_C_HI_STP_GNT_BENIGN 0x00000001
|
#define MSR_C_HI_STP_GNT_BENIGN 0x00000001
|
||||||
|
|
||||||
|
|
||||||
|
/* 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 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 the two driver architectures */
|
||||||
|
#define CPU_OPTERON 0
|
||||||
|
#define CPU_HW_PSTATE 1
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* There are restrictions frequencies have to follow:
|
* There are restrictions frequencies have to follow:
|
||||||
* - only 1 entry in the low fid table ( <=1.4GHz )
|
* - only 1 entry in the low fid table ( <=1.4GHz )
|
||||||
@ -182,6 +213,9 @@ static int core_frequency_transition(struct powernow_k8_data *data, u32 reqfid);
|
|||||||
|
|
||||||
static void powernow_k8_acpi_pst_values(struct powernow_k8_data *data, unsigned int index);
|
static void powernow_k8_acpi_pst_values(struct powernow_k8_data *data, unsigned int index);
|
||||||
|
|
||||||
|
static int fill_powernow_table_pstate(struct powernow_k8_data *data, struct cpufreq_frequency_table *powernow_table);
|
||||||
|
static int fill_powernow_table_fidvid(struct powernow_k8_data *data, struct cpufreq_frequency_table *powernow_table);
|
||||||
|
|
||||||
#ifdef CONFIG_SMP
|
#ifdef CONFIG_SMP
|
||||||
static inline void define_siblings(int cpu, cpumask_t cpu_sharedcore_mask[])
|
static inline void define_siblings(int cpu, cpumask_t cpu_sharedcore_mask[])
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user