[BACK]Return to powernow-k7.c CVS log [TXT][DIR] Up to [local] / sys / arch / i386 / i386

Annotation of sys/arch/i386/i386/powernow-k7.c, Revision 1.1

1.1     ! nbrk        1: /* $OpenBSD: powernow-k7.c,v 1.32 2007/07/27 03:03:37 gwk Exp $ */
        !             2:
        !             3: /*
        !             4:  * Copyright (c) 2004 Martin Végiard.
        !             5:  * Copyright (c) 2004-2005 Bruno Ducrot
        !             6:  * Copyright (c) 2004 FUKUDA Nobuhiko <nfukuda@spa.is.uec.ac.jp>
        !             7:  *
        !             8:  * Redistribution and use in source and binary forms, with or without
        !             9:  * modification, are permitted provided that the following conditions
        !            10:  * are met:
        !            11:  * 1. Redistributions of source code must retain the above copyright
        !            12:  *    notice, this list of conditions and the following disclaimer.
        !            13:  * 2. Redistributions in binary form must reproduce the above copyright
        !            14:  *    notice, this list of conditions and the following disclaimer in the
        !            15:  *    documentation and/or other materials provided with the distribution.
        !            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: /* AMD POWERNOW K7 driver */
        !            30:
        !            31: #include <sys/types.h>
        !            32: #include <sys/param.h>
        !            33: #include <sys/systm.h>
        !            34: #include <sys/malloc.h>
        !            35: #include <sys/sysctl.h>
        !            36:
        !            37: #include <machine/cpu.h>
        !            38: #include <machine/cpufunc.h>
        !            39: #include <machine/bus.h>
        !            40:
        !            41: #include <dev/isa/isareg.h>
        !            42: #include <i386/isa/isa_machdep.h>
        !            43:
        !            44: #include "acpicpu.h"
        !            45:
        !            46: #if NACPICPU > 0
        !            47: #include <dev/acpi/acpidev.h>
        !            48: #include <dev/acpi/acpivar.h>
        !            49: #endif
        !            50:
        !            51: #define BIOS_START                     0xe0000
        !            52: #define        BIOS_LEN                        0x20000
        !            53: #define BIOS_STEP                      16
        !            54:
        !            55: /*
        !            56:  * MSRs and bits used by PowerNow! technology
        !            57:  */
        !            58: #define MSR_AMDK7_FIDVID_CTL           0xc0010041
        !            59: #define MSR_AMDK7_FIDVID_STATUS                0xc0010042
        !            60: #define AMD_PN_FID_VID                 0x06
        !            61: #define AMD_ERRATA_A0_CPUSIG           0x660
        !            62:
        !            63: #define PN7_FLAG_ERRATA_A0             0x01
        !            64: #define PN7_FLAG_DESKTOP_VRM           0x02
        !            65:
        !            66: /* Bitfields used by K7 */
        !            67: #define PN7_PSB_VERSION                        0x12
        !            68: #define PN7_CTR_FID(x)                 ((x) & 0x1f)
        !            69: #define PN7_CTR_VID(x)                 (((x) & 0x1f) << 8)
        !            70: #define PN7_CTR_FIDC                   0x00010000
        !            71: #define PN7_CTR_VIDC                   0x00020000
        !            72: #define PN7_CTR_FIDCHRATIO             0x00100000
        !            73: #define PN7_CTR_SGTC(x)                        (((uint64_t)(x) & 0x000fffff) << 32)
        !            74:
        !            75: #define PN7_STA_CFID(x)                        ((x) & 0x1f)
        !            76: #define PN7_STA_SFID(x)                        (((x) >> 8) & 0x1f)
        !            77: #define PN7_STA_MFID(x)                        (((x) >> 16) & 0x1f)
        !            78: #define PN7_STA_CVID(x)                        (((x) >> 32) & 0x1f)
        !            79: #define PN7_STA_SVID(x)                        (((x) >> 40) & 0x1f)
        !            80: #define PN7_STA_MVID(x)                        (((x) >> 48) & 0x1f)
        !            81:
        !            82: /*
        !            83:  * ACPI ctr_val status register to powernow k7 configuration
        !            84:  */
        !            85: #define PN7_ACPI_CTRL_TO_FID(x)                ((x) & 0x1f)
        !            86: #define PN7_ACPI_CTRL_TO_VID(x)                (((x) >> 5) & 0x1f)
        !            87: #define PN7_ACPI_CTRL_TO_SGTC(x)       (((x) >> 10) & 0xffff)
        !            88:
        !            89: #define WRITE_FIDVID(fid, vid, ctrl)   \
        !            90:        wrmsr(MSR_AMDK7_FIDVID_CTL,     \
        !            91:            (((ctrl) << 32) | (1ULL << 16) | ((vid) << 8) | (fid)))
        !            92:
        !            93: /*
        !            94:  * Divide each value by 10 to get the processor multiplier.
        !            95:  * Taken from powernow-k7.c/Linux by Dave Jones
        !            96:  */
        !            97: static int k7pnow_fid_to_mult[32] = {
        !            98:        110, 115, 120, 125, 50, 55, 60, 65,
        !            99:        70, 75, 80, 85, 90, 95, 100, 105,
        !           100:        30, 190, 40, 200, 130, 135, 140, 210,
        !           101:        150, 225, 160, 165, 170, 180, -1, -1
        !           102: };
        !           103:
        !           104: #define POWERNOW_MAX_STATES            16
        !           105:
        !           106: struct k7pnow_state {
        !           107:        int freq;
        !           108:        int fid;
        !           109:        int vid;
        !           110: };
        !           111:
        !           112: struct k7pnow_cpu_state {
        !           113:        unsigned int fsb;
        !           114:        unsigned int sgtc;
        !           115:        struct k7pnow_state state_table[POWERNOW_MAX_STATES];
        !           116:        unsigned int n_states;
        !           117:        int flags;
        !           118: };
        !           119:
        !           120: struct psb_s {
        !           121:        char signature[10];     /* AMDK7PNOW! */
        !           122:        uint8_t version;
        !           123:        uint8_t flags;
        !           124:        uint16_t ttime;         /* Min Settling time */
        !           125:        uint8_t reserved;
        !           126:        uint8_t n_pst;
        !           127: };
        !           128:
        !           129: struct pst_s {
        !           130:        uint32_t signature;
        !           131:        uint8_t fsb;            /* Front Side Bus frequency (MHz) */
        !           132:        uint8_t fid;            /* Max Frequency code */
        !           133:        uint8_t vid;            /* Max Voltage code */
        !           134:        uint8_t n_states;       /* Number of states */
        !           135: };
        !           136:
        !           137: struct k7pnow_cpu_state *k7pnow_current_state;
        !           138: extern int setperf_prio;
        !           139:
        !           140: int k7pnow_decode_pst(struct k7pnow_cpu_state *, uint8_t *, int);
        !           141: int k7pnow_states(struct k7pnow_cpu_state *, uint32_t, unsigned int,
        !           142:     unsigned int);
        !           143:
        !           144: #if NACPICPU > 0
        !           145: int k7pnow_acpi_init(struct k7pnow_cpu_state * cstate, uint64_t status);
        !           146: int k7pnow_acpi_states(struct k7pnow_cpu_state * cstate,
        !           147:     struct acpicpu_pss *pss, int nstates, uint64_t status);
        !           148: void k7pnow_acpi_pss_changed(struct acpicpu_pss *pss, int npss);
        !           149: #endif
        !           150:
        !           151: void
        !           152: k7_powernow_setperf(int level)
        !           153: {
        !           154:        unsigned int i;
        !           155:        int cvid, cfid, vid = 0, fid = 0;
        !           156:        uint64_t status, ctl;
        !           157:        struct k7pnow_cpu_state * cstate;
        !           158:
        !           159:        cstate = k7pnow_current_state;
        !           160:
        !           161:        i = ((level * cstate->n_states) + 1) / 101;
        !           162:        if (i >= cstate->n_states)
        !           163:                i = cstate->n_states - 1;
        !           164:        fid = cstate->state_table[i].fid;
        !           165:        vid = cstate->state_table[i].vid;
        !           166:
        !           167:        if (fid == 0 || vid == 0)
        !           168:                return;
        !           169:
        !           170:        status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
        !           171:        cfid = PN7_STA_CFID(status);
        !           172:        cvid = PN7_STA_CVID(status);
        !           173:
        !           174:        /*
        !           175:         * We're already at the requested level.
        !           176:         */
        !           177:        if (fid == cfid && vid == cvid)
        !           178:                return;
        !           179:
        !           180:        ctl = rdmsr(MSR_AMDK7_FIDVID_CTL) & PN7_CTR_FIDCHRATIO;
        !           181:
        !           182:        ctl |= PN7_CTR_FID(fid);
        !           183:        ctl |= PN7_CTR_VID(vid);
        !           184:        ctl |= PN7_CTR_SGTC(cstate->sgtc);
        !           185:
        !           186:        if (cstate->flags & PN7_FLAG_ERRATA_A0)
        !           187:                disable_intr();
        !           188:
        !           189:        if (k7pnow_fid_to_mult[fid] < k7pnow_fid_to_mult[cfid]) {
        !           190:                wrmsr(MSR_AMDK7_FIDVID_CTL, ctl | PN7_CTR_FIDC);
        !           191:                if (vid != cvid)
        !           192:                        wrmsr(MSR_AMDK7_FIDVID_CTL, ctl | PN7_CTR_VIDC);
        !           193:        } else {
        !           194:                wrmsr(MSR_AMDK7_FIDVID_CTL, ctl | PN7_CTR_VIDC);
        !           195:                if (fid != cfid)
        !           196:                        wrmsr(MSR_AMDK7_FIDVID_CTL, ctl | PN7_CTR_FIDC);
        !           197:        }
        !           198:
        !           199:        if (cstate->flags & PN7_FLAG_ERRATA_A0)
        !           200:                enable_intr();
        !           201:
        !           202:        status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
        !           203:        cfid = PN7_STA_CFID(status);
        !           204:        cvid = PN7_STA_CVID(status);
        !           205:        if (cfid == fid || cvid == vid)
        !           206:                cpuspeed = cstate->state_table[i].freq;
        !           207: }
        !           208:
        !           209: /*
        !           210:  * Given a set of pair of fid/vid, and number of performance states,
        !           211:  * compute state_table via an insertion sort.
        !           212:  */
        !           213: int
        !           214: k7pnow_decode_pst(struct k7pnow_cpu_state * cstate, uint8_t *p, int npst)
        !           215: {
        !           216:        int i, j, n;
        !           217:        struct k7pnow_state state;
        !           218:
        !           219:        for (n = 0, i = 0; i < npst; ++i) {
        !           220:                state.fid = *p++;
        !           221:                state.vid = *p++;
        !           222:                state.freq = k7pnow_fid_to_mult[state.fid]/10 * cstate->fsb;
        !           223:                if ((cstate->flags & PN7_FLAG_ERRATA_A0) &&
        !           224:                    (k7pnow_fid_to_mult[state.fid] % 10) == 5)
        !           225:                        continue;
        !           226:
        !           227:                j = n;
        !           228:                while (j > 0 && cstate->state_table[j - 1].freq > state.freq) {
        !           229:                        memcpy(&cstate->state_table[j],
        !           230:                            &cstate->state_table[j - 1],
        !           231:                            sizeof(struct k7pnow_state));
        !           232:                        --j;
        !           233:                }
        !           234:                memcpy(&cstate->state_table[j], &state,
        !           235:                    sizeof(struct k7pnow_state));
        !           236:                ++n;
        !           237:        }
        !           238:        /*
        !           239:         * Fix powernow_max_states, if errata_a0 give us less states
        !           240:         * than expected.
        !           241:         */
        !           242:        cstate->n_states = n;
        !           243:        return 1;
        !           244: }
        !           245:
        !           246: int
        !           247: k7pnow_states(struct k7pnow_cpu_state *cstate, uint32_t cpusig,
        !           248:     unsigned int fid, unsigned int vid)
        !           249: {
        !           250:        int maxpst;
        !           251:        struct psb_s *psb;
        !           252:        struct pst_s *pst;
        !           253:        uint8_t *p;
        !           254:
        !           255:        /*
        !           256:         * Look in the 0xe0000 - 0x100000 physical address
        !           257:         * range for the pst tables; 16 byte blocks
        !           258:         */
        !           259:        for (p = (u_int8_t *)ISA_HOLE_VADDR(BIOS_START);
        !           260:            p < (u_int8_t *)ISA_HOLE_VADDR(BIOS_START + BIOS_LEN); p+=
        !           261:            BIOS_STEP) {
        !           262:                if (memcmp(p, "AMDK7PNOW!", 10) == 0) {
        !           263:                        psb = (struct psb_s *)p;
        !           264:                        if (psb->version != PN7_PSB_VERSION)
        !           265:                                return 0;
        !           266:
        !           267:                        cstate->sgtc = psb->ttime * cstate->fsb;
        !           268:                        if (cstate->sgtc < 100 * cstate->fsb)
        !           269:                                cstate->sgtc = 100 * cstate->fsb;
        !           270:                        if (psb->flags & 1)
        !           271:                                cstate->flags |= PN7_FLAG_DESKTOP_VRM;
        !           272:                        p += sizeof(struct psb_s);
        !           273:
        !           274:                        for (maxpst = 0; maxpst < psb->n_pst; maxpst++) {
        !           275:                                pst = (struct pst_s*) p;
        !           276:
        !           277:                                if (cpusig == pst->signature && fid == pst->fid
        !           278:                                    && vid == pst->vid) {
        !           279:
        !           280:                                        if (abs(cstate->fsb - pst->fsb) > 5)
        !           281:                                                continue;
        !           282:                                        cstate->n_states = pst->n_states;
        !           283:                                        return (k7pnow_decode_pst(cstate,
        !           284:                                            p + sizeof(struct pst_s),
        !           285:                                            cstate->n_states));
        !           286:                                }
        !           287:                                p += sizeof(struct pst_s) +
        !           288:                                    (2 * pst->n_states);
        !           289:                        }
        !           290:                }
        !           291:        }
        !           292:
        !           293:        return 0;
        !           294: }
        !           295:
        !           296: #if NACPICPU > 0
        !           297:
        !           298: int
        !           299: k7pnow_acpi_states(struct k7pnow_cpu_state * cstate, struct acpicpu_pss *pss,
        !           300:     int nstates, uint64_t status)
        !           301: {
        !           302:        struct k7pnow_state state;
        !           303:        int j, k, n;
        !           304:        uint32_t ctrl;
        !           305:
        !           306:        k = -1;
        !           307:        for (n = 0; n < cstate->n_states; n++) {
        !           308:                if (status == pss[n].pss_status)
        !           309:                        k = n;
        !           310:                ctrl = pss[n].pss_ctrl;
        !           311:                state.fid = PN7_ACPI_CTRL_TO_FID(ctrl);
        !           312:                state.vid = PN7_ACPI_CTRL_TO_VID(ctrl);
        !           313:
        !           314:                state.freq = pss[n].pss_core_freq;
        !           315:                j = n;
        !           316:                while (j > 0 && cstate->state_table[j - 1].freq > state.freq) {
        !           317:                        memcpy(&cstate->state_table[j],
        !           318:                            &cstate->state_table[j - 1],
        !           319:                        sizeof(struct k7pnow_state));
        !           320:                        --j;
        !           321:                }
        !           322:                memcpy(&cstate->state_table[j], &state,
        !           323:                    sizeof(struct k7pnow_state));
        !           324:        }
        !           325:        return k;
        !           326: }
        !           327:
        !           328: void
        !           329: k7pnow_acpi_pss_changed(struct acpicpu_pss *pss, int npss)
        !           330: {
        !           331:        int curs;
        !           332:        struct k7pnow_cpu_state *cstate;
        !           333:        uint32_t ctrl;
        !           334:        uint64_t status;
        !           335:
        !           336:        status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
        !           337:        cstate = k7pnow_current_state;
        !           338:
        !           339:        curs = k7pnow_acpi_states(cstate, pss, npss, status);
        !           340:        ctrl = pss[curs].pss_ctrl;
        !           341:        cstate->sgtc = PN7_ACPI_CTRL_TO_SGTC(ctrl);
        !           342:        cstate->n_states = npss;
        !           343: }
        !           344:
        !           345: int
        !           346: k7pnow_acpi_init(struct k7pnow_cpu_state *cstate, uint64_t status)
        !           347: {
        !           348:        int curs;
        !           349:        uint32_t ctrl;
        !           350:        struct acpicpu_pss *pss;
        !           351:        int mfid;
        !           352:
        !           353:        cstate->n_states = acpicpu_fetch_pss(&pss);
        !           354:        if (cstate->n_states == 0)
        !           355:                return 0;
        !           356:
        !           357:        /*
        !           358:         * XXX: Some BIOS supplied _PSS implementations have the wrong
        !           359:         * maximum frequency, if we encounter one of these punt and
        !           360:         * hope the legacy tables have correct values.
        !           361:         */
        !           362:        mfid = PN7_STA_MFID(status);
        !           363:        if (mfid != cstate->state_table[cstate->n_states - 1].fid) {
        !           364:                return 0;
        !           365:        }
        !           366:        curs = k7pnow_acpi_states(cstate, pss, cstate->n_states, status);
        !           367:
        !           368:        acpicpu_set_notify(k7pnow_acpi_pss_changed);
        !           369:        ctrl = pss[curs].pss_ctrl;
        !           370:        cstate->sgtc = PN7_ACPI_CTRL_TO_SGTC(ctrl);
        !           371:
        !           372:        return 1;
        !           373: }
        !           374:
        !           375: #endif /* NACPICPU */
        !           376:
        !           377: void
        !           378: k7_powernow_init(void)
        !           379: {
        !           380:        u_int regs[4];
        !           381:        uint64_t status;
        !           382:        u_int maxfid, startvid, currentfid;
        !           383:        struct k7pnow_cpu_state *cstate;
        !           384:        struct k7pnow_state *state;
        !           385:        struct cpu_info *ci;
        !           386:        char *techname = NULL;
        !           387:        int i;
        !           388:
        !           389:        if (setperf_prio > 1)
        !           390:                return;
        !           391:
        !           392:        ci = curcpu();
        !           393:
        !           394:        cpuid(0x80000000, regs);
        !           395:        if (regs[0] < 0x80000007)
        !           396:                return;
        !           397:
        !           398:        cpuid(0x80000007, regs);
        !           399:        if (!(regs[3] & AMD_PN_FID_VID))
        !           400:                return;
        !           401:
        !           402:        /* Extended CPUID signature value */
        !           403:        cpuid(0x80000001, regs);
        !           404:
        !           405:        cstate = malloc(sizeof(struct k7pnow_cpu_state), M_DEVBUF, M_NOWAIT);
        !           406:        if (!cstate)
        !           407:                return;
        !           408:
        !           409:        cstate->flags = cstate->n_states = 0;
        !           410:        if (ci->ci_signature == AMD_ERRATA_A0_CPUSIG)
        !           411:                cstate->flags |= PN7_FLAG_ERRATA_A0;
        !           412:
        !           413:        status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
        !           414:        maxfid = PN7_STA_MFID(status);
        !           415:        startvid = PN7_STA_SVID(status);
        !           416:        currentfid = PN7_STA_CFID(status);
        !           417:
        !           418:        cstate->fsb = cpuspeed / (k7pnow_fid_to_mult[currentfid]/10);
        !           419:
        !           420: #if NACPICPU > 0
        !           421:        /* If we have it try ACPI */
        !           422:        if (!k7pnow_acpi_init(cstate, status))
        !           423: #endif
        !           424:        {
        !           425:                /* if the base CPUID signature fails to match try, the extended one */
        !           426:                if (!k7pnow_states(cstate, ci->ci_signature, maxfid, startvid))
        !           427:                        k7pnow_states(cstate, regs[0], maxfid, startvid);
        !           428:        }
        !           429:
        !           430:        if (cstate->n_states) {
        !           431:                if (cstate->flags & PN7_FLAG_DESKTOP_VRM)
        !           432:                        techname = "Cool'n'Quiet K7";
        !           433:                else
        !           434:                        techname = "PowerNow! K7";
        !           435:                printf("%s: %s %d MHz: speeds:",
        !           436:                    ci->ci_dev.dv_xname, techname, cpuspeed);
        !           437:                for (i = cstate->n_states; i > 0; i--) {
        !           438:                        state = &cstate->state_table[i-1];
        !           439:                        printf(" %d", state->freq);
        !           440:                }
        !           441:                printf(" MHz\n");
        !           442:
        !           443:                k7pnow_current_state = cstate;
        !           444:                cpu_setperf = k7_powernow_setperf;
        !           445:                setperf_prio = 1;
        !           446:                return;
        !           447:        }
        !           448:        free(cstate, M_DEVBUF);
        !           449: }

CVSweb