[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
This commit is contained in:
Brandon Bergren 2021-12-23 15:45:24 -06:00 committed by Justin Hibbits
parent a34d3ca6ef
commit c583b02587
5 changed files with 283 additions and 1 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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_ */

View File

@ -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);
}