Module Name:    src
Committed By:   phx
Date:           Wed Oct 20 18:52:34 UTC 2010

Modified Files:
        src/sys/arch/macppc/dev: obio.c
        src/sys/arch/powerpc/include: cpu.h
        src/sys/arch/powerpc/oea: cpu_subr.c

Log Message:
Support sysctl machdep.cpu_speed for 7447A and 7448 based Macs. On those
machines the CPU's DFS (Dynamic Frequency Switching) feature is used instead
of a GPIO to control the speed.
Two new functions in powerpc/oea/cpu_subr.c were introduced to support
reading and writing of DFS: cpu_get_dfs() and cpu_set_dfs(). Also works
for multiple CPUs, but not before interrupts are enabled.


To generate a diff of this commit:
cvs rdiff -u -r1.29 -r1.30 src/sys/arch/macppc/dev/obio.c
cvs rdiff -u -r1.70 -r1.71 src/sys/arch/powerpc/include/cpu.h
cvs rdiff -u -r1.55 -r1.56 src/sys/arch/powerpc/oea/cpu_subr.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/arch/macppc/dev/obio.c
diff -u src/sys/arch/macppc/dev/obio.c:1.29 src/sys/arch/macppc/dev/obio.c:1.30
--- src/sys/arch/macppc/dev/obio.c:1.29	Sat Mar 14 15:36:09 2009
+++ src/sys/arch/macppc/dev/obio.c	Wed Oct 20 18:52:33 2010
@@ -1,4 +1,4 @@
-/*	$NetBSD: obio.c,v 1.29 2009/03/14 15:36:09 dsl Exp $	*/
+/*	$NetBSD: obio.c,v 1.30 2010/10/20 18:52:33 phx Exp $	*/
 
 /*-
  * Copyright (C) 1998	Internet Research Institute, Inc.
@@ -32,7 +32,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: obio.c,v 1.29 2009/03/14 15:36:09 dsl Exp $");
+__KERNEL_RCSID(0, "$NetBSD: obio.c,v 1.30 2010/10/20 18:52:33 phx Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -49,6 +49,8 @@
 
 #include <macppc/dev/obiovar.h>
 
+#include <powerpc/cpu.h>
+
 #include "opt_obio.h"
 
 #ifdef OBIO_DEBUG
@@ -328,9 +330,9 @@
 obio_setup_gpios(struct obio_softc *sc, int node)
 {
 	uint32_t reg[6];
-	struct sysctlnode *sysctl_node = NULL;
+	struct sysctlnode *sysctl_node;
 	char name[32];
-	int gpio_base, child;
+	int gpio_base, child, use_dfs;
 
 	if (of_compatible(sc->sc_node, keylargo) == -1)
 		return;
@@ -342,6 +344,7 @@
 	DPRINTF("gpio_base: %02x\n", gpio_base);
 
 	/* now look for voltage and bus speed gpios */
+	use_dfs = 0;
 	for (child = OF_child(node); child; child = OF_peer(child)) {
 
 		if (OF_getprop(child, "name", name, sizeof(name)) < 1)
@@ -358,14 +361,21 @@
 			DPRINTF("found voltage_gpio at %02x\n", reg[0]);
 			sc->sc_voltage = gpio_base + reg[0];
 		}
+		if (strcmp(name, "cpu-vcore-select") == 0) {
+			DPRINTF("found cpu-vcore-select at %02x\n", reg[0]);
+			sc->sc_voltage = gpio_base + reg[0];
+			/* frequency gpio is not needed, we use cpu's DFS */
+			use_dfs = 1;
+		}
 	}
 
-	if ((sc->sc_voltage < 0) || (sc->sc_busspeed < 0))
+	if ((sc->sc_voltage < 0) || (sc->sc_busspeed < 0 && !use_dfs))
 		return;
 
 	printf("%s: enabling Intrepid CPU speed control\n",
 	    sc->sc_dev.dv_xname);
 
+	sysctl_node = NULL;
 	sysctl_createv(NULL, 0, NULL, 
 	    (const struct sysctlnode **)&sysctl_node, 
 	    CTLFLAG_READWRITE | CTLFLAG_OWNDESC | CTLFLAG_IMMEDIATE,
@@ -380,15 +390,40 @@
 obio_set_cpu_speed(struct obio_softc *sc, int fast)
 {
 
-	if ((sc->sc_voltage < 0) || (sc->sc_busspeed < 0))
+	if (sc->sc_voltage < 0)
 		return;
 
-	if (fast) {
-		bus_space_write_1(sc->sc_tag, sc->sc_bh, sc->sc_voltage, 5);
-		bus_space_write_1(sc->sc_tag, sc->sc_bh, sc->sc_busspeed, 5);
-	} else {
-		bus_space_write_1(sc->sc_tag, sc->sc_bh, sc->sc_busspeed, 4);
-		bus_space_write_1(sc->sc_tag, sc->sc_bh, sc->sc_voltage, 4);
+	if (sc->sc_busspeed >= 0) {
+		/* set voltage and speed via gpio */
+		if (fast) {
+			bus_space_write_1(sc->sc_tag, sc->sc_bh,
+			    sc->sc_voltage, 5);
+			bus_space_write_1(sc->sc_tag, sc->sc_bh,
+			    sc->sc_busspeed, 5);
+		} else {
+			bus_space_write_1(sc->sc_tag, sc->sc_bh,
+			    sc->sc_busspeed, 4);
+			bus_space_write_1(sc->sc_tag, sc->sc_bh,
+			    sc->sc_voltage, 4);
+		}
+	}
+	else {
+		/* set voltage via gpio and speed via the 7447A's DFS bit */
+		if (fast) {
+			bus_space_write_1(sc->sc_tag, sc->sc_bh,
+			    sc->sc_voltage, 5);
+			DELAY(1000);
+		}
+
+		/* set DFS for all cpus */
+		cpu_set_dfs(fast ? 1 : 2);
+		DELAY(100);
+
+		if (!fast) {
+			bus_space_write_1(sc->sc_tag, sc->sc_bh,
+			    sc->sc_voltage, 4);
+			DELAY(1000);
+		}
 	}
 }
 
@@ -396,11 +431,16 @@
 obio_get_cpu_speed(struct obio_softc *sc)
 {
 	
-	if ((sc->sc_voltage < 0) || (sc->sc_busspeed < 0))
+	if (sc->sc_voltage < 0)
 		return 0;
 
-	if (bus_space_read_1(sc->sc_tag, sc->sc_bh, sc->sc_busspeed) & 1)
+	if (sc->sc_busspeed >= 0) {
+		if (bus_space_read_1(sc->sc_tag, sc->sc_bh, sc->sc_busspeed)
+		    & 1)
 		return 1;
+	}
+	else
+		return cpu_get_dfs() == 1;
 
 	return 0;
 }
@@ -421,7 +461,7 @@
 		node.sysctl_data = &speed;
 		if (sysctl_lookup(SYSCTLFN_CALL(&node)) == 0) {
 			int new_reg;
-			
+
 			new_reg = (max(0, min(1, node.sysctl_idata)));
 			obio_set_cpu_speed(sc, new_reg);
 			return 0;
@@ -434,4 +474,3 @@
 }
 
 #endif /* OBIO_SPEEDCONTROL */
-

Index: src/sys/arch/powerpc/include/cpu.h
diff -u src/sys/arch/powerpc/include/cpu.h:1.70 src/sys/arch/powerpc/include/cpu.h:1.71
--- src/sys/arch/powerpc/include/cpu.h:1.70	Sat Apr 24 09:39:57 2010
+++ src/sys/arch/powerpc/include/cpu.h	Wed Oct 20 18:52:33 2010
@@ -1,4 +1,4 @@
-/*	$NetBSD: cpu.h,v 1.70 2010/04/24 09:39:57 kiyohara Exp $	*/
+/*	$NetBSD: cpu.h,v 1.71 2010/10/20 18:52:33 phx Exp $	*/
 
 /*
  * Copyright (C) 1999 Wolfgang Solfrank.
@@ -353,6 +353,8 @@
 struct cpu_info *cpu_attach_common(struct device *, int);
 void cpu_setup(struct device *, struct cpu_info *);
 void cpu_identify(char *, size_t);
+int cpu_get_dfs(void);
+void cpu_set_dfs(int);
 void delay (unsigned int);
 void cpu_probe_cache(void);
 void dcache_flush_page(vaddr_t);

Index: src/sys/arch/powerpc/oea/cpu_subr.c
diff -u src/sys/arch/powerpc/oea/cpu_subr.c:1.55 src/sys/arch/powerpc/oea/cpu_subr.c:1.56
--- src/sys/arch/powerpc/oea/cpu_subr.c:1.55	Thu Feb 25 23:31:47 2010
+++ src/sys/arch/powerpc/oea/cpu_subr.c	Wed Oct 20 18:52:33 2010
@@ -1,4 +1,4 @@
-/*	$NetBSD: cpu_subr.c,v 1.55 2010/02/25 23:31:47 matt Exp $	*/
+/*	$NetBSD: cpu_subr.c,v 1.56 2010/10/20 18:52:33 phx Exp $	*/
 
 /*-
  * Copyright (c) 2001 Matt Thomas.
@@ -34,7 +34,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: cpu_subr.c,v 1.55 2010/02/25 23:31:47 matt Exp $");
+__KERNEL_RCSID(0, "$NetBSD: cpu_subr.c,v 1.56 2010/10/20 18:52:33 phx Exp $");
 
 #include "opt_ppcparam.h"
 #include "opt_multiprocessor.h"
@@ -47,6 +47,7 @@
 #include <sys/types.h>
 #include <sys/lwp.h>
 #include <sys/malloc.h>
+#include <sys/xcall.h>
 
 #include <uvm/uvm_extern.h>
 
@@ -64,6 +65,7 @@
 static void cpu_config_l3cr(int);
 static void cpu_probe_speed(struct cpu_info *);
 static void cpu_idlespin(void);
+static void cpu_set_dfs_xcall(void *, void *);
 #if NSYSMON_ENVSYS > 0
 static void cpu_tau_setup(struct cpu_info *);
 static void cpu_tau_refresh(struct sysmon_envsys *, envsys_data_t *);
@@ -984,7 +986,95 @@
 
 	mtspr(SPR_MMCR0, MMCR0_FC);
 
-	ci->ci_khz = cps / 1000;
+	ci->ci_khz = (cps * cpu_get_dfs()) / 1000;
+}
+
+/*
+ * Read the Dynamic Frequency Switching state and return a divisor for
+ * the maximum frequency.
+ */
+int
+cpu_get_dfs(void)
+{
+	u_int hid1, pvr, vers;
+
+	pvr = mfpvr();
+	vers = pvr >> 16;
+	hid1 = mfspr(SPR_HID1);
+
+	switch (vers) {
+	case MPC7448:
+		if (hid1 & HID1_DFS4)
+			return 4;
+	case MPC7447A:
+		if (hid1 & HID1_DFS2)
+			return 2;
+	}
+	return 1;
+}
+
+/*
+ * Set the Dynamic Frequency Switching divisor the same for all cpus.
+ */
+void
+cpu_set_dfs(int div)
+{
+	uint64_t where;
+	u_int dfs_mask, pvr, vers;
+
+	pvr = mfpvr();
+	vers = pvr >> 16;
+	dfs_mask = 0;
+
+	switch (vers) {
+	case MPC7448:
+		dfs_mask |= HID1_DFS4;
+	case MPC7447A:
+		dfs_mask |= HID1_DFS2;
+		break;
+	default:
+		printf("cpu_set_dfs: DFS not supported\n");
+		return;
+
+	}
+
+	where = xc_broadcast(0, (xcfunc_t)cpu_set_dfs_xcall, &div, &dfs_mask);
+	xc_wait(where);
+}
+
+static void
+cpu_set_dfs_xcall(void *arg1, void *arg2)
+{
+	u_int dfs_mask, hid1, old_hid1;
+	int *divisor, s;
+
+	divisor = arg1;
+	dfs_mask = *(u_int *)arg2;
+
+	s = splhigh();
+	hid1 = old_hid1 = mfspr(SPR_HID1);
+
+	switch (*divisor) {
+	case 1:
+		hid1 &= ~dfs_mask;
+		break;
+	case 2:
+		hid1 &= ~(dfs_mask & HID1_DFS4);
+		hid1 |= dfs_mask & HID1_DFS2;
+		break;
+	case 4:
+		hid1 &= ~(dfs_mask & HID1_DFS2);
+		hid1 |= dfs_mask & HID1_DFS4;
+		break;
+	}
+
+	if (hid1 != old_hid1) {
+		__asm volatile("sync");
+		mtspr(SPR_HID1, hid1);
+		__asm volatile("sync;isync");
+	}
+
+	splx(s);
 }
 
 #if NSYSMON_ENVSYS > 0

Reply via email to