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