Commit 7b349554 authored by Dave Jones's avatar Dave Jones Committed by Dave Jones

[CPUFREQ] Correcting SGTC. Timer is based upon FSB

From: Bruno Ducrot <ducrot@poupinou.org>

I think this patch is needed especially if the FSB is around 166MHz or
200MHz, or else I believe we get instabilities on some K7's motherboard
powernow capable (it's called Cool'n Quiet IIRC).
                                                                                         
                                                                                         
* Deduce fsb from cpu_khz and the max multiplier.  It will be given as kHz now,
  so that frequency associated to a multiplier will be computate more
  accurately.  Also, we need it for SGTC (see below).
* Fix how cpuid is computed in powernow_decode_bios().
* Be more restrictive for PST.  It may be possible (on desktop shipped with
  low power Athlon models) that FSB can be changed by dip switchs on
  motherboard for example.
* Fix computation for SGTC.  It use the bus timer (and then the bus
  frequency given by fsb).
parent 2aebc5b1
...@@ -85,6 +85,14 @@ static unsigned int fsb; ...@@ -85,6 +85,14 @@ static unsigned int fsb;
static unsigned int latency; static unsigned int latency;
static char have_a0; static char have_a0;
static int check_fsb(unsigned int fsbspeed)
{
int delta;
unsigned int f = fsb / 1000;
delta = (fsbspeed > f) ? fsbspeed - f : f - fsbspeed;
return (delta < 5);
}
static int check_powernow(void) static int check_powernow(void)
{ {
...@@ -140,7 +148,8 @@ static int check_powernow(void) ...@@ -140,7 +148,8 @@ static int check_powernow(void)
static int get_ranges (unsigned char *pst) static int get_ranges (unsigned char *pst)
{ {
unsigned int j, speed; unsigned int j;
unsigned int speed;
u8 fid, vid; u8 fid, vid;
powernow_table = kmalloc((sizeof(struct cpufreq_frequency_table) * (number_scales + 1)), GFP_KERNEL); powernow_table = kmalloc((sizeof(struct cpufreq_frequency_table) * (number_scales + 1)), GFP_KERNEL);
...@@ -151,12 +160,12 @@ static int get_ranges (unsigned char *pst) ...@@ -151,12 +160,12 @@ static int get_ranges (unsigned char *pst)
for (j=0 ; j < number_scales; j++) { for (j=0 ; j < number_scales; j++) {
fid = *pst++; fid = *pst++;
powernow_table[j].frequency = fsb * fid_codes[fid] * 100; powernow_table[j].frequency = (fsb * fid_codes[fid]) / 10;
powernow_table[j].index = fid; /* lower 8 bits */ powernow_table[j].index = fid; /* lower 8 bits */
speed = fsb * (fid_codes[fid]/10); speed = powernow_table[j].frequency;
if ((fid_codes[fid] % 10)==5) { if ((fid_codes[fid] % 10)==5) {
speed += fsb/2;
#if defined(CONFIG_ACPI_PROCESSOR) || defined(CONFIG_ACPI_PROCESSOR_MODULE) #if defined(CONFIG_ACPI_PROCESSOR) || defined(CONFIG_ACPI_PROCESSOR_MODULE)
if (have_a0 == 1) if (have_a0 == 1)
powernow_table[j].frequency = CPUFREQ_ENTRY_INVALID; powernow_table[j].frequency = CPUFREQ_ENTRY_INVALID;
...@@ -164,7 +173,7 @@ static int get_ranges (unsigned char *pst) ...@@ -164,7 +173,7 @@ static int get_ranges (unsigned char *pst)
} }
dprintk (KERN_INFO PFX " FID: 0x%x (%d.%dx [%dMHz])\t", fid, dprintk (KERN_INFO PFX " FID: 0x%x (%d.%dx [%dMHz])\t", fid,
fid_codes[fid] / 10, fid_codes[fid] % 10, speed); fid_codes[fid] / 10, fid_codes[fid] % 10, speed/1000);
if (speed < minimum_speed) if (speed < minimum_speed)
minimum_speed = speed; minimum_speed = speed;
...@@ -234,7 +243,8 @@ static void change_speed (unsigned int index) ...@@ -234,7 +243,8 @@ static void change_speed (unsigned int index)
rdmsrl (MSR_K7_FID_VID_STATUS, fidvidstatus.val); rdmsrl (MSR_K7_FID_VID_STATUS, fidvidstatus.val);
cfid = fidvidstatus.bits.CFID; cfid = fidvidstatus.bits.CFID;
freqs.old = fsb * fid_codes[cfid] * 100; freqs.old = fsb * fid_codes[cfid] / 10;
freqs.new = powernow_table[index].frequency; freqs.new = powernow_table[index].frequency;
cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
...@@ -266,15 +276,12 @@ static int powernow_decode_bios (int maxfid, int startvid) ...@@ -266,15 +276,12 @@ static int powernow_decode_bios (int maxfid, int startvid)
{ {
struct psb_s *psb; struct psb_s *psb;
struct pst_s *pst; struct pst_s *pst;
struct cpuinfo_x86 *c = cpu_data;
unsigned int i, j; unsigned int i, j;
unsigned char *p; unsigned char *p;
unsigned int etuple; unsigned int etuple;
unsigned int ret; unsigned int ret;
etuple = cpuid_eax(0x80000001); etuple = cpuid_eax(0x80000001);
etuple &= 0xf00;
etuple |= (c->x86_model<<4)|(c->x86_mask);
for (i=0xC0000; i < 0xffff0 ; i+=16) { for (i=0xC0000; i < 0xffff0 ; i+=16) {
...@@ -305,7 +312,6 @@ static int powernow_decode_bios (int maxfid, int startvid) ...@@ -305,7 +312,6 @@ static int powernow_decode_bios (int maxfid, int startvid)
} }
dprintk (KERN_INFO PFX "Settling Time: %d microseconds.\n", psb->settlingtime); dprintk (KERN_INFO PFX "Settling Time: %d microseconds.\n", psb->settlingtime);
dprintk (KERN_INFO PFX "Has %d PST tables. (Only dumping ones relevant to this CPU).\n", psb->numpst); dprintk (KERN_INFO PFX "Has %d PST tables. (Only dumping ones relevant to this CPU).\n", psb->numpst);
latency *= 100; /* SGTC needs to be in units of 10ns */
p += sizeof (struct psb_s); p += sizeof (struct psb_s);
...@@ -315,7 +321,8 @@ static int powernow_decode_bios (int maxfid, int startvid) ...@@ -315,7 +321,8 @@ static int powernow_decode_bios (int maxfid, int startvid)
pst = (struct pst_s *) p; pst = (struct pst_s *) p;
number_scales = pst->numpstates; number_scales = pst->numpstates;
if ((etuple == pst->cpuid) && (maxfid==pst->maxfid) && (startvid==pst->startvid)) if ((etuple == pst->cpuid) && check_fsb(pst->fsbspeed) &&
(maxfid==pst->maxfid) && (startvid==pst->startvid))
{ {
dprintk (KERN_INFO PFX "PST:%d (@%p)\n", i, pst); dprintk (KERN_INFO PFX "PST:%d (@%p)\n", i, pst);
dprintk (KERN_INFO PFX " cpuid: 0x%x\t", pst->cpuid); dprintk (KERN_INFO PFX " cpuid: 0x%x\t", pst->cpuid);
...@@ -323,7 +330,6 @@ static int powernow_decode_bios (int maxfid, int startvid) ...@@ -323,7 +330,6 @@ static int powernow_decode_bios (int maxfid, int startvid)
dprintk ("maxFID: 0x%x\t", pst->maxfid); dprintk ("maxFID: 0x%x\t", pst->maxfid);
dprintk ("startvid: 0x%x\n", pst->startvid); dprintk ("startvid: 0x%x\n", pst->startvid);
fsb = pst->fsbspeed;
ret = get_ranges ((char *) pst + sizeof (struct pst_s)); ret = get_ranges ((char *) pst + sizeof (struct pst_s));
return ret; return ret;
...@@ -365,6 +371,33 @@ static int powernow_verify (struct cpufreq_policy *policy) ...@@ -365,6 +371,33 @@ static int powernow_verify (struct cpufreq_policy *policy)
return cpufreq_frequency_table_verify(policy, powernow_table); return cpufreq_frequency_table_verify(policy, powernow_table);
} }
/*
* We use the fact that the bus frequency is somehow
* a multiple of 100000/3 khz, then we compute sgtc according
* to this multiple.
* That way, we match more how AMD thinks all of that work.
* We will then get the same kind of behaviour already tested under
* the "well-known" other OS.
*/
static int __init fixup_sgtc(void)
{
unsigned int sgtc;
unsigned int m;
m = fsb / 3333;
if ((m % 10) >= 5)
m += 5;
m /= 10;
sgtc = 100 * m * latency;
sgtc = sgtc / 3;
if (sgtc > 0xfffff) {
printk(KERN_WARNING PFX "SGTC too large %d\n", sgtc);
sgtc = 0xfffff;
}
return sgtc;
}
static int __init powernow_cpu_init (struct cpufreq_policy *policy) static int __init powernow_cpu_init (struct cpufreq_policy *policy)
{ {
...@@ -376,18 +409,28 @@ static int __init powernow_cpu_init (struct cpufreq_policy *policy) ...@@ -376,18 +409,28 @@ static int __init powernow_cpu_init (struct cpufreq_policy *policy)
rdmsrl (MSR_K7_FID_VID_STATUS, fidvidstatus.val); rdmsrl (MSR_K7_FID_VID_STATUS, fidvidstatus.val);
/* A K7 with powernow technology is set to max frequency by BIOS */
fsb = (10 * cpu_khz) / fid_codes[fidvidstatus.bits.MFID];
if (!fsb) {
printk(KERN_WARNING PFX "can not determine bus frequency\n");
return -EINVAL;
}
dprintk(KERN_INFO PFX "FSB: %3d.%03d MHz\n", fsb/1000, fsb%1000);
result = powernow_decode_bios(fidvidstatus.bits.MFID, fidvidstatus.bits.SVID); result = powernow_decode_bios(fidvidstatus.bits.MFID, fidvidstatus.bits.SVID);
if (result) if (result)
return result; return result;
printk (KERN_INFO PFX "Minimum speed %d MHz. Maximum speed %d MHz.\n", printk (KERN_INFO PFX "Minimum speed %d MHz. Maximum speed %d MHz.\n",
minimum_speed, maximum_speed); minimum_speed/1000, maximum_speed/1000);
policy->governor = CPUFREQ_DEFAULT_GOVERNOR; policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
/* latency is in 10 ns (look for SGTC above) for each VID policy->cpuinfo.transition_latency = latency * 2;
* and FID transition, so multiply that value with 20 */
policy->cpuinfo.transition_latency = latency * 20; /* SGTC use the bus clock as timer */
latency = fixup_sgtc();
printk(KERN_INFO PFX "SGTC: %d\n", latency);
policy->cur = maximum_speed; policy->cur = maximum_speed;
......
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