[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     ! 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