svn commit: r244868 - in projects/pmac_pmu/sys: conf powerpc/aim powerpc/cpufreq powerpc/include powerpc/powermac powerpc/powerpc
Justin Hibbits
jhibbits at FreeBSD.org
Sun Dec 30 17:28:11 UTC 2012
Author: jhibbits
Date: Sun Dec 30 17:28:08 2012
New Revision: 244868
URL: http://svnweb.freebsd.org/changeset/base/244868
Log:
Initial changes for PMU sleep/speed-change/resume. With this code, the PMU
successfully changes speed on the CPU (MPC745x-based machines), but upon wake-up
the machine panics.
Added:
projects/pmac_pmu/sys/powerpc/cpufreq/pmufreq.c
Modified:
projects/pmac_pmu/sys/conf/files.powerpc
projects/pmac_pmu/sys/powerpc/aim/mp_cpudep.c
projects/pmac_pmu/sys/powerpc/include/cpu.h
projects/pmac_pmu/sys/powerpc/include/pcpu.h
projects/pmac_pmu/sys/powerpc/include/spr.h
projects/pmac_pmu/sys/powerpc/powermac/macio.c
projects/pmac_pmu/sys/powerpc/powermac/maciovar.h
projects/pmac_pmu/sys/powerpc/powermac/platform_powermac.c
projects/pmac_pmu/sys/powerpc/powermac/pmu.c
projects/pmac_pmu/sys/powerpc/powermac/pmuvar.h
projects/pmac_pmu/sys/powerpc/powermac/uninorth.c
projects/pmac_pmu/sys/powerpc/powermac/uninorthpci.c
projects/pmac_pmu/sys/powerpc/powermac/uninorthvar.h
projects/pmac_pmu/sys/powerpc/powermac/viareg.h
projects/pmac_pmu/sys/powerpc/powerpc/mp_machdep.c
Modified: projects/pmac_pmu/sys/conf/files.powerpc
==============================================================================
--- projects/pmac_pmu/sys/conf/files.powerpc Sun Dec 30 16:41:17 2012 (r244867)
+++ projects/pmac_pmu/sys/conf/files.powerpc Sun Dec 30 17:28:08 2012 (r244868)
@@ -116,6 +116,7 @@ powerpc/booke/trap.c optional booke
powerpc/booke/vm_machdep.c optional booke
powerpc/cpufreq/dfs.c optional cpufreq
powerpc/cpufreq/pcr.c optional cpufreq aim
+powerpc/cpufreq/pmufreq.c optional cpufreq aim
powerpc/fpu/fpu_add.c optional fpu_emu powerpc
powerpc/fpu/fpu_compare.c optional fpu_emu powerpc
powerpc/fpu/fpu_div.c optional fpu_emu powerpc
Modified: projects/pmac_pmu/sys/powerpc/aim/mp_cpudep.c
==============================================================================
--- projects/pmac_pmu/sys/powerpc/aim/mp_cpudep.c Sun Dec 30 16:41:17 2012 (r244867)
+++ projects/pmac_pmu/sys/powerpc/aim/mp_cpudep.c Sun Dec 30 17:28:08 2012 (r244868)
@@ -322,17 +322,13 @@ cpudep_ap_setup()
mtspr(SPR_CELL_TSRL, bsp_state[5]);
break;
- case MPC7450:
- case MPC7455:
- case MPC7457:
- /* Only MPC745x CPUs have an L3 cache. */
- reg = mpc745x_l3_enable(bsp_state[3]);
-
- /* Fallthrough */
case MPC7400:
case MPC7410:
case MPC7447A:
case MPC7448:
+ case MPC7450:
+ case MPC7455:
+ case MPC7457:
/* XXX: Program the CPU ID into PIR */
__asm __volatile("mtspr 1023,%0" :: "r"(PCPU_GET(cpuid)));
@@ -342,6 +338,17 @@ cpudep_ap_setup()
mtspr(SPR_HID0, bsp_state[0]); isync();
mtspr(SPR_HID1, bsp_state[1]); isync();
+ /* Now enable the L3 cache. */
+ switch (vers) {
+ case MPC7450:
+ case MPC7455:
+ case MPC7457:
+ /* Only MPC745x CPUs have an L3 cache. */
+ reg = mpc745x_l3_enable(bsp_state[3]);
+ default:
+ break;
+ }
+
reg = mpc74xx_l2_enable(bsp_state[2]);
reg = mpc74xx_l1d_enable();
reg = mpc74xx_l1i_enable();
Added: projects/pmac_pmu/sys/powerpc/cpufreq/pmufreq.c
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ projects/pmac_pmu/sys/powerpc/cpufreq/pmufreq.c Sun Dec 30 17:28:08 2012 (r244868)
@@ -0,0 +1,226 @@
+/*-
+ * Copyright (c) 2011 Justin Hibbits
+ * Copyright (c) 2009 Nathan Whitehorn
+ * All rights reserved.
+ *
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: head/sys/powerpc/cpufreq/pmufreq.c 217065 2011-01-06 20:19:01Z andreast $");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/cpu.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/openfirm.h>
+
+#include "cpufreq_if.h"
+#include "powerpc/powermac/pmuvar.h"
+
+struct pmufreq_softc {
+ device_t dev;
+ uint32_t minfreq;
+ uint32_t maxfreq;
+ uint32_t curfreq;
+};
+
+static void pmufreq_identify(driver_t *driver, device_t parent);
+static int pmufreq_probe(device_t dev);
+static int pmufreq_attach(device_t dev);
+static int pmufreq_settings(device_t dev, struct cf_setting *sets, int *count);
+static int pmufreq_set(device_t dev, const struct cf_setting *set);
+static int pmufreq_get(device_t dev, struct cf_setting *set);
+static int pmufreq_type(device_t dev, int *type);
+
+static device_method_t pmufreq_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_identify, pmufreq_identify),
+ DEVMETHOD(device_probe, pmufreq_probe),
+ DEVMETHOD(device_attach, pmufreq_attach),
+
+ /* cpufreq interface */
+ DEVMETHOD(cpufreq_drv_set, pmufreq_set),
+ DEVMETHOD(cpufreq_drv_get, pmufreq_get),
+ DEVMETHOD(cpufreq_drv_type, pmufreq_type),
+ DEVMETHOD(cpufreq_drv_settings, pmufreq_settings),
+
+ {0, 0}
+};
+
+static driver_t pmufreq_driver = {
+ "pmufreq",
+ pmufreq_methods,
+ sizeof(struct pmufreq_softc)
+};
+
+static devclass_t pmufreq_devclass;
+DRIVER_MODULE(pmufreq, cpu, pmufreq_driver, pmufreq_devclass, 0, 0);
+
+static void
+pmufreq_identify(driver_t *driver, device_t parent)
+{
+ uint16_t vers;
+ vers = mfpvr() >> 16;
+
+ /* Check for an MPC7455 CPU */
+ switch (vers) {
+ case MPC7455:
+ break;
+ default:
+ return;
+ }
+
+ /* Make sure we're not being doubly invoked. */
+ if (device_find_child(parent, "pmufreq", -1) != NULL)
+ return;
+
+ /*
+ * We attach a child for every CPU since settings need to
+ * be performed on every CPU in the SMP case.
+ */
+ if (BUS_ADD_CHILD(parent, 10, "pmufreq", -1) == NULL)
+ device_printf(parent, "add pmufreq child failed\n");
+}
+
+static int
+pmufreq_probe(device_t dev)
+{
+ uint32_t min_freq;
+ struct pmufreq_softc *sc;
+ phandle_t node;
+
+ if (resource_disabled("pmufreq", 0))
+ return (ENXIO);
+
+ sc = device_get_softc(dev);
+ node = ofw_bus_get_node(device_get_parent(dev));
+ /*
+ * A scalable MPC7455 has min-clock-frequency/max-clock-frequency as OFW
+ * properties of the 'cpu' node.
+ */
+ if (OF_getprop(node, "min-clock-frequency", &min_freq, sizeof(min_freq)) == -1)
+ return (ENXIO);
+ device_set_desc(dev, "PMU-based frequency scaling");
+ return (0);
+}
+
+static int
+pmufreq_attach(device_t dev)
+{
+ struct pmufreq_softc *sc;
+ phandle_t node;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+
+ node = ofw_bus_get_node(device_get_parent(dev));
+ OF_getprop(node, "min-clock-frequency", &sc->minfreq, sizeof(sc->minfreq));
+ OF_getprop(node, "max-clock-frequency", &sc->maxfreq, sizeof(sc->maxfreq));
+ OF_getprop(node, "rounded-clock-frequency", &sc->curfreq, sizeof(sc->curfreq));
+ sc->minfreq /= 1000000;
+ sc->maxfreq /= 1000000;
+ sc->curfreq /= 1000000;
+
+ cpufreq_register(dev);
+ return (0);
+}
+
+static int
+pmufreq_settings(device_t dev, struct cf_setting *sets, int *count)
+{
+ struct pmufreq_softc *sc;
+
+ sc = device_get_softc(dev);
+ if (sets == NULL || count == NULL)
+ return (EINVAL);
+ if (*count < 2)
+ return (E2BIG);
+
+ /* Return a list of valid settings for this driver. */
+ memset(sets, CPUFREQ_VAL_UNKNOWN, sizeof(*sets) * 2);
+
+ sets[0].freq = sc->maxfreq; sets[0].dev = dev;
+ sets[1].freq = sc->minfreq; sets[1].dev = dev;
+ /* Set high latency for CPU frequency changes, it's a tedious process. */
+ sets[0].lat = 1000000;
+ sets[1].lat = 1000000;
+ *count = 2;
+
+ return (0);
+}
+
+static int
+pmufreq_set(device_t dev, const struct cf_setting *set)
+{
+ struct pmufreq_softc *sc;
+ int speed_sel;
+ int error;
+
+ if (set == NULL)
+ return (EINVAL);
+
+ sc = device_get_softc(dev);
+
+ if (set->freq == sc->maxfreq)
+ speed_sel = 1;
+ else
+ speed_sel = 0;
+
+ error = pmu_set_speed(speed_sel);
+ if (error == 0)
+ sc->curfreq = set->freq;
+
+ printf("exit from here\n");
+ return error;
+}
+
+static int
+pmufreq_get(device_t dev, struct cf_setting *set)
+{
+ struct pmufreq_softc *sc;
+
+ if (set == NULL)
+ return (EINVAL);
+ sc = device_get_softc(dev);
+
+ set->freq = sc->curfreq;
+ set->dev = dev;
+
+ return (0);
+}
+
+static int
+pmufreq_type(device_t dev, int *type)
+{
+
+ if (type == NULL)
+ return (EINVAL);
+
+ *type = CPUFREQ_TYPE_ABSOLUTE;
+ return (0);
+}
+
Modified: projects/pmac_pmu/sys/powerpc/include/cpu.h
==============================================================================
--- projects/pmac_pmu/sys/powerpc/include/cpu.h Sun Dec 30 16:41:17 2012 (r244867)
+++ projects/pmac_pmu/sys/powerpc/include/cpu.h Sun Dec 30 17:28:08 2012 (r244868)
@@ -98,4 +98,6 @@ void cpu_reset(void);
void fork_trampoline(void);
void swi_vm(void *);
+void flush_disable_caches(void);
+
#endif /* _MACHINE_CPU_H_ */
Modified: projects/pmac_pmu/sys/powerpc/include/pcpu.h
==============================================================================
--- projects/pmac_pmu/sys/powerpc/include/pcpu.h Sun Dec 30 16:41:17 2012 (r244867)
+++ projects/pmac_pmu/sys/powerpc/include/pcpu.h Sun Dec 30 17:28:08 2012 (r244868)
@@ -49,7 +49,8 @@ struct pmap;
uint32_t pc_ipimask; \
register_t pc_tempsave[CPUSAVE_LEN]; \
register_t pc_disisave[CPUSAVE_LEN]; \
- register_t pc_dbsave[CPUSAVE_LEN];
+ register_t pc_dbsave[CPUSAVE_LEN]; \
+ void *pc_restore;
#define PCPU_MD_AIM32_FIELDS
Modified: projects/pmac_pmu/sys/powerpc/include/spr.h
==============================================================================
--- projects/pmac_pmu/sys/powerpc/include/spr.h Sun Dec 30 16:41:17 2012 (r244867)
+++ projects/pmac_pmu/sys/powerpc/include/spr.h Sun Dec 30 17:28:08 2012 (r244868)
@@ -518,6 +518,8 @@
#define MSSCR0_EMODE 0x00200000 /* 10: MPX bus mode (read-only) */
#define MSSCR0_ABD 0x00100000 /* 11: address bus driven (read-only) */
#define MSSCR0_MBZ 0x000fffff /* 12-31: must be zero */
+#define MSSCR0_L2PFE 0x00000003 /* 30-31: L2 prefetch enable */
+#define SPR_LDSTCR 0x3f8 /* .6. Load/Store Control Register */
#define SPR_L2PM 0x3f8 /* .6. L2 Private Memory Control Register */
#define SPR_L2CR 0x3f9 /* .6. L2 Control Register */
#define L2CR_L2E 0x80000000 /* 0: L2 enable */
@@ -542,12 +544,14 @@
Setting this bit disables instruction
caching. */
#define L2CR_L2I 0x00200000 /* 10: L2 global invalidate. */
+#define L2CR_L2IO_7450 0x00010000 /* 11: L2 instruction-only (MPC745x). */
#define L2CR_L2CTL 0x00100000 /* 11: L2 RAM control (ZZ enable).
Enables automatic operation of the
L2ZZ (low-power mode) signal. */
#define L2CR_L2WT 0x00080000 /* 12: L2 write-through. */
#define L2CR_L2TS 0x00040000 /* 13: L2 test support. */
#define L2CR_L2OH 0x00030000 /* 14-15: L2 output hold. */
+#define L2CR_L2DO_7450 0x00010000 /* 15: L2 data-only (MPC745x). */
#define L2CR_L2SL 0x00008000 /* 16: L2 DLL slow. */
#define L2CR_L2DF 0x00004000 /* 17: L2 differential clock. */
#define L2CR_L2BYP 0x00002000 /* 18: L2 DLL bypass. */
Modified: projects/pmac_pmu/sys/powerpc/powermac/macio.c
==============================================================================
--- projects/pmac_pmu/sys/powerpc/powermac/macio.c Sun Dec 30 16:41:17 2012 (r244867)
+++ projects/pmac_pmu/sys/powerpc/powermac/macio.c Sun Dec 30 17:28:08 2012 (r244868)
@@ -69,12 +69,17 @@ struct macio_softc {
/* FCR registers */
int sc_memrid;
struct resource *sc_memr;
+ int sc_rev;
+ int sc_devid;
+ uint32_t saved_fcrs[6];
};
static MALLOC_DEFINE(M_MACIO, "macio", "macio device information");
static int macio_probe(device_t);
static int macio_attach(device_t);
+static int macio_suspend(device_t);
+static int macio_resume(device_t);
static int macio_print_child(device_t dev, device_t child);
static void macio_probe_nomatch(device_t, device_t);
static struct resource *macio_alloc_resource(device_t, device_t, int, int *,
@@ -97,8 +102,8 @@ static device_method_t macio_methods[] =
DEVMETHOD(device_attach, macio_attach),
DEVMETHOD(device_detach, bus_generic_detach),
DEVMETHOD(device_shutdown, bus_generic_shutdown),
- DEVMETHOD(device_suspend, bus_generic_suspend),
- DEVMETHOD(device_resume, bus_generic_resume),
+ DEVMETHOD(device_suspend, macio_suspend),
+ DEVMETHOD(device_resume, macio_resume),
/* Bus interface */
DEVMETHOD(bus_print_child, macio_print_child),
@@ -319,6 +324,13 @@ macio_attach(device_t dev)
}
/*
+ * If possible, get the device ID and revision ID.
+ */
+ OF_getprop(root, "revision-id", &sc->sc_rev, sizeof(sc->sc_rev));
+ OF_getprop(root, "device-id", &sc->sc_devid, sizeof(sc->sc_devid));
+
+
+ /*
* Iterate through the sub-devices
*/
for (child = OF_child(root); child != 0; child = OF_peer(child)) {
@@ -605,3 +617,96 @@ macio_get_devinfo(device_t dev, device_t
dinfo = device_get_ivars(child);
return (&dinfo->mdi_obdinfo);
}
+
+static int macio_suspend(device_t dev)
+{
+ int error;
+ uint32_t temp;
+ struct macio_softc *sc = device_get_softc(dev);
+
+ error = bus_generic_suspend(dev);
+
+ if (error)
+ return (error);
+
+ sc->saved_fcrs[0] = bus_read_4(sc->sc_memr, KEYLARGO_FCR0);
+ sc->saved_fcrs[1] = bus_read_4(sc->sc_memr, KEYLARGO_FCR1);
+ sc->saved_fcrs[2] = bus_read_4(sc->sc_memr, KEYLARGO_FCR2);
+ sc->saved_fcrs[3] = bus_read_4(sc->sc_memr, KEYLARGO_FCR3);
+
+ temp = bus_read_4(sc->sc_memr, KEYLARGO_FCR0);
+ temp |= FCR0_USB_REF_SUSPEND;
+ temp &= ~(FCR0_SCCA_ENABLE | FCR0_SCCB_ENABLE |
+ FCR0_SCC_CELL_ENABLE | FCR0_IRDA_ENABLE |
+ FCR0_IRDA_CLK32_ENABLE |
+ FCR0_IRDA_CLK19_ENABLE);
+ bus_write_4(sc->sc_memr, KEYLARGO_FCR0, temp);
+ DELAY(1000);
+
+ if (sc->sc_devid == 0x22) {
+ temp = bus_read_4(sc->sc_memr, KEYLARGO_MEDIABAY);
+ temp |= KEYLARGO_MB0_DEV_ENABLE;
+ bus_write_4(sc->sc_memr, KEYLARGO_MEDIABAY, temp);
+ }
+
+ temp = bus_read_4(sc->sc_memr, KEYLARGO_FCR1);
+ temp &= ~(FCR1_AUDIO_SEL_22MCLK | FCR1_AUDIO_CLK_ENABLE |
+ FCR1_AUDIO_CLKOUT_ENABLE | FCR1_AUDIO_CELL_ENABLE |
+ FCR1_I2S0_CELL_ENABLE | FCR1_I2S0_CLK_ENABLE |
+ FCR1_I2S0_ENABLE |
+ FCR1_I2S1_CELL_ENABLE | FCR1_I2S1_CLK_ENABLE |
+ FCR1_I2S1_ENABLE |
+ FCR1_EIDE0_ENABLE | FCR1_EIDE0_RESET |
+ FCR1_EIDE1_ENABLE | FCR1_EIDE1_RESET |
+ FCR1_UIDE_ENABLE
+ );
+ bus_write_4(sc->sc_memr, KEYLARGO_FCR1, temp);
+
+ temp = bus_read_4(sc->sc_memr, KEYLARGO_FCR2);
+ temp &= ~FCR2_IOBUS_ENABLE;
+ bus_write_4(sc->sc_memr, KEYLARGO_FCR2, temp);
+
+ temp = bus_read_4(sc->sc_memr, KEYLARGO_FCR3);
+ temp |= (FCR3_SHUTDOWN_PLL_KW6 | FCR3_SHUTDOWN_PLL_KW4 |
+ FCR3_SHUTDOWN_PLL_KW35 | FCR3_SHUTDOWN_PLL_KW12);
+ temp &= ~(FCR3_CLK_66_ENABLE | FCR3_CLK_49_ENABLE |
+ FCR3_CLK_45_ENABLE | FCR3_CLK_31_ENABLE |
+ FCR3_TMR_CLK18_ENABLE | FCR3_I2S1_CLK18_ENABLE |
+ FCR3_I2S0_CLK18_ENABLE | FCR3_VA_CLK16_ENABLE);
+ if (sc->sc_rev >= 2)
+ temp |= (FCR3_SHUTDOWN_PLL_2X | FCR3_SHUTDOWN_PLL_TOTAL);
+ bus_write_4(sc->sc_memr, KEYLARGO_FCR3, temp);
+ /* Delay for a millisecond to let things settle. */
+ temp = bus_read_4(sc->sc_memr, KEYLARGO_FCR0);
+ DELAY(1000);
+
+ return (0);
+}
+
+static int macio_resume(device_t dev)
+{
+ struct macio_softc *sc = device_get_softc(dev);
+
+ bus_write_4(sc->sc_memr, KEYLARGO_FCR0, sc->saved_fcrs[0]);
+ bus_read_4(sc->sc_memr, KEYLARGO_FCR0);
+ DELAY(10);
+ bus_write_4(sc->sc_memr, KEYLARGO_FCR1, sc->saved_fcrs[1]);
+ bus_read_4(sc->sc_memr, KEYLARGO_FCR1);
+ DELAY(10);
+ bus_write_4(sc->sc_memr, KEYLARGO_FCR2, sc->saved_fcrs[2]);
+ bus_read_4(sc->sc_memr, KEYLARGO_FCR2);
+ DELAY(10);
+ bus_write_4(sc->sc_memr, KEYLARGO_FCR3, sc->saved_fcrs[3]);
+ bus_read_4(sc->sc_memr, KEYLARGO_FCR3);
+ DELAY(10);
+ bus_write_4(sc->sc_memr, KEYLARGO_FCR4, sc->saved_fcrs[4]);
+ bus_read_4(sc->sc_memr, KEYLARGO_FCR4);
+ DELAY(10);
+ bus_write_4(sc->sc_memr, KEYLARGO_FCR5, sc->saved_fcrs[5]);
+ bus_read_4(sc->sc_memr, KEYLARGO_FCR5);
+ DELAY(10);
+
+ bus_generic_resume(dev);
+
+ return (0);
+}
Modified: projects/pmac_pmu/sys/powerpc/powermac/maciovar.h
==============================================================================
--- projects/pmac_pmu/sys/powerpc/powermac/maciovar.h Sun Dec 30 16:41:17 2012 (r244867)
+++ projects/pmac_pmu/sys/powerpc/powermac/maciovar.h Sun Dec 30 17:28:08 2012 (r244868)
@@ -43,10 +43,77 @@
#define HEATHROW_FCR 0x38
#define KEYLARGO_FCR0 0x38
#define KEYLARGO_FCR1 0x3c
+#define KEYLARGO_FCR2 0x40
+#define KEYLARGO_FCR3 0x44
+#define KEYLARGO_FCR4 0x48
+#define KEYLARGO_FCR5 0x4c
#define FCR_ENET_ENABLE 0x60000000
#define FCR_ENET_RESET 0x80000000
+#define KEYLARGO_MEDIABAY 0x34
+#define KEYLARGO_MB0_DEV_ENABLE 0x00001000
+#define KEYLARGO_MB0_DEV_POWER 0x00000400
+#define KEYLARGO_MB0_DEV_RESET 0x00000200
+#define KEYLARGO_MB0_ENABLE 0x00000100
+#define KEYLARGO_MB1_DEV_ENABLE 0x10000000
+#define KEYLARGO_MB1_DEV_POWER 0x04000000
+#define KEYLARGO_MB1_DEV_RESET 0x02000000
+#define KEYLARGO_MB1_ENABLE 0x01000000
+
+#define KEYLARGO_GPIO_BASE 0x6a
+#define KEYLARGO_GPIO_LEVELS_0 0x50
+#define KEYLARGO_GPIO_LEVELS_1 0x54
+
+#define KEYLARGO_EXTINT_GPIO_REG_BASE 0x58
+
+#define FCR0_CHOOSE_SCCB 0x00000001
+#define FCR0_CHOOSE_SCCA 0x00000002
+#define FCR0_SLOW_SCC_PCLK 0x00000004
+#define FCR0_RESET_SCC 0x00000008
+#define FCR0_SCCA_ENABLE 0x00000010
+#define FCR0_SCCB_ENABLE 0x00000020
+#define FCR0_SCC_CELL_ENABLE 0x00000040
+#define FCR0_IRDA_ENABLE 0x00008000
+#define FCR0_IRDA_CLK32_ENABLE 0x00010000
+#define FCR0_IRDA_CLK19_ENABLE 0x00020000
+
+#define FCR0_USB_REF_SUSPEND 0x10000000
+
+#define FCR1_AUDIO_SEL_22MCLK 0x00000002
+#define FCR1_AUDIO_CLK_ENABLE 0x00000008
+#define FCR1_AUDIO_CLKOUT_ENABLE 0x00000020
+#define FCR1_AUDIO_CELL_ENABLE 0x00000040
+#define FCR1_I2S0_CELL_ENABLE 0x00000400
+#define FCR1_I2S0_CLK_ENABLE 0x00001000
+#define FCR1_I2S0_ENABLE 0x00002000
+#define FCR1_I2S1_CELL_ENABLE 0x00020000
+#define FCR1_I2S1_CLK_ENABLE 0x00080000
+#define FCR1_I2S1_ENABLE 0x00100000
+#define FCR1_EIDE0_ENABLE 0x00800000
+#define FCR1_EIDE0_RESET 0x01000000
+#define FCR1_EIDE1_ENABLE 0x04000000
+#define FCR1_EIDE1_RESET 0x08000000
+#define FCR1_UIDE_ENABLE 0x20000000
+#define FCR1_UIDE_RESET 0x40000000
+
+#define FCR2_IOBUS_ENABLE 0x00000002
+
+#define FCR3_SHUTDOWN_PLL_TOTAL 0x00000001
+#define FCR3_SHUTDOWN_PLL_KW6 0x00000002
+#define FCR3_SHUTDOWN_PLL_KW4 0x00000004
+#define FCR3_SHUTDOWN_PLL_KW35 0x00000008
+#define FCR3_SHUTDOWN_PLL_KW12 0x00000010
+#define FCR3_SHUTDOWN_PLL_2X 0x00000080
+#define FCR3_CLK_66_ENABLE 0x00000100
+#define FCR3_CLK_49_ENABLE 0x00000200
+#define FCR3_CLK_45_ENABLE 0x00000400
+#define FCR3_CLK_31_ENABLE 0x00000800
+#define FCR3_TMR_CLK18_ENABLE 0x00001000
+#define FCR3_I2S1_CLK18_ENABLE 0x00002000
+#define FCR3_I2S0_CLK18_ENABLE 0x00004000
+#define FCR3_VA_CLK16_ENABLE 0x00008000
+
/*
* Format of a macio reg property entry.
*/
Modified: projects/pmac_pmu/sys/powerpc/powermac/platform_powermac.c
==============================================================================
--- projects/pmac_pmu/sys/powerpc/powermac/platform_powermac.c Sun Dec 30 16:41:17 2012 (r244867)
+++ projects/pmac_pmu/sys/powerpc/powermac/platform_powermac.c Sun Dec 30 17:28:08 2012 (r244868)
@@ -280,6 +280,97 @@ powermac_smp_start_cpu(platform_t plat,
#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 *romp;
+ 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);
+
+ romp = (uint32_t *)0xfff00000;
+ x = 0xfe;
+
+ for (; x != 0xff;) {
+ mtspr(SPR_LDSTCR, x);
+ for (i = 0; i < 128; i++) {
+ temp = *romp;
+ romp += 32/sizeof(*romp);
+ }
+ x = ((x << 1) | 1) & 0xff;
+ }
+
+ 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)
{
Modified: projects/pmac_pmu/sys/powerpc/powermac/pmu.c
==============================================================================
--- projects/pmac_pmu/sys/powerpc/powermac/pmu.c Sun Dec 30 16:41:17 2012 (r244867)
+++ projects/pmac_pmu/sys/powerpc/powermac/pmu.c Sun Dec 30 17:28:08 2012 (r244868)
@@ -43,11 +43,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>
@@ -60,6 +67,10 @@ __FBSDID("$FreeBSD$");
#include "pmuvar.h"
#include "viareg.h"
+#define PMU_DEFAULTS PMU_INT_TICK | PMU_INT_ADB | \
+ PMU_INT_PCEJECT | PMU_INT_SNDBRT | \
+ PMU_INT_BATTERY | PMU_INT_ENVIRONMENT
+
/*
* Bus interface
*/
@@ -89,10 +100,13 @@ static u_int pmu_poll(device_t dev);
static void pmu_shutdown(void *xsc, int howto);
static void pmu_set_sleepled(void *xsc, int onoff);
static int pmu_server_mode(SYSCTL_HANDLER_ARGS);
+static int pmu_sleep(SYSCTL_HANDLER_ARGS);
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_restore_state(struct pmu_softc *sc);
+static void pmu_save_state(struct pmu_softc *sc);
/*
* List of battery-related sysctls we might ask for
@@ -193,7 +207,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 +243,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 +371,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, ®, 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);
@@ -407,6 +422,10 @@ pmu_attach(device_t dev)
"server_mode", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
pmu_server_mode, "I", "Enable reboot after power failure");
+ SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+ "sleep", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
+ pmu_sleep, "I", "Put the machine to sleep");
+
if (sc->sc_batteries > 0) {
struct sysctl_oid *oid, *battroot;
char battnum[2];
@@ -464,6 +483,7 @@ pmu_attach(device_t dev)
}
}
+ sc->lid_closed = 0;
/*
* Set up LED interface
*/
@@ -515,6 +535,34 @@ pmu_write_reg(struct pmu_softc *sc, u_in
bus_write_1(sc->sc_memr, offset, value);
}
+static void
+pmu_save_state(struct pmu_softc *sc)
+{
+ sc->saved_regs[0] = pmu_read_reg(sc, vBufA);
+ sc->saved_regs[1] = pmu_read_reg(sc, vDirA);
+ sc->saved_regs[2] = pmu_read_reg(sc, vBufB);
+ sc->saved_regs[3] = pmu_read_reg(sc, vDirB);
+ sc->saved_regs[4] = pmu_read_reg(sc, vPCR);
+ sc->saved_regs[5] = pmu_read_reg(sc, vACR);
+ sc->saved_regs[6] = pmu_read_reg(sc, vIER);
+ sc->saved_regs[7] = pmu_read_reg(sc, vT1C);
+ sc->saved_regs[8] = pmu_read_reg(sc, vT1CH);
+}
+
+static void
+pmu_restore_state(struct pmu_softc *sc)
+{
+ pmu_write_reg(sc, vBufA, sc->saved_regs[0]);
+ pmu_write_reg(sc, vDirA, sc->saved_regs[1]);
+ pmu_write_reg(sc, vBufB, sc->saved_regs[2]);
+ pmu_write_reg(sc, vDirB, sc->saved_regs[3]);
+ pmu_write_reg(sc, vPCR, sc->saved_regs[4]);
+ pmu_write_reg(sc, vACR, sc->saved_regs[5]);
+ pmu_write_reg(sc, vIER, sc->saved_regs[6]);
+ pmu_write_reg(sc, vT1C, sc->saved_regs[7]);
+ pmu_write_reg(sc, vT1CH, sc->saved_regs[8]);
+}
+
static int
pmu_send_byte(struct pmu_softc *sc, uint8_t data)
{
@@ -1018,3 +1066,178 @@ pmu_settime(device_t dev, struct timespe
return (0);
}
+static jmp_buf resetjb;
+static register_t sprgs[4];
+static register_t srrs[2];
+/* static register_t sprs[1]; */
+extern void *ap_pcpu;
+extern int unin_chip_sleep(device_t dev, int idle);
+extern int unin_chip_resume(device_t dev);
+extern u_quad_t ap_timebase;
+extern void move_sp(uint32_t *newptr);
+extern void pmu_sleep_int(void);
+extern void low_sleep_handler(void);
+
+void pmu_sleep_int(void)
+{
+ register_t hid0;
+ register_t msr;
+ register_t saved_msr;
+ ap_pcpu = pcpup;
+
+ PCPU_SET(restore, &resetjb);
+
+ *(unsigned long *)0x80 = 0x100;
+ saved_msr = mfmsr();
+ ap_timebase = mftb();
+ flush_disable_caches();
+ if (PCPU_GET(fputhread) != NULL)
+ save_fpu(PCPU_GET(fputhread));
+ if (PCPU_GET(vecthread) != NULL)
+ save_vec(PCPU_GET(vecthread));
+ 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);
+ hid0 = mfspr(SPR_HID0);
+ hid0 = (hid0 & ~(HID0_DOZE | HID0_NAP)) | HID0_SLEEP;
+ powerpc_sync();
+ isync();
+ mtspr(SPR_HID0, hid0);
+ powerpc_sync();
+
+ msr = mfmsr() | PSL_POW;
+ while (1)
+ mtmsr(msr);
+ }
+ 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);
+ powerpc_sync();
+}
+
+static int
+pmu_sleep(SYSCTL_HANDLER_ARGS)
+{
+ u_int sleep = 0;
+ int error;
+ struct pmu_softc *sc = arg1;
+ uint8_t clrcmd[] = {PMU_PWR_CLR_POWERUP_EVENTS, 0xff, 0xff};
+ uint8_t setcmd[] = {PMU_PWR_SET_POWERUP_EVENTS, 0, PMU_PWR_WAKEUP_LID_OPEN|PMU_PWR_WAKEUP_KEY};
+ uint8_t sleepcmd[] = {'M', 'A', 'T', 'T'};
+ uint8_t resp[16];
+ uint8_t reg;
+ uint8_t cmd[2] = {2, 0};
+
+ error = sysctl_handle_int(oidp, &sleep, 0, req);
+
+ if (error || !req->newptr)
+ return (error);
+
+ error = DEVICE_SUSPEND(root_bus);
+ mtx_lock(&sc->sc_mutex);
+ if (error == 0) {
+ mtx_lock(&Giant);
+ spinlock_enter();
+ reg = 0;
+ pmu_send(sc, PMU_SET_IMASK, 1, ®, 16, resp);
+ pmu_send(sc, PMU_POWER_EVENTS, 3, clrcmd, 16, resp);
+ pmu_send(sc, PMU_POWER_EVENTS, 3, setcmd, 2, resp);
+ pmu_save_state(sc);
+
+ pmu_send(sc, PMU_SLEEP, 4, sleepcmd, 16, resp);
+ unin_chip_sleep(NULL, 0);
+ pmu_sleep_int();
+ unin_chip_resume(NULL);
+
+ pmu_restore_state(sc);
+ pmu_send(sc, PMU_SYSTEM_READY, 1, cmd, 16, resp);
+ reg = PMU_DEFAULTS;
+ pmu_send(sc, PMU_SET_IMASK, 1, ®, 16, resp);
+ spinlock_exit();
+ mtx_unlock(&Giant);
+ }
+ mtx_unlock(&sc->sc_mutex);
+ printf("failure: %d\n", error);
+ DEVICE_RESUME(root_bus);
+
+ return (error);
+}
+
+static void
+pmu_print_registers(void)
+{
+ register_t reg;
+ int i;
+
+ printf("curthread: %p\n", curthread);
+ printf("srr0: %"PRIxPTR"\n", mfspr(SPR_SRR0));
+ printf("DBAT0U %"PRIxPTR"\n", mfspr(SPR_DBAT0U));
+ printf("DBAT0L %"PRIxPTR"\n", mfspr(SPR_DBAT0L));
+ printf("DBAT1U %"PRIxPTR"\n", mfspr(SPR_DBAT1U));
+ printf("DBAT1L %"PRIxPTR"\n", mfspr(SPR_DBAT1L));
+ printf("DBAT2U %"PRIxPTR"\n", mfspr(SPR_DBAT2U));
+ printf("DBAT2L %"PRIxPTR"\n", mfspr(SPR_DBAT2L));
+ printf("DBAT3U %"PRIxPTR"\n", mfspr(SPR_DBAT3U));
+ printf("DBAT3L %"PRIxPTR"\n", mfspr(SPR_DBAT3L));
+ printf("IBAT0U %"PRIxPTR"\n", mfspr(SPR_IBAT0U));
+ printf("IBAT0L %"PRIxPTR"\n", mfspr(SPR_IBAT0L));
+ printf("IBAT1U %"PRIxPTR"\n", mfspr(SPR_IBAT1U));
+ printf("IBAT1L %"PRIxPTR"\n", mfspr(SPR_IBAT1L));
+ printf("IBAT2U %"PRIxPTR"\n", mfspr(SPR_IBAT2U));
+ printf("IBAT2L %"PRIxPTR"\n", mfspr(SPR_IBAT2L));
+ printf("IBAT3U %"PRIxPTR"\n", mfspr(SPR_IBAT3U));
+ printf("IBAT3L %"PRIxPTR"\n", mfspr(SPR_IBAT3L));
+
+ for (i = 0; i < 16; i++) {
+ reg = mfsrin(i << ADDR_SR_SHFT);
+ printf("sr%d = %"PRIxPTR"\n", i, reg);
+ }
+ reg = mfspr(SPR_SDR1);
+ printf("SDR1 = %"PRIxPTR"\n", reg);
+}
+
+int
+pmu_set_speed(int high_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);
+
+ /* The PMU speed change command actually uses '1' to denote low-speed. */
+ if (high_speed)
+ sleepcmd[4] = 0;
+ else
+ sleepcmd[4] = 1;
+
+ mtx_lock(&sc->sc_mutex);
+ pmu_send(sc, PMU_CPU_SPEED, 5, sleepcmd, 16, resp);
+ mtx_unlock(&sc->sc_mutex);
+ pmu_print_registers();
+ unin_chip_sleep(NULL, 1);
+ pmu_sleep_int();
+ unin_chip_resume(NULL);
+
+ pmu_print_registers();
+// mtdec(1);
+ spinlock_exit();
+ pmu_write_reg(sc, vIER, 0x90);
+
+ return (0);
+}
Modified: projects/pmac_pmu/sys/powerpc/powermac/pmuvar.h
==============================================================================
--- projects/pmac_pmu/sys/powerpc/powermac/pmuvar.h Sun Dec 30 16:41:17 2012 (r244867)
+++ projects/pmac_pmu/sys/powerpc/powermac/pmuvar.h Sun Dec 30 17:28:08 2012 (r244868)
@@ -161,6 +161,7 @@ struct pmu_softc {
int sc_batteries;
struct cdev *sc_leddev;
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 high_speed);
+
#endif /* PMUVAR_H */
Modified: projects/pmac_pmu/sys/powerpc/powermac/uninorth.c
==============================================================================
--- projects/pmac_pmu/sys/powerpc/powermac/uninorth.c Sun Dec 30 16:41:17 2012 (r244867)
+++ projects/pmac_pmu/sys/powerpc/powermac/uninorth.c Sun Dec 30 17:28:08 2012 (r244868)
@@ -65,6 +65,8 @@ static MALLOC_DEFINE(M_UNIN, "unin", "un
static int unin_chip_probe(device_t);
static int unin_chip_attach(device_t);
+static int unin_chip_suspend(device_t);
+//static int unin_chip_resume(device_t);
/*
* Bus interface.
@@ -102,6 +104,8 @@ static device_method_t unin_chip_methods
/* Device interface */
DEVMETHOD(device_probe, unin_chip_probe),
DEVMETHOD(device_attach, unin_chip_attach),
+ DEVMETHOD(device_suspend, unin_chip_suspend),
+ DEVMETHOD(device_resume, unin_chip_resume),
/* Bus interface */
*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
More information about the svn-src-projects
mailing list