1*f4e70637Sjsg /* $OpenBSD: powernow-k8.c,v 1.35 2023/01/30 10:49:05 jsg Exp $ */
296771376Sderaadt
37aa8d3a7Stedu /*
47aa8d3a7Stedu * Copyright (c) 2004 Martin V�giard.
57aa8d3a7Stedu * Copyright (c) 2004-2005 Bruno Ducrot
67aa8d3a7Stedu * Copyright (c) 2004 FUKUDA Nobuhiko <nfukuda@spa.is.uec.ac.jp>
77aa8d3a7Stedu *
87aa8d3a7Stedu * Redistribution and use in source and binary forms, with or without
97aa8d3a7Stedu * modification, are permitted provided that the following conditions
107aa8d3a7Stedu * are met:
117aa8d3a7Stedu * 1. Redistributions of source code must retain the above copyright
127aa8d3a7Stedu * notice, this list of conditions and the following disclaimer.
137aa8d3a7Stedu * 2. Redistributions in binary form must reproduce the above copyright
147aa8d3a7Stedu * notice, this list of conditions and the following disclaimer in the
157aa8d3a7Stedu * documentation and/or other materials provided with the distribution.
167aa8d3a7Stedu *
177aa8d3a7Stedu * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
187aa8d3a7Stedu * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
197aa8d3a7Stedu * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
207aa8d3a7Stedu * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
217aa8d3a7Stedu * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
227aa8d3a7Stedu * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
237aa8d3a7Stedu * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
247aa8d3a7Stedu * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
257aa8d3a7Stedu * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
267aa8d3a7Stedu * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
277aa8d3a7Stedu */
287aa8d3a7Stedu /* AMD POWERNOW K8 driver */
297aa8d3a7Stedu
307aa8d3a7Stedu #include <sys/param.h>
317aa8d3a7Stedu #include <sys/systm.h>
327aa8d3a7Stedu #include <sys/malloc.h>
337aa8d3a7Stedu #include <sys/sysctl.h>
347aa8d3a7Stedu
357aa8d3a7Stedu #include <dev/isa/isareg.h>
367aa8d3a7Stedu #include <i386/isa/isa_machdep.h>
377aa8d3a7Stedu
387aa8d3a7Stedu #include <machine/cpufunc.h>
397aa8d3a7Stedu #include <machine/bus.h>
407aa8d3a7Stedu
413cd93248Sgwk #include "acpicpu.h"
423cd93248Sgwk
433cd93248Sgwk #if NACPICPU > 0
443cd93248Sgwk #include <dev/acpi/acpidev.h>
453cd93248Sgwk #endif
463cd93248Sgwk
477aa8d3a7Stedu #define BIOS_START 0xe0000
487aa8d3a7Stedu #define BIOS_LEN 0x20000
49668c25f3Sdlg #define BIOS_STEP 16
507aa8d3a7Stedu
517aa8d3a7Stedu /*
52bda76761Stom * MSRs and bits used by PowerNow! technology
537aa8d3a7Stedu */
547aa8d3a7Stedu #define MSR_AMDK7_FIDVID_CTL 0xc0010041
557aa8d3a7Stedu #define MSR_AMDK7_FIDVID_STATUS 0xc0010042
56668c25f3Sdlg #define AMD_PN_FID_VID 0x06
577aa8d3a7Stedu
587aa8d3a7Stedu /* Bitfields used by K8 */
597aa8d3a7Stedu
607aa8d3a7Stedu #define PN8_CTR_FID(x) ((x) & 0x3f)
617aa8d3a7Stedu #define PN8_CTR_VID(x) (((x) & 0x1f) << 8)
627aa8d3a7Stedu #define PN8_CTR_PENDING(x) (((x) & 1) << 32)
637aa8d3a7Stedu
647aa8d3a7Stedu #define PN8_STA_CFID(x) ((x) & 0x3f)
657aa8d3a7Stedu #define PN8_STA_SFID(x) (((x) >> 8) & 0x3f)
667aa8d3a7Stedu #define PN8_STA_MFID(x) (((x) >> 16) & 0x3f)
677aa8d3a7Stedu #define PN8_STA_PENDING(x) (((x) >> 31) & 0x01)
687aa8d3a7Stedu #define PN8_STA_CVID(x) (((x) >> 32) & 0x1f)
697aa8d3a7Stedu #define PN8_STA_SVID(x) (((x) >> 40) & 0x1f)
707aa8d3a7Stedu #define PN8_STA_MVID(x) (((x) >> 48) & 0x1f)
717aa8d3a7Stedu
727aa8d3a7Stedu /* Reserved1 to powernow k8 configuration */
73668c25f3Sdlg #define PN8_PSB_VERSION 0x14
747aa8d3a7Stedu #define PN8_PSB_TO_RVO(x) ((x) & 0x03)
757aa8d3a7Stedu #define PN8_PSB_TO_IRT(x) (((x) >> 2) & 0x03)
767aa8d3a7Stedu #define PN8_PSB_TO_MVS(x) (((x) >> 4) & 0x03)
777aa8d3a7Stedu #define PN8_PSB_TO_BATT(x) (((x) >> 6) & 0x03)
787aa8d3a7Stedu
797aa8d3a7Stedu /* ACPI ctr_val status register to powernow k8 configuration */
80668c25f3Sdlg #define PN8_ACPI_CTRL_TO_FID(x) ((x) & 0x3f)
81668c25f3Sdlg #define PN8_ACPI_CTRL_TO_VID(x) (((x) >> 6) & 0x1f)
82668c25f3Sdlg #define PN8_ACPI_CTRL_TO_VST(x) (((x) >> 11) & 0x1f)
83668c25f3Sdlg #define PN8_ACPI_CTRL_TO_MVS(x) (((x) >> 18) & 0x03)
84668c25f3Sdlg #define PN8_ACPI_CTRL_TO_PLL(x) (((x) >> 20) & 0x7f)
85668c25f3Sdlg #define PN8_ACPI_CTRL_TO_RVO(x) (((x) >> 28) & 0x03)
86668c25f3Sdlg #define PN8_ACPI_CTRL_TO_IRT(x) (((x) >> 30) & 0x03)
877aa8d3a7Stedu
88c5cdb568Sgwk #define PN8_PSS_CFID(x) ((x) & 0x3f)
89c5cdb568Sgwk #define PN8_PSS_CVID(x) (((x) >> 6) & 0x1f)
90c5cdb568Sgwk
91668c25f3Sdlg #define PN8_PLL_LOCK(x) ((x) * 1000/5)
927aa8d3a7Stedu #define WRITE_FIDVID(fid, vid, ctrl) \
937aa8d3a7Stedu wrmsr(MSR_AMDK7_FIDVID_CTL, \
947aa8d3a7Stedu (((ctrl) << 32) | (1ULL << 16) | ((vid) << 8) | (fid)))
957aa8d3a7Stedu
967aa8d3a7Stedu #define COUNT_OFF_IRT(irt) DELAY(10 * (1 << (irt)))
977aa8d3a7Stedu #define COUNT_OFF_VST(vst) DELAY(20 * (vst))
987aa8d3a7Stedu
997aa8d3a7Stedu #define FID_TO_VCO_FID(fid) \
1007aa8d3a7Stedu (((fid) < 8) ? (8 + ((fid) << 1)) : (fid))
1017aa8d3a7Stedu
1027aa8d3a7Stedu #define POWERNOW_MAX_STATES 16
1037aa8d3a7Stedu
1047aa8d3a7Stedu struct k8pnow_state {
1057aa8d3a7Stedu int freq;
10635958b2eStedu uint8_t fid;
10735958b2eStedu uint8_t vid;
1087aa8d3a7Stedu };
1097aa8d3a7Stedu
1107aa8d3a7Stedu struct k8pnow_cpu_state {
1117aa8d3a7Stedu struct k8pnow_state state_table[POWERNOW_MAX_STATES];
1127aa8d3a7Stedu unsigned int n_states;
1137aa8d3a7Stedu unsigned int sgtc;
1147aa8d3a7Stedu unsigned int vst;
1157aa8d3a7Stedu unsigned int mvs;
1167aa8d3a7Stedu unsigned int pll;
1177aa8d3a7Stedu unsigned int rvo;
1187aa8d3a7Stedu unsigned int irt;
1197aa8d3a7Stedu int low;
1207aa8d3a7Stedu };
1217aa8d3a7Stedu
1227aa8d3a7Stedu struct psb_s {
1237aa8d3a7Stedu char signature[10]; /* AMDK7PNOW! */
1247aa8d3a7Stedu uint8_t version;
1257aa8d3a7Stedu uint8_t flags;
1267aa8d3a7Stedu uint16_t ttime; /* Min Settling time */
1277aa8d3a7Stedu uint8_t reserved;
1287aa8d3a7Stedu uint8_t n_pst;
1297aa8d3a7Stedu };
1307aa8d3a7Stedu
1317aa8d3a7Stedu struct pst_s {
13235958b2eStedu uint32_t cpuid;
13335958b2eStedu uint8_t pll;
13435958b2eStedu uint8_t fid;
13535958b2eStedu uint8_t vid;
13635958b2eStedu uint8_t n_states;
1377aa8d3a7Stedu };
1387aa8d3a7Stedu
13934b4ab5eSuwe struct k8pnow_cpu_state *k8pnow_current_state = NULL;
140aad5aaffSgwk extern int setperf_prio;
141c5cdb568Sgwk extern int perflevel;
1427aa8d3a7Stedu
1437aa8d3a7Stedu int k8pnow_read_pending_wait(uint64_t *);
14435958b2eStedu int k8pnow_decode_pst(struct k8pnow_cpu_state *, uint8_t *);
145c5cdb568Sgwk int k8pnow_states(struct k8pnow_cpu_state *, uint32_t, unsigned int, unsigned int);
146c5cdb568Sgwk void k8pnow_transition(struct k8pnow_cpu_state *e, int);
1477aa8d3a7Stedu
1483cd93248Sgwk #if NACPICPU > 0
1493cd93248Sgwk int k8pnow_acpi_init(struct k8pnow_cpu_state *, uint64_t);
1503cd93248Sgwk void k8pnow_acpi_pss_changed(struct acpicpu_pss *, int);
1513cd93248Sgwk int k8pnow_acpi_states(struct k8pnow_cpu_state *, struct acpicpu_pss *, int,
1523cd93248Sgwk uint64_t);
1533cd93248Sgwk #endif
1543cd93248Sgwk
15596771376Sderaadt int
k8pnow_read_pending_wait(uint64_t * status)15696771376Sderaadt k8pnow_read_pending_wait(uint64_t *status)
15796771376Sderaadt {
1589e71eeb7Sgwk unsigned int i = 100000;
15996771376Sderaadt
16035958b2eStedu while (i--) {
1617aa8d3a7Stedu *status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
16235958b2eStedu if (!PN8_STA_PENDING(*status))
1637aa8d3a7Stedu return 0;
16435958b2eStedu
16535958b2eStedu }
16635958b2eStedu printf("k8pnow_read_pending_wait: change pending stuck.\n");
16735958b2eStedu return 1;
1687aa8d3a7Stedu }
1697aa8d3a7Stedu
17096773847Sdim void
k8_powernow_setperf(int level)1717aa8d3a7Stedu k8_powernow_setperf(int level)
1727aa8d3a7Stedu {
1731f3ce405Sgwk unsigned int i;
1747aa8d3a7Stedu struct k8pnow_cpu_state *cstate;
1757aa8d3a7Stedu
176c5cdb568Sgwk cstate = k8pnow_current_state;
177c5cdb568Sgwk
178c5cdb568Sgwk i = ((level * cstate->n_states) + 1) / 101;
179c5cdb568Sgwk if (i >= cstate->n_states)
180c5cdb568Sgwk i = cstate->n_states - 1;
181c5cdb568Sgwk
182c5cdb568Sgwk k8pnow_transition(cstate, i);
183c5cdb568Sgwk }
184c5cdb568Sgwk
185c5cdb568Sgwk void
k8pnow_transition(struct k8pnow_cpu_state * cstate,int level)186c5cdb568Sgwk k8pnow_transition(struct k8pnow_cpu_state *cstate, int level)
187c5cdb568Sgwk {
188c5cdb568Sgwk uint64_t status;
189c5cdb568Sgwk int cfid, cvid, fid = 0, vid = 0;
190c5cdb568Sgwk int rvo;
191c5cdb568Sgwk u_int val;
192c5cdb568Sgwk
1937aa8d3a7Stedu /*
1947aa8d3a7Stedu * We dont do a k8pnow_read_pending_wait here, need to ensure that the
1957aa8d3a7Stedu * change pending bit isn't stuck,
1967aa8d3a7Stedu */
1977aa8d3a7Stedu status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
1987aa8d3a7Stedu if (PN8_STA_PENDING(status))
19996773847Sdim return;
2007aa8d3a7Stedu cfid = PN8_STA_CFID(status);
2017aa8d3a7Stedu cvid = PN8_STA_CVID(status);
2027aa8d3a7Stedu
203c5cdb568Sgwk fid = cstate->state_table[level].fid;
204c5cdb568Sgwk vid = cstate->state_table[level].vid;
2057aa8d3a7Stedu
2067aa8d3a7Stedu if (fid == cfid && vid == cvid)
20796773847Sdim return;
2087aa8d3a7Stedu
2097aa8d3a7Stedu /*
2107aa8d3a7Stedu * Phase 1: Raise core voltage to requested VID if frequency is
2117aa8d3a7Stedu * going up.
2127aa8d3a7Stedu */
2137aa8d3a7Stedu while (cvid > vid) {
2147aa8d3a7Stedu val = cvid - (1 << cstate->mvs);
2157aa8d3a7Stedu WRITE_FIDVID(cfid, (val > 0) ? val : 0, 1ULL);
2167aa8d3a7Stedu if (k8pnow_read_pending_wait(&status))
21796773847Sdim return;
2187aa8d3a7Stedu cvid = PN8_STA_CVID(status);
2197aa8d3a7Stedu COUNT_OFF_VST(cstate->vst);
2207aa8d3a7Stedu }
2217aa8d3a7Stedu
2227aa8d3a7Stedu /* ... then raise to voltage + RVO (if required) */
2237aa8d3a7Stedu for (rvo = cstate->rvo; rvo > 0 && cvid > 0; --rvo) {
2247aa8d3a7Stedu /* XXX It's not clear from spec if we have to do that
2257aa8d3a7Stedu * in 0.25 step or in MVS. Therefore do it as it's done
2267aa8d3a7Stedu * under Linux */
2277aa8d3a7Stedu WRITE_FIDVID(cfid, cvid - 1, 1ULL);
2287aa8d3a7Stedu if (k8pnow_read_pending_wait(&status))
22996773847Sdim return;
2307aa8d3a7Stedu cvid = PN8_STA_CVID(status);
2317aa8d3a7Stedu COUNT_OFF_VST(cstate->vst);
2327aa8d3a7Stedu }
2337aa8d3a7Stedu
2347aa8d3a7Stedu /* Phase 2: change to requested core frequency */
2357aa8d3a7Stedu if (cfid != fid) {
2367aa8d3a7Stedu u_int vco_fid, vco_cfid;
2377aa8d3a7Stedu
2387aa8d3a7Stedu vco_fid = FID_TO_VCO_FID(fid);
2397aa8d3a7Stedu vco_cfid = FID_TO_VCO_FID(cfid);
2407aa8d3a7Stedu
2417aa8d3a7Stedu while (abs(vco_fid - vco_cfid) > 2) {
2427aa8d3a7Stedu if (fid > cfid) {
2437aa8d3a7Stedu if (cfid > 6)
2447aa8d3a7Stedu val = cfid + 2;
2457aa8d3a7Stedu else
2467aa8d3a7Stedu val = FID_TO_VCO_FID(cfid) + 2;
2477aa8d3a7Stedu } else
2487aa8d3a7Stedu val = cfid - 2;
249c5cdb568Sgwk WRITE_FIDVID(val, cvid, (uint64_t)cstate->pll * 1000 / 5);
2507aa8d3a7Stedu
2517aa8d3a7Stedu if (k8pnow_read_pending_wait(&status))
25296773847Sdim return;
2537aa8d3a7Stedu cfid = PN8_STA_CFID(status);
2547aa8d3a7Stedu COUNT_OFF_IRT(cstate->irt);
2557aa8d3a7Stedu
2567aa8d3a7Stedu vco_cfid = FID_TO_VCO_FID(cfid);
2577aa8d3a7Stedu }
2587aa8d3a7Stedu
259c5cdb568Sgwk WRITE_FIDVID(fid, cvid, (uint64_t) cstate->pll * 1000 / 5);
2607aa8d3a7Stedu if (k8pnow_read_pending_wait(&status))
26196773847Sdim return;
2627aa8d3a7Stedu cfid = PN8_STA_CFID(status);
2637aa8d3a7Stedu COUNT_OFF_IRT(cstate->irt);
2647aa8d3a7Stedu }
2657aa8d3a7Stedu
2667aa8d3a7Stedu /* Phase 3: change to requested voltage */
2677aa8d3a7Stedu if (cvid != vid) {
2687aa8d3a7Stedu WRITE_FIDVID(cfid, vid, 1ULL);
2697aa8d3a7Stedu if (k8pnow_read_pending_wait(&status))
27096773847Sdim return;
2717aa8d3a7Stedu cvid = PN8_STA_CVID(status);
2727aa8d3a7Stedu COUNT_OFF_VST(cstate->vst);
2737aa8d3a7Stedu }
2747aa8d3a7Stedu
27530634235Sgwk if (cfid == fid || cvid == vid)
276c5cdb568Sgwk cpuspeed = cstate->state_table[level].freq;
2777aa8d3a7Stedu }
2787aa8d3a7Stedu
2797aa8d3a7Stedu /*
2807aa8d3a7Stedu * Given a set of pair of fid/vid, and number of performance states,
2817aa8d3a7Stedu * compute state_table via an insertion sort.
2827aa8d3a7Stedu */
2837aa8d3a7Stedu int
k8pnow_decode_pst(struct k8pnow_cpu_state * cstate,uint8_t * p)28435958b2eStedu k8pnow_decode_pst(struct k8pnow_cpu_state *cstate, uint8_t *p)
2857aa8d3a7Stedu {
2867aa8d3a7Stedu int i, j, n;
2877aa8d3a7Stedu struct k8pnow_state state;
28896771376Sderaadt
28935958b2eStedu for (n = 0, i = 0; i < cstate->n_states; i++) {
2907aa8d3a7Stedu state.fid = *p++;
2917aa8d3a7Stedu state.vid = *p++;
2927aa8d3a7Stedu
29334b4ab5eSuwe /*
29434b4ab5eSuwe * The minimum supported frequency per the data sheet is 800MHz
29534b4ab5eSuwe * The maximum supported frequency is 5000MHz.
29634b4ab5eSuwe */
29734b4ab5eSuwe state.freq = 800 + state.fid * 100;
2987aa8d3a7Stedu j = n;
29935958b2eStedu while (j > 0 && cstate->state_table[j - 1].freq > state.freq) {
3007aa8d3a7Stedu memcpy(&cstate->state_table[j],
3017aa8d3a7Stedu &cstate->state_table[j - 1],
3027aa8d3a7Stedu sizeof(struct k8pnow_state));
3037aa8d3a7Stedu --j;
3047aa8d3a7Stedu }
3057aa8d3a7Stedu memcpy(&cstate->state_table[j], &state,
3067aa8d3a7Stedu sizeof(struct k8pnow_state));
3077aa8d3a7Stedu n++;
3087aa8d3a7Stedu }
3097aa8d3a7Stedu return 1;
3107aa8d3a7Stedu }
3117aa8d3a7Stedu
3127aa8d3a7Stedu int
k8pnow_states(struct k8pnow_cpu_state * cstate,uint32_t cpusig,unsigned int fid,unsigned int vid)3137aa8d3a7Stedu k8pnow_states(struct k8pnow_cpu_state *cstate, uint32_t cpusig,
3147aa8d3a7Stedu unsigned int fid, unsigned int vid)
3157aa8d3a7Stedu {
3167aa8d3a7Stedu struct psb_s *psb;
3177aa8d3a7Stedu struct pst_s *pst;
3187aa8d3a7Stedu uint8_t *p;
31935958b2eStedu int i;
3207aa8d3a7Stedu
3210b2508fdSmlarkin /*
3220b2508fdSmlarkin * Look in the 0xe0000 - 0x100000 physical address
3230b2508fdSmlarkin * range for the pst tables; 16 byte blocks. End 10 bytes
3240b2508fdSmlarkin * before the end of the range to avoid memcmp across a
3250b2508fdSmlarkin * page boundary into unmapped memory.
3260b2508fdSmlarkin */
3277aa8d3a7Stedu for (p = (u_int8_t *)ISA_HOLE_VADDR(BIOS_START);
3280b2508fdSmlarkin p < (u_int8_t *)ISA_HOLE_VADDR(BIOS_START + BIOS_LEN) - 10;
3290b2508fdSmlarkin p += BIOS_STEP) {
3307aa8d3a7Stedu if (memcmp(p, "AMDK7PNOW!", 10) == 0) {
3317aa8d3a7Stedu psb = (struct psb_s *)p;
332668c25f3Sdlg if (psb->version != PN8_PSB_VERSION)
3337aa8d3a7Stedu return 0;
3347aa8d3a7Stedu
3357aa8d3a7Stedu cstate->vst = psb->ttime;
3367aa8d3a7Stedu cstate->rvo = PN8_PSB_TO_RVO(psb->reserved);
3377aa8d3a7Stedu cstate->irt = PN8_PSB_TO_IRT(psb->reserved);
3387aa8d3a7Stedu cstate->mvs = PN8_PSB_TO_MVS(psb->reserved);
3397aa8d3a7Stedu cstate->low = PN8_PSB_TO_BATT(psb->reserved);
3407aa8d3a7Stedu p+= sizeof(struct psb_s);
34135958b2eStedu
34235958b2eStedu for (i = 0; i < psb->n_pst; ++i) {
3437aa8d3a7Stedu pst = (struct pst_s *) p;
34435958b2eStedu
34535958b2eStedu cstate->pll = pst->pll;
3467aa8d3a7Stedu cstate->n_states = pst->n_states;
34735958b2eStedu if (cpusig == pst->cpuid &&
34835958b2eStedu pst->fid == fid && pst->vid == vid) {
3497aa8d3a7Stedu return (k8pnow_decode_pst(cstate,
35035958b2eStedu p+= sizeof (struct pst_s)));
3517aa8d3a7Stedu }
3521f3ce405Sgwk p += sizeof(struct pst_s) + 2
3531f3ce405Sgwk * cstate->n_states;
3547aa8d3a7Stedu }
3557aa8d3a7Stedu }
3567aa8d3a7Stedu }
3577aa8d3a7Stedu
3587aa8d3a7Stedu return 0;
3597aa8d3a7Stedu
3607aa8d3a7Stedu }
3617aa8d3a7Stedu
3623cd93248Sgwk #if NACPICPU > 0
3633cd93248Sgwk
3643cd93248Sgwk int
k8pnow_acpi_states(struct k8pnow_cpu_state * cstate,struct acpicpu_pss * pss,int nstates,uint64_t status)3653cd93248Sgwk k8pnow_acpi_states(struct k8pnow_cpu_state * cstate, struct acpicpu_pss * pss,
3663cd93248Sgwk int nstates, uint64_t status)
3673cd93248Sgwk {
3683cd93248Sgwk struct k8pnow_state state;
3693cd93248Sgwk int j, k, n;
3703cd93248Sgwk uint32_t ctrl;
3713cd93248Sgwk
3723cd93248Sgwk k = -1;
3733cd93248Sgwk
3743cd93248Sgwk for (n = 0; n < cstate->n_states; n++) {
375c5cdb568Sgwk if ((PN8_STA_CFID(status) == PN8_PSS_CFID(pss[n].pss_status)) &&
376c5cdb568Sgwk (PN8_STA_CVID(status) == PN8_PSS_CVID(pss[n].pss_status)))
3773cd93248Sgwk k = n;
3783cd93248Sgwk ctrl = pss[n].pss_ctrl;
3793cd93248Sgwk state.fid = PN8_ACPI_CTRL_TO_FID(ctrl);
3803cd93248Sgwk state.vid = PN8_ACPI_CTRL_TO_VID(ctrl);
3813cd93248Sgwk
3823cd93248Sgwk state.freq = pss[n].pss_core_freq;
3833cd93248Sgwk j = n;
3843cd93248Sgwk while (j > 0 && cstate->state_table[j - 1].freq > state.freq) {
3853cd93248Sgwk memcpy(&cstate->state_table[j],
3863cd93248Sgwk &cstate->state_table[j - 1],
3873cd93248Sgwk sizeof(struct k8pnow_state));
3883cd93248Sgwk --j;
3893cd93248Sgwk }
3903cd93248Sgwk memcpy(&cstate->state_table[j], &state,
3913cd93248Sgwk sizeof(struct k8pnow_state));
3923cd93248Sgwk }
3933cd93248Sgwk
3943cd93248Sgwk return k;
3953cd93248Sgwk }
3963cd93248Sgwk
3973cd93248Sgwk void
k8pnow_acpi_pss_changed(struct acpicpu_pss * pss,int npss)3983cd93248Sgwk k8pnow_acpi_pss_changed(struct acpicpu_pss * pss, int npss)
3993cd93248Sgwk {
4003cd93248Sgwk int curs;
4013cd93248Sgwk struct k8pnow_cpu_state * cstate;
4023cd93248Sgwk uint32_t ctrl;
4033cd93248Sgwk uint64_t status;
4043cd93248Sgwk
4053cd93248Sgwk status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
4063cd93248Sgwk cstate = k8pnow_current_state;
4073cd93248Sgwk
4083cd93248Sgwk curs = k8pnow_acpi_states(cstate, pss, npss, status);
4093cd93248Sgwk ctrl = pss[curs].pss_ctrl;
410940ac101Sgwk
411940ac101Sgwk cstate->rvo = PN8_ACPI_CTRL_TO_RVO(ctrl);
4123cd93248Sgwk cstate->vst = PN8_ACPI_CTRL_TO_VST(ctrl);
4133cd93248Sgwk cstate->mvs = PN8_ACPI_CTRL_TO_MVS(ctrl);
4143cd93248Sgwk cstate->pll = PN8_ACPI_CTRL_TO_PLL(ctrl);
4153cd93248Sgwk cstate->irt = PN8_ACPI_CTRL_TO_IRT(ctrl);
4163cd93248Sgwk cstate->low = 0;
4173cd93248Sgwk cstate->n_states = npss;
4183cd93248Sgwk }
4193cd93248Sgwk
4203cd93248Sgwk int
k8pnow_acpi_init(struct k8pnow_cpu_state * cstate,uint64_t status)4213cd93248Sgwk k8pnow_acpi_init(struct k8pnow_cpu_state * cstate, uint64_t status)
4223cd93248Sgwk {
4233cd93248Sgwk int curs;
4243cd93248Sgwk uint32_t ctrl;
4253cd93248Sgwk struct acpicpu_pss *pss;
4263cd93248Sgwk
4273cd93248Sgwk cstate->n_states = acpicpu_fetch_pss(&pss);
4283cd93248Sgwk if (cstate->n_states == 0)
4293cd93248Sgwk return 0;
4303cd93248Sgwk acpicpu_set_notify(k8pnow_acpi_pss_changed);
4313cd93248Sgwk
4323cd93248Sgwk curs = k8pnow_acpi_states(cstate, pss, cstate->n_states, status);
4333cd93248Sgwk ctrl = pss[curs].pss_ctrl;
4343cd93248Sgwk
435940ac101Sgwk cstate->rvo = PN8_ACPI_CTRL_TO_RVO(ctrl);
4363cd93248Sgwk cstate->vst = PN8_ACPI_CTRL_TO_VST(ctrl);
4373cd93248Sgwk cstate->mvs = PN8_ACPI_CTRL_TO_MVS(ctrl);
4383cd93248Sgwk cstate->pll = PN8_ACPI_CTRL_TO_PLL(ctrl);
4393cd93248Sgwk cstate->irt = PN8_ACPI_CTRL_TO_IRT(ctrl);
4403cd93248Sgwk cstate->low = 0;
4413cd93248Sgwk
4423cd93248Sgwk return 1;
4433cd93248Sgwk }
4443cd93248Sgwk
4453cd93248Sgwk #endif /* NACPICPU */
4463cd93248Sgwk
4477aa8d3a7Stedu void
k8_powernow_init(void)4487aa8d3a7Stedu k8_powernow_init(void)
4497aa8d3a7Stedu {
45035958b2eStedu uint64_t status;
45134b4ab5eSuwe u_int maxfid, maxvid, i;
4527aa8d3a7Stedu struct k8pnow_cpu_state *cstate;
45335958b2eStedu struct k8pnow_state *state;
4547aa8d3a7Stedu struct cpu_info * ci;
4557aa8d3a7Stedu char * techname = NULL;
456668c25f3Sdlg u_int32_t regs[4];
457668c25f3Sdlg
4587aa8d3a7Stedu ci = curcpu();
4597aa8d3a7Stedu
460aad5aaffSgwk if (setperf_prio > 1)
461aad5aaffSgwk return;
462aad5aaffSgwk
46334b4ab5eSuwe if (k8pnow_current_state)
46434b4ab5eSuwe return;
46534b4ab5eSuwe
466668c25f3Sdlg cpuid(0x80000000, regs);
467668c25f3Sdlg if (regs[0] < 0x80000007)
468668c25f3Sdlg return;
469668c25f3Sdlg
470668c25f3Sdlg cpuid(0x80000007, regs);
471668c25f3Sdlg if (!(regs[3] & AMD_PN_FID_VID))
472668c25f3Sdlg return;
473668c25f3Sdlg
47402fa6f89Sgwk /* Extended CPUID signature value */
47502fa6f89Sgwk cpuid(0x80000001, regs);
47602fa6f89Sgwk
47716427c27Stedu cstate = malloc(sizeof(struct k8pnow_cpu_state), M_DEVBUF, M_NOWAIT);
47816427c27Stedu if (!cstate)
47916427c27Stedu return;
4807aa8d3a7Stedu
48102fa6f89Sgwk cstate->n_states = 0;
4827aa8d3a7Stedu status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
4837aa8d3a7Stedu maxfid = PN8_STA_MFID(status);
4847aa8d3a7Stedu maxvid = PN8_STA_MVID(status);
4857aa8d3a7Stedu
4867aa8d3a7Stedu /*
4877aa8d3a7Stedu * If start FID is different to max FID, then it is a
4887aa8d3a7Stedu * mobile processor. If not, it is a low powered desktop
4897aa8d3a7Stedu * processor.
4907aa8d3a7Stedu */
4917aa8d3a7Stedu if (PN8_STA_SFID(status) != PN8_STA_MFID(status))
4927aa8d3a7Stedu techname = "PowerNow! K8";
4937aa8d3a7Stedu else
494bda76761Stom techname = "Cool'n'Quiet K8";
4957aa8d3a7Stedu
4963cd93248Sgwk #if NACPICPU > 0
4973cd93248Sgwk /* If we have acpi check acpi first */
4983cd93248Sgwk if (!k8pnow_acpi_init(cstate, status))
4993cd93248Sgwk #endif
5003cd93248Sgwk {
50102fa6f89Sgwk if (!k8pnow_states(cstate, ci->ci_signature, maxfid, maxvid))
50202fa6f89Sgwk k8pnow_states(cstate, regs[0], maxfid, maxvid);
5033cd93248Sgwk }
5047aa8d3a7Stedu if (cstate->n_states) {
505e959411aStom printf("%s: %s %d MHz: speeds:",
5068fa1e2ebSbluhm ci->ci_dev->dv_xname, techname, cpuspeed);
507b4cc6758Sdlg for (i = cstate->n_states; i > 0; i--) {
508b4cc6758Sdlg state = &cstate->state_table[i-1];
509b4cc6758Sdlg printf(" %d", state->freq);
51035958b2eStedu }
511e959411aStom printf(" MHz\n");
51234b4ab5eSuwe k8pnow_current_state = cstate;
5137aa8d3a7Stedu cpu_setperf = k8_powernow_setperf;
514aad5aaffSgwk setperf_prio = 1;
515f0ff5ec6Sdlg return;
5167aa8d3a7Stedu }
517864cf24eSderaadt free(cstate, M_DEVBUF, sizeof(*cstate));
5187aa8d3a7Stedu }
519