xref: /netbsd/sys/arch/i386/i386/longrun.c (revision 6550d01e)
1 /*	$NetBSD: longrun.c,v 1.3 2008/02/27 20:18:56 xtraeme 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.3 2008/02/27 20:18:56 xtraeme 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 
96 	/* create the sysctl machdep.tm_longrun_* nodes */
97         sysctl_createv(NULL, 0, NULL, &mnode, CTLFLAG_PERMANENT,
98 		       CTLTYPE_NODE, "machdep", NULL,
99 		       NULL, 0, NULL, 0,
100 		       CTL_MACHDEP, CTL_EOL);
101 
102         sysctl_createv(NULL, 0, NULL, NULL, CTLFLAG_READWRITE,
103 		       CTLTYPE_INT, "tm_longrun_mode", NULL,
104 		       sysctl_machdep_tm_longrun, 0, NULL, 0,
105 		       CTL_MACHDEP, CPU_TMLR_MODE, CTL_EOL);
106 
107 	sysctl_createv(NULL, 0, NULL, NULL, 0,
108 		       CTLTYPE_INT, "tm_longrun_frequency", NULL,
109 		       sysctl_machdep_tm_longrun, 0, NULL, 0,
110 		       CTL_MACHDEP, CPU_TMLR_FREQUENCY, CTL_EOL);
111 
112 	sysctl_createv(NULL, 0, NULL, NULL, 0,
113 		       CTLTYPE_INT, "tm_longrun_voltage", NULL,
114 		       sysctl_machdep_tm_longrun, 0, NULL, 0,
115 		       CTL_MACHDEP, CPU_TMLR_VOLTAGE, CTL_EOL);
116 
117 	sysctl_createv(NULL, 0, NULL, NULL, 0,
118 		       CTLTYPE_INT, "tm_longrun_percentage", NULL,
119 		       sysctl_machdep_tm_longrun, 0, NULL, 0,
120 		       CTL_MACHDEP, CPU_TMLR_PERCENTAGE, CTL_EOL);
121 }
122 
123 u_int
124 tmx86_get_longrun_mode(void)
125 {
126 	u_long		eflags;
127 	union msrinfo	msrinfo;
128 	u_int		low, high, flags, mode;
129 
130 	eflags = x86_read_psl();
131 	x86_disable_intr();
132 
133 	msrinfo.msr = rdmsr(MSR_TMx86_LONGRUN);
134 	low = LONGRUN_MODE_MASK(msrinfo.regs[0]);
135 	high = LONGRUN_MODE_MASK(msrinfo.regs[1]);
136 	flags = rdmsr(MSR_TMx86_LONGRUN_FLAGS) & 0x01;
137 
138 	for (mode = 0; mode < LONGRUN_MODE_MAX; mode++) {
139 		if (low   == longrun_modes[mode][0] &&
140 		    high  == longrun_modes[mode][1] &&
141 		    flags == longrun_modes[mode][2]) {
142 			goto out;
143 		}
144 	}
145 	mode = LONGRUN_MODE_UNKNOWN;
146 out:
147 	x86_write_psl(eflags);
148 	return mode;
149 }
150 
151 void
152 tmx86_get_longrun_status(u_int *frequency, u_int *voltage, u_int *percentage)
153 {
154 	u_long eflags;
155 	u_int descs[4];
156 
157 	eflags = x86_read_psl();
158 	x86_disable_intr();
159 
160 	x86_cpuid(0x80860007, descs);
161 	*frequency = descs[0];
162 	*voltage = descs[1];
163 	*percentage = descs[2];
164 
165 	x86_write_psl(eflags);
166 }
167 
168 static u_int
169 tmx86_set_longrun_mode(u_int mode)
170 {
171 	u_long		eflags;
172 	union msrinfo	msrinfo;
173 
174 	if (mode >= LONGRUN_MODE_UNKNOWN)
175 		return 0;
176 
177 	eflags = x86_read_psl();
178 	x86_disable_intr();
179 
180 	/* Write LongRun mode values to Model Specific Register. */
181 	msrinfo.msr = rdmsr(MSR_TMx86_LONGRUN);
182 	msrinfo.regs[0] = LONGRUN_MODE_WRITE(msrinfo.regs[0],
183 	    longrun_modes[mode][0]);
184 	msrinfo.regs[1] = LONGRUN_MODE_WRITE(msrinfo.regs[1],
185 	    longrun_modes[mode][1]);
186 	wrmsr(MSR_TMx86_LONGRUN, msrinfo.msr);
187 
188 	/* Write LongRun mode flags to Model Specific Register. */
189 	msrinfo.msr = rdmsr(MSR_TMx86_LONGRUN_FLAGS);
190 	msrinfo.regs[0] = (msrinfo.regs[0] & ~0x01) | longrun_modes[mode][2];
191 	wrmsr(MSR_TMx86_LONGRUN_FLAGS, msrinfo.msr);
192 
193 	x86_write_psl(eflags);
194 	return 1;
195 }
196 
197 static void
198 tmx86_get_longrun_status_all(void)
199 {
200 
201 	tmx86_get_longrun_status(&crusoe_frequency,
202 	    &crusoe_voltage, &crusoe_percentage);
203 }
204 
205 /*
206  * sysctl helper routine for machdep.tm* nodes.
207  */
208 static int
209 sysctl_machdep_tm_longrun(SYSCTLFN_ARGS)
210 {
211 	struct sysctlnode node;
212 	int io, error;
213 
214 	node = *rnode;
215 	node.sysctl_data = &io;
216 
217 	switch (rnode->sysctl_num) {
218 	case CPU_TMLR_MODE:
219 		io = (int)(crusoe_longrun = tmx86_get_longrun_mode());
220 		break;
221 	case CPU_TMLR_FREQUENCY:
222 		tmx86_get_longrun_status_all();
223 		io = crusoe_frequency;
224 		break;
225 	case CPU_TMLR_VOLTAGE:
226 		tmx86_get_longrun_status_all();
227 		io = crusoe_voltage;
228 		break;
229 	case CPU_TMLR_PERCENTAGE:
230 		tmx86_get_longrun_status_all();
231 		io = crusoe_percentage;
232 		break;
233 	default:
234 		return EOPNOTSUPP;
235 	}
236 
237 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
238 	if (error || newp == NULL)
239 		return error;
240 
241 	if (rnode->sysctl_num == CPU_TMLR_MODE) {
242 		if (tmx86_set_longrun_mode(io))
243 			crusoe_longrun = (u_int)io;
244 		else
245 			return EINVAL;
246 	}
247 
248 	return 0;
249 }
250