/*
 * 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;
}