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