summaryrefslogtreecommitdiffstats
path: root/sys/i386/cpufreq/powernow.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/i386/cpufreq/powernow.c')
-rw-r--r--sys/i386/cpufreq/powernow.c970
1 files changed, 0 insertions, 970 deletions
diff --git a/sys/i386/cpufreq/powernow.c b/sys/i386/cpufreq/powernow.c
deleted file mode 100644
index b248cc8..0000000
--- a/sys/i386/cpufreq/powernow.c
+++ /dev/null
@@ -1,970 +0,0 @@
-/*-
- * Copyright (c) 2004-2005 Bruno Ducrot
- * Copyright (c) 2004 FUKUDA Nobuhiko <nfukuda@spa.is.uec.ac.jp>
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-/*
- * Many thanks to Nate Lawson for his helpful comments on this driver and
- * to Jung-uk Kim for testing.
- */
-
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
-#include <sys/param.h>
-#include <sys/bus.h>
-#include <sys/cpu.h>
-#include <sys/kernel.h>
-#include <sys/malloc.h>
-#include <sys/module.h>
-#include <sys/pcpu.h>
-#include <sys/systm.h>
-
-#include <machine/pc/bios.h>
-#include <machine/md_var.h>
-#include <machine/specialreg.h>
-#include <machine/cputypes.h>
-#include <machine/vmparam.h>
-#include <sys/rman.h>
-
-#include <vm/vm.h>
-#include <vm/pmap.h>
-
-#include "cpufreq_if.h"
-
-#define PN7_TYPE 0
-#define PN8_TYPE 1
-
-/* Flags for some hardware bugs. */
-#define A0_ERRATA 0x1 /* Bugs for the rev. A0 of Athlon (K7):
- * Interrupts must be disabled and no half
- * multipliers are allowed */
-#define PENDING_STUCK 0x2 /* With some buggy chipset and some newer AMD64
- * processor (Rev. G?):
- * the pending bit from the msr FIDVID_STATUS
- * is set forever. No workaround :( */
-
-/* Legacy configuration via BIOS table PSB. */
-#define PSB_START 0
-#define PSB_STEP 0x10
-#define PSB_SIG "AMDK7PNOW!"
-#define PSB_LEN 10
-#define PSB_OFF 0
-
-struct psb_header {
- char signature[10];
- uint8_t version;
- uint8_t flags;
- uint16_t settlingtime;
- uint8_t res1;
- uint8_t numpst;
-} __packed;
-
-struct pst_header {
- uint32_t cpuid;
- uint8_t fsb;
- uint8_t maxfid;
- uint8_t startvid;
- uint8_t numpstates;
-} __packed;
-
-/*
- * MSRs and bits used by Powernow technology
- */
-#define MSR_AMDK7_FIDVID_CTL 0xc0010041
-#define MSR_AMDK7_FIDVID_STATUS 0xc0010042
-
-/* Bitfields used by K7 */
-
-#define PN7_CTR_FID(x) ((x) & 0x1f)
-#define PN7_CTR_VID(x) (((x) & 0x1f) << 8)
-#define PN7_CTR_FIDC 0x00010000
-#define PN7_CTR_VIDC 0x00020000
-#define PN7_CTR_FIDCHRATIO 0x00100000
-#define PN7_CTR_SGTC(x) (((uint64_t)(x) & 0x000fffff) << 32)
-
-#define PN7_STA_CFID(x) ((x) & 0x1f)
-#define PN7_STA_SFID(x) (((x) >> 8) & 0x1f)
-#define PN7_STA_MFID(x) (((x) >> 16) & 0x1f)
-#define PN7_STA_CVID(x) (((x) >> 32) & 0x1f)
-#define PN7_STA_SVID(x) (((x) >> 40) & 0x1f)
-#define PN7_STA_MVID(x) (((x) >> 48) & 0x1f)
-
-/* ACPI ctr_val status register to powernow k7 configuration */
-#define ACPI_PN7_CTRL_TO_FID(x) ((x) & 0x1f)
-#define ACPI_PN7_CTRL_TO_VID(x) (((x) >> 5) & 0x1f)
-#define ACPI_PN7_CTRL_TO_SGTC(x) (((x) >> 10) & 0xffff)
-
-/* Bitfields used by K8 */
-
-#define PN8_CTR_FID(x) ((x) & 0x3f)
-#define PN8_CTR_VID(x) (((x) & 0x1f) << 8)
-#define PN8_CTR_PENDING(x) (((x) & 1) << 32)
-
-#define PN8_STA_CFID(x) ((x) & 0x3f)
-#define PN8_STA_SFID(x) (((x) >> 8) & 0x3f)
-#define PN8_STA_MFID(x) (((x) >> 16) & 0x3f)
-#define PN8_STA_PENDING(x) (((x) >> 31) & 0x01)
-#define PN8_STA_CVID(x) (((x) >> 32) & 0x1f)
-#define PN8_STA_SVID(x) (((x) >> 40) & 0x1f)
-#define PN8_STA_MVID(x) (((x) >> 48) & 0x1f)
-
-/* Reserved1 to powernow k8 configuration */
-#define PN8_PSB_TO_RVO(x) ((x) & 0x03)
-#define PN8_PSB_TO_IRT(x) (((x) >> 2) & 0x03)
-#define PN8_PSB_TO_MVS(x) (((x) >> 4) & 0x03)
-#define PN8_PSB_TO_BATT(x) (((x) >> 6) & 0x03)
-
-/* ACPI ctr_val status register to powernow k8 configuration */
-#define ACPI_PN8_CTRL_TO_FID(x) ((x) & 0x3f)
-#define ACPI_PN8_CTRL_TO_VID(x) (((x) >> 6) & 0x1f)
-#define ACPI_PN8_CTRL_TO_VST(x) (((x) >> 11) & 0x1f)
-#define ACPI_PN8_CTRL_TO_MVS(x) (((x) >> 18) & 0x03)
-#define ACPI_PN8_CTRL_TO_PLL(x) (((x) >> 20) & 0x7f)
-#define ACPI_PN8_CTRL_TO_RVO(x) (((x) >> 28) & 0x03)
-#define ACPI_PN8_CTRL_TO_IRT(x) (((x) >> 30) & 0x03)
-
-
-#define WRITE_FIDVID(fid, vid, ctrl) \
- wrmsr(MSR_AMDK7_FIDVID_CTL, \
- (((ctrl) << 32) | (1ULL << 16) | ((vid) << 8) | (fid)))
-
-#define COUNT_OFF_IRT(irt) DELAY(10 * (1 << (irt)))
-#define COUNT_OFF_VST(vst) DELAY(20 * (vst))
-
-#define FID_TO_VCO_FID(fid) \
- (((fid) < 8) ? (8 + ((fid) << 1)) : (fid))
-
-/*
- * Divide each value by 10 to get the processor multiplier.
- * Some of those tables are the same as the Linux powernow-k7
- * implementation by Dave Jones.
- */
-static int pn7_fid_to_mult[32] = {
- 110, 115, 120, 125, 50, 55, 60, 65,
- 70, 75, 80, 85, 90, 95, 100, 105,
- 30, 190, 40, 200, 130, 135, 140, 210,
- 150, 225, 160, 165, 170, 180, 0, 0,
-};
-
-
-static int pn8_fid_to_mult[64] = {
- 40, 45, 50, 55, 60, 65, 70, 75,
- 80, 85, 90, 95, 100, 105, 110, 115,
- 120, 125, 130, 135, 140, 145, 150, 155,
- 160, 165, 170, 175, 180, 185, 190, 195,
- 200, 205, 210, 215, 220, 225, 230, 235,
- 240, 245, 250, 255, 260, 265, 270, 275,
- 280, 285, 290, 295, 300, 305, 310, 315,
- 320, 325, 330, 335, 340, 345, 350, 355,
-};
-
-/*
- * Units are in mV.
- */
-/* Mobile VRM (K7) */
-static int pn7_mobile_vid_to_volts[] = {
- 2000, 1950, 1900, 1850, 1800, 1750, 1700, 1650,
- 1600, 1550, 1500, 1450, 1400, 1350, 1300, 0,
- 1275, 1250, 1225, 1200, 1175, 1150, 1125, 1100,
- 1075, 1050, 1025, 1000, 975, 950, 925, 0,
-};
-/* Desktop VRM (K7) */
-static int pn7_desktop_vid_to_volts[] = {
- 2000, 1950, 1900, 1850, 1800, 1750, 1700, 1650,
- 1600, 1550, 1500, 1450, 1400, 1350, 1300, 0,
- 1275, 1250, 1225, 1200, 1175, 1150, 1125, 1100,
- 1075, 1050, 1025, 1000, 975, 950, 925, 0,
-};
-/* Desktop and Mobile VRM (K8) */
-static int pn8_vid_to_volts[] = {
- 1550, 1525, 1500, 1475, 1450, 1425, 1400, 1375,
- 1350, 1325, 1300, 1275, 1250, 1225, 1200, 1175,
- 1150, 1125, 1100, 1075, 1050, 1025, 1000, 975,
- 950, 925, 900, 875, 850, 825, 800, 0,
-};
-
-#define POWERNOW_MAX_STATES 16
-
-struct powernow_state {
- int freq;
- int power;
- int fid;
- int vid;
-};
-
-struct pn_softc {
- device_t dev;
- int pn_type;
- struct powernow_state powernow_states[POWERNOW_MAX_STATES];
- u_int fsb;
- u_int sgtc;
- u_int vst;
- u_int mvs;
- u_int pll;
- u_int rvo;
- u_int irt;
- int low;
- int powernow_max_states;
- u_int powernow_state;
- u_int errata;
- int *vid_to_volts;
-};
-
-/*
- * Offsets in struct cf_setting array for private values given by
- * acpi_perf driver.
- */
-#define PX_SPEC_CONTROL 0
-#define PX_SPEC_STATUS 1
-
-static void pn_identify(driver_t *driver, device_t parent);
-static int pn_probe(device_t dev);
-static int pn_attach(device_t dev);
-static int pn_detach(device_t dev);
-static int pn_set(device_t dev, const struct cf_setting *cf);
-static int pn_get(device_t dev, struct cf_setting *cf);
-static int pn_settings(device_t dev, struct cf_setting *sets,
- int *count);
-static int pn_type(device_t dev, int *type);
-
-static device_method_t pn_methods[] = {
- /* Device interface */
- DEVMETHOD(device_identify, pn_identify),
- DEVMETHOD(device_probe, pn_probe),
- DEVMETHOD(device_attach, pn_attach),
- DEVMETHOD(device_detach, pn_detach),
-
- /* cpufreq interface */
- DEVMETHOD(cpufreq_drv_set, pn_set),
- DEVMETHOD(cpufreq_drv_get, pn_get),
- DEVMETHOD(cpufreq_drv_settings, pn_settings),
- DEVMETHOD(cpufreq_drv_type, pn_type),
-
- {0, 0}
-};
-
-static devclass_t pn_devclass;
-static driver_t pn_driver = {
- "powernow",
- pn_methods,
- sizeof(struct pn_softc),
-};
-
-DRIVER_MODULE(powernow, cpu, pn_driver, pn_devclass, 0, 0);
-
-static int
-pn7_setfidvid(struct pn_softc *sc, int fid, int vid)
-{
- int cfid, cvid;
- uint64_t status, ctl;
-
- status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
- cfid = PN7_STA_CFID(status);
- cvid = PN7_STA_CVID(status);
-
- /* We're already at the requested level. */
- if (fid == cfid && vid == cvid)
- return (0);
-
- ctl = rdmsr(MSR_AMDK7_FIDVID_CTL) & PN7_CTR_FIDCHRATIO;
-
- ctl |= PN7_CTR_FID(fid);
- ctl |= PN7_CTR_VID(vid);
- ctl |= PN7_CTR_SGTC(sc->sgtc);
-
- if (sc->errata & A0_ERRATA)
- disable_intr();
-
- if (pn7_fid_to_mult[fid] < pn7_fid_to_mult[cfid]) {
- wrmsr(MSR_AMDK7_FIDVID_CTL, ctl | PN7_CTR_FIDC);
- if (vid != cvid)
- wrmsr(MSR_AMDK7_FIDVID_CTL, ctl | PN7_CTR_VIDC);
- } else {
- wrmsr(MSR_AMDK7_FIDVID_CTL, ctl | PN7_CTR_VIDC);
- if (fid != cfid)
- wrmsr(MSR_AMDK7_FIDVID_CTL, ctl | PN7_CTR_FIDC);
- }
-
- if (sc->errata & A0_ERRATA)
- enable_intr();
-
- return (0);
-}
-
-static int
-pn8_read_pending_wait(uint64_t *status)
-{
- int i = 10000;
-
- do
- *status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
- while (PN8_STA_PENDING(*status) && --i);
-
- return (i == 0 ? ENXIO : 0);
-}
-
-static int
-pn8_write_fidvid(u_int fid, u_int vid, uint64_t ctrl, uint64_t *status)
-{
- int i = 100;
-
- do
- WRITE_FIDVID(fid, vid, ctrl);
- while (pn8_read_pending_wait(status) && --i);
-
- return (i == 0 ? ENXIO : 0);
-}
-
-static int
-pn8_setfidvid(struct pn_softc *sc, int fid, int vid)
-{
- uint64_t status;
- int cfid, cvid;
- int rvo;
- int rv;
- u_int val;
-
- rv = pn8_read_pending_wait(&status);
- if (rv)
- return (rv);
-
- cfid = PN8_STA_CFID(status);
- cvid = PN8_STA_CVID(status);
-
- if (fid == cfid && vid == cvid)
- return (0);
-
- /*
- * Phase 1: Raise core voltage to requested VID if frequency is
- * going up.
- */
- while (cvid > vid) {
- val = cvid - (1 << sc->mvs);
- rv = pn8_write_fidvid(cfid, (val > 0) ? val : 0, 1ULL, &status);
- if (rv) {
- sc->errata |= PENDING_STUCK;
- return (rv);
- }
- cvid = PN8_STA_CVID(status);
- COUNT_OFF_VST(sc->vst);
- }
-
- /* ... then raise to voltage + RVO (if required) */
- for (rvo = sc->rvo; rvo > 0 && cvid > 0; --rvo) {
- /* XXX It's not clear from spec if we have to do that
- * in 0.25 step or in MVS. Therefore do it as it's done
- * under Linux */
- rv = pn8_write_fidvid(cfid, cvid - 1, 1ULL, &status);
- if (rv) {
- sc->errata |= PENDING_STUCK;
- return (rv);
- }
- cvid = PN8_STA_CVID(status);
- COUNT_OFF_VST(sc->vst);
- }
-
- /* Phase 2: change to requested core frequency */
- if (cfid != fid) {
- u_int vco_fid, vco_cfid, fid_delta;
-
- vco_fid = FID_TO_VCO_FID(fid);
- vco_cfid = FID_TO_VCO_FID(cfid);
-
- while (abs(vco_fid - vco_cfid) > 2) {
- fid_delta = (vco_cfid & 1) ? 1 : 2;
- if (fid > cfid) {
- if (cfid > 7)
- val = cfid + fid_delta;
- else
- val = FID_TO_VCO_FID(cfid) + fid_delta;
- } else
- val = cfid - fid_delta;
- rv = pn8_write_fidvid(val, cvid,
- sc->pll * (uint64_t) sc->fsb,
- &status);
- if (rv) {
- sc->errata |= PENDING_STUCK;
- return (rv);
- }
- cfid = PN8_STA_CFID(status);
- COUNT_OFF_IRT(sc->irt);
-
- vco_cfid = FID_TO_VCO_FID(cfid);
- }
-
- rv = pn8_write_fidvid(fid, cvid,
- sc->pll * (uint64_t) sc->fsb,
- &status);
- if (rv) {
- sc->errata |= PENDING_STUCK;
- return (rv);
- }
- cfid = PN8_STA_CFID(status);
- COUNT_OFF_IRT(sc->irt);
- }
-
- /* Phase 3: change to requested voltage */
- if (cvid != vid) {
- rv = pn8_write_fidvid(cfid, vid, 1ULL, &status);
- cvid = PN8_STA_CVID(status);
- COUNT_OFF_VST(sc->vst);
- }
-
- /* Check if transition failed. */
- if (cfid != fid || cvid != vid)
- rv = ENXIO;
-
- return (rv);
-}
-
-static int
-pn_set(device_t dev, const struct cf_setting *cf)
-{
- struct pn_softc *sc;
- int fid, vid;
- int i;
- int rv;
-
- if (cf == NULL)
- return (EINVAL);
- sc = device_get_softc(dev);
-
- if (sc->errata & PENDING_STUCK)
- return (ENXIO);
-
- for (i = 0; i < sc->powernow_max_states; ++i)
- if (CPUFREQ_CMP(sc->powernow_states[i].freq / 1000, cf->freq))
- break;
-
- fid = sc->powernow_states[i].fid;
- vid = sc->powernow_states[i].vid;
-
- rv = ENODEV;
-
- switch (sc->pn_type) {
- case PN7_TYPE:
- rv = pn7_setfidvid(sc, fid, vid);
- break;
- case PN8_TYPE:
- rv = pn8_setfidvid(sc, fid, vid);
- break;
- }
-
- return (rv);
-}
-
-static int
-pn_get(device_t dev, struct cf_setting *cf)
-{
- struct pn_softc *sc;
- u_int cfid = 0, cvid = 0;
- int i;
- uint64_t status;
-
- if (cf == NULL)
- return (EINVAL);
- sc = device_get_softc(dev);
- if (sc->errata & PENDING_STUCK)
- return (ENXIO);
-
- status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
-
- switch (sc->pn_type) {
- case PN7_TYPE:
- cfid = PN7_STA_CFID(status);
- cvid = PN7_STA_CVID(status);
- break;
- case PN8_TYPE:
- cfid = PN8_STA_CFID(status);
- cvid = PN8_STA_CVID(status);
- break;
- }
- for (i = 0; i < sc->powernow_max_states; ++i)
- if (cfid == sc->powernow_states[i].fid &&
- cvid == sc->powernow_states[i].vid)
- break;
-
- if (i < sc->powernow_max_states) {
- cf->freq = sc->powernow_states[i].freq / 1000;
- cf->power = sc->powernow_states[i].power;
- cf->lat = 200;
- cf->volts = sc->vid_to_volts[cvid];
- cf->dev = dev;
- } else {
- memset(cf, CPUFREQ_VAL_UNKNOWN, sizeof(*cf));
- cf->dev = NULL;
- }
-
- return (0);
-}
-
-static int
-pn_settings(device_t dev, struct cf_setting *sets, int *count)
-{
- struct pn_softc *sc;
- int i;
-
- if (sets == NULL|| count == NULL)
- return (EINVAL);
- sc = device_get_softc(dev);
- if (*count < sc->powernow_max_states)
- return (E2BIG);
- for (i = 0; i < sc->powernow_max_states; ++i) {
- sets[i].freq = sc->powernow_states[i].freq / 1000;
- sets[i].power = sc->powernow_states[i].power;
- sets[i].lat = 200;
- sets[i].volts = sc->vid_to_volts[sc->powernow_states[i].vid];
- sets[i].dev = dev;
- }
- *count = sc->powernow_max_states;
-
- return (0);
-}
-
-static int
-pn_type(device_t dev, int *type)
-{
- if (type == NULL)
- return (EINVAL);
-
- *type = CPUFREQ_TYPE_ABSOLUTE;
-
- return (0);
-}
-
-/*
- * Given a set of pair of fid/vid, and number of performance states,
- * compute powernow_states via an insertion sort.
- */
-static int
-decode_pst(struct pn_softc *sc, uint8_t *p, int npstates)
-{
- int i, j, n;
- struct powernow_state state;
-
- for (i = 0; i < POWERNOW_MAX_STATES; ++i)
- sc->powernow_states[i].freq = CPUFREQ_VAL_UNKNOWN;
-
- for (n = 0, i = 0; i < npstates; ++i) {
- state.fid = *p++;
- state.vid = *p++;
- state.power = CPUFREQ_VAL_UNKNOWN;
-
- switch (sc->pn_type) {
- case PN7_TYPE:
- state.freq = 100 * pn7_fid_to_mult[state.fid] * sc->fsb;
- if ((sc->errata & A0_ERRATA) &&
- (pn7_fid_to_mult[state.fid] % 10) == 5)
- continue;
- break;
- case PN8_TYPE:
- state.freq = 100 * pn8_fid_to_mult[state.fid] * sc->fsb;
- break;
- }
-
- j = n;
- while (j > 0 && sc->powernow_states[j - 1].freq < state.freq) {
- memcpy(&sc->powernow_states[j],
- &sc->powernow_states[j - 1],
- sizeof(struct powernow_state));
- --j;
- }
- memcpy(&sc->powernow_states[j], &state,
- sizeof(struct powernow_state));
- ++n;
- }
-
- /*
- * Fix powernow_max_states, if errata a0 give us less states
- * than expected.
- */
- sc->powernow_max_states = n;
-
- if (bootverbose)
- for (i = 0; i < sc->powernow_max_states; ++i) {
- int fid = sc->powernow_states[i].fid;
- int vid = sc->powernow_states[i].vid;
-
- printf("powernow: %2i %8dkHz FID %02x VID %02x\n",
- i,
- sc->powernow_states[i].freq,
- fid,
- vid);
- }
-
- return (0);
-}
-
-static int
-cpuid_is_k7(u_int cpuid)
-{
-
- switch (cpuid) {
- case 0x760:
- case 0x761:
- case 0x762:
- case 0x770:
- case 0x771:
- case 0x780:
- case 0x781:
- case 0x7a0:
- return (TRUE);
- }
- return (FALSE);
-}
-
-static int
-pn_decode_pst(device_t dev)
-{
- int maxpst;
- struct pn_softc *sc;
- u_int cpuid, maxfid, startvid;
- u_long sig;
- struct psb_header *psb;
- uint8_t *p;
- u_int regs[4];
- uint64_t status;
-
- sc = device_get_softc(dev);
-
- do_cpuid(0x80000001, regs);
- cpuid = regs[0];
-
- if ((cpuid & 0xfff) == 0x760)
- sc->errata |= A0_ERRATA;
-
- status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
-
- switch (sc->pn_type) {
- case PN7_TYPE:
- maxfid = PN7_STA_MFID(status);
- startvid = PN7_STA_SVID(status);
- break;
- case PN8_TYPE:
- maxfid = PN8_STA_MFID(status);
- /*
- * we should actually use a variable named 'maxvid' if K8,
- * but why introducing a new variable for that?
- */
- startvid = PN8_STA_MVID(status);
- break;
- default:
- return (ENODEV);
- }
-
- if (bootverbose) {
- device_printf(dev, "STATUS: 0x%jx\n", status);
- device_printf(dev, "STATUS: maxfid: 0x%02x\n", maxfid);
- device_printf(dev, "STATUS: %s: 0x%02x\n",
- sc->pn_type == PN7_TYPE ? "startvid" : "maxvid",
- startvid);
- }
-
- sig = bios_sigsearch(PSB_START, PSB_SIG, PSB_LEN, PSB_STEP, PSB_OFF);
- if (sig) {
- struct pst_header *pst;
-
- psb = (struct psb_header*)(uintptr_t)BIOS_PADDRTOVADDR(sig);
-
- switch (psb->version) {
- default:
- return (ENODEV);
- case 0x14:
- /*
- * We can't be picky about numpst since at least
- * some systems have a value of 1 and some have 2.
- * We trust that cpuid_is_k7() will be better at
- * catching that we're on a K8 anyway.
- */
- if (sc->pn_type != PN8_TYPE)
- return (EINVAL);
- sc->vst = psb->settlingtime;
- sc->rvo = PN8_PSB_TO_RVO(psb->res1),
- sc->irt = PN8_PSB_TO_IRT(psb->res1),
- sc->mvs = PN8_PSB_TO_MVS(psb->res1),
- sc->low = PN8_PSB_TO_BATT(psb->res1);
- if (bootverbose) {
- device_printf(dev, "PSB: VST: %d\n",
- psb->settlingtime);
- device_printf(dev, "PSB: RVO %x IRT %d "
- "MVS %d BATT %d\n",
- sc->rvo,
- sc->irt,
- sc->mvs,
- sc->low);
- }
- break;
- case 0x12:
- if (sc->pn_type != PN7_TYPE)
- return (EINVAL);
- sc->sgtc = psb->settlingtime * sc->fsb;
- if (sc->sgtc < 100 * sc->fsb)
- sc->sgtc = 100 * sc->fsb;
- break;
- }
-
- p = ((uint8_t *) psb) + sizeof(struct psb_header);
- pst = (struct pst_header*) p;
-
- maxpst = 200;
-
- do {
- struct pst_header *pst = (struct pst_header*) p;
-
- if (cpuid == pst->cpuid &&
- maxfid == pst->maxfid &&
- startvid == pst->startvid) {
- sc->powernow_max_states = pst->numpstates;
- switch (sc->pn_type) {
- case PN7_TYPE:
- if (abs(sc->fsb - pst->fsb) > 5)
- continue;
- break;
- case PN8_TYPE:
- break;
- }
- return (decode_pst(sc,
- p + sizeof(struct pst_header),
- sc->powernow_max_states));
- }
-
- p += sizeof(struct pst_header) + (2 * pst->numpstates);
- } while (cpuid_is_k7(pst->cpuid) && maxpst--);
-
- device_printf(dev, "no match for extended cpuid %.3x\n", cpuid);
- }
-
- return (ENODEV);
-}
-
-static int
-pn_decode_acpi(device_t dev, device_t perf_dev)
-{
- int i, j, n;
- uint64_t status;
- uint32_t ctrl;
- u_int cpuid;
- u_int regs[4];
- struct pn_softc *sc;
- struct powernow_state state;
- struct cf_setting sets[POWERNOW_MAX_STATES];
- int count = POWERNOW_MAX_STATES;
- int type;
- int rv;
-
- if (perf_dev == NULL)
- return (ENXIO);
-
- rv = CPUFREQ_DRV_SETTINGS(perf_dev, sets, &count);
- if (rv)
- return (ENXIO);
- rv = CPUFREQ_DRV_TYPE(perf_dev, &type);
- if (rv || (type & CPUFREQ_FLAG_INFO_ONLY) == 0)
- return (ENXIO);
-
- sc = device_get_softc(dev);
-
- do_cpuid(0x80000001, regs);
- cpuid = regs[0];
- if ((cpuid & 0xfff) == 0x760)
- sc->errata |= A0_ERRATA;
-
- ctrl = 0;
- sc->sgtc = 0;
- for (n = 0, i = 0; i < count; ++i) {
- ctrl = sets[i].spec[PX_SPEC_CONTROL];
- switch (sc->pn_type) {
- case PN7_TYPE:
- state.fid = ACPI_PN7_CTRL_TO_FID(ctrl);
- state.vid = ACPI_PN7_CTRL_TO_VID(ctrl);
- if ((sc->errata & A0_ERRATA) &&
- (pn7_fid_to_mult[state.fid] % 10) == 5)
- continue;
- state.freq = 100 * pn7_fid_to_mult[state.fid] * sc->fsb;
- break;
- case PN8_TYPE:
- state.fid = ACPI_PN8_CTRL_TO_FID(ctrl);
- state.vid = ACPI_PN8_CTRL_TO_VID(ctrl);
- state.freq = 100 * pn8_fid_to_mult[state.fid] * sc->fsb;
- break;
- }
-
- state.power = sets[i].power;
-
- j = n;
- while (j > 0 && sc->powernow_states[j - 1].freq < state.freq) {
- memcpy(&sc->powernow_states[j],
- &sc->powernow_states[j - 1],
- sizeof(struct powernow_state));
- --j;
- }
- memcpy(&sc->powernow_states[j], &state,
- sizeof(struct powernow_state));
- ++n;
- }
-
- sc->powernow_max_states = n;
- state = sc->powernow_states[0];
- status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
-
- switch (sc->pn_type) {
- case PN7_TYPE:
- sc->sgtc = ACPI_PN7_CTRL_TO_SGTC(ctrl);
- /*
- * XXX Some bios forget the max frequency!
- * This maybe indicates we have the wrong tables. Therefore,
- * don't implement a quirk, but fallback to BIOS legacy
- * tables instead.
- */
- if (PN7_STA_MFID(status) != state.fid) {
- device_printf(dev, "ACPI MAX frequency not found\n");
- return (EINVAL);
- }
- break;
- case PN8_TYPE:
- sc->vst = ACPI_PN8_CTRL_TO_VST(ctrl),
- sc->mvs = ACPI_PN8_CTRL_TO_MVS(ctrl),
- sc->pll = ACPI_PN8_CTRL_TO_PLL(ctrl),
- sc->rvo = ACPI_PN8_CTRL_TO_RVO(ctrl),
- sc->irt = ACPI_PN8_CTRL_TO_IRT(ctrl);
- sc->low = 0; /* XXX */
-
- /*
- * powernow k8 supports only one low frequency.
- */
- if (sc->powernow_max_states >= 2 &&
- (sc->powernow_states[sc->powernow_max_states - 2].fid < 8))
- return (EINVAL);
- break;
- }
-
- return (0);
-}
-
-static void
-pn_identify(driver_t *driver, device_t parent)
-{
-
- if ((amd_pminfo & AMDPM_FID) == 0 || (amd_pminfo & AMDPM_VID) == 0)
- return;
- switch (cpu_id & 0xf00) {
- case 0x600:
- case 0xf00:
- break;
- default:
- return;
- }
- if (device_find_child(parent, "powernow", -1) != NULL)
- return;
- if (BUS_ADD_CHILD(parent, 10, "powernow", -1) == NULL)
- device_printf(parent, "powernow: add child failed\n");
-}
-
-static int
-pn_probe(device_t dev)
-{
- struct pn_softc *sc;
- uint64_t status;
- uint64_t rate;
- struct pcpu *pc;
- u_int sfid, mfid, cfid;
-
- sc = device_get_softc(dev);
- sc->errata = 0;
- status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
-
- pc = cpu_get_pcpu(dev);
- if (pc == NULL)
- return (ENODEV);
-
- cpu_est_clockrate(pc->pc_cpuid, &rate);
-
- switch (cpu_id & 0xf00) {
- case 0x600:
- sfid = PN7_STA_SFID(status);
- mfid = PN7_STA_MFID(status);
- cfid = PN7_STA_CFID(status);
- sc->pn_type = PN7_TYPE;
- sc->fsb = rate / 100000 / pn7_fid_to_mult[cfid];
-
- /*
- * If start FID is different to max FID, then it is a
- * mobile processor. If not, it is a low powered desktop
- * processor.
- */
- if (PN7_STA_SFID(status) != PN7_STA_MFID(status)) {
- sc->vid_to_volts = pn7_mobile_vid_to_volts;
- device_set_desc(dev, "PowerNow! K7");
- } else {
- sc->vid_to_volts = pn7_desktop_vid_to_volts;
- device_set_desc(dev, "Cool`n'Quiet K7");
- }
- break;
-
- case 0xf00:
- sfid = PN8_STA_SFID(status);
- mfid = PN8_STA_MFID(status);
- cfid = PN8_STA_CFID(status);
- sc->pn_type = PN8_TYPE;
- sc->vid_to_volts = pn8_vid_to_volts;
- sc->fsb = rate / 100000 / pn8_fid_to_mult[cfid];
-
- if (PN8_STA_SFID(status) != PN8_STA_MFID(status))
- device_set_desc(dev, "PowerNow! K8");
- else
- device_set_desc(dev, "Cool`n'Quiet K8");
- break;
- default:
- return (ENODEV);
- }
-
- return (0);
-}
-
-static int
-pn_attach(device_t dev)
-{
- int rv;
- device_t child;
-
- child = device_find_child(device_get_parent(dev), "acpi_perf", -1);
- if (child) {
- rv = pn_decode_acpi(dev, child);
- if (rv)
- rv = pn_decode_pst(dev);
- } else
- rv = pn_decode_pst(dev);
-
- if (rv != 0)
- return (ENXIO);
- cpufreq_register(dev);
- return (0);
-}
-
-static int
-pn_detach(device_t dev)
-{
-
- return (cpufreq_unregister(dev));
-}
OpenPOWER on IntegriCloud