1 /* 2 * Copyright (c) 2004 Martin V\xe9giard. Copyright (c) 2004-2005 Bruno Ducrot 3 * Copyright (c) 2004 FUKUDA Nobuhiko <nfukuda@spa.is.uec.ac.jp> Copyright 4 * (c) 2004, 2006 The NetBSD Foundation, Inc. All rights reserved. 5 * 6 * This code is derived from software contributed to The NetBSD Foundation by 7 * Juan Romero Pardines and Martin Vegiard. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions are 11 * met: 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 2. 13 * Redistributions in binary form must reproduce the above copyright notice, 14 * this list of conditions and the following disclaimer in the documentation 15 * and/or other materials provided with the distribution. THIS SOFTWARE IS 16 * PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 17 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 18 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 19 * AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 20 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 21 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 24 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 * POSSIBILITY OF SUCH DAMAGE. 26 */ 27 /* AMD POWERNOW K8 driver */ 28 29 #include <sys/param.h> 30 #include <sys/systm.h> 31 #include <sys/malloc.h> 32 #include <sys/kernel.h> 33 #include <sys/module.h> 34 #include <sys/sysctl.h> 35 #include <bus/isa/isa.h> 36 #include <machine/cpu.h> 37 #include <machine/pmap.h> 38 #include <machine/pc/bios.h> 39 #include <machine/cpufunc.h> 40 #include <machine/md_var.h> 41 #include <machine/specialreg.h> 42 #include <machine/vmparam.h> 43 44 #define PN8_STA_MFID(x) (((x) >> 16) & 0x3f) 45 #define PN8_STA_MVID(x) (((x) >> 48) & 0x1f) 46 #define PN8_STA_SFID(x) (((x) >> 8) & 0x3f) 47 48 /* 49 * MSRs and bits used by PowerNow! technology 50 */ 51 #define MSR_AMDK7_FIDVID_CTL 0xc0010041 52 #define MSR_AMDK7_FIDVID_STATUS 0xc0010042 53 #define AMD_PN_FID_VID 0x06 54 55 #define BIOS_START 0xe0000 56 #define BIOS_LEN 0x20000 57 #define BIOS_STEP 16 58 59 #define PN8_PSB_VERSION 0x14 60 #define PN8_PSB_TO_RVO(x) ((x) & 0x03) 61 #define PN8_PSB_TO_IRT(x) (((x) >> 2) & 0x03) 62 #define PN8_PSB_TO_MVS(x) (((x) >> 4) & 0x03) 63 #define PN8_PSB_TO_BATT(x) (((x) >> 6) & 0x03) 64 /* Bitfields used by K8 */ 65 #define PN8_CTR_FID(x) ((x) & 0x3f) 66 #define PN8_CTR_VID(x) (((x) & 0x1f) << 8) 67 #define PN8_CTR_PENDING(x) (((x) & 1) << 32) 68 #define PN8_STA_CFID(x) ((x) & 0x3f) 69 #define PN8_STA_SFID(x) (((x) >> 8) & 0x3f) 70 #define PN8_STA_MFID(x) (((x) >> 16) & 0x3f) 71 #define PN8_STA_PENDING(x) (((x) >> 31) & 0x01) 72 #define PN8_STA_CVID(x) (((x) >> 32) & 0x1f) 73 #define PN8_STA_SVID(x) (((x) >> 40) & 0x1f) 74 #define PN8_STA_MVID(x) (((x) >> 48) & 0x1f) 75 #define PN8_PLL_LOCK(x) ((x) * 1000/5) 76 #define WRITE_FIDVID(fid, vid, ctrl) \ 77 wrmsr(MSR_AMDK7_FIDVID_CTL, \ 78 (((ctrl) << 32) | (1ULL << 16) | ((vid) << 8) | (fid))) 79 #define COUNT_OFF_IRT(irt) DELAY(10 * (1 << (irt))) 80 #define COUNT_OFF_VST(vst) DELAY(20 * (vst)) 81 #define FID_TO_VCO_FID(fid) \ 82 (((fid) < 8) ? (8 + ((fid) << 1)) : (fid)) 83 84 #define READ_PENDING_WAIT(status) \ 85 do { \ 86 (status) = rdmsr(MSR_AMDK7_FIDVID_STATUS); \ 87 } while (PN8_STA_PENDING(status)) 88 #define abs(x) ( (x) < 0 ? -(x) : (x) ) 89 90 #define POWERNOW_MAX_STATES 16 91 92 struct k8pnow_state { 93 int freq; 94 uint8_t fid; 95 uint8_t vid; 96 }; 97 98 struct k8pnow_cpu_state { 99 struct k8pnow_state state_table[POWERNOW_MAX_STATES]; 100 unsigned int n_states; 101 unsigned int vst; 102 unsigned int mvs; 103 unsigned int pll; 104 unsigned int rvo; 105 unsigned int irt; 106 int low; 107 }; 108 109 struct psb_s { 110 char signature [10]; /* AMDK7PNOW! */ 111 uint8_t version; 112 uint8_t flags; 113 uint16_t ttime; /* Min Settling time */ 114 uint8_t reserved; 115 uint8_t n_pst; 116 }; 117 struct pst_s { 118 uint32_t cpuid; 119 uint8_t pll; 120 uint8_t fid; 121 uint8_t vid; 122 uint8_t n_states; 123 }; 124 125 static struct k8pnow_cpu_state *k8pnow_current_state = NULL; 126 int cpuspeed; 127 128 int 129 k8pnow_states(struct k8pnow_cpu_state *cstate, uint32_t cpusig, 130 unsigned int fid, unsigned int vid); 131 int 132 k8pnow_decode_pst(struct k8pnow_cpu_state *cstate, uint8_t * p); 133 134 /* 135 * Given a set of pair of fid/vid, and number of performance states, compute 136 * state_table via an insertion sort. 137 */ 138 int 139 k8pnow_decode_pst(struct k8pnow_cpu_state *cstate, uint8_t * p) 140 { 141 int i , j, n; 142 struct k8pnow_state state; 143 for (n = 0, i = 0; i < cstate->n_states; i++) { 144 state.fid = *p++; 145 state.vid = *p++; 146 147 /* 148 * The minimum supported frequency per the data sheet is 149 * 800MHz The maximum supported frequency is 5000MHz. 150 */ 151 state.freq = 800 + state.fid * 100; 152 j = n; 153 while (j > 0 && cstate->state_table[j - 1].freq > state.freq) { 154 memcpy(&cstate->state_table[j], 155 &cstate->state_table[j - 1], 156 sizeof(struct k8pnow_state)); 157 --j; 158 } 159 memcpy(&cstate->state_table[j], &state, 160 sizeof(struct k8pnow_state)); 161 n++; 162 } 163 return 1; 164 } 165 166 int 167 k8pnow_states(struct k8pnow_cpu_state *cstate, uint32_t cpusig, 168 unsigned int fid, unsigned int vid) 169 { 170 struct psb_s *psb; 171 struct pst_s *pst; 172 uint8_t *p; 173 int i; 174 for (p = (u_int8_t *) BIOS_PADDRTOVADDR(BIOS_START); 175 p < (u_int8_t *) BIOS_PADDRTOVADDR(BIOS_START + BIOS_LEN); p += 176 BIOS_STEP) { 177 if (memcmp(p, "AMDK7PNOW!", 10) == 0) { 178 psb = (struct psb_s *)p; 179 if (psb->version != PN8_PSB_VERSION) 180 return 0; 181 cstate->vst = psb->ttime; 182 cstate->rvo = PN8_PSB_TO_RVO(psb->reserved); 183 cstate->irt = PN8_PSB_TO_IRT(psb->reserved); 184 cstate->mvs = PN8_PSB_TO_MVS(psb->reserved); 185 cstate->low = PN8_PSB_TO_BATT(psb->reserved); 186 p += sizeof(struct psb_s); 187 for (i = 0; i < psb->n_pst; ++i) { 188 pst = (struct pst_s *)p; 189 cstate->pll = pst->pll; 190 cstate->n_states = pst->n_states; 191 if (cpusig == pst->cpuid && 192 pst->fid == fid && pst->vid == vid) { 193 return (k8pnow_decode_pst(cstate, 194 p += sizeof(struct pst_s))); 195 } 196 p += sizeof(struct pst_s) + 2 197 * cstate->n_states; 198 } 199 } 200 } 201 return 0; 202 } 203 204 static int 205 k8_get_curfreq(void) 206 { 207 unsigned int i; 208 uint64_t status; 209 int cfid , cvid, fid = 0, vid = 0; 210 struct k8pnow_cpu_state *cstate; 211 status = rdmsr(MSR_AMDK7_FIDVID_STATUS); 212 if (PN8_STA_PENDING(status)) 213 return 1; 214 cfid = PN8_STA_CFID(status); 215 cvid = PN8_STA_CVID(status); 216 cstate = k8pnow_current_state; 217 for (i = 0; i < cstate->n_states; i++) { 218 if (cstate->state_table[i].fid == cfid && 219 cstate->state_table[i].vid == cvid) { 220 fid = cstate->state_table[i].fid; 221 vid = cstate->state_table[i].vid; 222 return (cstate->state_table[i].freq); 223 } 224 } 225 /* Not reached */ 226 return -1; 227 } 228 229 static int 230 k8_powernow_setperf(unsigned int freq) 231 { 232 unsigned int i; 233 uint64_t status; 234 uint32_t val; 235 int cfid , cvid, fid = 0, vid = 0; 236 int rvo; 237 struct k8pnow_cpu_state *cstate; 238 /* 239 * We dont do a k8pnow_read_pending_wait here, need to ensure that 240 * the change pending bit isn't stuck, 241 */ 242 status = rdmsr(MSR_AMDK7_FIDVID_STATUS); 243 if (PN8_STA_PENDING(status)) 244 return 1; 245 cfid = PN8_STA_CFID(status); 246 cvid = PN8_STA_CVID(status); 247 cstate = k8pnow_current_state; 248 for (i = 0; i < cstate->n_states; i++) { 249 if (cstate->state_table[i].freq >= freq) { 250 fid = cstate->state_table[i].fid; 251 vid = cstate->state_table[i].vid; 252 break; 253 } 254 } 255 if (fid == cfid && vid == cvid) { 256 cpuspeed = freq; 257 return 0; 258 } 259 /* 260 * Phase 1: Raise core voltage to requested VID if frequency is going 261 * up. 262 */ 263 while (cvid > vid) { 264 val = cvid - (1 << cstate->mvs); 265 WRITE_FIDVID(cfid, (val > 0) ? val : 0, 1ULL); 266 READ_PENDING_WAIT(status); 267 cvid = PN8_STA_CVID(status); 268 COUNT_OFF_VST(cstate->vst); 269 } 270 271 /* ... then raise to voltage + RVO (if required) */ 272 for (rvo = cstate->rvo; rvo > 0 && cvid > 0; --rvo) { 273 /* 274 * XXX It's not clear from spec if we have to do that in 0.25 275 * step or in MVS. Therefore do it as it's done under Linux 276 */ 277 WRITE_FIDVID(cfid, cvid - 1, 1ULL); 278 READ_PENDING_WAIT(status); 279 cvid = PN8_STA_CVID(status); 280 COUNT_OFF_VST(cstate->vst); 281 } 282 /* Phase 2: change to requested core frequency */ 283 if (cfid != fid) { 284 uint32_t vco_fid, vco_cfid; 285 vco_fid = FID_TO_VCO_FID(fid); 286 vco_cfid = FID_TO_VCO_FID(cfid); 287 while (abs(vco_fid - vco_cfid) > 2) { 288 if (fid > cfid) { 289 if (cfid > 6) 290 val = cfid + 2; 291 else 292 val = FID_TO_VCO_FID(cfid) + 2; 293 } else 294 val = cfid - 2; 295 WRITE_FIDVID(val, cvid, (uint64_t) cstate->pll * 1000 / 5); 296 READ_PENDING_WAIT(status); 297 cfid = PN8_STA_CFID(status); 298 COUNT_OFF_IRT(cstate->irt); 299 vco_cfid = FID_TO_VCO_FID(cfid); 300 } 301 WRITE_FIDVID(fid, cvid, (uint64_t) cstate->pll * 1000 / 5); 302 READ_PENDING_WAIT(status); 303 cfid = PN8_STA_CFID(status); 304 COUNT_OFF_IRT(cstate->irt); 305 } 306 /* Phase 3: change to requested voltage */ 307 if (cvid != vid) { 308 WRITE_FIDVID(cfid, vid, 1ULL); 309 READ_PENDING_WAIT(status); 310 cvid = PN8_STA_CVID(status); 311 COUNT_OFF_VST(cstate->vst); 312 } 313 if (cfid == fid || cvid == vid) 314 cpuspeed = cstate->state_table[i].freq; 315 return 0; 316 } 317 318 static int 319 powernow_sysctl_helper(SYSCTL_HANDLER_ARGS) 320 { 321 int fq , err = 0; 322 int i; 323 struct k8pnow_cpu_state *cstate; 324 struct k8pnow_state *state; 325 cstate = k8pnow_current_state; 326 if (req->newptr != NULL) { 327 err = SYSCTL_IN(req, &fq, sizeof(fq)); 328 if (err) 329 return err; 330 if (fq != cpuspeed) { 331 for (i = cstate->n_states; i > 0; i--) { 332 state = &cstate->state_table[i - 1]; 333 if (fq == state->freq) { 334 k8_powernow_setperf(fq); 335 break; 336 } 337 } 338 } 339 } else { 340 err = SYSCTL_OUT(req, &cpuspeed, sizeof(cpuspeed)); 341 } 342 return err; 343 } 344 345 static struct sysctl_ctx_list machdep_powernow_ctx; 346 static char freqs_available[80]; 347 348 static int 349 powernow_init(void) 350 { 351 uint64_t status; 352 size_t len , freq_len; 353 uint32_t maxfid, maxvid, i; 354 struct k8pnow_cpu_state *cstate; 355 struct k8pnow_state *state; 356 const char *techname; 357 u_int32_t regs [4]; 358 cpuspeed = 0; 359 struct sysctl_oid *oid, *leaf; 360 361 do_cpuid(0x80000000, regs); 362 if (regs[0] < 0x80000007) 363 return 1; 364 do_cpuid(0x80000007, regs); 365 if (!(regs[3] & AMD_PN_FID_VID)) 366 return 2; 367 /* Extended CPUID signature value */ 368 do_cpuid(0x80000001, regs); 369 cstate = kmalloc(sizeof(struct k8pnow_cpu_state), M_DEVBUF, M_WAITOK); 370 cstate->n_states = 0; 371 372 status = rdmsr(MSR_AMDK7_FIDVID_STATUS); 373 maxfid = PN8_STA_MFID(status); 374 maxvid = PN8_STA_MVID(status); 375 376 if (PN8_STA_SFID(status) != PN8_STA_MFID(status)) 377 techname = "PowerNow!"; 378 else 379 techname = "Cool`n'Quiet"; 380 k8pnow_states(cstate, regs[0], maxfid, maxvid); 381 len = 0; 382 if (cstate->n_states) { 383 freq_len = cstate->n_states * (sizeof("9999 ") - 1) + 1; 384 kprintf("%s speeds:", 385 techname); 386 for (i = cstate->n_states; i > 0; i--) { 387 state = &cstate->state_table[i - 1]; 388 kprintf(" %d", state->freq); 389 len += ksnprintf(freqs_available + len, freq_len - len, "%d%s", 390 state->freq, 391 i > 1 ? " " : ""); 392 } 393 kprintf(" MHz\n"); 394 k8pnow_current_state = cstate; 395 k8_powernow_setperf(k8_get_curfreq()); 396 } else { 397 kfree(cstate, M_DEVBUF); 398 kprintf("powernow: no power states found\n"); 399 return 3; 400 } 401 402 /* 403 * Setup the sysctl sub-tree machdep.powernow.* 404 */ 405 oid = SYSCTL_ADD_NODE(&machdep_powernow_ctx, 406 SYSCTL_STATIC_CHILDREN(_machdep), OID_AUTO, "powernow", 407 CTLFLAG_RD, NULL, ""); 408 if (oid == NULL) 409 return (EOPNOTSUPP); 410 oid = SYSCTL_ADD_NODE(&machdep_powernow_ctx, SYSCTL_CHILDREN(oid), 411 OID_AUTO, "frequency", CTLFLAG_RD, NULL, ""); 412 if (oid == NULL) 413 return (EOPNOTSUPP); 414 leaf = SYSCTL_ADD_PROC(&machdep_powernow_ctx, SYSCTL_CHILDREN(oid), 415 OID_AUTO, "target", CTLTYPE_INT | CTLFLAG_RW, NULL, 0, 416 powernow_sysctl_helper, "I", 417 "Target CPU frequency for AMD PowerNow!"); 418 if (leaf == NULL) 419 return (EOPNOTSUPP); 420 leaf = SYSCTL_ADD_PROC(&machdep_powernow_ctx, SYSCTL_CHILDREN(oid), 421 OID_AUTO, "current", CTLTYPE_INT | CTLFLAG_RD, NULL, 0, 422 powernow_sysctl_helper, "I", 423 "Current CPU frequency for AMD PowerNow!"); 424 if (leaf == NULL) 425 return (EOPNOTSUPP); 426 leaf = SYSCTL_ADD_STRING(&machdep_powernow_ctx, SYSCTL_CHILDREN(oid), 427 OID_AUTO, "available", CTLFLAG_RD, freqs_available, 428 sizeof(freqs_available), 429 "CPU frequencies supported by AMD PowerNow!"); 430 if (leaf == NULL) 431 return (EOPNOTSUPP); 432 return (0); 433 } 434 435 static int 436 powernow_modevh(struct module *m, int what, void *arg __unused) 437 { 438 int error; 439 440 switch (what) { 441 case MOD_LOAD: 442 error = sysctl_ctx_init(&machdep_powernow_ctx); 443 if (error != 0) 444 break; 445 error = powernow_init(); 446 break; 447 case MOD_UNLOAD: 448 if (k8pnow_current_state) 449 kfree(k8pnow_current_state, M_DEVBUF); 450 error = sysctl_ctx_free(&machdep_powernow_ctx); 451 break; 452 default: 453 error = EINVAL; 454 break; 455 } 456 return (error); 457 } 458 static moduledata_t powernow_mod = { 459 "powernow", 460 powernow_modevh, 461 NULL, 462 }; 463 464 DECLARE_MODULE(powernow, powernow_mod, SI_BOOT2_KLD, SI_ORDER_ANY); 465