*/
#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")
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;
}
-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;
}
* 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;
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:
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);
#define ROUNDING 0xf
-static int _guess (int guess, int maxmult)
+static int _guess(int guess)
{
int target;
}
-static int guess_fsb(int maxmult)
+static int guess_fsb(void)
{
int speed = (cpu_khz/1000);
int i;
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:
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;
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",
}
-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;
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:
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);
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;
};
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;
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");
}
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.");