2 * Copyright (c) 2001 Tamotsu Hattori.
3 * Copyright (c) 2001 Mitsuru IWASAKI.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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.
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
36 #include <sys/cdefs.h>
37 __FBSDID("$FreeBSD$");
41 #include <sys/param.h>
42 #include <sys/systm.h>
43 #include <sys/kernel.h>
45 #include <sys/power.h>
46 #include <sys/sysctl.h>
47 #include <sys/types.h>
49 #include <machine/cputypes.h>
50 #include <machine/md_var.h>
51 #include <machine/specialreg.h>
54 * Transmeta Crusoe LongRun Support by Tamotsu Hattori.
57 #define MSR_TMx86_LONGRUN 0x80868010
58 #define MSR_TMx86_LONGRUN_FLAGS 0x80868011
60 #define LONGRUN_MODE_MASK(x) ((x) & 0x000000007f)
61 #define LONGRUN_MODE_RESERVED(x) ((x) & 0xffffff80)
62 #define LONGRUN_MODE_WRITE(x, y) (LONGRUN_MODE_RESERVED(x) | LONGRUN_MODE_MASK(y))
64 #define LONGRUN_MODE_MINFREQUENCY 0x00
65 #define LONGRUN_MODE_ECONOMY 0x01
66 #define LONGRUN_MODE_PERFORMANCE 0x02
67 #define LONGRUN_MODE_MAXFREQUENCY 0x03
68 #define LONGRUN_MODE_UNKNOWN 0x04
69 #define LONGRUN_MODE_MAX 0x04
76 static u_int32_t longrun_modes
[LONGRUN_MODE_MAX
][3] = {
77 /* MSR low, MSR high, flags bit0 */
78 { 0, 0, 0}, /* LONGRUN_MODE_MINFREQUENCY */
79 { 0, 100, 0}, /* LONGRUN_MODE_ECONOMY */
80 { 0, 100, 1}, /* LONGRUN_MODE_PERFORMANCE */
81 { 100, 100, 1}, /* LONGRUN_MODE_MAXFREQUENCY */
85 tmx86_get_longrun_mode(void)
88 union msrinfo msrinfo
;
89 u_int low
, high
, flags
, mode
;
91 saveintr
= intr_disable();
93 msrinfo
.msr
= rdmsr(MSR_TMx86_LONGRUN
);
94 low
= LONGRUN_MODE_MASK(msrinfo
.regs
[0]);
95 high
= LONGRUN_MODE_MASK(msrinfo
.regs
[1]);
96 flags
= rdmsr(MSR_TMx86_LONGRUN_FLAGS
) & 0x01;
98 for (mode
= 0; mode
< LONGRUN_MODE_MAX
; mode
++) {
99 if (low
== longrun_modes
[mode
][0] &&
100 high
== longrun_modes
[mode
][1] &&
101 flags
== longrun_modes
[mode
][2]) {
105 mode
= LONGRUN_MODE_UNKNOWN
;
107 intr_restore(saveintr
);
112 tmx86_get_longrun_status(u_int
* frequency
, u_int
* voltage
, u_int
* percentage
)
117 saveintr
= intr_disable();
119 do_cpuid(0x80860007, regs
);
120 *frequency
= regs
[0];
122 *percentage
= regs
[2];
124 intr_restore(saveintr
);
129 tmx86_set_longrun_mode(u_int mode
)
132 union msrinfo msrinfo
;
134 if (mode
>= LONGRUN_MODE_UNKNOWN
) {
138 saveintr
= intr_disable();
140 /* Write LongRun mode values to Model Specific Register. */
141 msrinfo
.msr
= rdmsr(MSR_TMx86_LONGRUN
);
142 msrinfo
.regs
[0] = LONGRUN_MODE_WRITE(msrinfo
.regs
[0],
143 longrun_modes
[mode
][0]);
144 msrinfo
.regs
[1] = LONGRUN_MODE_WRITE(msrinfo
.regs
[1],
145 longrun_modes
[mode
][1]);
146 wrmsr(MSR_TMx86_LONGRUN
, msrinfo
.msr
);
148 /* Write LongRun mode flags to Model Specific Register. */
149 msrinfo
.msr
= rdmsr(MSR_TMx86_LONGRUN_FLAGS
);
150 msrinfo
.regs
[0] = (msrinfo
.regs
[0] & ~0x01) | longrun_modes
[mode
][2];
151 wrmsr(MSR_TMx86_LONGRUN_FLAGS
, msrinfo
.msr
);
153 intr_restore(saveintr
);
157 static u_int crusoe_longrun
;
158 static u_int crusoe_frequency
;
159 static u_int crusoe_voltage
;
160 static u_int crusoe_percentage
;
161 static u_int crusoe_performance_longrun
= LONGRUN_MODE_PERFORMANCE
;
162 static u_int crusoe_economy_longrun
= LONGRUN_MODE_ECONOMY
;
163 static struct sysctl_ctx_list crusoe_sysctl_ctx
;
164 static struct sysctl_oid
*crusoe_sysctl_tree
;
167 tmx86_longrun_power_profile(void *arg
)
172 state
= power_profile_get_state();
173 if (state
!= POWER_PROFILE_PERFORMANCE
&&
174 state
!= POWER_PROFILE_ECONOMY
) {
179 case POWER_PROFILE_PERFORMANCE
:
180 new =crusoe_performance_longrun
;
182 case POWER_PROFILE_ECONOMY
:
183 new = crusoe_economy_longrun
;
186 new = tmx86_get_longrun_mode();
190 if (tmx86_get_longrun_mode() != new) {
191 tmx86_set_longrun_mode(new);
196 tmx86_longrun_sysctl(SYSCTL_HANDLER_ARGS
)
201 crusoe_longrun
= tmx86_get_longrun_mode();
202 mode
= crusoe_longrun
;
203 error
= sysctl_handle_int(oidp
, &mode
, 0, req
);
204 if (error
|| !req
->newptr
) {
207 if (mode
>= LONGRUN_MODE_UNKNOWN
) {
211 if (crusoe_longrun
!= mode
) {
212 crusoe_longrun
= mode
;
213 tmx86_set_longrun_mode(crusoe_longrun
);
220 tmx86_status_sysctl(SYSCTL_HANDLER_ARGS
)
225 tmx86_get_longrun_status(&crusoe_frequency
,
226 &crusoe_voltage
, &crusoe_percentage
);
227 val
= *(u_int
*)oidp
->oid_arg1
;
228 error
= sysctl_handle_int(oidp
, &val
, 0, req
);
233 tmx86_longrun_profile_sysctl(SYSCTL_HANDLER_ARGS
)
239 argp
= (u_int32_t
*)oidp
->oid_arg1
;
241 error
= sysctl_handle_int(oidp
, &arg
, 0, req
);
243 /* error or no new value */
244 if ((error
!= 0) || (req
->newptr
== NULL
))
248 if (arg
>= LONGRUN_MODE_UNKNOWN
)
251 /* set new value and possibly switch */
254 tmx86_longrun_power_profile(NULL
);
261 setup_tmx86_longrun(void *dummy __unused
)
264 if (cpu_vendor_id
!= CPU_VENDOR_TRANSMETA
)
267 crusoe_longrun
= tmx86_get_longrun_mode();
268 tmx86_get_longrun_status(&crusoe_frequency
,
269 &crusoe_voltage
, &crusoe_percentage
);
270 printf("Crusoe LongRun support enabled, current mode: %d "
271 "<%dMHz %dmV %d%%>\n", crusoe_longrun
, crusoe_frequency
,
272 crusoe_voltage
, crusoe_percentage
);
274 sysctl_ctx_init(&crusoe_sysctl_ctx
);
275 crusoe_sysctl_tree
= SYSCTL_ADD_NODE(&crusoe_sysctl_ctx
,
276 SYSCTL_STATIC_CHILDREN(_hw
), OID_AUTO
,
277 "crusoe", CTLFLAG_RD
, 0,
278 "Transmeta Crusoe LongRun support");
279 SYSCTL_ADD_PROC(&crusoe_sysctl_ctx
, SYSCTL_CHILDREN(crusoe_sysctl_tree
),
280 OID_AUTO
, "longrun", CTLTYPE_INT
| CTLFLAG_RW
,
281 &crusoe_longrun
, 0, tmx86_longrun_sysctl
, "I",
282 "LongRun mode [0-3]");
283 SYSCTL_ADD_PROC(&crusoe_sysctl_ctx
, SYSCTL_CHILDREN(crusoe_sysctl_tree
),
284 OID_AUTO
, "frequency", CTLTYPE_INT
| CTLFLAG_RD
,
285 &crusoe_frequency
, 0, tmx86_status_sysctl
, "I",
286 "Current frequency (MHz)");
287 SYSCTL_ADD_PROC(&crusoe_sysctl_ctx
, SYSCTL_CHILDREN(crusoe_sysctl_tree
),
288 OID_AUTO
, "voltage", CTLTYPE_INT
| CTLFLAG_RD
,
289 &crusoe_voltage
, 0, tmx86_status_sysctl
, "I",
290 "Current voltage (mV)");
291 SYSCTL_ADD_PROC(&crusoe_sysctl_ctx
, SYSCTL_CHILDREN(crusoe_sysctl_tree
),
292 OID_AUTO
, "percentage", CTLTYPE_INT
| CTLFLAG_RD
,
293 &crusoe_percentage
, 0, tmx86_status_sysctl
, "I",
294 "Processing performance (%)");
295 SYSCTL_ADD_PROC(&crusoe_sysctl_ctx
, SYSCTL_CHILDREN(crusoe_sysctl_tree
),
296 OID_AUTO
, "performance_longrun", CTLTYPE_INT
| CTLFLAG_RD
| CTLFLAG_RW
,
297 &crusoe_performance_longrun
, 0, tmx86_longrun_profile_sysctl
, "I", "");
298 SYSCTL_ADD_PROC(&crusoe_sysctl_ctx
, SYSCTL_CHILDREN(crusoe_sysctl_tree
),
299 OID_AUTO
, "economy_longrun", CTLTYPE_INT
| CTLFLAG_RD
| CTLFLAG_RW
,
300 &crusoe_economy_longrun
, 0, tmx86_longrun_profile_sysctl
, "I", "");
302 /* register performance profile change handler */
303 EVENTHANDLER_REGISTER(power_profile_change
, tmx86_longrun_power_profile
, NULL
, 0);
305 SYSINIT(setup_tmx86_longrun
, SI_SUB_CPU
, SI_ORDER_ANY
, setup_tmx86_longrun
,