Commit c5829cd0 authored by Mark Langsdorf's avatar Mark Langsdorf Committed by Dave Jones
Browse files

[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: default avatarMark Langsdorf <mark.langsdorf@amd.com>
Signed-off-by: default avatarAndreas Herrmann <andreas.herrmann3@amd.com>
Signed-off-by: default avatarDave Jones <davej@redhat.com>
parent 63bd8c48
Loading
Loading
Loading
Loading
+23 −67
Original line number Original line Diff line number Diff line
@@ -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 2.00.00"
#define VERSION "version 2.20.00"
#include "powernow-k8.h"
#include "powernow-k8.h"


/* serialize freq changes  */
/* serialize freq changes  */
@@ -73,33 +73,11 @@ 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 frequency in MHz, given an input fid and did */
static u32 find_khz_freq_from_pstate(struct cpufreq_frequency_table *data, u32 pstate)
static u32 find_freq_from_fiddid(u32 fid, u32 did)
{
{
	if (current_cpu_data.x86 == 0x10)
	return data[pstate].frequency;
		return 100 * (fid + 0x10) >> did;
	else
		return 100 * (fid + 0x8) >> 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
 *
 *
@@ -142,9 +120,7 @@ static int query_current_values_with_pending_wait(struct powernow_k8_data *data)
	if (cpu_family == CPU_HW_PSTATE) {
	if (cpu_family == CPU_HW_PSTATE) {
		rdmsr(MSR_PSTATE_STATUS, lo, hi);
		rdmsr(MSR_PSTATE_STATUS, lo, hi);
		i = lo & HW_PSTATE_MASK;
		i = lo & HW_PSTATE_MASK;
		rdmsr(MSR_PSTATE_DEF_BASE + i, lo, hi);
		data->currpstate = i;
		data->currfid = lo & HW_PSTATE_FID_MASK;
		data->currdid = (lo & HW_PSTATE_DID_MASK) >> HW_PSTATE_DID_SHIFT;
		return 0;
		return 0;
	}
	}
	do {
	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)
static int transition_pstate(struct powernow_k8_data *data, u32 pstate)
{
{
	wrmsr(MSR_PSTATE_CTRL, pstate, 0);
	wrmsr(MSR_PSTATE_CTRL, pstate, 0);
	data->currfid = find_fid_from_pstate(pstate);
	data->currpstate = pstate;
	return 0;
	return 0;
}
}


@@ -845,17 +821,20 @@ err_out:
static int fill_powernow_table_pstate(struct powernow_k8_data *data, struct cpufreq_frequency_table *powernow_table)
static int fill_powernow_table_pstate(struct powernow_k8_data *data, struct cpufreq_frequency_table *powernow_table)
{
{
	int i;
	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++) {
	for (i = 0; i < data->acpi_data.state_count; i++) {
		u32 index;
		u32 index;
		u32 hi = 0, lo = 0;
		u32 hi = 0, lo = 0;
		u32 fid;
		u32 did;


		index = data->acpi_data.states[i].control & HW_PSTATE_MASK;
		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 "invalid pstate %d - bad value %d.\n", i, index);
			printk(KERN_ERR PFX "Please report to BIOS manufacturer\n");
			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);
		rdmsr(MSR_PSTATE_DEF_BASE + index, lo, hi);
		if (!(hi & HW_PSTATE_VALID_MASK)) {
		if (!(hi & HW_PSTATE_VALID_MASK)) {
@@ -864,22 +843,9 @@ static int fill_powernow_table_pstate(struct powernow_k8_data *data, struct cpuf
			continue;
			continue;
		}
		}


		fid = lo & HW_PSTATE_FID_MASK;
		powernow_table[i].index = index;
		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].frequency = data->acpi_data.states[i].core_frequency * 1000;

		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;
	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 */
/* Take a frequency, and issue the hardware pstate transition command */
static int transition_frequency_pstate(struct powernow_k8_data *data, unsigned int index)
static int transition_frequency_pstate(struct powernow_k8_data *data, unsigned int index)
{
{
	u32 fid = 0;
	u32 did = 0;
	u32 pstate = 0;
	u32 pstate = 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);


	/* get fid did for hardware pstate transition */
	/* get MSR index for hardware pstate transition */
	pstate = index & HW_PSTATE_MASK;
	pstate = index & HW_PSTATE_MASK;
	if (pstate > MAX_HW_PSTATE)
	if (pstate > data->max_hw_pstate)
		return 0;
		return 0;
	fid = (index & HW_FID_INDEX_MASK) >> HW_FID_INDEX_SHIFT;
	freqs.old = find_khz_freq_from_pstate(data->powernow_table, data->currpstate);
	did = (index & HW_DID_INDEX_MASK) >> HW_DID_INDEX_SHIFT;
	freqs.new = find_khz_freq_from_pstate(data->powernow_table, pstate);
	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)) {
	for_each_cpu_mask(i, *(data->available_cores)) {
		freqs.cpu = i;
		freqs.cpu = i;
@@ -1043,9 +1005,7 @@ static int transition_frequency_pstate(struct powernow_k8_data *data, unsigned i
	}
	}


	res = transition_pstate(data, pstate);
	res = transition_pstate(data, pstate);
	data->currfid = find_fid_from_pstate(pstate);
	freqs.new = find_khz_freq_from_pstate(data->powernow_table, 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)) {
	for_each_cpu_mask(i, *(data->available_cores)) {
		freqs.cpu = i;
		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))
	if (query_current_values_with_pending_wait(data))
		goto err_out;
		goto err_out;


	if (cpu_family == CPU_HW_PSTATE)
	if (cpu_family != CPU_HW_PSTATE) {
		dprintk("targ: curr fid 0x%x, did 0x%x\n",
			data->currfid, data->currdid);
	else {
		dprintk("targ: curr fid 0x%x, vid 0x%x\n",
		dprintk("targ: curr fid 0x%x, vid 0x%x\n",
		data->currfid, data->currvid);
		data->currfid, data->currvid);


@@ -1124,7 +1081,7 @@ static int powernowk8_target(struct cpufreq_policy *pol, unsigned targfreq, unsi
	mutex_unlock(&fidvid_mutex);
	mutex_unlock(&fidvid_mutex);


	if (cpu_family == CPU_HW_PSTATE)
	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
	else
		pol->cur = find_khz_freq_from_fid(data->currfid);
		pol->cur = find_khz_freq_from_fid(data->currfid);
	ret = 0;
	ret = 0;
@@ -1223,7 +1180,7 @@ static int __cpuinit powernowk8_cpu_init(struct cpufreq_policy *pol)
	    + (3 * (1 << data->irt) * 10)) * 1000;
	    + (3 * (1 << data->irt) * 10)) * 1000;


	if (cpu_family == CPU_HW_PSTATE)
	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
	else
		pol->cur = find_khz_freq_from_fid(data->currfid);
		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);
@@ -1240,8 +1197,7 @@ 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);


	if (cpu_family == CPU_HW_PSTATE)
	if (cpu_family == CPU_HW_PSTATE)
		dprintk("cpu_init done, current fid 0x%x, did 0x%x\n",
		dprintk("cpu_init done, current pstate 0x%x\n", data->currpstate);
			data->currfid, data->currdid);
	else
	else
		dprintk("cpu_init done, current fid 0x%x, vid 0x%x\n",
		dprintk("cpu_init done, current fid 0x%x, vid 0x%x\n",
			data->currfid, data->currvid);
			data->currfid, data->currvid);
@@ -1297,7 +1253,7 @@ static unsigned int powernowk8_get (unsigned int cpu)
		goto out;
		goto out;


	if (cpu_family == CPU_HW_PSTATE)
	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
	else
		khz = find_khz_freq_from_fid(data->currfid);
		khz = find_khz_freq_from_fid(data->currfid);


+6 −14
Original line number Original line Diff line number Diff line
@@ -10,6 +10,7 @@ struct powernow_k8_data {


	u32 numps;  /* number of p-states */
	u32 numps;  /* number of p-states */
	u32 batps;  /* number of p-states supported on battery */
	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
	/* these values are constant when the PSB is used to determine
	 * vid/fid pairings, but are modified during the ->target() call
	 * 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 plllock; /* pll lock time, units 1 us */
        u32 exttype; /* extended interface = 1 */
        u32 exttype; /* extended interface = 1 */


	/* keep track of the current fid / vid or did */
	/* keep track of the current fid / vid or pstate */
	u32 currvid, currfid, currdid;
	u32 currvid, currfid, currpstate;


	/* 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.
@@ -87,23 +88,14 @@ struct powernow_k8_data {


/* Hardware Pstate _PSS and MSR definitions */
/* Hardware Pstate _PSS and MSR definitions */
#define USE_HW_PSTATE		0x00000080
#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_MASK 		0x00000007
#define HW_PSTATE_VALID_MASK 	0x80000000
#define HW_PSTATE_VALID_MASK 	0x80000000
#define HW_FID_INDEX_SHIFT	8
#define HW_PSTATE_MAX_MASK	0x000000f0
#define HW_FID_INDEX_MASK	0x0000ff00
#define HW_PSTATE_MAX_SHIFT	4
#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_DEF_BASE 	0xc0010064 /* base of Pstate MSRs */
#define MSR_PSTATE_STATUS 	0xc0010063 /* Pstate Status MSR */
#define MSR_PSTATE_STATUS 	0xc0010063 /* Pstate Status MSR */
#define MSR_PSTATE_CTRL 	0xc0010062 /* Pstate control 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 the two driver architectures */
#define CPU_OPTERON 0
#define CPU_OPTERON 0