summaryrefslogtreecommitdiffstats
path: root/sys/powerpc/powermac
diff options
context:
space:
mode:
authorjhibbits <jhibbits@FreeBSD.org>2013-12-13 02:37:35 +0000
committerjhibbits <jhibbits@FreeBSD.org>2013-12-13 02:37:35 +0000
commit7a0c976f7e101a3ce3af15861c1a9cd1bd8df649 (patch)
treea61e46af12f52b85be23d94481b87a41d65134bd /sys/powerpc/powermac
parentdb185b5f7ce440a45f9b33029ccdbfca7bac096a (diff)
downloadFreeBSD-src-7a0c976f7e101a3ce3af15861c1a9cd1bd8df649.zip
FreeBSD-src-7a0c976f7e101a3ce3af15861c1a9cd1bd8df649.tar.gz
Add PMU-based CPU frequency scaling. This method is used on most Titanium
PowerBooks. MFC after: 1 month
Diffstat (limited to 'sys/powerpc/powermac')
-rw-r--r--sys/powerpc/powermac/platform_powermac.c104
-rw-r--r--sys/powerpc/powermac/pmu.c121
-rw-r--r--sys/powerpc/powermac/pmuvar.h5
-rw-r--r--sys/powerpc/powermac/uninorth.c69
-rw-r--r--sys/powerpc/powermac/uninorthvar.h22
-rw-r--r--sys/powerpc/powermac/viareg.h4
6 files changed, 300 insertions, 25 deletions
diff --git a/sys/powerpc/powermac/platform_powermac.c b/sys/powerpc/powermac/platform_powermac.c
index 49f3e1a..fd61846 100644
--- a/sys/powerpc/powermac/platform_powermac.c
+++ b/sys/powerpc/powermac/platform_powermac.c
@@ -376,6 +376,110 @@ powermac_smp_start_cpu(platform_t plat, struct pcpu *pc)
#endif
}
+/* From p3-53 of the MPC7450 RISC Microprocessor Family Reference Manual */
+void
+flush_disable_caches(void)
+{
+ register_t msr;
+ register_t msscr0;
+ register_t cache_reg;
+ volatile uint32_t *memp;
+ uint32_t temp;
+ int i;
+ int x;
+
+ msr = mfmsr();
+ powerpc_sync();
+ mtmsr(msr & ~(PSL_EE | PSL_DR));
+ msscr0 = mfspr(SPR_MSSCR0);
+ msscr0 &= ~MSSCR0_L2PFE;
+ mtspr(SPR_MSSCR0, msscr0);
+ powerpc_sync();
+ isync();
+ __asm__ __volatile__("dssall; sync");
+ powerpc_sync();
+ isync();
+ __asm__ __volatile__("dcbf 0,%0" :: "r"(0));
+ __asm__ __volatile__("dcbf 0,%0" :: "r"(0));
+ __asm__ __volatile__("dcbf 0,%0" :: "r"(0));
+
+ /* Lock the L1 Data cache. */
+ mtspr(SPR_LDSTCR, mfspr(SPR_LDSTCR) | 0xFF);
+ powerpc_sync();
+ isync();
+
+ mtspr(SPR_LDSTCR, 0);
+
+ /*
+ * Perform this in two stages: Flush the cache starting in RAM, then do it
+ * from ROM.
+ */
+ memp = (volatile uint32_t *)0x00000000;
+ for (i = 0; i < 128 * 1024; i++) {
+ temp = *memp;
+ __asm__ __volatile__("dcbf 0,%0" :: "r"(memp));
+ memp += 32/sizeof(*memp);
+ }
+
+ memp = (volatile uint32_t *)0xfff00000;
+ x = 0xfe;
+
+ for (; x != 0xff;) {
+ mtspr(SPR_LDSTCR, x);
+ for (i = 0; i < 128; i++) {
+ temp = *memp;
+ __asm__ __volatile__("dcbf 0,%0" :: "r"(memp));
+ memp += 32/sizeof(*memp);
+ }
+ x = ((x << 1) | 1) & 0xff;
+ }
+ mtspr(SPR_LDSTCR, 0);
+
+ cache_reg = mfspr(SPR_L2CR);
+ if (cache_reg & L2CR_L2E) {
+ cache_reg &= ~(L2CR_L2IO_7450 | L2CR_L2DO_7450);
+ mtspr(SPR_L2CR, cache_reg);
+ powerpc_sync();
+ mtspr(SPR_L2CR, cache_reg | L2CR_L2HWF);
+ while (mfspr(SPR_L2CR) & L2CR_L2HWF)
+ ; /* Busy wait for cache to flush */
+ powerpc_sync();
+ cache_reg &= ~L2CR_L2E;
+ mtspr(SPR_L2CR, cache_reg);
+ powerpc_sync();
+ mtspr(SPR_L2CR, cache_reg | L2CR_L2I);
+ powerpc_sync();
+ while (mfspr(SPR_L2CR) & L2CR_L2I)
+ ; /* Busy wait for L2 cache invalidate */
+ powerpc_sync();
+ }
+
+ cache_reg = mfspr(SPR_L3CR);
+ if (cache_reg & L3CR_L3E) {
+ cache_reg &= ~(L3CR_L3IO | L3CR_L3DO);
+ mtspr(SPR_L3CR, cache_reg);
+ powerpc_sync();
+ mtspr(SPR_L3CR, cache_reg | L3CR_L3HWF);
+ while (mfspr(SPR_L3CR) & L3CR_L3HWF)
+ ; /* Busy wait for cache to flush */
+ powerpc_sync();
+ cache_reg &= ~L3CR_L3E;
+ mtspr(SPR_L3CR, cache_reg);
+ powerpc_sync();
+ mtspr(SPR_L3CR, cache_reg | L3CR_L3I);
+ powerpc_sync();
+ while (mfspr(SPR_L3CR) & L3CR_L3I)
+ ; /* Busy wait for L3 cache invalidate */
+ powerpc_sync();
+ }
+
+ mtspr(SPR_HID0, mfspr(SPR_HID0) & ~HID0_DCE);
+ powerpc_sync();
+ isync();
+
+ mtmsr(msr);
+}
+
static void
powermac_reset(platform_t platform)
{
diff --git a/sys/powerpc/powermac/pmu.c b/sys/powerpc/powermac/pmu.c
index 9777004..ca979d8 100644
--- a/sys/powerpc/powermac/pmu.c
+++ b/sys/powerpc/powermac/pmu.c
@@ -36,6 +36,7 @@ __FBSDID("$FreeBSD$");
#include <sys/conf.h>
#include <sys/kernel.h>
#include <sys/clock.h>
+#include <sys/proc.h>
#include <sys/reboot.h>
#include <sys/sysctl.h>
@@ -43,11 +44,18 @@ __FBSDID("$FreeBSD$");
#include <dev/ofw/openfirm.h>
#include <dev/led/led.h>
+#include <machine/_inttypes.h>
+#include <machine/altivec.h> /* For save_vec() */
#include <machine/bus.h>
+#include <machine/cpu.h>
+#include <machine/fpu.h> /* For save_fpu() */
+#include <machine/hid.h>
#include <machine/intr_machdep.h>
#include <machine/md_var.h>
+#include <machine/pcb.h>
#include <machine/pio.h>
#include <machine/resource.h>
+#include <machine/setjmp.h>
#include <vm/vm.h>
#include <vm/pmap.h>
@@ -59,6 +67,11 @@ __FBSDID("$FreeBSD$");
#include "clock_if.h"
#include "pmuvar.h"
#include "viareg.h"
+#include "uninorthvar.h" /* For unin_chip_sleep()/unin_chip_wake() */
+
+#define PMU_DEFAULTS PMU_INT_TICK | PMU_INT_ADB | \
+ PMU_INT_PCEJECT | PMU_INT_SNDBRT | \
+ PMU_INT_BATTERY | PMU_INT_ENVIRONMENT
/*
* Bus interface
@@ -93,6 +106,7 @@ static int pmu_acline_state(SYSCTL_HANDLER_ARGS);
static int pmu_query_battery(struct pmu_softc *sc, int batt,
struct pmu_battstate *info);
static int pmu_battquery_sysctl(SYSCTL_HANDLER_ARGS);
+static void pmu_sleep_int(void);
/*
* List of battery-related sysctls we might ask for
@@ -115,8 +129,6 @@ static device_method_t pmu_methods[] = {
DEVMETHOD(device_attach, pmu_attach),
DEVMETHOD(device_detach, pmu_detach),
DEVMETHOD(device_shutdown, bus_generic_shutdown),
- DEVMETHOD(device_suspend, bus_generic_suspend),
- DEVMETHOD(device_resume, bus_generic_resume),
/* ADB bus interface */
DEVMETHOD(adb_hb_send_raw_packet, pmu_adb_send),
@@ -193,7 +205,7 @@ static signed char pm_send_cmd_type[] = {
0x02, -1, -1, -1, -1, -1, -1, -1,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -1, -1,
0x01, 0x01, 0x01, -1, -1, -1, -1, -1,
- 0x00, 0x00, -1, -1, -1, -1, 0x04, 0x04,
+ 0x00, 0x00, -1, -1, -1, 0x05, 0x04, 0x04,
0x04, -1, 0x00, -1, -1, -1, -1, -1,
0x00, -1, -1, -1, -1, -1, -1, -1,
0x01, 0x02, -1, -1, -1, -1, -1, -1,
@@ -229,7 +241,7 @@ static signed char pm_receive_cmd_type[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x04, 0x04, 0x03, 0x09, -1, -1, -1, -1,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- -1, -1, -1, -1, -1, -1, 0x01, 0x01,
+ -1, -1, -1, -1, -1, 0x01, 0x01, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x06, -1, -1, -1, -1, -1, -1, -1,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -357,12 +369,13 @@ pmu_attach(device_t dev)
/* Init PMU */
- reg = PMU_INT_TICK | PMU_INT_ADB | PMU_INT_PCEJECT | PMU_INT_SNDBRT;
- reg |= PMU_INT_BATTERY;
- reg |= PMU_INT_ENVIRONMENT;
+ pmu_write_reg(sc, vBufB, pmu_read_reg(sc, vBufB) | vPB4);
+ pmu_write_reg(sc, vDirB, (pmu_read_reg(sc, vDirB) | vPB4) & ~vPB3);
+
+ reg = PMU_DEFAULTS;
pmu_send(sc, PMU_SET_IMASK, 1, &reg, 16, resp);
- pmu_write_reg(sc, vIER, 0x90); /* make sure VIA interrupts are on */
+ pmu_write_reg(sc, vIER, 0x94); /* make sure VIA interrupts are on */
pmu_send(sc, PMU_SYSTEM_READY, 1, cmd, 16, resp);
pmu_send(sc, PMU_GET_VERSION, 1, cmd, 16, resp);
@@ -1018,3 +1031,95 @@ pmu_settime(device_t dev, struct timespec *ts)
return (0);
}
+static register_t sprgs[4];
+static register_t srrs[2];
+extern void *ap_pcpu;
+
+void pmu_sleep_int(void)
+{
+ static u_quad_t timebase = 0;
+ jmp_buf resetjb;
+ struct thread *fputd;
+ struct thread *vectd;
+ register_t hid0;
+ register_t msr;
+ register_t saved_msr;
+
+ ap_pcpu = pcpup;
+
+ PCPU_SET(restore, &resetjb);
+
+ *(unsigned long *)0x80 = 0x100;
+ saved_msr = mfmsr();
+ fputd = PCPU_GET(fputhread);
+ vectd = PCPU_GET(vecthread);
+ if (fputd != NULL)
+ save_fpu(fputd);
+ if (vectd != NULL)
+ save_vec(vectd);
+ if (setjmp(resetjb) == 0) {
+ sprgs[0] = mfspr(SPR_SPRG0);
+ sprgs[1] = mfspr(SPR_SPRG1);
+ sprgs[2] = mfspr(SPR_SPRG2);
+ sprgs[3] = mfspr(SPR_SPRG3);
+ srrs[0] = mfspr(SPR_SRR0);
+ srrs[1] = mfspr(SPR_SRR1);
+ timebase = mftb();
+ powerpc_sync();
+ flush_disable_caches();
+ hid0 = mfspr(SPR_HID0);
+ hid0 = (hid0 & ~(HID0_DOZE | HID0_NAP)) | HID0_SLEEP;
+ powerpc_sync();
+ isync();
+ msr = mfmsr() | PSL_POW;
+ mtspr(SPR_HID0, hid0);
+ powerpc_sync();
+
+ while (1)
+ mtmsr(msr);
+ }
+ mttb(timebase);
+ PCPU_SET(curthread, curthread);
+ PCPU_SET(curpcb, curthread->td_pcb);
+ pmap_activate(curthread);
+ powerpc_sync();
+ mtspr(SPR_SPRG0, sprgs[0]);
+ mtspr(SPR_SPRG1, sprgs[1]);
+ mtspr(SPR_SPRG2, sprgs[2]);
+ mtspr(SPR_SPRG3, sprgs[3]);
+ mtspr(SPR_SRR0, srrs[0]);
+ mtspr(SPR_SRR1, srrs[1]);
+ mtmsr(saved_msr);
+ if (fputd == curthread)
+ enable_fpu(curthread);
+ if (vectd == curthread)
+ enable_vec(curthread);
+ powerpc_sync();
+}
+
+int
+pmu_set_speed(int low_speed)
+{
+ struct pmu_softc *sc;
+ uint8_t sleepcmd[] = {'W', 'O', 'O', 'F', 0};
+ uint8_t resp[16];
+
+ sc = device_get_softc(pmu);
+ pmu_write_reg(sc, vIER, 0x10);
+ spinlock_enter();
+ mtdec(0x7fffffff);
+ mb();
+ mtdec(0x7fffffff);
+
+ sleepcmd[4] = low_speed;
+ pmu_send(sc, PMU_CPU_SPEED, 5, sleepcmd, 16, resp);
+ unin_chip_sleep(NULL, 1);
+ pmu_sleep_int();
+ unin_chip_wake(NULL);
+
+ mtdec(1); /* Force a decrementer exception */
+ spinlock_exit();
+ pmu_write_reg(sc, vIER, 0x90);
+
+ return (0);
+}
diff --git a/sys/powerpc/powermac/pmuvar.h b/sys/powerpc/powermac/pmuvar.h
index 25a85c7..98209f8 100644
--- a/sys/powerpc/powermac/pmuvar.h
+++ b/sys/powerpc/powermac/pmuvar.h
@@ -160,7 +160,8 @@ struct pmu_softc {
volatile int sc_autopoll;
int sc_batteries;
struct cdev *sc_leddev;
- int lid_closed;
+ int lid_closed;
+ uint8_t saved_regs[9];
};
struct pmu_battstate {
@@ -172,4 +173,6 @@ struct pmu_battstate {
int voltage;
};
+int pmu_set_speed(int low_speed);
+
#endif /* PMUVAR_H */
diff --git a/sys/powerpc/powermac/uninorth.c b/sys/powerpc/powermac/uninorth.c
index 748148e..ab051a5 100644
--- a/sys/powerpc/powermac/uninorth.c
+++ b/sys/powerpc/powermac/uninorth.c
@@ -136,6 +136,13 @@ static driver_t unin_chip_driver = {
static devclass_t unin_chip_devclass;
+/*
+ * Assume there is only one unin chip in a PowerMac, so that pmu.c functions can
+ * suspend the chip after the whole rest of the device tree is suspended, not
+ * earlier.
+ */
+static device_t unin_chip;
+
DRIVER_MODULE(unin, nexus, unin_chip_driver, unin_chip_devclass, 0, 0);
/*
@@ -210,31 +217,30 @@ unin_chip_add_reg(phandle_t devnode, struct unin_chip_devinfo *dinfo)
}
static void
-unin_enable_gmac(device_t dev)
+unin_update_reg(device_t dev, uint32_t regoff, uint32_t set, uint32_t clr)
{
- volatile u_int *clkreg;
+ volatile u_int *reg;
struct unin_chip_softc *sc;
u_int32_t tmpl;
sc = device_get_softc(dev);
- clkreg = (void *)(sc->sc_addr + UNIN_CLOCKCNTL);
- tmpl = inl(clkreg);
- tmpl |= UNIN_CLOCKCNTL_GMAC;
- outl(clkreg, tmpl);
+ reg = (void *)(sc->sc_addr + regoff);
+ tmpl = inl(reg);
+ tmpl &= ~clr;
+ tmpl |= set;
+ outl(reg, tmpl);
}
static void
-unin_enable_mpic(device_t dev)
+unin_enable_gmac(device_t dev)
{
- volatile u_int *toggle;
- struct unin_chip_softc *sc;
- u_int32_t tmpl;
+ unin_update_reg(dev, UNIN_CLOCKCNTL, UNIN_CLOCKCNTL_GMAC, 0);
+}
- sc = device_get_softc(dev);
- toggle = (void *)(sc->sc_addr + UNIN_TOGGLE_REG);
- tmpl = inl(toggle);
- tmpl |= UNIN_MPIC_RESET | UNIN_MPIC_OUTPUT_ENABLE;
- outl(toggle, tmpl);
+static void
+unin_enable_mpic(device_t dev)
+{
+ unin_update_reg(dev, UNIN_TOGGLE_REG, UNIN_MPIC_RESET | UNIN_MPIC_OUTPUT_ENABLE, 0);
}
static int
@@ -311,6 +317,9 @@ unin_chip_attach(device_t dev)
return (error);
}
+ if (unin_chip == NULL)
+ unin_chip = dev;
+
/*
* Iterate through the sub-devices
*/
@@ -631,3 +640,33 @@ unin_chip_get_devinfo(device_t dev, device_t child)
return (&dinfo->udi_obdinfo);
}
+int
+unin_chip_wake(device_t dev)
+{
+
+ if (dev == NULL)
+ dev = unin_chip;
+ unin_update_reg(dev, UNIN_PWR_MGMT, UNIN_PWR_NORMAL, UNIN_PWR_MASK);
+ DELAY(10);
+ unin_update_reg(dev, UNIN_HWINIT_STATE, UNIN_RUNNING, 0);
+ DELAY(100);
+
+ return (0);
+}
+
+int
+unin_chip_sleep(device_t dev, int idle)
+{
+ if (dev == NULL)
+ dev = unin_chip;
+
+ unin_update_reg(dev, UNIN_HWINIT_STATE, UNIN_SLEEPING, 0);
+ DELAY(10);
+ if (idle)
+ unin_update_reg(dev, UNIN_PWR_MGMT, UNIN_PWR_IDLE2, UNIN_PWR_MASK);
+ else
+ unin_update_reg(dev, UNIN_PWR_MGMT, UNIN_PWR_SLEEP, UNIN_PWR_MASK);
+ DELAY(10);
+
+ return (0);
+}
diff --git a/sys/powerpc/powermac/uninorthvar.h b/sys/powerpc/powermac/uninorthvar.h
index aa834f6..e08478d 100644
--- a/sys/powerpc/powermac/uninorthvar.h
+++ b/sys/powerpc/powermac/uninorthvar.h
@@ -28,6 +28,8 @@
#ifndef _POWERPC_POWERMAC_UNINORTHVAR_H_
#define _POWERPC_POWERMAC_UNINORTHVAR_H_
+#include <dev/ofw/ofw_bus_subr.h>
+#include <dev/ofw/ofw_pci.h>
#include <powerpc/ofw/ofw_pci.h>
struct uninorth_softc {
@@ -76,10 +78,30 @@ struct unin_chip_devinfo {
#define UNIN_CLOCKCNTL_GMAC 0x2
/*
+ * Power management register
+ */
+#define UNIN_PWR_MGMT 0x30
+#define UNIN_PWR_NORMAL 0x00
+#define UNIN_PWR_IDLE2 0x01
+#define UNIN_PWR_SLEEP 0x02
+#define UNIN_PWR_SAVE 0x03
+#define UNIN_PWR_MASK 0x03
+
+/*
+ * Hardware initialization state register
+ */
+#define UNIN_HWINIT_STATE 0x70
+#define UNIN_SLEEPING 0x01
+#define UNIN_RUNNING 0x02
+
+
+/*
* Toggle registers
*/
#define UNIN_TOGGLE_REG 0xe0
#define UNIN_MPIC_RESET 0x2
#define UNIN_MPIC_OUTPUT_ENABLE 0x4
+extern int unin_chip_sleep(device_t dev, int idle);
+extern int unin_chip_wake(device_t dev);
#endif /* _POWERPC_POWERMAC_UNINORTHVAR_H_ */
diff --git a/sys/powerpc/powermac/viareg.h b/sys/powerpc/powermac/viareg.h
index 85739cd..2f519b2 100644
--- a/sys/powerpc/powermac/viareg.h
+++ b/sys/powerpc/powermac/viareg.h
@@ -30,14 +30,16 @@
/* VIA interface registers */
#define vBufB 0x0000 /* register B */
-#define vBufA 0x0200 /* register A */
#define vDirB 0x0400 /* data direction register */
#define vDirA 0x0600 /* data direction register */
+#define vT1C 0x0800 /* Timer 1 counter Lo */
+#define vT1CH 0x0a00 /* Timer 1 counter Hi */
#define vSR 0x1400 /* shift register */
#define vACR 0x1600 /* aux control register */
#define vPCR 0x1800 /* peripheral control register */
#define vIFR 0x1a00 /* interrupt flag register */
#define vIER 0x1c00 /* interrupt enable register */
+#define vBufA 0x1e00 /* register A */
#define vPB 0x0000
#define vPB3 0x08
OpenPOWER on IntegriCloud