1231d9860SAlexander Polakov /* 2231d9860SAlexander Polakov * Copyright (c) 2004 Martin V\xe9giard. Copyright (c) 2004-2005 Bruno Ducrot 3231d9860SAlexander Polakov * Copyright (c) 2004 FUKUDA Nobuhiko <nfukuda@spa.is.uec.ac.jp> Copyright 4231d9860SAlexander Polakov * (c) 2004, 2006 The NetBSD Foundation, Inc. All rights reserved. 5231d9860SAlexander Polakov * 6231d9860SAlexander Polakov * This code is derived from software contributed to The NetBSD Foundation by 7231d9860SAlexander Polakov * Juan Romero Pardines and Martin Vegiard. 8231d9860SAlexander Polakov * 9231d9860SAlexander Polakov * Redistribution and use in source and binary forms, with or without 10231d9860SAlexander Polakov * modification, are permitted provided that the following conditions are 11231d9860SAlexander Polakov * met: 1. Redistributions of source code must retain the above copyright 12231d9860SAlexander Polakov * notice, this list of conditions and the following disclaimer. 2. 13231d9860SAlexander Polakov * Redistributions in binary form must reproduce the above copyright notice, 14231d9860SAlexander Polakov * this list of conditions and the following disclaimer in the documentation 15231d9860SAlexander Polakov * and/or other materials provided with the distribution. THIS SOFTWARE IS 16231d9860SAlexander Polakov * PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 17231d9860SAlexander Polakov * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 18231d9860SAlexander Polakov * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 19231d9860SAlexander Polakov * AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 20231d9860SAlexander Polakov * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 21231d9860SAlexander Polakov * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22231d9860SAlexander Polakov * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23231d9860SAlexander Polakov * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 24231d9860SAlexander Polakov * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25231d9860SAlexander Polakov * POSSIBILITY OF SUCH DAMAGE. 26231d9860SAlexander Polakov */ 27231d9860SAlexander Polakov /* AMD POWERNOW K8 driver */ 28231d9860SAlexander Polakov 29231d9860SAlexander Polakov #include <sys/param.h> 30231d9860SAlexander Polakov #include <sys/systm.h> 31231d9860SAlexander Polakov #include <sys/malloc.h> 32231d9860SAlexander Polakov #include <sys/kernel.h> 33231d9860SAlexander Polakov #include <sys/module.h> 34231d9860SAlexander Polakov #include <sys/sysctl.h> 35231d9860SAlexander Polakov #include <bus/isa/isa.h> 36231d9860SAlexander Polakov #include <machine/cpu.h> 37231d9860SAlexander Polakov #include <machine/pmap.h> 38231d9860SAlexander Polakov #include <machine/pc/bios.h> 39231d9860SAlexander Polakov #include <machine/cpufunc.h> 40231d9860SAlexander Polakov #include <machine/md_var.h> 41231d9860SAlexander Polakov #include <machine/specialreg.h> 42*db06e065SSascha Wildner #include <machine/vmparam.h> 43231d9860SAlexander Polakov 44231d9860SAlexander Polakov #define PN8_STA_MFID(x) (((x) >> 16) & 0x3f) 45231d9860SAlexander Polakov #define PN8_STA_MVID(x) (((x) >> 48) & 0x1f) 46231d9860SAlexander Polakov #define PN8_STA_SFID(x) (((x) >> 8) & 0x3f) 47231d9860SAlexander Polakov 48231d9860SAlexander Polakov /* 49231d9860SAlexander Polakov * MSRs and bits used by PowerNow! technology 50231d9860SAlexander Polakov */ 51231d9860SAlexander Polakov #define MSR_AMDK7_FIDVID_CTL 0xc0010041 52231d9860SAlexander Polakov #define MSR_AMDK7_FIDVID_STATUS 0xc0010042 53231d9860SAlexander Polakov #define AMD_PN_FID_VID 0x06 54231d9860SAlexander Polakov 55231d9860SAlexander Polakov #define BIOS_START 0xe0000 56231d9860SAlexander Polakov #define BIOS_LEN 0x20000 57231d9860SAlexander Polakov #define BIOS_STEP 16 58231d9860SAlexander Polakov 59231d9860SAlexander Polakov #define PN8_PSB_VERSION 0x14 60231d9860SAlexander Polakov #define PN8_PSB_TO_RVO(x) ((x) & 0x03) 61231d9860SAlexander Polakov #define PN8_PSB_TO_IRT(x) (((x) >> 2) & 0x03) 62231d9860SAlexander Polakov #define PN8_PSB_TO_MVS(x) (((x) >> 4) & 0x03) 63231d9860SAlexander Polakov #define PN8_PSB_TO_BATT(x) (((x) >> 6) & 0x03) 64231d9860SAlexander Polakov /* Bitfields used by K8 */ 65231d9860SAlexander Polakov #define PN8_CTR_FID(x) ((x) & 0x3f) 66231d9860SAlexander Polakov #define PN8_CTR_VID(x) (((x) & 0x1f) << 8) 67231d9860SAlexander Polakov #define PN8_CTR_PENDING(x) (((x) & 1) << 32) 68231d9860SAlexander Polakov #define PN8_STA_CFID(x) ((x) & 0x3f) 69231d9860SAlexander Polakov #define PN8_STA_SFID(x) (((x) >> 8) & 0x3f) 70231d9860SAlexander Polakov #define PN8_STA_MFID(x) (((x) >> 16) & 0x3f) 71231d9860SAlexander Polakov #define PN8_STA_PENDING(x) (((x) >> 31) & 0x01) 72231d9860SAlexander Polakov #define PN8_STA_CVID(x) (((x) >> 32) & 0x1f) 73231d9860SAlexander Polakov #define PN8_STA_SVID(x) (((x) >> 40) & 0x1f) 74231d9860SAlexander Polakov #define PN8_STA_MVID(x) (((x) >> 48) & 0x1f) 75231d9860SAlexander Polakov #define PN8_PLL_LOCK(x) ((x) * 1000/5) 76231d9860SAlexander Polakov #define WRITE_FIDVID(fid, vid, ctrl) \ 77231d9860SAlexander Polakov wrmsr(MSR_AMDK7_FIDVID_CTL, \ 78231d9860SAlexander Polakov (((ctrl) << 32) | (1ULL << 16) | ((vid) << 8) | (fid))) 79231d9860SAlexander Polakov #define COUNT_OFF_IRT(irt) DELAY(10 * (1 << (irt))) 80231d9860SAlexander Polakov #define COUNT_OFF_VST(vst) DELAY(20 * (vst)) 81231d9860SAlexander Polakov #define FID_TO_VCO_FID(fid) \ 82231d9860SAlexander Polakov (((fid) < 8) ? (8 + ((fid) << 1)) : (fid)) 83231d9860SAlexander Polakov 84231d9860SAlexander Polakov #define READ_PENDING_WAIT(status) \ 85231d9860SAlexander Polakov do { \ 86231d9860SAlexander Polakov (status) = rdmsr(MSR_AMDK7_FIDVID_STATUS); \ 87231d9860SAlexander Polakov } while (PN8_STA_PENDING(status)) 88231d9860SAlexander Polakov #define abs(x) ( x < 0 ? -x : x ) 89231d9860SAlexander Polakov 90231d9860SAlexander Polakov #define POWERNOW_MAX_STATES 16 91231d9860SAlexander Polakov 92231d9860SAlexander Polakov struct k8pnow_state { 93231d9860SAlexander Polakov int freq; 94231d9860SAlexander Polakov uint8_t fid; 95231d9860SAlexander Polakov uint8_t vid; 96231d9860SAlexander Polakov }; 97231d9860SAlexander Polakov 98231d9860SAlexander Polakov struct k8pnow_cpu_state { 99231d9860SAlexander Polakov struct k8pnow_state state_table[POWERNOW_MAX_STATES]; 100231d9860SAlexander Polakov unsigned int n_states; 101231d9860SAlexander Polakov unsigned int vst; 102231d9860SAlexander Polakov unsigned int mvs; 103231d9860SAlexander Polakov unsigned int pll; 104231d9860SAlexander Polakov unsigned int rvo; 105231d9860SAlexander Polakov unsigned int irt; 106231d9860SAlexander Polakov int low; 107231d9860SAlexander Polakov }; 108231d9860SAlexander Polakov 109231d9860SAlexander Polakov struct psb_s { 110231d9860SAlexander Polakov char signature [10]; /* AMDK7PNOW! */ 111231d9860SAlexander Polakov uint8_t version; 112231d9860SAlexander Polakov uint8_t flags; 113231d9860SAlexander Polakov uint16_t ttime; /* Min Settling time */ 114231d9860SAlexander Polakov uint8_t reserved; 115231d9860SAlexander Polakov uint8_t n_pst; 116231d9860SAlexander Polakov }; 117231d9860SAlexander Polakov struct pst_s { 118231d9860SAlexander Polakov uint32_t cpuid; 119231d9860SAlexander Polakov uint8_t pll; 120231d9860SAlexander Polakov uint8_t fid; 121231d9860SAlexander Polakov uint8_t vid; 122231d9860SAlexander Polakov uint8_t n_states; 123231d9860SAlexander Polakov }; 124231d9860SAlexander Polakov 125231d9860SAlexander Polakov static struct k8pnow_cpu_state *k8pnow_current_state = NULL; 126231d9860SAlexander Polakov int cpuspeed; 127231d9860SAlexander Polakov 128231d9860SAlexander Polakov int 129231d9860SAlexander Polakov k8pnow_states(struct k8pnow_cpu_state *cstate, uint32_t cpusig, 130231d9860SAlexander Polakov unsigned int fid, unsigned int vid); 131231d9860SAlexander Polakov int 132231d9860SAlexander Polakov k8pnow_decode_pst(struct k8pnow_cpu_state *cstate, uint8_t * p); 133231d9860SAlexander Polakov 134231d9860SAlexander Polakov /* 135231d9860SAlexander Polakov * Given a set of pair of fid/vid, and number of performance states, compute 136231d9860SAlexander Polakov * state_table via an insertion sort. 137231d9860SAlexander Polakov */ 138231d9860SAlexander Polakov int 139231d9860SAlexander Polakov k8pnow_decode_pst(struct k8pnow_cpu_state *cstate, uint8_t * p) 140231d9860SAlexander Polakov { 141231d9860SAlexander Polakov int i , j, n; 142231d9860SAlexander Polakov struct k8pnow_state state; 143231d9860SAlexander Polakov for (n = 0, i = 0; i < cstate->n_states; i++) { 144231d9860SAlexander Polakov state.fid = *p++; 145231d9860SAlexander Polakov state.vid = *p++; 146231d9860SAlexander Polakov 147231d9860SAlexander Polakov /* 148231d9860SAlexander Polakov * The minimum supported frequency per the data sheet is 149231d9860SAlexander Polakov * 800MHz The maximum supported frequency is 5000MHz. 150231d9860SAlexander Polakov */ 151231d9860SAlexander Polakov state.freq = 800 + state.fid * 100; 152231d9860SAlexander Polakov j = n; 153231d9860SAlexander Polakov while (j > 0 && cstate->state_table[j - 1].freq > state.freq) { 154231d9860SAlexander Polakov memcpy(&cstate->state_table[j], 155231d9860SAlexander Polakov &cstate->state_table[j - 1], 156231d9860SAlexander Polakov sizeof(struct k8pnow_state)); 157231d9860SAlexander Polakov --j; 158231d9860SAlexander Polakov } 159231d9860SAlexander Polakov memcpy(&cstate->state_table[j], &state, 160231d9860SAlexander Polakov sizeof(struct k8pnow_state)); 161231d9860SAlexander Polakov n++; 162231d9860SAlexander Polakov } 163231d9860SAlexander Polakov return 1; 164231d9860SAlexander Polakov } 165231d9860SAlexander Polakov 166231d9860SAlexander Polakov int 167231d9860SAlexander Polakov k8pnow_states(struct k8pnow_cpu_state *cstate, uint32_t cpusig, 168231d9860SAlexander Polakov unsigned int fid, unsigned int vid) 169231d9860SAlexander Polakov { 170231d9860SAlexander Polakov struct psb_s *psb; 171231d9860SAlexander Polakov struct pst_s *pst; 172231d9860SAlexander Polakov uint8_t *p; 173231d9860SAlexander Polakov int i; 174231d9860SAlexander Polakov for (p = (u_int8_t *) BIOS_PADDRTOVADDR(BIOS_START); 175231d9860SAlexander Polakov p < (u_int8_t *) BIOS_PADDRTOVADDR(BIOS_START + BIOS_LEN); p += 176231d9860SAlexander Polakov BIOS_STEP) { 177231d9860SAlexander Polakov if (memcmp(p, "AMDK7PNOW!", 10) == 0) { 178231d9860SAlexander Polakov psb = (struct psb_s *)p; 179231d9860SAlexander Polakov if (psb->version != PN8_PSB_VERSION) 180231d9860SAlexander Polakov return 0; 181231d9860SAlexander Polakov cstate->vst = psb->ttime; 182231d9860SAlexander Polakov cstate->rvo = PN8_PSB_TO_RVO(psb->reserved); 183231d9860SAlexander Polakov cstate->irt = PN8_PSB_TO_IRT(psb->reserved); 184231d9860SAlexander Polakov cstate->mvs = PN8_PSB_TO_MVS(psb->reserved); 185231d9860SAlexander Polakov cstate->low = PN8_PSB_TO_BATT(psb->reserved); 186231d9860SAlexander Polakov p += sizeof(struct psb_s); 187231d9860SAlexander Polakov for (i = 0; i < psb->n_pst; ++i) { 188231d9860SAlexander Polakov pst = (struct pst_s *)p; 189231d9860SAlexander Polakov cstate->pll = pst->pll; 190231d9860SAlexander Polakov cstate->n_states = pst->n_states; 191231d9860SAlexander Polakov if (cpusig == pst->cpuid && 192231d9860SAlexander Polakov pst->fid == fid && pst->vid == vid) { 193231d9860SAlexander Polakov return (k8pnow_decode_pst(cstate, 194231d9860SAlexander Polakov p += sizeof(struct pst_s))); 195231d9860SAlexander Polakov } 196231d9860SAlexander Polakov p += sizeof(struct pst_s) + 2 197231d9860SAlexander Polakov * cstate->n_states; 198231d9860SAlexander Polakov } 199231d9860SAlexander Polakov } 200231d9860SAlexander Polakov } 201231d9860SAlexander Polakov return 0; 202231d9860SAlexander Polakov } 203231d9860SAlexander Polakov 204231d9860SAlexander Polakov static int 205231d9860SAlexander Polakov k8_get_curfreq(void) 206231d9860SAlexander Polakov { 207231d9860SAlexander Polakov unsigned int i; 208231d9860SAlexander Polakov uint64_t status; 209231d9860SAlexander Polakov int cfid , cvid, fid = 0, vid = 0; 210231d9860SAlexander Polakov struct k8pnow_cpu_state *cstate; 211231d9860SAlexander Polakov status = rdmsr(MSR_AMDK7_FIDVID_STATUS); 212231d9860SAlexander Polakov if (PN8_STA_PENDING(status)) 213231d9860SAlexander Polakov return 1; 214231d9860SAlexander Polakov cfid = PN8_STA_CFID(status); 215231d9860SAlexander Polakov cvid = PN8_STA_CVID(status); 216231d9860SAlexander Polakov cstate = k8pnow_current_state; 217231d9860SAlexander Polakov for (i = 0; i < cstate->n_states; i++) { 218231d9860SAlexander Polakov if (cstate->state_table[i].fid == cfid && 219231d9860SAlexander Polakov cstate->state_table[i].vid == cvid) { 220231d9860SAlexander Polakov fid = cstate->state_table[i].fid; 221231d9860SAlexander Polakov vid = cstate->state_table[i].vid; 222231d9860SAlexander Polakov return (cstate->state_table[i].freq); 223231d9860SAlexander Polakov } 224231d9860SAlexander Polakov } 225231d9860SAlexander Polakov /* Not reached */ 226231d9860SAlexander Polakov return -1; 227231d9860SAlexander Polakov } 228231d9860SAlexander Polakov 229231d9860SAlexander Polakov static int 230231d9860SAlexander Polakov k8_powernow_setperf(unsigned int freq) 231231d9860SAlexander Polakov { 232231d9860SAlexander Polakov unsigned int i; 233231d9860SAlexander Polakov uint64_t status; 234231d9860SAlexander Polakov uint32_t val; 235231d9860SAlexander Polakov int cfid , cvid, fid = 0, vid = 0; 236231d9860SAlexander Polakov int rvo; 237231d9860SAlexander Polakov struct k8pnow_cpu_state *cstate; 238231d9860SAlexander Polakov /* 239231d9860SAlexander Polakov * We dont do a k8pnow_read_pending_wait here, need to ensure that 240231d9860SAlexander Polakov * the change pending bit isn't stuck, 241231d9860SAlexander Polakov */ 242231d9860SAlexander Polakov status = rdmsr(MSR_AMDK7_FIDVID_STATUS); 243231d9860SAlexander Polakov if (PN8_STA_PENDING(status)) 244231d9860SAlexander Polakov return 1; 245231d9860SAlexander Polakov cfid = PN8_STA_CFID(status); 246231d9860SAlexander Polakov cvid = PN8_STA_CVID(status); 247231d9860SAlexander Polakov cstate = k8pnow_current_state; 248231d9860SAlexander Polakov for (i = 0; i < cstate->n_states; i++) { 249231d9860SAlexander Polakov if (cstate->state_table[i].freq >= freq) { 250231d9860SAlexander Polakov fid = cstate->state_table[i].fid; 251231d9860SAlexander Polakov vid = cstate->state_table[i].vid; 252231d9860SAlexander Polakov break; 253231d9860SAlexander Polakov } 254231d9860SAlexander Polakov } 255231d9860SAlexander Polakov if (fid == cfid && vid == cvid) { 256231d9860SAlexander Polakov cpuspeed = freq; 257231d9860SAlexander Polakov return 0; 258231d9860SAlexander Polakov } 259231d9860SAlexander Polakov /* 260231d9860SAlexander Polakov * Phase 1: Raise core voltage to requested VID if frequency is going 261231d9860SAlexander Polakov * up. 262231d9860SAlexander Polakov */ 263231d9860SAlexander Polakov while (cvid > vid) { 264231d9860SAlexander Polakov val = cvid - (1 << cstate->mvs); 265231d9860SAlexander Polakov WRITE_FIDVID(cfid, (val > 0) ? val : 0, 1ULL); 266231d9860SAlexander Polakov READ_PENDING_WAIT(status); 267231d9860SAlexander Polakov cvid = PN8_STA_CVID(status); 268231d9860SAlexander Polakov COUNT_OFF_VST(cstate->vst); 269231d9860SAlexander Polakov } 270231d9860SAlexander Polakov 271231d9860SAlexander Polakov /* ... then raise to voltage + RVO (if required) */ 272231d9860SAlexander Polakov for (rvo = cstate->rvo; rvo > 0 && cvid > 0; --rvo) { 273231d9860SAlexander Polakov /* 274231d9860SAlexander Polakov * XXX It's not clear from spec if we have to do that in 0.25 275231d9860SAlexander Polakov * step or in MVS. Therefore do it as it's done under Linux 276231d9860SAlexander Polakov */ 277231d9860SAlexander Polakov WRITE_FIDVID(cfid, cvid - 1, 1ULL); 278231d9860SAlexander Polakov READ_PENDING_WAIT(status); 279231d9860SAlexander Polakov cvid = PN8_STA_CVID(status); 280231d9860SAlexander Polakov COUNT_OFF_VST(cstate->vst); 281231d9860SAlexander Polakov } 282231d9860SAlexander Polakov /* Phase 2: change to requested core frequency */ 283231d9860SAlexander Polakov if (cfid != fid) { 284231d9860SAlexander Polakov uint32_t vco_fid, vco_cfid; 285231d9860SAlexander Polakov vco_fid = FID_TO_VCO_FID(fid); 286231d9860SAlexander Polakov vco_cfid = FID_TO_VCO_FID(cfid); 287231d9860SAlexander Polakov while (abs(vco_fid - vco_cfid) > 2) { 288231d9860SAlexander Polakov if (fid > cfid) { 289231d9860SAlexander Polakov if (cfid > 6) 290231d9860SAlexander Polakov val = cfid + 2; 291231d9860SAlexander Polakov else 292231d9860SAlexander Polakov val = FID_TO_VCO_FID(cfid) + 2; 293231d9860SAlexander Polakov } else 294231d9860SAlexander Polakov val = cfid - 2; 295231d9860SAlexander Polakov WRITE_FIDVID(val, cvid, (uint64_t) cstate->pll * 1000 / 5); 296231d9860SAlexander Polakov READ_PENDING_WAIT(status); 297231d9860SAlexander Polakov cfid = PN8_STA_CFID(status); 298231d9860SAlexander Polakov COUNT_OFF_IRT(cstate->irt); 299231d9860SAlexander Polakov vco_cfid = FID_TO_VCO_FID(cfid); 300231d9860SAlexander Polakov } 301231d9860SAlexander Polakov WRITE_FIDVID(fid, cvid, (uint64_t) cstate->pll * 1000 / 5); 302231d9860SAlexander Polakov READ_PENDING_WAIT(status); 303231d9860SAlexander Polakov cfid = PN8_STA_CFID(status); 304231d9860SAlexander Polakov COUNT_OFF_IRT(cstate->irt); 305231d9860SAlexander Polakov } 306231d9860SAlexander Polakov /* Phase 3: change to requested voltage */ 307231d9860SAlexander Polakov if (cvid != vid) { 308231d9860SAlexander Polakov WRITE_FIDVID(cfid, vid, 1ULL); 309231d9860SAlexander Polakov READ_PENDING_WAIT(status); 310231d9860SAlexander Polakov cvid = PN8_STA_CVID(status); 311231d9860SAlexander Polakov COUNT_OFF_VST(cstate->vst); 312231d9860SAlexander Polakov } 313231d9860SAlexander Polakov if (cfid == fid || cvid == vid) 314231d9860SAlexander Polakov cpuspeed = cstate->state_table[i].freq; 315231d9860SAlexander Polakov return 0; 316231d9860SAlexander Polakov } 317231d9860SAlexander Polakov 318231d9860SAlexander Polakov static int 319231d9860SAlexander Polakov powernow_sysctl_helper(SYSCTL_HANDLER_ARGS) 320231d9860SAlexander Polakov { 321231d9860SAlexander Polakov int fq , err = 0; 322231d9860SAlexander Polakov int i; 323231d9860SAlexander Polakov struct k8pnow_cpu_state *cstate; 324231d9860SAlexander Polakov struct k8pnow_state *state; 325231d9860SAlexander Polakov cstate = k8pnow_current_state; 326231d9860SAlexander Polakov if (req->newptr != NULL) { 327231d9860SAlexander Polakov err = SYSCTL_IN(req, &fq, sizeof(fq)); 328231d9860SAlexander Polakov if (err) 329231d9860SAlexander Polakov return err; 330231d9860SAlexander Polakov if (fq != cpuspeed) { 331231d9860SAlexander Polakov for (i = cstate->n_states; i > 0; i--) { 332231d9860SAlexander Polakov state = &cstate->state_table[i - 1]; 333231d9860SAlexander Polakov if (fq == state->freq) { 334231d9860SAlexander Polakov k8_powernow_setperf(fq); 335231d9860SAlexander Polakov break; 336231d9860SAlexander Polakov } 337231d9860SAlexander Polakov } 338231d9860SAlexander Polakov } 339231d9860SAlexander Polakov } else { 340231d9860SAlexander Polakov err = SYSCTL_OUT(req, &cpuspeed, sizeof(cpuspeed)); 341231d9860SAlexander Polakov } 342231d9860SAlexander Polakov return err; 343231d9860SAlexander Polakov } 344231d9860SAlexander Polakov 345231d9860SAlexander Polakov static struct sysctl_ctx_list machdep_powernow_ctx; 346231d9860SAlexander Polakov static char freqs_available[80]; 347231d9860SAlexander Polakov 348231d9860SAlexander Polakov static int 349231d9860SAlexander Polakov powernow_init(void) 350231d9860SAlexander Polakov { 351231d9860SAlexander Polakov uint64_t status; 352231d9860SAlexander Polakov size_t len , freq_len; 353231d9860SAlexander Polakov uint32_t maxfid, maxvid, i; 354231d9860SAlexander Polakov struct k8pnow_cpu_state *cstate; 355231d9860SAlexander Polakov struct k8pnow_state *state; 356231d9860SAlexander Polakov const char *techname; 357231d9860SAlexander Polakov u_int32_t regs [4]; 358231d9860SAlexander Polakov cpuspeed = 0; 359231d9860SAlexander Polakov struct sysctl_oid *oid, *leaf; 360231d9860SAlexander Polakov 361231d9860SAlexander Polakov do_cpuid(0x80000000, regs); 362231d9860SAlexander Polakov if (regs[0] < 0x80000007) 363231d9860SAlexander Polakov return 1; 364231d9860SAlexander Polakov do_cpuid(0x80000007, regs); 365231d9860SAlexander Polakov if (!(regs[3] & AMD_PN_FID_VID)) 366231d9860SAlexander Polakov return 2; 367231d9860SAlexander Polakov /* Extended CPUID signature value */ 368231d9860SAlexander Polakov do_cpuid(0x80000001, regs); 369231d9860SAlexander Polakov cstate = kmalloc(sizeof(struct k8pnow_cpu_state), M_DEVBUF, M_WAITOK); 370231d9860SAlexander Polakov cstate->n_states = 0; 371231d9860SAlexander Polakov 372231d9860SAlexander Polakov status = rdmsr(MSR_AMDK7_FIDVID_STATUS); 373231d9860SAlexander Polakov maxfid = PN8_STA_MFID(status); 374231d9860SAlexander Polakov maxvid = PN8_STA_MVID(status); 375231d9860SAlexander Polakov 376231d9860SAlexander Polakov if (PN8_STA_SFID(status) != PN8_STA_MFID(status)) 377231d9860SAlexander Polakov techname = "PowerNow!"; 378231d9860SAlexander Polakov else 379231d9860SAlexander Polakov techname = "Cool`n'Quiet"; 380231d9860SAlexander Polakov k8pnow_states(cstate, regs[0], maxfid, maxvid); 381231d9860SAlexander Polakov len = 0; 382231d9860SAlexander Polakov if (cstate->n_states) { 383231d9860SAlexander Polakov freq_len = cstate->n_states * (sizeof("9999 ") - 1) + 1; 384231d9860SAlexander Polakov kprintf("%s speeds:", 385231d9860SAlexander Polakov techname); 386231d9860SAlexander Polakov for (i = cstate->n_states; i > 0; i--) { 387231d9860SAlexander Polakov state = &cstate->state_table[i - 1]; 388231d9860SAlexander Polakov kprintf(" %d", state->freq); 389231d9860SAlexander Polakov len += ksnprintf(freqs_available + len, freq_len - len, "%d%s", 390231d9860SAlexander Polakov state->freq, 391231d9860SAlexander Polakov i > 1 ? " " : ""); 392231d9860SAlexander Polakov } 393231d9860SAlexander Polakov kprintf(" MHz\n"); 394231d9860SAlexander Polakov k8pnow_current_state = cstate; 395231d9860SAlexander Polakov k8_powernow_setperf(k8_get_curfreq()); 396231d9860SAlexander Polakov } else { 397231d9860SAlexander Polakov kfree(cstate, M_DEVBUF); 398231d9860SAlexander Polakov kprintf("powernow: no power states found\n"); 399231d9860SAlexander Polakov return 3; 400231d9860SAlexander Polakov } 401231d9860SAlexander Polakov 402231d9860SAlexander Polakov /* 403231d9860SAlexander Polakov * Setup the sysctl sub-tree machdep.powernow.* 404231d9860SAlexander Polakov */ 405231d9860SAlexander Polakov oid = SYSCTL_ADD_NODE(&machdep_powernow_ctx, 406231d9860SAlexander Polakov SYSCTL_STATIC_CHILDREN(_machdep), OID_AUTO, "powernow", 407231d9860SAlexander Polakov CTLFLAG_RD, NULL, ""); 408231d9860SAlexander Polakov if (oid == NULL) 409231d9860SAlexander Polakov return (EOPNOTSUPP); 410231d9860SAlexander Polakov oid = SYSCTL_ADD_NODE(&machdep_powernow_ctx, SYSCTL_CHILDREN(oid), 411231d9860SAlexander Polakov OID_AUTO, "frequency", CTLFLAG_RD, NULL, ""); 412231d9860SAlexander Polakov if (oid == NULL) 413231d9860SAlexander Polakov return (EOPNOTSUPP); 414231d9860SAlexander Polakov leaf = SYSCTL_ADD_PROC(&machdep_powernow_ctx, SYSCTL_CHILDREN(oid), 415231d9860SAlexander Polakov OID_AUTO, "target", CTLTYPE_INT | CTLFLAG_RW, NULL, 0, 416231d9860SAlexander Polakov powernow_sysctl_helper, "I", 417231d9860SAlexander Polakov "Target CPU frequency for AMD PowerNow!"); 418231d9860SAlexander Polakov if (leaf == NULL) 419231d9860SAlexander Polakov return (EOPNOTSUPP); 420231d9860SAlexander Polakov leaf = SYSCTL_ADD_PROC(&machdep_powernow_ctx, SYSCTL_CHILDREN(oid), 421231d9860SAlexander Polakov OID_AUTO, "current", CTLTYPE_INT | CTLFLAG_RD, NULL, 0, 422231d9860SAlexander Polakov powernow_sysctl_helper, "I", 423231d9860SAlexander Polakov "Current CPU frequency for AMD PowerNow!"); 424231d9860SAlexander Polakov if (leaf == NULL) 425231d9860SAlexander Polakov return (EOPNOTSUPP); 426231d9860SAlexander Polakov leaf = SYSCTL_ADD_STRING(&machdep_powernow_ctx, SYSCTL_CHILDREN(oid), 427231d9860SAlexander Polakov OID_AUTO, "available", CTLFLAG_RD, freqs_available, 428231d9860SAlexander Polakov sizeof(freqs_available), 429231d9860SAlexander Polakov "CPU frequencies supported by AMD PowerNow!"); 430231d9860SAlexander Polakov if (leaf == NULL) 431231d9860SAlexander Polakov return (EOPNOTSUPP); 432231d9860SAlexander Polakov return (0); 433231d9860SAlexander Polakov } 434231d9860SAlexander Polakov 435231d9860SAlexander Polakov static int 436231d9860SAlexander Polakov powernow_modevh(struct module *m, int what, void *arg __unused) 437231d9860SAlexander Polakov { 438231d9860SAlexander Polakov int error; 439231d9860SAlexander Polakov 440231d9860SAlexander Polakov switch (what) { 441231d9860SAlexander Polakov case MOD_LOAD: 442231d9860SAlexander Polakov error = sysctl_ctx_init(&machdep_powernow_ctx); 443231d9860SAlexander Polakov if (error != 0) 444231d9860SAlexander Polakov break; 445231d9860SAlexander Polakov error = powernow_init(); 446231d9860SAlexander Polakov break; 447231d9860SAlexander Polakov case MOD_UNLOAD: 448231d9860SAlexander Polakov if (k8pnow_current_state) 449231d9860SAlexander Polakov kfree(k8pnow_current_state, M_DEVBUF); 450231d9860SAlexander Polakov error = sysctl_ctx_free(&machdep_powernow_ctx); 451231d9860SAlexander Polakov break; 452231d9860SAlexander Polakov default: 453231d9860SAlexander Polakov error = EINVAL; 454231d9860SAlexander Polakov break; 455231d9860SAlexander Polakov } 456231d9860SAlexander Polakov return (error); 457231d9860SAlexander Polakov } 458231d9860SAlexander Polakov static moduledata_t powernow_mod = { 459231d9860SAlexander Polakov "powernow", 460231d9860SAlexander Polakov powernow_modevh, 461231d9860SAlexander Polakov NULL, 462231d9860SAlexander Polakov }; 463231d9860SAlexander Polakov 464231d9860SAlexander Polakov DECLARE_MODULE(powernow, powernow_mod, SI_BOOT2_KLD, SI_ORDER_ANY); 465