git: c583b0258728 - main - [PowerPC] PowerMac timebase sync for G4

From: Justin Hibbits <jhibbits_at_FreeBSD.org>
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);
+}