Module Name: src Committed By: jruoho Date: Tue Aug 24 07:28:00 UTC 2010
Modified Files: src/share/man/man4: acpicpu.4 src/sys/arch/x86/acpi: acpi_cpu_md.c src/sys/dev/acpi: acpi_cpu.h Log Message: Add native support for AMD family 0Fh processors. This is the furthest we will go backwards; K7 will not be supported already due doubts about availability and reliability of ACPI during that era. Some unfortunate code duplication is present (but not overly much). Thanks to cegger@ and jakllsch@ for patiently testing this. To generate a diff of this commit: cvs rdiff -u -r1.14 -r1.15 src/share/man/man4/acpicpu.4 cvs rdiff -u -r1.31 -r1.32 src/sys/arch/x86/acpi/acpi_cpu_md.c cvs rdiff -u -r1.23 -r1.24 src/sys/dev/acpi/acpi_cpu.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/share/man/man4/acpicpu.4 diff -u src/share/man/man4/acpicpu.4:1.14 src/share/man/man4/acpicpu.4:1.15 --- src/share/man/man4/acpicpu.4:1.14 Fri Aug 20 06:35:55 2010 +++ src/share/man/man4/acpicpu.4 Tue Aug 24 07:27:59 2010 @@ -1,4 +1,4 @@ -.\" $NetBSD: acpicpu.4,v 1.14 2010/08/20 06:35:55 jruoho Exp $ +.\" $NetBSD: acpicpu.4,v 1.15 2010/08/24 07:27:59 jruoho Exp $ .\" .\" Coyright (c) 2010 Jukka Ruohonen <jruoho...@iki.fi> .\" All rights reserved. @@ -24,7 +24,7 @@ .\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE .\" POSSIBILITY OF SUCH DAMAGE. .\" -.Dd August 20, 2010 +.Dd August 24, 2010 .Dt ACPICPU 4 .Os .Sh NAME @@ -161,12 +161,17 @@ .Dq PowerSaver .Pq VIA . .Pp -The -.Dv P0 -state is always the highest operating frequency supported by the processor. +The P0-state is always the highest operating +frequency supported by the processor. The number of additional P-states may vary across processors and vendors. Each higher numbered P-state represents lower clock frequencies and hence lower power consumption. +Note that while +.Nm +always uses the exact frequencies internally, +the user-visible values reported by +.Tn ACPI +may be rounded or approximated by the vendor. .Pp Unlike conventional .Tn CPU @@ -174,8 +179,9 @@ .Tn ACPI provides support for Dynamic Voltage and Frequency Scaling .Pq Tn DVFS . -This means that the firmware may request the implementation to -dynamically scale the presently supported maximum clock frequency. +Among other things, +this means that the firmware may request the implementation to +dynamically scale the presently supported maximum or minimum clock frequency. For example, if .Xr acpiacad 4 is disconnected, the maximum available frequency may be lowered. @@ -191,7 +197,7 @@ time a processor is allowed to execute. Outside the .Tn ACPI -nomenclature, throttling may be known as +nomenclature, throttling and T-states may be known as .Dq on-demand clock modulation .Pq Tn ODCM . .Pp @@ -205,8 +211,7 @@ and thus, comparable to the C0-state, the processor is fully active. Each additional higher-numbered T-state indicates lower duty cycles. At most eight T-states may be available, although also T-states use -.Tn DVFS ; -both the maximum and the minimum available T-state may change dynamically. +.Tn DVFS . .Pp The duty cycle does not refer to the actual clock signal, but to the time period in which the clock signal is allowed @@ -218,7 +223,7 @@ .Tn CPU is forced to idle. Because of this, the use of T-states may -severely reduce system performance. +severely affect system performance. .Pp There are two typical situations for throttling: power management and thermal control. @@ -266,7 +271,7 @@ .Dv ENHANCED_SPEEDSTEP and .Dv POWERNOW_K8 . -Depending on the processor vendor, the second-level node is either +Depending on the processor, the second-level node is either .Ic machdep.est or .Ic machdep.powernow . @@ -299,9 +304,6 @@ .An Jukka Ruohonen .Aq jruoho...@iki.fi .Sh CAVEATS -The -.Nm -driver should be considered experimental. At least the following caveats can be mentioned. .Bl -bullet .It @@ -320,10 +322,6 @@ it is recommended to turn it off, with or without .Nm . .It -While P-states are supported on all Intel -.Tn CPUs , -not all AMD processors are yet supported. -.It Processor thermal control (see .Xr acpitz 4 ) is not yet supported. Index: src/sys/arch/x86/acpi/acpi_cpu_md.c diff -u src/sys/arch/x86/acpi/acpi_cpu_md.c:1.31 src/sys/arch/x86/acpi/acpi_cpu_md.c:1.32 --- src/sys/arch/x86/acpi/acpi_cpu_md.c:1.31 Mon Aug 23 16:20:44 2010 +++ src/sys/arch/x86/acpi/acpi_cpu_md.c Tue Aug 24 07:28:00 2010 @@ -1,4 +1,4 @@ -/* $NetBSD: acpi_cpu_md.c,v 1.31 2010/08/23 16:20:44 jruoho Exp $ */ +/* $NetBSD: acpi_cpu_md.c,v 1.32 2010/08/24 07:28:00 jruoho Exp $ */ /*- * Copyright (c) 2010 Jukka Ruohonen <jruoho...@iki.fi> @@ -27,7 +27,7 @@ * SUCH DAMAGE. */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: acpi_cpu_md.c,v 1.31 2010/08/23 16:20:44 jruoho Exp $"); +__KERNEL_RCSID(0, "$NetBSD: acpi_cpu_md.c,v 1.32 2010/08/24 07:28:00 jruoho Exp $"); #include <sys/param.h> #include <sys/bus.h> @@ -48,21 +48,53 @@ #include <dev/pci/pcivar.h> #include <dev/pci/pcidevs.h> -#define CPUID_INTEL_TSC __BIT(8) - -#define MSR_0FH_CONTROL 0xc0010041 /* Family 0Fh (and K7). */ -#define MSR_0FH_STATUS 0xc0010042 - -#define MSR_10H_LIMIT 0xc0010061 /* Families 10h and 11h. */ +/* + * AMD families 10h and 11h. + */ +#define MSR_10H_LIMIT 0xc0010061 #define MSR_10H_CONTROL 0xc0010062 #define MSR_10H_STATUS 0xc0010063 #define MSR_10H_CONFIG 0xc0010064 +/* + * AMD family 0Fh. + */ +#define MSR_0FH_CONTROL 0xc0010041 +#define MSR_0FH_STATUS 0xc0010042 + +#define MSR_0FH_STATUS_CFID __BITS( 0, 5) +#define MSR_0FH_STATUS_CVID __BITS(32, 36) +#define MSR_0FH_STATUS_PENDING __BITS(31, 31) + +#define MSR_0FH_CONTROL_FID __BITS( 0, 5) +#define MSR_0FH_CONTROL_VID __BITS( 8, 12) +#define MSR_0FH_CONTROL_CHG __BITS(16, 16) +#define MSR_0FH_CONTROL_CNT __BITS(32, 51) + +#define ACPI_0FH_STATUS_FID __BITS( 0, 5) +#define ACPI_0FH_STATUS_VID __BITS( 6, 10) + +#define ACPI_0FH_CONTROL_FID __BITS( 0, 5) +#define ACPI_0FH_CONTROL_VID __BITS( 6, 10) +#define ACPI_0FH_CONTROL_VST __BITS(11, 17) +#define ACPI_0FH_CONTROL_MVS __BITS(18, 19) +#define ACPI_0FH_CONTROL_PLL __BITS(20, 26) +#define ACPI_0FH_CONTROL_RVO __BITS(28, 29) +#define ACPI_0FH_CONTROL_IRT __BITS(30, 31) + +#define FID_TO_VCO_FID(fidd) (((fid) < 8) ? (8 + ((fid) << 1)) : (fid)) + static char native_idle_text[16]; void (*native_idle)(void) = NULL; static int acpicpu_md_quirks_piix4(struct pci_attach_args *); static void acpicpu_md_pstate_status(void *, void *); +static int acpicpu_md_pstate_fidvid_get(struct acpicpu_softc *, + uint32_t *); +static int acpicpu_md_pstate_fidvid_set(struct acpicpu_pstate *); +static int acpicpu_md_pstate_fidvid_read(uint32_t *, uint32_t *); +static void acpicpu_md_pstate_fidvid_write(uint32_t, uint32_t, + uint32_t, uint32_t); static void acpicpu_md_tstate_status(void *, void *); static int acpicpu_md_pstate_sysctl_init(void); static int acpicpu_md_pstate_sysctl_get(SYSCTLFN_PROTO); @@ -178,7 +210,7 @@ x86_cpuid(0x80000007, regs); - if ((regs[3] & CPUID_INTEL_TSC) != 0) + if ((regs[3] & __BIT(8)) != 0) val &= ~ACPICPU_FLAG_C_TSC; } @@ -186,19 +218,34 @@ case CPUVENDOR_AMD: + x86_cpuid(0x80000000, regs); + + if (regs[0] < 0x80000007) + break; + + x86_cpuid(0x80000007, regs); + family = CPUID2FAMILY(ci->ci_signature); if (family == 0xf) family += CPUID2EXTFAMILY(ci->ci_signature); - switch (family) { + switch (family) { case 0x0f: + + if ((regs[3] & CPUID_APM_FID) == 0) + break; + + if ((regs[3] & CPUID_APM_VID) == 0) + break; + + val |= ACPICPU_FLAG_P_FFH | ACPICPU_FLAG_P_FIDVID; + break; + case 0x10: case 0x11: - x86_cpuid(0x80000007, regs); - if ((regs[3] & CPUID_APM_TSC) != 0) val &= ~ACPICPU_FLAG_C_TSC; @@ -370,6 +417,9 @@ (void)memset(&msr, 0, sizeof(struct acpicpu_pstate)); + if ((sc->sc_flags & ACPICPU_FLAG_P_FIDVID) != 0) + msr.ps_flags = ACPICPU_FLAG_P_FIDVID; + switch (cpu_vendor) { case CPUVENDOR_IDT: @@ -390,6 +440,11 @@ switch (family) { + case 0x0f: + msr.ps_control_addr = MSR_0FH_CONTROL; + msr.ps_status_addr = MSR_0FH_STATUS; + break; + case 0x10: case 0x11: msr.ps_control_addr = MSR_10H_CONTROL; @@ -421,6 +476,9 @@ ps = &sc->sc_pstate[i]; + if (msr.ps_flags != 0) + ps->ps_flags |= msr.ps_flags; + if (msr.ps_status_addr != 0) ps->ps_status_addr = msr.ps_status_addr; @@ -466,11 +524,14 @@ uint64_t val; uint32_t i; + if ((sc->sc_flags & ACPICPU_FLAG_P_FIDVID) != 0) + return acpicpu_md_pstate_fidvid_get(sc, freq); + for (i = 0; i < sc->sc_pstate_count; i++) { ps = &sc->sc_pstate[i]; - if (ps->ps_freq != 0) + if (__predict_true(ps->ps_freq != 0)) break; } @@ -489,7 +550,7 @@ ps = &sc->sc_pstate[i]; - if (ps->ps_freq == 0) + if (__predict_false(ps->ps_freq == 0)) continue; if (val == ps->ps_status) { @@ -508,6 +569,9 @@ uint64_t xc; int rv = 0; + if ((ps->ps_flags & ACPICPU_FLAG_P_FIDVID) != 0) + return acpicpu_md_pstate_fidvid_set(ps); + msr.msr_read = false; msr.msr_type = ps->ps_control_addr; msr.msr_value = ps->ps_control; @@ -563,6 +627,189 @@ *(uintptr_t *)arg2 = EAGAIN; } +static int +acpicpu_md_pstate_fidvid_get(struct acpicpu_softc *sc, uint32_t *freq) +{ + struct acpicpu_pstate *ps; + uint32_t fid, i, vid; + uint32_t cfid, cvid; + int rv; + + /* + * AMD family 0Fh needs special treatment. + * While it wants to use ACPI, it does not + * comply with the ACPI specifications. + */ + rv = acpicpu_md_pstate_fidvid_read(&cfid, &cvid); + + if (rv != 0) + return rv; + + for (i = 0; i < sc->sc_pstate_count; i++) { + + ps = &sc->sc_pstate[i]; + + if (__predict_false(ps->ps_freq == 0)) + continue; + + fid = __SHIFTOUT(ps->ps_status, ACPI_0FH_STATUS_FID); + vid = __SHIFTOUT(ps->ps_status, ACPI_0FH_STATUS_VID); + + if (cfid == fid && cvid == vid) { + *freq = ps->ps_freq; + return 0; + } + } + + return EIO; +} + +static int +acpicpu_md_pstate_fidvid_set(struct acpicpu_pstate *ps) +{ + const uint64_t ctrl = ps->ps_control; + uint32_t cfid, cvid, fid, i, irt; + uint32_t pll, vco_cfid, vco_fid; + uint32_t val, vid, vst; + int rv; + + rv = acpicpu_md_pstate_fidvid_read(&cfid, &cvid); + + if (rv != 0) + return rv; + + fid = __SHIFTOUT(ctrl, ACPI_0FH_CONTROL_FID); + vid = __SHIFTOUT(ctrl, ACPI_0FH_CONTROL_VID); + irt = __SHIFTOUT(ctrl, ACPI_0FH_CONTROL_IRT); + vst = __SHIFTOUT(ctrl, ACPI_0FH_CONTROL_VST); + pll = __SHIFTOUT(ctrl, ACPI_0FH_CONTROL_PLL); + + vst = vst * 20; + pll = pll * 1000 / 5; + irt = 10 * __BIT(irt); + + /* + * Phase 1. + */ + while (cvid > vid) { + + val = 1 << __SHIFTOUT(ctrl, ACPI_0FH_CONTROL_MVS); + val = (val > cvid) ? 0 : cvid - val; + + acpicpu_md_pstate_fidvid_write(cfid, val, 1, vst); + rv = acpicpu_md_pstate_fidvid_read(NULL, &cvid); + + if (rv != 0) + return rv; + } + + i = __SHIFTOUT(ctrl, ACPI_0FH_CONTROL_RVO); + + for (; i > 0 && cvid > 0; --i) { + + acpicpu_md_pstate_fidvid_write(cfid, cvid - 1, 1, vst); + rv = acpicpu_md_pstate_fidvid_read(NULL, &cvid); + + if (rv != 0) + return rv; + } + + /* + * Phase 2. + */ + if (cfid != fid) { + + vco_fid = FID_TO_VCO_FID(fid); + vco_cfid = FID_TO_VCO_FID(cfid); + + while (abs(vco_fid - vco_cfid) > 2) { + + if (fid <= cfid) + val = cfid - 2; + else { + val = (cfid > 6) ? cfid + 2 : + FID_TO_VCO_FID(cfid) + 2; + } + + acpicpu_md_pstate_fidvid_write(val, cvid, pll, irt); + rv = acpicpu_md_pstate_fidvid_read(&cfid, NULL); + + if (rv != 0) + return rv; + + vco_cfid = FID_TO_VCO_FID(cfid); + } + + acpicpu_md_pstate_fidvid_write(fid, cvid, pll, irt); + rv = acpicpu_md_pstate_fidvid_read(&cfid, NULL); + + if (rv != 0) + return rv; + } + + /* + * Phase 3. + */ + if (cvid != vid) { + + acpicpu_md_pstate_fidvid_write(cfid, vid, 1, vst); + rv = acpicpu_md_pstate_fidvid_read(NULL, &cvid); + + if (rv != 0) + return rv; + } + + if (cfid != fid || cvid != vid) + return EIO; + + return 0; +} + +static int +acpicpu_md_pstate_fidvid_read(uint32_t *cfid, uint32_t *cvid) +{ + int i = ACPICPU_P_STATE_RETRY * 100; + uint64_t val; + + do { + val = rdmsr(MSR_0FH_STATUS); + + } while (__SHIFTOUT(val, MSR_0FH_STATUS_PENDING) != 0 && --i >= 0); + + if (i == 0) + return EAGAIN; + + if (cfid != NULL) + *cfid = __SHIFTOUT(val, MSR_0FH_STATUS_CFID); + + if (cvid != NULL) + *cvid = __SHIFTOUT(val, MSR_0FH_STATUS_CVID); + + return 0; +} + +static void +acpicpu_md_pstate_fidvid_write(uint32_t fid, + uint32_t vid, uint32_t cnt, uint32_t tmo) +{ + struct msr_rw_info msr; + uint64_t xc; + + msr.msr_read = false; + msr.msr_type = MSR_0FH_CONTROL; + msr.msr_value = 0; + + msr.msr_value |= __SHIFTIN(fid, MSR_0FH_CONTROL_FID); + msr.msr_value |= __SHIFTIN(vid, MSR_0FH_CONTROL_VID); + msr.msr_value |= __SHIFTIN(cnt, MSR_0FH_CONTROL_CNT); + msr.msr_value |= __SHIFTIN(0x1, MSR_0FH_CONTROL_CHG); + + xc = xc_broadcast(0, (xcfunc_t)x86_msr_xcall, &msr, NULL); + xc_wait(xc); + + DELAY(tmo); +} + int acpicpu_md_tstate_get(struct acpicpu_softc *sc, uint32_t *percent) { Index: src/sys/dev/acpi/acpi_cpu.h diff -u src/sys/dev/acpi/acpi_cpu.h:1.23 src/sys/dev/acpi/acpi_cpu.h:1.24 --- src/sys/dev/acpi/acpi_cpu.h:1.23 Mon Aug 23 16:20:45 2010 +++ src/sys/dev/acpi/acpi_cpu.h Tue Aug 24 07:27:59 2010 @@ -1,4 +1,4 @@ -/* $NetBSD: acpi_cpu.h,v 1.23 2010/08/23 16:20:45 jruoho Exp $ */ +/* $NetBSD: acpi_cpu.h,v 1.24 2010/08/24 07:27:59 jruoho Exp $ */ /*- * Copyright (c) 2010 Jukka Ruohonen <jruoho...@iki.fi> @@ -108,9 +108,10 @@ #define ACPICPU_FLAG_P_HW __BIT(13) /* HW coordination supported */ #define ACPICPU_FLAG_P_XPSS __BIT(14) /* Microsoft XPSS in use */ #define ACPICPU_FLAG_P_TURBO __BIT(15) /* Turbo Boost / Turbo Core */ +#define ACPICPU_FLAG_P_FIDVID __BIT(16) /* AMD "FID/VID algorithm" */ -#define ACPICPU_FLAG_T_FFH __BIT(16) /* Native throttling */ -#define ACPICPU_FLAG_T_FADT __BIT(17) /* Throttling with FADT */ +#define ACPICPU_FLAG_T_FFH __BIT(17) /* Native throttling */ +#define ACPICPU_FLAG_T_FADT __BIT(18) /* Throttling with FADT */ /* * This is AML_RESOURCE_GENERIC_REGISTER,