/* * longrun - Transmeta(TM) Crusoe(TM) LongRun(TM) utility * * Copyright (C) 2001 Transmeta Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ //#define TRASH #define VERSION "0.9" #define REVDATE "2001-02-14" #include #include #include #include #include #include #include #include #include #include #include #define __USE_UNIX98 /* for pread/pwrite */ #include #include #define MSR_DEVICE "/dev/cpu/0/msr" #define MSR_TMx86_LONGRUN 0x80868010 #define MSR_TMx86_LONGRUN_FLAGS 0x80868011 #define LONGRUN_MASK(x) ((x) & 0x0000007f) #define LONGRUN_RESERVED(x) ((x) & 0xffffff80) #define LONGRUN_WRITE(x, y) (LONGRUN_RESERVED(x) | LONGRUN_MASK(y)) #define CPUID_DEVICE "/dev/cpu/0/cpuid" #define CPUID_TMx86_VENDOR_ID 0x80860000 #define CPUID_TMx86_PROCESSOR_INFO 0x80860001 #define CPUID_TMx86_LONGRUN_STATUS 0x80860007 #define CPUID_TMx86_FEATURE_LONGRUN(x) ((x) & 0x02) /* Advanced Thermal Management */ #define LR_NORTHBRIDGE "/proc/bus/pci/00/00.0" #define ATM_ADDRESS 0xa8 #define ATM_EN(x) (((x) & 0x10) >> 4) #define ATM_LVL(x) (((x) & 0x0e) >> 1) #define LR_EN(x) ((x) & 0x01) char *progname; /* name of program */ int msr_fd; /* MSR file descriptor */ char *msr_device; /* MSR device name */ int cpuid_fd; /* CPUID file descriptor */ char *cpuid_device; /* CPUID device name */ int opt_verbose = 0; /* verbosity */ void usage(int status) { FILE *stream = status ? stderr : stdout; fprintf(stream, "%s %s (%s)\n" "usage: %s [-c device] [-m device] [-hlpv] [-f flag] [-s low high]\n" " -c device set CPUID device\n" " -m device set MSR device\n" " -h print this help\n" " -l list LongRun information about available performance levels\n" " -p print current LongRun settings and status\n" " -v be more verbose\n" " -f [flag] set a LongRun mode flag\n" " -s low high set current LongRun performance window (0 to 100)\n" "\n" "supported flags:\n" " economy set economy mode (turn off performance mode)\n" " performance set performance mode (turn off economy mode)\n", progname, VERSION, REVDATE, progname); exit(status); } char *my_basename (const char *filename) { char *base = strrchr (filename, '/'); return base ? base + 1 : (char *) filename; } void error_warn (const char *fmt, ...) { va_list arg_ptr; va_start(arg_ptr, fmt); fprintf(stderr, "%s: ", progname); vfprintf(stderr, fmt, arg_ptr); fprintf(stderr, ": %s\n", strerror(errno)); va_end(arg_ptr); } void error_die (const char *fmt, ...) { va_list arg_ptr; va_start(arg_ptr, fmt); fprintf(stderr, "%s: ", progname); vfprintf(stderr, fmt, arg_ptr); fprintf(stderr, ": %s\n", strerror(errno)); va_end(arg_ptr); exit(1); } /* read Advanced Thermal Management definitions register */ void read_atm(uint8_t *atm) { int nb; nb = open(LR_NORTHBRIDGE, O_RDONLY); if (nb < 0) { error_warn("error opening %s", LR_NORTHBRIDGE); if (errno == ENOENT) { fprintf(stderr, "make sure /proc is mounted\n"); } exit(1); } #ifndef TRASH if (lseek(nb, ATM_ADDRESS, SEEK_SET) == (off_t) -1) { error_die("error lseek() %s", LR_NORTHBRIDGE); } if (read(nb, atm, 1) != 1) { #ifdef TRASH error_die("error read() %s", LR_NORTHBRIDGE); #else *atm = (uint8_t) -1; #endif } #else if (pread(nb, atm, 1, ATM_ADDRESS) != 1) { error_die("error reading %s", LR_NORTHBRIDGE); } #endif close(nb); } /* note: if an output is NULL, then don't set it */ void read_msr(long address, int *lower, int *upper) { uint32_t data[2]; #ifndef TRASH if (lseek(msr_fd, address, SEEK_SET) == (off_t) -1) { error_die("error lseek() %s", msr_device); } if (read(msr_fd, &data, 8) != 8) { error_die("error read() %s", msr_device); } #else if (pread(msr_fd, &data, 8, address) != 8) { error_die("error reading %s", msr_device); } #endif if (lower) *lower = data[0]; if (upper) *upper = data[1]; } void write_msr(long address, int lower, int upper) { uint32_t data[2]; data[0] = (uint32_t) lower; data[1] = (uint32_t) upper; #ifndef TRASH if (lseek(msr_fd, address, SEEK_SET) == (off_t) -1) { error_die("error lseek() %s", msr_device); } if (write(msr_fd, &data, 8) != 8) { error_die("error read() %s", msr_device); } #else if (pwrite(msr_fd, &data, 8, address) != 8) { error_die("error writing %s", msr_device); } #endif } /* note: if an output is NULL, then don't set it */ void read_cpuid(long address, int *eax, int *ebx, int *ecx, int *edx) { uint32_t data[4]; #ifndef TRASH if (lseek(cpuid_fd, address, SEEK_SET) == (off_t) -1) { error_die("error lseek() %s", cpuid_device); } if (read(cpuid_fd, &data, 16) != 16) { error_die("error read() %s", cpuid_device); } #else if (pread(cpuid_fd, &data, 16, address) != 16) { error_die("error reading %s", cpuid_device); } #endif if (eax) *eax = data[0]; if (ebx) *ebx = data[1]; if (ecx) *ecx = data[2]; if (edx) *edx = data[3]; } void check_cpu() { int eax, ebx, ecx, edx; /* test for "TransmetaCPU" */ read_cpuid(CPUID_TMx86_VENDOR_ID, &eax, &ebx, &ecx, &edx); if (ebx != 0x6e617254 || ecx != 0x55504361 || edx != 0x74656d73) { fprintf(stderr, "%s: not a Transmeta x86 CPU\n", progname); exit(1); } /* test for LongRun feature flag */ read_cpuid(CPUID_TMx86_PROCESSOR_INFO, &eax, &ebx, &ecx, &edx); if (!CPUID_TMx86_FEATURE_LONGRUN(edx)) { printf("LongRun: unsupported\n"); exit(0); } } void list_longrun() { int i, save_lower, save_upper, pct_in, pct_last, steps, freq_max; int eax, ebx, ecx; float power_max, power_ratio; int *perf, *mhz, *volts; /* save current settings */ read_msr(MSR_TMx86_LONGRUN, &save_lower, &save_upper); /* find freq_max */ read_cpuid(CPUID_TMx86_PROCESSOR_INFO, 0, 0, &freq_max, 0); /* setup for probing */ write_msr(MSR_TMx86_LONGRUN, LONGRUN_WRITE(save_lower, 0), LONGRUN_WRITE(save_upper, 0)); read_cpuid(CPUID_TMx86_LONGRUN_STATUS, &eax, 0, 0, 0); steps = (((freq_max - eax)) / (100.0 / 3.0) + 0.5); /* 33 MHz steps */ perf = malloc(sizeof(int) * (steps + 1)); mhz = malloc(sizeof(int) * (steps + 1)); volts = malloc(sizeof(int) * (steps + 1)); for (i = 0; i <= steps; i++) { mhz[i] = 0; } /* probe */ pct_last = -1; for (i = 0; i <= steps; i++) { pct_in = ((float) i * (100.0 / (float) steps)); write_msr(MSR_TMx86_LONGRUN, LONGRUN_WRITE(save_lower, pct_in), LONGRUN_WRITE(save_upper, pct_in)); read_cpuid(CPUID_TMx86_LONGRUN_STATUS, &eax, &ebx, &ecx, 0); if (opt_verbose) printf("# set %d, got %d\n", pct_in, ecx); if (pct_last < ecx) { perf[i] = ecx; mhz[i] = eax; volts[i] = ebx; } if (ecx == 100) break; if (ecx > pct_in) i++; pct_last = ecx; } /* find power_max */ power_max = mhz[i] * volts[i] * volts[i]; /* print results */ printf("# %% MHz Volts usage\n"); for (i = 0; i <= steps; i++) { if (mhz[i]) { power_ratio = mhz[i] * volts[i] * volts[i] / power_max; printf("%3d %5d %6.3f %6.3f\n", perf[i], mhz[i], volts[i] / 1000.0, power_ratio); } } free(perf); free(mhz); free(volts); /* restore current settings */ write_msr(MSR_TMx86_LONGRUN, save_lower, save_upper); } void get_longrun(int *low, int *high) { read_msr(MSR_TMx86_LONGRUN, low, high); /* only look at desired bits */ *low = LONGRUN_MASK(*low); *high = LONGRUN_MASK(*high); } void set_longrun(int low, int high) { int lower, upper; read_msr(MSR_TMx86_LONGRUN, &lower, &upper); write_msr(MSR_TMx86_LONGRUN, LONGRUN_WRITE(lower, low), LONGRUN_WRITE(upper, high)); if (opt_verbose) { printf("Setting performance window: %d to %d\n", low, high); } } void set_longrun_flag(char *flag) { int lower, upper; read_msr(MSR_TMx86_LONGRUN_FLAGS, &lower, &upper); if (!strcmp(flag, "economy")) { write_msr(MSR_TMx86_LONGRUN_FLAGS, lower & 0xfffffffe, upper); if (opt_verbose) { printf("Setting flag: economy\n"); } } else if (!strcmp(flag, "performance")) { write_msr(MSR_TMx86_LONGRUN_FLAGS, (lower & 0xffffffff) | 0x1, upper); if (opt_verbose) { printf("Setting flag: performance\n"); } } else { usage(1); } } void print_longrun() { uint8_t atm; /* Advanced Thermal Management */ int lower, upper, percent; /* read the current performance level first */ read_cpuid(CPUID_TMx86_LONGRUN_STATUS, 0, 0, &percent, 0); read_atm(&atm); if (atm == (uint8_t) -1) printf("cannot determine LongRun status\n"); else { printf("LongRun: %s\n", LR_EN(atm) ? "enabled" : "disabled"); printf("LongRun Thermal Extensions (LTX): %s\n", ATM_EN(atm) ? "active" : "inactive"); if (ATM_EN(atm)) { printf("LTX setting: "); switch(ATM_LVL(atm)) { case 0: case 1: printf("reserved\n"); break; case 2: printf("75%% reduction\n"); break; case 3: printf("62.5%% reduction\n"); break; case 4: printf("50%% reduction\n"); break; case 5: printf("37.5%% reduction\n"); break; case 6: printf("25%% reduction\n"); break; case 7: printf("12.5%% reduction\n"); break; } } } get_longrun(&lower, &upper); printf("Current performance window: %d to %d\n", lower, upper); printf("Current performance level: %d\n", percent); read_msr(MSR_TMx86_LONGRUN_FLAGS, &lower, &upper); printf("LongRun flags: %s\n", (lower & 1) ? "performance" : "economy"); } int main(int argc, char *argv[]) { int low, high; int g; char *opt_flag = NULL; int opt_list = 0; int opt_print = 0; int opt_set = 0; if (argc) progname = my_basename(argv[0]); else progname = "longrun"; msr_device = MSR_DEVICE; cpuid_device = CPUID_DEVICE; /* command line options */ while ((g = getopt(argc, argv, "c:f:m:hlpsv")) != EOF) { switch (g) { case 'c': cpuid_device = optarg; break; case 'f': opt_flag = optarg; break; case 'm': msr_device = optarg; break; case 'h': usage(0); break; case 'l': opt_list = 1; break; case 'p': opt_print = 1; break; case 's': opt_set = 1; break; case 'v': opt_verbose++; break; } } if (opt_list + opt_print + opt_set + (opt_flag ? 1 : 0) != 1) usage(1); if (opt_set && (optind + 2 != argc)) usage(1); #ifdef TRASH2 if (geteuid()) { fprintf(stderr, "%s: must be run as root\n", progname); exit(1); } #endif if ((cpuid_fd = open(cpuid_device, O_RDWR)) < 0) { error_warn("error opening %s", cpuid_device); if (errno == ENODEV) { fprintf(stderr, "make sure your kernel was compiled with CONFIG_X86_CPUID=y\n"); } exit(1); } if ((msr_fd = open(msr_device, O_RDWR)) < 0) { error_warn("error opening %s", msr_device); if (errno == ENODEV) { fprintf(stderr, "make sure your kernel was compiled with CONFIG_X86_MSR=y\n"); } exit(1); } check_cpu(); if (opt_list) { list_longrun(); exit(0); } if (opt_set) { low = strtol(argv[optind], NULL, 10); high = strtol(argv[optind + 1], NULL, 10); set_longrun(low, high); } if (opt_flag) { set_longrun_flag(opt_flag); } if (opt_print || opt_verbose) { print_longrun(); } exit(0); }