1124961Ssobomax/*- 2124961Ssobomax * Copyright (c) 2001 Tamotsu Hattori. 3138722Snjl * Copyright (c) 2001 Mitsuru IWASAKI. 4124961Ssobomax * All rights reserved. 5124961Ssobomax * 6124961Ssobomax * Redistribution and use in source and binary forms, with or without 7124961Ssobomax * modification, are permitted provided that the following conditions 8124961Ssobomax * are met: 9124961Ssobomax * 1. Redistributions of source code must retain the above copyright 10124961Ssobomax * notice, this list of conditions and the following disclaimer. 11124961Ssobomax * 2. Redistributions in binary form must reproduce the above copyright 12124961Ssobomax * notice, this list of conditions and the following disclaimer in the 13124961Ssobomax * documentation and/or other materials provided with the distribution. 14124961Ssobomax * 3. All advertising materials mentioning features or use of this software 15124961Ssobomax * must display the following acknowledgement: 16124961Ssobomax * This product includes software developed by the University of 17124961Ssobomax * California, Berkeley and its contributors. 18124961Ssobomax * 4. Neither the name of the University nor the names of its contributors 19124961Ssobomax * may be used to endorse or promote products derived from this software 20124961Ssobomax * without specific prior written permission. 21124961Ssobomax * 22124961Ssobomax * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23124961Ssobomax * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24124961Ssobomax * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25124961Ssobomax * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26124961Ssobomax * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27124961Ssobomax * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28124961Ssobomax * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29124961Ssobomax * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30124961Ssobomax * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31124961Ssobomax * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32124961Ssobomax * SUCH DAMAGE. 33124961Ssobomax * 34124961Ssobomax */ 35124961Ssobomax 36124961Ssobomax#include <sys/cdefs.h> 37124961Ssobomax__FBSDID("$FreeBSD$"); 38124961Ssobomax 39124961Ssobomax#include "opt_cpu.h" 40124961Ssobomax 41124961Ssobomax#include <sys/param.h> 42124961Ssobomax#include <sys/systm.h> 43124961Ssobomax#include <sys/kernel.h> 44124961Ssobomax#include <sys/conf.h> 45124961Ssobomax#include <sys/power.h> 46124961Ssobomax#include <sys/sysctl.h> 47124961Ssobomax#include <sys/types.h> 48124961Ssobomax 49185341Sjkim#include <machine/cputypes.h> 50124961Ssobomax#include <machine/md_var.h> 51124961Ssobomax#include <machine/specialreg.h> 52124961Ssobomax 53124961Ssobomax/* 54124961Ssobomax * Transmeta Crusoe LongRun Support by Tamotsu Hattori. 55124961Ssobomax */ 56124961Ssobomax 57124961Ssobomax#define MSR_TMx86_LONGRUN 0x80868010 58124961Ssobomax#define MSR_TMx86_LONGRUN_FLAGS 0x80868011 59124961Ssobomax 60124961Ssobomax#define LONGRUN_MODE_MASK(x) ((x) & 0x000000007f) 61124961Ssobomax#define LONGRUN_MODE_RESERVED(x) ((x) & 0xffffff80) 62124961Ssobomax#define LONGRUN_MODE_WRITE(x, y) (LONGRUN_MODE_RESERVED(x) | LONGRUN_MODE_MASK(y)) 63124961Ssobomax 64124961Ssobomax#define LONGRUN_MODE_MINFREQUENCY 0x00 65124961Ssobomax#define LONGRUN_MODE_ECONOMY 0x01 66124961Ssobomax#define LONGRUN_MODE_PERFORMANCE 0x02 67124961Ssobomax#define LONGRUN_MODE_MAXFREQUENCY 0x03 68124961Ssobomax#define LONGRUN_MODE_UNKNOWN 0x04 69124961Ssobomax#define LONGRUN_MODE_MAX 0x04 70124961Ssobomax 71124961Ssobomaxunion msrinfo { 72124961Ssobomax u_int64_t msr; 73124961Ssobomax u_int32_t regs[2]; 74124961Ssobomax}; 75124961Ssobomax 76124961Ssobomaxstatic u_int32_t longrun_modes[LONGRUN_MODE_MAX][3] = { 77124961Ssobomax /* MSR low, MSR high, flags bit0 */ 78124961Ssobomax { 0, 0, 0}, /* LONGRUN_MODE_MINFREQUENCY */ 79124961Ssobomax { 0, 100, 0}, /* LONGRUN_MODE_ECONOMY */ 80124961Ssobomax { 0, 100, 1}, /* LONGRUN_MODE_PERFORMANCE */ 81124961Ssobomax { 100, 100, 1}, /* LONGRUN_MODE_MAXFREQUENCY */ 82124961Ssobomax}; 83124961Ssobomax 84124961Ssobomaxstatic u_int 85124961Ssobomaxtmx86_get_longrun_mode(void) 86124961Ssobomax{ 87214346Sjhb register_t saveintr; 88124961Ssobomax union msrinfo msrinfo; 89124961Ssobomax u_int low, high, flags, mode; 90124961Ssobomax 91214346Sjhb saveintr = intr_disable(); 92124961Ssobomax 93124961Ssobomax msrinfo.msr = rdmsr(MSR_TMx86_LONGRUN); 94124961Ssobomax low = LONGRUN_MODE_MASK(msrinfo.regs[0]); 95124961Ssobomax high = LONGRUN_MODE_MASK(msrinfo.regs[1]); 96124961Ssobomax flags = rdmsr(MSR_TMx86_LONGRUN_FLAGS) & 0x01; 97124961Ssobomax 98124961Ssobomax for (mode = 0; mode < LONGRUN_MODE_MAX; mode++) { 99124961Ssobomax if (low == longrun_modes[mode][0] && 100124961Ssobomax high == longrun_modes[mode][1] && 101124961Ssobomax flags == longrun_modes[mode][2]) { 102124961Ssobomax goto out; 103124961Ssobomax } 104124961Ssobomax } 105124961Ssobomax mode = LONGRUN_MODE_UNKNOWN; 106124961Ssobomaxout: 107214346Sjhb intr_restore(saveintr); 108124961Ssobomax return (mode); 109124961Ssobomax} 110124961Ssobomax 111124961Ssobomaxstatic u_int 112124961Ssobomaxtmx86_get_longrun_status(u_int * frequency, u_int * voltage, u_int * percentage) 113124961Ssobomax{ 114214346Sjhb register_t saveintr; 115124961Ssobomax u_int regs[4]; 116124961Ssobomax 117214346Sjhb saveintr = intr_disable(); 118124961Ssobomax 119124961Ssobomax do_cpuid(0x80860007, regs); 120124961Ssobomax *frequency = regs[0]; 121124961Ssobomax *voltage = regs[1]; 122124961Ssobomax *percentage = regs[2]; 123124961Ssobomax 124214346Sjhb intr_restore(saveintr); 125124961Ssobomax return (1); 126124961Ssobomax} 127124961Ssobomax 128124961Ssobomaxstatic u_int 129124961Ssobomaxtmx86_set_longrun_mode(u_int mode) 130124961Ssobomax{ 131214346Sjhb register_t saveintr; 132124961Ssobomax union msrinfo msrinfo; 133124961Ssobomax 134124961Ssobomax if (mode >= LONGRUN_MODE_UNKNOWN) { 135124961Ssobomax return (0); 136124961Ssobomax } 137124961Ssobomax 138214346Sjhb saveintr = intr_disable(); 139124961Ssobomax 140124961Ssobomax /* Write LongRun mode values to Model Specific Register. */ 141124961Ssobomax msrinfo.msr = rdmsr(MSR_TMx86_LONGRUN); 142124961Ssobomax msrinfo.regs[0] = LONGRUN_MODE_WRITE(msrinfo.regs[0], 143124961Ssobomax longrun_modes[mode][0]); 144124961Ssobomax msrinfo.regs[1] = LONGRUN_MODE_WRITE(msrinfo.regs[1], 145124961Ssobomax longrun_modes[mode][1]); 146124961Ssobomax wrmsr(MSR_TMx86_LONGRUN, msrinfo.msr); 147124961Ssobomax 148124961Ssobomax /* Write LongRun mode flags to Model Specific Register. */ 149124961Ssobomax msrinfo.msr = rdmsr(MSR_TMx86_LONGRUN_FLAGS); 150124961Ssobomax msrinfo.regs[0] = (msrinfo.regs[0] & ~0x01) | longrun_modes[mode][2]; 151124961Ssobomax wrmsr(MSR_TMx86_LONGRUN_FLAGS, msrinfo.msr); 152124961Ssobomax 153214346Sjhb intr_restore(saveintr); 154124961Ssobomax return (1); 155124961Ssobomax} 156124961Ssobomax 157124961Ssobomaxstatic u_int crusoe_longrun; 158124961Ssobomaxstatic u_int crusoe_frequency; 159124961Ssobomaxstatic u_int crusoe_voltage; 160124961Ssobomaxstatic u_int crusoe_percentage; 161124961Ssobomaxstatic u_int crusoe_performance_longrun = LONGRUN_MODE_PERFORMANCE; 162124961Ssobomaxstatic u_int crusoe_economy_longrun = LONGRUN_MODE_ECONOMY; 163124961Ssobomaxstatic struct sysctl_ctx_list crusoe_sysctl_ctx; 164124961Ssobomaxstatic struct sysctl_oid *crusoe_sysctl_tree; 165124961Ssobomax 166124961Ssobomaxstatic void 167124961Ssobomaxtmx86_longrun_power_profile(void *arg) 168124961Ssobomax{ 169124961Ssobomax int state; 170124961Ssobomax u_int new; 171124961Ssobomax 172124961Ssobomax state = power_profile_get_state(); 173124961Ssobomax if (state != POWER_PROFILE_PERFORMANCE && 174124961Ssobomax state != POWER_PROFILE_ECONOMY) { 175124961Ssobomax return; 176124961Ssobomax } 177124961Ssobomax 178124961Ssobomax switch (state) { 179124961Ssobomax case POWER_PROFILE_PERFORMANCE: 180124961Ssobomax new =crusoe_performance_longrun; 181124961Ssobomax break; 182124961Ssobomax case POWER_PROFILE_ECONOMY: 183124961Ssobomax new = crusoe_economy_longrun; 184124961Ssobomax break; 185124961Ssobomax default: 186124961Ssobomax new = tmx86_get_longrun_mode(); 187124961Ssobomax break; 188124961Ssobomax } 189124961Ssobomax 190124961Ssobomax if (tmx86_get_longrun_mode() != new) { 191124961Ssobomax tmx86_set_longrun_mode(new); 192124961Ssobomax } 193124961Ssobomax} 194124961Ssobomax 195124961Ssobomaxstatic int 196124961Ssobomaxtmx86_longrun_sysctl(SYSCTL_HANDLER_ARGS) 197124961Ssobomax{ 198124961Ssobomax u_int mode; 199124961Ssobomax int error; 200124961Ssobomax 201124961Ssobomax crusoe_longrun = tmx86_get_longrun_mode(); 202124961Ssobomax mode = crusoe_longrun; 203124961Ssobomax error = sysctl_handle_int(oidp, &mode, 0, req); 204124961Ssobomax if (error || !req->newptr) { 205124961Ssobomax return (error); 206124961Ssobomax } 207124961Ssobomax if (mode >= LONGRUN_MODE_UNKNOWN) { 208124961Ssobomax error = EINVAL; 209124961Ssobomax return (error); 210124961Ssobomax } 211124961Ssobomax if (crusoe_longrun != mode) { 212124961Ssobomax crusoe_longrun = mode; 213124961Ssobomax tmx86_set_longrun_mode(crusoe_longrun); 214124961Ssobomax } 215124961Ssobomax 216124961Ssobomax return (error); 217124961Ssobomax} 218124961Ssobomax 219124961Ssobomaxstatic int 220124961Ssobomaxtmx86_status_sysctl(SYSCTL_HANDLER_ARGS) 221124961Ssobomax{ 222124961Ssobomax u_int val; 223124961Ssobomax int error; 224124961Ssobomax 225124961Ssobomax tmx86_get_longrun_status(&crusoe_frequency, 226124961Ssobomax &crusoe_voltage, &crusoe_percentage); 227124961Ssobomax val = *(u_int *)oidp->oid_arg1; 228124961Ssobomax error = sysctl_handle_int(oidp, &val, 0, req); 229124961Ssobomax return (error); 230124961Ssobomax} 231124961Ssobomax 232124961Ssobomaxstatic int 233124961Ssobomaxtmx86_longrun_profile_sysctl(SYSCTL_HANDLER_ARGS) 234124961Ssobomax{ 235124961Ssobomax u_int32_t *argp; 236124961Ssobomax u_int32_t arg; 237124961Ssobomax int error; 238124961Ssobomax 239124961Ssobomax argp = (u_int32_t *)oidp->oid_arg1; 240124961Ssobomax arg = *argp; 241124961Ssobomax error = sysctl_handle_int(oidp, &arg, 0, req); 242124961Ssobomax 243124961Ssobomax /* error or no new value */ 244124961Ssobomax if ((error != 0) || (req->newptr == NULL)) 245124961Ssobomax return (error); 246124961Ssobomax 247124961Ssobomax /* range check */ 248124961Ssobomax if (arg >= LONGRUN_MODE_UNKNOWN) 249124961Ssobomax return (EINVAL); 250124961Ssobomax 251124961Ssobomax /* set new value and possibly switch */ 252124961Ssobomax *argp = arg; 253124961Ssobomax 254124961Ssobomax tmx86_longrun_power_profile(NULL); 255124961Ssobomax 256124961Ssobomax return (0); 257124961Ssobomax 258124961Ssobomax} 259124961Ssobomax 260124961Ssobomaxstatic void 261124961Ssobomaxsetup_tmx86_longrun(void *dummy __unused) 262124961Ssobomax{ 263185341Sjkim 264185341Sjkim if (cpu_vendor_id != CPU_VENDOR_TRANSMETA) 265124961Ssobomax return; 266124961Ssobomax 267124961Ssobomax crusoe_longrun = tmx86_get_longrun_mode(); 268124961Ssobomax tmx86_get_longrun_status(&crusoe_frequency, 269124961Ssobomax &crusoe_voltage, &crusoe_percentage); 270124961Ssobomax printf("Crusoe LongRun support enabled, current mode: %d " 271124961Ssobomax "<%dMHz %dmV %d%%>\n", crusoe_longrun, crusoe_frequency, 272124961Ssobomax crusoe_voltage, crusoe_percentage); 273124961Ssobomax 274124961Ssobomax sysctl_ctx_init(&crusoe_sysctl_ctx); 275124961Ssobomax crusoe_sysctl_tree = SYSCTL_ADD_NODE(&crusoe_sysctl_ctx, 276124961Ssobomax SYSCTL_STATIC_CHILDREN(_hw), OID_AUTO, 277124961Ssobomax "crusoe", CTLFLAG_RD, 0, 278124961Ssobomax "Transmeta Crusoe LongRun support"); 279124961Ssobomax SYSCTL_ADD_PROC(&crusoe_sysctl_ctx, SYSCTL_CHILDREN(crusoe_sysctl_tree), 280124961Ssobomax OID_AUTO, "longrun", CTLTYPE_INT | CTLFLAG_RW, 281124961Ssobomax &crusoe_longrun, 0, tmx86_longrun_sysctl, "I", 282124961Ssobomax "LongRun mode [0-3]"); 283124961Ssobomax SYSCTL_ADD_PROC(&crusoe_sysctl_ctx, SYSCTL_CHILDREN(crusoe_sysctl_tree), 284124961Ssobomax OID_AUTO, "frequency", CTLTYPE_INT | CTLFLAG_RD, 285124961Ssobomax &crusoe_frequency, 0, tmx86_status_sysctl, "I", 286124961Ssobomax "Current frequency (MHz)"); 287124961Ssobomax SYSCTL_ADD_PROC(&crusoe_sysctl_ctx, SYSCTL_CHILDREN(crusoe_sysctl_tree), 288124961Ssobomax OID_AUTO, "voltage", CTLTYPE_INT | CTLFLAG_RD, 289124961Ssobomax &crusoe_voltage, 0, tmx86_status_sysctl, "I", 290124961Ssobomax "Current voltage (mV)"); 291124961Ssobomax SYSCTL_ADD_PROC(&crusoe_sysctl_ctx, SYSCTL_CHILDREN(crusoe_sysctl_tree), 292124961Ssobomax OID_AUTO, "percentage", CTLTYPE_INT | CTLFLAG_RD, 293124961Ssobomax &crusoe_percentage, 0, tmx86_status_sysctl, "I", 294124961Ssobomax "Processing performance (%)"); 295124961Ssobomax SYSCTL_ADD_PROC(&crusoe_sysctl_ctx, SYSCTL_CHILDREN(crusoe_sysctl_tree), 296124961Ssobomax OID_AUTO, "performance_longrun", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_RW, 297124961Ssobomax &crusoe_performance_longrun, 0, tmx86_longrun_profile_sysctl, "I", ""); 298124961Ssobomax SYSCTL_ADD_PROC(&crusoe_sysctl_ctx, SYSCTL_CHILDREN(crusoe_sysctl_tree), 299124961Ssobomax OID_AUTO, "economy_longrun", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_RW, 300124961Ssobomax &crusoe_economy_longrun, 0, tmx86_longrun_profile_sysctl, "I", ""); 301124961Ssobomax 302124961Ssobomax /* register performance profile change handler */ 303124961Ssobomax EVENTHANDLER_REGISTER(power_profile_change, tmx86_longrun_power_profile, NULL, 0); 304124961Ssobomax} 305124961SsobomaxSYSINIT(setup_tmx86_longrun, SI_SUB_CPU, SI_ORDER_ANY, setup_tmx86_longrun, 306124961Ssobomax NULL); 307