Initial import of binutils 2.22 on the new vendor branch
[dragonfly.git] / sys / dev / powermng / longrun / longrun.c
1 /*-
2  * Copyright (c) 2001 Tamotsu Hattori.
3  * Copyright (c) 2001 Mitsuru IWASAKI.
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. All advertising materials mentioning features or use of this software
15  *    must display the following acknowledgement:
16  *      This product includes software developed by the University of
17  *      California, Berkeley and its contributors.
18  * 4. Neither the name of the University nor the names of its contributors
19  *    may be used to endorse or promote products derived from this software
20  *    without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  *
34  * $FreeBSD: src/sys/i386/i386/longrun.c,v 1.5 2010/10/25 15:28:03 jhb Exp $
35  */
36
37 #include "opt_cpu.h"
38
39 #include <sys/param.h>
40 #include <sys/systm.h>
41 #include <sys/kernel.h>
42 #include <sys/conf.h>
43 #include <sys/power.h>
44 #include <sys/sysctl.h>
45 #include <sys/types.h>
46
47 #include <machine/cputypes.h>
48 #include <machine/md_var.h>
49 #include <machine/specialreg.h>
50
51 /*
52  * Transmeta Crusoe LongRun Support by Tamotsu Hattori.
53  */
54
55 #define MSR_TMx86_LONGRUN               0x80868010
56 #define MSR_TMx86_LONGRUN_FLAGS         0x80868011
57
58 #define LONGRUN_MODE_MASK(x)            ((x) & 0x000000007f)
59 #define LONGRUN_MODE_RESERVED(x)        ((x) & 0xffffff80)
60 #define LONGRUN_MODE_WRITE(x, y)        (LONGRUN_MODE_RESERVED(x) | 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         u_int64_t       msr;
71         u_int32_t       regs[2];
72 };
73
74 static u_int32_t longrun_modes[LONGRUN_MODE_MAX][3] = {
75         /*  MSR low, MSR high, flags bit0 */
76         {         0,      0,            0},     /* LONGRUN_MODE_MINFREQUENCY */
77         {         0,    100,            0},     /* LONGRUN_MODE_ECONOMY */
78         {         0,    100,            1},     /* LONGRUN_MODE_PERFORMANCE */
79         {       100,    100,            1},     /* LONGRUN_MODE_MAXFREQUENCY */
80 };
81
82 static u_int
83 tmx86_get_longrun_mode(void)
84 {
85         union msrinfo   msrinfo;
86         u_int           low, high, flags, mode;
87
88         mpintr_lock();
89
90         msrinfo.msr = rdmsr(MSR_TMx86_LONGRUN);
91         low = LONGRUN_MODE_MASK(msrinfo.regs[0]);
92         high = LONGRUN_MODE_MASK(msrinfo.regs[1]);
93         flags = rdmsr(MSR_TMx86_LONGRUN_FLAGS) & 0x01;
94
95         for (mode = 0; mode < LONGRUN_MODE_MAX; mode++) {
96                 if (low   == longrun_modes[mode][0] &&
97                     high  == longrun_modes[mode][1] &&
98                     flags == longrun_modes[mode][2]) {
99                         goto out;
100                 }
101         }
102         mode = LONGRUN_MODE_UNKNOWN;
103 out:
104         mpintr_unlock();
105         return (mode);
106 }
107
108 static u_int
109 tmx86_get_longrun_status(u_int * frequency, u_int * voltage, u_int * percentage)
110 {
111         u_int           regs[4];
112
113         mpintr_lock();
114
115         do_cpuid(0x80860007, regs);
116         *frequency = regs[0];
117         *voltage = regs[1];
118         *percentage = regs[2];
119
120         mpintr_unlock();
121         return (1);
122 }
123
124 static u_int
125 tmx86_set_longrun_mode(u_int mode)
126 {
127         union msrinfo   msrinfo;
128
129         if (mode >= LONGRUN_MODE_UNKNOWN) {
130                 return (0);
131         }
132
133         mpintr_lock();
134
135         /* Write LongRun mode values to Model Specific Register. */
136         msrinfo.msr = rdmsr(MSR_TMx86_LONGRUN);
137         msrinfo.regs[0] = LONGRUN_MODE_WRITE(msrinfo.regs[0],
138                                              longrun_modes[mode][0]);
139         msrinfo.regs[1] = LONGRUN_MODE_WRITE(msrinfo.regs[1],
140                                              longrun_modes[mode][1]);
141         wrmsr(MSR_TMx86_LONGRUN, msrinfo.msr);
142
143         /* Write LongRun mode flags to Model Specific Register. */
144         msrinfo.msr = rdmsr(MSR_TMx86_LONGRUN_FLAGS);
145         msrinfo.regs[0] = (msrinfo.regs[0] & ~0x01) | longrun_modes[mode][2];
146         wrmsr(MSR_TMx86_LONGRUN_FLAGS, msrinfo.msr);
147
148         mpintr_unlock();
149         return (1);
150 }
151
152 static u_int                     crusoe_longrun;
153 static u_int                     crusoe_frequency;
154 static u_int                     crusoe_voltage;
155 static u_int                     crusoe_percentage;
156 static u_int                     crusoe_performance_longrun = LONGRUN_MODE_PERFORMANCE;
157 static u_int                     crusoe_economy_longrun = LONGRUN_MODE_ECONOMY;
158 static struct sysctl_ctx_list    crusoe_sysctl_ctx;
159 static struct sysctl_oid        *crusoe_sysctl_tree;
160
161 static void
162 tmx86_longrun_power_profile(void *arg)
163 {
164         int     state;
165         u_int   new;
166
167         state = power_profile_get_state();
168         if (state != POWER_PROFILE_PERFORMANCE &&
169             state != POWER_PROFILE_ECONOMY) {
170                 return;
171         }
172
173         switch (state) {
174         case POWER_PROFILE_PERFORMANCE:
175                 new =crusoe_performance_longrun;
176                 break;
177         case POWER_PROFILE_ECONOMY:
178                 new = crusoe_economy_longrun;
179                 break;
180         default:
181                 new = tmx86_get_longrun_mode();
182                 break;
183         }
184
185         if (tmx86_get_longrun_mode() != new) {
186                 tmx86_set_longrun_mode(new);
187         }
188 }
189
190 static int
191 tmx86_longrun_sysctl(SYSCTL_HANDLER_ARGS)
192 {
193         u_int   mode;
194         int     error;
195
196         crusoe_longrun = tmx86_get_longrun_mode();
197         mode = crusoe_longrun;
198         error = sysctl_handle_int(oidp, &mode, 0, req);
199         if (error || !req->newptr) {
200                 return (error);
201         }
202         if (mode >= LONGRUN_MODE_UNKNOWN) {
203                 error = EINVAL;
204                 return (error);
205         }
206         if (crusoe_longrun != mode) {
207                 crusoe_longrun = mode;
208                 tmx86_set_longrun_mode(crusoe_longrun);
209         }
210
211         return (error);
212 }
213
214 static int
215 tmx86_status_sysctl(SYSCTL_HANDLER_ARGS)
216 {
217         u_int   val;
218         int     error;
219
220         tmx86_get_longrun_status(&crusoe_frequency,
221                                  &crusoe_voltage, &crusoe_percentage);
222         val = *(u_int *)oidp->oid_arg1;
223         error = sysctl_handle_int(oidp, &val, 0, req);
224         return (error);
225 }
226
227 static int
228 tmx86_longrun_profile_sysctl(SYSCTL_HANDLER_ARGS)
229 {
230         u_int32_t *argp;
231         u_int32_t arg;
232         int     error;
233
234         argp = (u_int32_t *)oidp->oid_arg1;
235         arg = *argp;
236         error = sysctl_handle_int(oidp, &arg, 0, req);
237
238         /* error or no new value */
239         if ((error != 0) || (req->newptr == NULL))
240                 return (error);
241
242         /* range check */
243         if (arg >= LONGRUN_MODE_UNKNOWN)
244                 return (EINVAL);
245
246         /* set new value and possibly switch */
247         *argp = arg;
248
249         tmx86_longrun_power_profile(NULL);
250
251         return (0);
252
253 }
254
255 static void
256 setup_tmx86_longrun(void *dummy __unused)
257 {
258
259         if (cpu_vendor_id != CPU_VENDOR_TRANSMETA)
260                 return;
261
262         crusoe_longrun = tmx86_get_longrun_mode();
263         tmx86_get_longrun_status(&crusoe_frequency,
264                                  &crusoe_voltage, &crusoe_percentage);
265         kprintf("Crusoe LongRun support enabled, current mode: %d "
266                "<%dMHz %dmV %d%%>\n", crusoe_longrun, crusoe_frequency,
267                crusoe_voltage, crusoe_percentage);
268
269         sysctl_ctx_init(&crusoe_sysctl_ctx);
270         crusoe_sysctl_tree = SYSCTL_ADD_NODE(&crusoe_sysctl_ctx,
271                                 SYSCTL_STATIC_CHILDREN(_hw), OID_AUTO,
272                                 "crusoe", CTLFLAG_RD, 0,
273                                 "Transmeta Crusoe LongRun support");
274         SYSCTL_ADD_PROC(&crusoe_sysctl_ctx, SYSCTL_CHILDREN(crusoe_sysctl_tree),
275                 OID_AUTO, "longrun", CTLTYPE_INT | CTLFLAG_RW,
276                 &crusoe_longrun, 0, tmx86_longrun_sysctl, "I",
277                 "LongRun mode [0-3]");
278         SYSCTL_ADD_PROC(&crusoe_sysctl_ctx, SYSCTL_CHILDREN(crusoe_sysctl_tree),
279                 OID_AUTO, "frequency", CTLTYPE_INT | CTLFLAG_RD,
280                 &crusoe_frequency, 0, tmx86_status_sysctl, "I",
281                 "Current frequency (MHz)");
282         SYSCTL_ADD_PROC(&crusoe_sysctl_ctx, SYSCTL_CHILDREN(crusoe_sysctl_tree),
283                 OID_AUTO, "voltage", CTLTYPE_INT | CTLFLAG_RD,
284                 &crusoe_voltage, 0, tmx86_status_sysctl, "I",
285                 "Current voltage (mV)");
286         SYSCTL_ADD_PROC(&crusoe_sysctl_ctx, SYSCTL_CHILDREN(crusoe_sysctl_tree),
287                 OID_AUTO, "percentage", CTLTYPE_INT | CTLFLAG_RD,
288                 &crusoe_percentage, 0, tmx86_status_sysctl, "I",
289                 "Processing performance (%)");
290         SYSCTL_ADD_PROC(&crusoe_sysctl_ctx, SYSCTL_CHILDREN(crusoe_sysctl_tree),
291                 OID_AUTO, "performance_longrun", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_RW,
292                 &crusoe_performance_longrun, 0, tmx86_longrun_profile_sysctl, "I", "");
293         SYSCTL_ADD_PROC(&crusoe_sysctl_ctx, SYSCTL_CHILDREN(crusoe_sysctl_tree),
294                 OID_AUTO, "economy_longrun", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_RW,
295                 &crusoe_economy_longrun, 0, tmx86_longrun_profile_sysctl, "I", "");
296
297         /* register performance profile change handler */
298         EVENTHANDLER_REGISTER(power_profile_change, tmx86_longrun_power_profile, NULL, 0);
299 }
300 SYSINIT(setup_tmx86_longrun, SI_BOOT2_BIOS, SI_ORDER_ANY, setup_tmx86_longrun,
301     NULL);