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