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,

Reply via email to