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