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; 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 return (cstate->state_table[i].freq); 221 } 222 /* Not reached */ 223 return -1; 224 } 225 226 static int 227 k8_powernow_setperf(unsigned int freq) 228 { 229 unsigned int i; 230 uint64_t status; 231 uint32_t val; 232 int cfid , cvid, fid = 0, vid = 0; 233 int rvo; 234 struct k8pnow_cpu_state *cstate; 235 /* 236 * We dont do a k8pnow_read_pending_wait here, need to ensure that 237 * the change pending bit isn't stuck, 238 */ 239 status = rdmsr(MSR_AMDK7_FIDVID_STATUS); 240 if (PN8_STA_PENDING(status)) 241 return 1; 242 cfid = PN8_STA_CFID(status); 243 cvid = PN8_STA_CVID(status); 244 cstate = k8pnow_current_state; 245 for (i = 0; i < cstate->n_states; i++) { 246 if (cstate->state_table[i].freq >= freq) { 247 fid = cstate->state_table[i].fid; 248 vid = cstate->state_table[i].vid; 249 break; 250 } 251 } 252 if (fid == cfid && vid == cvid) { 253 cpuspeed = freq; 254 return 0; 255 } 256 /* 257 * Phase 1: Raise core voltage to requested VID if frequency is going 258 * up. 259 */ 260 while (cvid > vid) { 261 val = cvid - (1 << cstate->mvs); 262 WRITE_FIDVID(cfid, (val > 0) ? val : 0, 1ULL); 263 READ_PENDING_WAIT(status); 264 cvid = PN8_STA_CVID(status); 265 COUNT_OFF_VST(cstate->vst); 266 } 267 268 /* ... then raise to voltage + RVO (if required) */ 269 for (rvo = cstate->rvo; rvo > 0 && cvid > 0; --rvo) { 270 /* 271 * XXX It's not clear from spec if we have to do that in 0.25 272 * step or in MVS. Therefore do it as it's done under Linux 273 */ 274 WRITE_FIDVID(cfid, cvid - 1, 1ULL); 275 READ_PENDING_WAIT(status); 276 cvid = PN8_STA_CVID(status); 277 COUNT_OFF_VST(cstate->vst); 278 } 279 /* Phase 2: change to requested core frequency */ 280 if (cfid != fid) { 281 uint32_t vco_fid, vco_cfid; 282 vco_fid = FID_TO_VCO_FID(fid); 283 vco_cfid = FID_TO_VCO_FID(cfid); 284 while (abs(vco_fid - vco_cfid) > 2) { 285 if (fid > cfid) { 286 if (cfid > 6) 287 val = cfid + 2; 288 else 289 val = FID_TO_VCO_FID(cfid) + 2; 290 } else 291 val = cfid - 2; 292 WRITE_FIDVID(val, cvid, (uint64_t) cstate->pll * 1000 / 5); 293 READ_PENDING_WAIT(status); 294 cfid = PN8_STA_CFID(status); 295 COUNT_OFF_IRT(cstate->irt); 296 vco_cfid = FID_TO_VCO_FID(cfid); 297 } 298 WRITE_FIDVID(fid, cvid, (uint64_t) cstate->pll * 1000 / 5); 299 READ_PENDING_WAIT(status); 300 cfid = PN8_STA_CFID(status); 301 COUNT_OFF_IRT(cstate->irt); 302 } 303 /* Phase 3: change to requested voltage */ 304 if (cvid != vid) { 305 WRITE_FIDVID(cfid, vid, 1ULL); 306 READ_PENDING_WAIT(status); 307 cvid = PN8_STA_CVID(status); 308 COUNT_OFF_VST(cstate->vst); 309 } 310 if (cfid == fid || cvid == vid) 311 cpuspeed = cstate->state_table[i].freq; 312 return 0; 313 } 314 315 static int 316 powernow_sysctl_helper(SYSCTL_HANDLER_ARGS) 317 { 318 int fq , err = 0; 319 int i; 320 struct k8pnow_cpu_state *cstate; 321 struct k8pnow_state *state; 322 cstate = k8pnow_current_state; 323 if (req->newptr != NULL) { 324 err = SYSCTL_IN(req, &fq, sizeof(fq)); 325 if (err) 326 return err; 327 if (fq != cpuspeed) { 328 for (i = cstate->n_states; i > 0; i--) { 329 state = &cstate->state_table[i - 1]; 330 if (fq == state->freq) { 331 k8_powernow_setperf(fq); 332 break; 333 } 334 } 335 } 336 } else { 337 err = SYSCTL_OUT(req, &cpuspeed, sizeof(cpuspeed)); 338 } 339 return err; 340 } 341 342 static struct sysctl_ctx_list machdep_powernow_ctx; 343 static char freqs_available[80]; 344 345 static int 346 powernow_init(void) 347 { 348 uint64_t status; 349 size_t len , freq_len; 350 uint32_t maxfid, maxvid, i; 351 struct k8pnow_cpu_state *cstate; 352 struct k8pnow_state *state; 353 const char *techname; 354 u_int32_t regs [4]; 355 cpuspeed = 0; 356 struct sysctl_oid *oid, *leaf; 357 358 do_cpuid(0x80000000, regs); 359 if (regs[0] < 0x80000007) 360 return 1; 361 do_cpuid(0x80000007, regs); 362 if (!(regs[3] & AMD_PN_FID_VID)) 363 return 2; 364 /* Extended CPUID signature value */ 365 do_cpuid(0x80000001, regs); 366 cstate = kmalloc(sizeof(struct k8pnow_cpu_state), M_DEVBUF, M_WAITOK); 367 cstate->n_states = 0; 368 369 status = rdmsr(MSR_AMDK7_FIDVID_STATUS); 370 maxfid = PN8_STA_MFID(status); 371 maxvid = PN8_STA_MVID(status); 372 373 if (PN8_STA_SFID(status) != PN8_STA_MFID(status)) 374 techname = "PowerNow!"; 375 else 376 techname = "Cool`n'Quiet"; 377 k8pnow_states(cstate, regs[0], maxfid, maxvid); 378 len = 0; 379 if (cstate->n_states) { 380 freq_len = cstate->n_states * (sizeof("9999 ") - 1) + 1; 381 kprintf("%s speeds:", 382 techname); 383 for (i = cstate->n_states; i > 0; i--) { 384 state = &cstate->state_table[i - 1]; 385 kprintf(" %d", state->freq); 386 len += ksnprintf(freqs_available + len, freq_len - len, "%d%s", 387 state->freq, 388 i > 1 ? " " : ""); 389 } 390 kprintf(" MHz\n"); 391 k8pnow_current_state = cstate; 392 k8_powernow_setperf(k8_get_curfreq()); 393 } else { 394 kfree(cstate, M_DEVBUF); 395 kprintf("powernow: no power states found\n"); 396 return 3; 397 } 398 399 /* 400 * Setup the sysctl sub-tree machdep.powernow.* 401 */ 402 oid = SYSCTL_ADD_NODE(&machdep_powernow_ctx, 403 SYSCTL_STATIC_CHILDREN(_machdep), OID_AUTO, "powernow", 404 CTLFLAG_RD, NULL, ""); 405 if (oid == NULL) 406 return (EOPNOTSUPP); 407 oid = SYSCTL_ADD_NODE(&machdep_powernow_ctx, SYSCTL_CHILDREN(oid), 408 OID_AUTO, "frequency", CTLFLAG_RD, NULL, ""); 409 if (oid == NULL) 410 return (EOPNOTSUPP); 411 leaf = SYSCTL_ADD_PROC(&machdep_powernow_ctx, SYSCTL_CHILDREN(oid), 412 OID_AUTO, "target", CTLTYPE_INT | CTLFLAG_RW, NULL, 0, 413 powernow_sysctl_helper, "I", 414 "Target CPU frequency for AMD PowerNow!"); 415 if (leaf == NULL) 416 return (EOPNOTSUPP); 417 leaf = SYSCTL_ADD_PROC(&machdep_powernow_ctx, SYSCTL_CHILDREN(oid), 418 OID_AUTO, "current", CTLTYPE_INT | CTLFLAG_RD, NULL, 0, 419 powernow_sysctl_helper, "I", 420 "Current CPU frequency for AMD PowerNow!"); 421 if (leaf == NULL) 422 return (EOPNOTSUPP); 423 leaf = SYSCTL_ADD_STRING(&machdep_powernow_ctx, SYSCTL_CHILDREN(oid), 424 OID_AUTO, "available", CTLFLAG_RD, freqs_available, 425 sizeof(freqs_available), 426 "CPU frequencies supported by AMD PowerNow!"); 427 if (leaf == NULL) 428 return (EOPNOTSUPP); 429 return (0); 430 } 431 432 static int 433 powernow_modevh(struct module *m, int what, void *arg __unused) 434 { 435 int error; 436 437 switch (what) { 438 case MOD_LOAD: 439 error = sysctl_ctx_init(&machdep_powernow_ctx); 440 if (error != 0) 441 break; 442 error = powernow_init(); 443 break; 444 case MOD_UNLOAD: 445 if (k8pnow_current_state) 446 kfree(k8pnow_current_state, M_DEVBUF); 447 error = sysctl_ctx_free(&machdep_powernow_ctx); 448 break; 449 default: 450 error = EINVAL; 451 break; 452 } 453 return (error); 454 } 455 static moduledata_t powernow_mod = { 456 "powernow", 457 powernow_modevh, 458 NULL, 459 }; 460 461 DECLARE_MODULE(powernow, powernow_mod, SI_BOOT2_KLD, SI_ORDER_ANY); 462