/*
 * VIA DP310 boards sometimes come with an old BIOS that 
 * configures the CPU pipeline length to a single instruction
 * thus degrading the system performance significantly. 
 * 
 * This simple tool reconfigures the pipeline to 
 * the full length of 12 instructions.
 *
 * Depends on 'msr.ko' kernel module providing device /dev/cpu/<CPU>/msr
 * 
 * By Michal Ludvig <michal@logix.cz> on 2006-01-02
 *    http://www.logix.cz/michal/devel/viadp310msr
 * 
 * License: GPL2
 */

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#define	VERSION	"1.0"
#define MSR	0x1143
#define	BIT	0x1
#define NCPUS	2
#define	QLEN1	1
#define	QLEN12	2

void usage()
{
	printf("Configure CPU pipeline length on VIA DP310 boards\n"
	"Version: %s\n"
	"\n"
	"Usage:\n"
	"viadp310msr [-p|-m0|-m1] [-v] [-h]\n"
	"	-h	help\n"
	"	-p	print current configuration\n"
	"	-v	verbose operation\n"
	"	-0	set pipeline length to 12 instructions (default)\n"
	"	-1	set pipeline length to 1 instruction\n"
	"\n"
	"By Michal Ludvig (http://www.logix.cz/michal/devel/viadp310msr)\n",
	VERSION);
	exit(10);
}

int main(int argc, char *argv[])
{
	unsigned int msr = MSR;
	int cpu, c;
	int printonly = 0;
	int verbose = 0;
	int mode = QLEN12;

	while ((c = getopt(argc, argv, "pvh01")) != -1) {
		switch (c) {
			case 'p':
				printonly = 1;
				break;
			case 'v':
				verbose = 1;
				break;
			case 'h':
				usage();
				break;
			case '0':
				mode = QLEN12;
				break;
			case '1':
				mode = QLEN1;
				break;
			default:
				usage();
		}
	}

	for (cpu = 0; cpu < NCPUS; cpu++) {
		char cpuname[16];
		int fh;
		unsigned long long val, val2;
		sprintf (cpuname, "/dev/cpu/%d/msr", cpu);

		fh = open (cpuname, O_RDWR);
		if (fh==-1) {
			perror("open");
			exit(1);
		}

		if (lseek (fh, msr, SEEK_CUR) < 0) {
			perror("lseek");
			exit(2);
		}

		if (read (fh, &val, 8) != 8) {
			perror("read");
			exit(3);
		}

		if (verbose)
			printf("MSR: 0x%04x@%d=0x%016llx\n", msr, cpu, val);
		else
			printf("CPU%d pipeline length: %d ",
				cpu, val & BIT ? 1 : 12);

		if (! printonly) {
			if (mode == QLEN12) {
				// clear bit 0 => QLen=12 instructions
				val &= ~BIT;
			} else {
				// set bit 0 => QLen=1 instruction
				val |= BIT;
			}
			// write back
			if (write (fh, &val, 8) != 8) {
				perror("write");
				exit(4);
			}
			// verify
			if (read (fh, &val2, 8) != 8) {
				perror("read");
				exit(5);
			}
			if (verbose) 
				printf ("MSR: 0x%04x@%d=0x%016llx (%s)\n",
					msr, cpu, val2,
					(val == val2 ? "OK" : "FAILED"));
			else
				printf ("-> %d %s", 
					val & BIT ? 1 : 12,
					val == val2 ? "(OK)" : "(FAILED)");
		}
		if (! verbose)
			printf("\n");
		close (fh);
	}
	return 0;
}
