git: c583b0258728 - main - [PowerPC] PowerMac timebase sync for G4
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Thu, 23 Dec 2021 22:06:30 UTC
The branch main has been updated by jhibbits:
URL: https://cgit.FreeBSD.org/src/commit/?id=c583b02587285535d1af8691be2d83ac3916f083
commit c583b02587285535d1af8691be2d83ac3916f083
Author: Brandon Bergren <bdragon@FreeBSD.org>
AuthorDate: 2021-12-23 21:45:24 +0000
Commit: Justin Hibbits <jhibbits@FreeBSD.org>
CommitDate: 2021-12-23 22:06:07 +0000
[PowerPC] PowerMac timebase sync for G4
Summary:
Disable timebase on (some) AIM platforms (tested on PowerMac G4) prior
to synchronization.
Some platforms use a GPIO to enable and disable timebase, while others
use a platform function.
This mirrors 0d69f00b on mpc85xx.
Todo:
* Implement various G5 timebase controls.
* Print out platform code on unknown G5s so we can collect it.
* Change API to be give/take pairs like Linux does so it's possible to
do a software sync protocol.
Reviewed By: #powerpc, jhibbits
Subscribers: mikael, markmi_dsl-only.net, luporl, alfredo
Tags: #powerpc
Differential Revision: https://reviews.freebsd.org/D29136
---
sys/conf/files.powerpc | 1 +
sys/powerpc/powermac/macio.c | 42 +++++++++
sys/powerpc/powermac/platform_powermac.c | 59 +++++++++++-
sys/powerpc/powermac/platform_powermac.h | 34 +++++++
sys/powerpc/powermac/tbgpio.c | 148 +++++++++++++++++++++++++++++++
5 files changed, 283 insertions(+), 1 deletion(-)
diff --git a/sys/conf/files.powerpc b/sys/conf/files.powerpc
index 25fdfea388b9..f904c17b2294 100644
--- a/sys/conf/files.powerpc
+++ b/sys/conf/files.powerpc
@@ -216,6 +216,7 @@ powerpc/powermac/pswitch.c optional powermac pswitch
powerpc/powermac/pmu.c optional powermac pmu
powerpc/powermac/smu.c optional powermac smu
powerpc/powermac/smusat.c optional powermac smu
+powerpc/powermac/tbgpio.c optional powermac pci smp
powerpc/powermac/uninorth.c optional powermac
powerpc/powermac/uninorthpci.c optional powermac pci
powerpc/powermac/vcoregpio.c optional powermac
diff --git a/sys/powerpc/powermac/macio.c b/sys/powerpc/powermac/macio.c
index b475277be816..3e5007973726 100644
--- a/sys/powerpc/powermac/macio.c
+++ b/sys/powerpc/powermac/macio.c
@@ -54,6 +54,7 @@
#include <dev/ofw/openfirm.h>
#include <powerpc/powermac/maciovar.h>
+#include <powerpc/powermac/platform_powermac.h>
#include <dev/pci/pcivar.h>
#include <dev/pci/pcireg.h>
@@ -70,6 +71,9 @@ struct macio_softc {
/* FCR registers */
int sc_memrid;
struct resource *sc_memr;
+
+ /* GPIO offsets */
+ int sc_timebase;
};
static MALLOC_DEFINE(M_MACIO, "macio", "macio device information");
@@ -89,6 +93,9 @@ static int macio_release_resource(device_t, device_t, int, int,
struct resource *);
static struct resource_list *macio_get_resource_list (device_t, device_t);
static ofw_bus_get_devinfo_t macio_get_devinfo;
+#if !defined(__powerpc64__) && defined(SMP)
+static void macio_freeze_timebase(device_t, bool);
+#endif
/*
* Bus interface definition
@@ -430,6 +437,26 @@ macio_attach(device_t dev)
}
}
+#if !defined(__powerpc64__) && defined(SMP)
+ /*
+ * Detect an SMP G4 machine.
+ *
+ * On SMP G4, timebase freeze is via a GPIO on macio.
+ *
+ * When we are on an SMP G4, we need to install a handler to
+ * perform timebase freeze/unfreeze on behalf of the platform.
+ */
+ if ((child = OF_finddevice("/cpus/PowerPC,G4@0")) != -1 &&
+ OF_peer(child) != -1) {
+ if (OF_getprop(child, "timebase-enable", &sc->sc_timebase,
+ sizeof(sc->sc_timebase)) <= 0)
+ sc->sc_timebase = KEYLARGO_GPIO_BASE + 0x09;
+ powermac_register_timebase(dev, macio_freeze_timebase);
+ device_printf(dev, "GPIO timebase control at 0x%x\n",
+ sc->sc_timebase);
+ }
+#endif
+
return (bus_generic_attach(dev));
}
@@ -693,3 +720,18 @@ macio_enable_wireless(device_t dev, bool enable)
return (0);
}
+
+#if !defined(__powerpc64__) && defined(SMP)
+static void
+macio_freeze_timebase(device_t dev, bool freeze)
+{
+ struct macio_softc *sc = device_get_softc(dev);
+
+ if (freeze) {
+ bus_write_1(sc->sc_memr, sc->sc_timebase, 4);
+ } else {
+ bus_write_1(sc->sc_memr, sc->sc_timebase, 0);
+ }
+ bus_read_1(sc->sc_memr, sc->sc_timebase);
+}
+#endif
diff --git a/sys/powerpc/powermac/platform_powermac.c b/sys/powerpc/powermac/platform_powermac.c
index 7f78bc63c1ab..3e12a758d0b0 100644
--- a/sys/powerpc/powermac/platform_powermac.c
+++ b/sys/powerpc/powermac/platform_powermac.c
@@ -53,9 +53,15 @@ __FBSDID("$FreeBSD$");
#include <dev/ofw/openfirm.h>
#include <machine/ofw_machdep.h>
+#include <powerpc/powermac/platform_powermac.h>
+
#include "platform_if.h"
-extern void *ap_pcpu;
+extern volatile void *ap_pcpu;
+
+static void dummy_timebase(device_t, bool);
+static device_t powermac_tb_dev;
+static void (*freeze_timebase)(device_t, bool) = dummy_timebase;
static int powermac_probe(platform_t);
static int powermac_attach(platform_t);
@@ -395,11 +401,62 @@ powermac_smp_start_cpu(platform_t plat, struct pcpu *pc)
#endif
}
+void
+powermac_register_timebase(device_t dev, powermac_tb_disable_t cb)
+{
+ powermac_tb_dev = dev;
+ freeze_timebase = cb;
+}
+
static void
powermac_smp_timebase_sync(platform_t plat, u_long tb, int ap)
{
+ static volatile bool tb_ready;
+ static volatile int cpu_done;
+ /*
+ * XXX Temporary fallback for platforms we don't know how to freeze.
+ *
+ * This needs to be replaced with a cpu-to-cpu software sync
+ * protocol, because this is not a consistent way to sync timebase.
+ */
mttb(tb);
+ if (freeze_timebase == dummy_timebase)
+ return;
+
+ if (ap) {
+ /* APs. Hold off until we get a stable timebase. */
+ critical_enter();
+ while (!tb_ready)
+ atomic_thread_fence_seq_cst();
+ mttb(tb);
+ atomic_add_int(&cpu_done, 1);
+ while (cpu_done < mp_ncpus)
+ atomic_thread_fence_seq_cst();
+ critical_exit();
+ } else {
+ /* BSP */
+ critical_enter();
+ /* Ensure cpu_done is zeroed so we can resync at runtime */
+ atomic_set_int(&cpu_done, 0);
+ freeze_timebase(powermac_tb_dev, true);
+ tb_ready = true;
+ mttb(tb);
+ atomic_add_int(&cpu_done, 1);
+ while (cpu_done < mp_ncpus)
+ atomic_thread_fence_seq_cst();
+ freeze_timebase(powermac_tb_dev, false);
+ /* Reset tb_ready so we can resync at runtime */
+ tb_ready = false;
+ critical_exit();
+ }
+}
+
+/* Fallback freeze. In case no real handler is found in the device tree. */
+static void
+dummy_timebase(device_t dev, bool freeze)
+{
+ /* Nothing to do here, move along. */
}
static void
diff --git a/sys/powerpc/powermac/platform_powermac.h b/sys/powerpc/powermac/platform_powermac.h
new file mode 100644
index 000000000000..3a85d12feea2
--- /dev/null
+++ b/sys/powerpc/powermac/platform_powermac.h
@@ -0,0 +1,34 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2021 Brandon Bergren <bdragon@FreeBSD.org>
+ *
+ * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
+ */
+
+#ifndef _PLATFORM_POWERMAC_H_
+#define _PLATFORM_POWERMAC_H_
+
+typedef void (*powermac_tb_disable_t)(device_t, bool);
+extern void powermac_register_timebase(device_t, powermac_tb_disable_t);
+
+#endif /* _PLATFORM_POWERMAC_H_ */
diff --git a/sys/powerpc/powermac/tbgpio.c b/sys/powerpc/powermac/tbgpio.c
new file mode 100644
index 000000000000..348a8d26b688
--- /dev/null
+++ b/sys/powerpc/powermac/tbgpio.c
@@ -0,0 +1,148 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2021 Brandon Bergren <bdragon@FreeBSD.org>
+ *
+ * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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>
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/openfirm.h>
+
+#include <powerpc/powermac/macgpiovar.h>
+#include <powerpc/powermac/platform_powermac.h>
+
+static int tbgpio_probe(device_t);
+static int tbgpio_attach(device_t);
+static void tbgpio_freeze_timebase(device_t, bool);
+
+static device_method_t tbgpio_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, tbgpio_probe),
+ DEVMETHOD(device_attach, tbgpio_attach),
+ DEVMETHOD_END
+};
+
+struct tbgpio_softc {
+ uint32_t sc_value;
+ uint32_t sc_mask;
+};
+
+static driver_t tbgpio_driver = {
+ "tbgpio",
+ tbgpio_methods,
+ sizeof(struct tbgpio_softc)
+};
+
+static devclass_t tbgpio_devclass;
+
+EARLY_DRIVER_MODULE(tbgpio, macgpio, tbgpio_driver, tbgpio_devclass, 0, 0,
+ BUS_PASS_CPU);
+
+static int
+tbgpio_probe(device_t dev)
+{
+ phandle_t node;
+ const char *name;
+ pcell_t pfunc[32];
+ int res;
+
+ name = ofw_bus_get_name(dev);
+ node = ofw_bus_get_node(dev);
+
+ if (strcmp(name, "timebase-enable") != 0)
+ return (ENXIO);
+
+ res = OF_getencprop(node, "platform-do-cpu-timebase", pfunc,
+ sizeof(pfunc));
+ if (res == -1)
+ return (ENXIO);
+
+ /*
+ * If this doesn't look like a simple gpio_write pfunc,
+ * complain about it so we can collect the pfunc.
+ */
+ if (res != 20 || pfunc[2] != 0x01) {
+ printf("\nUnknown platform function detected!\n");
+ printf("Please send a PR including the following data:\n");
+ printf("===================\n");
+ printf("Func: platform-do-cpu-timebase\n");
+ hexdump(pfunc, res, NULL, HD_OMIT_CHARS);
+ printf("===================\n");
+ return (ENXIO);
+ }
+
+ device_set_desc(dev, "CPU Timebase Control");
+ return (BUS_PROBE_SPECIFIC);
+}
+
+static int
+tbgpio_attach(device_t dev)
+{
+ phandle_t node;
+ struct tbgpio_softc *sc;
+
+ /*
+ * Structure of pfunc:
+ * pfunc[0]: phandle to /cpus
+ * pfunc[1]: flags
+ * pfunc[2]: 0x1 == CMD_WRITE_GPIO
+ * pfunc[3]: value
+ * pfunc[4]: mask
+ */
+ pcell_t pfunc[5];
+
+ sc = device_get_softc(dev);
+ node = ofw_bus_get_node(dev);
+
+ OF_getencprop(node, "platform-do-cpu-timebase", pfunc, sizeof(pfunc));
+
+ sc->sc_value = pfunc[3];
+ sc->sc_mask = pfunc[4];
+
+ powermac_register_timebase(dev, tbgpio_freeze_timebase);
+ return (0);
+}
+
+static void
+tbgpio_freeze_timebase(device_t dev, bool freeze)
+{
+ struct tbgpio_softc *sc;
+ uint32_t val;
+
+ sc = device_get_softc(dev);
+
+ val = sc->sc_value;
+ if (freeze)
+ val = ~val;
+ val &= sc->sc_mask;
+
+ macgpio_write(dev, val);
+}