i386: Move CPU ID and APIC ID maps from mp_machdep.c to lapic.c
[dragonfly.git] / sys / platform / pc32 / i386 / p4tcc.c
1 /*      $OpenBSD: p4tcc.c,v 1.1 2003/12/20 18:23:18 tedu Exp $ */
2 /*
3  * Copyright (c) 2003 Ted Unangst
4  * Copyright (c) 2004 Maxim Sobolev <sobomax@FreeBSD.org>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 /*
29  * Restrict power consumption by using thermal control circuit.
30  * This operates independently of speedstep.
31  * Found on Pentium 4 and later models (feature TM).
32  *
33  * References:
34  * Intel Developer's manual v.3 #245472-012
35  *
36  * On some models, the cpu can hang if it's running at a slow speed.
37  * Workarounds included below.
38  *
39  * $FreeBSD: /repoman/r/ncvs/src/sys/i386/i386/p4tcc.c,v 1.3.2.1 2004/03/03 15:24:15 sobomax Exp $
40  */
41
42 #include "opt_cpu.h"
43 #include <sys/param.h>
44 #include <sys/systm.h>
45 #include <sys/kernel.h>
46 #include <sys/conf.h>
47 #include <sys/power.h>
48 #include <sys/sysctl.h>
49 #include <sys/types.h>
50
51 #include <machine/md_var.h>
52 #include <machine/specialreg.h>
53
54 static u_int                    p4tcc_percentage;
55 static u_int                    p4tcc_economy;
56 static u_int                    p4tcc_performance;
57 static struct sysctl_ctx_list   p4tcc_sysctl_ctx;
58 static struct sysctl_oid        *p4tcc_sysctl_tree;
59
60 static struct {
61         u_short level;
62         u_short rlevel;
63         u_short reg;
64 } tcc[] = {
65         { 88, 100, 0 },
66         { 75, 88,  7 },
67         { 63, 75,  6 },
68         { 50, 63,  5 },
69         { 38, 50,  4 },
70         { 25, 38,  3 },
71         { 13, 25,  2 },
72         { 0,  13,  1 }
73 };
74
75 #define TCC_LEVELS      NELEM(tcc)
76
77 static u_short
78 p4tcc_getperf(void)
79 {
80         u_int64_t msreg;
81         int i;
82
83         msreg = rdmsr(MSR_THERM_CONTROL);
84         msreg = (msreg >> 1) & 0x07;
85         for (i = 0; i < TCC_LEVELS; i++) {
86                 if (msreg == tcc[i].reg)
87                         break;
88         }
89
90         return (tcc[i].rlevel);
91 }
92
93 static void
94 p4tcc_setperf(u_int percentage)
95 {
96         int i;
97         u_int64_t msreg;
98
99         if (percentage > tcc[0].rlevel)
100                 percentage = tcc[0].rlevel;
101         for (i = 0; i < TCC_LEVELS - 1; i++) {
102                 if (percentage > tcc[i].level)
103                         break;
104         }
105
106         msreg = rdmsr(MSR_THERM_CONTROL);
107         msreg &= ~0x1e; /* bit 0 reserved */
108         if (tcc[i].reg != 0)
109                 msreg |= tcc[i].reg << 1 | 1 << 4;
110         wrmsr(MSR_THERM_CONTROL, msreg);
111 }
112
113 static int
114 p4tcc_perf_sysctl(SYSCTL_HANDLER_ARGS)
115 {
116         u_int percentage;
117         int error;
118
119         p4tcc_percentage = p4tcc_getperf();
120         percentage = p4tcc_percentage;
121         error = sysctl_handle_int(oidp, &percentage, 0, req);
122         if (error || !req->newptr) {
123                 return (error);
124         }
125         if (p4tcc_percentage != percentage) {
126                 p4tcc_setperf(percentage);
127         }
128
129         return (error);
130 }
131
132 static void
133 p4tcc_power_profile(void *arg)
134 {
135         int state;
136         u_int new;
137
138         state = power_profile_get_state();
139         if (state != POWER_PROFILE_PERFORMANCE &&
140             state != POWER_PROFILE_ECONOMY) {
141                 return;
142         }
143
144         switch (state) {
145         case POWER_PROFILE_PERFORMANCE:
146                 new = p4tcc_performance;
147                 break;
148         case POWER_PROFILE_ECONOMY:
149                 new = p4tcc_economy;
150                 break;
151         default:
152                 new = p4tcc_getperf();
153                 break;
154         }
155
156         if (p4tcc_getperf() != new) {
157                 p4tcc_setperf(new);
158         }
159 }
160
161 static int
162 p4tcc_profile_sysctl(SYSCTL_HANDLER_ARGS)
163 {
164         u_int32_t *argp;
165         u_int32_t arg;
166         int error;
167
168         argp = (u_int32_t *)oidp->oid_arg1;
169         arg = *argp;
170         error = sysctl_handle_int(oidp, &arg, 0, req);
171
172         /* error or no new value */
173         if ((error != 0) || (req->newptr == NULL))
174                 return (error);
175
176         /* range check */
177         if (arg > tcc[0].rlevel)
178                 arg = tcc[0].rlevel;
179
180         /* set new value and possibly switch */
181         *argp = arg;
182
183         p4tcc_power_profile(NULL);
184
185         *argp = p4tcc_getperf();
186
187         return (0);
188 }
189
190 static void
191 setup_p4tcc(void *dummy __unused)
192 {
193
194         if ((cpu_feature & (CPUID_ACPI | CPUID_TM)) !=
195             (CPUID_ACPI | CPUID_TM))
196                 return;
197
198         switch (cpu_id & 0xf) {
199         case 0x22:      /* errata O50 P44 and Z21 */
200         case 0x24:
201         case 0x25:
202         case 0x27:
203         case 0x29:
204                 /* hang with 12.5 */
205                 tcc[TCC_LEVELS - 1] = tcc[TCC_LEVELS - 2];
206                 break;
207         case 0x07:      /* errata N44 and P18 */
208         case 0x0a:
209         case 0x12:
210         case 0x13:
211                 /* hang at 12.5 and 25 */
212                 tcc[TCC_LEVELS - 1] = tcc[TCC_LEVELS - 2] = tcc[TCC_LEVELS - 3];
213                 break;
214         default:
215                 break;
216         }
217
218         p4tcc_economy = tcc[TCC_LEVELS - 1].rlevel;
219         p4tcc_performance = tcc[0].rlevel;
220
221         p4tcc_percentage = p4tcc_getperf();
222         kprintf("Pentium 4 TCC support enabled, current performance %u%%\n",
223             p4tcc_percentage);
224
225         sysctl_ctx_init(&p4tcc_sysctl_ctx);
226         p4tcc_sysctl_tree = SYSCTL_ADD_NODE(&p4tcc_sysctl_ctx,
227             SYSCTL_STATIC_CHILDREN(_hw), OID_AUTO, "p4tcc", CTLFLAG_RD, 0,
228             "Pentium 4 Thermal Control Circuitry support");
229         SYSCTL_ADD_PROC(&p4tcc_sysctl_ctx,
230             SYSCTL_CHILDREN(p4tcc_sysctl_tree), OID_AUTO,
231             "cpuperf", CTLTYPE_INT | CTLFLAG_RW,
232             &p4tcc_percentage, 0, p4tcc_perf_sysctl, "I",
233             "CPU performance in % of maximum");
234         SYSCTL_ADD_PROC(&p4tcc_sysctl_ctx,
235             SYSCTL_CHILDREN(p4tcc_sysctl_tree), OID_AUTO,
236             "cpuperf_performance", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_RW,
237             &p4tcc_performance, 0, p4tcc_profile_sysctl, "I",
238             "CPU performance in % of maximum in Performance mode");
239         SYSCTL_ADD_PROC(&p4tcc_sysctl_ctx,
240             SYSCTL_CHILDREN(p4tcc_sysctl_tree), OID_AUTO,
241             "cpuperf_economy", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_RW,
242             &p4tcc_economy, 0, p4tcc_profile_sysctl, "I",
243             "CPU performance in % of maximum in Economy mode");
244
245         /* register performance profile change handler */
246         EVENTHANDLER_REGISTER(power_profile_change, p4tcc_power_profile, NULL, 0);
247 }
248
249 /*
250  * Set this is pre-smp to give us a chance to play nice in case
251  * SMP startup locks the system up.
252  */
253 SYSINIT(setup_p4tcc, SI_BOOT2_PRESMP, SI_ORDER_ANY, setup_p4tcc, NULL);
254