xref: /netbsd/sys/arch/i386/i386/longrun.c (revision 72521d3d)
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
tmx86_init_longrun(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
tmx86_get_longrun_mode(void)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
tmx86_get_longrun_status(u_int * frequency,u_int * voltage,u_int * percentage)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
tmx86_set_longrun_mode(u_int mode)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
tmx86_get_longrun_status_all(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
sysctl_machdep_tm_longrun(SYSCTLFN_ARGS)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