powernow.c revision 184104
1144194Snjl/*- 2144194Snjl * Copyright (c) 2004-2005 Bruno Ducrot 3144194Snjl * Copyright (c) 2004 FUKUDA Nobuhiko <nfukuda@spa.is.uec.ac.jp> 4144194Snjl * 5144194Snjl * Redistribution and use in source and binary forms, with or without 6144194Snjl * modification, are permitted provided that the following conditions 7144194Snjl * are met: 8144194Snjl * 1. Redistributions of source code must retain the above copyright 9144194Snjl * notice, this list of conditions and the following disclaimer. 10144194Snjl * 2. Redistributions in binary form must reproduce the above copyright 11144194Snjl * notice, this list of conditions and the following disclaimer in the 12144194Snjl * documentation and/or other materials provided with the distribution. 13144194Snjl * 14144194Snjl * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15144194Snjl * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16144194Snjl * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17144194Snjl * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18144194Snjl * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19144194Snjl * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20144194Snjl * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21144194Snjl * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22144194Snjl * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23144194Snjl * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24144194Snjl */ 25144194Snjl 26144194Snjl/* 27144194Snjl * Many thanks to Nate Lawson for his helpful comments on this driver and 28144194Snjl * to Jung-uk Kim for testing. 29144194Snjl */ 30144194Snjl 31144194Snjl#include <sys/cdefs.h> 32144194Snjl__FBSDID("$FreeBSD: head/sys/i386/cpufreq/powernow.c 184104 2008-10-21 00:52:20Z jkim $"); 33144194Snjl 34144194Snjl#include <sys/param.h> 35144194Snjl#include <sys/bus.h> 36144194Snjl#include <sys/cpu.h> 37144194Snjl#include <sys/kernel.h> 38144194Snjl#include <sys/malloc.h> 39144194Snjl#include <sys/module.h> 40144194Snjl#include <sys/pcpu.h> 41144194Snjl#include <sys/systm.h> 42144194Snjl 43144194Snjl#include <machine/pc/bios.h> 44144194Snjl#include <machine/md_var.h> 45144194Snjl#include <machine/specialreg.h> 46144194Snjl#include <machine/cputypes.h> 47144194Snjl#include <machine/vmparam.h> 48144194Snjl#include <sys/rman.h> 49144194Snjl 50144194Snjl#include <vm/vm.h> 51144194Snjl#include <vm/pmap.h> 52144194Snjl 53144194Snjl#include "cpufreq_if.h" 54144194Snjl 55144194Snjl#define PN7_TYPE 0 56144194Snjl#define PN8_TYPE 1 57144194Snjl 58166197Sbruno/* Flags for some hardware bugs. */ 59166197Sbruno#define A0_ERRATA 0x1 /* Bugs for the rev. A0 of Athlon (K7): 60166197Sbruno * Interrupts must be disabled and no half 61166197Sbruno * multipliers are allowed */ 62166197Sbruno#define PENDING_STUCK 0x2 /* With some buggy chipset and some newer AMD64 63166197Sbruno * processor (Rev. G?): 64166197Sbruno * the pending bit from the msr FIDVID_STATUS 65166197Sbruno * is set forever. No workaround :( */ 66166197Sbruno 67144194Snjl/* Legacy configuration via BIOS table PSB. */ 68144194Snjl#define PSB_START 0 69144194Snjl#define PSB_STEP 0x10 70144194Snjl#define PSB_SIG "AMDK7PNOW!" 71144194Snjl#define PSB_LEN 10 72144194Snjl#define PSB_OFF 0 73144194Snjl 74144194Snjlstruct psb_header { 75144194Snjl char signature[10]; 76144194Snjl uint8_t version; 77144194Snjl uint8_t flags; 78144194Snjl uint16_t settlingtime; 79144194Snjl uint8_t res1; 80144194Snjl uint8_t numpst; 81144194Snjl} __packed; 82144194Snjl 83144194Snjlstruct pst_header { 84144194Snjl uint32_t cpuid; 85144194Snjl uint8_t fsb; 86144194Snjl uint8_t maxfid; 87144194Snjl uint8_t startvid; 88144194Snjl uint8_t numpstates; 89144194Snjl} __packed; 90144194Snjl 91144194Snjl/* 92144194Snjl * MSRs and bits used by Powernow technology 93144194Snjl */ 94144194Snjl#define MSR_AMDK7_FIDVID_CTL 0xc0010041 95144194Snjl#define MSR_AMDK7_FIDVID_STATUS 0xc0010042 96144194Snjl 97144194Snjl/* Bitfields used by K7 */ 98144194Snjl 99144194Snjl#define PN7_CTR_FID(x) ((x) & 0x1f) 100144194Snjl#define PN7_CTR_VID(x) (((x) & 0x1f) << 8) 101144194Snjl#define PN7_CTR_FIDC 0x00010000 102144194Snjl#define PN7_CTR_VIDC 0x00020000 103144194Snjl#define PN7_CTR_FIDCHRATIO 0x00100000 104144194Snjl#define PN7_CTR_SGTC(x) (((uint64_t)(x) & 0x000fffff) << 32) 105144194Snjl 106144194Snjl#define PN7_STA_CFID(x) ((x) & 0x1f) 107144194Snjl#define PN7_STA_SFID(x) (((x) >> 8) & 0x1f) 108144194Snjl#define PN7_STA_MFID(x) (((x) >> 16) & 0x1f) 109144194Snjl#define PN7_STA_CVID(x) (((x) >> 32) & 0x1f) 110144194Snjl#define PN7_STA_SVID(x) (((x) >> 40) & 0x1f) 111144194Snjl#define PN7_STA_MVID(x) (((x) >> 48) & 0x1f) 112144194Snjl 113144194Snjl/* ACPI ctr_val status register to powernow k7 configuration */ 114144194Snjl#define ACPI_PN7_CTRL_TO_FID(x) ((x) & 0x1f) 115144194Snjl#define ACPI_PN7_CTRL_TO_VID(x) (((x) >> 5) & 0x1f) 116144194Snjl#define ACPI_PN7_CTRL_TO_SGTC(x) (((x) >> 10) & 0xffff) 117144194Snjl 118144194Snjl/* Bitfields used by K8 */ 119144194Snjl 120144194Snjl#define PN8_CTR_FID(x) ((x) & 0x3f) 121144194Snjl#define PN8_CTR_VID(x) (((x) & 0x1f) << 8) 122144194Snjl#define PN8_CTR_PENDING(x) (((x) & 1) << 32) 123144194Snjl 124144194Snjl#define PN8_STA_CFID(x) ((x) & 0x3f) 125144194Snjl#define PN8_STA_SFID(x) (((x) >> 8) & 0x3f) 126144194Snjl#define PN8_STA_MFID(x) (((x) >> 16) & 0x3f) 127144194Snjl#define PN8_STA_PENDING(x) (((x) >> 31) & 0x01) 128144194Snjl#define PN8_STA_CVID(x) (((x) >> 32) & 0x1f) 129144194Snjl#define PN8_STA_SVID(x) (((x) >> 40) & 0x1f) 130144194Snjl#define PN8_STA_MVID(x) (((x) >> 48) & 0x1f) 131144194Snjl 132144194Snjl/* Reserved1 to powernow k8 configuration */ 133144194Snjl#define PN8_PSB_TO_RVO(x) ((x) & 0x03) 134144194Snjl#define PN8_PSB_TO_IRT(x) (((x) >> 2) & 0x03) 135144194Snjl#define PN8_PSB_TO_MVS(x) (((x) >> 4) & 0x03) 136144194Snjl#define PN8_PSB_TO_BATT(x) (((x) >> 6) & 0x03) 137144194Snjl 138144194Snjl/* ACPI ctr_val status register to powernow k8 configuration */ 139144194Snjl#define ACPI_PN8_CTRL_TO_FID(x) ((x) & 0x3f) 140144194Snjl#define ACPI_PN8_CTRL_TO_VID(x) (((x) >> 6) & 0x1f) 141144194Snjl#define ACPI_PN8_CTRL_TO_VST(x) (((x) >> 11) & 0x1f) 142144194Snjl#define ACPI_PN8_CTRL_TO_MVS(x) (((x) >> 18) & 0x03) 143144194Snjl#define ACPI_PN8_CTRL_TO_PLL(x) (((x) >> 20) & 0x7f) 144144194Snjl#define ACPI_PN8_CTRL_TO_RVO(x) (((x) >> 28) & 0x03) 145144194Snjl#define ACPI_PN8_CTRL_TO_IRT(x) (((x) >> 30) & 0x03) 146144194Snjl 147144194Snjl 148144194Snjl#define WRITE_FIDVID(fid, vid, ctrl) \ 149144194Snjl wrmsr(MSR_AMDK7_FIDVID_CTL, \ 150144194Snjl (((ctrl) << 32) | (1ULL << 16) | ((vid) << 8) | (fid))) 151144194Snjl 152144194Snjl#define COUNT_OFF_IRT(irt) DELAY(10 * (1 << (irt))) 153144194Snjl#define COUNT_OFF_VST(vst) DELAY(20 * (vst)) 154144194Snjl 155144194Snjl#define FID_TO_VCO_FID(fid) \ 156144194Snjl (((fid) < 8) ? (8 + ((fid) << 1)) : (fid)) 157144194Snjl 158144194Snjl/* 159144194Snjl * Divide each value by 10 to get the processor multiplier. 160144194Snjl * Some of those tables are the same as the Linux powernow-k7 161144194Snjl * implementation by Dave Jones. 162144194Snjl */ 163144194Snjlstatic int pn7_fid_to_mult[32] = { 164144194Snjl 110, 115, 120, 125, 50, 55, 60, 65, 165144194Snjl 70, 75, 80, 85, 90, 95, 100, 105, 166144194Snjl 30, 190, 40, 200, 130, 135, 140, 210, 167144194Snjl 150, 225, 160, 165, 170, 180, 0, 0, 168144194Snjl}; 169144194Snjl 170144194Snjl 171166197Sbrunostatic int pn8_fid_to_mult[64] = { 172166197Sbruno 40, 45, 50, 55, 60, 65, 70, 75, 173166197Sbruno 80, 85, 90, 95, 100, 105, 110, 115, 174166197Sbruno 120, 125, 130, 135, 140, 145, 150, 155, 175166197Sbruno 160, 165, 170, 175, 180, 185, 190, 195, 176166197Sbruno 200, 205, 210, 215, 220, 225, 230, 235, 177166197Sbruno 240, 245, 250, 255, 260, 265, 270, 275, 178166197Sbruno 280, 285, 290, 295, 300, 305, 310, 315, 179166197Sbruno 320, 325, 330, 335, 340, 345, 350, 355, 180144194Snjl}; 181144194Snjl 182144194Snjl/* 183144194Snjl * Units are in mV. 184144194Snjl */ 185144194Snjl/* Mobile VRM (K7) */ 186144194Snjlstatic int pn7_mobile_vid_to_volts[] = { 187144194Snjl 2000, 1950, 1900, 1850, 1800, 1750, 1700, 1650, 188144194Snjl 1600, 1550, 1500, 1450, 1400, 1350, 1300, 0, 189144194Snjl 1275, 1250, 1225, 1200, 1175, 1150, 1125, 1100, 190144194Snjl 1075, 1050, 1025, 1000, 975, 950, 925, 0, 191144194Snjl}; 192144194Snjl/* Desktop VRM (K7) */ 193144194Snjlstatic int pn7_desktop_vid_to_volts[] = { 194144194Snjl 2000, 1950, 1900, 1850, 1800, 1750, 1700, 1650, 195144194Snjl 1600, 1550, 1500, 1450, 1400, 1350, 1300, 0, 196144194Snjl 1275, 1250, 1225, 1200, 1175, 1150, 1125, 1100, 197144194Snjl 1075, 1050, 1025, 1000, 975, 950, 925, 0, 198144194Snjl}; 199144194Snjl/* Desktop and Mobile VRM (K8) */ 200144194Snjlstatic int pn8_vid_to_volts[] = { 201144194Snjl 1550, 1525, 1500, 1475, 1450, 1425, 1400, 1375, 202144194Snjl 1350, 1325, 1300, 1275, 1250, 1225, 1200, 1175, 203144194Snjl 1150, 1125, 1100, 1075, 1050, 1025, 1000, 975, 204144194Snjl 950, 925, 900, 875, 850, 825, 800, 0, 205144194Snjl}; 206144194Snjl 207144194Snjl#define POWERNOW_MAX_STATES 16 208144194Snjl 209144194Snjlstruct powernow_state { 210144194Snjl int freq; 211144194Snjl int power; 212144194Snjl int fid; 213144194Snjl int vid; 214144194Snjl}; 215144194Snjl 216144194Snjlstruct pn_softc { 217144194Snjl device_t dev; 218144194Snjl int pn_type; 219144194Snjl struct powernow_state powernow_states[POWERNOW_MAX_STATES]; 220144194Snjl u_int fsb; 221144194Snjl u_int sgtc; 222144194Snjl u_int vst; 223144194Snjl u_int mvs; 224144194Snjl u_int pll; 225144194Snjl u_int rvo; 226144194Snjl u_int irt; 227144194Snjl int low; 228144194Snjl int powernow_max_states; 229144194Snjl u_int powernow_state; 230166197Sbruno u_int errata; 231144194Snjl int *vid_to_volts; 232144194Snjl}; 233144194Snjl 234144194Snjl/* 235144194Snjl * Offsets in struct cf_setting array for private values given by 236144194Snjl * acpi_perf driver. 237144194Snjl */ 238144194Snjl#define PX_SPEC_CONTROL 0 239144194Snjl#define PX_SPEC_STATUS 1 240144194Snjl 241144194Snjlstatic void pn_identify(driver_t *driver, device_t parent); 242144194Snjlstatic int pn_probe(device_t dev); 243144194Snjlstatic int pn_attach(device_t dev); 244144194Snjlstatic int pn_detach(device_t dev); 245144194Snjlstatic int pn_set(device_t dev, const struct cf_setting *cf); 246144194Snjlstatic int pn_get(device_t dev, struct cf_setting *cf); 247144194Snjlstatic int pn_settings(device_t dev, struct cf_setting *sets, 248144194Snjl int *count); 249144194Snjlstatic int pn_type(device_t dev, int *type); 250144194Snjl 251144194Snjlstatic device_method_t pn_methods[] = { 252144194Snjl /* Device interface */ 253144194Snjl DEVMETHOD(device_identify, pn_identify), 254144194Snjl DEVMETHOD(device_probe, pn_probe), 255144194Snjl DEVMETHOD(device_attach, pn_attach), 256144194Snjl DEVMETHOD(device_detach, pn_detach), 257144194Snjl 258144194Snjl /* cpufreq interface */ 259144194Snjl DEVMETHOD(cpufreq_drv_set, pn_set), 260144194Snjl DEVMETHOD(cpufreq_drv_get, pn_get), 261144194Snjl DEVMETHOD(cpufreq_drv_settings, pn_settings), 262144194Snjl DEVMETHOD(cpufreq_drv_type, pn_type), 263144194Snjl 264144194Snjl {0, 0} 265144194Snjl}; 266144194Snjl 267144194Snjlstatic devclass_t pn_devclass; 268144194Snjlstatic driver_t pn_driver = { 269144194Snjl "powernow", 270144194Snjl pn_methods, 271144194Snjl sizeof(struct pn_softc), 272144194Snjl}; 273144194Snjl 274144194SnjlDRIVER_MODULE(powernow, cpu, pn_driver, pn_devclass, 0, 0); 275144194Snjl 276144194Snjlstatic int 277144194Snjlpn7_setfidvid(struct pn_softc *sc, int fid, int vid) 278144194Snjl{ 279144194Snjl int cfid, cvid; 280144194Snjl uint64_t status, ctl; 281144194Snjl 282144194Snjl status = rdmsr(MSR_AMDK7_FIDVID_STATUS); 283144194Snjl cfid = PN7_STA_CFID(status); 284144194Snjl cvid = PN7_STA_CVID(status); 285144194Snjl 286144194Snjl /* We're already at the requested level. */ 287144194Snjl if (fid == cfid && vid == cvid) 288144194Snjl return (0); 289144194Snjl 290144194Snjl ctl = rdmsr(MSR_AMDK7_FIDVID_CTL) & PN7_CTR_FIDCHRATIO; 291144194Snjl 292144194Snjl ctl |= PN7_CTR_FID(fid); 293144194Snjl ctl |= PN7_CTR_VID(vid); 294144194Snjl ctl |= PN7_CTR_SGTC(sc->sgtc); 295144194Snjl 296166197Sbruno if (sc->errata & A0_ERRATA) 297144194Snjl disable_intr(); 298144194Snjl 299144194Snjl if (pn7_fid_to_mult[fid] < pn7_fid_to_mult[cfid]) { 300144194Snjl wrmsr(MSR_AMDK7_FIDVID_CTL, ctl | PN7_CTR_FIDC); 301144194Snjl if (vid != cvid) 302144194Snjl wrmsr(MSR_AMDK7_FIDVID_CTL, ctl | PN7_CTR_VIDC); 303144194Snjl } else { 304144194Snjl wrmsr(MSR_AMDK7_FIDVID_CTL, ctl | PN7_CTR_VIDC); 305144194Snjl if (fid != cfid) 306144194Snjl wrmsr(MSR_AMDK7_FIDVID_CTL, ctl | PN7_CTR_FIDC); 307144194Snjl } 308144194Snjl 309166197Sbruno if (sc->errata & A0_ERRATA) 310144194Snjl enable_intr(); 311144194Snjl 312144194Snjl return (0); 313144194Snjl} 314144194Snjl 315144194Snjlstatic int 316166197Sbrunopn8_read_pending_wait(uint64_t *status) 317166197Sbruno{ 318166197Sbruno int i = 10000; 319166197Sbruno 320166197Sbruno do 321166197Sbruno *status = rdmsr(MSR_AMDK7_FIDVID_STATUS); 322166197Sbruno while (PN8_STA_PENDING(*status) && --i); 323166197Sbruno 324166197Sbruno return (i == 0 ? ENXIO : 0); 325166197Sbruno} 326166197Sbruno 327166197Sbrunostatic int 328166197Sbrunopn8_write_fidvid(u_int fid, u_int vid, uint64_t ctrl, uint64_t *status) 329166197Sbruno{ 330166197Sbruno int i = 100; 331166197Sbruno 332166197Sbruno do 333166197Sbruno WRITE_FIDVID(fid, vid, ctrl); 334166197Sbruno while (pn8_read_pending_wait(status) && --i); 335166197Sbruno 336166197Sbruno return (i == 0 ? ENXIO : 0); 337166197Sbruno} 338166197Sbruno 339166197Sbrunostatic int 340144194Snjlpn8_setfidvid(struct pn_softc *sc, int fid, int vid) 341144194Snjl{ 342144194Snjl uint64_t status; 343144194Snjl int cfid, cvid; 344144194Snjl int rvo; 345166197Sbruno int rv; 346144194Snjl u_int val; 347144194Snjl 348166197Sbruno rv = pn8_read_pending_wait(&status); 349166197Sbruno if (rv) 350166197Sbruno return (rv); 351166197Sbruno 352144194Snjl cfid = PN8_STA_CFID(status); 353144194Snjl cvid = PN8_STA_CVID(status); 354144194Snjl 355144194Snjl if (fid == cfid && vid == cvid) 356144194Snjl return (0); 357144194Snjl 358144194Snjl /* 359144194Snjl * Phase 1: Raise core voltage to requested VID if frequency is 360144194Snjl * going up. 361144194Snjl */ 362144194Snjl while (cvid > vid) { 363144194Snjl val = cvid - (1 << sc->mvs); 364166197Sbruno rv = pn8_write_fidvid(cfid, (val > 0) ? val : 0, 1ULL, &status); 365166197Sbruno if (rv) { 366166197Sbruno sc->errata |= PENDING_STUCK; 367166197Sbruno return (rv); 368166197Sbruno } 369144194Snjl cvid = PN8_STA_CVID(status); 370144194Snjl COUNT_OFF_VST(sc->vst); 371144194Snjl } 372144194Snjl 373144194Snjl /* ... then raise to voltage + RVO (if required) */ 374144194Snjl for (rvo = sc->rvo; rvo > 0 && cvid > 0; --rvo) { 375144194Snjl /* XXX It's not clear from spec if we have to do that 376144194Snjl * in 0.25 step or in MVS. Therefore do it as it's done 377144194Snjl * under Linux */ 378166197Sbruno rv = pn8_write_fidvid(cfid, cvid - 1, 1ULL, &status); 379166197Sbruno if (rv) { 380166197Sbruno sc->errata |= PENDING_STUCK; 381166197Sbruno return (rv); 382166197Sbruno } 383144194Snjl cvid = PN8_STA_CVID(status); 384144194Snjl COUNT_OFF_VST(sc->vst); 385144194Snjl } 386144194Snjl 387144194Snjl /* Phase 2: change to requested core frequency */ 388144194Snjl if (cfid != fid) { 389166197Sbruno u_int vco_fid, vco_cfid, fid_delta; 390144194Snjl 391144194Snjl vco_fid = FID_TO_VCO_FID(fid); 392144194Snjl vco_cfid = FID_TO_VCO_FID(cfid); 393144194Snjl 394144194Snjl while (abs(vco_fid - vco_cfid) > 2) { 395166197Sbruno fid_delta = (vco_cfid & 1) ? 1 : 2; 396144194Snjl if (fid > cfid) { 397166197Sbruno if (cfid > 7) 398166197Sbruno val = cfid + fid_delta; 399144194Snjl else 400166197Sbruno val = FID_TO_VCO_FID(cfid) + fid_delta; 401144194Snjl } else 402166197Sbruno val = cfid - fid_delta; 403166197Sbruno rv = pn8_write_fidvid(val, cvid, 404166197Sbruno sc->pll * (uint64_t) sc->fsb, 405166197Sbruno &status); 406166197Sbruno if (rv) { 407166197Sbruno sc->errata |= PENDING_STUCK; 408166197Sbruno return (rv); 409166197Sbruno } 410144194Snjl cfid = PN8_STA_CFID(status); 411144194Snjl COUNT_OFF_IRT(sc->irt); 412144194Snjl 413144194Snjl vco_cfid = FID_TO_VCO_FID(cfid); 414144194Snjl } 415144194Snjl 416166197Sbruno rv = pn8_write_fidvid(fid, cvid, 417166197Sbruno sc->pll * (uint64_t) sc->fsb, 418166197Sbruno &status); 419166197Sbruno if (rv) { 420166197Sbruno sc->errata |= PENDING_STUCK; 421166197Sbruno return (rv); 422166197Sbruno } 423144194Snjl cfid = PN8_STA_CFID(status); 424144194Snjl COUNT_OFF_IRT(sc->irt); 425144194Snjl } 426144194Snjl 427144194Snjl /* Phase 3: change to requested voltage */ 428144194Snjl if (cvid != vid) { 429166197Sbruno rv = pn8_write_fidvid(cfid, vid, 1ULL, &status); 430144194Snjl cvid = PN8_STA_CVID(status); 431144194Snjl COUNT_OFF_VST(sc->vst); 432144194Snjl } 433144194Snjl 434144194Snjl /* Check if transition failed. */ 435144194Snjl if (cfid != fid || cvid != vid) 436166197Sbruno rv = ENXIO; 437144194Snjl 438166197Sbruno return (rv); 439144194Snjl} 440144194Snjl 441144194Snjlstatic int 442144194Snjlpn_set(device_t dev, const struct cf_setting *cf) 443144194Snjl{ 444144194Snjl struct pn_softc *sc; 445144194Snjl int fid, vid; 446144194Snjl int i; 447144194Snjl int rv; 448144194Snjl 449144194Snjl if (cf == NULL) 450144194Snjl return (EINVAL); 451144194Snjl sc = device_get_softc(dev); 452144194Snjl 453166197Sbruno if (sc->errata & PENDING_STUCK) 454166197Sbruno return (ENXIO); 455166197Sbruno 456144194Snjl for (i = 0; i < sc->powernow_max_states; ++i) 457144194Snjl if (CPUFREQ_CMP(sc->powernow_states[i].freq / 1000, cf->freq)) 458144194Snjl break; 459144194Snjl 460144194Snjl fid = sc->powernow_states[i].fid; 461144194Snjl vid = sc->powernow_states[i].vid; 462144194Snjl 463144194Snjl rv = ENODEV; 464144194Snjl 465144194Snjl switch (sc->pn_type) { 466144194Snjl case PN7_TYPE: 467144194Snjl rv = pn7_setfidvid(sc, fid, vid); 468144194Snjl break; 469144194Snjl case PN8_TYPE: 470144194Snjl rv = pn8_setfidvid(sc, fid, vid); 471144194Snjl break; 472144194Snjl } 473144194Snjl 474144194Snjl return (rv); 475144194Snjl} 476144194Snjl 477144194Snjlstatic int 478144194Snjlpn_get(device_t dev, struct cf_setting *cf) 479144194Snjl{ 480144194Snjl struct pn_softc *sc; 481144194Snjl u_int cfid = 0, cvid = 0; 482144194Snjl int i; 483144194Snjl uint64_t status; 484144194Snjl 485144194Snjl if (cf == NULL) 486144194Snjl return (EINVAL); 487144194Snjl sc = device_get_softc(dev); 488166197Sbruno if (sc->errata & PENDING_STUCK) 489166197Sbruno return (ENXIO); 490144194Snjl 491144194Snjl status = rdmsr(MSR_AMDK7_FIDVID_STATUS); 492144194Snjl 493144194Snjl switch (sc->pn_type) { 494144194Snjl case PN7_TYPE: 495144194Snjl cfid = PN7_STA_CFID(status); 496144194Snjl cvid = PN7_STA_CVID(status); 497144194Snjl break; 498144194Snjl case PN8_TYPE: 499144194Snjl cfid = PN8_STA_CFID(status); 500144194Snjl cvid = PN8_STA_CVID(status); 501144194Snjl break; 502144194Snjl } 503144194Snjl for (i = 0; i < sc->powernow_max_states; ++i) 504144194Snjl if (cfid == sc->powernow_states[i].fid && 505144194Snjl cvid == sc->powernow_states[i].vid) 506144194Snjl break; 507144194Snjl 508144194Snjl if (i < sc->powernow_max_states) { 509144194Snjl cf->freq = sc->powernow_states[i].freq / 1000; 510144194Snjl cf->power = sc->powernow_states[i].power; 511144194Snjl cf->lat = 200; 512144194Snjl cf->volts = sc->vid_to_volts[cvid]; 513144194Snjl cf->dev = dev; 514144194Snjl } else { 515144194Snjl memset(cf, CPUFREQ_VAL_UNKNOWN, sizeof(*cf)); 516144194Snjl cf->dev = NULL; 517144194Snjl } 518144194Snjl 519144194Snjl return (0); 520144194Snjl} 521144194Snjl 522144194Snjlstatic int 523144194Snjlpn_settings(device_t dev, struct cf_setting *sets, int *count) 524144194Snjl{ 525144194Snjl struct pn_softc *sc; 526144194Snjl int i; 527144194Snjl 528144194Snjl if (sets == NULL|| count == NULL) 529144194Snjl return (EINVAL); 530144194Snjl sc = device_get_softc(dev); 531144194Snjl if (*count < sc->powernow_max_states) 532144194Snjl return (E2BIG); 533144194Snjl for (i = 0; i < sc->powernow_max_states; ++i) { 534144194Snjl sets[i].freq = sc->powernow_states[i].freq / 1000; 535144194Snjl sets[i].power = sc->powernow_states[i].power; 536144194Snjl sets[i].lat = 200; 537144194Snjl sets[i].volts = sc->vid_to_volts[sc->powernow_states[i].vid]; 538144194Snjl sets[i].dev = dev; 539144194Snjl } 540144194Snjl *count = sc->powernow_max_states; 541144194Snjl 542144194Snjl return (0); 543144194Snjl} 544144194Snjl 545144194Snjlstatic int 546144194Snjlpn_type(device_t dev, int *type) 547144194Snjl{ 548144194Snjl if (type == NULL) 549144194Snjl return (EINVAL); 550144194Snjl 551144194Snjl *type = CPUFREQ_TYPE_ABSOLUTE; 552144194Snjl 553144194Snjl return (0); 554144194Snjl} 555144194Snjl 556144194Snjl/* 557144194Snjl * Given a set of pair of fid/vid, and number of performance states, 558144194Snjl * compute powernow_states via an insertion sort. 559144194Snjl */ 560144194Snjlstatic int 561144194Snjldecode_pst(struct pn_softc *sc, uint8_t *p, int npstates) 562144194Snjl{ 563144194Snjl int i, j, n; 564144194Snjl struct powernow_state state; 565144194Snjl 566144194Snjl for (i = 0; i < POWERNOW_MAX_STATES; ++i) 567144194Snjl sc->powernow_states[i].freq = CPUFREQ_VAL_UNKNOWN; 568144194Snjl 569144194Snjl for (n = 0, i = 0; i < npstates; ++i) { 570144194Snjl state.fid = *p++; 571144194Snjl state.vid = *p++; 572144194Snjl state.power = CPUFREQ_VAL_UNKNOWN; 573144194Snjl 574144194Snjl switch (sc->pn_type) { 575144194Snjl case PN7_TYPE: 576144194Snjl state.freq = 100 * pn7_fid_to_mult[state.fid] * sc->fsb; 577166197Sbruno if ((sc->errata & A0_ERRATA) && 578144194Snjl (pn7_fid_to_mult[state.fid] % 10) == 5) 579144194Snjl continue; 580144194Snjl break; 581144194Snjl case PN8_TYPE: 582166197Sbruno state.freq = 100 * pn8_fid_to_mult[state.fid] * sc->fsb; 583144194Snjl break; 584144194Snjl } 585144194Snjl 586144194Snjl j = n; 587144194Snjl while (j > 0 && sc->powernow_states[j - 1].freq < state.freq) { 588144194Snjl memcpy(&sc->powernow_states[j], 589144194Snjl &sc->powernow_states[j - 1], 590144194Snjl sizeof(struct powernow_state)); 591144194Snjl --j; 592144194Snjl } 593144194Snjl memcpy(&sc->powernow_states[j], &state, 594144194Snjl sizeof(struct powernow_state)); 595144194Snjl ++n; 596144194Snjl } 597144194Snjl 598144194Snjl /* 599166197Sbruno * Fix powernow_max_states, if errata a0 give us less states 600144194Snjl * than expected. 601144194Snjl */ 602144194Snjl sc->powernow_max_states = n; 603144194Snjl 604144194Snjl if (bootverbose) 605144194Snjl for (i = 0; i < sc->powernow_max_states; ++i) { 606144194Snjl int fid = sc->powernow_states[i].fid; 607144194Snjl int vid = sc->powernow_states[i].vid; 608144194Snjl 609144194Snjl printf("powernow: %2i %8dkHz FID %02x VID %02x\n", 610144194Snjl i, 611144194Snjl sc->powernow_states[i].freq, 612144194Snjl fid, 613144194Snjl vid); 614144194Snjl } 615144194Snjl 616144194Snjl return (0); 617144194Snjl} 618144194Snjl 619144194Snjlstatic int 620144194Snjlcpuid_is_k7(u_int cpuid) 621144194Snjl{ 622144194Snjl 623144194Snjl switch (cpuid) { 624144194Snjl case 0x760: 625144194Snjl case 0x761: 626144194Snjl case 0x762: 627144194Snjl case 0x770: 628144194Snjl case 0x771: 629144194Snjl case 0x780: 630144194Snjl case 0x781: 631144194Snjl case 0x7a0: 632144194Snjl return (TRUE); 633144194Snjl } 634144194Snjl return (FALSE); 635144194Snjl} 636144194Snjl 637144194Snjlstatic int 638144194Snjlpn_decode_pst(device_t dev) 639144194Snjl{ 640144194Snjl int maxpst; 641144194Snjl struct pn_softc *sc; 642144194Snjl u_int cpuid, maxfid, startvid; 643144194Snjl u_long sig; 644144194Snjl struct psb_header *psb; 645144194Snjl uint8_t *p; 646144194Snjl u_int regs[4]; 647144194Snjl uint64_t status; 648144194Snjl 649144194Snjl sc = device_get_softc(dev); 650144194Snjl 651144194Snjl do_cpuid(0x80000001, regs); 652144194Snjl cpuid = regs[0]; 653144194Snjl 654144194Snjl if ((cpuid & 0xfff) == 0x760) 655166197Sbruno sc->errata |= A0_ERRATA; 656144194Snjl 657144194Snjl status = rdmsr(MSR_AMDK7_FIDVID_STATUS); 658144194Snjl 659144194Snjl switch (sc->pn_type) { 660144194Snjl case PN7_TYPE: 661144194Snjl maxfid = PN7_STA_MFID(status); 662144194Snjl startvid = PN7_STA_SVID(status); 663144194Snjl break; 664144194Snjl case PN8_TYPE: 665144194Snjl maxfid = PN8_STA_MFID(status); 666144194Snjl /* 667144194Snjl * we should actually use a variable named 'maxvid' if K8, 668144194Snjl * but why introducing a new variable for that? 669144194Snjl */ 670144194Snjl startvid = PN8_STA_MVID(status); 671144194Snjl break; 672144194Snjl default: 673144194Snjl return (ENODEV); 674144194Snjl } 675144194Snjl 676144194Snjl if (bootverbose) { 677144194Snjl device_printf(dev, "STATUS: 0x%jx\n", status); 678144194Snjl device_printf(dev, "STATUS: maxfid: 0x%02x\n", maxfid); 679144194Snjl device_printf(dev, "STATUS: %s: 0x%02x\n", 680144194Snjl sc->pn_type == PN7_TYPE ? "startvid" : "maxvid", 681144194Snjl startvid); 682144194Snjl } 683144194Snjl 684144194Snjl sig = bios_sigsearch(PSB_START, PSB_SIG, PSB_LEN, PSB_STEP, PSB_OFF); 685144194Snjl if (sig) { 686144194Snjl struct pst_header *pst; 687144194Snjl 688144194Snjl psb = (struct psb_header*)(uintptr_t)BIOS_PADDRTOVADDR(sig); 689144194Snjl 690144194Snjl switch (psb->version) { 691144194Snjl default: 692144194Snjl return (ENODEV); 693144194Snjl case 0x14: 694144380Snjl /* 695144380Snjl * We can't be picky about numpst since at least 696144380Snjl * some systems have a value of 1 and some have 2. 697144380Snjl * We trust that cpuid_is_k7() will be better at 698144380Snjl * catching that we're on a K8 anyway. 699144380Snjl */ 700144380Snjl if (sc->pn_type != PN8_TYPE) 701144194Snjl return (EINVAL); 702144194Snjl sc->vst = psb->settlingtime; 703144194Snjl sc->rvo = PN8_PSB_TO_RVO(psb->res1), 704144194Snjl sc->irt = PN8_PSB_TO_IRT(psb->res1), 705144194Snjl sc->mvs = PN8_PSB_TO_MVS(psb->res1), 706144194Snjl sc->low = PN8_PSB_TO_BATT(psb->res1); 707144194Snjl if (bootverbose) { 708144194Snjl device_printf(dev, "PSB: VST: %d\n", 709144194Snjl psb->settlingtime); 710144194Snjl device_printf(dev, "PSB: RVO %x IRT %d " 711144194Snjl "MVS %d BATT %d\n", 712144194Snjl sc->rvo, 713144194Snjl sc->irt, 714144194Snjl sc->mvs, 715144194Snjl sc->low); 716144194Snjl } 717144194Snjl break; 718144194Snjl case 0x12: 719144194Snjl if (sc->pn_type != PN7_TYPE) 720144194Snjl return (EINVAL); 721144194Snjl sc->sgtc = psb->settlingtime * sc->fsb; 722144194Snjl if (sc->sgtc < 100 * sc->fsb) 723144194Snjl sc->sgtc = 100 * sc->fsb; 724144194Snjl break; 725144194Snjl } 726144194Snjl 727144194Snjl p = ((uint8_t *) psb) + sizeof(struct psb_header); 728144194Snjl pst = (struct pst_header*) p; 729144194Snjl 730144194Snjl maxpst = 200; 731144194Snjl 732144194Snjl do { 733144194Snjl struct pst_header *pst = (struct pst_header*) p; 734144194Snjl 735144194Snjl if (cpuid == pst->cpuid && 736144194Snjl maxfid == pst->maxfid && 737144194Snjl startvid == pst->startvid) { 738144194Snjl sc->powernow_max_states = pst->numpstates; 739144194Snjl switch (sc->pn_type) { 740144194Snjl case PN7_TYPE: 741144194Snjl if (abs(sc->fsb - pst->fsb) > 5) 742144194Snjl continue; 743144194Snjl break; 744144194Snjl case PN8_TYPE: 745144194Snjl break; 746144194Snjl } 747144194Snjl return (decode_pst(sc, 748144194Snjl p + sizeof(struct pst_header), 749144194Snjl sc->powernow_max_states)); 750144194Snjl } 751144194Snjl 752144194Snjl p += sizeof(struct pst_header) + (2 * pst->numpstates); 753144194Snjl } while (cpuid_is_k7(pst->cpuid) && maxpst--); 754144194Snjl 755144194Snjl device_printf(dev, "no match for extended cpuid %.3x\n", cpuid); 756144194Snjl } 757144194Snjl 758144194Snjl return (ENODEV); 759144194Snjl} 760144194Snjl 761144194Snjlstatic int 762144194Snjlpn_decode_acpi(device_t dev, device_t perf_dev) 763144194Snjl{ 764144194Snjl int i, j, n; 765144194Snjl uint64_t status; 766144194Snjl uint32_t ctrl; 767144194Snjl u_int cpuid; 768144194Snjl u_int regs[4]; 769144194Snjl struct pn_softc *sc; 770144194Snjl struct powernow_state state; 771144194Snjl struct cf_setting sets[POWERNOW_MAX_STATES]; 772144194Snjl int count = POWERNOW_MAX_STATES; 773144194Snjl int type; 774144194Snjl int rv; 775144194Snjl 776144194Snjl if (perf_dev == NULL) 777144194Snjl return (ENXIO); 778144194Snjl 779144194Snjl rv = CPUFREQ_DRV_SETTINGS(perf_dev, sets, &count); 780144194Snjl if (rv) 781144194Snjl return (ENXIO); 782144194Snjl rv = CPUFREQ_DRV_TYPE(perf_dev, &type); 783144194Snjl if (rv || (type & CPUFREQ_FLAG_INFO_ONLY) == 0) 784144194Snjl return (ENXIO); 785144194Snjl 786144194Snjl sc = device_get_softc(dev); 787144194Snjl 788144194Snjl do_cpuid(0x80000001, regs); 789144194Snjl cpuid = regs[0]; 790144194Snjl if ((cpuid & 0xfff) == 0x760) 791166197Sbruno sc->errata |= A0_ERRATA; 792144194Snjl 793144194Snjl ctrl = 0; 794144194Snjl sc->sgtc = 0; 795144194Snjl for (n = 0, i = 0; i < count; ++i) { 796144194Snjl ctrl = sets[i].spec[PX_SPEC_CONTROL]; 797144194Snjl switch (sc->pn_type) { 798144194Snjl case PN7_TYPE: 799144194Snjl state.fid = ACPI_PN7_CTRL_TO_FID(ctrl); 800144194Snjl state.vid = ACPI_PN7_CTRL_TO_VID(ctrl); 801166197Sbruno if ((sc->errata & A0_ERRATA) && 802144194Snjl (pn7_fid_to_mult[state.fid] % 10) == 5) 803144194Snjl continue; 804144194Snjl state.freq = 100 * pn7_fid_to_mult[state.fid] * sc->fsb; 805144194Snjl break; 806144194Snjl case PN8_TYPE: 807144194Snjl state.fid = ACPI_PN8_CTRL_TO_FID(ctrl); 808144194Snjl state.vid = ACPI_PN8_CTRL_TO_VID(ctrl); 809166197Sbruno state.freq = 100 * pn8_fid_to_mult[state.fid] * sc->fsb; 810144194Snjl break; 811144194Snjl } 812144194Snjl 813144194Snjl state.power = sets[i].power; 814144194Snjl 815144194Snjl j = n; 816144194Snjl while (j > 0 && sc->powernow_states[j - 1].freq < state.freq) { 817144194Snjl memcpy(&sc->powernow_states[j], 818144194Snjl &sc->powernow_states[j - 1], 819144194Snjl sizeof(struct powernow_state)); 820144194Snjl --j; 821144194Snjl } 822144194Snjl memcpy(&sc->powernow_states[j], &state, 823144194Snjl sizeof(struct powernow_state)); 824144194Snjl ++n; 825144194Snjl } 826144194Snjl 827144194Snjl sc->powernow_max_states = n; 828144194Snjl state = sc->powernow_states[0]; 829144194Snjl status = rdmsr(MSR_AMDK7_FIDVID_STATUS); 830144194Snjl 831144194Snjl switch (sc->pn_type) { 832144194Snjl case PN7_TYPE: 833144194Snjl sc->sgtc = ACPI_PN7_CTRL_TO_SGTC(ctrl); 834144194Snjl /* 835144194Snjl * XXX Some bios forget the max frequency! 836144194Snjl * This maybe indicates we have the wrong tables. Therefore, 837144194Snjl * don't implement a quirk, but fallback to BIOS legacy 838144194Snjl * tables instead. 839144194Snjl */ 840144194Snjl if (PN7_STA_MFID(status) != state.fid) { 841144194Snjl device_printf(dev, "ACPI MAX frequency not found\n"); 842144194Snjl return (EINVAL); 843144194Snjl } 844144194Snjl break; 845144194Snjl case PN8_TYPE: 846144194Snjl sc->vst = ACPI_PN8_CTRL_TO_VST(ctrl), 847144194Snjl sc->mvs = ACPI_PN8_CTRL_TO_MVS(ctrl), 848144194Snjl sc->pll = ACPI_PN8_CTRL_TO_PLL(ctrl), 849144194Snjl sc->rvo = ACPI_PN8_CTRL_TO_RVO(ctrl), 850144194Snjl sc->irt = ACPI_PN8_CTRL_TO_IRT(ctrl); 851144194Snjl sc->low = 0; /* XXX */ 852144194Snjl 853144194Snjl /* 854144194Snjl * powernow k8 supports only one low frequency. 855144194Snjl */ 856144194Snjl if (sc->powernow_max_states >= 2 && 857144194Snjl (sc->powernow_states[sc->powernow_max_states - 2].fid < 8)) 858144194Snjl return (EINVAL); 859144194Snjl break; 860144194Snjl } 861144194Snjl 862144194Snjl return (0); 863144194Snjl} 864144194Snjl 865144194Snjlstatic void 866144194Snjlpn_identify(driver_t *driver, device_t parent) 867144194Snjl{ 868144194Snjl device_t child; 869144194Snjl 870184104Sjkim if ((amd_pminfo & AMDPM_FID) == 0 || (amd_pminfo & AMDPM_VID) == 0) 871144194Snjl return; 872144194Snjl switch (cpu_id & 0xf00) { 873144194Snjl case 0x600: 874144194Snjl case 0xf00: 875144194Snjl break; 876144194Snjl default: 877144194Snjl return; 878144194Snjl } 879144194Snjl if (device_find_child(parent, "powernow", -1) != NULL) 880144194Snjl return; 881181691Sjhb if ((child = BUS_ADD_CHILD(parent, 10, "powernow", -1)) == NULL) 882144194Snjl device_printf(parent, "powernow: add child failed\n"); 883144194Snjl} 884144194Snjl 885144194Snjlstatic int 886144194Snjlpn_probe(device_t dev) 887144194Snjl{ 888144194Snjl struct pn_softc *sc; 889144194Snjl uint64_t status; 890144194Snjl uint64_t rate; 891144194Snjl struct pcpu *pc; 892144194Snjl u_int sfid, mfid, cfid; 893144194Snjl 894144194Snjl sc = device_get_softc(dev); 895166197Sbruno sc->errata = 0; 896144194Snjl status = rdmsr(MSR_AMDK7_FIDVID_STATUS); 897144194Snjl 898144194Snjl pc = cpu_get_pcpu(dev); 899144194Snjl if (pc == NULL) 900144194Snjl return (ENODEV); 901144194Snjl 902144194Snjl cpu_est_clockrate(pc->pc_cpuid, &rate); 903144194Snjl 904144194Snjl switch (cpu_id & 0xf00) { 905144194Snjl case 0x600: 906144194Snjl sfid = PN7_STA_SFID(status); 907144194Snjl mfid = PN7_STA_MFID(status); 908144194Snjl cfid = PN7_STA_CFID(status); 909144194Snjl sc->pn_type = PN7_TYPE; 910144194Snjl sc->fsb = rate / 100000 / pn7_fid_to_mult[cfid]; 911144194Snjl 912144194Snjl /* 913144194Snjl * If start FID is different to max FID, then it is a 914144194Snjl * mobile processor. If not, it is a low powered desktop 915144194Snjl * processor. 916144194Snjl */ 917144194Snjl if (PN7_STA_SFID(status) != PN7_STA_MFID(status)) { 918144194Snjl sc->vid_to_volts = pn7_mobile_vid_to_volts; 919144194Snjl device_set_desc(dev, "PowerNow! K7"); 920144194Snjl } else { 921144194Snjl sc->vid_to_volts = pn7_desktop_vid_to_volts; 922144194Snjl device_set_desc(dev, "Cool`n'Quiet K7"); 923144194Snjl } 924144194Snjl break; 925144194Snjl 926144194Snjl case 0xf00: 927144194Snjl sfid = PN8_STA_SFID(status); 928144194Snjl mfid = PN8_STA_MFID(status); 929144194Snjl cfid = PN8_STA_CFID(status); 930144194Snjl sc->pn_type = PN8_TYPE; 931144194Snjl sc->vid_to_volts = pn8_vid_to_volts; 932166197Sbruno sc->fsb = rate / 100000 / pn8_fid_to_mult[cfid]; 933144194Snjl 934144194Snjl if (PN8_STA_SFID(status) != PN8_STA_MFID(status)) 935144194Snjl device_set_desc(dev, "PowerNow! K8"); 936144194Snjl else 937144194Snjl device_set_desc(dev, "Cool`n'Quiet K8"); 938144194Snjl break; 939144194Snjl default: 940144194Snjl return (ENODEV); 941144194Snjl } 942144194Snjl 943144194Snjl return (0); 944144194Snjl} 945144194Snjl 946144194Snjlstatic int 947144194Snjlpn_attach(device_t dev) 948144194Snjl{ 949144194Snjl int rv; 950144194Snjl device_t child; 951144194Snjl 952144194Snjl child = device_find_child(device_get_parent(dev), "acpi_perf", -1); 953144194Snjl if (child) { 954144194Snjl rv = pn_decode_acpi(dev, child); 955144194Snjl if (rv) 956144194Snjl rv = pn_decode_pst(dev); 957144194Snjl } else 958144194Snjl rv = pn_decode_pst(dev); 959144194Snjl 960144194Snjl if (rv != 0) 961144194Snjl return (ENXIO); 962144194Snjl cpufreq_register(dev); 963144194Snjl return (0); 964144194Snjl} 965144194Snjl 966144194Snjlstatic int 967144194Snjlpn_detach(device_t dev) 968144194Snjl{ 969144194Snjl 970182401Sjhb return (cpufreq_unregister(dev)); 971144194Snjl} 972