patch-2_6_7-vs1_9_1_12
[linux-2.6.git] / arch / i386 / kernel / cpu / cpufreq / longhaul.c
index d8ddf39..57c0377 100644 (file)
@@ -18,7 +18,8 @@
  */
 
 #include <linux/kernel.h>
-#include <linux/module.h> 
+#include <linux/module.h>
+#include <linux/moduleparam.h>
 #include <linux/init.h>
 #include <linux/cpufreq.h>
 #include <linux/slab.h>
 
 #include "longhaul.h"
 
-#define DEBUG
-
-#ifdef DEBUG
-#define dprintk(msg...) printk(msg)
-#else
-#define dprintk(msg...) do { } while(0)
-#endif
-
 #define PFX "longhaul: "
 
 static unsigned int numscales=16, numvscales;
+static unsigned int fsb;
 static int minvid, maxvid;
+static unsigned int minmult, maxmult;
 static int can_scale_voltage;
 static int vrmrev;
 
-
 /* Module parameters */
 static int dont_scale_voltage;
-static unsigned int fsb;
+static int debug;
+
+static void dprintk(const char *fmt, ...)
+{
+       char s[256];
+       va_list args;
+
+       if (debug == 0)
+               return;
+
+       va_start(args, fmt);
+       vsprintf(s, fmt, args);
+       printk(s);
+       va_end(args);
+}
+
 
 #define __hlt()     __asm__ __volatile__("hlt": : :"memory")
 
@@ -61,7 +70,7 @@ static int longhaul_version;
 static struct cpufreq_frequency_table *longhaul_table;
 
 
-static unsigned int calc_speed (int mult, int fsb)
+static unsigned int calc_speed(int mult, int fsb)
 {
        int khz;
        khz = (mult/10)*fsb;
@@ -72,13 +81,13 @@ static unsigned int calc_speed (int mult, int fsb)
 }
 
 
-static int longhaul_get_cpu_mult (void)
+static int longhaul_get_cpu_mult(void)
 {
        unsigned long invalue=0,lo, hi;
 
        rdmsr (MSR_IA32_EBL_CR_POWERON, lo, hi);
        invalue = (lo & (1<<22|1<<23|1<<24|1<<25)) >>22;
-       if (longhaul_version==2) {
+       if (longhaul_version==2 || longhaul_version==3) {
                if (lo & (1<<27))
                        invalue+=16;
        }
@@ -93,7 +102,7 @@ static int longhaul_get_cpu_mult (void)
  * Sets a new clock ratio, and -if applicable- a new Front Side Bus
  */
 
-static void longhaul_setstate (unsigned int clock_ratio_index)
+static void longhaul_setstate(unsigned int clock_ratio_index)
 {
        int speed, mult;
        struct cpufreq_freqs freqs;
@@ -114,8 +123,7 @@ static void longhaul_setstate (unsigned int clock_ratio_index)
 
        cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
 
-       dprintk (KERN_INFO PFX "FSB:%d Mult:%d.%dx\n", fsb,
-                               mult/10, mult%10);
+       dprintk (KERN_INFO PFX "FSB:%d Mult:%d.%dx\n", fsb, mult/10, mult%10);
 
        switch (longhaul_version) {
        case 1:
@@ -158,6 +166,22 @@ static void longhaul_setstate (unsigned int clock_ratio_index)
                longhaul.bits.RevisionKey = 3;
                wrmsrl (MSR_VIA_LONGHAUL, longhaul.val);
                break;
+       case 3:
+               rdmsrl (MSR_VIA_LONGHAUL, longhaul.val);
+               longhaul.bits.SoftBusRatio = clock_ratio_index & 0xf;
+               longhaul.bits.SoftBusRatio4 = (clock_ratio_index & 0x10) >> 4;
+               longhaul.bits.EnableSoftBusRatio = 1;
+
+               longhaul.bits.RevisionKey = 0x0;
+
+               wrmsrl(MSR_VIA_LONGHAUL, longhaul.val);
+               __hlt();
+
+               rdmsrl (MSR_VIA_LONGHAUL, longhaul.val);
+               longhaul.bits.EnableSoftBusRatio = 0;
+               longhaul.bits.RevisionKey = 0xf;
+               wrmsrl (MSR_VIA_LONGHAUL, longhaul.val);
+               break;
        }
 
        cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
@@ -174,7 +198,7 @@ static void longhaul_setstate (unsigned int clock_ratio_index)
 
 #define ROUNDING       0xf
 
-static int _guess (int guess, int maxmult)
+static int _guess(int guess)
 {
        int target;
 
@@ -187,7 +211,7 @@ static int _guess (int guess, int maxmult)
 }
 
 
-static int guess_fsb(int maxmult)
+static int guess_fsb(void)
 {
        int speed = (cpu_khz/1000);
        int i;
@@ -197,25 +221,25 @@ static int guess_fsb(int maxmult)
        speed &= ~ROUNDING;
 
        for (i=0; i<3; i++) {
-               if (_guess(speeds[i],maxmult) == speed)
+               if (_guess(speeds[i]) == speed)
                        return speeds[i];
        }
        return 0;
 }
 
 
-static int __init longhaul_get_ranges (void)
+static int __init longhaul_get_ranges(void)
 {
        struct cpuinfo_x86 *c = cpu_data;
        unsigned long invalue;
-       unsigned int minmult=0, maxmult=0;
        unsigned int multipliers[32]= {
                50,30,40,100,55,35,45,95,90,70,80,60,120,75,85,65,
                -1,110,120,-1,135,115,125,105,130,150,160,140,-1,155,-1,145 };
        unsigned int j, k = 0;
        union msr_longhaul longhaul;
        unsigned long lo, hi;
-       unsigned int eblcr_fsb_table[] = { 66, 133, 100, -1 };
+       unsigned int eblcr_fsb_table_v1[] = { 66, 133, 100, -1 };
+       unsigned int eblcr_fsb_table_v2[] = { 133, 100, -1, 66 };
 
        switch (longhaul_version) {
        case 1:
@@ -226,16 +250,14 @@ static int __init longhaul_get_ranges (void)
                rdmsr (MSR_IA32_EBL_CR_POWERON, lo, hi);
                invalue = (lo & (1<<18|1<<19)) >>18;
                if (c->x86_model==6)
-                       fsb = eblcr_fsb_table[invalue];
+                       fsb = eblcr_fsb_table_v1[invalue];
                else
-                       fsb = guess_fsb(maxmult);
+                       fsb = guess_fsb();
                break;
 
        case 2:
                rdmsrl (MSR_VIA_LONGHAUL, longhaul.val);
 
-               //TODO: Nehemiah may have borken MaxMHzBR.
-               // need to extrapolate from FSB.
                invalue = longhaul.bits.MaxMHzBR;
                if (longhaul.bits.MaxMHzBR4)
                        invalue += 16;
@@ -247,21 +269,38 @@ static int __init longhaul_get_ranges (void)
                else
                        minmult = multipliers[invalue];
 
-               switch (longhaul.bits.MaxMHzFSB) {
-               case 0x0:       fsb=133;
-                               break;
-               case 0x1:       fsb=100;
-                               break;
-               case 0x2:       printk (KERN_INFO PFX "Invalid (reserved) FSB!\n");
-                       return -EINVAL;
-               case 0x3:       fsb=66;
-                               break;
-               }
+               fsb = eblcr_fsb_table_v2[longhaul.bits.MaxMHzFSB];
+               break;
+
+       case 3:
+               rdmsrl (MSR_VIA_LONGHAUL, longhaul.val);
+
+               /*
+                * TODO: This code works, but raises a lot of questions.
+                * - Some Nehemiah's seem to have broken Min/MaxMHzBR's.
+                *   We get around this by using a hardcoded multiplier of 5.0x
+                *   for the minimimum speed, and the speed we booted up at for the max.
+                *   This is done in longhaul_get_cpu_mult() by reading the EBLCR register.
+                * - According to some VIA documentation EBLCR is only
+                *   in pre-Nehemiah C3s. How this still works is a mystery.
+                *   We're possibly using something undocumented and unsupported,
+                *   But it works, so we don't grumble.
+                */
+               minmult=50;
+               maxmult=longhaul_get_cpu_mult();
+
+               fsb = eblcr_fsb_table_v2[longhaul.bits.MaxMHzFSB];
                break;
        }
 
        dprintk (KERN_INFO PFX "MinMult=%d.%dx MaxMult=%d.%dx\n",
                 minmult/10, minmult%10, maxmult/10, maxmult%10);
+
+       if (fsb == -1) {
+               printk (KERN_INFO PFX "Invalid (reserved) FSB!\n");
+               return -EINVAL;
+       }
+
        highest_speed = calc_speed (maxmult, fsb);
        lowest_speed = calc_speed (minmult,fsb);
        dprintk (KERN_INFO PFX "FSB: %dMHz Lowestspeed=%dMHz Highestspeed=%dMHz\n",
@@ -363,24 +402,31 @@ static int longhaul_verify(struct cpufreq_policy *policy)
 }
 
 
-static int longhaul_target (struct cpufreq_policy *policy,
+static int longhaul_target(struct cpufreq_policy *policy,
                            unsigned int target_freq,
                            unsigned int relation)
 {
        unsigned int table_index = 0;
-       unsigned int new_clock_ratio = 0;
+       unsigned int new_clock_ratio = 0;
 
        if (cpufreq_frequency_table_target(policy, longhaul_table, target_freq, relation, &table_index))
                return -EINVAL;
 
        new_clock_ratio = longhaul_table[table_index].index & 0xFF;
+
        longhaul_setstate(new_clock_ratio);
 
        return 0;
 }
 
-static int __init longhaul_cpu_init (struct cpufreq_policy *policy)
+static unsigned int longhaul_get(unsigned int cpu)
+{
+       if (cpu)
+               return 0;
+       return (calc_speed (longhaul_get_cpu_mult(), fsb));
+}
+
+static int __init longhaul_cpu_init(struct cpufreq_policy *policy)
 {
        struct cpuinfo_x86 *c = cpu_data;
        char *cpuname=NULL;
@@ -423,11 +469,25 @@ static int __init longhaul_cpu_init (struct cpufreq_policy *policy)
                break;
 
        case 9:
-               cpuname = "C3 'Nehemiah' [C5N]";
-               longhaul_version=2;
+               longhaul_version=3;
                numscales=32;
-               memcpy (clock_ratio, nehemiah_clock_ratio, sizeof(nehemiah_clock_ratio));
-               memcpy (eblcr_table, nehemiah_eblcr, sizeof(nehemiah_eblcr));
+               switch (c->x86_mask) {
+               case 0 ... 1:
+                       cpuname = "C3 'Nehemiah A' [C5N]";
+                       memcpy (clock_ratio, nehemiah_a_clock_ratio, sizeof(nehemiah_a_clock_ratio));
+                       memcpy (eblcr_table, nehemiah_a_eblcr, sizeof(nehemiah_a_eblcr));
+                       break;
+               case 2 ... 4:
+                       cpuname = "C3 'Nehemiah B' [C5N]";
+                       memcpy (clock_ratio, nehemiah_b_clock_ratio, sizeof(nehemiah_b_clock_ratio));
+                       memcpy (eblcr_table, nehemiah_b_eblcr, sizeof(nehemiah_b_eblcr));
+                       break;
+               case 5 ... 15:
+                       cpuname = "C3 'Nehemiah C' [C5N]";
+                       memcpy (clock_ratio, nehemiah_c_clock_ratio, sizeof(nehemiah_c_clock_ratio));
+                       memcpy (eblcr_table, nehemiah_c_eblcr, sizeof(nehemiah_c_eblcr));
+                       break;
+               }
                break;
 
        default:
@@ -442,11 +502,11 @@ static int __init longhaul_cpu_init (struct cpufreq_policy *policy)
        if (ret != 0)
                return ret;
 
-       if ((longhaul_version==2) && (dont_scale_voltage==0))
+       if ((longhaul_version==2) && (dont_scale_voltage==0))
                longhaul_setup_voltagescaling();
 
        policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
-       policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
+       policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
        policy->cur = calc_speed (longhaul_get_cpu_mult(), fsb);
 
        ret = cpufreq_frequency_table_cpuinfo(policy, longhaul_table);
@@ -458,7 +518,7 @@ static int __init longhaul_cpu_init (struct cpufreq_policy *policy)
        return 0;
 }
 
-static int __exit longhaul_cpu_exit(struct cpufreq_policy *policy)
+static int __devexit longhaul_cpu_exit(struct cpufreq_policy *policy)
 {
        cpufreq_frequency_table_put_attr(policy->cpu);
        return 0;
@@ -470,16 +530,17 @@ static struct freq_attr* longhaul_attr[] = {
 };
 
 static struct cpufreq_driver longhaul_driver = {
-       .verify         = longhaul_verify,
-       .target         = longhaul_target,
-       .init           = longhaul_cpu_init,
-       .exit           = longhaul_cpu_exit,
-       .name           = "longhaul",
-       .owner          = THIS_MODULE,
-       .attr           = longhaul_attr,
+       .verify = longhaul_verify,
+       .target = longhaul_target,
+       .get    = longhaul_get,
+       .init   = longhaul_cpu_init,
+       .exit   = __devexit_p(longhaul_cpu_exit),
+       .name   = "longhaul",
+       .owner  = THIS_MODULE,
+       .attr   = longhaul_attr,
 };
 
-static int __init longhaul_init (void)
+static int __init longhaul_init(void)
 {
        struct cpuinfo_x86 *c = cpu_data;
 
@@ -487,12 +548,8 @@ static int __init longhaul_init (void)
                return -ENODEV;
 
        switch (c->x86_model) {
-       case 6 ... 8:
+       case 6 ... 9:
                return cpufreq_register_driver(&longhaul_driver);
-       case 9:
-               printk (KERN_INFO PFX "Nehemiah unsupported: Waiting on working silicon "
-                                               "from VIA before this is usable.\n");
-               break;
        default:
                printk (KERN_INFO PFX "Unknown VIA CPU. Contact davej@codemonkey.org.uk\n");
        }
@@ -500,13 +557,26 @@ static int __init longhaul_init (void)
        return -ENODEV;
 }
 
-static void __exit longhaul_exit (void)
+static void __exit longhaul_exit(void)
 {
+       int i=0;
+       unsigned int new_clock_ratio;
+
+       while (clock_ratio[i] != maxmult)
+               i++;
+
+       new_clock_ratio = longhaul_table[i].index & 0xFF;
+       longhaul_setstate(new_clock_ratio);
+
        cpufreq_unregister_driver(&longhaul_driver);
        kfree(longhaul_table);
 }
 
-MODULE_PARM (dont_scale_voltage, "i");
+module_param (dont_scale_voltage, int, 0644);
+MODULE_PARM_DESC(dont_scale_voltage, "Don't scale voltage of processor");
+
+module_param (debug, int, 0644);
+MODULE_PARM_DESC(debug, "Dump debugging information.");
 
 MODULE_AUTHOR ("Dave Jones <davej@codemonkey.org.uk>");
 MODULE_DESCRIPTION ("Longhaul driver for VIA Cyrix processors.");