Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

[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
Loading
Loading
Loading
Loading
+76 −37
Original line number Diff line number Diff line
@@ -4,7 +4,7 @@
 *  GNU general public license version 2. See "COPYING" or
 *  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.
 *  (C) 2003 Dave Jones <davej@codemonkey.org.uk> on behalf of SuSE Labs
@@ -15,6 +15,7 @@
 *
 *  Valuable input gratefully received from Dave Jones, Pavel Machek,
 *  Dominik Brodowski, and others.
 *  Originally developed by Paul Devriendt.
 *  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
@@ -30,6 +31,7 @@
#include <linux/cpufreq.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/cpumask.h>

#include <asm/msr.h>
#include <asm/io.h>
@@ -42,7 +44,7 @@

#define PFX "powernow-k8: "
#define BFX PFX "BIOS error: "
#define VERSION "version 1.00.09e"
#define VERSION "version 1.40.2"
#include "powernow-k8.h"

/* serialize freq changes  */
@@ -50,6 +52,10 @@ static DECLARE_MUTEX(fidvid_sem);

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 */
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 savefid = data->currfid;
	u32 maxvid, lo;

	dprintk("ph1 (cpu%d): start, currfid 0x%x, currvid 0x%x, reqvid 0x%x, rvo 0x%x\n",
		smp_processor_id(),
		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) {
		dprintk("ph1: curr 0x%x, req vid 0x%x\n",
			data->currvid, reqvid);
@@ -287,7 +300,7 @@ static int core_voltage_pre_transition(struct powernow_k8_data *data, u32 reqvid
	}

	while ((rvosteps > 0) && ((data->rvo + data->currvid) > reqvid)) {
		if (data->currvid == 0) {
		if (data->currvid == maxvid) {
			rvosteps = 0;
		} else {
			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
	 * 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;
}

@@ -695,7 +708,7 @@ static int powernow_k8_cpu_init_acpi(struct powernow_k8_data *data)
	struct cpufreq_frequency_table *powernow_table;

	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;
	}

@@ -746,6 +759,7 @@ static int powernow_k8_cpu_init_acpi(struct powernow_k8_data *data)
			continue;
		}

		/* 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
@@ -816,7 +830,7 @@ static int transition_frequency(struct powernow_k8_data *data, unsigned int inde
{
	u32 fid;
	u32 vid;
	int res;
	int res, i;
	struct cpufreq_freqs freqs;

	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)) {
		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);
		return 1;
	}
@@ -850,18 +865,20 @@ static int transition_frequency(struct powernow_k8_data *data, unsigned int inde
		smp_processor_id(), fid, vid);

	freqs.cpu = data->cpu;

	freqs.old = find_khz_freq_from_fid(data->currfid);
	freqs.new = find_khz_freq_from_fid(fid);
	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);
	up(&fidvid_sem);

	freqs.new = find_khz_freq_from_fid(data->currfid);
	for_each_cpu_mask(i, cpu_core_map[data->cpu]) {
		freqs.cpu = i;
		cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);

        }
	return res;
}

@@ -874,6 +891,7 @@ static int powernowk8_target(struct cpufreq_policy *pol, unsigned targfreq, unsi
	u32 checkvid = data->currvid;
	unsigned int newstate;
	int ret = -EIO;
	int i;

	/* only run on specific CPU from here on */
	oldmask = current->cpus_allowed;
@@ -902,22 +920,41 @@ static int powernowk8_target(struct cpufreq_policy *pol, unsigned targfreq, unsi
		data->currfid, data->currvid);

	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",
		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))
		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);

	if (transition_frequency(data, newstate)) {
		printk(KERN_ERR PFX "transition frequency failed\n");
		ret = 1;
		up(&fidvid_sem);
		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);
	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)) {
			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);
			return -ENODEV;
		}
@@ -1003,6 +1040,7 @@ static int __init powernowk8_cpu_init(struct cpufreq_policy *pol)
	schedule();

	pol->governor = CPUFREQ_DEFAULT_GOVERNOR;
	pol->cpus = cpu_core_map[pol->cpu];

	/* Take a crude guess here. 
	 * That guess was in microseconds, so multiply with 1000 */
@@ -1127,9 +1165,10 @@ static void __exit powernowk8_exit(void)
	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_LICENSE("GPL");

late_initcall(powernowk8_init);
module_exit(powernowk8_exit);
+15 −0
Original line number Diff line number Diff line
@@ -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 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