Prex Home / Browse Source - Prex Version: 0.9.0

root/bsp/drv/dev/cpufreq/est.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. est_setperf
  2. est_getperf
  3. est_getinfo
  4. est_identify
  5. est_probe
  6. est_init

   1 /*      $OpenBSD: est.c,v 1.11 2005/03/07 06:59:14 mbalmer Exp $ */
   2 /*
   3  * Copyright (c) 2003 Michael Eriksson.
   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. The name of the author may not be used to endorse or promote products
  15  *    derived from this software without specific prior written permission.
  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 WARRANTIES
  19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
  21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  27  */
  28 /*
  29  * This is a driver for Intel's Enhanced SpeedStep, as implemented in
  30  * Pentium M processors.
  31  *
  32  * Reference documentation:
  33  *
  34  * - IA-32 Intel Architecture Software Developer's Manual, Volume 3:
  35  *   System Programming Guide.
  36  *   Section 13.14, Enhanced Intel SpeedStep technology.
  37  *   Table B-2, MSRs in Pentium M Processors.
  38  *   http://www.intel.com/design/pentium4/manuals/245472.htm
  39  *
  40  * - Intel Pentium M Processor Datasheet.
  41  *   Table 5, Voltage and Current Specifications.
  42  *   http://www.intel.com/design/mobile/datashts/252612.htm
  43  *
  44  * - Intel Pentium M Processor on 90 nm Process with 2-MB L2 Cache Datasheet
  45  *   Table 3-4, Voltage and Current Specifications.
  46  *   http://www.intel.com/design/mobile/datashts/302189.htm
  47  *
  48  * - Linux cpufreq patches, speedstep-centrino.c.
  49  *   Encoding of MSR_PERF_CTL and MSR_PERF_STATUS.
  50  *   http://www.codemonkey.org.uk/projects/cpufreq/cpufreq-2.4.22-pre6-1.gz
  51  */
  52 
  53 /*
  54  * est.c - Intel enhanced speedstep driver from OpenBSD.
  55  */
  56 
  57 #include <driver.h>
  58 #include <cpufreq.h>
  59 #include <cpufunc.h>
  60 #include <sys/ioctl.h>
  61 
  62 /* #define DEBUG_EST 1 */
  63 
  64 #ifdef DEBUG_EST
  65 #define DPRINTF(a) printf a
  66 #else
  67 #define DPRINTF(a)
  68 #endif
  69 
  70 /* Status/control registers (from the IA-32 System Programming Guide). */
  71 #define MSR_PERF_STATUS         0x198
  72 #define MSR_PERF_CTL            0x199
  73 
  74 /* Register and bit for enabling SpeedStep. */
  75 #define MSR_MISC_ENABLE         0x1a0
  76 #define MSR_SS_ENABLE           (1<<16)
  77 
  78 static int      est_probe(struct driver *);
  79 static int      est_init(struct driver *);
  80 static int      est_setperf(int);
  81 static int      est_getperf(void);
  82 static void     est_getinfo(struct cpufreqinfo *);
  83 static int      est_identify(char *);
  84 
  85 static struct cpufreq_ops est_ops = {
  86         /* setperf */   est_setperf,
  87         /* getpref */   est_getperf,
  88         /* getinfo */   est_getinfo,
  89 };
  90 
  91 struct driver est_driver = {
  92         /* name */      "est",
  93         /* devops */    NULL,
  94         /* flags */     0,
  95         /* flags */     0,
  96         /* probe */     est_probe,
  97         /* init */      est_init,
  98         /* shutdown */  NULL,
  99 };
 100 
 101 /*
 102  * Frequency tables
 103  */
 104 struct fq_info {
 105         int     mhz;
 106         int     mv;
 107 };
 108 
 109 /* Ultra Low Voltage Intel Pentium M processor 900 MHz */
 110 static const struct fq_info pentium_m_900[] = {
 111         {  900, 1004 },
 112         {  800,  988 },
 113         {  600,  844 },
 114 };
 115 
 116 /* Ultra Low Voltage Intel Pentium M processor 1.00 GHz */
 117 static const struct fq_info pentium_m_1000[] = {
 118         { 1000, 1004 },
 119         {  900,  988 },
 120         {  800,  972 },
 121         {  600,  844 },
 122 };
 123 
 124 /* Low Voltage Intel Pentium M processor 1.10 GHz */
 125 static const struct fq_info pentium_m_1100[] = {
 126         { 1100, 1180 },
 127         { 1000, 1164 },
 128         {  900, 1100 },
 129         {  800, 1020 },
 130         {  600,  956 },
 131 };
 132 
 133 /* Low Voltage Intel Pentium M processor 1.20 GHz */
 134 static const struct fq_info pentium_m_1200[] = {
 135         { 1200, 1180 },
 136         { 1100, 1164 },
 137         { 1000, 1100 },
 138         {  900, 1020 },
 139         {  800, 1004 },
 140         {  600,  956 },
 141 };
 142 
 143 /* Intel Pentium M processor 1.30 GHz */
 144 static const struct fq_info pentium_m_1300[] = {
 145         { 1300, 1388 },
 146         { 1200, 1356 },
 147         { 1000, 1292 },
 148         {  800, 1260 },
 149         {  600,  956 },
 150 };
 151 
 152 /* Intel Pentium M processor 1.40 GHz */
 153 static const struct fq_info pentium_m_1400[] = {
 154         { 1400, 1484 },
 155         { 1200, 1436 },
 156         { 1000, 1308 },
 157         {  800, 1180 },
 158         {  600,  956 }
 159 };
 160 
 161 /* Intel Pentium M processor 1.50 GHz */
 162 static const struct fq_info pentium_m_1500[] = {
 163         { 1500, 1484 },
 164         { 1400, 1452 },
 165         { 1200, 1356 },
 166         { 1000, 1228 },
 167         {  800, 1116 },
 168         {  600,  956 }
 169 };
 170 
 171 /* Intel Pentium M processor 1.60 GHz */
 172 static const struct fq_info pentium_m_1600[] = {
 173         { 1600, 1484 },
 174         { 1400, 1420 },
 175         { 1200, 1276 },
 176         { 1000, 1164 },
 177         {  800, 1036 },
 178         {  600,  956 }
 179 };
 180 
 181 /* Intel Pentium M processor 1.70 GHz */
 182 static const struct fq_info pentium_m_1700[] = {
 183         { 1700, 1484 },
 184         { 1400, 1308 },
 185         { 1200, 1228 },
 186         { 1000, 1116 },
 187         {  800, 1004 },
 188         {  600,  956 }
 189 };
 190 
 191 
 192 /* Intel Pentium M processor 723 1.0 GHz */
 193 static const struct fq_info pentium_m_n723[] = {
 194         { 1000,  940 },
 195         {  900,  908 },
 196         {  800,  876 },
 197         {  600,  812 }
 198 };
 199 
 200 /* Intel Pentium M processor 733 1.1 GHz */
 201 static const struct fq_info pentium_m_n733[] = {
 202         { 1100,  940 },
 203         { 1000,  924 },
 204         {  900,  892 },
 205         {  800,  876 },
 206         {  600,  812 }
 207 };
 208 
 209 /* Intel Pentium M processor 753 1.2 GHz */
 210 static const struct fq_info pentium_m_n753[] = {
 211         { 1200,  940 },
 212         { 1100,  924 },
 213         { 1000,  908 },
 214         {  900,  876 },
 215         {  800,  860 },
 216         {  600,  812 }
 217 };
 218 
 219 /* Intel Pentium M processor 738 1.4 GHz */
 220 static const struct fq_info pentium_m_n738[] = {
 221         { 1400, 1116 },
 222         { 1300, 1116 },
 223         { 1200, 1100 },
 224         { 1100, 1068 },
 225         { 1000, 1052 },
 226         {  900, 1036 },
 227         {  800, 1020 },
 228         {  600,  988 }
 229 };
 230 
 231 #if 0
 232 /* Intel Pentium M processor 758 1.5 GHz */
 233 static const struct fq_info pentium_m_n758[] = {
 234         { 1500, 1116 },
 235         { 1400, 1116 },
 236         { 1300, 1100 },
 237         { 1200, 1084 },
 238         { 1100, 1068 },
 239         { 1000, 1052 },
 240         {  900, 1036 },
 241         {  800, 1020 },
 242         {  600,  988 }
 243 };
 244 #endif
 245 
 246 /* Intel Pentium M processor 715 1.5 GHz */
 247 static const struct fq_info pentium_m_n715[] = {
 248         { 1500, 1340 },
 249         { 1200, 1228 },
 250         { 1000, 1148 },
 251         {  800, 1068 },
 252         {  600,  988 }
 253 };
 254 
 255 /* Intel Pentium M processor 725 1.6 GHz */
 256 static const struct fq_info pentium_m_n725[] = {
 257         { 1600, 1340 },
 258         { 1400, 1276 },
 259         { 1200, 1212 },
 260         { 1000, 1132 },
 261         {  800, 1068 },
 262         {  600,  988 }
 263 };
 264 
 265 /* Intel Pentium M processor 735 1.7 GHz */
 266 static const struct fq_info pentium_m_n735[] = {
 267         { 1700, 1340 },
 268         { 1400, 1244 },
 269         { 1200, 1180 },
 270         { 1000, 1116 },
 271         {  800, 1052 },
 272         {  600,  988 }
 273 };
 274 
 275 /* Intel Pentium M processor 745 1.8 GHz */
 276 static const struct fq_info pentium_m_n745[] = {
 277         { 1800, 1340 },
 278         { 1600, 1292 },
 279         { 1400, 1228 },
 280         { 1200, 1164 },
 281         { 1000, 1116 },
 282         {  800, 1052 },
 283         {  600,  988 }
 284 };
 285 
 286 /* Intel Pentium M processor 755 2.0 GHz */
 287 static const struct fq_info pentium_m_n755[] = {
 288         { 2000, 1340 },
 289         { 1800, 1292 },
 290         { 1600, 1244 },
 291         { 1400, 1196 },
 292         { 1200, 1148 },
 293         { 1000, 1100 },
 294         {  800, 1052 },
 295         {  600,  988 }
 296 };
 297 
 298 /* Intel Pentium M processor 765 2.1 GHz */
 299 static const struct fq_info pentium_m_n765[] = {
 300         { 2100, 1340 },
 301         { 1800, 1276 },
 302         { 1600, 1228 },
 303         { 1400, 1180 },
 304         { 1200, 1132 },
 305         { 1000, 1084 },
 306         {  800, 1036 },
 307         {  600,  988 }
 308 };
 309 
 310 struct fqlist {
 311         const char *brand_tag;
 312         const struct fq_info *table;
 313         int n;
 314 };
 315 
 316 static const struct fqlist pentium_m[] = {
 317 #define ENTRY(s, v)     { s, v, (int)(sizeof(v) / sizeof((v)[0])) }
 318         ENTRY(" 900", pentium_m_900),
 319         ENTRY("1000", pentium_m_1000),
 320         ENTRY("1100", pentium_m_1100),
 321         ENTRY("1200", pentium_m_1200),
 322         ENTRY("1300", pentium_m_1300),
 323         ENTRY("1400", pentium_m_1400),
 324         ENTRY("1500", pentium_m_1500),
 325         ENTRY("1600", pentium_m_1600),
 326         ENTRY("1700", pentium_m_1700),
 327 #undef ENTRY
 328 };
 329 
 330 static const struct fqlist pentium_m_dothan[] = {
 331 #define ENTRY(s, v)     { s, v, (int)(sizeof(v) / sizeof((v)[0])) }
 332         ENTRY("1.00", pentium_m_n723),
 333         ENTRY("1.10", pentium_m_n733),
 334         ENTRY("1.20", pentium_m_n753),
 335         ENTRY("1.40", pentium_m_n738),
 336 #if 0
 337         ENTRY("1.50", pentium_m_n758),
 338 #endif
 339         ENTRY("1.50", pentium_m_n715),
 340         ENTRY("1.60", pentium_m_n725),
 341         ENTRY("1.70", pentium_m_n735),
 342         ENTRY("1.80", pentium_m_n745),
 343         ENTRY("2.00", pentium_m_n755),
 344         ENTRY("2.10", pentium_m_n765),
 345 #undef ENTRY
 346 };
 347 
 348 struct est_cpu {
 349         const char *brand_prefix;
 350         const char *brand_suffix;
 351         const struct fqlist *list;
 352         int n;
 353 };
 354 
 355 static const struct est_cpu est_cpus[] = {
 356         {
 357                 "Intel(R) Pentium(R) M processor ", "MHz",
 358                 pentium_m,
 359                 (int)(sizeof(pentium_m) / sizeof(pentium_m[0]))
 360         },
 361         {
 362                 "Intel(R) Pentium(R) M processor ", "GHz",
 363                 pentium_m_dothan,
 364                 (int)(sizeof(pentium_m_dothan) / sizeof(pentium_m_dothan[0]))
 365         },
 366 };
 367 
 368 #define NESTCPUS          (int)(sizeof(est_cpus) / sizeof(est_cpus[0]))
 369 
 370 
 371 #define MSRVALUE(mhz, mv)       ((((mhz) / 100) << 8) | (((mv) - 700) / 16))
 372 #define MSR2MHZ(msr)            (int)((((u_int) (msr) >> 8) & 0xff) * 100)
 373 #define MSR2MV(msr)             (((int) (msr) & 0xff) * 16 + 700)
 374 
 375 static const struct fqlist *est_fqlist;
 376 
 377 static int      maxfreq;        /* max speed in Mhz */
 378 static int      maxvolts;       /* max voltage in mV */
 379 static int      curfreq;        /* current speed in Mhz */
 380 static int      curvolts;       /* current voltage in mV */
 381 #ifdef CONFIG_DVS_EMULATION
 382 static int      bochs;          /* true if bochs is active */
 383 #endif
 384 
 385 /*
 386  * Set CPU performance
 387  *
 388  * @level: percent of cpu speed
 389  */
 390 static int
 391 est_setperf(int level)
 392 {
 393         int i;
 394         int fq, max_mhz;
 395         u_int msr_lo, msr_hi;
 396 
 397         max_mhz = est_fqlist->table[0].mhz;
 398         fq = max_mhz * level / 100;
 399 
 400         for (i = est_fqlist->n - 1; i > 0; i--)
 401                 if (est_fqlist->table[i].mhz >= fq)
 402                         break;
 403 
 404         if (est_fqlist->table[i].mhz == curfreq)
 405                 return 0;
 406 
 407         curfreq = est_fqlist->table[i].mhz;
 408         curvolts = est_fqlist->table[i].mv;
 409 #ifdef DEBUG_EST
 410         DPRINTF(("setperf: %dMHz %dmV\n", curfreq, curvolts));
 411 #endif
 412 #ifdef CONFIG_DVS_EMULATION
 413         if (bochs)
 414                 return 0;
 415 #endif
 416         rdmsr(MSR_PERF_CTL, &msr_lo, &msr_hi);
 417         msr_lo = (msr_lo & ~0xffff) |
 418                 MSRVALUE(est_fqlist->table[i].mhz, est_fqlist->table[i].mv);
 419         wrmsr(MSR_PERF_CTL, msr_lo, msr_hi);
 420         return 0;
 421 }
 422 
 423 /*
 424  * Get CPU performance
 425  */
 426 static int
 427 est_getperf(void)
 428 {
 429         int max_mhz;
 430         int level;
 431 
 432         max_mhz = est_fqlist->table[0].mhz;
 433         ASSERT(max_mhz);
 434         level = curfreq * 100 / max_mhz;
 435         return level;
 436 }
 437 
 438 static void
 439 est_getinfo(struct cpufreqinfo *info)
 440 {
 441 
 442         info->maxfreq = maxfreq;
 443         info->maxvolts = maxvolts;
 444         info->freq = curfreq;
 445         info->volts = curvolts;
 446 }
 447 
 448 static int
 449 est_identify(char *brand_str)
 450 {
 451         int i, j, mhz, mv;
 452         size_t len;
 453         const struct est_cpu *cpu;
 454         u_int msr_lo, msr_hi;
 455         char *tag;
 456         const struct fqlist *fql;
 457 
 458         DPRINTF(("CPU brand: %s\n", brand_str));
 459 
 460 #ifdef CONFIG_DVS_EMULATION
 461         if (bochs) {
 462                 msr_lo = 0x1031;
 463                 cpu = &est_cpus[0];
 464                 est_fqlist = &cpu->list[7];
 465         } else
 466                 rdmsr(MSR_PERF_STATUS, &msr_lo, &msr_hi);
 467 #else
 468         rdmsr(MSR_PERF_STATUS, &msr_lo, &msr_hi);
 469 #endif
 470         mhz = MSR2MHZ(msr_lo);
 471         mv = MSR2MV(msr_lo);
 472 
 473 #ifdef CONFIG_DVS_EMULATION
 474         if (!bochs) {
 475 #endif
 476         /*
 477          * Look for a CPU matching brand_str.
 478          */
 479         for (i = 0; est_fqlist == NULL && i < NESTCPUS; i++) {
 480                 cpu = &est_cpus[i];
 481                 len = strnlen(cpu->brand_prefix, 48);
 482                 if (strncmp(cpu->brand_prefix, brand_str, len) != 0)
 483                         continue;
 484                 tag = brand_str + len;
 485                 for (j = 0; j < cpu->n; j++) {
 486                         fql = &cpu->list[j];
 487                         len = strnlen(fql->brand_tag, 48);
 488                         if (!strncmp(fql->brand_tag, tag, len) &&
 489                             !strncmp(cpu->brand_suffix, tag + len, 48)) {
 490                                 est_fqlist = fql;
 491                                 break;
 492                         }
 493                 }
 494         }
 495         if (est_fqlist == NULL) {
 496                 DPRINTF(("Unknown EST cpu, no changes possible\n"));
 497                 return ENXIO;
 498         }
 499 
 500         /*
 501          * Check that the current operating point is in our list.
 502          */
 503         for (i = est_fqlist->n - 1; i >= 0; i--)
 504                 if (est_fqlist->table[i].mhz == mhz)
 505                         break;
 506         if (i < 0) {
 507                 DPRINTF((" (not in table)\n"));
 508                 return ENXIO;
 509         }
 510 #ifdef CONFIG_DVS_EMULATION
 511         }
 512 #endif
 513         /*
 514          * Store current state
 515          */
 516         maxfreq = est_fqlist->table[0].mhz;
 517         maxvolts = est_fqlist->table[0].mv;
 518         curfreq = mhz;
 519         curvolts = mv;
 520         return 0;
 521 }
 522 
 523 static int
 524 est_probe(struct driver *self)
 525 {
 526         u_int regs[4];
 527         char brand_str[49];
 528         char *p, *q;
 529         u_int cpu_id;
 530 
 531 #ifdef CONFIG_DVS_EMULATION
 532         bochs = 0;
 533         if (bus_read_8(0xe9) == 0xe9) {
 534                 /*
 535                  * Detect Bochs. Fake the cpuid value.
 536                  */
 537                 bochs = 1;
 538                 cpu_id = 0x6d6;
 539                 strlcpy(brand_str,
 540                         "Intel(R) Pentium(R) M processor 1600MHz",
 541                         sizeof(brand_str));
 542                 DPRINTF(("CPU ID: %08x\n", cpu_id));
 543                 return est_identify(brand_str);
 544         }
 545 #endif
 546         /*
 547          * Check enhanced speed step capability
 548          */
 549         cpuid(1, regs);
 550         cpu_id = regs[0];
 551         DPRINTF(("CPU ID: %08x\n", cpu_id));
 552         if ((regs[2] & 0x80) == 0) {
 553                 DPRINTF(("cpu: clock control not supported\n"));
 554                 return ENXIO;
 555         }
 556 
 557         /*
 558          * Get CPU brand string
 559          */
 560         cpuid(0x80000002, regs);
 561         memcpy(brand_str, regs, sizeof(regs));
 562         cpuid(0x80000003, regs);
 563         memcpy(brand_str + 16, regs, sizeof(regs));
 564         cpuid(0x80000004, regs);
 565         memcpy(brand_str + 32, regs, sizeof(regs));
 566 
 567         /* Store string with lef-align */
 568         p = q = brand_str;
 569         while (*p == ' ')
 570                 p++;
 571         while (*p)
 572                 *q++ = *p++;
 573         *q = '\0';
 574 
 575         return est_identify(brand_str);
 576 }
 577 
 578 static int
 579 est_init(struct driver *self)
 580 {
 581 #ifdef DEBUG
 582         int i;
 583 #endif
 584 
 585         cpufreq_attach(&est_ops);
 586 
 587 #ifdef DEBUG
 588         printf("Enhanced SpeedStep %d MHz (%d mV)\n", curfreq, curvolts);
 589         printf("Speeds: ");
 590         for (i = 0; i < est_fqlist->n; i++)
 591                 printf("%d%s", est_fqlist->table[i].mhz,
 592                        i < est_fqlist->n - 1 ? ", " : " MHz\n");
 593 #endif
 594         return 0;
 595 }

/* [<][>][^][v][top][bottom][index][help] */