[BACK]Return to est.c CVS log [TXT][DIR] Up to [local] / sys / arch / amd64 / amd64

Annotation of sys/arch/amd64/amd64/est.c, Revision 1.1.1.1

1.1       nbrk        1: /*     $OpenBSD: est.c,v 1.5 2007/06/07 11:20:58 dim 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:
                     30: /*
                     31:  * This is a driver for Intel's Enhanced SpeedStep, as implemented in
                     32:  * Pentium M processors.
                     33:  *
                     34:  * Reference documentation:
                     35:  *
                     36:  * - IA-32 Intel Architecture Software Developer's Manual, Volume 3:
                     37:  *   System Programming Guide.
                     38:  *   Section 13.14, Enhanced Intel SpeedStep technology.
                     39:  *   Table B-2, MSRs in Pentium M Processors.
                     40:  *   http://www.intel.com/design/pentium4/manuals/245472.htm
                     41:  *
                     42:  * - Intel Pentium M Processor Datasheet.
                     43:  *   Table 5, Voltage and Current Specifications.
                     44:  *   http://www.intel.com/design/mobile/datashts/252612.htm
                     45:  *
                     46:  * - Intel Pentium M Processor on 90 nm Process with 2-MB L2 Cache Datasheet
                     47:  *   Table 3-4, Voltage and Current Specifications.
                     48:  *   http://www.intel.com/design/mobile/datashts/302189.htm
                     49:  *
                     50:  * - Linux cpufreq patches, speedstep-centrino.c.
                     51:  *   Encoding of MSR_PERF_CTL and MSR_PERF_STATUS.
                     52:  *   http://www.codemonkey.org.uk/projects/cpufreq/cpufreq-2.4.22-pre6-1.gz
                     53:  */
                     54:
                     55:
                     56: #include <sys/param.h>
                     57: #include <sys/systm.h>
                     58: #include <sys/sysctl.h>
                     59:
                     60: #include <machine/cpu.h>
                     61: #include <machine/cpufunc.h>
                     62: #include <machine/specialreg.h>
                     63:
                     64: #define CPUVENDOR_INTEL 0
                     65: #define CPUVENDOR_VIA 8
                     66:
                     67:
                     68: /* Convert MHz and mV into IDs for passing to the MSR. */
                     69: #define ID16(MHz, mV, bus_clk) \
                     70:        ((((MHz * 100 + 50) / bus_clk) << 8) | ((mV ? mV - 700 : 0) >> 4))
                     71:
                     72: /* Possible bus speeds (multiplied by 100 for rounding) */
                     73: #define BUS100 10000
                     74: #define BUS133 13333
                     75: #define BUS166 16667
                     76: #define BUS200 20000
                     77: #define BUS266 26667
                     78: #define BUS333 33333
                     79:
                     80: #define MSR2MHZ(msr, bus) \
                     81:        (((((int) (msr) >> 8) & 0xff) * (bus) + 50) / 100)
                     82: #define MSR2MV(msr) \
                     83:        (((int) (msr) & 0xff) * 16 + 700)
                     84:
                     85: struct fqlist {
                     86:        int vendor: 5;
                     87:        unsigned bus_clk : 1;
                     88:        unsigned n : 5;
                     89:        const u_int16_t *table;
                     90: };
                     91:
                     92: static const struct fqlist *est_fqlist;
                     93:
                     94: static u_int16_t fake_table[3];
                     95: static struct fqlist fake_fqlist;
                     96:
                     97: extern int setperf_prio;
                     98: extern int perflevel;
                     99:
                    100: int bus_clock;
                    101:
                    102: void p4_get_bus_clock(struct cpu_info *);
                    103: void p3_get_bus_clock(struct cpu_info *);
                    104:
                    105: void
                    106: p4_get_bus_clock(struct cpu_info *ci)
                    107: {
                    108:        u_int64_t msr;
                    109:        int model, bus;
                    110:
                    111:        model = (ci->ci_signature >> 4) & 15;
                    112:        msr = rdmsr(MSR_EBC_FREQUENCY_ID);
                    113:        if (model < 2) {
                    114:                bus = (msr >> 21) & 0x7;
                    115:                switch (bus) {
                    116:                case 0:
                    117:                        bus_clock = BUS100;
                    118:                        break;
                    119:                case 1:
                    120:                        bus_clock = BUS133;
                    121:                        break;
                    122:                default:
                    123:                        printf("%s: unknown Pentium 4 (model %d) "
                    124:                            "EBC_FREQUENCY_ID value %d\n",
                    125:                            ci->ci_dev->dv_xname, model, bus);
                    126:                        break;
                    127:                }
                    128:        } else {
                    129:                bus = (msr >> 16) & 0x7;
                    130:                switch (bus) {
                    131:                case 0:
                    132:                        bus_clock = (model == 2) ? BUS100 : BUS266;
                    133:                        break;
                    134:                case 1:
                    135:                        bus_clock = BUS133;
                    136:                        break;
                    137:                case 2:
                    138:                        bus_clock = BUS200;
                    139:                        break;
                    140:                case 3:
                    141:                        bus_clock = BUS166;
                    142:                        break;
                    143:                default:
                    144:                        printf("%s: unknown Pentium 4 (model %d) "
                    145:                            "EBC_FREQUENCY_ID value %d\n",
                    146:                            ci->ci_dev->dv_xname, model, bus);
                    147:                        break;
                    148:                }
                    149:        }
                    150: }
                    151:
                    152: void
                    153: p3_get_bus_clock(struct cpu_info *ci)
                    154: {
                    155:        u_int64_t msr;
                    156:        int model, bus;
                    157:
                    158:        model = (ci->ci_signature >> 4) & 15;
                    159:        switch (model) {
                    160:        case 0xe: /* Core Duo/Solo */
                    161:        case 0xf: /* Core Xeon */
                    162:                msr = rdmsr(MSR_FSB_FREQ);
                    163:                bus = (msr >> 0) & 0x7;
                    164:                switch (bus) {
                    165:                case 5:
                    166:                        bus_clock = BUS100;
                    167:                        break;
                    168:                case 1:
                    169:                        bus_clock = BUS133;
                    170:                        break;
                    171:                case 3:
                    172:                        bus_clock = BUS166;
                    173:                        break;
                    174:                case 2:
                    175:                        bus_clock = BUS200;
                    176:                        break;
                    177:                case 0:
                    178:                        bus_clock = BUS266;
                    179:                        break;
                    180:                case 4:
                    181:                        bus_clock = BUS333;
                    182:                        break;
                    183:                default:
                    184:                        printf("%s: unknown Core FSB_FREQ value %d",
                    185:                            ci->ci_dev->dv_xname, bus);
                    186:                        break;
                    187:                }
                    188:                break;
                    189:        default:
                    190:                printf("%s: unknown i686 model %d, can't get bus clock",
                    191:                    ci->ci_dev->dv_xname, model);
                    192:        }
                    193: }
                    194:
                    195: void
                    196: est_init(struct cpu_info *ci)
                    197: {
                    198:        const char *cpu_device = ci->ci_dev->dv_xname;
                    199:        int vendor = -1;
                    200:        int i, mhz, mv, low, high, family;
                    201:        u_int64_t msr;
                    202:        u_int16_t idhi, idlo, cur;
                    203:        u_int8_t crhi, crlo, crcur;
                    204:
                    205:        if (setperf_prio > 3)
                    206:                return;
                    207:
                    208:        family = (ci->ci_signature >> 8) & 15;
                    209:        if (family == 0xf) {
                    210:                p4_get_bus_clock(ci);
                    211:        } else if (family == 6) {
                    212:                p3_get_bus_clock(ci);
                    213:        }
                    214:        if (bus_clock == 0) {
                    215:                printf("%s: EST: unknown system bus clock\n", cpu_device);
                    216:                return;
                    217:        }
                    218:
                    219:        msr = rdmsr(MSR_PERF_STATUS);
                    220:        idhi = (msr >> 32) & 0xffff;
                    221:        idlo = (msr >> 48) & 0xffff;
                    222:        cur = msr & 0xffff;
                    223:        crhi = (idhi  >> 8) & 0xff;
                    224:        crlo = (idlo  >> 8) & 0xff;
                    225:        crcur = (cur >> 8) & 0xff;
                    226:        if (crlo == 0 || crhi == crlo) {
                    227:                /*
                    228:                 * Don't complain about these cases, and silently disable EST:
                    229:                 * - A lowest clock ratio of 0, which seems to happen on all
                    230:                 *   Pentium 4's that report EST.
                    231:                 * - An equal highest and lowest clock ratio, which happens on
                    232:                 *   at least the Core 2 Duo X6800, maybe on newer models too.
                    233:                 */
                    234:                return;
                    235:        }
                    236:        if (crhi == 0 || crcur == 0 || crlo > crhi ||
                    237:            crcur < crlo || crcur > crhi) {
                    238:                /*
                    239:                 * Do complain about other weirdness, because we first want to
                    240:                 * know about it, before we decide what to do with it.
                    241:                 */
                    242:                printf("%s: EST: strange msr value 0x%016llx\n",
                    243:                    cpu_device, msr);
                    244:                return;
                    245:        }
                    246:        if (est_fqlist == NULL) {
                    247:                printf("%s: unknown Enhanced SpeedStep CPU, msr 0x%016llx\n",
                    248:                    cpu_device, msr);
                    249:
                    250:                /*
                    251:                 * Generate a fake table with the power states we know.
                    252:                 */
                    253:                fake_table[0] = idhi;
                    254:                if (cur == idhi || cur == idlo) {
                    255:                        printf("%s: using only highest and lowest power "
                    256:                            "states\n", cpu_device);
                    257:
                    258:                        fake_table[1] = idlo;
                    259:                        fake_fqlist.n = 2;
                    260:                } else {
                    261:                        printf("%s: using only highest, current and lowest "
                    262:                            "power states\n", cpu_device);
                    263:
                    264:                        fake_table[1] = cur;
                    265:                        fake_table[2] = idlo;
                    266:                        fake_fqlist.n = 3;
                    267:                }
                    268:                fake_fqlist.vendor = vendor;
                    269:                fake_fqlist.table = fake_table;
                    270:                est_fqlist = &fake_fqlist;
                    271:        }
                    272:
                    273:        mhz = MSR2MHZ(cur, bus_clock);
                    274:        mv = MSR2MV(cur);
                    275:        printf("%s: Enhanced SpeedStep %d MHz (%d mV)", cpu_device, mhz, mv);
                    276:
                    277:        low = MSR2MHZ(est_fqlist->table[est_fqlist->n - 1], bus_clock);
                    278:        high = MSR2MHZ(est_fqlist->table[0], bus_clock);
                    279:        perflevel = (mhz - low) * 100 / (high - low);
                    280:
                    281:        /*
                    282:         * OK, tell the user the available frequencies.
                    283:         */
                    284:        printf(": speeds: ");
                    285:        for (i = 0; i < est_fqlist->n; i++)
                    286:                printf("%d%s", MSR2MHZ(est_fqlist->table[i], bus_clock),
                    287:                    i < est_fqlist->n - 1 ? ", " : " MHz\n");
                    288:
                    289:        cpu_setperf = est_setperf;
                    290:        setperf_prio = 3;
                    291: }
                    292:
                    293: void
                    294: est_setperf(int level)
                    295: {
                    296:        int i;
                    297:        uint64_t msr;
                    298:
                    299:        if (est_fqlist == NULL)
                    300:                return;
                    301:
                    302:        i = ((level * est_fqlist->n) + 1) / 101;
                    303:        if (i >= est_fqlist->n)
                    304:                i = est_fqlist->n - 1;
                    305:        i = est_fqlist->n - 1 - i;
                    306:
                    307:        msr = rdmsr(MSR_PERF_CTL);
                    308:        msr &= ~0xffffULL;
                    309:        msr |= est_fqlist->table[i];
                    310:        wrmsr(MSR_PERF_CTL, msr);
                    311:        cpuspeed = MSR2MHZ(est_fqlist->table[i], bus_clock);
                    312: }

CVSweb