132580301SAttilio Rao /*- 2*ebf5747bSPedro F. Giffuni * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3*ebf5747bSPedro F. Giffuni * 432580301SAttilio Rao * Copyright (c) 2004-2005 Bruno Ducrot 532580301SAttilio Rao * Copyright (c) 2004 FUKUDA Nobuhiko <nfukuda@spa.is.uec.ac.jp> 632580301SAttilio Rao * 732580301SAttilio Rao * Redistribution and use in source and binary forms, with or without 832580301SAttilio Rao * modification, are permitted provided that the following conditions 932580301SAttilio Rao * are met: 1032580301SAttilio Rao * 1. Redistributions of source code must retain the above copyright 1132580301SAttilio Rao * notice, this list of conditions and the following disclaimer. 1232580301SAttilio Rao * 2. Redistributions in binary form must reproduce the above copyright 1332580301SAttilio Rao * notice, this list of conditions and the following disclaimer in the 1432580301SAttilio Rao * documentation and/or other materials provided with the distribution. 1532580301SAttilio Rao * 1632580301SAttilio Rao * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1732580301SAttilio Rao * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1832580301SAttilio Rao * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 1932580301SAttilio Rao * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 2032580301SAttilio Rao * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2132580301SAttilio Rao * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2232580301SAttilio Rao * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2332580301SAttilio Rao * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2432580301SAttilio Rao * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2532580301SAttilio Rao * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2632580301SAttilio Rao */ 2732580301SAttilio Rao 2832580301SAttilio Rao /* 2932580301SAttilio Rao * Many thanks to Nate Lawson for his helpful comments on this driver and 3032580301SAttilio Rao * to Jung-uk Kim for testing. 3132580301SAttilio Rao */ 3232580301SAttilio Rao 3332580301SAttilio Rao #include <sys/cdefs.h> 3432580301SAttilio Rao __FBSDID("$FreeBSD$"); 3532580301SAttilio Rao 3632580301SAttilio Rao #include <sys/param.h> 3732580301SAttilio Rao #include <sys/bus.h> 3832580301SAttilio Rao #include <sys/cpu.h> 3932580301SAttilio Rao #include <sys/kernel.h> 4032580301SAttilio Rao #include <sys/malloc.h> 4132580301SAttilio Rao #include <sys/module.h> 4232580301SAttilio Rao #include <sys/pcpu.h> 4332580301SAttilio Rao #include <sys/systm.h> 4432580301SAttilio Rao 4532580301SAttilio Rao #include <machine/pc/bios.h> 4632580301SAttilio Rao #include <machine/md_var.h> 4732580301SAttilio Rao #include <machine/specialreg.h> 4832580301SAttilio Rao #include <machine/cputypes.h> 4932580301SAttilio Rao #include <machine/vmparam.h> 5032580301SAttilio Rao #include <sys/rman.h> 5132580301SAttilio Rao 5232580301SAttilio Rao #include <vm/vm.h> 5332580301SAttilio Rao #include <vm/pmap.h> 5432580301SAttilio Rao 5532580301SAttilio Rao #include "cpufreq_if.h" 5632580301SAttilio Rao 5732580301SAttilio Rao #define PN7_TYPE 0 5832580301SAttilio Rao #define PN8_TYPE 1 5932580301SAttilio Rao 6032580301SAttilio Rao /* Flags for some hardware bugs. */ 6132580301SAttilio Rao #define A0_ERRATA 0x1 /* Bugs for the rev. A0 of Athlon (K7): 6232580301SAttilio Rao * Interrupts must be disabled and no half 6332580301SAttilio Rao * multipliers are allowed */ 6432580301SAttilio Rao #define PENDING_STUCK 0x2 /* With some buggy chipset and some newer AMD64 6532580301SAttilio Rao * processor (Rev. G?): 6632580301SAttilio Rao * the pending bit from the msr FIDVID_STATUS 6732580301SAttilio Rao * is set forever. No workaround :( */ 6832580301SAttilio Rao 6932580301SAttilio Rao /* Legacy configuration via BIOS table PSB. */ 7032580301SAttilio Rao #define PSB_START 0 7132580301SAttilio Rao #define PSB_STEP 0x10 7232580301SAttilio Rao #define PSB_SIG "AMDK7PNOW!" 7332580301SAttilio Rao #define PSB_LEN 10 7432580301SAttilio Rao #define PSB_OFF 0 7532580301SAttilio Rao 7632580301SAttilio Rao struct psb_header { 7732580301SAttilio Rao char signature[10]; 7832580301SAttilio Rao uint8_t version; 7932580301SAttilio Rao uint8_t flags; 8032580301SAttilio Rao uint16_t settlingtime; 8132580301SAttilio Rao uint8_t res1; 8232580301SAttilio Rao uint8_t numpst; 8332580301SAttilio Rao } __packed; 8432580301SAttilio Rao 8532580301SAttilio Rao struct pst_header { 8632580301SAttilio Rao uint32_t cpuid; 8732580301SAttilio Rao uint8_t fsb; 8832580301SAttilio Rao uint8_t maxfid; 8932580301SAttilio Rao uint8_t startvid; 9032580301SAttilio Rao uint8_t numpstates; 9132580301SAttilio Rao } __packed; 9232580301SAttilio Rao 9332580301SAttilio Rao /* 9432580301SAttilio Rao * MSRs and bits used by Powernow technology 9532580301SAttilio Rao */ 9632580301SAttilio Rao #define MSR_AMDK7_FIDVID_CTL 0xc0010041 9732580301SAttilio Rao #define MSR_AMDK7_FIDVID_STATUS 0xc0010042 9832580301SAttilio Rao 9932580301SAttilio Rao /* Bitfields used by K7 */ 10032580301SAttilio Rao 10132580301SAttilio Rao #define PN7_CTR_FID(x) ((x) & 0x1f) 10232580301SAttilio Rao #define PN7_CTR_VID(x) (((x) & 0x1f) << 8) 10332580301SAttilio Rao #define PN7_CTR_FIDC 0x00010000 10432580301SAttilio Rao #define PN7_CTR_VIDC 0x00020000 10532580301SAttilio Rao #define PN7_CTR_FIDCHRATIO 0x00100000 10632580301SAttilio Rao #define PN7_CTR_SGTC(x) (((uint64_t)(x) & 0x000fffff) << 32) 10732580301SAttilio Rao 10832580301SAttilio Rao #define PN7_STA_CFID(x) ((x) & 0x1f) 10932580301SAttilio Rao #define PN7_STA_SFID(x) (((x) >> 8) & 0x1f) 11032580301SAttilio Rao #define PN7_STA_MFID(x) (((x) >> 16) & 0x1f) 11132580301SAttilio Rao #define PN7_STA_CVID(x) (((x) >> 32) & 0x1f) 11232580301SAttilio Rao #define PN7_STA_SVID(x) (((x) >> 40) & 0x1f) 11332580301SAttilio Rao #define PN7_STA_MVID(x) (((x) >> 48) & 0x1f) 11432580301SAttilio Rao 11532580301SAttilio Rao /* ACPI ctr_val status register to powernow k7 configuration */ 11632580301SAttilio Rao #define ACPI_PN7_CTRL_TO_FID(x) ((x) & 0x1f) 11732580301SAttilio Rao #define ACPI_PN7_CTRL_TO_VID(x) (((x) >> 5) & 0x1f) 11832580301SAttilio Rao #define ACPI_PN7_CTRL_TO_SGTC(x) (((x) >> 10) & 0xffff) 11932580301SAttilio Rao 12032580301SAttilio Rao /* Bitfields used by K8 */ 12132580301SAttilio Rao 12232580301SAttilio Rao #define PN8_CTR_FID(x) ((x) & 0x3f) 12332580301SAttilio Rao #define PN8_CTR_VID(x) (((x) & 0x1f) << 8) 12432580301SAttilio Rao #define PN8_CTR_PENDING(x) (((x) & 1) << 32) 12532580301SAttilio Rao 12632580301SAttilio Rao #define PN8_STA_CFID(x) ((x) & 0x3f) 12732580301SAttilio Rao #define PN8_STA_SFID(x) (((x) >> 8) & 0x3f) 12832580301SAttilio Rao #define PN8_STA_MFID(x) (((x) >> 16) & 0x3f) 12932580301SAttilio Rao #define PN8_STA_PENDING(x) (((x) >> 31) & 0x01) 13032580301SAttilio Rao #define PN8_STA_CVID(x) (((x) >> 32) & 0x1f) 13132580301SAttilio Rao #define PN8_STA_SVID(x) (((x) >> 40) & 0x1f) 13232580301SAttilio Rao #define PN8_STA_MVID(x) (((x) >> 48) & 0x1f) 13332580301SAttilio Rao 13432580301SAttilio Rao /* Reserved1 to powernow k8 configuration */ 13532580301SAttilio Rao #define PN8_PSB_TO_RVO(x) ((x) & 0x03) 13632580301SAttilio Rao #define PN8_PSB_TO_IRT(x) (((x) >> 2) & 0x03) 13732580301SAttilio Rao #define PN8_PSB_TO_MVS(x) (((x) >> 4) & 0x03) 13832580301SAttilio Rao #define PN8_PSB_TO_BATT(x) (((x) >> 6) & 0x03) 13932580301SAttilio Rao 14032580301SAttilio Rao /* ACPI ctr_val status register to powernow k8 configuration */ 14132580301SAttilio Rao #define ACPI_PN8_CTRL_TO_FID(x) ((x) & 0x3f) 14232580301SAttilio Rao #define ACPI_PN8_CTRL_TO_VID(x) (((x) >> 6) & 0x1f) 14332580301SAttilio Rao #define ACPI_PN8_CTRL_TO_VST(x) (((x) >> 11) & 0x1f) 14432580301SAttilio Rao #define ACPI_PN8_CTRL_TO_MVS(x) (((x) >> 18) & 0x03) 14532580301SAttilio Rao #define ACPI_PN8_CTRL_TO_PLL(x) (((x) >> 20) & 0x7f) 14632580301SAttilio Rao #define ACPI_PN8_CTRL_TO_RVO(x) (((x) >> 28) & 0x03) 14732580301SAttilio Rao #define ACPI_PN8_CTRL_TO_IRT(x) (((x) >> 30) & 0x03) 14832580301SAttilio Rao 14932580301SAttilio Rao 15032580301SAttilio Rao #define WRITE_FIDVID(fid, vid, ctrl) \ 15132580301SAttilio Rao wrmsr(MSR_AMDK7_FIDVID_CTL, \ 15232580301SAttilio Rao (((ctrl) << 32) | (1ULL << 16) | ((vid) << 8) | (fid))) 15332580301SAttilio Rao 15432580301SAttilio Rao #define COUNT_OFF_IRT(irt) DELAY(10 * (1 << (irt))) 15532580301SAttilio Rao #define COUNT_OFF_VST(vst) DELAY(20 * (vst)) 15632580301SAttilio Rao 15732580301SAttilio Rao #define FID_TO_VCO_FID(fid) \ 15832580301SAttilio Rao (((fid) < 8) ? (8 + ((fid) << 1)) : (fid)) 15932580301SAttilio Rao 16032580301SAttilio Rao /* 16132580301SAttilio Rao * Divide each value by 10 to get the processor multiplier. 16232580301SAttilio Rao * Some of those tables are the same as the Linux powernow-k7 16332580301SAttilio Rao * implementation by Dave Jones. 16432580301SAttilio Rao */ 16532580301SAttilio Rao static int pn7_fid_to_mult[32] = { 16632580301SAttilio Rao 110, 115, 120, 125, 50, 55, 60, 65, 16732580301SAttilio Rao 70, 75, 80, 85, 90, 95, 100, 105, 16832580301SAttilio Rao 30, 190, 40, 200, 130, 135, 140, 210, 16932580301SAttilio Rao 150, 225, 160, 165, 170, 180, 0, 0, 17032580301SAttilio Rao }; 17132580301SAttilio Rao 17232580301SAttilio Rao 17332580301SAttilio Rao static int pn8_fid_to_mult[64] = { 17432580301SAttilio Rao 40, 45, 50, 55, 60, 65, 70, 75, 17532580301SAttilio Rao 80, 85, 90, 95, 100, 105, 110, 115, 17632580301SAttilio Rao 120, 125, 130, 135, 140, 145, 150, 155, 17732580301SAttilio Rao 160, 165, 170, 175, 180, 185, 190, 195, 17832580301SAttilio Rao 200, 205, 210, 215, 220, 225, 230, 235, 17932580301SAttilio Rao 240, 245, 250, 255, 260, 265, 270, 275, 18032580301SAttilio Rao 280, 285, 290, 295, 300, 305, 310, 315, 18132580301SAttilio Rao 320, 325, 330, 335, 340, 345, 350, 355, 18232580301SAttilio Rao }; 18332580301SAttilio Rao 18432580301SAttilio Rao /* 18532580301SAttilio Rao * Units are in mV. 18632580301SAttilio Rao */ 18732580301SAttilio Rao /* Mobile VRM (K7) */ 18832580301SAttilio Rao static int pn7_mobile_vid_to_volts[] = { 18932580301SAttilio Rao 2000, 1950, 1900, 1850, 1800, 1750, 1700, 1650, 19032580301SAttilio Rao 1600, 1550, 1500, 1450, 1400, 1350, 1300, 0, 19132580301SAttilio Rao 1275, 1250, 1225, 1200, 1175, 1150, 1125, 1100, 19232580301SAttilio Rao 1075, 1050, 1025, 1000, 975, 950, 925, 0, 19332580301SAttilio Rao }; 19432580301SAttilio Rao /* Desktop VRM (K7) */ 19532580301SAttilio Rao static int pn7_desktop_vid_to_volts[] = { 19632580301SAttilio Rao 2000, 1950, 1900, 1850, 1800, 1750, 1700, 1650, 19732580301SAttilio Rao 1600, 1550, 1500, 1450, 1400, 1350, 1300, 0, 19832580301SAttilio Rao 1275, 1250, 1225, 1200, 1175, 1150, 1125, 1100, 19932580301SAttilio Rao 1075, 1050, 1025, 1000, 975, 950, 925, 0, 20032580301SAttilio Rao }; 20132580301SAttilio Rao /* Desktop and Mobile VRM (K8) */ 20232580301SAttilio Rao static int pn8_vid_to_volts[] = { 20332580301SAttilio Rao 1550, 1525, 1500, 1475, 1450, 1425, 1400, 1375, 20432580301SAttilio Rao 1350, 1325, 1300, 1275, 1250, 1225, 1200, 1175, 20532580301SAttilio Rao 1150, 1125, 1100, 1075, 1050, 1025, 1000, 975, 20632580301SAttilio Rao 950, 925, 900, 875, 850, 825, 800, 0, 20732580301SAttilio Rao }; 20832580301SAttilio Rao 20932580301SAttilio Rao #define POWERNOW_MAX_STATES 16 21032580301SAttilio Rao 21132580301SAttilio Rao struct powernow_state { 21232580301SAttilio Rao int freq; 21332580301SAttilio Rao int power; 21432580301SAttilio Rao int fid; 21532580301SAttilio Rao int vid; 21632580301SAttilio Rao }; 21732580301SAttilio Rao 21832580301SAttilio Rao struct pn_softc { 21932580301SAttilio Rao device_t dev; 22032580301SAttilio Rao int pn_type; 22132580301SAttilio Rao struct powernow_state powernow_states[POWERNOW_MAX_STATES]; 22232580301SAttilio Rao u_int fsb; 22332580301SAttilio Rao u_int sgtc; 22432580301SAttilio Rao u_int vst; 22532580301SAttilio Rao u_int mvs; 22632580301SAttilio Rao u_int pll; 22732580301SAttilio Rao u_int rvo; 22832580301SAttilio Rao u_int irt; 22932580301SAttilio Rao int low; 23032580301SAttilio Rao int powernow_max_states; 23132580301SAttilio Rao u_int powernow_state; 23232580301SAttilio Rao u_int errata; 23332580301SAttilio Rao int *vid_to_volts; 23432580301SAttilio Rao }; 23532580301SAttilio Rao 23632580301SAttilio Rao /* 23732580301SAttilio Rao * Offsets in struct cf_setting array for private values given by 23832580301SAttilio Rao * acpi_perf driver. 23932580301SAttilio Rao */ 24032580301SAttilio Rao #define PX_SPEC_CONTROL 0 24132580301SAttilio Rao #define PX_SPEC_STATUS 1 24232580301SAttilio Rao 24332580301SAttilio Rao static void pn_identify(driver_t *driver, device_t parent); 24432580301SAttilio Rao static int pn_probe(device_t dev); 24532580301SAttilio Rao static int pn_attach(device_t dev); 24632580301SAttilio Rao static int pn_detach(device_t dev); 24732580301SAttilio Rao static int pn_set(device_t dev, const struct cf_setting *cf); 24832580301SAttilio Rao static int pn_get(device_t dev, struct cf_setting *cf); 24932580301SAttilio Rao static int pn_settings(device_t dev, struct cf_setting *sets, 25032580301SAttilio Rao int *count); 25132580301SAttilio Rao static int pn_type(device_t dev, int *type); 25232580301SAttilio Rao 25332580301SAttilio Rao static device_method_t pn_methods[] = { 25432580301SAttilio Rao /* Device interface */ 25532580301SAttilio Rao DEVMETHOD(device_identify, pn_identify), 25632580301SAttilio Rao DEVMETHOD(device_probe, pn_probe), 25732580301SAttilio Rao DEVMETHOD(device_attach, pn_attach), 25832580301SAttilio Rao DEVMETHOD(device_detach, pn_detach), 25932580301SAttilio Rao 26032580301SAttilio Rao /* cpufreq interface */ 26132580301SAttilio Rao DEVMETHOD(cpufreq_drv_set, pn_set), 26232580301SAttilio Rao DEVMETHOD(cpufreq_drv_get, pn_get), 26332580301SAttilio Rao DEVMETHOD(cpufreq_drv_settings, pn_settings), 26432580301SAttilio Rao DEVMETHOD(cpufreq_drv_type, pn_type), 26532580301SAttilio Rao 26632580301SAttilio Rao {0, 0} 26732580301SAttilio Rao }; 26832580301SAttilio Rao 26932580301SAttilio Rao static devclass_t pn_devclass; 27032580301SAttilio Rao static driver_t pn_driver = { 27132580301SAttilio Rao "powernow", 27232580301SAttilio Rao pn_methods, 27332580301SAttilio Rao sizeof(struct pn_softc), 27432580301SAttilio Rao }; 27532580301SAttilio Rao 27632580301SAttilio Rao DRIVER_MODULE(powernow, cpu, pn_driver, pn_devclass, 0, 0); 27732580301SAttilio Rao 27832580301SAttilio Rao static int 27932580301SAttilio Rao pn7_setfidvid(struct pn_softc *sc, int fid, int vid) 28032580301SAttilio Rao { 28132580301SAttilio Rao int cfid, cvid; 28232580301SAttilio Rao uint64_t status, ctl; 28332580301SAttilio Rao 28432580301SAttilio Rao status = rdmsr(MSR_AMDK7_FIDVID_STATUS); 28532580301SAttilio Rao cfid = PN7_STA_CFID(status); 28632580301SAttilio Rao cvid = PN7_STA_CVID(status); 28732580301SAttilio Rao 28832580301SAttilio Rao /* We're already at the requested level. */ 28932580301SAttilio Rao if (fid == cfid && vid == cvid) 29032580301SAttilio Rao return (0); 29132580301SAttilio Rao 29232580301SAttilio Rao ctl = rdmsr(MSR_AMDK7_FIDVID_CTL) & PN7_CTR_FIDCHRATIO; 29332580301SAttilio Rao 29432580301SAttilio Rao ctl |= PN7_CTR_FID(fid); 29532580301SAttilio Rao ctl |= PN7_CTR_VID(vid); 29632580301SAttilio Rao ctl |= PN7_CTR_SGTC(sc->sgtc); 29732580301SAttilio Rao 29832580301SAttilio Rao if (sc->errata & A0_ERRATA) 29932580301SAttilio Rao disable_intr(); 30032580301SAttilio Rao 30132580301SAttilio Rao if (pn7_fid_to_mult[fid] < pn7_fid_to_mult[cfid]) { 30232580301SAttilio Rao wrmsr(MSR_AMDK7_FIDVID_CTL, ctl | PN7_CTR_FIDC); 30332580301SAttilio Rao if (vid != cvid) 30432580301SAttilio Rao wrmsr(MSR_AMDK7_FIDVID_CTL, ctl | PN7_CTR_VIDC); 30532580301SAttilio Rao } else { 30632580301SAttilio Rao wrmsr(MSR_AMDK7_FIDVID_CTL, ctl | PN7_CTR_VIDC); 30732580301SAttilio Rao if (fid != cfid) 30832580301SAttilio Rao wrmsr(MSR_AMDK7_FIDVID_CTL, ctl | PN7_CTR_FIDC); 30932580301SAttilio Rao } 31032580301SAttilio Rao 31132580301SAttilio Rao if (sc->errata & A0_ERRATA) 31232580301SAttilio Rao enable_intr(); 31332580301SAttilio Rao 31432580301SAttilio Rao return (0); 31532580301SAttilio Rao } 31632580301SAttilio Rao 31732580301SAttilio Rao static int 31832580301SAttilio Rao pn8_read_pending_wait(uint64_t *status) 31932580301SAttilio Rao { 32032580301SAttilio Rao int i = 10000; 32132580301SAttilio Rao 32232580301SAttilio Rao do 32332580301SAttilio Rao *status = rdmsr(MSR_AMDK7_FIDVID_STATUS); 32432580301SAttilio Rao while (PN8_STA_PENDING(*status) && --i); 32532580301SAttilio Rao 32632580301SAttilio Rao return (i == 0 ? ENXIO : 0); 32732580301SAttilio Rao } 32832580301SAttilio Rao 32932580301SAttilio Rao static int 33032580301SAttilio Rao pn8_write_fidvid(u_int fid, u_int vid, uint64_t ctrl, uint64_t *status) 33132580301SAttilio Rao { 33232580301SAttilio Rao int i = 100; 33332580301SAttilio Rao 33432580301SAttilio Rao do 33532580301SAttilio Rao WRITE_FIDVID(fid, vid, ctrl); 33632580301SAttilio Rao while (pn8_read_pending_wait(status) && --i); 33732580301SAttilio Rao 33832580301SAttilio Rao return (i == 0 ? ENXIO : 0); 33932580301SAttilio Rao } 34032580301SAttilio Rao 34132580301SAttilio Rao static int 34232580301SAttilio Rao pn8_setfidvid(struct pn_softc *sc, int fid, int vid) 34332580301SAttilio Rao { 34432580301SAttilio Rao uint64_t status; 34532580301SAttilio Rao int cfid, cvid; 34632580301SAttilio Rao int rvo; 34732580301SAttilio Rao int rv; 34832580301SAttilio Rao u_int val; 34932580301SAttilio Rao 35032580301SAttilio Rao rv = pn8_read_pending_wait(&status); 35132580301SAttilio Rao if (rv) 35232580301SAttilio Rao return (rv); 35332580301SAttilio Rao 35432580301SAttilio Rao cfid = PN8_STA_CFID(status); 35532580301SAttilio Rao cvid = PN8_STA_CVID(status); 35632580301SAttilio Rao 35732580301SAttilio Rao if (fid == cfid && vid == cvid) 35832580301SAttilio Rao return (0); 35932580301SAttilio Rao 36032580301SAttilio Rao /* 36132580301SAttilio Rao * Phase 1: Raise core voltage to requested VID if frequency is 36232580301SAttilio Rao * going up. 36332580301SAttilio Rao */ 36432580301SAttilio Rao while (cvid > vid) { 36532580301SAttilio Rao val = cvid - (1 << sc->mvs); 36632580301SAttilio Rao rv = pn8_write_fidvid(cfid, (val > 0) ? val : 0, 1ULL, &status); 36732580301SAttilio Rao if (rv) { 36832580301SAttilio Rao sc->errata |= PENDING_STUCK; 36932580301SAttilio Rao return (rv); 37032580301SAttilio Rao } 37132580301SAttilio Rao cvid = PN8_STA_CVID(status); 37232580301SAttilio Rao COUNT_OFF_VST(sc->vst); 37332580301SAttilio Rao } 37432580301SAttilio Rao 37532580301SAttilio Rao /* ... then raise to voltage + RVO (if required) */ 37632580301SAttilio Rao for (rvo = sc->rvo; rvo > 0 && cvid > 0; --rvo) { 37732580301SAttilio Rao /* XXX It's not clear from spec if we have to do that 37832580301SAttilio Rao * in 0.25 step or in MVS. Therefore do it as it's done 37932580301SAttilio Rao * under Linux */ 38032580301SAttilio Rao rv = pn8_write_fidvid(cfid, cvid - 1, 1ULL, &status); 38132580301SAttilio Rao if (rv) { 38232580301SAttilio Rao sc->errata |= PENDING_STUCK; 38332580301SAttilio Rao return (rv); 38432580301SAttilio Rao } 38532580301SAttilio Rao cvid = PN8_STA_CVID(status); 38632580301SAttilio Rao COUNT_OFF_VST(sc->vst); 38732580301SAttilio Rao } 38832580301SAttilio Rao 38932580301SAttilio Rao /* Phase 2: change to requested core frequency */ 39032580301SAttilio Rao if (cfid != fid) { 39132580301SAttilio Rao u_int vco_fid, vco_cfid, fid_delta; 39232580301SAttilio Rao 39332580301SAttilio Rao vco_fid = FID_TO_VCO_FID(fid); 39432580301SAttilio Rao vco_cfid = FID_TO_VCO_FID(cfid); 39532580301SAttilio Rao 39632580301SAttilio Rao while (abs(vco_fid - vco_cfid) > 2) { 39732580301SAttilio Rao fid_delta = (vco_cfid & 1) ? 1 : 2; 39832580301SAttilio Rao if (fid > cfid) { 39932580301SAttilio Rao if (cfid > 7) 40032580301SAttilio Rao val = cfid + fid_delta; 40132580301SAttilio Rao else 40232580301SAttilio Rao val = FID_TO_VCO_FID(cfid) + fid_delta; 40332580301SAttilio Rao } else 40432580301SAttilio Rao val = cfid - fid_delta; 40532580301SAttilio Rao rv = pn8_write_fidvid(val, cvid, 40632580301SAttilio Rao sc->pll * (uint64_t) sc->fsb, 40732580301SAttilio Rao &status); 40832580301SAttilio Rao if (rv) { 40932580301SAttilio Rao sc->errata |= PENDING_STUCK; 41032580301SAttilio Rao return (rv); 41132580301SAttilio Rao } 41232580301SAttilio Rao cfid = PN8_STA_CFID(status); 41332580301SAttilio Rao COUNT_OFF_IRT(sc->irt); 41432580301SAttilio Rao 41532580301SAttilio Rao vco_cfid = FID_TO_VCO_FID(cfid); 41632580301SAttilio Rao } 41732580301SAttilio Rao 41832580301SAttilio Rao rv = pn8_write_fidvid(fid, cvid, 41932580301SAttilio Rao sc->pll * (uint64_t) sc->fsb, 42032580301SAttilio Rao &status); 42132580301SAttilio Rao if (rv) { 42232580301SAttilio Rao sc->errata |= PENDING_STUCK; 42332580301SAttilio Rao return (rv); 42432580301SAttilio Rao } 42532580301SAttilio Rao cfid = PN8_STA_CFID(status); 42632580301SAttilio Rao COUNT_OFF_IRT(sc->irt); 42732580301SAttilio Rao } 42832580301SAttilio Rao 42932580301SAttilio Rao /* Phase 3: change to requested voltage */ 43032580301SAttilio Rao if (cvid != vid) { 43132580301SAttilio Rao rv = pn8_write_fidvid(cfid, vid, 1ULL, &status); 43232580301SAttilio Rao cvid = PN8_STA_CVID(status); 43332580301SAttilio Rao COUNT_OFF_VST(sc->vst); 43432580301SAttilio Rao } 43532580301SAttilio Rao 43632580301SAttilio Rao /* Check if transition failed. */ 43732580301SAttilio Rao if (cfid != fid || cvid != vid) 43832580301SAttilio Rao rv = ENXIO; 43932580301SAttilio Rao 44032580301SAttilio Rao return (rv); 44132580301SAttilio Rao } 44232580301SAttilio Rao 44332580301SAttilio Rao static int 44432580301SAttilio Rao pn_set(device_t dev, const struct cf_setting *cf) 44532580301SAttilio Rao { 44632580301SAttilio Rao struct pn_softc *sc; 44732580301SAttilio Rao int fid, vid; 44832580301SAttilio Rao int i; 44932580301SAttilio Rao int rv; 45032580301SAttilio Rao 45132580301SAttilio Rao if (cf == NULL) 45232580301SAttilio Rao return (EINVAL); 45332580301SAttilio Rao sc = device_get_softc(dev); 45432580301SAttilio Rao 45532580301SAttilio Rao if (sc->errata & PENDING_STUCK) 45632580301SAttilio Rao return (ENXIO); 45732580301SAttilio Rao 45832580301SAttilio Rao for (i = 0; i < sc->powernow_max_states; ++i) 45932580301SAttilio Rao if (CPUFREQ_CMP(sc->powernow_states[i].freq / 1000, cf->freq)) 46032580301SAttilio Rao break; 46132580301SAttilio Rao 46232580301SAttilio Rao fid = sc->powernow_states[i].fid; 46332580301SAttilio Rao vid = sc->powernow_states[i].vid; 46432580301SAttilio Rao 46532580301SAttilio Rao rv = ENODEV; 46632580301SAttilio Rao 46732580301SAttilio Rao switch (sc->pn_type) { 46832580301SAttilio Rao case PN7_TYPE: 46932580301SAttilio Rao rv = pn7_setfidvid(sc, fid, vid); 47032580301SAttilio Rao break; 47132580301SAttilio Rao case PN8_TYPE: 47232580301SAttilio Rao rv = pn8_setfidvid(sc, fid, vid); 47332580301SAttilio Rao break; 47432580301SAttilio Rao } 47532580301SAttilio Rao 47632580301SAttilio Rao return (rv); 47732580301SAttilio Rao } 47832580301SAttilio Rao 47932580301SAttilio Rao static int 48032580301SAttilio Rao pn_get(device_t dev, struct cf_setting *cf) 48132580301SAttilio Rao { 48232580301SAttilio Rao struct pn_softc *sc; 48332580301SAttilio Rao u_int cfid = 0, cvid = 0; 48432580301SAttilio Rao int i; 48532580301SAttilio Rao uint64_t status; 48632580301SAttilio Rao 48732580301SAttilio Rao if (cf == NULL) 48832580301SAttilio Rao return (EINVAL); 48932580301SAttilio Rao sc = device_get_softc(dev); 49032580301SAttilio Rao if (sc->errata & PENDING_STUCK) 49132580301SAttilio Rao return (ENXIO); 49232580301SAttilio Rao 49332580301SAttilio Rao status = rdmsr(MSR_AMDK7_FIDVID_STATUS); 49432580301SAttilio Rao 49532580301SAttilio Rao switch (sc->pn_type) { 49632580301SAttilio Rao case PN7_TYPE: 49732580301SAttilio Rao cfid = PN7_STA_CFID(status); 49832580301SAttilio Rao cvid = PN7_STA_CVID(status); 49932580301SAttilio Rao break; 50032580301SAttilio Rao case PN8_TYPE: 50132580301SAttilio Rao cfid = PN8_STA_CFID(status); 50232580301SAttilio Rao cvid = PN8_STA_CVID(status); 50332580301SAttilio Rao break; 50432580301SAttilio Rao } 50532580301SAttilio Rao for (i = 0; i < sc->powernow_max_states; ++i) 50632580301SAttilio Rao if (cfid == sc->powernow_states[i].fid && 50732580301SAttilio Rao cvid == sc->powernow_states[i].vid) 50832580301SAttilio Rao break; 50932580301SAttilio Rao 51032580301SAttilio Rao if (i < sc->powernow_max_states) { 51132580301SAttilio Rao cf->freq = sc->powernow_states[i].freq / 1000; 51232580301SAttilio Rao cf->power = sc->powernow_states[i].power; 51332580301SAttilio Rao cf->lat = 200; 51432580301SAttilio Rao cf->volts = sc->vid_to_volts[cvid]; 51532580301SAttilio Rao cf->dev = dev; 51632580301SAttilio Rao } else { 51732580301SAttilio Rao memset(cf, CPUFREQ_VAL_UNKNOWN, sizeof(*cf)); 51832580301SAttilio Rao cf->dev = NULL; 51932580301SAttilio Rao } 52032580301SAttilio Rao 52132580301SAttilio Rao return (0); 52232580301SAttilio Rao } 52332580301SAttilio Rao 52432580301SAttilio Rao static int 52532580301SAttilio Rao pn_settings(device_t dev, struct cf_setting *sets, int *count) 52632580301SAttilio Rao { 52732580301SAttilio Rao struct pn_softc *sc; 52832580301SAttilio Rao int i; 52932580301SAttilio Rao 53032580301SAttilio Rao if (sets == NULL|| count == NULL) 53132580301SAttilio Rao return (EINVAL); 53232580301SAttilio Rao sc = device_get_softc(dev); 53332580301SAttilio Rao if (*count < sc->powernow_max_states) 53432580301SAttilio Rao return (E2BIG); 53532580301SAttilio Rao for (i = 0; i < sc->powernow_max_states; ++i) { 53632580301SAttilio Rao sets[i].freq = sc->powernow_states[i].freq / 1000; 53732580301SAttilio Rao sets[i].power = sc->powernow_states[i].power; 53832580301SAttilio Rao sets[i].lat = 200; 53932580301SAttilio Rao sets[i].volts = sc->vid_to_volts[sc->powernow_states[i].vid]; 54032580301SAttilio Rao sets[i].dev = dev; 54132580301SAttilio Rao } 54232580301SAttilio Rao *count = sc->powernow_max_states; 54332580301SAttilio Rao 54432580301SAttilio Rao return (0); 54532580301SAttilio Rao } 54632580301SAttilio Rao 54732580301SAttilio Rao static int 54832580301SAttilio Rao pn_type(device_t dev, int *type) 54932580301SAttilio Rao { 55032580301SAttilio Rao if (type == NULL) 55132580301SAttilio Rao return (EINVAL); 55232580301SAttilio Rao 55332580301SAttilio Rao *type = CPUFREQ_TYPE_ABSOLUTE; 55432580301SAttilio Rao 55532580301SAttilio Rao return (0); 55632580301SAttilio Rao } 55732580301SAttilio Rao 55832580301SAttilio Rao /* 55932580301SAttilio Rao * Given a set of pair of fid/vid, and number of performance states, 56032580301SAttilio Rao * compute powernow_states via an insertion sort. 56132580301SAttilio Rao */ 56232580301SAttilio Rao static int 56332580301SAttilio Rao decode_pst(struct pn_softc *sc, uint8_t *p, int npstates) 56432580301SAttilio Rao { 56532580301SAttilio Rao int i, j, n; 56632580301SAttilio Rao struct powernow_state state; 56732580301SAttilio Rao 56832580301SAttilio Rao for (i = 0; i < POWERNOW_MAX_STATES; ++i) 56932580301SAttilio Rao sc->powernow_states[i].freq = CPUFREQ_VAL_UNKNOWN; 57032580301SAttilio Rao 57132580301SAttilio Rao for (n = 0, i = 0; i < npstates; ++i) { 57232580301SAttilio Rao state.fid = *p++; 57332580301SAttilio Rao state.vid = *p++; 57432580301SAttilio Rao state.power = CPUFREQ_VAL_UNKNOWN; 57532580301SAttilio Rao 57632580301SAttilio Rao switch (sc->pn_type) { 57732580301SAttilio Rao case PN7_TYPE: 57832580301SAttilio Rao state.freq = 100 * pn7_fid_to_mult[state.fid] * sc->fsb; 57932580301SAttilio Rao if ((sc->errata & A0_ERRATA) && 58032580301SAttilio Rao (pn7_fid_to_mult[state.fid] % 10) == 5) 58132580301SAttilio Rao continue; 58232580301SAttilio Rao break; 58332580301SAttilio Rao case PN8_TYPE: 58432580301SAttilio Rao state.freq = 100 * pn8_fid_to_mult[state.fid] * sc->fsb; 58532580301SAttilio Rao break; 58632580301SAttilio Rao } 58732580301SAttilio Rao 58832580301SAttilio Rao j = n; 58932580301SAttilio Rao while (j > 0 && sc->powernow_states[j - 1].freq < state.freq) { 59032580301SAttilio Rao memcpy(&sc->powernow_states[j], 59132580301SAttilio Rao &sc->powernow_states[j - 1], 59232580301SAttilio Rao sizeof(struct powernow_state)); 59332580301SAttilio Rao --j; 59432580301SAttilio Rao } 59532580301SAttilio Rao memcpy(&sc->powernow_states[j], &state, 59632580301SAttilio Rao sizeof(struct powernow_state)); 59732580301SAttilio Rao ++n; 59832580301SAttilio Rao } 59932580301SAttilio Rao 60032580301SAttilio Rao /* 60132580301SAttilio Rao * Fix powernow_max_states, if errata a0 give us less states 60232580301SAttilio Rao * than expected. 60332580301SAttilio Rao */ 60432580301SAttilio Rao sc->powernow_max_states = n; 60532580301SAttilio Rao 60632580301SAttilio Rao if (bootverbose) 60732580301SAttilio Rao for (i = 0; i < sc->powernow_max_states; ++i) { 60832580301SAttilio Rao int fid = sc->powernow_states[i].fid; 60932580301SAttilio Rao int vid = sc->powernow_states[i].vid; 61032580301SAttilio Rao 61132580301SAttilio Rao printf("powernow: %2i %8dkHz FID %02x VID %02x\n", 61232580301SAttilio Rao i, 61332580301SAttilio Rao sc->powernow_states[i].freq, 61432580301SAttilio Rao fid, 61532580301SAttilio Rao vid); 61632580301SAttilio Rao } 61732580301SAttilio Rao 61832580301SAttilio Rao return (0); 61932580301SAttilio Rao } 62032580301SAttilio Rao 62132580301SAttilio Rao static int 62232580301SAttilio Rao cpuid_is_k7(u_int cpuid) 62332580301SAttilio Rao { 62432580301SAttilio Rao 62532580301SAttilio Rao switch (cpuid) { 62632580301SAttilio Rao case 0x760: 62732580301SAttilio Rao case 0x761: 62832580301SAttilio Rao case 0x762: 62932580301SAttilio Rao case 0x770: 63032580301SAttilio Rao case 0x771: 63132580301SAttilio Rao case 0x780: 63232580301SAttilio Rao case 0x781: 63332580301SAttilio Rao case 0x7a0: 63432580301SAttilio Rao return (TRUE); 63532580301SAttilio Rao } 63632580301SAttilio Rao return (FALSE); 63732580301SAttilio Rao } 63832580301SAttilio Rao 63932580301SAttilio Rao static int 64032580301SAttilio Rao pn_decode_pst(device_t dev) 64132580301SAttilio Rao { 64232580301SAttilio Rao int maxpst; 64332580301SAttilio Rao struct pn_softc *sc; 64432580301SAttilio Rao u_int cpuid, maxfid, startvid; 64532580301SAttilio Rao u_long sig; 64632580301SAttilio Rao struct psb_header *psb; 64732580301SAttilio Rao uint8_t *p; 64832580301SAttilio Rao u_int regs[4]; 64932580301SAttilio Rao uint64_t status; 65032580301SAttilio Rao 65132580301SAttilio Rao sc = device_get_softc(dev); 65232580301SAttilio Rao 65332580301SAttilio Rao do_cpuid(0x80000001, regs); 65432580301SAttilio Rao cpuid = regs[0]; 65532580301SAttilio Rao 65632580301SAttilio Rao if ((cpuid & 0xfff) == 0x760) 65732580301SAttilio Rao sc->errata |= A0_ERRATA; 65832580301SAttilio Rao 65932580301SAttilio Rao status = rdmsr(MSR_AMDK7_FIDVID_STATUS); 66032580301SAttilio Rao 66132580301SAttilio Rao switch (sc->pn_type) { 66232580301SAttilio Rao case PN7_TYPE: 66332580301SAttilio Rao maxfid = PN7_STA_MFID(status); 66432580301SAttilio Rao startvid = PN7_STA_SVID(status); 66532580301SAttilio Rao break; 66632580301SAttilio Rao case PN8_TYPE: 66732580301SAttilio Rao maxfid = PN8_STA_MFID(status); 66832580301SAttilio Rao /* 66932580301SAttilio Rao * we should actually use a variable named 'maxvid' if K8, 67032580301SAttilio Rao * but why introducing a new variable for that? 67132580301SAttilio Rao */ 67232580301SAttilio Rao startvid = PN8_STA_MVID(status); 67332580301SAttilio Rao break; 67432580301SAttilio Rao default: 67532580301SAttilio Rao return (ENODEV); 67632580301SAttilio Rao } 67732580301SAttilio Rao 67832580301SAttilio Rao if (bootverbose) { 67932580301SAttilio Rao device_printf(dev, "STATUS: 0x%jx\n", status); 68032580301SAttilio Rao device_printf(dev, "STATUS: maxfid: 0x%02x\n", maxfid); 68132580301SAttilio Rao device_printf(dev, "STATUS: %s: 0x%02x\n", 68232580301SAttilio Rao sc->pn_type == PN7_TYPE ? "startvid" : "maxvid", 68332580301SAttilio Rao startvid); 68432580301SAttilio Rao } 68532580301SAttilio Rao 68632580301SAttilio Rao sig = bios_sigsearch(PSB_START, PSB_SIG, PSB_LEN, PSB_STEP, PSB_OFF); 68732580301SAttilio Rao if (sig) { 68832580301SAttilio Rao struct pst_header *pst; 68932580301SAttilio Rao 69032580301SAttilio Rao psb = (struct psb_header*)(uintptr_t)BIOS_PADDRTOVADDR(sig); 69132580301SAttilio Rao 69232580301SAttilio Rao switch (psb->version) { 69332580301SAttilio Rao default: 69432580301SAttilio Rao return (ENODEV); 69532580301SAttilio Rao case 0x14: 69632580301SAttilio Rao /* 69732580301SAttilio Rao * We can't be picky about numpst since at least 69832580301SAttilio Rao * some systems have a value of 1 and some have 2. 69932580301SAttilio Rao * We trust that cpuid_is_k7() will be better at 70032580301SAttilio Rao * catching that we're on a K8 anyway. 70132580301SAttilio Rao */ 70232580301SAttilio Rao if (sc->pn_type != PN8_TYPE) 70332580301SAttilio Rao return (EINVAL); 70432580301SAttilio Rao sc->vst = psb->settlingtime; 705a061aa46SPedro F. Giffuni sc->rvo = PN8_PSB_TO_RVO(psb->res1); 706a061aa46SPedro F. Giffuni sc->irt = PN8_PSB_TO_IRT(psb->res1); 707a061aa46SPedro F. Giffuni sc->mvs = PN8_PSB_TO_MVS(psb->res1); 70832580301SAttilio Rao sc->low = PN8_PSB_TO_BATT(psb->res1); 70932580301SAttilio Rao if (bootverbose) { 71032580301SAttilio Rao device_printf(dev, "PSB: VST: %d\n", 71132580301SAttilio Rao psb->settlingtime); 71232580301SAttilio Rao device_printf(dev, "PSB: RVO %x IRT %d " 71332580301SAttilio Rao "MVS %d BATT %d\n", 71432580301SAttilio Rao sc->rvo, 71532580301SAttilio Rao sc->irt, 71632580301SAttilio Rao sc->mvs, 71732580301SAttilio Rao sc->low); 71832580301SAttilio Rao } 71932580301SAttilio Rao break; 72032580301SAttilio Rao case 0x12: 72132580301SAttilio Rao if (sc->pn_type != PN7_TYPE) 72232580301SAttilio Rao return (EINVAL); 72332580301SAttilio Rao sc->sgtc = psb->settlingtime * sc->fsb; 72432580301SAttilio Rao if (sc->sgtc < 100 * sc->fsb) 72532580301SAttilio Rao sc->sgtc = 100 * sc->fsb; 72632580301SAttilio Rao break; 72732580301SAttilio Rao } 72832580301SAttilio Rao 72932580301SAttilio Rao p = ((uint8_t *) psb) + sizeof(struct psb_header); 73032580301SAttilio Rao pst = (struct pst_header*) p; 73132580301SAttilio Rao 73232580301SAttilio Rao maxpst = 200; 73332580301SAttilio Rao 73432580301SAttilio Rao do { 73532580301SAttilio Rao struct pst_header *pst = (struct pst_header*) p; 73632580301SAttilio Rao 73732580301SAttilio Rao if (cpuid == pst->cpuid && 73832580301SAttilio Rao maxfid == pst->maxfid && 73932580301SAttilio Rao startvid == pst->startvid) { 74032580301SAttilio Rao sc->powernow_max_states = pst->numpstates; 74132580301SAttilio Rao switch (sc->pn_type) { 74232580301SAttilio Rao case PN7_TYPE: 74332580301SAttilio Rao if (abs(sc->fsb - pst->fsb) > 5) 74432580301SAttilio Rao continue; 74532580301SAttilio Rao break; 74632580301SAttilio Rao case PN8_TYPE: 74732580301SAttilio Rao break; 74832580301SAttilio Rao } 74932580301SAttilio Rao return (decode_pst(sc, 75032580301SAttilio Rao p + sizeof(struct pst_header), 75132580301SAttilio Rao sc->powernow_max_states)); 75232580301SAttilio Rao } 75332580301SAttilio Rao 75432580301SAttilio Rao p += sizeof(struct pst_header) + (2 * pst->numpstates); 75532580301SAttilio Rao } while (cpuid_is_k7(pst->cpuid) && maxpst--); 75632580301SAttilio Rao 75732580301SAttilio Rao device_printf(dev, "no match for extended cpuid %.3x\n", cpuid); 75832580301SAttilio Rao } 75932580301SAttilio Rao 76032580301SAttilio Rao return (ENODEV); 76132580301SAttilio Rao } 76232580301SAttilio Rao 76332580301SAttilio Rao static int 76432580301SAttilio Rao pn_decode_acpi(device_t dev, device_t perf_dev) 76532580301SAttilio Rao { 76632580301SAttilio Rao int i, j, n; 76732580301SAttilio Rao uint64_t status; 76832580301SAttilio Rao uint32_t ctrl; 76932580301SAttilio Rao u_int cpuid; 77032580301SAttilio Rao u_int regs[4]; 77132580301SAttilio Rao struct pn_softc *sc; 77232580301SAttilio Rao struct powernow_state state; 77332580301SAttilio Rao struct cf_setting sets[POWERNOW_MAX_STATES]; 77432580301SAttilio Rao int count = POWERNOW_MAX_STATES; 77532580301SAttilio Rao int type; 77632580301SAttilio Rao int rv; 77732580301SAttilio Rao 77832580301SAttilio Rao if (perf_dev == NULL) 77932580301SAttilio Rao return (ENXIO); 78032580301SAttilio Rao 78132580301SAttilio Rao rv = CPUFREQ_DRV_SETTINGS(perf_dev, sets, &count); 78232580301SAttilio Rao if (rv) 78332580301SAttilio Rao return (ENXIO); 78432580301SAttilio Rao rv = CPUFREQ_DRV_TYPE(perf_dev, &type); 78532580301SAttilio Rao if (rv || (type & CPUFREQ_FLAG_INFO_ONLY) == 0) 78632580301SAttilio Rao return (ENXIO); 78732580301SAttilio Rao 78832580301SAttilio Rao sc = device_get_softc(dev); 78932580301SAttilio Rao 79032580301SAttilio Rao do_cpuid(0x80000001, regs); 79132580301SAttilio Rao cpuid = regs[0]; 79232580301SAttilio Rao if ((cpuid & 0xfff) == 0x760) 79332580301SAttilio Rao sc->errata |= A0_ERRATA; 79432580301SAttilio Rao 79532580301SAttilio Rao ctrl = 0; 79632580301SAttilio Rao sc->sgtc = 0; 79732580301SAttilio Rao for (n = 0, i = 0; i < count; ++i) { 79832580301SAttilio Rao ctrl = sets[i].spec[PX_SPEC_CONTROL]; 79932580301SAttilio Rao switch (sc->pn_type) { 80032580301SAttilio Rao case PN7_TYPE: 80132580301SAttilio Rao state.fid = ACPI_PN7_CTRL_TO_FID(ctrl); 80232580301SAttilio Rao state.vid = ACPI_PN7_CTRL_TO_VID(ctrl); 80332580301SAttilio Rao if ((sc->errata & A0_ERRATA) && 80432580301SAttilio Rao (pn7_fid_to_mult[state.fid] % 10) == 5) 80532580301SAttilio Rao continue; 80632580301SAttilio Rao break; 80732580301SAttilio Rao case PN8_TYPE: 80832580301SAttilio Rao state.fid = ACPI_PN8_CTRL_TO_FID(ctrl); 80932580301SAttilio Rao state.vid = ACPI_PN8_CTRL_TO_VID(ctrl); 81032580301SAttilio Rao break; 81132580301SAttilio Rao } 81243d645f9SJung-uk Kim state.freq = sets[i].freq * 1000; 81332580301SAttilio Rao state.power = sets[i].power; 81432580301SAttilio Rao 81532580301SAttilio Rao j = n; 81632580301SAttilio Rao while (j > 0 && sc->powernow_states[j - 1].freq < state.freq) { 81732580301SAttilio Rao memcpy(&sc->powernow_states[j], 81832580301SAttilio Rao &sc->powernow_states[j - 1], 81932580301SAttilio Rao sizeof(struct powernow_state)); 82032580301SAttilio Rao --j; 82132580301SAttilio Rao } 82232580301SAttilio Rao memcpy(&sc->powernow_states[j], &state, 82332580301SAttilio Rao sizeof(struct powernow_state)); 82432580301SAttilio Rao ++n; 82532580301SAttilio Rao } 82632580301SAttilio Rao 82732580301SAttilio Rao sc->powernow_max_states = n; 82832580301SAttilio Rao state = sc->powernow_states[0]; 82932580301SAttilio Rao status = rdmsr(MSR_AMDK7_FIDVID_STATUS); 83032580301SAttilio Rao 83132580301SAttilio Rao switch (sc->pn_type) { 83232580301SAttilio Rao case PN7_TYPE: 83332580301SAttilio Rao sc->sgtc = ACPI_PN7_CTRL_TO_SGTC(ctrl); 83432580301SAttilio Rao /* 83532580301SAttilio Rao * XXX Some bios forget the max frequency! 83632580301SAttilio Rao * This maybe indicates we have the wrong tables. Therefore, 83732580301SAttilio Rao * don't implement a quirk, but fallback to BIOS legacy 83832580301SAttilio Rao * tables instead. 83932580301SAttilio Rao */ 84032580301SAttilio Rao if (PN7_STA_MFID(status) != state.fid) { 84132580301SAttilio Rao device_printf(dev, "ACPI MAX frequency not found\n"); 84232580301SAttilio Rao return (EINVAL); 84332580301SAttilio Rao } 84443d645f9SJung-uk Kim sc->fsb = state.freq / 100 / pn7_fid_to_mult[state.fid]; 84532580301SAttilio Rao break; 84632580301SAttilio Rao case PN8_TYPE: 84732580301SAttilio Rao sc->vst = ACPI_PN8_CTRL_TO_VST(ctrl), 84832580301SAttilio Rao sc->mvs = ACPI_PN8_CTRL_TO_MVS(ctrl), 84932580301SAttilio Rao sc->pll = ACPI_PN8_CTRL_TO_PLL(ctrl), 85032580301SAttilio Rao sc->rvo = ACPI_PN8_CTRL_TO_RVO(ctrl), 85132580301SAttilio Rao sc->irt = ACPI_PN8_CTRL_TO_IRT(ctrl); 85232580301SAttilio Rao sc->low = 0; /* XXX */ 85332580301SAttilio Rao 85432580301SAttilio Rao /* 85532580301SAttilio Rao * powernow k8 supports only one low frequency. 85632580301SAttilio Rao */ 85732580301SAttilio Rao if (sc->powernow_max_states >= 2 && 85832580301SAttilio Rao (sc->powernow_states[sc->powernow_max_states - 2].fid < 8)) 85932580301SAttilio Rao return (EINVAL); 86043d645f9SJung-uk Kim sc->fsb = state.freq / 100 / pn8_fid_to_mult[state.fid]; 86132580301SAttilio Rao break; 86232580301SAttilio Rao } 86332580301SAttilio Rao 86432580301SAttilio Rao return (0); 86532580301SAttilio Rao } 86632580301SAttilio Rao 86732580301SAttilio Rao static void 86832580301SAttilio Rao pn_identify(driver_t *driver, device_t parent) 86932580301SAttilio Rao { 87032580301SAttilio Rao 87132580301SAttilio Rao if ((amd_pminfo & AMDPM_FID) == 0 || (amd_pminfo & AMDPM_VID) == 0) 87232580301SAttilio Rao return; 87332580301SAttilio Rao switch (cpu_id & 0xf00) { 87432580301SAttilio Rao case 0x600: 87532580301SAttilio Rao case 0xf00: 87632580301SAttilio Rao break; 87732580301SAttilio Rao default: 87832580301SAttilio Rao return; 87932580301SAttilio Rao } 88032580301SAttilio Rao if (device_find_child(parent, "powernow", -1) != NULL) 88132580301SAttilio Rao return; 88232580301SAttilio Rao if (BUS_ADD_CHILD(parent, 10, "powernow", -1) == NULL) 88332580301SAttilio Rao device_printf(parent, "powernow: add child failed\n"); 88432580301SAttilio Rao } 88532580301SAttilio Rao 88632580301SAttilio Rao static int 88732580301SAttilio Rao pn_probe(device_t dev) 88832580301SAttilio Rao { 88932580301SAttilio Rao struct pn_softc *sc; 89032580301SAttilio Rao uint64_t status; 89132580301SAttilio Rao uint64_t rate; 89232580301SAttilio Rao struct pcpu *pc; 89332580301SAttilio Rao u_int sfid, mfid, cfid; 89432580301SAttilio Rao 89532580301SAttilio Rao sc = device_get_softc(dev); 89632580301SAttilio Rao sc->errata = 0; 89732580301SAttilio Rao status = rdmsr(MSR_AMDK7_FIDVID_STATUS); 89832580301SAttilio Rao 89932580301SAttilio Rao pc = cpu_get_pcpu(dev); 90032580301SAttilio Rao if (pc == NULL) 90132580301SAttilio Rao return (ENODEV); 90232580301SAttilio Rao 90332580301SAttilio Rao cpu_est_clockrate(pc->pc_cpuid, &rate); 90432580301SAttilio Rao 90532580301SAttilio Rao switch (cpu_id & 0xf00) { 90632580301SAttilio Rao case 0x600: 90732580301SAttilio Rao sfid = PN7_STA_SFID(status); 90832580301SAttilio Rao mfid = PN7_STA_MFID(status); 90932580301SAttilio Rao cfid = PN7_STA_CFID(status); 91032580301SAttilio Rao sc->pn_type = PN7_TYPE; 91132580301SAttilio Rao sc->fsb = rate / 100000 / pn7_fid_to_mult[cfid]; 91232580301SAttilio Rao 91332580301SAttilio Rao /* 91432580301SAttilio Rao * If start FID is different to max FID, then it is a 91532580301SAttilio Rao * mobile processor. If not, it is a low powered desktop 91632580301SAttilio Rao * processor. 91732580301SAttilio Rao */ 91832580301SAttilio Rao if (PN7_STA_SFID(status) != PN7_STA_MFID(status)) { 91932580301SAttilio Rao sc->vid_to_volts = pn7_mobile_vid_to_volts; 92032580301SAttilio Rao device_set_desc(dev, "PowerNow! K7"); 92132580301SAttilio Rao } else { 92232580301SAttilio Rao sc->vid_to_volts = pn7_desktop_vid_to_volts; 92332580301SAttilio Rao device_set_desc(dev, "Cool`n'Quiet K7"); 92432580301SAttilio Rao } 92532580301SAttilio Rao break; 92632580301SAttilio Rao 92732580301SAttilio Rao case 0xf00: 92832580301SAttilio Rao sfid = PN8_STA_SFID(status); 92932580301SAttilio Rao mfid = PN8_STA_MFID(status); 93032580301SAttilio Rao cfid = PN8_STA_CFID(status); 93132580301SAttilio Rao sc->pn_type = PN8_TYPE; 93232580301SAttilio Rao sc->vid_to_volts = pn8_vid_to_volts; 93332580301SAttilio Rao sc->fsb = rate / 100000 / pn8_fid_to_mult[cfid]; 93432580301SAttilio Rao 93532580301SAttilio Rao if (PN8_STA_SFID(status) != PN8_STA_MFID(status)) 93632580301SAttilio Rao device_set_desc(dev, "PowerNow! K8"); 93732580301SAttilio Rao else 93832580301SAttilio Rao device_set_desc(dev, "Cool`n'Quiet K8"); 93932580301SAttilio Rao break; 94032580301SAttilio Rao default: 94132580301SAttilio Rao return (ENODEV); 94232580301SAttilio Rao } 94332580301SAttilio Rao 94432580301SAttilio Rao return (0); 94532580301SAttilio Rao } 94632580301SAttilio Rao 94732580301SAttilio Rao static int 94832580301SAttilio Rao pn_attach(device_t dev) 94932580301SAttilio Rao { 95032580301SAttilio Rao int rv; 95132580301SAttilio Rao device_t child; 95232580301SAttilio Rao 95332580301SAttilio Rao child = device_find_child(device_get_parent(dev), "acpi_perf", -1); 95432580301SAttilio Rao if (child) { 95532580301SAttilio Rao rv = pn_decode_acpi(dev, child); 95632580301SAttilio Rao if (rv) 95732580301SAttilio Rao rv = pn_decode_pst(dev); 95832580301SAttilio Rao } else 95932580301SAttilio Rao rv = pn_decode_pst(dev); 96032580301SAttilio Rao 96132580301SAttilio Rao if (rv != 0) 96232580301SAttilio Rao return (ENXIO); 96332580301SAttilio Rao cpufreq_register(dev); 96432580301SAttilio Rao return (0); 96532580301SAttilio Rao } 96632580301SAttilio Rao 96732580301SAttilio Rao static int 96832580301SAttilio Rao pn_detach(device_t dev) 96932580301SAttilio Rao { 97032580301SAttilio Rao 97132580301SAttilio Rao return (cpufreq_unregister(dev)); 97232580301SAttilio Rao } 973