[CPUFREQ] Longhaul - Add voltage scaling to driver
authorRafa³ Bilski <rafalbilski@interia.pl>
Tue, 15 Aug 2006 23:07:33 +0000 (01:07 +0200)
committerDave Jones <davej@redhat.com>
Tue, 5 Sep 2006 21:28:42 +0000 (17:28 -0400)
Rename option "dont_scale_voltage" to "scale_voltage" because
don't will be default.
Use "pos" for calculating voltage. In this way driver don't need
to know mV value or low level value. Simply min U is one pos and
max U is second pos. All pos between these two are used.
Assume that min U is for min f and max U for max f. For frequency
between min and max calculate pos based on difference between
current frequency and min f.
Values in mobile VRM table changed to values from
C3-M datasheet.

Signed-off-by: Rafa³ Bilski <rafalbilski@interia.pl>
Signed-off-by: Dave Jones <davej@redhat.com>
arch/i386/kernel/cpu/cpufreq/longhaul.c
arch/i386/kernel/cpu/cpufreq/longhaul.h

index 43bbf948d45d5659af59b2972f737c3e63ac7960..f5cc9f5c9bab3dae9d0cf050507ac774b945037b 100644 (file)
 #define        CPU_NEHEMIAH    5
 
 static int cpu_model;
-static unsigned int numscales=16, numvscales;
+static unsigned int numscales=16;
 static unsigned int fsb;
-static int minvid, maxvid;
+
+static struct mV_pos *vrm_mV_table;
+static unsigned char *mV_vrm_table;
+struct f_msr {
+       unsigned char vrm;
+};
+static struct f_msr f_msr_table[32];
+
+static unsigned int highest_speed, lowest_speed; /* kHz */
 static unsigned int minmult, maxmult;
 static int can_scale_voltage;
-static int vrmrev;
 static struct acpi_processor *pr = NULL;
 static struct acpi_processor_cx *cx = NULL;
-static int port22_en = 0;
+static int port22_en;
 
 /* Module parameters */
-static int dont_scale_voltage;
-static int ignore_latency = 0;
+static int scale_voltage;
+static int ignore_latency;
 
 #define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, "longhaul", msg)
 
@@ -73,7 +80,6 @@ static int ignore_latency = 0;
 /* Clock ratios multiplied by 10 */
 static int clock_ratio[32];
 static int eblcr_table[32];
-static int voltage_table[32];
 static unsigned int highest_speed, lowest_speed; /* kHz */
 static int longhaul_version;
 static struct cpufreq_frequency_table *longhaul_table;
@@ -163,6 +169,11 @@ static void do_powersaver(int cx_address, unsigned int clock_ratio_index)
        longhaul.bits.SoftBusRatio4 = (clock_ratio_index & 0x10) >> 4;
        longhaul.bits.EnableSoftBusRatio = 1;
 
+       if (can_scale_voltage) {
+               longhaul.bits.SoftVID = f_msr_table[clock_ratio_index].vrm;
+               longhaul.bits.EnableSoftVID = 1;
+       }
+
        /* Sync to timer tick */
        safe_halt();
        /* Change frequency on next halt or sleep */
@@ -454,53 +465,57 @@ static int __init longhaul_get_ranges(void)
 static void __init longhaul_setup_voltagescaling(void)
 {
        union msr_longhaul longhaul;
+       struct mV_pos minvid, maxvid;
+       unsigned int j, speed, pos, kHz_step, numvscales;
 
-       rdmsrl (MSR_VIA_LONGHAUL, longhaul.val);
-
-       if (!(longhaul.bits.RevisionID & 1))
+       rdmsrl(MSR_VIA_LONGHAUL, longhaul.val);
+       if (!(longhaul.bits.RevisionID & 1)) {
+               printk(KERN_INFO PFX "Voltage scaling not supported by CPU.\n");
                return;
+       }
+
+       if (!longhaul.bits.VRMRev) {
+               printk (KERN_INFO PFX "VRM 8.5\n");
+               vrm_mV_table = &vrm85_mV[0];
+               mV_vrm_table = &mV_vrm85[0];
+       } else {
+               printk (KERN_INFO PFX "Mobile VRM\n");
+               vrm_mV_table = &mobilevrm_mV[0];
+               mV_vrm_table = &mV_mobilevrm[0];
+       }
 
-       minvid = longhaul.bits.MinimumVID;
-       maxvid = longhaul.bits.MaximumVID;
-       vrmrev = longhaul.bits.VRMRev;
+       minvid = vrm_mV_table[longhaul.bits.MinimumVID];
+       maxvid = vrm_mV_table[longhaul.bits.MaximumVID];
+       numvscales = maxvid.pos - minvid.pos + 1;
+       kHz_step = (highest_speed - lowest_speed) / numvscales;
 
-       if (minvid == 0 || maxvid == 0) {
+       if (minvid.mV == 0 || maxvid.mV == 0 || minvid.mV > maxvid.mV) {
                printk (KERN_INFO PFX "Bogus values Min:%d.%03d Max:%d.%03d. "
                                        "Voltage scaling disabled.\n",
-                                       minvid/1000, minvid%1000, maxvid/1000, maxvid%1000);
+                                       minvid.mV/1000, minvid.mV%1000, maxvid.mV/1000, maxvid.mV%1000);
                return;
        }
 
-       if (minvid == maxvid) {
+       if (minvid.mV == maxvid.mV) {
                printk (KERN_INFO PFX "Claims to support voltage scaling but min & max are "
                                "both %d.%03d. Voltage scaling disabled\n",
-                               maxvid/1000, maxvid%1000);
+                               maxvid.mV/1000, maxvid.mV%1000);
                return;
        }
 
-       if (vrmrev==0) {
-               dprintk ("VRM 8.5\n");
-               memcpy (voltage_table, vrm85scales, sizeof(voltage_table));
-               numvscales = (voltage_table[maxvid]-voltage_table[minvid])/25;
-       } else {
-               dprintk ("Mobile VRM\n");
-               memcpy (voltage_table, mobilevrmscales, sizeof(voltage_table));
-               numvscales = (voltage_table[maxvid]-voltage_table[minvid])/5;
+       printk(KERN_INFO PFX "Max VID=%d.%03d  Min VID=%d.%03d, %d possible voltage scales\n",
+               maxvid.mV/1000, maxvid.mV%1000,
+               minvid.mV/1000, minvid.mV%1000,
+               numvscales);
+       
+       j = 0;
+       while (longhaul_table[j].frequency != CPUFREQ_TABLE_END) {
+               speed = longhaul_table[j].frequency;
+               pos = (speed - lowest_speed) / kHz_step + minvid.pos;
+               f_msr_table[longhaul_table[j].index].vrm = mV_vrm_table[pos];
+               j++;
        }
 
-       /* Current voltage isn't readable at first, so we need to
-          set it to a known value. The spec says to use maxvid */
-       longhaul.bits.RevisionKey = longhaul.bits.RevisionID;   /* FIXME: This is bad. */
-       longhaul.bits.EnableSoftVID = 1;
-       longhaul.bits.SoftVID = maxvid;
-       wrmsrl (MSR_VIA_LONGHAUL, longhaul.val);
-
-       minvid = voltage_table[minvid];
-       maxvid = voltage_table[maxvid];
-
-       dprintk ("Min VID=%d.%03d Max VID=%d.%03d, %d possible voltage scales\n",
-               maxvid/1000, maxvid%1000, minvid/1000, minvid%1000, numvscales);
-
        can_scale_voltage = 1;
 }
 
@@ -685,7 +700,7 @@ static int __init longhaul_cpu_init(struct cpufreq_policy *policy)
                return ret;
 
        if ((longhaul_version==TYPE_LONGHAUL_V2 || longhaul_version==TYPE_POWERSAVER) &&
-                (dont_scale_voltage==0))
+                (scale_voltage != 0))
                longhaul_setup_voltagescaling();
 
        policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
@@ -773,8 +788,8 @@ static void __exit longhaul_exit(void)
        kfree(longhaul_table);
 }
 
-module_param (dont_scale_voltage, int, 0644);
-MODULE_PARM_DESC(dont_scale_voltage, "Don't scale voltage of processor");
+module_param (scale_voltage, int, 0644);
+MODULE_PARM_DESC(scale_voltage, "Scale voltage of processor");
 module_param(ignore_latency, int, 0644);
 MODULE_PARM_DESC(ignore_latency, "Skip ACPI C3 latency test");
 
index d3a95d77ee85014f54a79dcb59224c4a6de14770..bc4682aad69b506acd909307ef6eba787013fd64 100644 (file)
@@ -450,17 +450,45 @@ static int __initdata nehemiah_c_eblcr[32] = {
  * Voltage scales. Div/Mod by 1000 to get actual voltage.
  * Which scale to use depends on the VRM type in use.
  */
-static int __initdata vrm85scales[32] = {
-       1250, 1200, 1150, 1100, 1050, 1800, 1750, 1700,
-       1650, 1600, 1550, 1500, 1450, 1400, 1350, 1300,
-       1275, 1225, 1175, 1125, 1075, 1825, 1775, 1725,
-       1675, 1625, 1575, 1525, 1475, 1425, 1375, 1325,
+
+struct mV_pos {
+       unsigned short mV;
+       unsigned short pos;
+};
+
+static struct mV_pos __initdata vrm85_mV[32] = {
+       {1250, 8},      {1200, 6},      {1150, 4},      {1100, 2},
+       {1050, 0},      {1800, 30},     {1750, 28},     {1700, 26},
+       {1650, 24},     {1600, 22},     {1550, 20},     {1500, 18},
+       {1450, 16},     {1400, 14},     {1350, 12},     {1300, 10},
+       {1275, 9},      {1225, 7},      {1175, 5},      {1125, 3},
+       {1075, 1},      {1825, 31},     {1775, 29},     {1725, 27},
+       {1675, 25},     {1625, 23},     {1575, 21},     {1525, 19},
+       {1475, 17},     {1425, 15},     {1375, 13},     {1325, 11}
+};
+
+static unsigned char __initdata mV_vrm85[32] = {
+       0x04,   0x14,   0x03,   0x13,   0x02,   0x12,   0x01,   0x11,
+       0x00,   0x10,   0x0f,   0x1f,   0x0e,   0x1e,   0x0d,   0x1d,
+       0x0c,   0x1c,   0x0b,   0x1b,   0x0a,   0x1a,   0x09,   0x19,
+       0x08,   0x18,   0x07,   0x17,   0x06,   0x16,   0x05,   0x15
+};
+
+static struct mV_pos __initdata mobilevrm_mV[32] = {
+       {1750, 31},     {1700, 30},     {1650, 29},     {1600, 28},
+       {1550, 27},     {1500, 26},     {1450, 25},     {1400, 24},
+       {1350, 23},     {1300, 22},     {1250, 21},     {1200, 20},
+       {1150, 19},     {1100, 18},     {1050, 17},     {1000, 16},
+       {975, 15},      {950, 14},      {925, 13},      {900, 12},
+       {875, 11},      {850, 10},      {825, 9},       {800, 8},
+       {775, 7},       {750, 6},       {725, 5},       {700, 4},
+       {675, 3},       {650, 2},       {625, 1},       {600, 0}
 };
 
-static int __initdata mobilevrmscales[32] = {
-       2000, 1950, 1900, 1850, 1800, 1750, 1700, 1650,
-       1600, 1550, 1500, 1450, 1500, 1350, 1300, -1,
-       1275, 1250, 1225, 1200, 1175, 1150, 1125, 1100,
-       1075, 1050, 1025, 1000, 975, 950, 925, -1,
+static unsigned char __initdata mV_mobilevrm[32] = {
+       0x1f,   0x1e,   0x1d,   0x1c,   0x1b,   0x1a,   0x19,   0x18,
+       0x17,   0x16,   0x15,   0x14,   0x13,   0x12,   0x11,   0x10,
+       0x0f,   0x0e,   0x0d,   0x0c,   0x0b,   0x0a,   0x09,   0x08,
+       0x07,   0x06,   0x05,   0x04,   0x03,   0x02,   0x01,   0x00
 };