Add Enhanced SpeedStep driver(EST), based on Colin Percival's early
[dragonfly.git] / sys / platform / pc32 / i386 / est.c
1 /*-
2  * Copyright 2004 Colin Percival.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted providing that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *      This product includes software developed by Colin Percival.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
25  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
26  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27  * POSSIBILITY OF SUCH DAMAGE.
28  *
29  * $DragonFly: src/sys/platform/pc32/i386/est.c,v 1.1 2006/06/21 22:25:43 y0netan1 Exp $
30  */
31
32 #include <sys/param.h>
33 #include <sys/errno.h>
34 #include <sys/systm.h>
35 #include <sys/sysctl.h>
36 #include <sys/module.h>
37 #include <sys/kernel.h>
38 #include <sys/proc.h>
39
40 typedef struct {
41         int mhz;
42         int mv;
43 } freq_info;
44
45 typedef struct {
46         freq_info *freqtab;
47         size_t tabsize;
48         uint32_t VID;
49 } cpu_info;
50
51 /* Obtained from Linux patch by Jeremy Fitzhardinge */
52 #define mhzmv2msr(mhz, mv)      ((((mhz) / 100) << 8) + (((mv) - 700) >> 4))
53 #define msr2mhz(msr)            ((((msr) >> 8) & 0xff) * 100)
54 #define msr2mv(msr)             ((((msr) & 0xff) << 4) + 700)
55
56 /* Obtained by observation of MSR contents */
57 #define ID(mhz_hi, mv_hi, mhz_lo, mv_lo)        \
58         ((mhzmv2msr(mhz_lo, mv_lo) << 16) + mhzmv2msr(mhz_hi, mv_hi))
59 #define CPUINFO(t, zhi, vhi, zlo, vlo)  \
60         { t, sizeof(t) / sizeof(t[0]), ID(zhi, vhi, zlo, vlo) }
61
62 /* Names and numbers from IA-32 System Programming Guide */
63 #define MSR_PERF_STATUS         0x198
64 #define MSR_PERF_CTL            0x199
65
66 /*
67  * Data from
68  * Intel Pentium M Processor Datasheet (Order Number 252612), Table 5
69  */
70 static freq_info PM17_130[] = {
71 /* 130nm 1.70GHz Pentium M */
72         { 1700, 1484 },
73         { 1400, 1308 },
74         { 1200, 1228 },
75         { 1000, 1116 },
76         {  800, 1004 },
77         {  600,  956 },
78 };
79 static freq_info PM16_130[] = {
80 /* 130nm 1.60GHz Pentium M */
81         { 1600, 1484 },
82         { 1400, 1420 },
83         { 1200, 1276 },
84         { 1000, 1164 },
85         {  800, 1036 },
86         {  600,  956 },
87 };
88 static freq_info PM15_130[] = {
89 /* 130nm 1.50GHz Pentium M */
90         { 1500, 1484 },
91         { 1400, 1452 },
92         { 1200, 1356 },
93         { 1000, 1228 },
94         {  800, 1116 },
95         {  600,  956 },
96 };
97 static freq_info PM14_130[] = {
98 /* 130nm 1.40GHz Pentium M */
99         { 1400, 1484 },
100         { 1200, 1436 },
101         { 1000, 1308 },
102         {  800, 1180 },
103         {  600,  956 },
104 };
105 static freq_info PM13_130[] = {
106 /* 130nm 1.30GHz Pentium M */
107         { 1300, 1388 },
108         { 1200, 1356 },
109         { 1000, 1292 },
110         {  800, 1260 },
111         {  600,  956 },
112 };
113 static freq_info PM13_LV_130[] = {
114 /* 130nm 1.30GHz Low Voltage Pentium M */
115         { 1300, 1180 },
116         { 1200, 1164 },
117         { 1100, 1100 },
118         { 1000, 1020 },
119         {  900, 1004 },
120         {  800,  988 },
121         {  600,  956 },
122 };
123 static freq_info PM12_LV_130[] = {
124 /* 130 nm 1.20GHz Low Voltage Pentium M */
125         { 1200, 1180 },
126         { 1100, 1164 },
127         { 1000, 1100 },
128         {  900, 1020 },
129         {  800, 1004 },
130         {  600,  956 },
131 };
132 static freq_info PM11_LV_130[] = {
133 /* 130 nm 1.10GHz Low Voltage Pentium M */
134         { 1100, 1180 },
135         { 1000, 1164 },
136         {  900, 1100 },
137         {  800, 1020 },
138         {  600,  956 },
139 };
140 static freq_info PM11_ULV_130[] = {
141 /* 130 nm 1.10GHz Ultra Low Voltage Pentium M */
142         { 1100, 1004 },
143         { 1000,  988 },
144         {  900,  972 },
145         {  800,  956 },
146         {  600,  844 },
147 };
148 static freq_info PM10_ULV_130[] = {
149 /* 130 nm 1.00GHz Ultra Low Voltage Pentium M */
150         { 1000, 1004 },
151         {  900,  988 },
152         {  800,  972 },
153         {  600,  844 },
154 };
155
156 /*
157  * Data from
158  * Intel Pentium M Processor on 90nm Process with 2-MB L2 Cache
159  * Datasheet (Order Number 302189), Table 5
160  */
161 static freq_info PM_755A_90[] = {
162 /* 90 nm 2.00GHz Pentium M, VID #A */
163         { 2000, 1340 },
164         { 1800, 1292 },
165         { 1600, 1244 },
166         { 1400, 1196 },
167         { 1200, 1148 },
168         { 1000, 1100 },
169         {  800, 1052 },
170         {  600,  988 },
171 };
172 static freq_info PM_755B_90[] = {
173 /* 90 nm 2.00GHz Pentium M, VID #B */
174         { 2000, 1324 },
175         { 1800, 1276 },
176         { 1600, 1228 },
177         { 1400, 1180 },
178         { 1200, 1132 },
179         { 1000, 1084 },
180         {  800, 1036 },
181         {  600,  988 },
182 };
183 static freq_info PM_755C_90[] = {
184 /* 90 nm 2.00GHz Pentium M, VID #C */
185         { 2000, 1308 },
186         { 1800, 1276 },
187         { 1600, 1228 },
188         { 1400, 1180 },
189         { 1200, 1132 },
190         { 1000, 1084 },
191         {  800, 1036 },
192         {  600,  988 },
193 };
194 static freq_info PM_755D_90[] = {
195 /* 90 nm 2.00GHz Pentium M, VID #D */
196         { 2000, 1276 },
197         { 1800, 1244 },
198         { 1600, 1196 },
199         { 1400, 1164 },
200         { 1200, 1116 },
201         { 1000, 1084 },
202         {  800, 1036 },
203         {  600,  988 },
204 };
205 static freq_info PM_745A_90[] = {
206 /* 90 nm 1.80GHz Pentium M, VID #A */
207         { 1800, 1340 },
208         { 1600, 1292 },
209         { 1400, 1228 },
210         { 1200, 1164 },
211         { 1000, 1116 },
212         {  800, 1052 },
213         {  600,  988 },
214 };
215 static freq_info PM_745B_90[] = {
216 /* 90 nm 1.80GHz Pentium M, VID #B */
217         { 1800, 1324 },
218         { 1600, 1276 },
219         { 1400, 1212 },
220         { 1200, 1164 },
221         { 1000, 1116 },
222         {  800, 1052 },
223         {  600,  988 },
224 };
225 static freq_info PM_745C_90[] = {
226 /* 90 nm 1.80GHz Pentium M, VID #C */
227         { 1800, 1308 },
228         { 1600, 1260 },
229         { 1400, 1212 },
230         { 1200, 1148 },
231         { 1000, 1100 },
232         {  800, 1052 },
233         {  600,  988 },
234 };
235 static freq_info PM_745D_90[] = {
236 /* 90 nm 1.80GHz Pentium M, VID #D */
237         { 1800, 1276 },
238         { 1600, 1228 },
239         { 1400, 1180 },
240         { 1200, 1132 },
241         { 1000, 1084 },
242         {  800, 1036 },
243         {  600,  988 },
244 };
245 static freq_info PM_735A_90[] = {
246 /* 90 nm 1.70GHz Pentium M, VID #A */
247         { 1700, 1340 },
248         { 1400, 1244 },
249         { 1200, 1180 },
250         { 1000, 1116 },
251         {  800, 1052 },
252         {  600,  988 },
253 };
254 static freq_info PM_735B_90[] = {
255 /* 90 nm 1.70GHz Pentium M, VID #B */
256         { 1700, 1324 },
257         { 1400, 1244 },
258         { 1200, 1180 },
259         { 1000, 1116 },
260         {  800, 1052 },
261         {  600,  988 },
262 };
263 static freq_info PM_735C_90[] = {
264 /* 90 nm 1.70GHz Pentium M, VID #C */
265         { 1700, 1308 },
266         { 1400, 1228 },
267         { 1200, 1164 },
268         { 1000, 1116 },
269         {  800, 1052 },
270         {  600,  988 },
271 };
272 static freq_info PM_735D_90[] = {
273 /* 90 nm 1.70GHz Pentium M, VID #D */
274         { 1700, 1276 },
275         { 1400, 1212 },
276         { 1200, 1148 },
277         { 1000, 1100 },
278         {  800, 1052 },
279         {  600,  988 },
280 };
281 static freq_info PM_725A_90[] = {
282 /* 90 nm 1.60GHz Pentium M, VID #A */
283         { 1600, 1340 },
284         { 1400, 1276 },
285         { 1200, 1212 },
286         { 1000, 1132 },
287         {  800, 1068 },
288         {  600,  988 },
289 };
290 static freq_info PM_725B_90[] = {
291 /* 90 nm 1.60GHz Pentium M, VID #B */
292         { 1600, 1324 },
293         { 1400, 1260 },
294         { 1200, 1196 },
295         { 1000, 1132 },
296         {  800, 1068 },
297         {  600,  988 },
298 };
299 static freq_info PM_725C_90[] = {
300 /* 90 nm 1.60GHz Pentium M, VID #C */
301         { 1600, 1308 },
302         { 1400, 1244 },
303         { 1200, 1180 },
304         { 1000, 1116 },
305         {  800, 1052 },
306         {  600,  988 },
307 };
308 static freq_info PM_725D_90[] = {
309 /* 90 nm 1.60GHz Pentium M, VID #D */
310         { 1600, 1276 },
311         { 1400, 1228 },
312         { 1200, 1164 },
313         { 1000, 1116 },
314         {  800, 1052 },
315         {  600,  988 },
316 };
317 static freq_info PM_715A_90[] = {
318 /* 90 nm 1.50GHz Pentium M, VID #A */
319         { 1500, 1340 },
320         { 1200, 1228 },
321         { 1000, 1148 },
322         {  800, 1068 },
323         {  600,  988 },
324 };
325 static freq_info PM_715B_90[] = {
326 /* 90 nm 1.50GHz Pentium M, VID #B */
327         { 1500, 1324 },
328         { 1200, 1212 },
329         { 1000, 1148 },
330         {  800, 1068 },
331         {  600,  988 },
332 };
333 static freq_info PM_715C_90[] = {
334 /* 90 nm 1.50GHz Pentium M, VID #C */
335         { 1500, 1308 },
336         { 1200, 1212 },
337         { 1000, 1132 },
338         {  800, 1068 },
339         {  600,  988 },
340 };
341 static freq_info PM_715D_90[] = {
342 /* 90 nm 1.50GHz Pentium M, VID #D */
343         { 1500, 1276 },
344         { 1200, 1180 },
345         { 1000, 1116 },
346         {  800, 1052 },
347         {  600,  988 },
348 };
349 static freq_info PM_710_90[] = {                                               
350 /* 90 nm 1.40GHz Pentium M */                                          
351         { 1400, 1340 },                                                         
352         { 1200, 1228 },                                                         
353         { 1000, 1148 },                                                         
354         {  800, 1068 },                                                         
355         {  600,  988 },                                                         
356 };
357 static freq_info PM_738_90[] = {
358 /* 90 nm 1.40GHz Low Voltage Pentium M */
359         { 1400, 1116 },
360         { 1300, 1116 },
361         { 1200, 1100 },
362         { 1100, 1068 },
363         { 1000, 1052 },
364         {  900, 1036 },
365         {  800, 1020 },
366         {  600,  988 },
367 };
368 static freq_info PM_733_90[] = {
369 /* 90 nm 1.10GHz Ultra Low Voltage Pentium M */
370         { 1100,  940 },
371         { 1000,  924 },
372         {  900,  892 },
373         {  800,  876 },
374         {  600,  812 },
375 };
376 static freq_info PM_723_90[] = {
377 /* 90 nm 1.00GHz Ultra Low Voltage Pentium M */
378         { 1000,  940 },
379         {  900,  908 },
380         {  800,  876 },
381         {  600,  812 },
382 };
383
384 static cpu_info ESTprocs[] = {
385         CPUINFO(PM17_130, 1700, 1484, 600, 956),
386         CPUINFO(PM16_130, 1600, 1484, 600, 956),
387         CPUINFO(PM15_130, 1500, 1484, 600, 956),
388         CPUINFO(PM14_130, 1400, 1484, 600, 956),
389         CPUINFO(PM13_130, 1300, 1388, 600, 956),
390         CPUINFO(PM13_LV_130, 1300, 1180, 600, 956),
391         CPUINFO(PM12_LV_130, 1200, 1180, 600, 956),
392         CPUINFO(PM11_LV_130, 1100, 1180, 600, 956),
393         CPUINFO(PM11_ULV_130, 1100, 1004, 600, 844),
394         CPUINFO(PM10_ULV_130, 1000, 1004, 600, 844),
395         CPUINFO(PM_755A_90, 2000, 1340, 600, 988),
396         CPUINFO(PM_755B_90, 2000, 1324, 600, 988),
397         CPUINFO(PM_755C_90, 2000, 1308, 600, 988),
398         CPUINFO(PM_755D_90, 2000, 1276, 600, 988),
399         CPUINFO(PM_745A_90, 1800, 1340, 600, 988),
400         CPUINFO(PM_745B_90, 1800, 1324, 600, 988),
401         CPUINFO(PM_745C_90, 1800, 1308, 600, 988),
402         CPUINFO(PM_745D_90, 1800, 1276, 600, 988),
403         CPUINFO(PM_735A_90, 1700, 1340, 600, 988),
404         CPUINFO(PM_735B_90, 1700, 1324, 600, 988),
405         CPUINFO(PM_735C_90, 1700, 1308, 600, 988),
406         CPUINFO(PM_735D_90, 1700, 1276, 600, 988),
407         CPUINFO(PM_725A_90, 1600, 1340, 600, 988),
408         CPUINFO(PM_725B_90, 1600, 1324, 600, 988),
409         CPUINFO(PM_725C_90, 1600, 1308, 600, 988),
410         CPUINFO(PM_725D_90, 1600, 1276, 600, 988),
411         CPUINFO(PM_715A_90, 1500, 1340, 600, 988),
412         CPUINFO(PM_715B_90, 1500, 1324, 600, 988),
413         CPUINFO(PM_715C_90, 1500, 1308, 600, 988),
414         CPUINFO(PM_715D_90, 1500, 1276, 600, 988),
415         CPUINFO(PM_710_90, 1400, 1340, 600, 988),
416         CPUINFO(PM_738_90, 1400, 1116, 600, 988),
417         CPUINFO(PM_733_90, 1100, 940, 600, 812),
418         CPUINFO(PM_723_90, 1000, 940, 600, 812),
419 };
420
421 static cpu_info *freq_list = NULL;      /* NULL if EST is disabled */
422
423 static int
424 est_sysctl_mhz(SYSCTL_HANDLER_ARGS)
425 {
426         uint64_t msr;
427         int mhz, mv;
428         int mhz_wanted;
429         freq_info *f, *f_end;
430         int err = 0;
431
432         if (freq_list == NULL)
433                 return(EOPNOTSUPP);
434
435         msr = rdmsr(MSR_PERF_STATUS);
436         mhz = msr2mhz(msr);
437         mv = msr2mv(msr);
438
439         if (req->newptr) {
440                 err = SYSCTL_IN(req, &mhz_wanted, sizeof(int));
441                 if (err)
442                         return(err);
443                 if (mhz == mhz_wanted)
444                         return(0);
445                 f_end = freq_list->freqtab + freq_list->tabsize;
446                 for (f = freq_list->freqtab; f < f_end; f++) {
447                         if (f->mhz == mhz_wanted)
448                                 break;
449                 }
450                 if (f->mhz == 0)
451                         return(EOPNOTSUPP);
452                 printf("Changing CPU frequency from %d MHz to %d MHz\n",
453                         mhz, mhz_wanted);
454                 msr = rdmsr(MSR_PERF_CTL);
455                 msr = (msr & ~(uint64_t)(0xffff)) | mhzmv2msr(f->mhz, f->mv);
456                 wrmsr(MSR_PERF_CTL, msr);
457                 /*
458                  * Sleep for a short time, to let the cpu find
459                  * its new frequency before we return to the user
460                  */
461                 tsleep(&freq_list, 0, "EST", 1);
462         } else
463                 err = SYSCTL_OUT(req, &mhz, sizeof(int));
464         return(err);
465 }
466
467 SYSCTL_NODE(_machdep, OID_AUTO, est, CTLFLAG_RD, 0, "");
468 SYSCTL_NODE(_machdep_est, OID_AUTO, frequency, CTLFLAG_RD, 0,
469             "Enhanced SpeedStep driver parameters");
470 SYSCTL_PROC(_machdep_est_frequency, OID_AUTO, current,
471             CTLTYPE_INT | CTLFLAG_RD, 0, 0, &est_sysctl_mhz, "I",
472             "Current CPU frequency for Enhanced SpeedStep");
473 SYSCTL_PROC(_machdep_est_frequency, OID_AUTO, target,
474             CTLTYPE_INT | CTLFLAG_RW, 0, 0, &est_sysctl_mhz, "I",
475             "Target CPU frequency for Enhanced SpeedStep");
476
477 static char est_frequencies[80] = "";
478 SYSCTL_STRING(_machdep_est_frequency, OID_AUTO, available, CTLFLAG_RD,
479               est_frequencies, 0,
480               "CPU frequencies supported by Enhanced SpeedStep");
481
482 static int
483 findcpu(void)
484 {
485         uint64_t        msr;
486         uint32_t        VID;
487         cpu_info        *cpinfo;
488         size_t          N = sizeof(ESTprocs) / sizeof(ESTprocs[0]);
489
490         msr = rdmsr(MSR_PERF_STATUS);
491         VID = (msr >> 32);
492         for (cpinfo = ESTprocs; cpinfo < ESTprocs + N; cpinfo++) {
493                 if (VID == cpinfo->VID)
494                         break;
495         }
496         if (cpinfo == ESTprocs + N)
497                 return(EOPNOTSUPP);
498         freq_list = cpinfo;
499         return(0);
500 }
501
502 static int
503 est_loader(struct module *m __unused, int what, void *arg __unused)
504 {
505         char            hwmodel[128];
506         int             mib[] = { CTL_HW, HW_MODEL };
507         size_t          modellen;
508         size_t          freqlen, l;
509         uint64_t        msr;
510         int             mhz, mv;
511         freq_info       *fq;
512         int             err = 0;
513
514         switch (what) {
515         case MOD_LOAD:
516                 modellen = sizeof(hwmodel);
517                 err = kernel_sysctl(mib, 2, hwmodel, &modellen, NULL, 0, NULL);
518                 if (err) {
519                         printf("kernel_sysctl hw.model failed\n");
520                         return(err);
521                 }
522                 err = EOPNOTSUPP;
523                 if (ncpus != 1) {
524                         printf("Enhanced SpeedStep not supported"
525                                " with more than one processor\n");
526                         break;
527                 }
528                 if (strncmp(hwmodel, "Intel(R) Pentium(R) M processor", 31) ||
529                     (findcpu() != 0)) {
530                         printf("%s: Enhanced Speedstep not supported"
531                                " on this processor\n", hwmodel);
532                         break;
533                 }
534
535                 freqlen = freq_list->tabsize * (sizeof(" 9999") - 1) + 1;
536                 if (sizeof(est_frequencies) <= freqlen) {
537                         printf("please enlarge est_frequencies[]\n");
538                         err = ENOMEM;
539                         break;
540                 }
541                 l = 0;
542                 for (fq = freq_list->freqtab + freq_list->tabsize;
543                      --fq >= freq_list->freqtab;) {
544                         l += snprintf(est_frequencies + l, freqlen - l, "%s%d",
545                             l > 0 ? " " : "", fq->mhz);
546                 }
547
548                 msr = rdmsr(MSR_PERF_STATUS);
549                 mhz = msr2mhz(msr);
550                 mv = msr2mv(msr);
551                 printf("%s: Enhanced SpeedStep running at %d MHz (%d mV)\n",
552                        hwmodel, mhz, mv);
553                 err = 0;
554                 break;
555         case MOD_UNLOAD:
556                 break;
557         default:
558                 err = EINVAL;
559                 break;
560         }
561         return(err);
562 }
563
564 static moduledata_t est_mod = {
565         "est",
566         est_loader,
567         NULL
568 };
569
570 DECLARE_MODULE(est, est_mod, SI_SUB_KLD, SI_ORDER_ANY);