Commit db44aaf3 authored by Rafa Bilski's avatar Rafa Bilski Committed by Dave Jones

[CPUFREQ] Longhaul - Add voltage scaling to driver

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: default avatarRafa³ Bilski <rafalbilski@interia.pl>
Signed-off-by: default avatarDave Jones <davej@redhat.com>
parent 23e735bc
...@@ -53,19 +53,26 @@ ...@@ -53,19 +53,26 @@
#define CPU_NEHEMIAH 5 #define CPU_NEHEMIAH 5
static int cpu_model; static int cpu_model;
static unsigned int numscales=16, numvscales; static unsigned int numscales=16;
static unsigned int fsb; 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 unsigned int minmult, maxmult;
static int can_scale_voltage; static int can_scale_voltage;
static int vrmrev;
static struct acpi_processor *pr = NULL; static struct acpi_processor *pr = NULL;
static struct acpi_processor_cx *cx = NULL; static struct acpi_processor_cx *cx = NULL;
static int port22_en = 0; static int port22_en;
/* Module parameters */ /* Module parameters */
static int dont_scale_voltage; static int scale_voltage;
static int ignore_latency = 0; static int ignore_latency;
#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, "longhaul", msg) #define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, "longhaul", msg)
...@@ -73,7 +80,6 @@ static int ignore_latency = 0; ...@@ -73,7 +80,6 @@ static int ignore_latency = 0;
/* Clock ratios multiplied by 10 */ /* Clock ratios multiplied by 10 */
static int clock_ratio[32]; static int clock_ratio[32];
static int eblcr_table[32]; static int eblcr_table[32];
static int voltage_table[32];
static unsigned int highest_speed, lowest_speed; /* kHz */ static unsigned int highest_speed, lowest_speed; /* kHz */
static int longhaul_version; static int longhaul_version;
static struct cpufreq_frequency_table *longhaul_table; static struct cpufreq_frequency_table *longhaul_table;
...@@ -163,6 +169,11 @@ static void do_powersaver(int cx_address, unsigned int clock_ratio_index) ...@@ -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.SoftBusRatio4 = (clock_ratio_index & 0x10) >> 4;
longhaul.bits.EnableSoftBusRatio = 1; 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 */ /* Sync to timer tick */
safe_halt(); safe_halt();
/* Change frequency on next halt or sleep */ /* Change frequency on next halt or sleep */
...@@ -454,53 +465,57 @@ static int __init longhaul_get_ranges(void) ...@@ -454,53 +465,57 @@ static int __init longhaul_get_ranges(void)
static void __init longhaul_setup_voltagescaling(void) static void __init longhaul_setup_voltagescaling(void)
{ {
union msr_longhaul longhaul; union msr_longhaul longhaul;
struct mV_pos minvid, maxvid;
unsigned int j, speed, pos, kHz_step, numvscales;
rdmsrl (MSR_VIA_LONGHAUL, longhaul.val); rdmsrl(MSR_VIA_LONGHAUL, longhaul.val);
if (!(longhaul.bits.RevisionID & 1)) {
if (!(longhaul.bits.RevisionID & 1)) printk(KERN_INFO PFX "Voltage scaling not supported by CPU.\n");
return; return;
}
minvid = longhaul.bits.MinimumVID; if (!longhaul.bits.VRMRev) {
maxvid = longhaul.bits.MaximumVID; printk (KERN_INFO PFX "VRM 8.5\n");
vrmrev = longhaul.bits.VRMRev; 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];
}
if (minvid == 0 || maxvid == 0) { 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.mV == 0 || maxvid.mV == 0 || minvid.mV > maxvid.mV) {
printk (KERN_INFO PFX "Bogus values Min:%d.%03d Max:%d.%03d. " printk (KERN_INFO PFX "Bogus values Min:%d.%03d Max:%d.%03d. "
"Voltage scaling disabled.\n", "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; return;
} }
if (minvid == maxvid) { if (minvid.mV == maxvid.mV) {
printk (KERN_INFO PFX "Claims to support voltage scaling but min & max are " printk (KERN_INFO PFX "Claims to support voltage scaling but min & max are "
"both %d.%03d. Voltage scaling disabled\n", "both %d.%03d. Voltage scaling disabled\n",
maxvid/1000, maxvid%1000); maxvid.mV/1000, maxvid.mV%1000);
return; return;
} }
if (vrmrev==0) { printk(KERN_INFO PFX "Max VID=%d.%03d Min VID=%d.%03d, %d possible voltage scales\n",
dprintk ("VRM 8.5\n"); maxvid.mV/1000, maxvid.mV%1000,
memcpy (voltage_table, vrm85scales, sizeof(voltage_table)); minvid.mV/1000, minvid.mV%1000,
numvscales = (voltage_table[maxvid]-voltage_table[minvid])/25; numvscales);
} else {
dprintk ("Mobile VRM\n"); j = 0;
memcpy (voltage_table, mobilevrmscales, sizeof(voltage_table)); while (longhaul_table[j].frequency != CPUFREQ_TABLE_END) {
numvscales = (voltage_table[maxvid]-voltage_table[minvid])/5; 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; can_scale_voltage = 1;
} }
...@@ -685,7 +700,7 @@ static int __init longhaul_cpu_init(struct cpufreq_policy *policy) ...@@ -685,7 +700,7 @@ static int __init longhaul_cpu_init(struct cpufreq_policy *policy)
return ret; return ret;
if ((longhaul_version==TYPE_LONGHAUL_V2 || longhaul_version==TYPE_POWERSAVER) && if ((longhaul_version==TYPE_LONGHAUL_V2 || longhaul_version==TYPE_POWERSAVER) &&
(dont_scale_voltage==0)) (scale_voltage != 0))
longhaul_setup_voltagescaling(); longhaul_setup_voltagescaling();
policy->governor = CPUFREQ_DEFAULT_GOVERNOR; policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
...@@ -773,8 +788,8 @@ static void __exit longhaul_exit(void) ...@@ -773,8 +788,8 @@ static void __exit longhaul_exit(void)
kfree(longhaul_table); kfree(longhaul_table);
} }
module_param (dont_scale_voltage, int, 0644); module_param (scale_voltage, int, 0644);
MODULE_PARM_DESC(dont_scale_voltage, "Don't scale voltage of processor"); MODULE_PARM_DESC(scale_voltage, "Scale voltage of processor");
module_param(ignore_latency, int, 0644); module_param(ignore_latency, int, 0644);
MODULE_PARM_DESC(ignore_latency, "Skip ACPI C3 latency test"); MODULE_PARM_DESC(ignore_latency, "Skip ACPI C3 latency test");
......
...@@ -450,17 +450,45 @@ static int __initdata nehemiah_c_eblcr[32] = { ...@@ -450,17 +450,45 @@ static int __initdata nehemiah_c_eblcr[32] = {
* Voltage scales. Div/Mod by 1000 to get actual voltage. * Voltage scales. Div/Mod by 1000 to get actual voltage.
* Which scale to use depends on the VRM type in use. * Which scale to use depends on the VRM type in use.
*/ */
static int __initdata vrm85scales[32] = {
1250, 1200, 1150, 1100, 1050, 1800, 1750, 1700, struct mV_pos {
1650, 1600, 1550, 1500, 1450, 1400, 1350, 1300, unsigned short mV;
1275, 1225, 1175, 1125, 1075, 1825, 1775, 1725, unsigned short pos;
1675, 1625, 1575, 1525, 1475, 1425, 1375, 1325, };
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] = { static unsigned char __initdata mV_mobilevrm[32] = {
2000, 1950, 1900, 1850, 1800, 1750, 1700, 1650, 0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18,
1600, 1550, 1500, 1450, 1500, 1350, 1300, -1, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10,
1275, 1250, 1225, 1200, 1175, 1150, 1125, 1100, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08,
1075, 1050, 1025, 1000, 975, 950, 925, -1, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00
}; };
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