diff --git a/sys/arch/i386/i386/machdep.c b/sys/arch/i386/i386/machdep.c index 423c7543c520..a4ab7a864105 100644 --- a/sys/arch/i386/i386/machdep.c +++ b/sys/arch/i386/i386/machdep.c @@ -1,4 +1,4 @@ -/* $NetBSD: machdep.c,v 1.460 2001/11/15 07:03:30 lukem Exp $ */ +/* $NetBSD: machdep.c,v 1.461 2001/11/17 08:20:58 christos Exp $ */ /*- * Copyright (c) 1996, 1997, 1998, 2000 The NetBSD Foundation, Inc. @@ -76,7 +76,7 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: machdep.c,v 1.460 2001/11/15 07:03:30 lukem Exp $"); +__KERNEL_RCSID(0, "$NetBSD: machdep.c,v 1.461 2001/11/17 08:20:58 christos Exp $"); #include "opt_cputype.h" #include "opt_ddb.h" @@ -213,6 +213,8 @@ int i386_use_fxsave; int i386_has_sse; int i386_has_sse2; +int tmx86_has_longrun; + #define CPUID2FAMILY(cpuid) (((cpuid) >> 8) & 15) #define CPUID2MODEL(cpuid) (((cpuid) >> 4) & 15) #define CPUID2STEPPING(cpuid) ((cpuid) & 15) @@ -273,6 +275,7 @@ static int exec_nomid __P((struct proc *, struct exec_package *)); void cyrix6x86_cpu_setup __P((void)); void winchip_cpu_setup __P((void)); void amd_family5_setup __P((void)); +void transmeta_cpu_setup __P((void)); void intel_cpuid_cpu_cacheinfo __P((struct cpu_info *)); void amd_cpuid_cpu_cacheinfo __P((struct cpu_info *)); @@ -883,6 +886,55 @@ const struct cpu_cpuid_nameclass i386_cpuid_cpus[] = { NULL, NULL, } } + }, + { + "GenuineTMx86", + CPUVENDOR_TRANSMETA, + "Transmeta", + /* Family 4, Transmeta never had any of these */ + { { + CPUCLASS_486, + { + 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + "486 compatible" /* Default */ + }, + NULL, + NULL, + }, + /* Family 5 */ + { + CPUCLASS_586, + { + 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + "Crusoe" /* Default */ + }, + transmeta_cpu_setup, + NULL, + }, + /* Family 6, not yet available from Transmeta */ + { + CPUCLASS_686, + { + 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + "Pentium Pro compatible" /* Default */ + }, + NULL, + NULL, + }, + /* Family > 6, not yet available from Transmeta */ + { + CPUCLASS_686, + { + 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + "Pentium Pro compatible" /* Default */ + }, + NULL, + NULL, + } } } }; @@ -978,6 +1030,178 @@ amd_family5_setup(void) } } +/* + * Transmeta Crusoe LongRun Support by Tamotsu Hattori. + * Port from FreeBSD-current(August, 2001) to NetBSD by tshiozak. + */ + +#define MSR_TMx86_LONGRUN 0x80868010 +#define MSR_TMx86_LONGRUN_FLAGS 0x80868011 + +#define LONGRUN_MODE_MASK(x) ((x) & 0x000000007f) +#define LONGRUN_MODE_RESERVED(x) ((x) & 0xffffff80) +#define LONGRUN_MODE_WRITE(x, y) (LONGRUN_MODE_RESERVED(x) | LONGRUN_MODE_MASK(y)) + +#define LONGRUN_MODE_MINFREQUENCY 0x00 +#define LONGRUN_MODE_ECONOMY 0x01 +#define LONGRUN_MODE_PERFORMANCE 0x02 +#define LONGRUN_MODE_MAXFREQUENCY 0x03 +#define LONGRUN_MODE_UNKNOWN 0x04 +#define LONGRUN_MODE_MAX 0x04 + +union msrinfo { + u_int64_t msr; + u_int32_t regs[2]; +}; + +u_int32_t longrun_modes[LONGRUN_MODE_MAX][3] = { + /* MSR low, MSR high, flags bit0 */ + { 0, 0, 0}, /* LONGRUN_MODE_MINFREQUENCY */ + { 0, 100, 0}, /* LONGRUN_MODE_ECONOMY */ + { 0, 100, 1}, /* LONGRUN_MODE_PERFORMANCE */ + { 100, 100, 1}, /* LONGRUN_MODE_MAXFREQUENCY */ +}; + +static u_int +tmx86_get_longrun_mode(void) +{ + u_long eflags; + union msrinfo msrinfo; + u_int low, high, flags, mode; + + eflags = read_eflags(); + disable_intr(); + + msrinfo.msr = rdmsr(MSR_TMx86_LONGRUN); + low = LONGRUN_MODE_MASK(msrinfo.regs[0]); + high = LONGRUN_MODE_MASK(msrinfo.regs[1]); + flags = rdmsr(MSR_TMx86_LONGRUN_FLAGS) & 0x01; + + for (mode = 0; mode < LONGRUN_MODE_MAX; mode++) { + if (low == longrun_modes[mode][0] && + high == longrun_modes[mode][1] && + flags == longrun_modes[mode][2]) { + goto out; + } + } + mode = LONGRUN_MODE_UNKNOWN; +out: + write_eflags(eflags); + return (mode); +} + +static u_int +tmx86_get_longrun_status(u_int * frequency, u_int * voltage, u_int * percentage) +{ + u_long eflags; + u_int regs[4]; + + eflags = read_eflags(); + disable_intr(); + + do_cpuid(0x80860007, regs); + *frequency = regs[0]; + *voltage = regs[1]; + *percentage = regs[2]; + + write_eflags(eflags); + return (1); +} + +static u_int +tmx86_set_longrun_mode(u_int mode) +{ + u_long eflags; + union msrinfo msrinfo; + + if (mode >= LONGRUN_MODE_UNKNOWN) { + return (0); + } + + eflags = read_eflags(); + disable_intr(); + + /* Write LongRun mode values to Model Specific Register. */ + msrinfo.msr = rdmsr(MSR_TMx86_LONGRUN); + msrinfo.regs[0] = LONGRUN_MODE_WRITE(msrinfo.regs[0], + longrun_modes[mode][0]); + msrinfo.regs[1] = LONGRUN_MODE_WRITE(msrinfo.regs[1], + longrun_modes[mode][1]); + wrmsr(MSR_TMx86_LONGRUN, msrinfo.msr); + + /* Write LongRun mode flags to Model Specific Register. */ + msrinfo.msr = rdmsr(MSR_TMx86_LONGRUN_FLAGS); + msrinfo.regs[0] = (msrinfo.regs[0] & ~0x01) | longrun_modes[mode][2]; + wrmsr(MSR_TMx86_LONGRUN_FLAGS, msrinfo.msr); + + write_eflags(eflags); + return (1); +} + +static u_int crusoe_longrun; +static u_int crusoe_frequency; +static u_int crusoe_voltage; +static u_int crusoe_percentage; + +static void +tmx86_get_longrun_status_all(void) +{ + tmx86_get_longrun_status(&crusoe_frequency, + &crusoe_voltage, &crusoe_percentage); +} + + +static void +print_transmeta_info(void) +{ + u_int regs[4], nreg = 0; + + do_cpuid(0x80860000, regs); + nreg = regs[0]; + if (nreg >= 0x80860001) { + do_cpuid(0x80860001, regs); + printf(" Processor revision %u.%u.%u.%u\n", + (regs[1] >> 24) & 0xff, + (regs[1] >> 16) & 0xff, + (regs[1] >> 8) & 0xff, + regs[1] & 0xff); + } + if (nreg >= 0x80860002) { + do_cpuid(0x80860002, regs); + printf(" Code Morphing Software revision %u.%u.%u-%u-%u\n", + (regs[1] >> 24) & 0xff, + (regs[1] >> 16) & 0xff, + (regs[1] >> 8) & 0xff, + regs[1] & 0xff, + regs[2]); + } + if (nreg >= 0x80860006) { + char info[65]; + do_cpuid(0x80860003, (u_int*) &info[0]); + do_cpuid(0x80860004, (u_int*) &info[16]); + do_cpuid(0x80860005, (u_int*) &info[32]); + do_cpuid(0x80860006, (u_int*) &info[48]); + info[64] = 0; + printf(" %s\n", info); + } + + crusoe_longrun = tmx86_get_longrun_mode(); + tmx86_get_longrun_status(&crusoe_frequency, + &crusoe_voltage, &crusoe_percentage); + printf(" LongRun mode: %d <%dMHz %dmV %d%%>\n", crusoe_longrun, + crusoe_frequency, crusoe_voltage, crusoe_percentage); +} + +void +transmeta_cpu_setup(void) +{ + tmx86_has_longrun = 1; + print_transmeta_info(); +} + + +/* ---------------------------------------------------------------------- */ + static const struct i386_cache_info * cache_info_lookup(const struct i386_cache_info *cai, int count, u_int8_t desc) { @@ -1597,6 +1821,7 @@ cpu_sysctl(name, namelen, oldp, oldlenp, newp, newlen, p) { dev_t consdev; struct btinfo_bootpath *bibp; + int error, mode; /* all sysctl names at this level are terminal */ if (namelen != 1) @@ -1640,7 +1865,34 @@ cpu_sysctl(name, namelen, oldp, oldlenp, newp, newlen, p) return (sysctl_rdint(oldp, oldlenp, newp, i386_has_sse)); case CPU_SSE2: return (sysctl_rdint(oldp, oldlenp, newp, i386_has_sse2)); - + case CPU_TMLR_MODE: + if (!tmx86_has_longrun) + return (EOPNOTSUPP); + mode = (int)(crusoe_longrun = tmx86_get_longrun_mode()); + error = sysctl_int(oldp, oldlenp, newp, newlen, &mode); + if (!error && (u_int)mode!=crusoe_longrun) { + if (tmx86_set_longrun_mode(mode)) { + crusoe_longrun = (u_int)mode; + } else { + error = EINVAL; + } + } + return (error); + case CPU_TMLR_FREQUENCY: + if (!tmx86_has_longrun) + return (EOPNOTSUPP); + tmx86_get_longrun_status_all(); + return (sysctl_rdint(oldp, oldlenp, newp, crusoe_frequency)); + case CPU_TMLR_VOLTAGE: + if (!tmx86_has_longrun) + return (EOPNOTSUPP); + tmx86_get_longrun_status_all(); + return (sysctl_rdint(oldp, oldlenp, newp, crusoe_voltage)); + case CPU_TMLR_PERCENTAGE: + if (!tmx86_has_longrun) + return (EOPNOTSUPP); + tmx86_get_longrun_status_all(); + return (sysctl_rdint(oldp, oldlenp, newp, crusoe_percentage)); default: return (EOPNOTSUPP); }