xref: /netbsd/sys/arch/i386/i386/longrun.c (revision 72521d3d)
1*72521d3dSjmcneill /*	$NetBSD: longrun.c,v 1.4 2011/10/23 13:02:32 jmcneill Exp $	*/
2c524510bSxtraeme 
3c524510bSxtraeme /*-
4c524510bSxtraeme  * Copyright (c) 2001 Tamotsu Hattori.
5c524510bSxtraeme  * Copyright (c) 2001 Mitsuru IWASAKI.
6c524510bSxtraeme  * All rights reserved.
7c524510bSxtraeme  *
8c524510bSxtraeme  * Redistribution and use in source and binary forms, with or without
9c524510bSxtraeme  * modification, are permitted provided that the following conditions
10c524510bSxtraeme  * are met:
11c524510bSxtraeme  * 1. Redistributions of source code must retain the above copyright
12c524510bSxtraeme  *    notice, this list of conditions and the following disclaimer.
13c524510bSxtraeme  * 2. Redistributions in binary form must reproduce the above copyright
14c524510bSxtraeme  *    notice, this list of conditions and the following disclaimer in the
15c524510bSxtraeme  *    documentation and/or other materials provided with the distribution.
16c524510bSxtraeme  * 3. All advertising materials mentioning features or use of this software
17c524510bSxtraeme  *    must display the following acknowledgement:
18c524510bSxtraeme  *	This product includes software developed by the University of
19c524510bSxtraeme  *	California, Berkeley and its contributors.
20c524510bSxtraeme  * 4. Neither the name of the University nor the names of its contributors
21c524510bSxtraeme  *    may be used to endorse or promote products derived from this software
22c524510bSxtraeme  *    without specific prior written permission.
23c524510bSxtraeme  *
24c524510bSxtraeme  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25c524510bSxtraeme  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26c524510bSxtraeme  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27c524510bSxtraeme  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28c524510bSxtraeme  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29c524510bSxtraeme  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30c524510bSxtraeme  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31c524510bSxtraeme  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32c524510bSxtraeme  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33c524510bSxtraeme  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34c524510bSxtraeme  * SUCH DAMAGE.
35c524510bSxtraeme  *
36c524510bSxtraeme  */
37c524510bSxtraeme #include <sys/cdefs.h>
38*72521d3dSjmcneill __KERNEL_RCSID(0, "$NetBSD: longrun.c,v 1.4 2011/10/23 13:02:32 jmcneill Exp $");
39c524510bSxtraeme 
40c524510bSxtraeme #include <sys/param.h>
41c524510bSxtraeme #include <sys/systm.h>
42c524510bSxtraeme #include <sys/kernel.h>
43c524510bSxtraeme #include <sys/types.h>
44c524510bSxtraeme #include <sys/sysctl.h>
45c524510bSxtraeme 
46c524510bSxtraeme #include <machine/specialreg.h>
47c524510bSxtraeme #include <machine/cpu.h>
48c524510bSxtraeme 
49c524510bSxtraeme /*
50c524510bSxtraeme  * Transmeta Crusoe LongRun Support by Tamotsu Hattori.
51c524510bSxtraeme  * Port from FreeBSD-current(August, 2001) to NetBSD by tshiozak.
52c524510bSxtraeme  */
53c524510bSxtraeme 
54c524510bSxtraeme #define	MSR_TMx86_LONGRUN		0x80868010
55c524510bSxtraeme #define	MSR_TMx86_LONGRUN_FLAGS		0x80868011
56c524510bSxtraeme 
57c524510bSxtraeme #define	LONGRUN_MODE_MASK(x)		((x) & 0x0000007f)
58c524510bSxtraeme #define	LONGRUN_MODE_RESERVED(x)	((x) & 0xffffff80)
59c524510bSxtraeme #define	LONGRUN_MODE_WRITE(x, y)	(LONGRUN_MODE_RESERVED(x) | \
60c524510bSxtraeme 					    LONGRUN_MODE_MASK(y))
61c524510bSxtraeme 
62c524510bSxtraeme #define	LONGRUN_MODE_MINFREQUENCY	0x00
63c524510bSxtraeme #define	LONGRUN_MODE_ECONOMY		0x01
64c524510bSxtraeme #define	LONGRUN_MODE_PERFORMANCE	0x02
65c524510bSxtraeme #define	LONGRUN_MODE_MAXFREQUENCY	0x03
66c524510bSxtraeme #define	LONGRUN_MODE_UNKNOWN		0x04
67c524510bSxtraeme #define	LONGRUN_MODE_MAX		0x04
68c524510bSxtraeme 
69c524510bSxtraeme union msrinfo {
70c524510bSxtraeme 	uint64_t	msr;
71c524510bSxtraeme 	uint32_t	regs[2];
72c524510bSxtraeme };
73c524510bSxtraeme 
74c524510bSxtraeme static u_int crusoe_longrun = 0;
75c524510bSxtraeme static u_int crusoe_frequency = 0;
76c524510bSxtraeme static u_int crusoe_voltage = 0;
77c524510bSxtraeme static u_int crusoe_percentage = 0;
78c524510bSxtraeme 
79c524510bSxtraeme static const uint32_t longrun_modes[LONGRUN_MODE_MAX][3] = {
80c524510bSxtraeme 	/*  MSR low, MSR high, flags bit0 */
81c524510bSxtraeme 	{	  0,	  0,		0},	/* LONGRUN_MODE_MINFREQUENCY */
82c524510bSxtraeme 	{	  0,	100,		0},	/* LONGRUN_MODE_ECONOMY */
83c524510bSxtraeme 	{	  0,	100,		1},	/* LONGRUN_MODE_PERFORMANCE */
84c524510bSxtraeme 	{	100,	100,		1},	/* LONGRUN_MODE_MAXFREQUENCY */
85c524510bSxtraeme };
86c524510bSxtraeme 
87c524510bSxtraeme static u_int tmx86_set_longrun_mode(u_int);
88c524510bSxtraeme static void tmx86_get_longrun_status_all(void);
89c524510bSxtraeme static int sysctl_machdep_tm_longrun(SYSCTLFN_PROTO);
90c524510bSxtraeme 
91c524510bSxtraeme void
tmx86_init_longrun(void)92c524510bSxtraeme tmx86_init_longrun(void)
93c524510bSxtraeme {
944ac581c0Sxtraeme 	const struct sysctlnode *mnode;
95*72521d3dSjmcneill 	uint64_t msr;
96*72521d3dSjmcneill 
97*72521d3dSjmcneill 	/* PR #32894, make sure the longrun MSR is present */
98*72521d3dSjmcneill 	if (rdmsr_safe(MSR_TMx86_LONGRUN, &msr) == EFAULT)
99*72521d3dSjmcneill 		return;
100e4a52ebbSxtraeme 
101c524510bSxtraeme 	/* create the sysctl machdep.tm_longrun_* nodes */
102e4a52ebbSxtraeme         sysctl_createv(NULL, 0, NULL, &mnode, CTLFLAG_PERMANENT,
103c524510bSxtraeme 		       CTLTYPE_NODE, "machdep", NULL,
104c524510bSxtraeme 		       NULL, 0, NULL, 0,
105c524510bSxtraeme 		       CTL_MACHDEP, CTL_EOL);
106e4a52ebbSxtraeme 
1074ac581c0Sxtraeme         sysctl_createv(NULL, 0, NULL, NULL, CTLFLAG_READWRITE,
108c524510bSxtraeme 		       CTLTYPE_INT, "tm_longrun_mode", NULL,
109c524510bSxtraeme 		       sysctl_machdep_tm_longrun, 0, NULL, 0,
1104ac581c0Sxtraeme 		       CTL_MACHDEP, CPU_TMLR_MODE, CTL_EOL);
111e4a52ebbSxtraeme 
1124ac581c0Sxtraeme 	sysctl_createv(NULL, 0, NULL, NULL, 0,
113c524510bSxtraeme 		       CTLTYPE_INT, "tm_longrun_frequency", NULL,
114c524510bSxtraeme 		       sysctl_machdep_tm_longrun, 0, NULL, 0,
1154ac581c0Sxtraeme 		       CTL_MACHDEP, CPU_TMLR_FREQUENCY, CTL_EOL);
116e4a52ebbSxtraeme 
1174ac581c0Sxtraeme 	sysctl_createv(NULL, 0, NULL, NULL, 0,
118c524510bSxtraeme 		       CTLTYPE_INT, "tm_longrun_voltage", NULL,
119c524510bSxtraeme 		       sysctl_machdep_tm_longrun, 0, NULL, 0,
1204ac581c0Sxtraeme 		       CTL_MACHDEP, CPU_TMLR_VOLTAGE, CTL_EOL);
121e4a52ebbSxtraeme 
1224ac581c0Sxtraeme 	sysctl_createv(NULL, 0, NULL, NULL, 0,
123c524510bSxtraeme 		       CTLTYPE_INT, "tm_longrun_percentage", NULL,
124c524510bSxtraeme 		       sysctl_machdep_tm_longrun, 0, NULL, 0,
1254ac581c0Sxtraeme 		       CTL_MACHDEP, CPU_TMLR_PERCENTAGE, CTL_EOL);
126c524510bSxtraeme }
127c524510bSxtraeme 
128c524510bSxtraeme u_int
tmx86_get_longrun_mode(void)129c524510bSxtraeme tmx86_get_longrun_mode(void)
130c524510bSxtraeme {
131c524510bSxtraeme 	u_long		eflags;
132c524510bSxtraeme 	union msrinfo	msrinfo;
133c524510bSxtraeme 	u_int		low, high, flags, mode;
134c524510bSxtraeme 
135c524510bSxtraeme 	eflags = x86_read_psl();
136c524510bSxtraeme 	x86_disable_intr();
137c524510bSxtraeme 
138c524510bSxtraeme 	msrinfo.msr = rdmsr(MSR_TMx86_LONGRUN);
139c524510bSxtraeme 	low = LONGRUN_MODE_MASK(msrinfo.regs[0]);
140c524510bSxtraeme 	high = LONGRUN_MODE_MASK(msrinfo.regs[1]);
141c524510bSxtraeme 	flags = rdmsr(MSR_TMx86_LONGRUN_FLAGS) & 0x01;
142c524510bSxtraeme 
143c524510bSxtraeme 	for (mode = 0; mode < LONGRUN_MODE_MAX; mode++) {
144c524510bSxtraeme 		if (low   == longrun_modes[mode][0] &&
145c524510bSxtraeme 		    high  == longrun_modes[mode][1] &&
146c524510bSxtraeme 		    flags == longrun_modes[mode][2]) {
147c524510bSxtraeme 			goto out;
148c524510bSxtraeme 		}
149c524510bSxtraeme 	}
150c524510bSxtraeme 	mode = LONGRUN_MODE_UNKNOWN;
151c524510bSxtraeme out:
152c524510bSxtraeme 	x86_write_psl(eflags);
153c524510bSxtraeme 	return mode;
154c524510bSxtraeme }
155c524510bSxtraeme 
156c524510bSxtraeme void
tmx86_get_longrun_status(u_int * frequency,u_int * voltage,u_int * percentage)157c524510bSxtraeme tmx86_get_longrun_status(u_int *frequency, u_int *voltage, u_int *percentage)
158c524510bSxtraeme {
159c524510bSxtraeme 	u_long eflags;
160c524510bSxtraeme 	u_int descs[4];
161c524510bSxtraeme 
162c524510bSxtraeme 	eflags = x86_read_psl();
163c524510bSxtraeme 	x86_disable_intr();
164c524510bSxtraeme 
165c524510bSxtraeme 	x86_cpuid(0x80860007, descs);
166c524510bSxtraeme 	*frequency = descs[0];
167c524510bSxtraeme 	*voltage = descs[1];
168c524510bSxtraeme 	*percentage = descs[2];
169c524510bSxtraeme 
170c524510bSxtraeme 	x86_write_psl(eflags);
171c524510bSxtraeme }
172c524510bSxtraeme 
173c524510bSxtraeme static u_int
tmx86_set_longrun_mode(u_int mode)174c524510bSxtraeme tmx86_set_longrun_mode(u_int mode)
175c524510bSxtraeme {
176c524510bSxtraeme 	u_long		eflags;
177c524510bSxtraeme 	union msrinfo	msrinfo;
178c524510bSxtraeme 
179c524510bSxtraeme 	if (mode >= LONGRUN_MODE_UNKNOWN)
180c524510bSxtraeme 		return 0;
181c524510bSxtraeme 
182c524510bSxtraeme 	eflags = x86_read_psl();
183c524510bSxtraeme 	x86_disable_intr();
184c524510bSxtraeme 
185c524510bSxtraeme 	/* Write LongRun mode values to Model Specific Register. */
186c524510bSxtraeme 	msrinfo.msr = rdmsr(MSR_TMx86_LONGRUN);
187c524510bSxtraeme 	msrinfo.regs[0] = LONGRUN_MODE_WRITE(msrinfo.regs[0],
188c524510bSxtraeme 	    longrun_modes[mode][0]);
189c524510bSxtraeme 	msrinfo.regs[1] = LONGRUN_MODE_WRITE(msrinfo.regs[1],
190c524510bSxtraeme 	    longrun_modes[mode][1]);
191c524510bSxtraeme 	wrmsr(MSR_TMx86_LONGRUN, msrinfo.msr);
192c524510bSxtraeme 
193c524510bSxtraeme 	/* Write LongRun mode flags to Model Specific Register. */
194c524510bSxtraeme 	msrinfo.msr = rdmsr(MSR_TMx86_LONGRUN_FLAGS);
195c524510bSxtraeme 	msrinfo.regs[0] = (msrinfo.regs[0] & ~0x01) | longrun_modes[mode][2];
196c524510bSxtraeme 	wrmsr(MSR_TMx86_LONGRUN_FLAGS, msrinfo.msr);
197c524510bSxtraeme 
198c524510bSxtraeme 	x86_write_psl(eflags);
199c524510bSxtraeme 	return 1;
200c524510bSxtraeme }
201c524510bSxtraeme 
202c524510bSxtraeme static void
tmx86_get_longrun_status_all(void)203c524510bSxtraeme tmx86_get_longrun_status_all(void)
204c524510bSxtraeme {
205c524510bSxtraeme 
206c524510bSxtraeme 	tmx86_get_longrun_status(&crusoe_frequency,
207c524510bSxtraeme 	    &crusoe_voltage, &crusoe_percentage);
208c524510bSxtraeme }
209c524510bSxtraeme 
210c524510bSxtraeme /*
211c524510bSxtraeme  * sysctl helper routine for machdep.tm* nodes.
212c524510bSxtraeme  */
213c524510bSxtraeme static int
sysctl_machdep_tm_longrun(SYSCTLFN_ARGS)214c524510bSxtraeme sysctl_machdep_tm_longrun(SYSCTLFN_ARGS)
215c524510bSxtraeme {
216c524510bSxtraeme 	struct sysctlnode node;
217c524510bSxtraeme 	int io, error;
218c524510bSxtraeme 
219c524510bSxtraeme 	node = *rnode;
220c524510bSxtraeme 	node.sysctl_data = &io;
221c524510bSxtraeme 
2224ac581c0Sxtraeme 	switch (rnode->sysctl_num) {
2234ac581c0Sxtraeme 	case CPU_TMLR_MODE:
224c524510bSxtraeme 		io = (int)(crusoe_longrun = tmx86_get_longrun_mode());
2254ac581c0Sxtraeme 		break;
2264ac581c0Sxtraeme 	case CPU_TMLR_FREQUENCY:
227c524510bSxtraeme 		tmx86_get_longrun_status_all();
228c524510bSxtraeme 		io = crusoe_frequency;
2294ac581c0Sxtraeme 		break;
2304ac581c0Sxtraeme 	case CPU_TMLR_VOLTAGE:
231c524510bSxtraeme 		tmx86_get_longrun_status_all();
232c524510bSxtraeme 		io = crusoe_voltage;
2334ac581c0Sxtraeme 		break;
2344ac581c0Sxtraeme 	case CPU_TMLR_PERCENTAGE:
235c524510bSxtraeme 		tmx86_get_longrun_status_all();
236c524510bSxtraeme 		io = crusoe_percentage;
2374ac581c0Sxtraeme 		break;
2384ac581c0Sxtraeme 	default:
239c524510bSxtraeme 		return EOPNOTSUPP;
240c524510bSxtraeme 	}
241c524510bSxtraeme 
242c524510bSxtraeme 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
243c524510bSxtraeme 	if (error || newp == NULL)
244c524510bSxtraeme 		return error;
245c524510bSxtraeme 
2464ac581c0Sxtraeme 	if (rnode->sysctl_num == CPU_TMLR_MODE) {
247c524510bSxtraeme 		if (tmx86_set_longrun_mode(io))
248c524510bSxtraeme 			crusoe_longrun = (u_int)io;
249c524510bSxtraeme 		else
250c524510bSxtraeme 			return EINVAL;
251c524510bSxtraeme 	}
252c524510bSxtraeme 
253c524510bSxtraeme 	return 0;
254c524510bSxtraeme }
255