powernow.c revision 144380
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 144380 2005-03-31 06:11:04Z njl $"); 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/clock.h> 48144194Snjl#include <machine/vmparam.h> 49144194Snjl#include <sys/rman.h> 50144194Snjl 51144194Snjl#include <vm/vm.h> 52144194Snjl#include <vm/pmap.h> 53144194Snjl 54144194Snjl#include "cpufreq_if.h" 55144194Snjl 56144194Snjl#define PN7_TYPE 0 57144194Snjl#define PN8_TYPE 1 58144194Snjl 59144194Snjl/* Legacy configuration via BIOS table PSB. */ 60144194Snjl#define PSB_START 0 61144194Snjl#define PSB_STEP 0x10 62144194Snjl#define PSB_SIG "AMDK7PNOW!" 63144194Snjl#define PSB_LEN 10 64144194Snjl#define PSB_OFF 0 65144194Snjl 66144194Snjlstruct psb_header { 67144194Snjl char signature[10]; 68144194Snjl uint8_t version; 69144194Snjl uint8_t flags; 70144194Snjl uint16_t settlingtime; 71144194Snjl uint8_t res1; 72144194Snjl uint8_t numpst; 73144194Snjl} __packed; 74144194Snjl 75144194Snjlstruct pst_header { 76144194Snjl uint32_t cpuid; 77144194Snjl uint8_t fsb; 78144194Snjl uint8_t maxfid; 79144194Snjl uint8_t startvid; 80144194Snjl uint8_t numpstates; 81144194Snjl} __packed; 82144194Snjl 83144194Snjl/* 84144194Snjl * MSRs and bits used by Powernow technology 85144194Snjl */ 86144194Snjl#define MSR_AMDK7_FIDVID_CTL 0xc0010041 87144194Snjl#define MSR_AMDK7_FIDVID_STATUS 0xc0010042 88144194Snjl 89144194Snjl/* Bitfields used by K7 */ 90144194Snjl 91144194Snjl#define PN7_CTR_FID(x) ((x) & 0x1f) 92144194Snjl#define PN7_CTR_VID(x) (((x) & 0x1f) << 8) 93144194Snjl#define PN7_CTR_FIDC 0x00010000 94144194Snjl#define PN7_CTR_VIDC 0x00020000 95144194Snjl#define PN7_CTR_FIDCHRATIO 0x00100000 96144194Snjl#define PN7_CTR_SGTC(x) (((uint64_t)(x) & 0x000fffff) << 32) 97144194Snjl 98144194Snjl#define PN7_STA_CFID(x) ((x) & 0x1f) 99144194Snjl#define PN7_STA_SFID(x) (((x) >> 8) & 0x1f) 100144194Snjl#define PN7_STA_MFID(x) (((x) >> 16) & 0x1f) 101144194Snjl#define PN7_STA_CVID(x) (((x) >> 32) & 0x1f) 102144194Snjl#define PN7_STA_SVID(x) (((x) >> 40) & 0x1f) 103144194Snjl#define PN7_STA_MVID(x) (((x) >> 48) & 0x1f) 104144194Snjl 105144194Snjl/* ACPI ctr_val status register to powernow k7 configuration */ 106144194Snjl#define ACPI_PN7_CTRL_TO_FID(x) ((x) & 0x1f) 107144194Snjl#define ACPI_PN7_CTRL_TO_VID(x) (((x) >> 5) & 0x1f) 108144194Snjl#define ACPI_PN7_CTRL_TO_SGTC(x) (((x) >> 10) & 0xffff) 109144194Snjl 110144194Snjl/* Bitfields used by K8 */ 111144194Snjl 112144194Snjl#define PN8_CTR_FID(x) ((x) & 0x3f) 113144194Snjl#define PN8_CTR_VID(x) (((x) & 0x1f) << 8) 114144194Snjl#define PN8_CTR_PENDING(x) (((x) & 1) << 32) 115144194Snjl 116144194Snjl#define PN8_STA_CFID(x) ((x) & 0x3f) 117144194Snjl#define PN8_STA_SFID(x) (((x) >> 8) & 0x3f) 118144194Snjl#define PN8_STA_MFID(x) (((x) >> 16) & 0x3f) 119144194Snjl#define PN8_STA_PENDING(x) (((x) >> 31) & 0x01) 120144194Snjl#define PN8_STA_CVID(x) (((x) >> 32) & 0x1f) 121144194Snjl#define PN8_STA_SVID(x) (((x) >> 40) & 0x1f) 122144194Snjl#define PN8_STA_MVID(x) (((x) >> 48) & 0x1f) 123144194Snjl 124144194Snjl/* Reserved1 to powernow k8 configuration */ 125144194Snjl#define PN8_PSB_TO_RVO(x) ((x) & 0x03) 126144194Snjl#define PN8_PSB_TO_IRT(x) (((x) >> 2) & 0x03) 127144194Snjl#define PN8_PSB_TO_MVS(x) (((x) >> 4) & 0x03) 128144194Snjl#define PN8_PSB_TO_BATT(x) (((x) >> 6) & 0x03) 129144194Snjl 130144194Snjl/* ACPI ctr_val status register to powernow k8 configuration */ 131144194Snjl#define ACPI_PN8_CTRL_TO_FID(x) ((x) & 0x3f) 132144194Snjl#define ACPI_PN8_CTRL_TO_VID(x) (((x) >> 6) & 0x1f) 133144194Snjl#define ACPI_PN8_CTRL_TO_VST(x) (((x) >> 11) & 0x1f) 134144194Snjl#define ACPI_PN8_CTRL_TO_MVS(x) (((x) >> 18) & 0x03) 135144194Snjl#define ACPI_PN8_CTRL_TO_PLL(x) (((x) >> 20) & 0x7f) 136144194Snjl#define ACPI_PN8_CTRL_TO_RVO(x) (((x) >> 28) & 0x03) 137144194Snjl#define ACPI_PN8_CTRL_TO_IRT(x) (((x) >> 30) & 0x03) 138144194Snjl 139144194Snjl 140144194Snjl#define WRITE_FIDVID(fid, vid, ctrl) \ 141144194Snjl wrmsr(MSR_AMDK7_FIDVID_CTL, \ 142144194Snjl (((ctrl) << 32) | (1ULL << 16) | ((vid) << 8) | (fid))) 143144194Snjl 144144194Snjl#define READ_PENDING_WAIT(status) \ 145144194Snjl do { \ 146144194Snjl (status) = rdmsr(MSR_AMDK7_FIDVID_STATUS); \ 147144194Snjl } while (PN8_STA_PENDING(status)) 148144194Snjl 149144194Snjl#define COUNT_OFF_IRT(irt) DELAY(10 * (1 << (irt))) 150144194Snjl#define COUNT_OFF_VST(vst) DELAY(20 * (vst)) 151144194Snjl 152144194Snjl#define FID_TO_VCO_FID(fid) \ 153144194Snjl (((fid) < 8) ? (8 + ((fid) << 1)) : (fid)) 154144194Snjl 155144194Snjl/* 156144194Snjl * Divide each value by 10 to get the processor multiplier. 157144194Snjl * Some of those tables are the same as the Linux powernow-k7 158144194Snjl * implementation by Dave Jones. 159144194Snjl */ 160144194Snjlstatic int pn7_fid_to_mult[32] = { 161144194Snjl 110, 115, 120, 125, 50, 55, 60, 65, 162144194Snjl 70, 75, 80, 85, 90, 95, 100, 105, 163144194Snjl 30, 190, 40, 200, 130, 135, 140, 210, 164144194Snjl 150, 225, 160, 165, 170, 180, 0, 0, 165144194Snjl}; 166144194Snjl 167144194Snjl 168144194Snjlstatic int pn8_fid_to_mult[32] = { 169144194Snjl 40, 50, 60, 70, 80, 90, 100, 110, 170144194Snjl 120, 130, 140, 150, 160, 170, 180, 190, 171144194Snjl 220, 230, 240, 250, 260, 270, 280, 290, 172144194Snjl 300, 310, 320, 330, 340, 350, 173144194Snjl}; 174144194Snjl 175144194Snjl/* 176144194Snjl * Units are in mV. 177144194Snjl */ 178144194Snjl/* Mobile VRM (K7) */ 179144194Snjlstatic int pn7_mobile_vid_to_volts[] = { 180144194Snjl 2000, 1950, 1900, 1850, 1800, 1750, 1700, 1650, 181144194Snjl 1600, 1550, 1500, 1450, 1400, 1350, 1300, 0, 182144194Snjl 1275, 1250, 1225, 1200, 1175, 1150, 1125, 1100, 183144194Snjl 1075, 1050, 1025, 1000, 975, 950, 925, 0, 184144194Snjl}; 185144194Snjl/* Desktop VRM (K7) */ 186144194Snjlstatic int pn7_desktop_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 and Mobile VRM (K8) */ 193144194Snjlstatic int pn8_vid_to_volts[] = { 194144194Snjl 1550, 1525, 1500, 1475, 1450, 1425, 1400, 1375, 195144194Snjl 1350, 1325, 1300, 1275, 1250, 1225, 1200, 1175, 196144194Snjl 1150, 1125, 1100, 1075, 1050, 1025, 1000, 975, 197144194Snjl 950, 925, 900, 875, 850, 825, 800, 0, 198144194Snjl}; 199144194Snjl 200144194Snjl#define POWERNOW_MAX_STATES 16 201144194Snjl 202144194Snjlstruct powernow_state { 203144194Snjl int freq; 204144194Snjl int power; 205144194Snjl int fid; 206144194Snjl int vid; 207144194Snjl}; 208144194Snjl 209144194Snjlstruct pn_softc { 210144194Snjl device_t dev; 211144194Snjl int pn_type; 212144194Snjl struct powernow_state powernow_states[POWERNOW_MAX_STATES]; 213144194Snjl u_int fsb; 214144194Snjl u_int sgtc; 215144194Snjl u_int vst; 216144194Snjl u_int mvs; 217144194Snjl u_int pll; 218144194Snjl u_int rvo; 219144194Snjl u_int irt; 220144194Snjl int low; 221144194Snjl int powernow_max_states; 222144194Snjl u_int powernow_state; 223144194Snjl int errata_a0; 224144194Snjl int *vid_to_volts; 225144194Snjl}; 226144194Snjl 227144194Snjl/* 228144194Snjl * Offsets in struct cf_setting array for private values given by 229144194Snjl * acpi_perf driver. 230144194Snjl */ 231144194Snjl#define PX_SPEC_CONTROL 0 232144194Snjl#define PX_SPEC_STATUS 1 233144194Snjl 234144194Snjlstatic void pn_identify(driver_t *driver, device_t parent); 235144194Snjlstatic int pn_probe(device_t dev); 236144194Snjlstatic int pn_attach(device_t dev); 237144194Snjlstatic int pn_detach(device_t dev); 238144194Snjlstatic int pn_set(device_t dev, const struct cf_setting *cf); 239144194Snjlstatic int pn_get(device_t dev, struct cf_setting *cf); 240144194Snjlstatic int pn_settings(device_t dev, struct cf_setting *sets, 241144194Snjl int *count); 242144194Snjlstatic int pn_type(device_t dev, int *type); 243144194Snjl 244144194Snjlstatic device_method_t pn_methods[] = { 245144194Snjl /* Device interface */ 246144194Snjl DEVMETHOD(device_identify, pn_identify), 247144194Snjl DEVMETHOD(device_probe, pn_probe), 248144194Snjl DEVMETHOD(device_attach, pn_attach), 249144194Snjl DEVMETHOD(device_detach, pn_detach), 250144194Snjl 251144194Snjl /* cpufreq interface */ 252144194Snjl DEVMETHOD(cpufreq_drv_set, pn_set), 253144194Snjl DEVMETHOD(cpufreq_drv_get, pn_get), 254144194Snjl DEVMETHOD(cpufreq_drv_settings, pn_settings), 255144194Snjl DEVMETHOD(cpufreq_drv_type, pn_type), 256144194Snjl 257144194Snjl {0, 0} 258144194Snjl}; 259144194Snjl 260144194Snjlstatic devclass_t pn_devclass; 261144194Snjlstatic driver_t pn_driver = { 262144194Snjl "powernow", 263144194Snjl pn_methods, 264144194Snjl sizeof(struct pn_softc), 265144194Snjl}; 266144194Snjl 267144194SnjlDRIVER_MODULE(powernow, cpu, pn_driver, pn_devclass, 0, 0); 268144194Snjl 269144194Snjlstatic int 270144194Snjlpn7_setfidvid(struct pn_softc *sc, int fid, int vid) 271144194Snjl{ 272144194Snjl int cfid, cvid; 273144194Snjl uint64_t status, ctl; 274144194Snjl 275144194Snjl status = rdmsr(MSR_AMDK7_FIDVID_STATUS); 276144194Snjl cfid = PN7_STA_CFID(status); 277144194Snjl cvid = PN7_STA_CVID(status); 278144194Snjl 279144194Snjl /* We're already at the requested level. */ 280144194Snjl if (fid == cfid && vid == cvid) 281144194Snjl return (0); 282144194Snjl 283144194Snjl ctl = rdmsr(MSR_AMDK7_FIDVID_CTL) & PN7_CTR_FIDCHRATIO; 284144194Snjl 285144194Snjl ctl |= PN7_CTR_FID(fid); 286144194Snjl ctl |= PN7_CTR_VID(vid); 287144194Snjl ctl |= PN7_CTR_SGTC(sc->sgtc); 288144194Snjl 289144194Snjl if (sc->errata_a0) 290144194Snjl disable_intr(); 291144194Snjl 292144194Snjl if (pn7_fid_to_mult[fid] < pn7_fid_to_mult[cfid]) { 293144194Snjl wrmsr(MSR_AMDK7_FIDVID_CTL, ctl | PN7_CTR_FIDC); 294144194Snjl if (vid != cvid) 295144194Snjl wrmsr(MSR_AMDK7_FIDVID_CTL, ctl | PN7_CTR_VIDC); 296144194Snjl } else { 297144194Snjl wrmsr(MSR_AMDK7_FIDVID_CTL, ctl | PN7_CTR_VIDC); 298144194Snjl if (fid != cfid) 299144194Snjl wrmsr(MSR_AMDK7_FIDVID_CTL, ctl | PN7_CTR_FIDC); 300144194Snjl } 301144194Snjl 302144194Snjl if (sc->errata_a0) 303144194Snjl enable_intr(); 304144194Snjl 305144194Snjl return (0); 306144194Snjl} 307144194Snjl 308144194Snjlstatic int 309144194Snjlpn8_setfidvid(struct pn_softc *sc, int fid, int vid) 310144194Snjl{ 311144194Snjl uint64_t status; 312144194Snjl int cfid, cvid; 313144194Snjl int rvo; 314144194Snjl u_int val; 315144194Snjl 316144194Snjl READ_PENDING_WAIT(status); 317144194Snjl cfid = PN8_STA_CFID(status); 318144194Snjl cvid = PN8_STA_CVID(status); 319144194Snjl 320144194Snjl if (fid == cfid && vid == cvid) 321144194Snjl return (0); 322144194Snjl 323144194Snjl /* 324144194Snjl * Phase 1: Raise core voltage to requested VID if frequency is 325144194Snjl * going up. 326144194Snjl */ 327144194Snjl while (cvid > vid) { 328144194Snjl val = cvid - (1 << sc->mvs); 329144194Snjl WRITE_FIDVID(cfid, (val > 0) ? val : 0, 1ULL); 330144194Snjl READ_PENDING_WAIT(status); 331144194Snjl cvid = PN8_STA_CVID(status); 332144194Snjl COUNT_OFF_VST(sc->vst); 333144194Snjl } 334144194Snjl 335144194Snjl /* ... then raise to voltage + RVO (if required) */ 336144194Snjl for (rvo = sc->rvo; rvo > 0 && cvid > 0; --rvo) { 337144194Snjl /* XXX It's not clear from spec if we have to do that 338144194Snjl * in 0.25 step or in MVS. Therefore do it as it's done 339144194Snjl * under Linux */ 340144194Snjl WRITE_FIDVID(cfid, cvid - 1, 1ULL); 341144194Snjl READ_PENDING_WAIT(status); 342144194Snjl cvid = PN8_STA_CVID(status); 343144194Snjl COUNT_OFF_VST(sc->vst); 344144194Snjl } 345144194Snjl 346144194Snjl /* Phase 2: change to requested core frequency */ 347144194Snjl if (cfid != fid) { 348144194Snjl u_int vco_fid, vco_cfid; 349144194Snjl 350144194Snjl vco_fid = FID_TO_VCO_FID(fid); 351144194Snjl vco_cfid = FID_TO_VCO_FID(cfid); 352144194Snjl 353144194Snjl while (abs(vco_fid - vco_cfid) > 2) { 354144194Snjl if (fid > cfid) { 355144194Snjl if (cfid > 6) 356144194Snjl val = cfid + 2; 357144194Snjl else 358144194Snjl val = FID_TO_VCO_FID(cfid) + 2; 359144194Snjl } else 360144194Snjl val = cfid - 2; 361144194Snjl WRITE_FIDVID(val, cvid, sc->pll * (uint64_t) sc->fsb); 362144194Snjl READ_PENDING_WAIT(status); 363144194Snjl cfid = PN8_STA_CFID(status); 364144194Snjl COUNT_OFF_IRT(sc->irt); 365144194Snjl 366144194Snjl vco_cfid = FID_TO_VCO_FID(cfid); 367144194Snjl } 368144194Snjl 369144194Snjl WRITE_FIDVID(fid, cvid, sc->pll * (uint64_t) sc->fsb); 370144194Snjl READ_PENDING_WAIT(status); 371144194Snjl cfid = PN8_STA_CFID(status); 372144194Snjl COUNT_OFF_IRT(sc->irt); 373144194Snjl } 374144194Snjl 375144194Snjl /* Phase 3: change to requested voltage */ 376144194Snjl if (cvid != vid) { 377144194Snjl WRITE_FIDVID(cfid, vid, 1ULL); 378144194Snjl READ_PENDING_WAIT(status); 379144194Snjl cvid = PN8_STA_CVID(status); 380144194Snjl COUNT_OFF_VST(sc->vst); 381144194Snjl } 382144194Snjl 383144194Snjl /* Check if transition failed. */ 384144194Snjl if (cfid != fid || cvid != vid) 385144194Snjl return (ENXIO); 386144194Snjl 387144194Snjl return (0); 388144194Snjl} 389144194Snjl 390144194Snjlstatic int 391144194Snjlpn_set(device_t dev, const struct cf_setting *cf) 392144194Snjl{ 393144194Snjl struct pn_softc *sc; 394144194Snjl int fid, vid; 395144194Snjl int i; 396144194Snjl int rv; 397144194Snjl 398144194Snjl if (cf == NULL) 399144194Snjl return (EINVAL); 400144194Snjl sc = device_get_softc(dev); 401144194Snjl 402144194Snjl for (i = 0; i < sc->powernow_max_states; ++i) 403144194Snjl if (CPUFREQ_CMP(sc->powernow_states[i].freq / 1000, cf->freq)) 404144194Snjl break; 405144194Snjl 406144194Snjl fid = sc->powernow_states[i].fid; 407144194Snjl vid = sc->powernow_states[i].vid; 408144194Snjl 409144194Snjl rv = ENODEV; 410144194Snjl 411144194Snjl switch (sc->pn_type) { 412144194Snjl case PN7_TYPE: 413144194Snjl rv = pn7_setfidvid(sc, fid, vid); 414144194Snjl break; 415144194Snjl case PN8_TYPE: 416144194Snjl rv = pn8_setfidvid(sc, fid, vid); 417144194Snjl break; 418144194Snjl } 419144194Snjl 420144194Snjl return (rv); 421144194Snjl} 422144194Snjl 423144194Snjlstatic int 424144194Snjlpn_get(device_t dev, struct cf_setting *cf) 425144194Snjl{ 426144194Snjl struct pn_softc *sc; 427144194Snjl u_int cfid = 0, cvid = 0; 428144194Snjl int i; 429144194Snjl uint64_t status; 430144194Snjl 431144194Snjl if (cf == NULL) 432144194Snjl return (EINVAL); 433144194Snjl sc = device_get_softc(dev); 434144194Snjl 435144194Snjl status = rdmsr(MSR_AMDK7_FIDVID_STATUS); 436144194Snjl 437144194Snjl switch (sc->pn_type) { 438144194Snjl case PN7_TYPE: 439144194Snjl cfid = PN7_STA_CFID(status); 440144194Snjl cvid = PN7_STA_CVID(status); 441144194Snjl break; 442144194Snjl case PN8_TYPE: 443144194Snjl cfid = PN8_STA_CFID(status); 444144194Snjl cvid = PN8_STA_CVID(status); 445144194Snjl break; 446144194Snjl } 447144194Snjl for (i = 0; i < sc->powernow_max_states; ++i) 448144194Snjl if (cfid == sc->powernow_states[i].fid && 449144194Snjl cvid == sc->powernow_states[i].vid) 450144194Snjl break; 451144194Snjl 452144194Snjl if (i < sc->powernow_max_states) { 453144194Snjl cf->freq = sc->powernow_states[i].freq / 1000; 454144194Snjl cf->power = sc->powernow_states[i].power; 455144194Snjl cf->lat = 200; 456144194Snjl cf->volts = sc->vid_to_volts[cvid]; 457144194Snjl cf->dev = dev; 458144194Snjl } else { 459144194Snjl memset(cf, CPUFREQ_VAL_UNKNOWN, sizeof(*cf)); 460144194Snjl cf->dev = NULL; 461144194Snjl } 462144194Snjl 463144194Snjl return (0); 464144194Snjl} 465144194Snjl 466144194Snjlstatic int 467144194Snjlpn_settings(device_t dev, struct cf_setting *sets, int *count) 468144194Snjl{ 469144194Snjl struct pn_softc *sc; 470144194Snjl int i; 471144194Snjl 472144194Snjl if (sets == NULL|| count == NULL) 473144194Snjl return (EINVAL); 474144194Snjl sc = device_get_softc(dev); 475144194Snjl if (*count < sc->powernow_max_states) 476144194Snjl return (E2BIG); 477144194Snjl for (i = 0; i < sc->powernow_max_states; ++i) { 478144194Snjl sets[i].freq = sc->powernow_states[i].freq / 1000; 479144194Snjl sets[i].power = sc->powernow_states[i].power; 480144194Snjl sets[i].lat = 200; 481144194Snjl sets[i].volts = sc->vid_to_volts[sc->powernow_states[i].vid]; 482144194Snjl sets[i].dev = dev; 483144194Snjl } 484144194Snjl *count = sc->powernow_max_states; 485144194Snjl 486144194Snjl return (0); 487144194Snjl} 488144194Snjl 489144194Snjlstatic int 490144194Snjlpn_type(device_t dev, int *type) 491144194Snjl{ 492144194Snjl if (type == NULL) 493144194Snjl return (EINVAL); 494144194Snjl 495144194Snjl *type = CPUFREQ_TYPE_ABSOLUTE; 496144194Snjl 497144194Snjl return (0); 498144194Snjl} 499144194Snjl 500144194Snjl/* 501144194Snjl * Given a set of pair of fid/vid, and number of performance states, 502144194Snjl * compute powernow_states via an insertion sort. 503144194Snjl */ 504144194Snjlstatic int 505144194Snjldecode_pst(struct pn_softc *sc, uint8_t *p, int npstates) 506144194Snjl{ 507144194Snjl int i, j, n; 508144194Snjl struct powernow_state state; 509144194Snjl 510144194Snjl for (i = 0; i < POWERNOW_MAX_STATES; ++i) 511144194Snjl sc->powernow_states[i].freq = CPUFREQ_VAL_UNKNOWN; 512144194Snjl 513144194Snjl for (n = 0, i = 0; i < npstates; ++i) { 514144194Snjl state.fid = *p++; 515144194Snjl state.vid = *p++; 516144194Snjl state.power = CPUFREQ_VAL_UNKNOWN; 517144194Snjl 518144194Snjl switch (sc->pn_type) { 519144194Snjl case PN7_TYPE: 520144194Snjl state.freq = 100 * pn7_fid_to_mult[state.fid] * sc->fsb; 521144194Snjl if (sc->errata_a0 && 522144194Snjl (pn7_fid_to_mult[state.fid] % 10) == 5) 523144194Snjl continue; 524144194Snjl break; 525144194Snjl case PN8_TYPE: 526144194Snjl state.freq = 100 * pn8_fid_to_mult[state.fid >> 1] * 527144194Snjl sc->fsb; 528144194Snjl break; 529144194Snjl } 530144194Snjl 531144194Snjl j = n; 532144194Snjl while (j > 0 && sc->powernow_states[j - 1].freq < state.freq) { 533144194Snjl memcpy(&sc->powernow_states[j], 534144194Snjl &sc->powernow_states[j - 1], 535144194Snjl sizeof(struct powernow_state)); 536144194Snjl --j; 537144194Snjl } 538144194Snjl memcpy(&sc->powernow_states[j], &state, 539144194Snjl sizeof(struct powernow_state)); 540144194Snjl ++n; 541144194Snjl } 542144194Snjl 543144194Snjl /* 544144194Snjl * Fix powernow_max_states, if errata_a0 give us less states 545144194Snjl * than expected. 546144194Snjl */ 547144194Snjl sc->powernow_max_states = n; 548144194Snjl 549144194Snjl if (bootverbose) 550144194Snjl for (i = 0; i < sc->powernow_max_states; ++i) { 551144194Snjl int fid = sc->powernow_states[i].fid; 552144194Snjl int vid = sc->powernow_states[i].vid; 553144194Snjl 554144194Snjl printf("powernow: %2i %8dkHz FID %02x VID %02x\n", 555144194Snjl i, 556144194Snjl sc->powernow_states[i].freq, 557144194Snjl fid, 558144194Snjl vid); 559144194Snjl } 560144194Snjl 561144194Snjl return (0); 562144194Snjl} 563144194Snjl 564144194Snjlstatic int 565144194Snjlcpuid_is_k7(u_int cpuid) 566144194Snjl{ 567144194Snjl 568144194Snjl switch (cpuid) { 569144194Snjl case 0x760: 570144194Snjl case 0x761: 571144194Snjl case 0x762: 572144194Snjl case 0x770: 573144194Snjl case 0x771: 574144194Snjl case 0x780: 575144194Snjl case 0x781: 576144194Snjl case 0x7a0: 577144194Snjl return (TRUE); 578144194Snjl } 579144194Snjl return (FALSE); 580144194Snjl} 581144194Snjl 582144194Snjlstatic int 583144194Snjlpn_decode_pst(device_t dev) 584144194Snjl{ 585144194Snjl int maxpst; 586144194Snjl struct pn_softc *sc; 587144194Snjl u_int cpuid, maxfid, startvid; 588144194Snjl u_long sig; 589144194Snjl struct psb_header *psb; 590144194Snjl uint8_t *p; 591144194Snjl u_int regs[4]; 592144194Snjl uint64_t status; 593144194Snjl 594144194Snjl sc = device_get_softc(dev); 595144194Snjl 596144194Snjl do_cpuid(0x80000001, regs); 597144194Snjl cpuid = regs[0]; 598144194Snjl 599144194Snjl if ((cpuid & 0xfff) == 0x760) 600144194Snjl sc->errata_a0 = TRUE; 601144194Snjl 602144194Snjl status = rdmsr(MSR_AMDK7_FIDVID_STATUS); 603144194Snjl 604144194Snjl switch (sc->pn_type) { 605144194Snjl case PN7_TYPE: 606144194Snjl maxfid = PN7_STA_MFID(status); 607144194Snjl startvid = PN7_STA_SVID(status); 608144194Snjl break; 609144194Snjl case PN8_TYPE: 610144194Snjl maxfid = PN8_STA_MFID(status); 611144194Snjl /* 612144194Snjl * we should actually use a variable named 'maxvid' if K8, 613144194Snjl * but why introducing a new variable for that? 614144194Snjl */ 615144194Snjl startvid = PN8_STA_MVID(status); 616144194Snjl break; 617144194Snjl default: 618144194Snjl return (ENODEV); 619144194Snjl } 620144194Snjl 621144194Snjl if (bootverbose) { 622144194Snjl device_printf(dev, "STATUS: 0x%jx\n", status); 623144194Snjl device_printf(dev, "STATUS: maxfid: 0x%02x\n", maxfid); 624144194Snjl device_printf(dev, "STATUS: %s: 0x%02x\n", 625144194Snjl sc->pn_type == PN7_TYPE ? "startvid" : "maxvid", 626144194Snjl startvid); 627144194Snjl } 628144194Snjl 629144194Snjl sig = bios_sigsearch(PSB_START, PSB_SIG, PSB_LEN, PSB_STEP, PSB_OFF); 630144194Snjl if (sig) { 631144194Snjl struct pst_header *pst; 632144194Snjl 633144194Snjl psb = (struct psb_header*)(uintptr_t)BIOS_PADDRTOVADDR(sig); 634144194Snjl 635144194Snjl switch (psb->version) { 636144194Snjl default: 637144194Snjl return (ENODEV); 638144194Snjl case 0x14: 639144380Snjl /* 640144380Snjl * We can't be picky about numpst since at least 641144380Snjl * some systems have a value of 1 and some have 2. 642144380Snjl * We trust that cpuid_is_k7() will be better at 643144380Snjl * catching that we're on a K8 anyway. 644144380Snjl */ 645144380Snjl if (sc->pn_type != PN8_TYPE) 646144194Snjl return (EINVAL); 647144194Snjl sc->vst = psb->settlingtime; 648144194Snjl sc->rvo = PN8_PSB_TO_RVO(psb->res1), 649144194Snjl sc->irt = PN8_PSB_TO_IRT(psb->res1), 650144194Snjl sc->mvs = PN8_PSB_TO_MVS(psb->res1), 651144194Snjl sc->low = PN8_PSB_TO_BATT(psb->res1); 652144194Snjl if (bootverbose) { 653144194Snjl device_printf(dev, "PSB: VST: %d\n", 654144194Snjl psb->settlingtime); 655144194Snjl device_printf(dev, "PSB: RVO %x IRT %d " 656144194Snjl "MVS %d BATT %d\n", 657144194Snjl sc->rvo, 658144194Snjl sc->irt, 659144194Snjl sc->mvs, 660144194Snjl sc->low); 661144194Snjl } 662144194Snjl break; 663144194Snjl case 0x12: 664144194Snjl if (sc->pn_type != PN7_TYPE) 665144194Snjl return (EINVAL); 666144194Snjl sc->sgtc = psb->settlingtime * sc->fsb; 667144194Snjl if (sc->sgtc < 100 * sc->fsb) 668144194Snjl sc->sgtc = 100 * sc->fsb; 669144194Snjl break; 670144194Snjl } 671144194Snjl 672144194Snjl p = ((uint8_t *) psb) + sizeof(struct psb_header); 673144194Snjl pst = (struct pst_header*) p; 674144194Snjl 675144194Snjl maxpst = 200; 676144194Snjl 677144194Snjl do { 678144194Snjl struct pst_header *pst = (struct pst_header*) p; 679144194Snjl 680144194Snjl if (cpuid == pst->cpuid && 681144194Snjl maxfid == pst->maxfid && 682144194Snjl startvid == pst->startvid) { 683144194Snjl sc->powernow_max_states = pst->numpstates; 684144194Snjl switch (sc->pn_type) { 685144194Snjl case PN7_TYPE: 686144194Snjl if (abs(sc->fsb - pst->fsb) > 5) 687144194Snjl continue; 688144194Snjl break; 689144194Snjl case PN8_TYPE: 690144194Snjl break; 691144194Snjl } 692144194Snjl return (decode_pst(sc, 693144194Snjl p + sizeof(struct pst_header), 694144194Snjl sc->powernow_max_states)); 695144194Snjl } 696144194Snjl 697144194Snjl p += sizeof(struct pst_header) + (2 * pst->numpstates); 698144194Snjl } while (cpuid_is_k7(pst->cpuid) && maxpst--); 699144194Snjl 700144194Snjl device_printf(dev, "no match for extended cpuid %.3x\n", cpuid); 701144194Snjl } 702144194Snjl 703144194Snjl return (ENODEV); 704144194Snjl} 705144194Snjl 706144194Snjl/* 707144194Snjl * TODO: this should be done in sys/ARCH/ARCH/identcpu.c 708144194Snjl */ 709144194Snjlstatic int 710144194Snjlcpu_is_powernow_capable(void) 711144194Snjl{ 712144194Snjl u_int regs[4]; 713144194Snjl 714144194Snjl if (strcmp(cpu_vendor, "AuthenticAMD") != 0 || 715144194Snjl cpu_exthigh < 0x80000007) 716144194Snjl return (FALSE); 717144194Snjl 718144194Snjl do_cpuid(0x80000007, regs); 719144194Snjl return (regs[3] & 0x6); 720144194Snjl} 721144194Snjl 722144194Snjlstatic int 723144194Snjlpn_decode_acpi(device_t dev, device_t perf_dev) 724144194Snjl{ 725144194Snjl int i, j, n; 726144194Snjl uint64_t status; 727144194Snjl uint32_t ctrl; 728144194Snjl u_int cpuid; 729144194Snjl u_int regs[4]; 730144194Snjl struct pn_softc *sc; 731144194Snjl struct powernow_state state; 732144194Snjl struct cf_setting sets[POWERNOW_MAX_STATES]; 733144194Snjl int count = POWERNOW_MAX_STATES; 734144194Snjl int type; 735144194Snjl int rv; 736144194Snjl 737144194Snjl if (perf_dev == NULL) 738144194Snjl return (ENXIO); 739144194Snjl 740144194Snjl rv = CPUFREQ_DRV_SETTINGS(perf_dev, sets, &count); 741144194Snjl if (rv) 742144194Snjl return (ENXIO); 743144194Snjl rv = CPUFREQ_DRV_TYPE(perf_dev, &type); 744144194Snjl if (rv || (type & CPUFREQ_FLAG_INFO_ONLY) == 0) 745144194Snjl return (ENXIO); 746144194Snjl 747144194Snjl sc = device_get_softc(dev); 748144194Snjl 749144194Snjl do_cpuid(0x80000001, regs); 750144194Snjl cpuid = regs[0]; 751144194Snjl if ((cpuid & 0xfff) == 0x760) 752144194Snjl sc->errata_a0 = TRUE; 753144194Snjl 754144194Snjl ctrl = 0; 755144194Snjl sc->sgtc = 0; 756144194Snjl for (n = 0, i = 0; i < count; ++i) { 757144194Snjl ctrl = sets[i].spec[PX_SPEC_CONTROL]; 758144194Snjl switch (sc->pn_type) { 759144194Snjl case PN7_TYPE: 760144194Snjl state.fid = ACPI_PN7_CTRL_TO_FID(ctrl); 761144194Snjl state.vid = ACPI_PN7_CTRL_TO_VID(ctrl); 762144194Snjl if (sc->errata_a0 && 763144194Snjl (pn7_fid_to_mult[state.fid] % 10) == 5) 764144194Snjl continue; 765144194Snjl state.freq = 100 * pn7_fid_to_mult[state.fid] * sc->fsb; 766144194Snjl break; 767144194Snjl case PN8_TYPE: 768144194Snjl state.fid = ACPI_PN8_CTRL_TO_FID(ctrl); 769144194Snjl state.vid = ACPI_PN8_CTRL_TO_VID(ctrl); 770144194Snjl state.freq = 100 * pn8_fid_to_mult[state.fid >> 1] * 771144194Snjl sc->fsb; 772144194Snjl break; 773144194Snjl } 774144194Snjl 775144194Snjl state.power = sets[i].power; 776144194Snjl 777144194Snjl j = n; 778144194Snjl while (j > 0 && sc->powernow_states[j - 1].freq < state.freq) { 779144194Snjl memcpy(&sc->powernow_states[j], 780144194Snjl &sc->powernow_states[j - 1], 781144194Snjl sizeof(struct powernow_state)); 782144194Snjl --j; 783144194Snjl } 784144194Snjl memcpy(&sc->powernow_states[j], &state, 785144194Snjl sizeof(struct powernow_state)); 786144194Snjl ++n; 787144194Snjl } 788144194Snjl 789144194Snjl sc->powernow_max_states = n; 790144194Snjl state = sc->powernow_states[0]; 791144194Snjl status = rdmsr(MSR_AMDK7_FIDVID_STATUS); 792144194Snjl 793144194Snjl switch (sc->pn_type) { 794144194Snjl case PN7_TYPE: 795144194Snjl sc->sgtc = ACPI_PN7_CTRL_TO_SGTC(ctrl); 796144194Snjl /* 797144194Snjl * XXX Some bios forget the max frequency! 798144194Snjl * This maybe indicates we have the wrong tables. Therefore, 799144194Snjl * don't implement a quirk, but fallback to BIOS legacy 800144194Snjl * tables instead. 801144194Snjl */ 802144194Snjl if (PN7_STA_MFID(status) != state.fid) { 803144194Snjl device_printf(dev, "ACPI MAX frequency not found\n"); 804144194Snjl return (EINVAL); 805144194Snjl } 806144194Snjl break; 807144194Snjl case PN8_TYPE: 808144194Snjl sc->vst = ACPI_PN8_CTRL_TO_VST(ctrl), 809144194Snjl sc->mvs = ACPI_PN8_CTRL_TO_MVS(ctrl), 810144194Snjl sc->pll = ACPI_PN8_CTRL_TO_PLL(ctrl), 811144194Snjl sc->rvo = ACPI_PN8_CTRL_TO_RVO(ctrl), 812144194Snjl sc->irt = ACPI_PN8_CTRL_TO_IRT(ctrl); 813144194Snjl sc->low = 0; /* XXX */ 814144194Snjl 815144194Snjl /* 816144194Snjl * powernow k8 supports only one low frequency. 817144194Snjl */ 818144194Snjl if (sc->powernow_max_states >= 2 && 819144194Snjl (sc->powernow_states[sc->powernow_max_states - 2].fid < 8)) 820144194Snjl return (EINVAL); 821144194Snjl break; 822144194Snjl } 823144194Snjl 824144194Snjl return (0); 825144194Snjl} 826144194Snjl 827144194Snjlstatic void 828144194Snjlpn_identify(driver_t *driver, device_t parent) 829144194Snjl{ 830144194Snjl device_t child; 831144194Snjl 832144194Snjl if (cpu_is_powernow_capable() == 0) 833144194Snjl return; 834144194Snjl switch (cpu_id & 0xf00) { 835144194Snjl case 0x600: 836144194Snjl case 0xf00: 837144194Snjl break; 838144194Snjl default: 839144194Snjl return; 840144194Snjl } 841144194Snjl if (device_find_child(parent, "powernow", -1) != NULL) 842144194Snjl return; 843144194Snjl if ((child = BUS_ADD_CHILD(parent, 0, "powernow", -1)) == NULL) 844144194Snjl device_printf(parent, "powernow: add child failed\n"); 845144194Snjl} 846144194Snjl 847144194Snjlstatic int 848144194Snjlpn_probe(device_t dev) 849144194Snjl{ 850144194Snjl struct pn_softc *sc; 851144194Snjl uint64_t status; 852144194Snjl uint64_t rate; 853144194Snjl struct pcpu *pc; 854144194Snjl u_int sfid, mfid, cfid; 855144194Snjl 856144194Snjl sc = device_get_softc(dev); 857144194Snjl sc->errata_a0 = FALSE; 858144194Snjl status = rdmsr(MSR_AMDK7_FIDVID_STATUS); 859144194Snjl 860144194Snjl pc = cpu_get_pcpu(dev); 861144194Snjl if (pc == NULL) 862144194Snjl return (ENODEV); 863144194Snjl 864144194Snjl cpu_est_clockrate(pc->pc_cpuid, &rate); 865144194Snjl 866144194Snjl switch (cpu_id & 0xf00) { 867144194Snjl case 0x600: 868144194Snjl sfid = PN7_STA_SFID(status); 869144194Snjl mfid = PN7_STA_MFID(status); 870144194Snjl cfid = PN7_STA_CFID(status); 871144194Snjl sc->pn_type = PN7_TYPE; 872144194Snjl sc->fsb = rate / 100000 / pn7_fid_to_mult[cfid]; 873144194Snjl 874144194Snjl /* 875144194Snjl * If start FID is different to max FID, then it is a 876144194Snjl * mobile processor. If not, it is a low powered desktop 877144194Snjl * processor. 878144194Snjl */ 879144194Snjl if (PN7_STA_SFID(status) != PN7_STA_MFID(status)) { 880144194Snjl sc->vid_to_volts = pn7_mobile_vid_to_volts; 881144194Snjl device_set_desc(dev, "PowerNow! K7"); 882144194Snjl } else { 883144194Snjl sc->vid_to_volts = pn7_desktop_vid_to_volts; 884144194Snjl device_set_desc(dev, "Cool`n'Quiet K7"); 885144194Snjl } 886144194Snjl break; 887144194Snjl 888144194Snjl case 0xf00: 889144194Snjl sfid = PN8_STA_SFID(status); 890144194Snjl mfid = PN8_STA_MFID(status); 891144194Snjl cfid = PN8_STA_CFID(status); 892144194Snjl sc->pn_type = PN8_TYPE; 893144194Snjl sc->vid_to_volts = pn8_vid_to_volts; 894144194Snjl sc->fsb = rate / 100000 / pn8_fid_to_mult[cfid >> 1]; 895144194Snjl 896144194Snjl if (PN8_STA_SFID(status) != PN8_STA_MFID(status)) 897144194Snjl device_set_desc(dev, "PowerNow! K8"); 898144194Snjl else 899144194Snjl device_set_desc(dev, "Cool`n'Quiet K8"); 900144194Snjl break; 901144194Snjl default: 902144194Snjl return (ENODEV); 903144194Snjl } 904144194Snjl 905144194Snjl return (0); 906144194Snjl} 907144194Snjl 908144194Snjlstatic int 909144194Snjlpn_attach(device_t dev) 910144194Snjl{ 911144194Snjl int rv; 912144194Snjl device_t child; 913144194Snjl 914144194Snjl child = device_find_child(device_get_parent(dev), "acpi_perf", -1); 915144194Snjl if (child) { 916144194Snjl rv = pn_decode_acpi(dev, child); 917144194Snjl if (rv) 918144194Snjl rv = pn_decode_pst(dev); 919144194Snjl } else 920144194Snjl rv = pn_decode_pst(dev); 921144194Snjl 922144194Snjl if (rv != 0) 923144194Snjl return (ENXIO); 924144194Snjl cpufreq_register(dev); 925144194Snjl return (0); 926144194Snjl} 927144194Snjl 928144194Snjlstatic int 929144194Snjlpn_detach(device_t dev) 930144194Snjl{ 931144194Snjl 932144194Snjl cpufreq_unregister(dev); 933144194Snjl return (0); 934144194Snjl} 935