Commit 065b807c authored by Dave Jones's avatar Dave Jones

[CPUFREQ] dual-core powernow-k8

With the release of the dual-core AMD Opterons last week,
it's high time that cpufreq supported them.  The attached
patch applies cleanly to 2.6.12-rc3 and updates powernow-k8
to support the latest Athlon 64 and Opteron processors.

Update the driver to version 1.40.0 and provide support
for dual-core processors.
Signed-off-by: default avatarMark Langsdorf <mark.langsdorf@amd.com>
Signed-off-by: default avatarDave Jones <davej@redhat.com>
parent 7f335d4e
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
* 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
* *
* Support : paul.devriendt@amd.com * Support : mark.langsdorf@amd.com
* *
* Based on the powernow-k7.c module written by Dave Jones. * Based on the powernow-k7.c module written by Dave Jones.
* (C) 2003 Dave Jones <davej@codemonkey.org.uk> on behalf of SuSE Labs * (C) 2003 Dave Jones <davej@codemonkey.org.uk> on behalf of SuSE Labs
...@@ -15,12 +15,13 @@ ...@@ -15,12 +15,13 @@
* *
* Valuable input gratefully received from Dave Jones, Pavel Machek, * Valuable input gratefully received from Dave Jones, Pavel Machek,
* Dominik Brodowski, and others. * Dominik Brodowski, and others.
* 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
* Opteron Processors" available for download from www.amd.com * Opteron Processors" available for download from www.amd.com
* *
* Tables for specific CPUs can be infrerred from * Tables for specific CPUs can be infrerred from
* http://www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/30430.pdf * http://www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/30430.pdf
*/ */
#include <linux/kernel.h> #include <linux/kernel.h>
...@@ -30,6 +31,7 @@ ...@@ -30,6 +31,7 @@
#include <linux/cpufreq.h> #include <linux/cpufreq.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/cpumask.h>
#include <asm/msr.h> #include <asm/msr.h>
#include <asm/io.h> #include <asm/io.h>
...@@ -42,7 +44,7 @@ ...@@ -42,7 +44,7 @@
#define PFX "powernow-k8: " #define PFX "powernow-k8: "
#define BFX PFX "BIOS error: " #define BFX PFX "BIOS error: "
#define VERSION "version 1.00.09e" #define VERSION "version 1.40.2"
#include "powernow-k8.h" #include "powernow-k8.h"
/* serialize freq changes */ /* serialize freq changes */
...@@ -50,6 +52,10 @@ static DECLARE_MUTEX(fidvid_sem); ...@@ -50,6 +52,10 @@ static DECLARE_MUTEX(fidvid_sem);
static struct powernow_k8_data *powernow_data[NR_CPUS]; static struct powernow_k8_data *powernow_data[NR_CPUS];
#ifndef CONFIG_SMP
static cpumask_t cpu_core_map[1];
#endif
/* Return a frequency in MHz, given an input fid */ /* Return a frequency in MHz, given an input fid */
static u32 find_freq_from_fid(u32 fid) static u32 find_freq_from_fid(u32 fid)
{ {
...@@ -274,11 +280,18 @@ static int core_voltage_pre_transition(struct powernow_k8_data *data, u32 reqvid ...@@ -274,11 +280,18 @@ static int core_voltage_pre_transition(struct powernow_k8_data *data, u32 reqvid
{ {
u32 rvosteps = data->rvo; u32 rvosteps = data->rvo;
u32 savefid = data->currfid; u32 savefid = data->currfid;
u32 maxvid, lo;
dprintk("ph1 (cpu%d): start, currfid 0x%x, currvid 0x%x, reqvid 0x%x, rvo 0x%x\n", dprintk("ph1 (cpu%d): start, currfid 0x%x, currvid 0x%x, reqvid 0x%x, rvo 0x%x\n",
smp_processor_id(), smp_processor_id(),
data->currfid, data->currvid, reqvid, data->rvo); data->currfid, data->currvid, reqvid, data->rvo);
rdmsr(MSR_FIDVID_STATUS, lo, maxvid);
maxvid = 0x1f & (maxvid >> 16);
dprintk("ph1 maxvid=0x%x\n", maxvid);
if (reqvid < maxvid) /* lower numbers are higher voltages */
reqvid = maxvid;
while (data->currvid > reqvid) { while (data->currvid > reqvid) {
dprintk("ph1: curr 0x%x, req vid 0x%x\n", dprintk("ph1: curr 0x%x, req vid 0x%x\n",
data->currvid, reqvid); data->currvid, reqvid);
...@@ -286,8 +299,8 @@ static int core_voltage_pre_transition(struct powernow_k8_data *data, u32 reqvid ...@@ -286,8 +299,8 @@ static int core_voltage_pre_transition(struct powernow_k8_data *data, u32 reqvid
return 1; return 1;
} }
while ((rvosteps > 0) && ((data->rvo + data->currvid) > reqvid)) { while ((rvosteps > 0) && ((data->rvo + data->currvid) > reqvid)) {
if (data->currvid == 0) { if (data->currvid == maxvid) {
rvosteps = 0; rvosteps = 0;
} else { } else {
dprintk("ph1: changing vid for rvo, req 0x%x\n", dprintk("ph1: changing vid for rvo, req 0x%x\n",
...@@ -671,7 +684,7 @@ static int find_psb_table(struct powernow_k8_data *data) ...@@ -671,7 +684,7 @@ static int find_psb_table(struct powernow_k8_data *data)
* BIOS and Kernel Developer's Guide, which is available on * BIOS and Kernel Developer's Guide, which is available on
* www.amd.com * www.amd.com
*/ */
printk(KERN_ERR PFX "BIOS error - no PSB\n"); printk(KERN_INFO PFX "BIOS error - no PSB or ACPI _PSS objects\n");
return -ENODEV; return -ENODEV;
} }
...@@ -695,7 +708,7 @@ static int powernow_k8_cpu_init_acpi(struct powernow_k8_data *data) ...@@ -695,7 +708,7 @@ static int powernow_k8_cpu_init_acpi(struct powernow_k8_data *data)
struct cpufreq_frequency_table *powernow_table; struct cpufreq_frequency_table *powernow_table;
if (acpi_processor_register_performance(&data->acpi_data, data->cpu)) { if (acpi_processor_register_performance(&data->acpi_data, data->cpu)) {
dprintk("register performance failed\n"); dprintk("register performance failed: bad ACPI data\n");
return -EIO; return -EIO;
} }
...@@ -746,22 +759,23 @@ static int powernow_k8_cpu_init_acpi(struct powernow_k8_data *data) ...@@ -746,22 +759,23 @@ static int powernow_k8_cpu_init_acpi(struct powernow_k8_data *data)
continue; continue;
} }
if (fid < HI_FID_TABLE_BOTTOM) { /* verify only 1 entry from the lo frequency table */
if (cntlofreq) { if (fid < HI_FID_TABLE_BOTTOM) {
/* if both entries are the same, ignore this if (cntlofreq) {
* one... /* if both entries are the same, ignore this
*/ * one...
if ((powernow_table[i].frequency != powernow_table[cntlofreq].frequency) || */
(powernow_table[i].index != powernow_table[cntlofreq].index)) { if ((powernow_table[i].frequency != powernow_table[cntlofreq].frequency) ||
printk(KERN_ERR PFX "Too many lo freq table entries\n"); (powernow_table[i].index != powernow_table[cntlofreq].index)) {
goto err_out_mem; printk(KERN_ERR PFX "Too many lo freq table entries\n");
} goto err_out_mem;
}
dprintk("double low frequency table entry, ignoring it.\n");
powernow_table[i].frequency = CPUFREQ_ENTRY_INVALID; dprintk("double low frequency table entry, ignoring it.\n");
continue; powernow_table[i].frequency = CPUFREQ_ENTRY_INVALID;
} else continue;
cntlofreq = i; } else
cntlofreq = i;
} }
if (powernow_table[i].frequency != (data->acpi_data.states[i].core_frequency * 1000)) { if (powernow_table[i].frequency != (data->acpi_data.states[i].core_frequency * 1000)) {
...@@ -816,7 +830,7 @@ static int transition_frequency(struct powernow_k8_data *data, unsigned int inde ...@@ -816,7 +830,7 @@ static int transition_frequency(struct powernow_k8_data *data, unsigned int inde
{ {
u32 fid; u32 fid;
u32 vid; u32 vid;
int res; 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);
...@@ -841,7 +855,8 @@ static int transition_frequency(struct powernow_k8_data *data, unsigned int inde ...@@ -841,7 +855,8 @@ static int transition_frequency(struct powernow_k8_data *data, unsigned int inde
} }
if ((fid < HI_FID_TABLE_BOTTOM) && (data->currfid < HI_FID_TABLE_BOTTOM)) { if ((fid < HI_FID_TABLE_BOTTOM) && (data->currfid < HI_FID_TABLE_BOTTOM)) {
printk("ignoring illegal change in lo freq table-%x to 0x%x\n", printk(KERN_ERR PFX
"ignoring illegal change in lo freq table-%x to 0x%x\n",
data->currfid, fid); data->currfid, fid);
return 1; return 1;
} }
...@@ -850,18 +865,20 @@ static int transition_frequency(struct powernow_k8_data *data, unsigned int inde ...@@ -850,18 +865,20 @@ static int transition_frequency(struct powernow_k8_data *data, unsigned int inde
smp_processor_id(), fid, vid); smp_processor_id(), fid, vid);
freqs.cpu = data->cpu; 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);
cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); for_each_cpu_mask(i, cpu_core_map[data->cpu]) {
freqs.cpu = i;
cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
}
down(&fidvid_sem);
res = transition_fid_vid(data, fid, vid); res = transition_fid_vid(data, fid, vid);
up(&fidvid_sem);
freqs.new = find_khz_freq_from_fid(data->currfid); freqs.new = find_khz_freq_from_fid(data->currfid);
cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); for_each_cpu_mask(i, cpu_core_map[data->cpu]) {
freqs.cpu = i;
cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
}
return res; return res;
} }
...@@ -874,6 +891,7 @@ static int powernowk8_target(struct cpufreq_policy *pol, unsigned targfreq, unsi ...@@ -874,6 +891,7 @@ static int powernowk8_target(struct cpufreq_policy *pol, unsigned targfreq, unsi
u32 checkvid = data->currvid; u32 checkvid = data->currvid;
unsigned int newstate; unsigned int newstate;
int ret = -EIO; int ret = -EIO;
int i;
/* only run on specific CPU from here on */ /* only run on specific CPU from here on */
oldmask = current->cpus_allowed; oldmask = current->cpus_allowed;
...@@ -902,22 +920,41 @@ static int powernowk8_target(struct cpufreq_policy *pol, unsigned targfreq, unsi ...@@ -902,22 +920,41 @@ static int powernowk8_target(struct cpufreq_policy *pol, unsigned targfreq, unsi
data->currfid, data->currvid); data->currfid, data->currvid);
if ((checkvid != data->currvid) || (checkfid != data->currfid)) { if ((checkvid != data->currvid) || (checkfid != data->currfid)) {
printk(KERN_ERR PFX printk(KERN_INFO PFX
"error - out of sync, fid 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))
goto err_out; goto err_out;
down(&fidvid_sem);
for_each_cpu_mask(i, cpu_core_map[pol->cpu]) {
/* make sure the sibling is initialized */
if (!powernow_data[i]) {
ret = 0;
up(&fidvid_sem);
goto err_out;
}
}
powernow_k8_acpi_pst_values(data, newstate); powernow_k8_acpi_pst_values(data, newstate);
if (transition_frequency(data, newstate)) { if (transition_frequency(data, newstate)) {
printk(KERN_ERR PFX "transition frequency failed\n"); printk(KERN_ERR PFX "transition frequency failed\n");
ret = 1; ret = 1;
up(&fidvid_sem);
goto err_out; goto err_out;
} }
/* Update all the fid/vids of our siblings */
for_each_cpu_mask(i, cpu_core_map[pol->cpu]) {
powernow_data[i]->currvid = data->currvid;
powernow_data[i]->currfid = data->currfid;
}
up(&fidvid_sem);
pol->cur = find_khz_freq_from_fid(data->currfid); pol->cur = find_khz_freq_from_fid(data->currfid);
ret = 0; ret = 0;
...@@ -962,7 +999,7 @@ static int __init powernowk8_cpu_init(struct cpufreq_policy *pol) ...@@ -962,7 +999,7 @@ static int __init powernowk8_cpu_init(struct cpufreq_policy *pol)
*/ */
if ((num_online_cpus() != 1) || (num_possible_cpus() != 1)) { if ((num_online_cpus() != 1) || (num_possible_cpus() != 1)) {
printk(KERN_INFO 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;
} }
...@@ -1003,6 +1040,7 @@ static int __init powernowk8_cpu_init(struct cpufreq_policy *pol) ...@@ -1003,6 +1040,7 @@ static int __init powernowk8_cpu_init(struct cpufreq_policy *pol)
schedule(); schedule();
pol->governor = CPUFREQ_DEFAULT_GOVERNOR; pol->governor = CPUFREQ_DEFAULT_GOVERNOR;
pol->cpus = cpu_core_map[pol->cpu];
/* 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 */
...@@ -1069,7 +1107,7 @@ static unsigned int powernowk8_get (unsigned int cpu) ...@@ -1069,7 +1107,7 @@ static unsigned int powernowk8_get (unsigned int cpu)
return 0; return 0;
} }
preempt_disable(); preempt_disable();
if (query_current_values_with_pending_wait(data)) if (query_current_values_with_pending_wait(data))
goto out; goto out;
...@@ -1127,9 +1165,10 @@ static void __exit powernowk8_exit(void) ...@@ -1127,9 +1165,10 @@ static void __exit powernowk8_exit(void)
cpufreq_unregister_driver(&cpufreq_amd64_driver); cpufreq_unregister_driver(&cpufreq_amd64_driver);
} }
MODULE_AUTHOR("Paul Devriendt <paul.devriendt@amd.com>"); MODULE_AUTHOR("Paul Devriendt <paul.devriendt@amd.com> and Mark Langsdorf <mark.langsdorf@amd.com.");
MODULE_DESCRIPTION("AMD Athlon 64 and Opteron processor frequency driver."); MODULE_DESCRIPTION("AMD Athlon 64 and Opteron processor frequency driver.");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
late_initcall(powernowk8_init); late_initcall(powernowk8_init);
module_exit(powernowk8_exit); module_exit(powernowk8_exit);
...@@ -174,3 +174,18 @@ static int core_voltage_post_transition(struct powernow_k8_data *data, u32 reqvi ...@@ -174,3 +174,18 @@ static int core_voltage_post_transition(struct powernow_k8_data *data, u32 reqvi
static int core_frequency_transition(struct powernow_k8_data *data, u32 reqfid); 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);
#ifndef for_each_cpu_mask
#define for_each_cpu_mask(i,mask) for (i=0;i<1;i++)
#endif
#ifdef CONFIG_SMP
static inline void define_siblings(int cpu, cpumask_t cpu_sharedcore_mask[])
{
}
#else
static inline void define_siblings(int cpu, cpumask_t cpu_sharedcore_mask[])
{
cpu_set(0, cpu_sharedcore_mask[0]);
}
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment