powernow.c revision 184104
1139749Simp/*- 253790Sobrien * Copyright (c) 2004-2005 Bruno Ducrot 353790Sobrien * Copyright (c) 2004 FUKUDA Nobuhiko <nfukuda@spa.is.uec.ac.jp> 453790Sobrien * 586266Sgroudier * Redistribution and use in source and binary forms, with or without 653790Sobrien * modification, are permitted provided that the following conditions 753790Sobrien * are met: 859743Sgroudier * 1. Redistributions of source code must retain the above copyright 959743Sgroudier * notice, this list of conditions and the following disclaimer. 1053790Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1153790Sobrien * notice, this list of conditions and the following disclaimer in the 1253790Sobrien * documentation and/or other materials provided with the distribution. 1353790Sobrien * 1453790Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1553790Sobrien * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1653790Sobrien * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 1753790Sobrien * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 1853790Sobrien * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 1953790Sobrien * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2053790Sobrien * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2153790Sobrien * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2253790Sobrien * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2353790Sobrien * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2453790Sobrien */ 2553790Sobrien 2653790Sobrien/* 2753790Sobrien * Many thanks to Nate Lawson for his helpful comments on this driver and 2853790Sobrien * to Jung-uk Kim for testing. 2953790Sobrien */ 3053790Sobrien 3153790Sobrien#include <sys/cdefs.h> 3253790Sobrien__FBSDID("$FreeBSD: head/sys/i386/cpufreq/powernow.c 184104 2008-10-21 00:52:20Z jkim $"); 3353790Sobrien 3453790Sobrien#include <sys/param.h> 3553790Sobrien#include <sys/bus.h> 3653790Sobrien#include <sys/cpu.h> 3753790Sobrien#include <sys/kernel.h> 3853790Sobrien#include <sys/malloc.h> 3953790Sobrien#include <sys/module.h> 4053790Sobrien#include <sys/pcpu.h> 4153790Sobrien#include <sys/systm.h> 4253790Sobrien 4353790Sobrien#include <machine/pc/bios.h> 4453790Sobrien#include <machine/md_var.h> 4553790Sobrien#include <machine/specialreg.h> 4653790Sobrien#include <machine/cputypes.h> 4753790Sobrien#include <machine/vmparam.h> 4853790Sobrien#include <sys/rman.h> 4953790Sobrien 5053790Sobrien#include <vm/vm.h> 5153790Sobrien#include <vm/pmap.h> 5253790Sobrien 5353790Sobrien#include "cpufreq_if.h" 5453790Sobrien 5553790Sobrien#define PN7_TYPE 0 5653790Sobrien#define PN8_TYPE 1 5755258Sobrien 5855258Sobrien/* Flags for some hardware bugs. */ 5955258Sobrien#define A0_ERRATA 0x1 /* Bugs for the rev. A0 of Athlon (K7): 6053790Sobrien * Interrupts must be disabled and no half 6153790Sobrien * multipliers are allowed */ 6253790Sobrien#define PENDING_STUCK 0x2 /* With some buggy chipset and some newer AMD64 6353790Sobrien * processor (Rev. G?): 6453790Sobrien * the pending bit from the msr FIDVID_STATUS 6553790Sobrien * is set forever. No workaround :( */ 6653790Sobrien 6753790Sobrien/* Legacy configuration via BIOS table PSB. */ 6853790Sobrien#define PSB_START 0 6959743Sgroudier#define PSB_STEP 0x10 7059743Sgroudier#define PSB_SIG "AMDK7PNOW!" 7159743Sgroudier#define PSB_LEN 10 7259743Sgroudier#define PSB_OFF 0 7359743Sgroudier 7453790Sobrienstruct psb_header { 7553790Sobrien char signature[10]; 7654690Sobrien uint8_t version; 7753790Sobrien uint8_t flags; 7853790Sobrien uint16_t settlingtime; 7953790Sobrien uint8_t res1; 8053790Sobrien uint8_t numpst; 8153790Sobrien} __packed; 8253790Sobrien 8354690Sobrienstruct pst_header { 8453790Sobrien uint32_t cpuid; 8553790Sobrien uint8_t fsb; 86236061Smarius uint8_t maxfid; 87236061Smarius uint8_t startvid; 88236061Smarius uint8_t numpstates; 89236061Smarius} __packed; 90236061Smarius 91236061Smarius/* 92236061Smarius * MSRs and bits used by Powernow technology 93237101Smarius */ 9453790Sobrien#define MSR_AMDK7_FIDVID_CTL 0xc0010041 95237101Smarius#define MSR_AMDK7_FIDVID_STATUS 0xc0010042 96237101Smarius 9753790Sobrien/* Bitfields used by K7 */ 98237101Smarius 9953790Sobrien#define PN7_CTR_FID(x) ((x) & 0x1f) 10053790Sobrien#define PN7_CTR_VID(x) (((x) & 0x1f) << 8) 10153790Sobrien#define PN7_CTR_FIDC 0x00010000 10253790Sobrien#define PN7_CTR_VIDC 0x00020000 10353790Sobrien#define PN7_CTR_FIDCHRATIO 0x00100000 10454690Sobrien#define PN7_CTR_SGTC(x) (((uint64_t)(x) & 0x000fffff) << 32) 10553790Sobrien 10653790Sobrien#define PN7_STA_CFID(x) ((x) & 0x1f) 10753790Sobrien#define PN7_STA_SFID(x) (((x) >> 8) & 0x1f) 10853790Sobrien#define PN7_STA_MFID(x) (((x) >> 16) & 0x1f) 10953790Sobrien#define PN7_STA_CVID(x) (((x) >> 32) & 0x1f) 11053790Sobrien#define PN7_STA_SVID(x) (((x) >> 40) & 0x1f) 11153790Sobrien#define PN7_STA_MVID(x) (((x) >> 48) & 0x1f) 11253790Sobrien 11354690Sobrien/* ACPI ctr_val status register to powernow k7 configuration */ 11453790Sobrien#define ACPI_PN7_CTRL_TO_FID(x) ((x) & 0x1f) 11553790Sobrien#define ACPI_PN7_CTRL_TO_VID(x) (((x) >> 5) & 0x1f) 11653790Sobrien#define ACPI_PN7_CTRL_TO_SGTC(x) (((x) >> 10) & 0xffff) 11753790Sobrien 11853790Sobrien/* Bitfields used by K8 */ 11953790Sobrien 12053790Sobrien#define PN8_CTR_FID(x) ((x) & 0x3f) 12153790Sobrien#define PN8_CTR_VID(x) (((x) & 0x1f) << 8) 12253790Sobrien#define PN8_CTR_PENDING(x) (((x) & 1) << 32) 12353790Sobrien 12454690Sobrien#define PN8_STA_CFID(x) ((x) & 0x3f) 12553790Sobrien#define PN8_STA_SFID(x) (((x) >> 8) & 0x3f) 12653790Sobrien#define PN8_STA_MFID(x) (((x) >> 16) & 0x3f) 12753790Sobrien#define PN8_STA_PENDING(x) (((x) >> 31) & 0x01) 12853790Sobrien#define PN8_STA_CVID(x) (((x) >> 32) & 0x1f) 12954690Sobrien#define PN8_STA_SVID(x) (((x) >> 40) & 0x1f) 13059743Sgroudier#define PN8_STA_MVID(x) (((x) >> 48) & 0x1f) 13153790Sobrien 13253790Sobrien/* Reserved1 to powernow k8 configuration */ 13353790Sobrien#define PN8_PSB_TO_RVO(x) ((x) & 0x03) 13453790Sobrien#define PN8_PSB_TO_IRT(x) (((x) >> 2) & 0x03) 13553790Sobrien#define PN8_PSB_TO_MVS(x) (((x) >> 4) & 0x03) 13654690Sobrien#define PN8_PSB_TO_BATT(x) (((x) >> 6) & 0x03) 13753790Sobrien 13853790Sobrien/* ACPI ctr_val status register to powernow k8 configuration */ 13953790Sobrien#define ACPI_PN8_CTRL_TO_FID(x) ((x) & 0x3f) 14053790Sobrien#define ACPI_PN8_CTRL_TO_VID(x) (((x) >> 6) & 0x1f) 14153790Sobrien#define ACPI_PN8_CTRL_TO_VST(x) (((x) >> 11) & 0x1f) 14253790Sobrien#define ACPI_PN8_CTRL_TO_MVS(x) (((x) >> 18) & 0x03) 14353790Sobrien#define ACPI_PN8_CTRL_TO_PLL(x) (((x) >> 20) & 0x7f) 14453790Sobrien#define ACPI_PN8_CTRL_TO_RVO(x) (((x) >> 28) & 0x03) 14553790Sobrien#define ACPI_PN8_CTRL_TO_IRT(x) (((x) >> 30) & 0x03) 14653790Sobrien 14754690Sobrien 14853790Sobrien#define WRITE_FIDVID(fid, vid, ctrl) \ 14953790Sobrien wrmsr(MSR_AMDK7_FIDVID_CTL, \ 15053790Sobrien (((ctrl) << 32) | (1ULL << 16) | ((vid) << 8) | (fid))) 15153790Sobrien 15254690Sobrien#define COUNT_OFF_IRT(irt) DELAY(10 * (1 << (irt))) 15353790Sobrien#define COUNT_OFF_VST(vst) DELAY(20 * (vst)) 15453790Sobrien 15553790Sobrien#define FID_TO_VCO_FID(fid) \ 15653790Sobrien (((fid) < 8) ? (8 + ((fid) << 1)) : (fid)) 15754690Sobrien 15853790Sobrien/* 15953790Sobrien * Divide each value by 10 to get the processor multiplier. 16053790Sobrien * Some of those tables are the same as the Linux powernow-k7 16153790Sobrien * implementation by Dave Jones. 16255628Sgroudier */ 16354690Sobrienstatic int pn7_fid_to_mult[32] = { 16453790Sobrien 110, 115, 120, 125, 50, 55, 60, 65, 16553790Sobrien 70, 75, 80, 85, 90, 95, 100, 105, 16653790Sobrien 30, 190, 40, 200, 130, 135, 140, 210, 16754690Sobrien 150, 225, 160, 165, 170, 180, 0, 0, 16853790Sobrien}; 16953790Sobrien 17053790Sobrien 17153790Sobrienstatic int pn8_fid_to_mult[64] = { 17254690Sobrien 40, 45, 50, 55, 60, 65, 70, 75, 17353790Sobrien 80, 85, 90, 95, 100, 105, 110, 115, 17453790Sobrien 120, 125, 130, 135, 140, 145, 150, 155, 17553790Sobrien 160, 165, 170, 175, 180, 185, 190, 195, 17653790Sobrien 200, 205, 210, 215, 220, 225, 230, 235, 17754690Sobrien 240, 245, 250, 255, 260, 265, 270, 275, 17853790Sobrien 280, 285, 290, 295, 300, 305, 310, 315, 17953790Sobrien 320, 325, 330, 335, 340, 345, 350, 355, 18053790Sobrien}; 18154690Sobrien 18254690Sobrien/* 18354690Sobrien * Units are in mV. 18454690Sobrien */ 18554690Sobrien/* Mobile VRM (K7) */ 18653790Sobrienstatic int pn7_mobile_vid_to_volts[] = { 18754690Sobrien 2000, 1950, 1900, 1850, 1800, 1750, 1700, 1650, 18854690Sobrien 1600, 1550, 1500, 1450, 1400, 1350, 1300, 0, 18954690Sobrien 1275, 1250, 1225, 1200, 1175, 1150, 1125, 1100, 19053790Sobrien 1075, 1050, 1025, 1000, 975, 950, 925, 0, 19153790Sobrien}; 19253790Sobrien/* Desktop VRM (K7) */ 19353790Sobrienstatic int pn7_desktop_vid_to_volts[] = { 19454690Sobrien 2000, 1950, 1900, 1850, 1800, 1750, 1700, 1650, 19553790Sobrien 1600, 1550, 1500, 1450, 1400, 1350, 1300, 0, 19653790Sobrien 1275, 1250, 1225, 1200, 1175, 1150, 1125, 1100, 19753790Sobrien 1075, 1050, 1025, 1000, 975, 950, 925, 0, 19853790Sobrien}; 19954690Sobrien/* Desktop and Mobile VRM (K8) */ 20053790Sobrienstatic int pn8_vid_to_volts[] = { 20153790Sobrien 1550, 1525, 1500, 1475, 1450, 1425, 1400, 1375, 20254690Sobrien 1350, 1325, 1300, 1275, 1250, 1225, 1200, 1175, 20354690Sobrien 1150, 1125, 1100, 1075, 1050, 1025, 1000, 975, 20454690Sobrien 950, 925, 900, 875, 850, 825, 800, 0, 20554690Sobrien}; 20654690Sobrien 20754690Sobrien#define POWERNOW_MAX_STATES 16 20854690Sobrien 20954690Sobrienstruct powernow_state { 21054690Sobrien int freq; 21154690Sobrien int power; 21254690Sobrien int fid; 21354690Sobrien int vid; 21454690Sobrien}; 21554690Sobrien 21654690Sobrienstruct pn_softc { 21754690Sobrien device_t dev; 21854690Sobrien int pn_type; 21954690Sobrien struct powernow_state powernow_states[POWERNOW_MAX_STATES]; 22054690Sobrien u_int fsb; 22154690Sobrien u_int sgtc; 22254690Sobrien u_int vst; 22354690Sobrien u_int mvs; 22454690Sobrien u_int pll; 22553790Sobrien u_int rvo; 22654690Sobrien u_int irt; 22754690Sobrien int low; 22854690Sobrien int powernow_max_states; 22953790Sobrien u_int powernow_state; 23053790Sobrien u_int errata; 23153790Sobrien int *vid_to_volts; 23253790Sobrien}; 23354690Sobrien 23453790Sobrien/* 23553790Sobrien * Offsets in struct cf_setting array for private values given by 23653790Sobrien * acpi_perf driver. 23753790Sobrien */ 23854690Sobrien#define PX_SPEC_CONTROL 0 23953790Sobrien#define PX_SPEC_STATUS 1 24053790Sobrien 24153790Sobrienstatic void pn_identify(driver_t *driver, device_t parent); 24253790Sobrienstatic int pn_probe(device_t dev); 24353790Sobrienstatic int pn_attach(device_t dev); 24454690Sobrienstatic int pn_detach(device_t dev); 24553790Sobrienstatic int pn_set(device_t dev, const struct cf_setting *cf); 24653790Sobrienstatic int pn_get(device_t dev, struct cf_setting *cf); 24753790Sobrienstatic int pn_settings(device_t dev, struct cf_setting *sets, 24853790Sobrien int *count); 24953790Sobrienstatic int pn_type(device_t dev, int *type); 25053790Sobrien 25154690Sobrienstatic device_method_t pn_methods[] = { 25254690Sobrien /* Device interface */ 25353790Sobrien DEVMETHOD(device_identify, pn_identify), 25453790Sobrien DEVMETHOD(device_probe, pn_probe), 25553790Sobrien DEVMETHOD(device_attach, pn_attach), 25653790Sobrien DEVMETHOD(device_detach, pn_detach), 25753790Sobrien 25853790Sobrien /* cpufreq interface */ 25953790Sobrien DEVMETHOD(cpufreq_drv_set, pn_set), 26054690Sobrien DEVMETHOD(cpufreq_drv_get, pn_get), 26153790Sobrien DEVMETHOD(cpufreq_drv_settings, pn_settings), 26254690Sobrien DEVMETHOD(cpufreq_drv_type, pn_type), 26354690Sobrien 26454690Sobrien {0, 0} 26554690Sobrien}; 26654690Sobrien 26754690Sobrienstatic devclass_t pn_devclass; 26854690Sobrienstatic driver_t pn_driver = { 26954690Sobrien "powernow", 27054690Sobrien pn_methods, 27154690Sobrien sizeof(struct pn_softc), 27254690Sobrien}; 27354690Sobrien 27454690SobrienDRIVER_MODULE(powernow, cpu, pn_driver, pn_devclass, 0, 0); 27554690Sobrien 27654690Sobrienstatic int 27754690Sobrienpn7_setfidvid(struct pn_softc *sc, int fid, int vid) 27854690Sobrien{ 27954690Sobrien int cfid, cvid; 28054690Sobrien uint64_t status, ctl; 28154690Sobrien 28254690Sobrien status = rdmsr(MSR_AMDK7_FIDVID_STATUS); 28354690Sobrien cfid = PN7_STA_CFID(status); 28454690Sobrien cvid = PN7_STA_CVID(status); 28554690Sobrien 28654690Sobrien /* We're already at the requested level. */ 28759743Sgroudier if (fid == cfid && vid == cvid) 28859743Sgroudier return (0); 28959743Sgroudier 29059743Sgroudier ctl = rdmsr(MSR_AMDK7_FIDVID_CTL) & PN7_CTR_FIDCHRATIO; 29159743Sgroudier 29254690Sobrien ctl |= PN7_CTR_FID(fid); 29354690Sobrien ctl |= PN7_CTR_VID(vid); 29454690Sobrien ctl |= PN7_CTR_SGTC(sc->sgtc); 29554690Sobrien 29654690Sobrien if (sc->errata & A0_ERRATA) 29754690Sobrien disable_intr(); 29854690Sobrien 29954690Sobrien if (pn7_fid_to_mult[fid] < pn7_fid_to_mult[cfid]) { 30054690Sobrien wrmsr(MSR_AMDK7_FIDVID_CTL, ctl | PN7_CTR_FIDC); 30153790Sobrien if (vid != cvid) 302 wrmsr(MSR_AMDK7_FIDVID_CTL, ctl | PN7_CTR_VIDC); 303 } else { 304 wrmsr(MSR_AMDK7_FIDVID_CTL, ctl | PN7_CTR_VIDC); 305 if (fid != cfid) 306 wrmsr(MSR_AMDK7_FIDVID_CTL, ctl | PN7_CTR_FIDC); 307 } 308 309 if (sc->errata & A0_ERRATA) 310 enable_intr(); 311 312 return (0); 313} 314 315static int 316pn8_read_pending_wait(uint64_t *status) 317{ 318 int i = 10000; 319 320 do 321 *status = rdmsr(MSR_AMDK7_FIDVID_STATUS); 322 while (PN8_STA_PENDING(*status) && --i); 323 324 return (i == 0 ? ENXIO : 0); 325} 326 327static int 328pn8_write_fidvid(u_int fid, u_int vid, uint64_t ctrl, uint64_t *status) 329{ 330 int i = 100; 331 332 do 333 WRITE_FIDVID(fid, vid, ctrl); 334 while (pn8_read_pending_wait(status) && --i); 335 336 return (i == 0 ? ENXIO : 0); 337} 338 339static int 340pn8_setfidvid(struct pn_softc *sc, int fid, int vid) 341{ 342 uint64_t status; 343 int cfid, cvid; 344 int rvo; 345 int rv; 346 u_int val; 347 348 rv = pn8_read_pending_wait(&status); 349 if (rv) 350 return (rv); 351 352 cfid = PN8_STA_CFID(status); 353 cvid = PN8_STA_CVID(status); 354 355 if (fid == cfid && vid == cvid) 356 return (0); 357 358 /* 359 * Phase 1: Raise core voltage to requested VID if frequency is 360 * going up. 361 */ 362 while (cvid > vid) { 363 val = cvid - (1 << sc->mvs); 364 rv = pn8_write_fidvid(cfid, (val > 0) ? val : 0, 1ULL, &status); 365 if (rv) { 366 sc->errata |= PENDING_STUCK; 367 return (rv); 368 } 369 cvid = PN8_STA_CVID(status); 370 COUNT_OFF_VST(sc->vst); 371 } 372 373 /* ... then raise to voltage + RVO (if required) */ 374 for (rvo = sc->rvo; rvo > 0 && cvid > 0; --rvo) { 375 /* XXX It's not clear from spec if we have to do that 376 * in 0.25 step or in MVS. Therefore do it as it's done 377 * under Linux */ 378 rv = pn8_write_fidvid(cfid, cvid - 1, 1ULL, &status); 379 if (rv) { 380 sc->errata |= PENDING_STUCK; 381 return (rv); 382 } 383 cvid = PN8_STA_CVID(status); 384 COUNT_OFF_VST(sc->vst); 385 } 386 387 /* Phase 2: change to requested core frequency */ 388 if (cfid != fid) { 389 u_int vco_fid, vco_cfid, fid_delta; 390 391 vco_fid = FID_TO_VCO_FID(fid); 392 vco_cfid = FID_TO_VCO_FID(cfid); 393 394 while (abs(vco_fid - vco_cfid) > 2) { 395 fid_delta = (vco_cfid & 1) ? 1 : 2; 396 if (fid > cfid) { 397 if (cfid > 7) 398 val = cfid + fid_delta; 399 else 400 val = FID_TO_VCO_FID(cfid) + fid_delta; 401 } else 402 val = cfid - fid_delta; 403 rv = pn8_write_fidvid(val, cvid, 404 sc->pll * (uint64_t) sc->fsb, 405 &status); 406 if (rv) { 407 sc->errata |= PENDING_STUCK; 408 return (rv); 409 } 410 cfid = PN8_STA_CFID(status); 411 COUNT_OFF_IRT(sc->irt); 412 413 vco_cfid = FID_TO_VCO_FID(cfid); 414 } 415 416 rv = pn8_write_fidvid(fid, cvid, 417 sc->pll * (uint64_t) sc->fsb, 418 &status); 419 if (rv) { 420 sc->errata |= PENDING_STUCK; 421 return (rv); 422 } 423 cfid = PN8_STA_CFID(status); 424 COUNT_OFF_IRT(sc->irt); 425 } 426 427 /* Phase 3: change to requested voltage */ 428 if (cvid != vid) { 429 rv = pn8_write_fidvid(cfid, vid, 1ULL, &status); 430 cvid = PN8_STA_CVID(status); 431 COUNT_OFF_VST(sc->vst); 432 } 433 434 /* Check if transition failed. */ 435 if (cfid != fid || cvid != vid) 436 rv = ENXIO; 437 438 return (rv); 439} 440 441static int 442pn_set(device_t dev, const struct cf_setting *cf) 443{ 444 struct pn_softc *sc; 445 int fid, vid; 446 int i; 447 int rv; 448 449 if (cf == NULL) 450 return (EINVAL); 451 sc = device_get_softc(dev); 452 453 if (sc->errata & PENDING_STUCK) 454 return (ENXIO); 455 456 for (i = 0; i < sc->powernow_max_states; ++i) 457 if (CPUFREQ_CMP(sc->powernow_states[i].freq / 1000, cf->freq)) 458 break; 459 460 fid = sc->powernow_states[i].fid; 461 vid = sc->powernow_states[i].vid; 462 463 rv = ENODEV; 464 465 switch (sc->pn_type) { 466 case PN7_TYPE: 467 rv = pn7_setfidvid(sc, fid, vid); 468 break; 469 case PN8_TYPE: 470 rv = pn8_setfidvid(sc, fid, vid); 471 break; 472 } 473 474 return (rv); 475} 476 477static int 478pn_get(device_t dev, struct cf_setting *cf) 479{ 480 struct pn_softc *sc; 481 u_int cfid = 0, cvid = 0; 482 int i; 483 uint64_t status; 484 485 if (cf == NULL) 486 return (EINVAL); 487 sc = device_get_softc(dev); 488 if (sc->errata & PENDING_STUCK) 489 return (ENXIO); 490 491 status = rdmsr(MSR_AMDK7_FIDVID_STATUS); 492 493 switch (sc->pn_type) { 494 case PN7_TYPE: 495 cfid = PN7_STA_CFID(status); 496 cvid = PN7_STA_CVID(status); 497 break; 498 case PN8_TYPE: 499 cfid = PN8_STA_CFID(status); 500 cvid = PN8_STA_CVID(status); 501 break; 502 } 503 for (i = 0; i < sc->powernow_max_states; ++i) 504 if (cfid == sc->powernow_states[i].fid && 505 cvid == sc->powernow_states[i].vid) 506 break; 507 508 if (i < sc->powernow_max_states) { 509 cf->freq = sc->powernow_states[i].freq / 1000; 510 cf->power = sc->powernow_states[i].power; 511 cf->lat = 200; 512 cf->volts = sc->vid_to_volts[cvid]; 513 cf->dev = dev; 514 } else { 515 memset(cf, CPUFREQ_VAL_UNKNOWN, sizeof(*cf)); 516 cf->dev = NULL; 517 } 518 519 return (0); 520} 521 522static int 523pn_settings(device_t dev, struct cf_setting *sets, int *count) 524{ 525 struct pn_softc *sc; 526 int i; 527 528 if (sets == NULL|| count == NULL) 529 return (EINVAL); 530 sc = device_get_softc(dev); 531 if (*count < sc->powernow_max_states) 532 return (E2BIG); 533 for (i = 0; i < sc->powernow_max_states; ++i) { 534 sets[i].freq = sc->powernow_states[i].freq / 1000; 535 sets[i].power = sc->powernow_states[i].power; 536 sets[i].lat = 200; 537 sets[i].volts = sc->vid_to_volts[sc->powernow_states[i].vid]; 538 sets[i].dev = dev; 539 } 540 *count = sc->powernow_max_states; 541 542 return (0); 543} 544 545static int 546pn_type(device_t dev, int *type) 547{ 548 if (type == NULL) 549 return (EINVAL); 550 551 *type = CPUFREQ_TYPE_ABSOLUTE; 552 553 return (0); 554} 555 556/* 557 * Given a set of pair of fid/vid, and number of performance states, 558 * compute powernow_states via an insertion sort. 559 */ 560static int 561decode_pst(struct pn_softc *sc, uint8_t *p, int npstates) 562{ 563 int i, j, n; 564 struct powernow_state state; 565 566 for (i = 0; i < POWERNOW_MAX_STATES; ++i) 567 sc->powernow_states[i].freq = CPUFREQ_VAL_UNKNOWN; 568 569 for (n = 0, i = 0; i < npstates; ++i) { 570 state.fid = *p++; 571 state.vid = *p++; 572 state.power = CPUFREQ_VAL_UNKNOWN; 573 574 switch (sc->pn_type) { 575 case PN7_TYPE: 576 state.freq = 100 * pn7_fid_to_mult[state.fid] * sc->fsb; 577 if ((sc->errata & A0_ERRATA) && 578 (pn7_fid_to_mult[state.fid] % 10) == 5) 579 continue; 580 break; 581 case PN8_TYPE: 582 state.freq = 100 * pn8_fid_to_mult[state.fid] * sc->fsb; 583 break; 584 } 585 586 j = n; 587 while (j > 0 && sc->powernow_states[j - 1].freq < state.freq) { 588 memcpy(&sc->powernow_states[j], 589 &sc->powernow_states[j - 1], 590 sizeof(struct powernow_state)); 591 --j; 592 } 593 memcpy(&sc->powernow_states[j], &state, 594 sizeof(struct powernow_state)); 595 ++n; 596 } 597 598 /* 599 * Fix powernow_max_states, if errata a0 give us less states 600 * than expected. 601 */ 602 sc->powernow_max_states = n; 603 604 if (bootverbose) 605 for (i = 0; i < sc->powernow_max_states; ++i) { 606 int fid = sc->powernow_states[i].fid; 607 int vid = sc->powernow_states[i].vid; 608 609 printf("powernow: %2i %8dkHz FID %02x VID %02x\n", 610 i, 611 sc->powernow_states[i].freq, 612 fid, 613 vid); 614 } 615 616 return (0); 617} 618 619static int 620cpuid_is_k7(u_int cpuid) 621{ 622 623 switch (cpuid) { 624 case 0x760: 625 case 0x761: 626 case 0x762: 627 case 0x770: 628 case 0x771: 629 case 0x780: 630 case 0x781: 631 case 0x7a0: 632 return (TRUE); 633 } 634 return (FALSE); 635} 636 637static int 638pn_decode_pst(device_t dev) 639{ 640 int maxpst; 641 struct pn_softc *sc; 642 u_int cpuid, maxfid, startvid; 643 u_long sig; 644 struct psb_header *psb; 645 uint8_t *p; 646 u_int regs[4]; 647 uint64_t status; 648 649 sc = device_get_softc(dev); 650 651 do_cpuid(0x80000001, regs); 652 cpuid = regs[0]; 653 654 if ((cpuid & 0xfff) == 0x760) 655 sc->errata |= A0_ERRATA; 656 657 status = rdmsr(MSR_AMDK7_FIDVID_STATUS); 658 659 switch (sc->pn_type) { 660 case PN7_TYPE: 661 maxfid = PN7_STA_MFID(status); 662 startvid = PN7_STA_SVID(status); 663 break; 664 case PN8_TYPE: 665 maxfid = PN8_STA_MFID(status); 666 /* 667 * we should actually use a variable named 'maxvid' if K8, 668 * but why introducing a new variable for that? 669 */ 670 startvid = PN8_STA_MVID(status); 671 break; 672 default: 673 return (ENODEV); 674 } 675 676 if (bootverbose) { 677 device_printf(dev, "STATUS: 0x%jx\n", status); 678 device_printf(dev, "STATUS: maxfid: 0x%02x\n", maxfid); 679 device_printf(dev, "STATUS: %s: 0x%02x\n", 680 sc->pn_type == PN7_TYPE ? "startvid" : "maxvid", 681 startvid); 682 } 683 684 sig = bios_sigsearch(PSB_START, PSB_SIG, PSB_LEN, PSB_STEP, PSB_OFF); 685 if (sig) { 686 struct pst_header *pst; 687 688 psb = (struct psb_header*)(uintptr_t)BIOS_PADDRTOVADDR(sig); 689 690 switch (psb->version) { 691 default: 692 return (ENODEV); 693 case 0x14: 694 /* 695 * We can't be picky about numpst since at least 696 * some systems have a value of 1 and some have 2. 697 * We trust that cpuid_is_k7() will be better at 698 * catching that we're on a K8 anyway. 699 */ 700 if (sc->pn_type != PN8_TYPE) 701 return (EINVAL); 702 sc->vst = psb->settlingtime; 703 sc->rvo = PN8_PSB_TO_RVO(psb->res1), 704 sc->irt = PN8_PSB_TO_IRT(psb->res1), 705 sc->mvs = PN8_PSB_TO_MVS(psb->res1), 706 sc->low = PN8_PSB_TO_BATT(psb->res1); 707 if (bootverbose) { 708 device_printf(dev, "PSB: VST: %d\n", 709 psb->settlingtime); 710 device_printf(dev, "PSB: RVO %x IRT %d " 711 "MVS %d BATT %d\n", 712 sc->rvo, 713 sc->irt, 714 sc->mvs, 715 sc->low); 716 } 717 break; 718 case 0x12: 719 if (sc->pn_type != PN7_TYPE) 720 return (EINVAL); 721 sc->sgtc = psb->settlingtime * sc->fsb; 722 if (sc->sgtc < 100 * sc->fsb) 723 sc->sgtc = 100 * sc->fsb; 724 break; 725 } 726 727 p = ((uint8_t *) psb) + sizeof(struct psb_header); 728 pst = (struct pst_header*) p; 729 730 maxpst = 200; 731 732 do { 733 struct pst_header *pst = (struct pst_header*) p; 734 735 if (cpuid == pst->cpuid && 736 maxfid == pst->maxfid && 737 startvid == pst->startvid) { 738 sc->powernow_max_states = pst->numpstates; 739 switch (sc->pn_type) { 740 case PN7_TYPE: 741 if (abs(sc->fsb - pst->fsb) > 5) 742 continue; 743 break; 744 case PN8_TYPE: 745 break; 746 } 747 return (decode_pst(sc, 748 p + sizeof(struct pst_header), 749 sc->powernow_max_states)); 750 } 751 752 p += sizeof(struct pst_header) + (2 * pst->numpstates); 753 } while (cpuid_is_k7(pst->cpuid) && maxpst--); 754 755 device_printf(dev, "no match for extended cpuid %.3x\n", cpuid); 756 } 757 758 return (ENODEV); 759} 760 761static int 762pn_decode_acpi(device_t dev, device_t perf_dev) 763{ 764 int i, j, n; 765 uint64_t status; 766 uint32_t ctrl; 767 u_int cpuid; 768 u_int regs[4]; 769 struct pn_softc *sc; 770 struct powernow_state state; 771 struct cf_setting sets[POWERNOW_MAX_STATES]; 772 int count = POWERNOW_MAX_STATES; 773 int type; 774 int rv; 775 776 if (perf_dev == NULL) 777 return (ENXIO); 778 779 rv = CPUFREQ_DRV_SETTINGS(perf_dev, sets, &count); 780 if (rv) 781 return (ENXIO); 782 rv = CPUFREQ_DRV_TYPE(perf_dev, &type); 783 if (rv || (type & CPUFREQ_FLAG_INFO_ONLY) == 0) 784 return (ENXIO); 785 786 sc = device_get_softc(dev); 787 788 do_cpuid(0x80000001, regs); 789 cpuid = regs[0]; 790 if ((cpuid & 0xfff) == 0x760) 791 sc->errata |= A0_ERRATA; 792 793 ctrl = 0; 794 sc->sgtc = 0; 795 for (n = 0, i = 0; i < count; ++i) { 796 ctrl = sets[i].spec[PX_SPEC_CONTROL]; 797 switch (sc->pn_type) { 798 case PN7_TYPE: 799 state.fid = ACPI_PN7_CTRL_TO_FID(ctrl); 800 state.vid = ACPI_PN7_CTRL_TO_VID(ctrl); 801 if ((sc->errata & A0_ERRATA) && 802 (pn7_fid_to_mult[state.fid] % 10) == 5) 803 continue; 804 state.freq = 100 * pn7_fid_to_mult[state.fid] * sc->fsb; 805 break; 806 case PN8_TYPE: 807 state.fid = ACPI_PN8_CTRL_TO_FID(ctrl); 808 state.vid = ACPI_PN8_CTRL_TO_VID(ctrl); 809 state.freq = 100 * pn8_fid_to_mult[state.fid] * sc->fsb; 810 break; 811 } 812 813 state.power = sets[i].power; 814 815 j = n; 816 while (j > 0 && sc->powernow_states[j - 1].freq < state.freq) { 817 memcpy(&sc->powernow_states[j], 818 &sc->powernow_states[j - 1], 819 sizeof(struct powernow_state)); 820 --j; 821 } 822 memcpy(&sc->powernow_states[j], &state, 823 sizeof(struct powernow_state)); 824 ++n; 825 } 826 827 sc->powernow_max_states = n; 828 state = sc->powernow_states[0]; 829 status = rdmsr(MSR_AMDK7_FIDVID_STATUS); 830 831 switch (sc->pn_type) { 832 case PN7_TYPE: 833 sc->sgtc = ACPI_PN7_CTRL_TO_SGTC(ctrl); 834 /* 835 * XXX Some bios forget the max frequency! 836 * This maybe indicates we have the wrong tables. Therefore, 837 * don't implement a quirk, but fallback to BIOS legacy 838 * tables instead. 839 */ 840 if (PN7_STA_MFID(status) != state.fid) { 841 device_printf(dev, "ACPI MAX frequency not found\n"); 842 return (EINVAL); 843 } 844 break; 845 case PN8_TYPE: 846 sc->vst = ACPI_PN8_CTRL_TO_VST(ctrl), 847 sc->mvs = ACPI_PN8_CTRL_TO_MVS(ctrl), 848 sc->pll = ACPI_PN8_CTRL_TO_PLL(ctrl), 849 sc->rvo = ACPI_PN8_CTRL_TO_RVO(ctrl), 850 sc->irt = ACPI_PN8_CTRL_TO_IRT(ctrl); 851 sc->low = 0; /* XXX */ 852 853 /* 854 * powernow k8 supports only one low frequency. 855 */ 856 if (sc->powernow_max_states >= 2 && 857 (sc->powernow_states[sc->powernow_max_states - 2].fid < 8)) 858 return (EINVAL); 859 break; 860 } 861 862 return (0); 863} 864 865static void 866pn_identify(driver_t *driver, device_t parent) 867{ 868 device_t child; 869 870 if ((amd_pminfo & AMDPM_FID) == 0 || (amd_pminfo & AMDPM_VID) == 0) 871 return; 872 switch (cpu_id & 0xf00) { 873 case 0x600: 874 case 0xf00: 875 break; 876 default: 877 return; 878 } 879 if (device_find_child(parent, "powernow", -1) != NULL) 880 return; 881 if ((child = BUS_ADD_CHILD(parent, 10, "powernow", -1)) == NULL) 882 device_printf(parent, "powernow: add child failed\n"); 883} 884 885static int 886pn_probe(device_t dev) 887{ 888 struct pn_softc *sc; 889 uint64_t status; 890 uint64_t rate; 891 struct pcpu *pc; 892 u_int sfid, mfid, cfid; 893 894 sc = device_get_softc(dev); 895 sc->errata = 0; 896 status = rdmsr(MSR_AMDK7_FIDVID_STATUS); 897 898 pc = cpu_get_pcpu(dev); 899 if (pc == NULL) 900 return (ENODEV); 901 902 cpu_est_clockrate(pc->pc_cpuid, &rate); 903 904 switch (cpu_id & 0xf00) { 905 case 0x600: 906 sfid = PN7_STA_SFID(status); 907 mfid = PN7_STA_MFID(status); 908 cfid = PN7_STA_CFID(status); 909 sc->pn_type = PN7_TYPE; 910 sc->fsb = rate / 100000 / pn7_fid_to_mult[cfid]; 911 912 /* 913 * If start FID is different to max FID, then it is a 914 * mobile processor. If not, it is a low powered desktop 915 * processor. 916 */ 917 if (PN7_STA_SFID(status) != PN7_STA_MFID(status)) { 918 sc->vid_to_volts = pn7_mobile_vid_to_volts; 919 device_set_desc(dev, "PowerNow! K7"); 920 } else { 921 sc->vid_to_volts = pn7_desktop_vid_to_volts; 922 device_set_desc(dev, "Cool`n'Quiet K7"); 923 } 924 break; 925 926 case 0xf00: 927 sfid = PN8_STA_SFID(status); 928 mfid = PN8_STA_MFID(status); 929 cfid = PN8_STA_CFID(status); 930 sc->pn_type = PN8_TYPE; 931 sc->vid_to_volts = pn8_vid_to_volts; 932 sc->fsb = rate / 100000 / pn8_fid_to_mult[cfid]; 933 934 if (PN8_STA_SFID(status) != PN8_STA_MFID(status)) 935 device_set_desc(dev, "PowerNow! K8"); 936 else 937 device_set_desc(dev, "Cool`n'Quiet K8"); 938 break; 939 default: 940 return (ENODEV); 941 } 942 943 return (0); 944} 945 946static int 947pn_attach(device_t dev) 948{ 949 int rv; 950 device_t child; 951 952 child = device_find_child(device_get_parent(dev), "acpi_perf", -1); 953 if (child) { 954 rv = pn_decode_acpi(dev, child); 955 if (rv) 956 rv = pn_decode_pst(dev); 957 } else 958 rv = pn_decode_pst(dev); 959 960 if (rv != 0) 961 return (ENXIO); 962 cpufreq_register(dev); 963 return (0); 964} 965 966static int 967pn_detach(device_t dev) 968{ 969 970 return (cpufreq_unregister(dev)); 971} 972