longhaul.c 26.3 KB
Newer Older
L
Linus Torvalds 已提交
1
/*
D
Dave Jones 已提交
2
 *  (C) 2001-2004  Dave Jones. <davej@redhat.com>
L
Linus Torvalds 已提交
3 4 5 6 7 8 9 10
 *  (C) 2002  Padraig Brady. <padraig@antefacto.com>
 *
 *  Licensed under the terms of the GNU GPL License version 2.
 *  Based upon datasheets & sample CPUs kindly provided by VIA.
 *
 *  VIA have currently 3 different versions of Longhaul.
 *  Version 1 (Longhaul) uses the BCR2 MSR at 0x1147.
 *   It is present only in Samuel 1 (C5A), Samuel 2 (C5B) stepping 0.
11 12 13
 *  Version 2 of longhaul is backward compatible with v1, but adds
 *   LONGHAUL MSR for purpose of both frequency and voltage scaling.
 *   Present in Samuel 2 (steppings 1-7 only) (C5B), and Ezra (C5C).
L
Linus Torvalds 已提交
14
 *  Version 3 of longhaul got renamed to Powersaver and redesigned
15
 *   to use only the POWERSAVER MSR at 0x110a.
L
Linus Torvalds 已提交
16 17 18 19 20 21 22 23 24 25 26 27 28
 *   It is present in Ezra-T (C5M), Nehemiah (C5X) and above.
 *   It's pretty much the same feature wise to longhaul v2, though
 *   there is provision for scaling FSB too, but this doesn't work
 *   too well in practice so we don't even try to use this.
 *
 *  BIG FAT DISCLAIMER: Work in progress code. Possibly *dangerous*
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/cpufreq.h>
29
#include <linux/pci.h>
L
Linus Torvalds 已提交
30 31
#include <linux/slab.h>
#include <linux/string.h>
32
#include <linux/delay.h>
33 34 35
#include <linux/timex.h>
#include <linux/io.h>
#include <linux/acpi.h>
L
Linus Torvalds 已提交
36 37

#include <asm/msr.h>
38
#include <asm/cpu_device_id.h>
39
#include <acpi/processor.h>
L
Linus Torvalds 已提交
40 41 42 43 44 45 46 47 48 49 50 51 52 53

#include "longhaul.h"

#define PFX "longhaul: "

#define TYPE_LONGHAUL_V1	1
#define TYPE_LONGHAUL_V2	2
#define TYPE_POWERSAVER		3

#define	CPU_SAMUEL	1
#define	CPU_SAMUEL2	2
#define	CPU_EZRA	3
#define	CPU_EZRA_T	4
#define	CPU_NEHEMIAH	5
54
#define	CPU_NEHEMIAH_C	6
L
Linus Torvalds 已提交
55

56 57 58 59
/* Flags */
#define USE_ACPI_C3		(1 << 1)
#define USE_NORTHBRIDGE		(1 << 2)

L
Linus Torvalds 已提交
60
static int cpu_model;
61
static unsigned int numscales = 16;
L
Linus Torvalds 已提交
62
static unsigned int fsb;
63

64 65
static const struct mV_pos *vrm_mV_table;
static const unsigned char *mV_vrm_table;
66 67

static unsigned int highest_speed, lowest_speed; /* kHz */
L
Linus Torvalds 已提交
68 69
static unsigned int minmult, maxmult;
static int can_scale_voltage;
70 71
static struct acpi_processor *pr;
static struct acpi_processor_cx *cx;
72
static u32 acpi_regs_addr;
73
static u8 longhaul_flags;
74
static unsigned int longhaul_index;
L
Linus Torvalds 已提交
75 76

/* Module parameters */
77
static int scale_voltage;
78
static int disable_acpi_c3;
79
static int revid_errata;
80
static int enable;
L
Linus Torvalds 已提交
81 82

/* Clock ratios multiplied by 10 */
83 84
static int mults[32];
static int eblcr[32];
L
Linus Torvalds 已提交
85 86 87 88 89 90 91
static int longhaul_version;
static struct cpufreq_frequency_table *longhaul_table;

static char speedbuffer[8];

static char *print_speed(int speed)
{
92
	if (speed < 1000) {
93
		snprintf(speedbuffer, sizeof(speedbuffer), "%dMHz", speed);
94 95 96 97 98 99 100 101 102
		return speedbuffer;
	}

	if (speed%1000 == 0)
		snprintf(speedbuffer, sizeof(speedbuffer),
			"%dGHz", speed/1000);
	else
		snprintf(speedbuffer, sizeof(speedbuffer),
			"%d.%dGHz", speed/1000, (speed%1000)/100);
L
Linus Torvalds 已提交
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120

	return speedbuffer;
}


static unsigned int calc_speed(int mult)
{
	int khz;
	khz = (mult/10)*fsb;
	if (mult%10)
		khz += fsb/2;
	khz *= 1000;
	return khz;
}


static int longhaul_get_cpu_mult(void)
{
121
	unsigned long invalue = 0, lo, hi;
L
Linus Torvalds 已提交
122

123 124 125 126
	rdmsr(MSR_IA32_EBL_CR_POWERON, lo, hi);
	invalue = (lo & (1<<22|1<<23|1<<24|1<<25))>>22;
	if (longhaul_version == TYPE_LONGHAUL_V2 ||
	    longhaul_version == TYPE_POWERSAVER) {
L
Linus Torvalds 已提交
127
		if (lo & (1<<27))
128
			invalue += 16;
L
Linus Torvalds 已提交
129
	}
130
	return eblcr[invalue];
L
Linus Torvalds 已提交
131 132
}

133
/* For processor with BCR2 MSR */
L
Linus Torvalds 已提交
134

135
static void do_longhaul1(unsigned int mults_index)
L
Linus Torvalds 已提交
136
{
137
	union msr_bcr2 bcr2;
L
Linus Torvalds 已提交
138

139 140 141
	rdmsrl(MSR_VIA_BCR2, bcr2.val);
	/* Enable software clock multiplier */
	bcr2.bits.ESOFTBF = 1;
142
	bcr2.bits.CLOCKMUL = mults_index & 0xff;
L
Linus Torvalds 已提交
143

144 145 146 147
	/* Sync to timer tick */
	safe_halt();
	/* Change frequency on next halt or sleep */
	wrmsrl(MSR_VIA_BCR2, bcr2.val);
148 149 150
	/* Invoke transition */
	ACPI_FLUSH_CPU_CACHE();
	halt();
151 152 153 154 155 156 157

	/* Disable software clock multiplier */
	local_irq_disable();
	rdmsrl(MSR_VIA_BCR2, bcr2.val);
	bcr2.bits.ESOFTBF = 0;
	wrmsrl(MSR_VIA_BCR2, bcr2.val);
}
158

159
/* For processor with Longhaul MSR */
160

161
static void do_powersaver(int cx_address, unsigned int mults_index,
162
			  unsigned int dir)
163 164 165
{
	union msr_longhaul longhaul;
	u32 t;
166

167
	rdmsrl(MSR_VIA_LONGHAUL, longhaul.val);
168
	/* Setup new frequency */
169 170 171 172
	if (!revid_errata)
		longhaul.bits.RevisionKey = longhaul.bits.RevisionID;
	else
		longhaul.bits.RevisionKey = 0;
173 174
	longhaul.bits.SoftBusRatio = mults_index & 0xf;
	longhaul.bits.SoftBusRatio4 = (mults_index & 0x10) >> 4;
175 176
	/* Setup new voltage */
	if (can_scale_voltage)
177
		longhaul.bits.SoftVID = (mults_index >> 8) & 0x1f;
178 179 180
	/* Sync to timer tick */
	safe_halt();
	/* Raise voltage if necessary */
181
	if (can_scale_voltage && dir) {
182
		longhaul.bits.EnableSoftVID = 1;
183 184 185 186 187 188 189 190 191 192 193
		wrmsrl(MSR_VIA_LONGHAUL, longhaul.val);
		/* Change voltage */
		if (!cx_address) {
			ACPI_FLUSH_CPU_CACHE();
			halt();
		} else {
			ACPI_FLUSH_CPU_CACHE();
			/* Invoke C3 */
			inb(cx_address);
			/* Dummy op - must do something useless after P_LVL3
			 * read */
194
			t = inl(acpi_gbl_FADT.xpm_timer_block.address);
195 196 197
		}
		longhaul.bits.EnableSoftVID = 0;
		wrmsrl(MSR_VIA_LONGHAUL, longhaul.val);
198 199
	}

200
	/* Change frequency on next halt or sleep */
201
	longhaul.bits.EnableSoftBusRatio = 1;
202
	wrmsrl(MSR_VIA_LONGHAUL, longhaul.val);
203
	if (!cx_address) {
204 205 206 207 208 209 210
		ACPI_FLUSH_CPU_CACHE();
		halt();
	} else {
		ACPI_FLUSH_CPU_CACHE();
		/* Invoke C3 */
		inb(cx_address);
		/* Dummy op - must do something useless after P_LVL3 read */
211
		t = inl(acpi_gbl_FADT.xpm_timer_block.address);
212
	}
213 214 215
	/* Disable bus ratio bit */
	longhaul.bits.EnableSoftBusRatio = 0;
	wrmsrl(MSR_VIA_LONGHAUL, longhaul.val);
216 217

	/* Reduce voltage if necessary */
218
	if (can_scale_voltage && !dir) {
219 220 221 222 223 224 225 226 227 228 229 230
		longhaul.bits.EnableSoftVID = 1;
		wrmsrl(MSR_VIA_LONGHAUL, longhaul.val);
		/* Change voltage */
		if (!cx_address) {
			ACPI_FLUSH_CPU_CACHE();
			halt();
		} else {
			ACPI_FLUSH_CPU_CACHE();
			/* Invoke C3 */
			inb(cx_address);
			/* Dummy op - must do something useless after P_LVL3
			 * read */
231
			t = inl(acpi_gbl_FADT.xpm_timer_block.address);
232 233 234 235
		}
		longhaul.bits.EnableSoftVID = 0;
		wrmsrl(MSR_VIA_LONGHAUL, longhaul.val);
	}
L
Linus Torvalds 已提交
236 237 238 239
}

/**
 * longhaul_set_cpu_frequency()
240
 * @mults_index : bitpattern of the new multiplier.
L
Linus Torvalds 已提交
241 242 243 244
 *
 * Sets a new clock ratio.
 */

245 246
static void longhaul_setstate(struct cpufreq_policy *policy,
		unsigned int table_index)
L
Linus Torvalds 已提交
247
{
248
	unsigned int mults_index;
L
Linus Torvalds 已提交
249 250
	int speed, mult;
	struct cpufreq_freqs freqs;
251 252
	unsigned long flags;
	unsigned int pic1_mask, pic2_mask;
253
	u16 bm_status = 0;
254
	u32 bm_timeout = 1000;
255
	unsigned int dir = 0;
L
Linus Torvalds 已提交
256

257
	mults_index = longhaul_table[table_index].driver_data;
258
	/* Safety precautions */
259
	mult = mults[mults_index & 0x1f];
L
Linus Torvalds 已提交
260 261 262 263 264
	if (mult == -1)
		return;
	speed = calc_speed(mult);
	if ((speed > highest_speed) || (speed < lowest_speed))
		return;
265 266 267
	/* Voltage transition before frequency transition? */
	if (can_scale_voltage && longhaul_index < table_index)
		dir = 1;
L
Linus Torvalds 已提交
268 269 270 271

	freqs.old = calc_speed(longhaul_get_cpu_mult());
	freqs.new = speed;

272
	cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE);
L
Linus Torvalds 已提交
273

274
	pr_debug("Setting to FSB:%dMHz Mult:%d.%dx (%s)\n",
L
Linus Torvalds 已提交
275
			fsb, mult/10, mult%10, print_speed(speed/1000));
276
retry_loop:
277 278 279 280 281
	preempt_disable();
	local_irq_save(flags);

	pic2_mask = inb(0xA1);
	pic1_mask = inb(0x21);	/* works on C3. save mask. */
282 283
	outb(0xFF, 0xA1);	/* Overkill */
	outb(0xFE, 0x21);	/* TMR0 only */
284

285
	/* Wait while PCI bus is busy. */
286 287 288
	if (acpi_regs_addr && (longhaul_flags & USE_NORTHBRIDGE
	    || ((pr != NULL) && pr->flags.bm_control))) {
		bm_status = inw(acpi_regs_addr);
289
		bm_status &= 1 << 4;
290
		while (bm_status && bm_timeout) {
291
			outw(1 << 4, acpi_regs_addr);
292
			bm_timeout--;
293
			bm_status = inw(acpi_regs_addr);
294
			bm_status &= 1 << 4;
295 296 297
		}
	}

298 299 300 301
	if (longhaul_flags & USE_NORTHBRIDGE) {
		/* Disable AGP and PCI arbiters */
		outb(3, 0x22);
	} else if ((pr != NULL) && pr->flags.bm_control) {
302
		/* Disable bus master arbitration */
303
		acpi_write_bit_register(ACPI_BITREG_ARB_DISABLE, 1);
304
	}
L
Linus Torvalds 已提交
305 306 307 308 309 310 311
	switch (longhaul_version) {

	/*
	 * Longhaul v1. (Samuel[C5A] and Samuel2 stepping 0[C5B])
	 * Software controlled multipliers only.
	 */
	case TYPE_LONGHAUL_V1:
312
		do_longhaul1(mults_index);
L
Linus Torvalds 已提交
313 314 315
		break;

	/*
316 317
	 * Longhaul v2 appears in Samuel2 Steppings 1->7 [C5B] and Ezra [C5C]
	 *
L
Linus Torvalds 已提交
318 319 320 321
	 * Longhaul v3 (aka Powersaver). (Ezra-T [C5M] & Nehemiah [C5N])
	 * Nehemiah can do FSB scaling too, but this has never been proven
	 * to work in practice.
	 */
322
	case TYPE_LONGHAUL_V2:
L
Linus Torvalds 已提交
323
	case TYPE_POWERSAVER:
324 325
		if (longhaul_flags & USE_ACPI_C3) {
			/* Don't allow wakeup */
326
			acpi_write_bit_register(ACPI_BITREG_BUS_MASTER_RLD, 0);
327
			do_powersaver(cx->address, mults_index, dir);
328
		} else {
329
			do_powersaver(0, mults_index, dir);
330
		}
L
Linus Torvalds 已提交
331 332 333
		break;
	}

334 335 336 337
	if (longhaul_flags & USE_NORTHBRIDGE) {
		/* Enable arbiters */
		outb(0, 0x22);
	} else if ((pr != NULL) && pr->flags.bm_control) {
338
		/* Enable bus master arbitration */
339
		acpi_write_bit_register(ACPI_BITREG_ARB_DISABLE, 0);
340
	}
341 342
	outb(pic2_mask, 0xA1);	/* restore mask */
	outb(pic1_mask, 0x21);
343 344 345 346

	local_irq_restore(flags);
	preempt_enable();

347
	freqs.new = calc_speed(longhaul_get_cpu_mult());
348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388
	/* Check if requested frequency is set. */
	if (unlikely(freqs.new != speed)) {
		printk(KERN_INFO PFX "Failed to set requested frequency!\n");
		/* Revision ID = 1 but processor is expecting revision key
		 * equal to 0. Jumpers at the bottom of processor will change
		 * multiplier and FSB, but will not change bits in Longhaul
		 * MSR nor enable voltage scaling. */
		if (!revid_errata) {
			printk(KERN_INFO PFX "Enabling \"Ignore Revision ID\" "
						"option.\n");
			revid_errata = 1;
			msleep(200);
			goto retry_loop;
		}
		/* Why ACPI C3 sometimes doesn't work is a mystery for me.
		 * But it does happen. Processor is entering ACPI C3 state,
		 * but it doesn't change frequency. I tried poking various
		 * bits in northbridge registers, but without success. */
		if (longhaul_flags & USE_ACPI_C3) {
			printk(KERN_INFO PFX "Disabling ACPI C3 support.\n");
			longhaul_flags &= ~USE_ACPI_C3;
			if (revid_errata) {
				printk(KERN_INFO PFX "Disabling \"Ignore "
						"Revision ID\" option.\n");
				revid_errata = 0;
			}
			msleep(200);
			goto retry_loop;
		}
		/* This shouldn't happen. Longhaul ver. 2 was reported not
		 * working on processors without voltage scaling, but with
		 * RevID = 1. RevID errata will make things right. Just
		 * to be 100% sure. */
		if (longhaul_version == TYPE_LONGHAUL_V2) {
			printk(KERN_INFO PFX "Switching to Longhaul ver. 1\n");
			longhaul_version = TYPE_LONGHAUL_V1;
			msleep(200);
			goto retry_loop;
		}
	}
	/* Report true CPU frequency */
389
	cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE);
390 391

	if (!bm_timeout)
392 393
		printk(KERN_INFO PFX "Warning: Timeout while waiting for "
				"idle PCI bus.\n");
L
Linus Torvalds 已提交
394 395 396 397 398 399 400 401 402 403 404 405 406
}

/*
 * Centaur decided to make life a little more tricky.
 * Only longhaul v1 is allowed to read EBLCR BSEL[0:1].
 * Samuel2 and above have to try and guess what the FSB is.
 * We do this by assuming we booted at maximum multiplier, and interpolate
 * between that value multiplied by possible FSBs and cpu_mhz which
 * was calculated at boot time. Really ugly, but no other way to do this.
 */

#define ROUNDING	0xf

407
static int guess_fsb(int mult)
L
Linus Torvalds 已提交
408
{
409
	int speed = cpu_khz / 1000;
L
Linus Torvalds 已提交
410
	int i;
411 412 413 414 415 416 417 418 419
	int speeds[] = { 666, 1000, 1333, 2000 };
	int f_max, f_min;

	for (i = 0; i < 4; i++) {
		f_max = ((speeds[i] * mult) + 50) / 100;
		f_max += (ROUNDING / 2);
		f_min = f_max - ROUNDING;
		if ((speed <= f_max) && (speed >= f_min))
			return speeds[i] / 10;
L
Linus Torvalds 已提交
420 421 422 423 424
	}
	return 0;
}


425
static int longhaul_get_ranges(void)
L
Linus Torvalds 已提交
426
{
427 428
	unsigned int i, j, k = 0;
	unsigned int ratio;
429
	int mult;
L
Linus Torvalds 已提交
430

431 432 433 434 435 436 437 438 439 440 441 442
	/* Get current frequency */
	mult = longhaul_get_cpu_mult();
	if (mult == -1) {
		printk(KERN_INFO PFX "Invalid (reserved) multiplier!\n");
		return -EINVAL;
	}
	fsb = guess_fsb(mult);
	if (fsb == 0) {
		printk(KERN_INFO PFX "Invalid (reserved) FSB!\n");
		return -EINVAL;
	}
	/* Get max multiplier - as we always did.
L
Lucas De Marchi 已提交
443
	 * Longhaul MSR is useful only when voltage scaling is enabled.
444 445 446
	 * C3 is booting at max anyway. */
	maxmult = mult;
	/* Get min multiplier */
447 448 449
	switch (cpu_model) {
	case CPU_NEHEMIAH:
		minmult = 50;
L
Linus Torvalds 已提交
450
		break;
451 452 453 454 455
	case CPU_NEHEMIAH_C:
		minmult = 40;
		break;
	default:
		minmult = 30;
456
		break;
L
Linus Torvalds 已提交
457 458
	}

459
	pr_debug("MinMult:%d.%dx MaxMult:%d.%dx\n",
L
Linus Torvalds 已提交
460 461 462 463
		 minmult/10, minmult%10, maxmult/10, maxmult%10);

	highest_speed = calc_speed(maxmult);
	lowest_speed = calc_speed(minmult);
464
	pr_debug("FSB:%dMHz  Lowest speed: %s   Highest speed:%s\n", fsb,
465
		 print_speed(lowest_speed/1000),
L
Linus Torvalds 已提交
466 467 468
		 print_speed(highest_speed/1000));

	if (lowest_speed == highest_speed) {
469
		printk(KERN_INFO PFX "highestspeed == lowest, aborting.\n");
L
Linus Torvalds 已提交
470 471 472
		return -EINVAL;
	}
	if (lowest_speed > highest_speed) {
473
		printk(KERN_INFO PFX "nonsense! lowest (%d > %d) !\n",
L
Linus Torvalds 已提交
474 475 476 477
			lowest_speed, highest_speed);
		return -EINVAL;
	}

478 479 480
	longhaul_table = kmalloc((numscales + 1) * sizeof(*longhaul_table),
			GFP_KERNEL);
	if (!longhaul_table)
L
Linus Torvalds 已提交
481 482
		return -ENOMEM;

483
	for (j = 0; j < numscales; j++) {
484
		ratio = mults[j];
L
Linus Torvalds 已提交
485 486 487 488 489
		if (ratio == -1)
			continue;
		if (ratio > maxmult || ratio < minmult)
			continue;
		longhaul_table[k].frequency = calc_speed(ratio);
490
		longhaul_table[k].driver_data	= j;
L
Linus Torvalds 已提交
491 492
		k++;
	}
493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508
	if (k <= 1) {
		kfree(longhaul_table);
		return -ENODEV;
	}
	/* Sort */
	for (j = 0; j < k - 1; j++) {
		unsigned int min_f, min_i;
		min_f = longhaul_table[j].frequency;
		min_i = j;
		for (i = j + 1; i < k; i++) {
			if (longhaul_table[i].frequency < min_f) {
				min_f = longhaul_table[i].frequency;
				min_i = i;
			}
		}
		if (min_i != j) {
D
Dave Jones 已提交
509 510
			swap(longhaul_table[j].frequency,
			     longhaul_table[min_i].frequency);
511 512
			swap(longhaul_table[j].driver_data,
			     longhaul_table[min_i].driver_data);
513 514
		}
	}
L
Linus Torvalds 已提交
515 516 517

	longhaul_table[k].frequency = CPUFREQ_TABLE_END;

518 519
	/* Find index we are running on */
	for (j = 0; j < k; j++) {
520
		if (mults[longhaul_table[j].driver_data & 0x1f] == mult) {
521 522 523 524
			longhaul_index = j;
			break;
		}
	}
L
Linus Torvalds 已提交
525 526 527 528
	return 0;
}


529
static void longhaul_setup_voltagescaling(void)
L
Linus Torvalds 已提交
530 531
{
	union msr_longhaul longhaul;
532
	struct mV_pos minvid, maxvid, vid;
533
	unsigned int j, speed, pos, kHz_step, numvscales;
534
	int min_vid_speed;
L
Linus Torvalds 已提交
535

536 537 538
	rdmsrl(MSR_VIA_LONGHAUL, longhaul.val);
	if (!(longhaul.bits.RevisionID & 1)) {
		printk(KERN_INFO PFX "Voltage scaling not supported by CPU.\n");
L
Linus Torvalds 已提交
539
		return;
540 541 542
	}

	if (!longhaul.bits.VRMRev) {
543
		printk(KERN_INFO PFX "VRM 8.5\n");
544 545 546
		vrm_mV_table = &vrm85_mV[0];
		mV_vrm_table = &mV_vrm85[0];
	} else {
547
		printk(KERN_INFO PFX "Mobile VRM\n");
548 549
		if (cpu_model < CPU_NEHEMIAH)
			return;
550 551 552
		vrm_mV_table = &mobilevrm_mV[0];
		mV_vrm_table = &mV_mobilevrm[0];
	}
L
Linus Torvalds 已提交
553

554 555
	minvid = vrm_mV_table[longhaul.bits.MinimumVID];
	maxvid = vrm_mV_table[longhaul.bits.MaximumVID];
L
Linus Torvalds 已提交
556

557
	if (minvid.mV == 0 || maxvid.mV == 0 || minvid.mV > maxvid.mV) {
558
		printk(KERN_INFO PFX "Bogus values Min:%d.%03d Max:%d.%03d. "
L
Linus Torvalds 已提交
559
					"Voltage scaling disabled.\n",
560 561
					minvid.mV/1000, minvid.mV%1000,
					maxvid.mV/1000, maxvid.mV%1000);
L
Linus Torvalds 已提交
562 563 564
		return;
	}

565
	if (minvid.mV == maxvid.mV) {
566 567 568
		printk(KERN_INFO PFX "Claims to support voltage scaling but "
				"min & max are both %d.%03d. "
				"Voltage scaling disabled\n",
569
				maxvid.mV/1000, maxvid.mV%1000);
L
Linus Torvalds 已提交
570 571 572
		return;
	}

573
	/* How many voltage steps*/
574 575 576 577 578
	numvscales = maxvid.pos - minvid.pos + 1;
	printk(KERN_INFO PFX
		"Max VID=%d.%03d  "
		"Min VID=%d.%03d, "
		"%d possible voltage scales\n",
579 580 581
		maxvid.mV/1000, maxvid.mV%1000,
		minvid.mV/1000, minvid.mV%1000,
		numvscales);
582 583 584 585 586

	/* Calculate max frequency at min voltage */
	j = longhaul.bits.MinMHzBR;
	if (longhaul.bits.MinMHzBR4)
		j += 16;
587
	min_vid_speed = eblcr[j];
588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608
	if (min_vid_speed == -1)
		return;
	switch (longhaul.bits.MinMHzFSB) {
	case 0:
		min_vid_speed *= 13333;
		break;
	case 1:
		min_vid_speed *= 10000;
		break;
	case 3:
		min_vid_speed *= 6666;
		break;
	default:
		return;
		break;
	}
	if (min_vid_speed >= highest_speed)
		return;
	/* Calculate kHz for one voltage step */
	kHz_step = (highest_speed - min_vid_speed) / numvscales;

609 610 611
	j = 0;
	while (longhaul_table[j].frequency != CPUFREQ_TABLE_END) {
		speed = longhaul_table[j].frequency;
612 613 614 615
		if (speed > min_vid_speed)
			pos = (speed - min_vid_speed) / kHz_step + minvid.pos;
		else
			pos = minvid.pos;
616
		longhaul_table[j].driver_data |= mV_vrm_table[pos] << 8;
617
		vid = vrm_mV_table[mV_vrm_table[pos]];
618 619
		printk(KERN_INFO PFX "f: %d kHz, index: %d, vid: %d mV\n",
				speed, j, vid.mV);
620
		j++;
L
Linus Torvalds 已提交
621 622 623
	}

	can_scale_voltage = 1;
624
	printk(KERN_INFO PFX "Voltage scaling enabled.\n");
L
Linus Torvalds 已提交
625 626 627 628 629 630 631 632 633 634 635 636 637
}


static int longhaul_verify(struct cpufreq_policy *policy)
{
	return cpufreq_frequency_table_verify(policy, longhaul_table);
}


static int longhaul_target(struct cpufreq_policy *policy,
			    unsigned int target_freq, unsigned int relation)
{
	unsigned int table_index = 0;
638 639 640
	unsigned int i;
	unsigned int dir = 0;
	u8 vid, current_vid;
L
Linus Torvalds 已提交
641

642 643
	if (cpufreq_frequency_table_target(policy, longhaul_table, target_freq,
				relation, &table_index))
L
Linus Torvalds 已提交
644 645
		return -EINVAL;

646 647 648
	/* Don't set same frequency again */
	if (longhaul_index == table_index)
		return 0;
L
Linus Torvalds 已提交
649

650
	if (!can_scale_voltage)
651
		longhaul_setstate(policy, table_index);
652 653 654 655 656 657 658
	else {
		/* On test system voltage transitions exceeding single
		 * step up or down were turning motherboard off. Both
		 * "ondemand" and "userspace" are unsafe. C7 is doing
		 * this in hardware, C3 is old and we need to do this
		 * in software. */
		i = longhaul_index;
659
		current_vid = (longhaul_table[longhaul_index].driver_data >> 8);
660
		current_vid &= 0x1f;
661 662 663
		if (table_index > longhaul_index)
			dir = 1;
		while (i != table_index) {
664
			vid = (longhaul_table[i].driver_data >> 8) & 0x1f;
665
			if (vid != current_vid) {
666
				longhaul_setstate(policy, i);
667 668 669 670 671 672 673 674
				current_vid = vid;
				msleep(200);
			}
			if (dir)
				i++;
			else
				i--;
		}
675
		longhaul_setstate(policy, table_index);
676 677
	}
	longhaul_index = table_index;
L
Linus Torvalds 已提交
678 679 680 681 682 683 684 685 686 687 688
	return 0;
}


static unsigned int longhaul_get(unsigned int cpu)
{
	if (cpu)
		return 0;
	return calc_speed(longhaul_get_cpu_mult());
}

689 690 691
static acpi_status longhaul_walk_callback(acpi_handle obj_handle,
					  u32 nesting_level,
					  void *context, void **return_value)
692 693 694
{
	struct acpi_device *d;

695
	if (acpi_bus_get_device(obj_handle, &d))
696
		return 0;
697

J
Jan Engelhardt 已提交
698
	*return_value = acpi_driver_data(d);
699 700
	return 1;
}
L
Linus Torvalds 已提交
701

702 703 704 705
/* VIA don't support PM2 reg, but have something similar */
static int enable_arbiter_disable(void)
{
	struct pci_dev *dev;
706
	int status = 1;
707
	int reg;
708 709 710
	u8 pci_cmd;

	/* Find PLE133 host bridge */
711
	reg = 0x78;
712 713
	dev = pci_get_device(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8601_0,
			     NULL);
714 715 716 717
	/* Find PM133/VT8605 host bridge */
	if (dev == NULL)
		dev = pci_get_device(PCI_VENDOR_ID_VIA,
				     PCI_DEVICE_ID_VIA_8605_0, NULL);
718 719 720
	/* Find CLE266 host bridge */
	if (dev == NULL) {
		reg = 0x76;
721 722
		dev = pci_get_device(PCI_VENDOR_ID_VIA,
				     PCI_DEVICE_ID_VIA_862X_0, NULL);
723 724
		/* Find CN400 V-Link host bridge */
		if (dev == NULL)
725
			dev = pci_get_device(PCI_VENDOR_ID_VIA, 0x7259, NULL);
726
	}
727 728
	if (dev != NULL) {
		/* Enable access to port 0x22 */
729
		pci_read_config_byte(dev, reg, &pci_cmd);
730
		if (!(pci_cmd & 1<<7)) {
731
			pci_cmd |= 1<<7;
732
			pci_write_config_byte(dev, reg, pci_cmd);
733 734 735 736
			pci_read_config_byte(dev, reg, &pci_cmd);
			if (!(pci_cmd & 1<<7)) {
				printk(KERN_ERR PFX
					"Can't enable access to port 0x22.\n");
737
				status = 0;
738
			}
739
		}
740 741
		pci_dev_put(dev);
		return status;
742 743 744 745
	}
	return 0;
}

746
static int longhaul_setup_southbridge(void)
747 748 749 750 751
{
	struct pci_dev *dev;
	u8 pci_cmd;

	/* Find VT8235 southbridge */
752
	dev = pci_get_device(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8235, NULL);
753
	if (dev == NULL)
754
		/* Find VT8237 southbridge */
755 756
		dev = pci_get_device(PCI_VENDOR_ID_VIA,
				     PCI_DEVICE_ID_VIA_8237, NULL);
757 758 759 760 761 762 763 764 765 766 767
	if (dev != NULL) {
		/* Set transition time to max */
		pci_read_config_byte(dev, 0xec, &pci_cmd);
		pci_cmd &= ~(1 << 2);
		pci_write_config_byte(dev, 0xec, pci_cmd);
		pci_read_config_byte(dev, 0xe4, &pci_cmd);
		pci_cmd &= ~(1 << 7);
		pci_write_config_byte(dev, 0xe4, pci_cmd);
		pci_read_config_byte(dev, 0xe5, &pci_cmd);
		pci_cmd |= 1 << 7;
		pci_write_config_byte(dev, 0xe5, pci_cmd);
768 769 770 771 772
		/* Get address of ACPI registers block*/
		pci_read_config_byte(dev, 0x81, &pci_cmd);
		if (pci_cmd & 1 << 7) {
			pci_read_config_dword(dev, 0x88, &acpi_regs_addr);
			acpi_regs_addr &= 0xff00;
773 774
			printk(KERN_INFO PFX "ACPI I/O at 0x%x\n",
					acpi_regs_addr);
775 776
		}

777
		pci_dev_put(dev);
778 779 780 781 782
		return 1;
	}
	return 0;
}

783
static int longhaul_cpu_init(struct cpufreq_policy *policy)
L
Linus Torvalds 已提交
784
{
785
	struct cpuinfo_x86 *c = &cpu_data(0);
786
	char *cpuname = NULL;
L
Linus Torvalds 已提交
787
	int ret;
788
	u32 lo, hi;
L
Linus Torvalds 已提交
789

790
	/* Check what we have on this motherboard */
L
Linus Torvalds 已提交
791 792 793 794 795
	switch (c->x86_model) {
	case 6:
		cpu_model = CPU_SAMUEL;
		cpuname = "C3 'Samuel' [C5A]";
		longhaul_version = TYPE_LONGHAUL_V1;
796 797
		memcpy(mults, samuel1_mults, sizeof(samuel1_mults));
		memcpy(eblcr, samuel1_eblcr, sizeof(samuel1_eblcr));
L
Linus Torvalds 已提交
798 799 800 801 802
		break;

	case 7:
		switch (c->x86_mask) {
		case 0:
803
			longhaul_version = TYPE_LONGHAUL_V1;
L
Linus Torvalds 已提交
804 805
			cpu_model = CPU_SAMUEL2;
			cpuname = "C3 'Samuel 2' [C5B]";
806 807
			/* Note, this is not a typo, early Samuel2's had
			 * Samuel1 ratios. */
808 809
			memcpy(mults, samuel1_mults, sizeof(samuel1_mults));
			memcpy(eblcr, samuel2_eblcr, sizeof(samuel2_eblcr));
L
Linus Torvalds 已提交
810 811
			break;
		case 1 ... 15:
812
			longhaul_version = TYPE_LONGHAUL_V2;
L
Linus Torvalds 已提交
813 814 815 816 817 818 819
			if (c->x86_mask < 8) {
				cpu_model = CPU_SAMUEL2;
				cpuname = "C3 'Samuel 2' [C5B]";
			} else {
				cpu_model = CPU_EZRA;
				cpuname = "C3 'Ezra' [C5C]";
			}
820 821
			memcpy(mults, ezra_mults, sizeof(ezra_mults));
			memcpy(eblcr, ezra_eblcr, sizeof(ezra_eblcr));
L
Linus Torvalds 已提交
822 823 824 825 826 827 828 829
			break;
		}
		break;

	case 8:
		cpu_model = CPU_EZRA_T;
		cpuname = "C3 'Ezra-T' [C5M]";
		longhaul_version = TYPE_POWERSAVER;
830 831 832
		numscales = 32;
		memcpy(mults, ezrat_mults, sizeof(ezrat_mults));
		memcpy(eblcr, ezrat_eblcr, sizeof(ezrat_eblcr));
L
Linus Torvalds 已提交
833 834 835 836
		break;

	case 9:
		longhaul_version = TYPE_POWERSAVER;
837
		numscales = 32;
838 839
		memcpy(mults, nehemiah_mults, sizeof(nehemiah_mults));
		memcpy(eblcr, nehemiah_eblcr, sizeof(nehemiah_eblcr));
L
Linus Torvalds 已提交
840 841
		switch (c->x86_mask) {
		case 0 ... 1:
842
			cpu_model = CPU_NEHEMIAH;
843
			cpuname = "C3 'Nehemiah A' [C5XLOE]";
L
Linus Torvalds 已提交
844 845
			break;
		case 2 ... 4:
846
			cpu_model = CPU_NEHEMIAH;
847
			cpuname = "C3 'Nehemiah B' [C5XLOH]";
L
Linus Torvalds 已提交
848 849
			break;
		case 5 ... 15:
850
			cpu_model = CPU_NEHEMIAH_C;
851
			cpuname = "C3 'Nehemiah C' [C5P]";
L
Linus Torvalds 已提交
852 853 854 855 856 857 858 859
			break;
		}
		break;

	default:
		cpuname = "Unknown";
		break;
	}
860 861 862 863 864 865 866
	/* Check Longhaul ver. 2 */
	if (longhaul_version == TYPE_LONGHAUL_V2) {
		rdmsr(MSR_VIA_LONGHAUL, lo, hi);
		if (lo == 0 && hi == 0)
			/* Looks like MSR isn't present */
			longhaul_version = TYPE_LONGHAUL_V1;
	}
L
Linus Torvalds 已提交
867

868
	printk(KERN_INFO PFX "VIA %s CPU detected.  ", cpuname);
L
Linus Torvalds 已提交
869 870 871
	switch (longhaul_version) {
	case TYPE_LONGHAUL_V1:
	case TYPE_LONGHAUL_V2:
872
		printk(KERN_CONT "Longhaul v%d supported.\n", longhaul_version);
L
Linus Torvalds 已提交
873 874
		break;
	case TYPE_POWERSAVER:
875
		printk(KERN_CONT "Powersaver supported.\n");
L
Linus Torvalds 已提交
876 877 878
		break;
	};

879
	/* Doesn't hurt */
880
	longhaul_setup_southbridge();
881

882
	/* Find ACPI data for processor */
883
	acpi_walk_namespace(ACPI_TYPE_PROCESSOR, ACPI_ROOT_OBJECT,
884
				ACPI_UINT32_MAX, &longhaul_walk_callback, NULL,
885
				NULL, (void *)&pr);
886

887
	/* Check ACPI support for C3 state */
888
	if (pr != NULL && longhaul_version == TYPE_POWERSAVER) {
889
		cx = &pr->power.states[ACPI_STATE_C3];
890
		if (cx->address > 0 && cx->latency <= 1000)
891
			longhaul_flags |= USE_ACPI_C3;
892
	}
893 894 895
	/* Disable if it isn't working */
	if (disable_acpi_c3)
		longhaul_flags &= ~USE_ACPI_C3;
896
	/* Check if northbridge is friendly */
897
	if (enable_arbiter_disable())
898
		longhaul_flags |= USE_NORTHBRIDGE;
899

900
	/* Check ACPI support for bus master arbiter disable */
901 902 903
	if (!(longhaul_flags & USE_ACPI_C3
	     || longhaul_flags & USE_NORTHBRIDGE)
	    && ((pr == NULL) || !(pr->flags.bm_control))) {
904 905 906
		printk(KERN_ERR PFX
			"No ACPI support. Unsupported northbridge.\n");
		return -ENODEV;
907
	}
908

909
	if (longhaul_flags & USE_NORTHBRIDGE)
910 911 912
		printk(KERN_INFO PFX "Using northbridge support.\n");
	if (longhaul_flags & USE_ACPI_C3)
		printk(KERN_INFO PFX "Using ACPI support.\n");
913

L
Linus Torvalds 已提交
914 915 916 917
	ret = longhaul_get_ranges();
	if (ret != 0)
		return ret;

918
	if ((longhaul_version != TYPE_LONGHAUL_V1) && (scale_voltage != 0))
L
Linus Torvalds 已提交
919 920
		longhaul_setup_voltagescaling();

921
	policy->cpuinfo.transition_latency = 200000;	/* nsec */
L
Linus Torvalds 已提交
922 923
	policy->cur = calc_speed(longhaul_get_cpu_mult());

924
	return cpufreq_table_validate_and_show(policy, longhaul_table);
L
Linus Torvalds 已提交
925 926
}

B
Bill Pemberton 已提交
927
static int longhaul_cpu_exit(struct cpufreq_policy *policy)
L
Linus Torvalds 已提交
928 929 930 931 932
{
	cpufreq_frequency_table_put_attr(policy->cpu);
	return 0;
}

933
static struct freq_attr *longhaul_attr[] = {
L
Linus Torvalds 已提交
934 935 936 937
	&cpufreq_freq_attr_scaling_available_freqs,
	NULL,
};

938
static struct cpufreq_driver longhaul_driver = {
L
Linus Torvalds 已提交
939 940 941 942
	.verify	= longhaul_verify,
	.target	= longhaul_target,
	.get	= longhaul_get,
	.init	= longhaul_cpu_init,
943
	.exit	= longhaul_cpu_exit,
L
Linus Torvalds 已提交
944 945 946 947
	.name	= "longhaul",
	.attr	= longhaul_attr,
};

948 949 950 951 952
static const struct x86_cpu_id longhaul_id[] = {
	{ X86_VENDOR_CENTAUR, 6 },
	{}
};
MODULE_DEVICE_TABLE(x86cpu, longhaul_id);
L
Linus Torvalds 已提交
953 954 955

static int __init longhaul_init(void)
{
956
	struct cpuinfo_x86 *c = &cpu_data(0);
L
Linus Torvalds 已提交
957

958
	if (!x86_match_cpu(longhaul_id))
L
Linus Torvalds 已提交
959 960
		return -ENODEV;

961 962 963 964
	if (!enable) {
		printk(KERN_ERR PFX "Option \"enable\" not set. Aborting.\n");
		return -ENODEV;
	}
965 966
#ifdef CONFIG_SMP
	if (num_online_cpus() > 1) {
967 968
		printk(KERN_ERR PFX "More than 1 CPU detected, "
				"longhaul disabled.\n");
969
		return -ENODEV;
970 971 972 973
	}
#endif
#ifdef CONFIG_X86_IO_APIC
	if (cpu_has_apic) {
974 975
		printk(KERN_ERR PFX "APIC detected. Longhaul is currently "
				"broken in this configuration.\n");
976 977 978
		return -ENODEV;
	}
#endif
L
Linus Torvalds 已提交
979 980 981
	switch (c->x86_model) {
	case 6 ... 9:
		return cpufreq_register_driver(&longhaul_driver);
982 983
	case 10:
		printk(KERN_ERR PFX "Use acpi-cpufreq driver for VIA C7\n");
L
Linus Torvalds 已提交
984
	default:
985
		;
L
Linus Torvalds 已提交
986 987 988 989 990 991 992 993
	}

	return -ENODEV;
}


static void __exit longhaul_exit(void)
{
994
	struct cpufreq_policy *policy = cpufreq_cpu_get(0);
995
	int i;
L
Linus Torvalds 已提交
996

997 998
	for (i = 0; i < numscales; i++) {
		if (mults[i] == maxmult) {
999
			longhaul_setstate(policy, i);
L
Linus Torvalds 已提交
1000 1001 1002 1003
			break;
		}
	}

1004
	cpufreq_cpu_put(policy);
L
Linus Torvalds 已提交
1005 1006 1007 1008
	cpufreq_unregister_driver(&longhaul_driver);
	kfree(longhaul_table);
}

1009 1010 1011
/* Even if BIOS is exporting ACPI C3 state, and it is used
 * with success when CPU is idle, this state doesn't
 * trigger frequency transition in some cases. */
1012
module_param(disable_acpi_c3, int, 0644);
1013
MODULE_PARM_DESC(disable_acpi_c3, "Don't use ACPI C3 support");
L
Lucas De Marchi 已提交
1014
/* Change CPU voltage with frequency. Very useful to save
1015
 * power, but most VIA C3 processors aren't supporting it. */
1016
module_param(scale_voltage, int, 0644);
1017
MODULE_PARM_DESC(scale_voltage, "Scale voltage of processor");
1018 1019 1020 1021 1022
/* Force revision key to 0 for processors which doesn't
 * support voltage scaling, but are introducing itself as
 * such. */
module_param(revid_errata, int, 0644);
MODULE_PARM_DESC(revid_errata, "Ignore CPU Revision ID");
1023 1024 1025 1026
/* By default driver is disabled to prevent incompatible
 * system freeze. */
module_param(enable, int, 0644);
MODULE_PARM_DESC(enable, "Enable driver");
L
Linus Torvalds 已提交
1027

1028 1029 1030
MODULE_AUTHOR("Dave Jones <davej@redhat.com>");
MODULE_DESCRIPTION("Longhaul driver for VIA Cyrix processors.");
MODULE_LICENSE("GPL");
L
Linus Torvalds 已提交
1031

1032
late_initcall(longhaul_init);
L
Linus Torvalds 已提交
1033
module_exit(longhaul_exit);