diff --git a/share/man/man4/man4.i386/Makefile b/share/man/man4/man4.i386/Makefile index 82a17b5ae8ee..5046a103098e 100644 --- a/share/man/man4/man4.i386/Makefile +++ b/share/man/man4/man4.i386/Makefile @@ -3,7 +3,7 @@ MAN= aic.4 alpm.4 apm.4 ar.4 asc.4 asr.4 \ cs.4 cx.4 cy.4 \ dgb.4 el.4 en.4 ep.4 ex.4 fe.4 gsc.4 \ - ie.4 io.4 labpc.4 le.4 linux.4 lnc.4 matcd.4 mcd.4 \ + ie.4 io.4 labpc.4 le.4 linux.4 lnc.4 longrun.4 matcd.4 mcd.4 \ meteor.4 mse.4 npx.4 \ pcf.4 perfmon.4 pnp.4 \ ray.4 rdp.4 sb.4 scd.4 \ diff --git a/share/man/man4/man4.i386/longrun.4 b/share/man/man4/man4.i386/longrun.4 new file mode 100644 index 000000000000..ee418eecef8e --- /dev/null +++ b/share/man/man4/man4.i386/longrun.4 @@ -0,0 +1,73 @@ +.\" Copyright (c) 2001 Tamotsu HATTORI +.\" Copyright (c) 2001 Mitsuru IWASAKI +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.Dd Jun 30, 2001 +.Dt LONGRUN 4 i386 +.Os +.Sh NAME +.Nm longrun +.Nd Transmeta(TM) Crusoe(TM) LongRun(TM) support +.Sh SYNOPSIS +LongRun support is a collection of power saving modes for the +Transmeta Crusoe chips, similar in scope to Intel's SpeedStep. +The following +.Xr sysctl 8 +MIBs control the different CPU modes: +.Bl -tag -width "hw.crusoe.percentage integer no " -compact +.It Sy "Name Type Changeable Description +.It "hw.crusoe.longrun integer yes LongRun mode. +.Bl -tag -width "0: minimum frequency mode" -compact +.It "0: minimum frequency mode +.It "1: power-saving mode +.It "2: performance mode +.It "3: maximum frequency mode +.El +.It "hw.crusoe.frequency integer no Current frequency (MHz). +.It "hw.crusoe.voltage integer no Current voltage (mV). +.It "hw.crusoe.percentage integer no Processing performance (%). +.El +.Pp +.Sh EXAMPLES +Print the current status: +.Bd -literal -offset indent +% sysctl hw.crusoe +.Ed +.Pp +To set LongRun mode to performance oriented variable frequency mode +(less power savings): +.Bd -literal -offset indent +# sysctl -w hw.crusoe.longrun=2 +.Ed +.Pp +.Sh AUTHORS +.An -nosplit +LongRun support and this manual page were written by +.An Tamotsu HATTORI Aq athlete@kta.att.ne.jp +and +.An Mitsuru IWASAKI Aq iwasaki@FreeBSD.org . +.Sh HISTORY +The Transmeta(TM) Crusoe(TM) LongRun(TM) support first appeared in +.Fx 5.0 . diff --git a/sys/amd64/amd64/identcpu.c b/sys/amd64/amd64/identcpu.c index 4a609241ec7e..485d94285ce0 100644 --- a/sys/amd64/amd64/identcpu.c +++ b/sys/amd64/amd64/identcpu.c @@ -2,6 +2,8 @@ * Copyright (c) 1992 Terrence R. Lambert. * Copyright (c) 1982, 1987, 1990 The Regents of the University of California. * Copyright (c) 1997 KATO Takenori. + * Copyright (c) 2001 Tamotsu Hattori. + * Copyright (c) 2001 Mitsuru IWASAKI. * All rights reserved. * * This code is derived from software contributed to Berkeley by @@ -77,6 +79,7 @@ static void print_AMD_features(u_int *regs); static void print_AMD_info(u_int amd_maxregs); static void print_AMD_assoc(int i); static void print_transmeta_info(void); +static void setup_tmx86_longrun(void); static void do_cpuid(u_int ax, u_int *p); u_int cyrix_did; /* Device ID of Cyrix CPU */ @@ -621,6 +624,11 @@ printcpuinfo(void) printf("\n"); #endif + if (strcmp(cpu_vendor, "GenuineTMx86") == 0 || + strcmp(cpu_vendor, "TransmetaCPU") == 0) { + setup_tmx86_longrun(); + } + if (!bootverbose) return; @@ -1026,6 +1034,189 @@ print_AMD_features(u_int *regs) ); } +/* + * Transmeta Crusoe LongRun Support by Tamotsu Hattori. + */ + +#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 struct sysctl_ctx_list crusoe_sysctl_ctx; +static struct sysctl_oid *crusoe_sysctl_tree; + +static int +tmx86_longrun_sysctl(SYSCTL_HANDLER_ARGS) +{ + u_int mode; + int error; + + crusoe_longrun = tmx86_get_longrun_mode(); + mode = crusoe_longrun; + error = sysctl_handle_int(oidp, &mode, 0, req); + if (error || !req->newptr) { + return (error); + } + if (mode >= LONGRUN_MODE_UNKNOWN) { + error = EINVAL; + return (error); + } + if (crusoe_longrun != mode) { + crusoe_longrun = mode; + tmx86_set_longrun_mode(crusoe_longrun); + } + + return (error); +} + +static int +tmx86_status_sysctl(SYSCTL_HANDLER_ARGS) +{ + u_int val; + int error; + + tmx86_get_longrun_status(&crusoe_frequency, + &crusoe_voltage, &crusoe_percentage); + val = *(u_int *)oidp->oid_arg1; + error = sysctl_handle_int(oidp, &val, 0, req); + return (error); +} + +static void +setup_tmx86_longrun(void) +{ + static int done = 0; + + if (done) + return; + done++; + + sysctl_ctx_init(&crusoe_sysctl_ctx); + crusoe_sysctl_tree = SYSCTL_ADD_NODE(&crusoe_sysctl_ctx, + SYSCTL_STATIC_CHILDREN(_hw), OID_AUTO, + "crusoe", CTLFLAG_RD, 0, + "Transmeta Crusoe LongRun support"); + SYSCTL_ADD_PROC(&crusoe_sysctl_ctx, SYSCTL_CHILDREN(crusoe_sysctl_tree), + OID_AUTO, "longrun", CTLTYPE_INT | CTLFLAG_RW, + &crusoe_longrun, 0, tmx86_longrun_sysctl, "I", + "LongRun mode [0-3]"); + SYSCTL_ADD_PROC(&crusoe_sysctl_ctx, SYSCTL_CHILDREN(crusoe_sysctl_tree), + OID_AUTO, "frequency", CTLTYPE_INT | CTLFLAG_RD, + &crusoe_frequency, 0, tmx86_status_sysctl, "I", + "Current frequency (MHz)"); + SYSCTL_ADD_PROC(&crusoe_sysctl_ctx, SYSCTL_CHILDREN(crusoe_sysctl_tree), + OID_AUTO, "voltage", CTLTYPE_INT | CTLFLAG_RD, + &crusoe_voltage, 0, tmx86_status_sysctl, "I", + "Current voltage (mV)"); + SYSCTL_ADD_PROC(&crusoe_sysctl_ctx, SYSCTL_CHILDREN(crusoe_sysctl_tree), + OID_AUTO, "percentage", CTLTYPE_INT | CTLFLAG_RD, + &crusoe_percentage, 0, tmx86_status_sysctl, "I", + "Processing performance (%)"); +} + static void print_transmeta_info() { @@ -1059,4 +1250,11 @@ print_transmeta_info() 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); } + diff --git a/sys/i386/i386/identcpu.c b/sys/i386/i386/identcpu.c index 4a609241ec7e..485d94285ce0 100644 --- a/sys/i386/i386/identcpu.c +++ b/sys/i386/i386/identcpu.c @@ -2,6 +2,8 @@ * Copyright (c) 1992 Terrence R. Lambert. * Copyright (c) 1982, 1987, 1990 The Regents of the University of California. * Copyright (c) 1997 KATO Takenori. + * Copyright (c) 2001 Tamotsu Hattori. + * Copyright (c) 2001 Mitsuru IWASAKI. * All rights reserved. * * This code is derived from software contributed to Berkeley by @@ -77,6 +79,7 @@ static void print_AMD_features(u_int *regs); static void print_AMD_info(u_int amd_maxregs); static void print_AMD_assoc(int i); static void print_transmeta_info(void); +static void setup_tmx86_longrun(void); static void do_cpuid(u_int ax, u_int *p); u_int cyrix_did; /* Device ID of Cyrix CPU */ @@ -621,6 +624,11 @@ printcpuinfo(void) printf("\n"); #endif + if (strcmp(cpu_vendor, "GenuineTMx86") == 0 || + strcmp(cpu_vendor, "TransmetaCPU") == 0) { + setup_tmx86_longrun(); + } + if (!bootverbose) return; @@ -1026,6 +1034,189 @@ print_AMD_features(u_int *regs) ); } +/* + * Transmeta Crusoe LongRun Support by Tamotsu Hattori. + */ + +#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 struct sysctl_ctx_list crusoe_sysctl_ctx; +static struct sysctl_oid *crusoe_sysctl_tree; + +static int +tmx86_longrun_sysctl(SYSCTL_HANDLER_ARGS) +{ + u_int mode; + int error; + + crusoe_longrun = tmx86_get_longrun_mode(); + mode = crusoe_longrun; + error = sysctl_handle_int(oidp, &mode, 0, req); + if (error || !req->newptr) { + return (error); + } + if (mode >= LONGRUN_MODE_UNKNOWN) { + error = EINVAL; + return (error); + } + if (crusoe_longrun != mode) { + crusoe_longrun = mode; + tmx86_set_longrun_mode(crusoe_longrun); + } + + return (error); +} + +static int +tmx86_status_sysctl(SYSCTL_HANDLER_ARGS) +{ + u_int val; + int error; + + tmx86_get_longrun_status(&crusoe_frequency, + &crusoe_voltage, &crusoe_percentage); + val = *(u_int *)oidp->oid_arg1; + error = sysctl_handle_int(oidp, &val, 0, req); + return (error); +} + +static void +setup_tmx86_longrun(void) +{ + static int done = 0; + + if (done) + return; + done++; + + sysctl_ctx_init(&crusoe_sysctl_ctx); + crusoe_sysctl_tree = SYSCTL_ADD_NODE(&crusoe_sysctl_ctx, + SYSCTL_STATIC_CHILDREN(_hw), OID_AUTO, + "crusoe", CTLFLAG_RD, 0, + "Transmeta Crusoe LongRun support"); + SYSCTL_ADD_PROC(&crusoe_sysctl_ctx, SYSCTL_CHILDREN(crusoe_sysctl_tree), + OID_AUTO, "longrun", CTLTYPE_INT | CTLFLAG_RW, + &crusoe_longrun, 0, tmx86_longrun_sysctl, "I", + "LongRun mode [0-3]"); + SYSCTL_ADD_PROC(&crusoe_sysctl_ctx, SYSCTL_CHILDREN(crusoe_sysctl_tree), + OID_AUTO, "frequency", CTLTYPE_INT | CTLFLAG_RD, + &crusoe_frequency, 0, tmx86_status_sysctl, "I", + "Current frequency (MHz)"); + SYSCTL_ADD_PROC(&crusoe_sysctl_ctx, SYSCTL_CHILDREN(crusoe_sysctl_tree), + OID_AUTO, "voltage", CTLTYPE_INT | CTLFLAG_RD, + &crusoe_voltage, 0, tmx86_status_sysctl, "I", + "Current voltage (mV)"); + SYSCTL_ADD_PROC(&crusoe_sysctl_ctx, SYSCTL_CHILDREN(crusoe_sysctl_tree), + OID_AUTO, "percentage", CTLTYPE_INT | CTLFLAG_RD, + &crusoe_percentage, 0, tmx86_status_sysctl, "I", + "Processing performance (%)"); +} + static void print_transmeta_info() { @@ -1059,4 +1250,11 @@ print_transmeta_info() 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); } +