|
@@ -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 @@
|
|
*
|
|
*
|
|
* 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 @@
|
|
#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 @@
|
|
|
|
|
|
#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);
|
|
|
|
|
|
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
|
|
{
|
|
{
|
|
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
|
|
return 1;
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
|
|
- while ((rvosteps > 0) && ((data->rvo + data->currvid) > reqvid)) {
|
|
|
|
- if (data->currvid == 0) {
|
|
|
|
|
|
+ while ((rvosteps > 0) && ((data->rvo + data->currvid) > reqvid)) {
|
|
|
|
+ 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)
|
|
* 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)
|
|
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)
|
|
continue;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
|
|
- if (fid < HI_FID_TABLE_BOTTOM) {
|
|
|
|
- if (cntlofreq) {
|
|
|
|
- /* 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)) {
|
|
|
|
- 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;
|
|
|
|
- continue;
|
|
|
|
- } else
|
|
|
|
- cntlofreq = i;
|
|
|
|
|
|
+ /* verify only 1 entry from the lo frequency table */
|
|
|
|
+ if (fid < HI_FID_TABLE_BOTTOM) {
|
|
|
|
+ if (cntlofreq) {
|
|
|
|
+ /* 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)) {
|
|
|
|
+ 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;
|
|
|
|
+ continue;
|
|
|
|
+ } 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
|
|
{
|
|
{
|
|
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
|
|
}
|
|
}
|
|
|
|
|
|
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
|
|
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
|
|
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
|
|
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
|
|
|
|
- "error - out of sync, fid 0x%x 0x%x, vid 0x%x 0x%x\n",
|
|
|
|
- checkfid, data->currfid, checkvid, data->currvid);
|
|
|
|
|
|
+ printk(KERN_INFO PFX
|
|
|
|
+ "error - out of sync, fix 0x%x 0x%x, vid 0x%x 0x%x\n",
|
|
|
|
+ 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)
|
|
*/
|
|
*/
|
|
|
|
|
|
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)
|
|
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)
|
|
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)
|
|
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);
|
|
|
|
+
|