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