The branch main has been updated by adrian: URL: https://cgit.FreeBSD.org/src/commit/?id=422c8719eab2b8a01b49f748a88dd372db25f888
commit 422c8719eab2b8a01b49f748a88dd372db25f888 Author: Adrian Chadd <[email protected]> AuthorDate: 2026-01-20 02:38:43 +0000 Commit: Adrian Chadd <[email protected]> CommitDate: 2026-01-27 04:38:23 +0000 powerpc: add a best-effort SMP time base sync for G5's that need it There's no timebase freeze platform routine registered on my dual 2.3GHz G5 PPC970FX Apple PowerMac. For platforms without an explicit timebase freeze/unfreeze, we'll have to make do with what we have - which for now is an explicit hand-crafted spinlock/rendezvous method. * For existing platforms, they'll still continue to clock freeze / rendezvous; albeit with some stronger atomic bits now (from jhibbits@.) * Instead of the fallback being "no timesync", implement a best-effort one which does a similar rendezvous barrier between BSP and APs, but instead of freeze/unfreeze the first instruction after the CPUs all register they're ready is to set the timebase. This has resulted in many reboots of my Powermac G5 dual-socket device correctly starting and running in SMP mode. Differential Revision: https://reviews.freebsd.org/D54821 Reviewed by: jhibbits --- sys/powerpc/powermac/platform_powermac.c | 99 +++++++++++++++++++++++++++++--- 1 file changed, 90 insertions(+), 9 deletions(-) diff --git a/sys/powerpc/powermac/platform_powermac.c b/sys/powerpc/powermac/platform_powermac.c index c63ef521ca8f..cbb2e212a00d 100644 --- a/sys/powerpc/powermac/platform_powermac.c +++ b/sys/powerpc/powermac/platform_powermac.c @@ -405,21 +405,88 @@ powermac_register_timebase(device_t dev, powermac_tb_disable_t cb) freeze_timebase = cb; } +/** + * @brief Implement a default platform AP/BSP SMP timebase synchronisation + * + * Some powermac platforms don't have a freeze/unfreeze method. + * Here just try our best to force synchronisation. + */ static void -powermac_smp_timebase_sync(platform_t plat, u_long tb, int ap) +powermac_smp_timebase_sync_fallback(platform_t plat, u_long tb, int ap) +{ + static volatile bool tb_ready = false; + static volatile int cpu_done; + + if (bootverbose) + printf("[%d] %s: called, AP tb=0x%lx tb=0x%lx\n", + ap, __func__, tb, mftb()); + + /* Do initial timebase sync */ + mttb(tb); + + if (ap) { + /* + * APs - wait until the BSP signals its ready to sync, + * then wait for all CPUs to be ready. + */ + critical_enter(); + while (!tb_ready) + atomic_thread_fence_seq_cst(); + atomic_add_int(&cpu_done, 1); + do { + atomic_thread_fence_seq_cst(); + } while (cpu_done < mp_ncpus); + mttb(tb); + critical_exit(); + } else { + /* + * BSP - signify that the timebase sync is about to start, + * then wait for other CPUs to be ready. + */ + critical_enter(); + /* Ensure cpu_done is zeroed so we can resync at runtime */ + atomic_store_int(&cpu_done, 0); + tb_ready = true; + atomic_add_int(&cpu_done, 1); + do { + atomic_thread_fence_seq_cst(); + } while (cpu_done < mp_ncpus); + mttb(tb); + /* Reset tb_ready so we can resync at runtime */ + tb_ready = false; + critical_exit(); + } + if (bootverbose) + printf("[%d] %s: finished; AP tb=0x%lx called tb=0x%lx\n", + ap, __func__, tb, mftb()); +} + +/** + * @brief Implement freeze/unfreeze AP/BSP SMP timebase synchronisation + * + * This implements SMP timebase synchronisation for hardware that + * implements freezing a shared timebase clock source. + * + * The BSP will freeze the timebase and signal the APs to program their + * local timebase with the shared timebase value. The BSP will then + * unfreeze the timebase clock, allowing all CPUs to march forward + * from the same base timebase value. + */ +static void +powermac_smp_timebase_sync_freeze(platform_t plat, u_long tb, int ap) { - static volatile bool tb_ready; + static volatile bool tb_ready = false; static volatile int cpu_done; + if (bootverbose) + printf("[%d] %s: called, AP tb=0x%lx tb=0x%lx\n", + ap, __func__, tb, mftb()); + /* - * 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. */ @@ -428,25 +495,39 @@ powermac_smp_timebase_sync(platform_t plat, u_long tb, int ap) atomic_thread_fence_seq_cst(); mttb(tb); atomic_add_int(&cpu_done, 1); - while (cpu_done < mp_ncpus) + do { atomic_thread_fence_seq_cst(); + } while (cpu_done < mp_ncpus); critical_exit(); } else { /* BSP */ critical_enter(); /* Ensure cpu_done is zeroed so we can resync at runtime */ - atomic_set_int(&cpu_done, 0); + atomic_store_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) + do { atomic_thread_fence_seq_cst(); + } while (cpu_done < mp_ncpus); freeze_timebase(powermac_tb_dev, false); /* Reset tb_ready so we can resync at runtime */ tb_ready = false; critical_exit(); } + if (bootverbose) + printf("[%d] %s: finished; AP tb=0x%lx called tb=0x%lx\n", + ap, __func__, tb, mftb()); +} + +static void +powermac_smp_timebase_sync(platform_t plat, u_long tb, int ap) +{ + if (freeze_timebase == dummy_timebase) + powermac_smp_timebase_sync_fallback(plat, tb, ap); + else + powermac_smp_timebase_sync_freeze(plat, tb, ap); } /* Fallback freeze. In case no real handler is found in the device tree. */
