longrun.c revision 125276
1/*- 2 * Copyright (c) 2001 Tamotsu Hattori. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by the University of 16 * California, Berkeley and its contributors. 17 * 4. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 * 33 */ 34 35#include <sys/cdefs.h> 36__FBSDID("$FreeBSD: head/sys/i386/i386/longrun.c 125276 2004-01-31 20:14:44Z shiba $"); 37 38#include "opt_cpu.h" 39 40#include <sys/param.h> 41#include <sys/systm.h> 42#include <sys/kernel.h> 43#include <sys/conf.h> 44#include <sys/power.h> 45#include <sys/sysctl.h> 46#include <sys/types.h> 47 48#include <machine/md_var.h> 49#include <machine/specialreg.h> 50 51/* 52 * Transmeta Crusoe LongRun Support by Tamotsu Hattori. 53 */ 54 55#define MSR_TMx86_LONGRUN 0x80868010 56#define MSR_TMx86_LONGRUN_FLAGS 0x80868011 57 58#define LONGRUN_MODE_MASK(x) ((x) & 0x000000007f) 59#define LONGRUN_MODE_RESERVED(x) ((x) & 0xffffff80) 60#define LONGRUN_MODE_WRITE(x, y) (LONGRUN_MODE_RESERVED(x) | LONGRUN_MODE_MASK(y)) 61 62#define LONGRUN_MODE_MINFREQUENCY 0x00 63#define LONGRUN_MODE_ECONOMY 0x01 64#define LONGRUN_MODE_PERFORMANCE 0x02 65#define LONGRUN_MODE_MAXFREQUENCY 0x03 66#define LONGRUN_MODE_UNKNOWN 0x04 67#define LONGRUN_MODE_MAX 0x04 68 69union msrinfo { 70 u_int64_t msr; 71 u_int32_t regs[2]; 72}; 73 74static u_int32_t longrun_modes[LONGRUN_MODE_MAX][3] = { 75 /* MSR low, MSR high, flags bit0 */ 76 { 0, 0, 0}, /* LONGRUN_MODE_MINFREQUENCY */ 77 { 0, 100, 0}, /* LONGRUN_MODE_ECONOMY */ 78 { 0, 100, 1}, /* LONGRUN_MODE_PERFORMANCE */ 79 { 100, 100, 1}, /* LONGRUN_MODE_MAXFREQUENCY */ 80}; 81 82static u_int 83tmx86_get_longrun_mode(void) 84{ 85 u_long eflags; 86 union msrinfo msrinfo; 87 u_int low, high, flags, mode; 88 89 eflags = read_eflags(); 90 disable_intr(); 91 92 msrinfo.msr = rdmsr(MSR_TMx86_LONGRUN); 93 low = LONGRUN_MODE_MASK(msrinfo.regs[0]); 94 high = LONGRUN_MODE_MASK(msrinfo.regs[1]); 95 flags = rdmsr(MSR_TMx86_LONGRUN_FLAGS) & 0x01; 96 97 for (mode = 0; mode < LONGRUN_MODE_MAX; mode++) { 98 if (low == longrun_modes[mode][0] && 99 high == longrun_modes[mode][1] && 100 flags == longrun_modes[mode][2]) { 101 goto out; 102 } 103 } 104 mode = LONGRUN_MODE_UNKNOWN; 105out: 106 write_eflags(eflags); 107 return (mode); 108} 109 110static u_int 111tmx86_get_longrun_status(u_int * frequency, u_int * voltage, u_int * percentage) 112{ 113 u_long eflags; 114 u_int regs[4]; 115 116 eflags = read_eflags(); 117 disable_intr(); 118 119 do_cpuid(0x80860007, regs); 120 *frequency = regs[0]; 121 *voltage = regs[1]; 122 *percentage = regs[2]; 123 124 write_eflags(eflags); 125 return (1); 126} 127 128static u_int 129tmx86_set_longrun_mode(u_int mode) 130{ 131 u_long eflags; 132 union msrinfo msrinfo; 133 134 if (mode >= LONGRUN_MODE_UNKNOWN) { 135 return (0); 136 } 137 138 eflags = read_eflags(); 139 disable_intr(); 140 141 /* Write LongRun mode values to Model Specific Register. */ 142 msrinfo.msr = rdmsr(MSR_TMx86_LONGRUN); 143 msrinfo.regs[0] = LONGRUN_MODE_WRITE(msrinfo.regs[0], 144 longrun_modes[mode][0]); 145 msrinfo.regs[1] = LONGRUN_MODE_WRITE(msrinfo.regs[1], 146 longrun_modes[mode][1]); 147 wrmsr(MSR_TMx86_LONGRUN, msrinfo.msr); 148 149 /* Write LongRun mode flags to Model Specific Register. */ 150 msrinfo.msr = rdmsr(MSR_TMx86_LONGRUN_FLAGS); 151 msrinfo.regs[0] = (msrinfo.regs[0] & ~0x01) | longrun_modes[mode][2]; 152 wrmsr(MSR_TMx86_LONGRUN_FLAGS, msrinfo.msr); 153 154 write_eflags(eflags); 155 return (1); 156} 157 158static u_int crusoe_longrun; 159static u_int crusoe_frequency; 160static u_int crusoe_voltage; 161static u_int crusoe_percentage; 162static u_int crusoe_performance_longrun = LONGRUN_MODE_PERFORMANCE; 163static u_int crusoe_economy_longrun = LONGRUN_MODE_ECONOMY; 164static struct sysctl_ctx_list crusoe_sysctl_ctx; 165static struct sysctl_oid *crusoe_sysctl_tree; 166 167static void 168tmx86_longrun_power_profile(void *arg) 169{ 170 int state; 171 u_int new; 172 173 state = power_profile_get_state(); 174 if (state != POWER_PROFILE_PERFORMANCE && 175 state != POWER_PROFILE_ECONOMY) { 176 return; 177 } 178 179 switch (state) { 180 case POWER_PROFILE_PERFORMANCE: 181 new =crusoe_performance_longrun; 182 break; 183 case POWER_PROFILE_ECONOMY: 184 new = crusoe_economy_longrun; 185 break; 186 default: 187 new = tmx86_get_longrun_mode(); 188 break; 189 } 190 191 if (tmx86_get_longrun_mode() != new) { 192 tmx86_set_longrun_mode(new); 193 } 194} 195 196static int 197tmx86_longrun_sysctl(SYSCTL_HANDLER_ARGS) 198{ 199 u_int mode; 200 int error; 201 202 crusoe_longrun = tmx86_get_longrun_mode(); 203 mode = crusoe_longrun; 204 error = sysctl_handle_int(oidp, &mode, 0, req); 205 if (error || !req->newptr) { 206 return (error); 207 } 208 if (mode >= LONGRUN_MODE_UNKNOWN) { 209 error = EINVAL; 210 return (error); 211 } 212 if (crusoe_longrun != mode) { 213 crusoe_longrun = mode; 214 tmx86_set_longrun_mode(crusoe_longrun); 215 } 216 217 return (error); 218} 219 220static int 221tmx86_status_sysctl(SYSCTL_HANDLER_ARGS) 222{ 223 u_int val; 224 int error; 225 226 tmx86_get_longrun_status(&crusoe_frequency, 227 &crusoe_voltage, &crusoe_percentage); 228 val = *(u_int *)oidp->oid_arg1; 229 error = sysctl_handle_int(oidp, &val, 0, req); 230 return (error); 231} 232 233static int 234tmx86_longrun_profile_sysctl(SYSCTL_HANDLER_ARGS) 235{ 236 u_int32_t *argp; 237 u_int32_t arg; 238 int error; 239 240 argp = (u_int32_t *)oidp->oid_arg1; 241 arg = *argp; 242 error = sysctl_handle_int(oidp, &arg, 0, req); 243 244 /* error or no new value */ 245 if ((error != 0) || (req->newptr == NULL)) 246 return (error); 247 248 /* range check */ 249 if (arg >= LONGRUN_MODE_UNKNOWN) 250 return (EINVAL); 251 252 /* set new value and possibly switch */ 253 *argp = arg; 254 255 tmx86_longrun_power_profile(NULL); 256 257 return (0); 258 259} 260 261static void 262setup_tmx86_longrun(void *dummy __unused) 263{ 264 if (strcmp(cpu_vendor, "GenuineTMx86") != 0 && 265 strcmp(cpu_vendor, "TransmetaCPU") != 0) 266 return; 267 268 crusoe_longrun = tmx86_get_longrun_mode(); 269 tmx86_get_longrun_status(&crusoe_frequency, 270 &crusoe_voltage, &crusoe_percentage); 271 printf("Crusoe LongRun support enabled, current mode: %d " 272 "<%dMHz %dmV %d%%>\n", crusoe_longrun, crusoe_frequency, 273 crusoe_voltage, crusoe_percentage); 274 275 sysctl_ctx_init(&crusoe_sysctl_ctx); 276 crusoe_sysctl_tree = SYSCTL_ADD_NODE(&crusoe_sysctl_ctx, 277 SYSCTL_STATIC_CHILDREN(_hw), OID_AUTO, 278 "crusoe", CTLFLAG_RD, 0, 279 "Transmeta Crusoe LongRun support"); 280 SYSCTL_ADD_PROC(&crusoe_sysctl_ctx, SYSCTL_CHILDREN(crusoe_sysctl_tree), 281 OID_AUTO, "longrun", CTLTYPE_INT | CTLFLAG_RW, 282 &crusoe_longrun, 0, tmx86_longrun_sysctl, "I", 283 "LongRun mode [0-3]"); 284 SYSCTL_ADD_PROC(&crusoe_sysctl_ctx, SYSCTL_CHILDREN(crusoe_sysctl_tree), 285 OID_AUTO, "frequency", CTLTYPE_INT | CTLFLAG_RD, 286 &crusoe_frequency, 0, tmx86_status_sysctl, "I", 287 "Current frequency (MHz)"); 288 SYSCTL_ADD_PROC(&crusoe_sysctl_ctx, SYSCTL_CHILDREN(crusoe_sysctl_tree), 289 OID_AUTO, "voltage", CTLTYPE_INT | CTLFLAG_RD, 290 &crusoe_voltage, 0, tmx86_status_sysctl, "I", 291 "Current voltage (mV)"); 292 SYSCTL_ADD_PROC(&crusoe_sysctl_ctx, SYSCTL_CHILDREN(crusoe_sysctl_tree), 293 OID_AUTO, "percentage", CTLTYPE_INT | CTLFLAG_RD, 294 &crusoe_percentage, 0, tmx86_status_sysctl, "I", 295 "Processing performance (%)"); 296 SYSCTL_ADD_PROC(&crusoe_sysctl_ctx, SYSCTL_CHILDREN(crusoe_sysctl_tree), 297 OID_AUTO, "performance_longrun", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_RW, 298 &crusoe_performance_longrun, 0, tmx86_longrun_profile_sysctl, "I", ""); 299 SYSCTL_ADD_PROC(&crusoe_sysctl_ctx, SYSCTL_CHILDREN(crusoe_sysctl_tree), 300 OID_AUTO, "economy_longrun", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_RW, 301 &crusoe_economy_longrun, 0, tmx86_longrun_profile_sysctl, "I", ""); 302 303 /* register performance profile change handler */ 304 EVENTHANDLER_REGISTER(power_profile_change, tmx86_longrun_power_profile, NULL, 0); 305} 306SYSINIT(setup_tmx86_longrun, SI_SUB_CPU, SI_ORDER_ANY, setup_tmx86_longrun, 307 NULL); 308