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

Annotation of sys/arch/amd64/amd64/powernow-k8.c, Revision 1.1.1.1

1.1       nbrk        1: /*     $OpenBSD: powernow-k8.c,v 1.19 2007/05/31 17:49:15 gwk Exp $ */
                      2: /*
                      3:  * Copyright (c) 2004 Martin Végiard.
                      4:  * Copyright (c) 2004-2005 Bruno Ducrot
                      5:  * Copyright (c) 2004 FUKUDA Nobuhiko <nfukuda@spa.is.uec.ac.jp>
                      6:  *
                      7:  * Redistribution and use in source and binary forms, with or without
                      8:  * modification, are permitted provided that the following conditions
                      9:  * are met:
                     10:  * 1. Redistributions of source code must retain the above copyright
                     11:  *    notice, this list of conditions and the following disclaimer.
                     12:  * 2. Redistributions in binary form must reproduce the above copyright
                     13:  *    notice, this list of conditions and the following disclaimer in the
                     14:  *    documentation and/or other materials provided with the distribution.
                     15:  *
                     16:  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
                     17:  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
                     18:  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
                     19:  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
                     20:  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
                     21:  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
                     22:  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
                     23:  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
                     24:  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
                     25:  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
                     26:  */
                     27: /* AMD POWERNOW K8 driver */
                     28:
                     29: #include <sys/types.h>
                     30: #include <sys/param.h>
                     31: #include <sys/systm.h>
                     32: #include <sys/malloc.h>
                     33: #include <sys/sysctl.h>
                     34:
                     35: #include <dev/isa/isareg.h>
                     36: #include <amd64/include/isa_machdep.h>
                     37:
                     38: #include <machine/cpu.h>
                     39: #include <machine/cpufunc.h>
                     40: #include <machine/bus.h>
                     41:
                     42: #include "acpicpu.h"
                     43:
                     44: #if NACPICPU > 0
                     45: #include <dev/acpi/acpidev.h>
                     46: #include <dev/acpi/acpivar.h>
                     47: #endif
                     48:
                     49: #define BIOS_START                     0xe0000
                     50: #define        BIOS_LEN                        0x20000
                     51:
                     52: extern int setperf_prio;
                     53:
                     54: /*
                     55:  * MSRs and bits used by PowerNow technology
                     56:  */
                     57: #define MSR_AMDK7_FIDVID_CTL           0xc0010041
                     58: #define MSR_AMDK7_FIDVID_STATUS                0xc0010042
                     59:
                     60: /* Bitfields used by K8 */
                     61:
                     62: #define PN8_CTR_FID(x)                 ((x) & 0x3f)
                     63: #define PN8_CTR_VID(x)                 (((x) & 0x1f) << 8)
                     64: #define PN8_CTR_PENDING(x)             (((x) & 1) << 32)
                     65:
                     66: #define PN8_STA_CFID(x)                        ((x) & 0x3f)
                     67: #define PN8_STA_SFID(x)                        (((x) >> 8) & 0x3f)
                     68: #define PN8_STA_MFID(x)                        (((x) >> 16) & 0x3f)
                     69: #define PN8_STA_PENDING(x)             (((x) >> 31) & 0x01)
                     70: #define PN8_STA_CVID(x)                        (((x) >> 32) & 0x1f)
                     71: #define PN8_STA_SVID(x)                        (((x) >> 40) & 0x1f)
                     72: #define PN8_STA_MVID(x)                        (((x) >> 48) & 0x1f)
                     73:
                     74: /* Reserved1 to PowerNow K8 configuration */
                     75: #define PN8_PSB_TO_RVO(x)              ((x) & 0x03)
                     76: #define PN8_PSB_TO_IRT(x)              (((x) >> 2) & 0x03)
                     77: #define PN8_PSB_TO_MVS(x)              (((x) >> 4) & 0x03)
                     78: #define PN8_PSB_TO_BATT(x)             (((x) >> 6) & 0x03)
                     79:
                     80: /* ACPI ctr_val status register to PowerNow K8 configuration */
                     81: #define PN8_ACPI_CTRL_TO_FID(x)                ((x) & 0x3f)
                     82: #define PN8_ACPI_CTRL_TO_VID(x)                (((x) >> 6) & 0x1f)
                     83: #define PN8_ACPI_CTRL_TO_VST(x)                (((x) >> 11) & 0x1f)
                     84: #define PN8_ACPI_CTRL_TO_MVS(x)                (((x) >> 18) & 0x03)
                     85: #define PN8_ACPI_CTRL_TO_PLL(x)                (((x) >> 20) & 0x7f)
                     86: #define PN8_ACPI_CTRL_TO_RVO(x)                (((x) >> 28) & 0x03)
                     87: #define PN8_ACPI_CTRL_TO_IRT(x)                (((x) >> 30) & 0x03)
                     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: #define COUNT_OFF_IRT(irt)     DELAY(10 * (1 << (irt)))
                     95: #define COUNT_OFF_VST(vst)     DELAY(20 * (vst))
                     96:
                     97: #define FID_TO_VCO_FID(fid)    \
                     98:        (((fid) < 8) ? (8 + ((fid) << 1)) : (fid))
                     99:
                    100: #define POWERNOW_MAX_STATES            16
                    101:
                    102: struct k8pnow_state {
                    103:        int freq;
                    104:        uint8_t fid;
                    105:        uint8_t vid;
                    106: };
                    107:
                    108: struct k8pnow_cpu_state {
                    109:        struct k8pnow_state state_table[POWERNOW_MAX_STATES];
                    110:        unsigned int n_states;
                    111:        unsigned int sgtc;
                    112:        unsigned int vst;
                    113:        unsigned int mvs;
                    114:        unsigned int pll;
                    115:        unsigned int rvo;
                    116:        unsigned int irt;
                    117:        int low;
                    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 cpuid;
                    131:        uint8_t pll;
                    132:        uint8_t fid;
                    133:        uint8_t vid;
                    134:        uint8_t n_states;
                    135: };
                    136:
                    137: struct k8pnow_cpu_state *k8pnow_current_state;
                    138:
                    139: int k8pnow_read_pending_wait(uint64_t *);
                    140: int k8pnow_decode_pst(struct k8pnow_cpu_state *, uint8_t *);
                    141: int k8pnow_states(struct k8pnow_cpu_state *, uint32_t, unsigned int,
                    142:     unsigned int);
                    143:
                    144: #if NACPICPU > 0
                    145: int k8pnow_acpi_init(struct k8pnow_cpu_state *, uint64_t);
                    146: void k8pnow_acpi_pss_changed(struct acpicpu_pss *, int);
                    147: int k8pnow_acpi_states(struct k8pnow_cpu_state *, struct acpicpu_pss *, int,
                    148:     uint64_t);
                    149: #endif
                    150:
                    151: int
                    152: k8pnow_read_pending_wait(uint64_t *status)
                    153: {
                    154:        unsigned int i = 100000;
                    155:
                    156:        while (i--) {
                    157:                *status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
                    158:                if (!PN8_STA_PENDING(*status))
                    159:                        return 0;
                    160:
                    161:        }
                    162:        printf("k8pnow_read_pending_wait: change pending stuck.\n");
                    163:        return 1;
                    164: }
                    165:
                    166: void
                    167: k8_powernow_setperf(int level)
                    168: {
                    169:        unsigned int i;
                    170:        uint64_t status;
                    171:        int cfid, cvid, fid = 0, vid = 0;
                    172:        int rvo;
                    173:        u_int val;
                    174:        struct k8pnow_cpu_state *cstate;
                    175:
                    176:        /*
                    177:         * We dont do a k8pnow_read_pending_wait here, need to ensure that the
                    178:         * change pending bit isn't stuck,
                    179:         */
                    180:        status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
                    181:        if (PN8_STA_PENDING(status))
                    182:                return;
                    183:        cfid = PN8_STA_CFID(status);
                    184:        cvid = PN8_STA_CVID(status);
                    185:
                    186:        cstate = k8pnow_current_state;
                    187:
                    188:        i = ((level * cstate->n_states) + 1) / 101;
                    189:        if (i >= cstate->n_states)
                    190:                i = cstate->n_states - 1;
                    191:
                    192:        fid = cstate->state_table[i].fid;
                    193:        vid = cstate->state_table[i].vid;
                    194:
                    195:        if (fid == cfid && vid == cvid)
                    196:                return;
                    197:
                    198:        /*
                    199:         * Phase 1: Raise core voltage to requested VID if frequency is
                    200:         * going up.
                    201:         */
                    202:        while (cvid > vid) {
                    203:                val = cvid - (1 << cstate->mvs);
                    204:                WRITE_FIDVID(cfid, (val > 0) ? val : 0, 1ULL);
                    205:                if (k8pnow_read_pending_wait(&status))
                    206:                        return;
                    207:                cvid = PN8_STA_CVID(status);
                    208:                COUNT_OFF_VST(cstate->vst);
                    209:        }
                    210:
                    211:        /* ... then raise to voltage + RVO (if required) */
                    212:        for (rvo = cstate->rvo; rvo > 0 && cvid > 0; --rvo) {
                    213:                /* XXX It's not clear from spec if we have to do that
                    214:                 * in 0.25 step or in MVS.  Therefore do it as it's done
                    215:                 * under Linux */
                    216:                WRITE_FIDVID(cfid, cvid - 1, 1ULL);
                    217:                if (k8pnow_read_pending_wait(&status))
                    218:                        return;
                    219:                cvid = PN8_STA_CVID(status);
                    220:                COUNT_OFF_VST(cstate->vst);
                    221:        }
                    222:
                    223:        /* Phase 2: change to requested core frequency */
                    224:        if (cfid != fid) {
                    225:                u_int vco_fid, vco_cfid;
                    226:
                    227:                vco_fid = FID_TO_VCO_FID(fid);
                    228:                vco_cfid = FID_TO_VCO_FID(cfid);
                    229:
                    230:                while (abs(vco_fid - vco_cfid) > 2) {
                    231:                        if (fid > cfid) {
                    232:                                if (cfid > 6)
                    233:                                        val = cfid + 2;
                    234:                                else
                    235:                                        val = FID_TO_VCO_FID(cfid) + 2;
                    236:                        } else
                    237:                                val = cfid - 2;
                    238:                        WRITE_FIDVID(val, cvid, (uint64_t)cstate->pll * 1000 / 5);
                    239:
                    240:                        if (k8pnow_read_pending_wait(&status))
                    241:                                return;
                    242:                        cfid = PN8_STA_CFID(status);
                    243:                        COUNT_OFF_IRT(cstate->irt);
                    244:
                    245:                        vco_cfid = FID_TO_VCO_FID(cfid);
                    246:                }
                    247:
                    248:                WRITE_FIDVID(fid, cvid, (uint64_t) cstate->pll * 1000 / 5);
                    249:                if (k8pnow_read_pending_wait(&status))
                    250:                        return;
                    251:                cfid = PN8_STA_CFID(status);
                    252:                COUNT_OFF_IRT(cstate->irt);
                    253:        }
                    254:
                    255:        /* Phase 3: change to requested voltage */
                    256:        if (cvid != vid) {
                    257:                WRITE_FIDVID(cfid, vid, 1ULL);
                    258:                if (k8pnow_read_pending_wait(&status))
                    259:                        return;
                    260:                cvid = PN8_STA_CVID(status);
                    261:                COUNT_OFF_VST(cstate->vst);
                    262:        }
                    263:
                    264:        if (cfid == fid || cvid == vid)
                    265:                cpuspeed = cstate->state_table[i].freq;
                    266: }
                    267:
                    268: /*
                    269:  * Given a set of pair of fid/vid, and number of performance states,
                    270:  * compute state_table via an insertion sort.
                    271:  */
                    272: int
                    273: k8pnow_decode_pst(struct k8pnow_cpu_state *cstate, uint8_t *p)
                    274: {
                    275:        int i, j, n;
                    276:        struct k8pnow_state state;
                    277:        for (n = 0, i = 0; i < cstate->n_states; i++) {
                    278:                state.fid = *p++;
                    279:                state.vid = *p++;
                    280:
                    281:                /*
                    282:                 * The minimum supported frequency per the data sheet is 800MHz
                    283:                 * The maximum supported frequency is 5000MHz.
                    284:                 */
                    285:                state.freq = 800 + state.fid * 100;
                    286:                j = n;
                    287:                while (j > 0 && cstate->state_table[j - 1].freq > state.freq) {
                    288:                        memcpy(&cstate->state_table[j],
                    289:                            &cstate->state_table[j - 1],
                    290:                            sizeof(struct k8pnow_state));
                    291:                        --j;
                    292:                }
                    293:                memcpy(&cstate->state_table[j], &state,
                    294:                    sizeof(struct k8pnow_state));
                    295:                n++;
                    296:        }
                    297:        return 1;
                    298: }
                    299:
                    300: #if NACPICPU > 0
                    301:
                    302: int
                    303: k8pnow_acpi_states(struct k8pnow_cpu_state * cstate, struct acpicpu_pss * pss,
                    304:     int nstates, uint64_t status)
                    305: {
                    306:        struct k8pnow_state state;
                    307:        int j, k, n;
                    308:        uint32_t ctrl;
                    309:
                    310:        k = -1;
                    311:
                    312:        for (n = 0; n < cstate->n_states; n++) {
                    313:                if (status == pss[n].pss_status)
                    314:                        k = n;
                    315:                ctrl = pss[n].pss_ctrl;
                    316:                state.fid = PN8_ACPI_CTRL_TO_FID(ctrl);
                    317:                state.vid = PN8_ACPI_CTRL_TO_VID(ctrl);
                    318:
                    319:                state.freq = pss[n].pss_core_freq;
                    320:                j = n;
                    321:                while (j > 0 && cstate->state_table[j - 1].freq > state.freq) {
                    322:                        memcpy(&cstate->state_table[j],
                    323:                            &cstate->state_table[j - 1],
                    324:                            sizeof(struct k8pnow_state));
                    325:                        --j;
                    326:                }
                    327:                memcpy(&cstate->state_table[j], &state,
                    328:                    sizeof(struct k8pnow_state));
                    329:        }
                    330:
                    331:        return k;
                    332: }
                    333:
                    334: void
                    335: k8pnow_acpi_pss_changed(struct acpicpu_pss * pss, int npss)
                    336: {
                    337:        int curs;
                    338:        struct k8pnow_cpu_state * cstate;
                    339:        uint32_t ctrl;
                    340:        uint64_t status;
                    341:
                    342:        status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
                    343:        cstate = k8pnow_current_state;
                    344:
                    345:        curs = k8pnow_acpi_states(cstate, pss, npss, status);
                    346:        ctrl = pss[curs].pss_ctrl;
                    347:        cstate->vst = PN8_ACPI_CTRL_TO_VST(ctrl);
                    348:        cstate->mvs = PN8_ACPI_CTRL_TO_MVS(ctrl);
                    349:        cstate->pll = PN8_ACPI_CTRL_TO_PLL(ctrl);
                    350:        cstate->irt = PN8_ACPI_CTRL_TO_IRT(ctrl);
                    351:        cstate->low = 0;
                    352:        cstate->n_states = npss;
                    353: }
                    354:
                    355: int
                    356: k8pnow_acpi_init(struct k8pnow_cpu_state * cstate, uint64_t status)
                    357: {
                    358:        int curs;
                    359:        uint32_t ctrl;
                    360:        struct acpicpu_pss *pss;
                    361:
                    362:        cstate->n_states = acpicpu_fetch_pss(&pss);
                    363:        if (cstate->n_states == 0)
                    364:                return 0;
                    365:        acpicpu_set_notify(k8pnow_acpi_pss_changed);
                    366:
                    367:        curs = k8pnow_acpi_states(cstate, pss, cstate->n_states, status);
                    368:        ctrl = pss[curs].pss_ctrl;
                    369:
                    370:        cstate->vst = PN8_ACPI_CTRL_TO_VST(ctrl);
                    371:        cstate->mvs = PN8_ACPI_CTRL_TO_MVS(ctrl);
                    372:        cstate->pll = PN8_ACPI_CTRL_TO_PLL(ctrl);
                    373:        cstate->irt = PN8_ACPI_CTRL_TO_IRT(ctrl);
                    374:        cstate->low = 0;
                    375:
                    376:        return 1;
                    377: }
                    378:
                    379: #endif /* NACPICPU */
                    380:
                    381: int
                    382: k8pnow_states(struct k8pnow_cpu_state *cstate, uint32_t cpusig,
                    383:     unsigned int fid, unsigned int vid)
                    384: {
                    385:        struct psb_s *psb;
                    386:        struct pst_s *pst;
                    387:        uint8_t *p;
                    388:        int i;
                    389:
                    390:        for (p = (u_int8_t *)ISA_HOLE_VADDR(BIOS_START);
                    391:            p < (u_int8_t *)ISA_HOLE_VADDR(BIOS_START + BIOS_LEN); p += 16) {
                    392:                if (memcmp(p, "AMDK7PNOW!", 10) == 0) {
                    393:                        psb = (struct psb_s *)p;
                    394:                        if (psb->version != 0x14)
                    395:                                return 0;
                    396:
                    397:                        cstate->vst = psb->ttime;
                    398:                        cstate->rvo = PN8_PSB_TO_RVO(psb->reserved);
                    399:                        cstate->irt = PN8_PSB_TO_IRT(psb->reserved);
                    400:                        cstate->mvs = PN8_PSB_TO_MVS(psb->reserved);
                    401:                        cstate->low = PN8_PSB_TO_BATT(psb->reserved);
                    402:                        p+= sizeof(struct psb_s);
                    403:
                    404:                        for(i = 0; i < psb->n_pst; ++i) {
                    405:                                pst = (struct pst_s *) p;
                    406:
                    407:                                cstate->pll = pst->pll;
                    408:                                cstate->n_states = pst->n_states;
                    409:                                if (cpusig == pst->cpuid &&
                    410:                                    pst->fid == fid && pst->vid == vid) {
                    411:                                        return (k8pnow_decode_pst(cstate,
                    412:                                            p+= sizeof (struct pst_s)));
                    413:                                }
                    414:                                p += sizeof(struct pst_s) + 2
                    415:                                    * cstate->n_states;
                    416:                        }
                    417:                }
                    418:        }
                    419:
                    420:        return 0;
                    421:
                    422: }
                    423:
                    424: void
                    425: k8_powernow_init(struct cpu_info *ci)
                    426: {
                    427:        uint64_t status;
                    428:        u_int maxfid, maxvid, i;
                    429:        u_int32_t extcpuid, dummy;
                    430:        struct k8pnow_cpu_state *cstate;
                    431:        struct k8pnow_state *state;
                    432:        char * techname = NULL;
                    433:
                    434:        if (setperf_prio > 1)
                    435:                return;
                    436:
                    437:        cstate = malloc(sizeof(struct k8pnow_cpu_state), M_DEVBUF, M_NOWAIT);
                    438:        if (!cstate)
                    439:                return;
                    440:
                    441:        cstate->n_states = 0;
                    442:        status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
                    443:        maxfid = PN8_STA_MFID(status);
                    444:        maxvid = PN8_STA_MVID(status);
                    445:
                    446:        /*
                    447:        * If start FID is different to max FID, then it is a
                    448:        * mobile processor.  If not, it is a low powered desktop
                    449:        * processor.
                    450:        */
                    451:        if (PN8_STA_SFID(status) != PN8_STA_MFID(status))
                    452:                techname = "PowerNow! K8";
                    453:        else
                    454:                techname = "Cool'n'Quiet K8";
                    455:
                    456: #if NACPICPU > 0
                    457:        /* If we have acpi check acpi first */
                    458:        if (!k8pnow_acpi_init(cstate, status))
                    459: #endif
                    460:        {
                    461:                if (!k8pnow_states(cstate, ci->ci_signature, maxfid, maxvid)) {
                    462:                        /* Extended CPUID signature value */
                    463:                        CPUID(0x80000001, extcpuid, dummy, dummy, dummy);
                    464:                        k8pnow_states(cstate, extcpuid, maxfid, maxvid);
                    465:                }
                    466:        }
                    467:        if (cstate->n_states) {
                    468:                printf("%s: %s %d MHz: speeds:",
                    469:                    ci->ci_dev->dv_xname, techname, cpuspeed);
                    470:                for (i = cstate->n_states; i > 0; i--) {
                    471:                        state = &cstate->state_table[i-1];
                    472:                        printf(" %d", state->freq);
                    473:                }
                    474:                printf(" MHz\n");
                    475:                k8pnow_current_state = cstate;
                    476:                cpu_setperf = k8_powernow_setperf;
                    477:                setperf_prio = 1;
                    478:                return;
                    479:        }
                    480:        free(cstate, M_DEVBUF);
                    481: }

CVSweb