Home | History | Annotate | Line # | Download | only in i386
      1 /*	$NetBSD: longrun.c,v 1.4 2011/10/23 13:02:32 jmcneill Exp $	*/
      2 
      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 #include <sys/cdefs.h>
     38 __KERNEL_RCSID(0, "$NetBSD: longrun.c,v 1.4 2011/10/23 13:02:32 jmcneill Exp $");
     39 
     40 #include <sys/param.h>
     41 #include <sys/systm.h>
     42 #include <sys/kernel.h>
     43 #include <sys/types.h>
     44 #include <sys/sysctl.h>
     45 
     46 #include <machine/specialreg.h>
     47 #include <machine/cpu.h>
     48 
     49 /*
     50  * Transmeta Crusoe LongRun Support by Tamotsu Hattori.
     51  * Port from FreeBSD-current(August, 2001) to NetBSD by tshiozak.
     52  */
     53 
     54 #define	MSR_TMx86_LONGRUN		0x80868010
     55 #define	MSR_TMx86_LONGRUN_FLAGS		0x80868011
     56 
     57 #define	LONGRUN_MODE_MASK(x)		((x) & 0x0000007f)
     58 #define	LONGRUN_MODE_RESERVED(x)	((x) & 0xffffff80)
     59 #define	LONGRUN_MODE_WRITE(x, y)	(LONGRUN_MODE_RESERVED(x) | \
     60 					    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 
     69 union msrinfo {
     70 	uint64_t	msr;
     71 	uint32_t	regs[2];
     72 };
     73 
     74 static u_int crusoe_longrun = 0;
     75 static u_int crusoe_frequency = 0;
     76 static u_int crusoe_voltage = 0;
     77 static u_int crusoe_percentage = 0;
     78 
     79 static const uint32_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 tmx86_set_longrun_mode(u_int);
     88 static void tmx86_get_longrun_status_all(void);
     89 static int sysctl_machdep_tm_longrun(SYSCTLFN_PROTO);
     90 
     91 void
     92 tmx86_init_longrun(void)
     93 {
     94 	const struct sysctlnode *mnode;
     95 	uint64_t msr;
     96 
     97 	/* PR #32894, make sure the longrun MSR is present */
     98 	if (rdmsr_safe(MSR_TMx86_LONGRUN, &msr) == EFAULT)
     99 		return;
    100 
    101 	/* create the sysctl machdep.tm_longrun_* nodes */
    102         sysctl_createv(NULL, 0, NULL, &mnode, CTLFLAG_PERMANENT,
    103 		       CTLTYPE_NODE, "machdep", NULL,
    104 		       NULL, 0, NULL, 0,
    105 		       CTL_MACHDEP, CTL_EOL);
    106 
    107         sysctl_createv(NULL, 0, NULL, NULL, CTLFLAG_READWRITE,
    108 		       CTLTYPE_INT, "tm_longrun_mode", NULL,
    109 		       sysctl_machdep_tm_longrun, 0, NULL, 0,
    110 		       CTL_MACHDEP, CPU_TMLR_MODE, CTL_EOL);
    111 
    112 	sysctl_createv(NULL, 0, NULL, NULL, 0,
    113 		       CTLTYPE_INT, "tm_longrun_frequency", NULL,
    114 		       sysctl_machdep_tm_longrun, 0, NULL, 0,
    115 		       CTL_MACHDEP, CPU_TMLR_FREQUENCY, CTL_EOL);
    116 
    117 	sysctl_createv(NULL, 0, NULL, NULL, 0,
    118 		       CTLTYPE_INT, "tm_longrun_voltage", NULL,
    119 		       sysctl_machdep_tm_longrun, 0, NULL, 0,
    120 		       CTL_MACHDEP, CPU_TMLR_VOLTAGE, CTL_EOL);
    121 
    122 	sysctl_createv(NULL, 0, NULL, NULL, 0,
    123 		       CTLTYPE_INT, "tm_longrun_percentage", NULL,
    124 		       sysctl_machdep_tm_longrun, 0, NULL, 0,
    125 		       CTL_MACHDEP, CPU_TMLR_PERCENTAGE, CTL_EOL);
    126 }
    127 
    128 u_int
    129 tmx86_get_longrun_mode(void)
    130 {
    131 	u_long		eflags;
    132 	union msrinfo	msrinfo;
    133 	u_int		low, high, flags, mode;
    134 
    135 	eflags = x86_read_psl();
    136 	x86_disable_intr();
    137 
    138 	msrinfo.msr = rdmsr(MSR_TMx86_LONGRUN);
    139 	low = LONGRUN_MODE_MASK(msrinfo.regs[0]);
    140 	high = LONGRUN_MODE_MASK(msrinfo.regs[1]);
    141 	flags = rdmsr(MSR_TMx86_LONGRUN_FLAGS) & 0x01;
    142 
    143 	for (mode = 0; mode < LONGRUN_MODE_MAX; mode++) {
    144 		if (low   == longrun_modes[mode][0] &&
    145 		    high  == longrun_modes[mode][1] &&
    146 		    flags == longrun_modes[mode][2]) {
    147 			goto out;
    148 		}
    149 	}
    150 	mode = LONGRUN_MODE_UNKNOWN;
    151 out:
    152 	x86_write_psl(eflags);
    153 	return mode;
    154 }
    155 
    156 void
    157 tmx86_get_longrun_status(u_int *frequency, u_int *voltage, u_int *percentage)
    158 {
    159 	u_long eflags;
    160 	u_int descs[4];
    161 
    162 	eflags = x86_read_psl();
    163 	x86_disable_intr();
    164 
    165 	x86_cpuid(0x80860007, descs);
    166 	*frequency = descs[0];
    167 	*voltage = descs[1];
    168 	*percentage = descs[2];
    169 
    170 	x86_write_psl(eflags);
    171 }
    172 
    173 static u_int
    174 tmx86_set_longrun_mode(u_int mode)
    175 {
    176 	u_long		eflags;
    177 	union msrinfo	msrinfo;
    178 
    179 	if (mode >= LONGRUN_MODE_UNKNOWN)
    180 		return 0;
    181 
    182 	eflags = x86_read_psl();
    183 	x86_disable_intr();
    184 
    185 	/* Write LongRun mode values to Model Specific Register. */
    186 	msrinfo.msr = rdmsr(MSR_TMx86_LONGRUN);
    187 	msrinfo.regs[0] = LONGRUN_MODE_WRITE(msrinfo.regs[0],
    188 	    longrun_modes[mode][0]);
    189 	msrinfo.regs[1] = LONGRUN_MODE_WRITE(msrinfo.regs[1],
    190 	    longrun_modes[mode][1]);
    191 	wrmsr(MSR_TMx86_LONGRUN, msrinfo.msr);
    192 
    193 	/* Write LongRun mode flags to Model Specific Register. */
    194 	msrinfo.msr = rdmsr(MSR_TMx86_LONGRUN_FLAGS);
    195 	msrinfo.regs[0] = (msrinfo.regs[0] & ~0x01) | longrun_modes[mode][2];
    196 	wrmsr(MSR_TMx86_LONGRUN_FLAGS, msrinfo.msr);
    197 
    198 	x86_write_psl(eflags);
    199 	return 1;
    200 }
    201 
    202 static void
    203 tmx86_get_longrun_status_all(void)
    204 {
    205 
    206 	tmx86_get_longrun_status(&crusoe_frequency,
    207 	    &crusoe_voltage, &crusoe_percentage);
    208 }
    209 
    210 /*
    211  * sysctl helper routine for machdep.tm* nodes.
    212  */
    213 static int
    214 sysctl_machdep_tm_longrun(SYSCTLFN_ARGS)
    215 {
    216 	struct sysctlnode node;
    217 	int io, error;
    218 
    219 	node = *rnode;
    220 	node.sysctl_data = &io;
    221 
    222 	switch (rnode->sysctl_num) {
    223 	case CPU_TMLR_MODE:
    224 		io = (int)(crusoe_longrun = tmx86_get_longrun_mode());
    225 		break;
    226 	case CPU_TMLR_FREQUENCY:
    227 		tmx86_get_longrun_status_all();
    228 		io = crusoe_frequency;
    229 		break;
    230 	case CPU_TMLR_VOLTAGE:
    231 		tmx86_get_longrun_status_all();
    232 		io = crusoe_voltage;
    233 		break;
    234 	case CPU_TMLR_PERCENTAGE:
    235 		tmx86_get_longrun_status_all();
    236 		io = crusoe_percentage;
    237 		break;
    238 	default:
    239 		return EOPNOTSUPP;
    240 	}
    241 
    242 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
    243 	if (error || newp == NULL)
    244 		return error;
    245 
    246 	if (rnode->sysctl_num == CPU_TMLR_MODE) {
    247 		if (tmx86_set_longrun_mode(io))
    248 			crusoe_longrun = (u_int)io;
    249 		else
    250 			return EINVAL;
    251 	}
    252 
    253 	return 0;
    254 }
    255