--- include/ntp_stdlib.h.orig	2025-04-18 12:54:14.000000000 -0700
+++ include/ntp_stdlib.h	2025-04-20 16:24:35.000000000 -0700
@@ -103,7 +103,9 @@ extern	const char * eventstr	(int);
 extern	const char * ceventstr	(int);
 extern	const char * res_match_flags(unsigned short);
 extern	const char * res_access_flags(unsigned short);
+#ifdef HAVE_KERNEL_PLL
 extern	const char * k_st_flags	(uint32_t);
+#endif
 extern	char *	statustoa	(int, int);
 extern	const char * socktoa	(const sockaddr_u *);
 extern	const char * socktoa_r	(const sockaddr_u *sock, char *buf, size_t buflen);
--- include/ntp_syscall.h.orig	2022-12-22 22:08:52.000000000 -0800
+++ include/ntp_syscall.h	2025-04-20 16:24:35.000000000 -0700
@@ -9,9 +9,11 @@
 #ifndef GUARD_NTP_SYSCALL_H
 #define GUARD_NTP_SYSCALL_H
 
+#ifdef HAVE_SYS_TIMEX_H
 # include <sys/time.h>	/* prerequisite on NetBSD */
 # include <sys/timex.h>
 extern int ntp_adjtime_ns(struct timex *);
+#endif
 
 /*
  * The units of the maxerror and esterror fields vary by platform.  If
--- libntp/clockwork.c.orig	2023-12-28 20:53:56.000000000 -0800
+++ libntp/clockwork.c	2025-04-20 16:24:35.000000000 -0700
@@ -5,8 +5,10 @@
 #include "config.h"
 
 #include <unistd.h>
-#include <sys/time.h>	/* prerequisite on NetBSD */
-#include <sys/timex.h>
+#ifdef HAVE_SYS_TIMEX_H
+# include <sys/time.h>	/* prerequisite on NetBSD */
+# include <sys/timex.h>
+#endif
 
 #include "ntp.h"
 #include "ntp_machine.h"
--- libntp/statestr.c.orig	2025-04-18 12:54:14.000000000 -0700
+++ libntp/statestr.c	2025-04-20 16:24:35.000000000 -0700
@@ -12,7 +12,9 @@
 #include "lib_strbuf.h"
 #include "ntp_refclock.h"
 #include "ntp_control.h"
+#ifdef HAVE_KERNEL_PLL
 # include "ntp_syscall.h"
+#endif
 
 
 /*
@@ -186,23 +188,50 @@ static const struct codestring res_acces
 	/* not used with getcode(), no terminating entry needed */
 };
 
+#ifdef HAVE_KERNEL_PLL
 /*
  * kernel discipline status bits
  */
 static const struct codestring k_st_bits[] = {
+# ifdef STA_PLL
 	{ STA_PLL,			"pll" },
+# endif
+# ifdef STA_PPSFREQ
 	{ STA_PPSFREQ,			"ppsfreq" },
+# endif
+# ifdef STA_PPSTIME
 	{ STA_PPSTIME,			"ppstime" },
+# endif
+# ifdef STA_FLL
 	{ STA_FLL,			"fll" },
+# endif
+# ifdef STA_INS
 	{ STA_INS,			"ins" },
+# endif
+# ifdef STA_DEL
 	{ STA_DEL,			"del" },
+# endif
+# ifdef STA_UNSYNC
 	{ STA_UNSYNC,			"unsync" },
+# endif
+# ifdef STA_FREQHOLD
 	{ STA_FREQHOLD,			"freqhold" },
+# endif
+# ifdef STA_PPSSIGNAL
 	{ STA_PPSSIGNAL,		"ppssignal" },
+# endif
+# ifdef STA_PPSJITTER
 	{ STA_PPSJITTER,		"ppsjitter" },
+# endif
+# ifdef STA_PPSWANDER
 	{ STA_PPSWANDER,		"ppswander" },
+# endif
+# ifdef STA_PPSERROR
 	{ STA_PPSERROR,			"ppserror" },
+# endif
+# ifdef STA_CLOCKERR
 	{ STA_CLOCKERR,			"clockerr" },
+# endif
 # ifdef STA_NANO
 	{ STA_NANO,			"nano" },
 # endif
@@ -214,6 +243,7 @@ static const struct codestring k_st_bits
 # endif
 	/* not used with getcode(), no terminating entry needed */
 };
+#endif	/* HAVE_KERNEL_PLL */
 
 /* Forwards */
 static const char *	getcode(int, const struct codestring *);
@@ -318,10 +348,12 @@ decode_bitflags(
 		 "decode_bitflags(%s) can't decode 0x%x in %d bytes",
 		 (tab == peer_st_bits)
 		     ? "peer_st"
-		     :
+		     : 
+#ifdef HAVE_KERNEL_PLL
 		       (tab == k_st_bits)
 			   ? "kern_st"
 			   :
+#endif
 			     "",
 		 (unsigned)bits, (int)LIB_BUFLENGTH);
 	errno = saved_errno;
@@ -360,6 +392,7 @@ res_access_flags(
 }
 
 
+#ifdef HAVE_KERNEL_PLL
 const char *
 k_st_flags(
 	uint32_t st
@@ -367,6 +400,8 @@ k_st_flags(
 {
 	return decode_bitflags((int)st, " ", k_st_bits, COUNTOF(k_st_bits));
 }
+#endif	/* HAVE_KERNEL_PLL */
+
 
 /*
  * statustoa - return a descriptive string for a peer status
--- ntpclients/ntpq.py.orig	2025-04-18 12:54:14.000000000 -0700
+++ ntpclients/ntpq.py	2025-04-20 16:24:35.000000000 -0700
@@ -25,6 +25,14 @@ import socket
 import sys
 import time
 
+# The BrokenPipeError doesn't exist in Python 2, but the problem that requires
+# catching it also doesn't seem to exist in Python 2, so just make it a dummy
+# in Python 2.
+try:
+    BrokenPipeError
+except NameError:
+    BrokenPipeError = None
+
 try:
     import ntp.control
     import ntp.ntpc
--- ntpd/ntp_control.c.orig	2025-04-18 12:54:14.000000000 -0700
+++ ntpd/ntp_control.c	2025-04-20 16:24:35.000000000 -0700
@@ -37,7 +37,9 @@ struct utsname utsnamebuf;
 
 /* Variables that need updating each time. */
 static leap_signature_t lsig;
+#ifdef HAVE_KERNEL_PLL
 static struct timex ntx;
+#endif	/* HAVE_KERNEL_PLL */
 
 /* Ugh.  timex slots are tough.  The man page says "long"
  * But the actual implementation on Linux uses something else.
@@ -382,6 +384,7 @@ static const struct var sys_var[] = {
   Var_uli("authcmacdecrypts", RO, authcmacdecrypt),
   Var_uli("authcmacfails", RO, authcmacfail),
 
+#ifdef HAVE_KERNEL_PLL
 /* kerninfo: Kernel timekeeping info */
   Var_kli("koffset", RO|N_CLOCK|KNUToMS, ntx.offset),
   Var_kli("kfreq", RO|N_CLOCK|K_16, ntx.freq),
@@ -399,6 +402,7 @@ static const struct var sys_var[] = {
   Var_kli("kppscalibs", RO|N_CLOCK, ntx.calcnt),
   Var_kli("kppscaliberrs", RO|N_CLOCK, ntx.errcnt),
   Var_kli("kppsstbexc", RO|N_CLOCK, ntx.stbcnt),
+#endif	/* HAVE_KERNEL_PLL */
 
 
 /* refclock stuff in ntp_io */
@@ -1402,7 +1406,9 @@ ctl_putarray(
  */
 static void
 ctl_putsys(const struct var * v) {
+#ifdef HAVE_KERNEL_PLL
 	static unsigned long ntp_adjtime_time;
+#endif	/* HAVE_KERNEL_PLL */
 	static unsigned long ntp_leap_time;
 
 /* older compilers don't allow declarations on each case without {} */
@@ -1414,6 +1420,7 @@ ctl_putsys(const struct var * v) {
  * This should get pushed up a layer: flag, once per request
  * This could get data from 2 samples if the clock ticks while we are working..
  */
+#ifdef HAVE_KERNEL_PLL
 	/* The Kernel clock variables need up-to-date output of ntp_adjtime() */
 	if (v->flags&N_CLOCK && current_time != ntp_adjtime_time) {
 		ZERO(ntx);
@@ -1422,6 +1429,7 @@ ctl_putsys(const struct var * v) {
                             "MODE6: ntp_adjtime() for mode 6 query failed: %s", strerror(errno));
                 ntp_adjtime_time = current_time;
 	}
+#endif	/* HAVE_KERNEL_PLL */
 
 	/* The leap second variables need up-to-date info */
         if (v->flags&N_LEAP && current_time != ntp_leap_time) {
--- ntpd/ntp_loopfilter.c.orig	2025-04-18 12:54:14.000000000 -0700
+++ ntpd/ntp_loopfilter.c	2025-04-20 16:24:35.000000000 -0700
@@ -23,8 +23,10 @@
 
 #define NTP_MAXFREQ	500e-6
 
+#ifdef HAVE_KERNEL_PLL
 # define FREQTOD(x)	((x) / 65536e6)            /* NTP to double */
 # define DTOFREQ(x)	((int32_t)((x) * 65536e6)) /* double to NTP */
+#endif /* HAVE_KERNEL_PLL */
 
 /*
  * This is an implementation of the clock discipline algorithm described
@@ -125,6 +127,7 @@ static void rstclock (int, double); /* t
 static double direct_freq(double); /* direct set frequency */
 static void set_freq(double);	/* set frequency */
 
+#ifdef HAVE_KERNEL_PLL
 #ifndef PATH_MAX
 # define PATH_MAX MAX_PATH
 #endif
@@ -138,6 +141,7 @@ static unsigned int loop_tai;	/* last TA
 #endif /* STA_NANO */
 static	void	start_kern_loop(void);
 static	void	stop_kern_loop(void);
+#endif /* HAVE_KERNEL_PLL */
 
 /*
  * Clock state machine control flags
@@ -154,7 +158,9 @@ struct clock_control_flags clock_ctl = {
 int	freq_cnt;		/* initial frequency clamp */
 
 static int freq_set;		/* initial set frequency switch */
+#ifdef HAVE_KERNEL_PLL
 static bool	ext_enable;	/* external clock enabled */
+#endif /* HAVE_KERNEL_PLL */
 
 /*
  * Clock state machine variables
@@ -172,10 +178,13 @@ static int sys_hufflen;		/* huff-n'-puff
 static int sys_huffptr;		/* huff-n'-puff filter pointer */
 static double sys_mindly;	/* huff-n'-puff filter min delay */
 
+#if defined(HAVE_KERNEL_PLL)
 /* Emacs cc-mode goes nuts if we split the next line... */
 #define MOD_BITS (MOD_OFFSET | MOD_MAXERROR | MOD_ESTERROR | \
     MOD_STATUS | MOD_TIMECONST)
+#endif /* HAVE_KERNEL_PLL */
 
+#ifdef HAVE_KERNEL_PLL
 static void
 sync_status(const char *what, int ostatus, int nstatus) {
 	/* only used in non-lockclock case */
@@ -199,6 +208,7 @@ static char *file_name(void) {
 	}
 	return this_file;
 }
+#endif /* HAVE_KERNEL_PLL */
 
 /*
  * init_loopfilter - initialize loop filter data
@@ -213,6 +223,7 @@ init_loopfilter(void) {
 	freq_cnt = (int)clock_minstep;
 }
 
+#ifdef HAVE_KERNEL_PLL
 /*
  * ntp_adjtime_error_handler - process errors from ntp_adjtime
  */
@@ -411,6 +422,7 @@ or, from ntp_adjtime():
 	}
 	return;
 }
+#endif /* HAVE_KERNEL_PLL */
 
 /*
  * local_clock - the NTP logical clock loop filter.
@@ -444,7 +456,9 @@ local_clock(
 
 	int	rval;		/* return code */
 	int	osys_poll;	/* old system poll */
+#ifdef HAVE_KERNEL_PLL
 	int	ntp_adj_ret;	/* returned by ntp_adjtime */
+#endif /* HAVE_KERNEL_PLL */
 	double	mu;		/* interval since last update */
 	double	clock_frequency; /* clock frequency */
 	double	dtemp, etemp;	/* double temps */
@@ -701,6 +715,7 @@ local_clock(
 		}
 	}
 
+#ifdef HAVE_KERNEL_PLL
 	/*
 	 * This code segment works when clock adjustments are made using
 	 * precision time kernel support and the ntp_adjtime() system
@@ -810,6 +825,7 @@ local_clock(
 		}
 #endif /* STA_NANO */
 	}
+#endif /* HAVE_KERNEL_PLL */
 
 	/*
 	 * Clamp the frequency within the tolerance range and calculate
@@ -921,8 +937,10 @@ adj_host_clock(
 	} else if (freq_cnt > 0) {
 		offset_adj = clock_offset / (CLOCK_PLL * ULOGTOD(1));
 		freq_cnt--;
+#ifdef HAVE_KERNEL_PLL
 	} else if (clock_ctl.pll_control && clock_ctl.kern_enable) {
 		offset_adj = 0.;
+#endif /* HAVE_KERNEL_PLL */
 	} else {
 		offset_adj = clock_offset / (CLOCK_PLL * ULOGTOD(clkstate.sys_poll));
 	}
@@ -933,9 +951,11 @@ adj_host_clock(
 	 * set_freq().  Otherwise it is a component of the adj_systime()
 	 * offset.
 	 */
+#ifdef HAVE_KERNEL_PLL
 	if (clock_ctl.pll_control && clock_ctl.kern_enable)
 		freq_adj = 0.;
 	else
+#endif /* HAVE_KERNEL_PLL */
 		freq_adj = loop_data.drift_comp;
 
 	/* Bound absolute value of total adjustment to NTP_MAXFREQ. */
@@ -1024,6 +1044,7 @@ set_freq(
 
 	loop_data.drift_comp = freq;
 	loop_desc = "ntpd";
+#ifdef HAVE_KERNEL_PLL
 	if (clock_ctl.pll_control) {
 		int ntp_adj_ret;
 		ZERO(ntv);
@@ -1036,10 +1057,12 @@ set_freq(
 		    ntp_adjtime_error_handler(__func__, &ntv, ntp_adj_ret, errno, false, false, __LINE__ - 1);
 		}
 	}
+#endif /* HAVE_KERNEL_PLL */
 	mprintf_event(EVNT_FSET, NULL, "%s %.6f PPM", loop_desc,
 	    loop_data.drift_comp * US_PER_S);
 }
 
+#ifdef HAVE_KERNEL_PLL
 static void
 start_kern_loop(void)
 {
@@ -1075,8 +1098,10 @@ start_kern_loop(void)
 	  	    "kernel time sync enabled");
 	}
 }
+#endif	/* HAVE_KERNEL_PLL */
 
 
+#ifdef HAVE_KERNEL_PLL
 static void
 stop_kern_loop(void)
 {
@@ -1084,6 +1109,7 @@ stop_kern_loop(void)
 		report_event(EVNT_KERN, NULL,
 		    "kernel time sync disabled");
 }
+#endif	/* HAVE_KERNEL_PLL */
 
 
 /*
@@ -1096,11 +1122,15 @@ select_loop(
 {
 	if (clock_ctl.kern_enable == use_kern_loop)
 		return;
+#ifdef HAVE_KERNEL_PLL
 	if (clock_ctl.pll_control && !use_kern_loop)
 		stop_kern_loop();
+#endif /* HAVE_KERNEL_PLL */
 	clock_ctl.kern_enable = use_kern_loop;
+#ifdef HAVE_KERNEL_PLL
 	if (clock_ctl.pll_control && use_kern_loop)
 		start_kern_loop();
+#endif /* HAVE_KERNEL_PLL */
 	/*
 	 * If this loop selection change occurs after initial startup,
 	 * call set_freq() to switch the frequency compensation to or
@@ -1156,10 +1186,12 @@ loop_config(
 	 * variables. Otherwise, continue leaving no harm behind.
 	 */
 	case LOOP_DRIFTINIT:
+#ifdef HAVE_KERNEL_PLL
 		if (loop_data.lockclock || clock_ctl.mode_ntpdate)
 			break;
 
 		start_kern_loop();
+#endif /* HAVE_KERNEL_PLL */
 
 		/*
 		 * Initialize frequency if given; otherwise, begin frequency
@@ -1178,7 +1210,9 @@ loop_config(
 			rstclock(EVNT_FSET, 0);
 		else
 			rstclock(EVNT_NSET, 0);
+#ifdef HAVE_KERNEL_PLL
 		loop_started = true;
+#endif /* HAVE_KERNEL_PLL */
 		break;
 
 	/*
@@ -1252,4 +1286,3 @@ loop_config(
 		    "CONFIG: loop_config: unsupported option %d", item);
 	}
 }
-
--- ntpd/ntp_timer.c.orig	2025-04-18 12:54:14.000000000 -0700
+++ ntpd/ntp_timer.c	2025-04-20 16:24:35.000000000 -0700
@@ -13,7 +13,9 @@
 #include <signal.h>
 #include <unistd.h>
 
+#ifdef HAVE_KERNEL_PLL
 #include "ntp_syscall.h"
+#endif /* HAVE_KERNEL_PLL */
 
 #ifdef HAVE_TIMER_CREATE
 /* TC_ERR represents the timer_create() error return value. */
@@ -373,7 +375,11 @@ check_leapsec(
 
 	leap_result_t lsdata;
 	uint32_t       lsprox;
+#ifdef HAVE_KERNEL_PLL
 	leapsec_electric((clock_ctl.pll_control && clock_ctl.kern_enable) ? electric_on : electric_off);
+#else
+	leapsec_electric(electric_off);
+#endif
 #ifdef ENABLE_LEAP_SMEAR
 	leap_smear.enabled = (leap_smear_intv != 0);
 #endif
--- ntpd/refclock_local.c.orig	2025-04-18 12:54:14.000000000 -0700
+++ ntpd/refclock_local.c	2025-04-20 16:24:35.000000000 -0700
@@ -164,6 +164,7 @@ local_poll(
 	 * If another process is disciplining the system clock, we set
 	 * the leap bits and quality indicators from the kernel.
 	 */
+#ifdef HAVE_KERNEL_PLL
 	if (loop_data.lockclock) {
 /* Both ntp_adjtime() and ntp_gettime() return the clock status
  * which includes leap pending info.  See man ntp_adjtime
@@ -210,6 +211,13 @@ local_poll(
 		pp->disp = DISPERSION;
 		pp->jitter = 0;
 	}
+	pp->disp = 0;
+	pp->jitter = 0;
+#else /* !HAVE_KERNEL_PLL */
+	pp->leap = LEAP_NOWARNING;
+	pp->disp = DISPERSION;
+	pp->jitter = 0;
+#endif /* !HAVE_KERNEL_PLL */
 	pp->lastref = pp->lastrec;
 	refclock_receive(peer);
 }
--- tests/libntp/statestr.c.orig	2025-04-18 12:54:14.000000000 -0700
+++ tests/libntp/statestr.c	2025-04-20 16:24:35.000000000 -0700
@@ -4,7 +4,9 @@
 #include "unity.h"
 #include "unity_fixture.h"
 
+#ifdef HAVE_SYS_TIMEX_H
 #include <sys/timex.h>
+#endif
 
 TEST_GROUP(statestr);
 
@@ -32,7 +34,9 @@ TEST(statestr, ResAccessFlags2) {
 
 // k_st_flags()
 TEST(statestr, KSTFlags) {
+#ifdef STA_PPSFREQ
 	TEST_ASSERT_EQUAL_STRING("ppsfreq", k_st_flags(STA_PPSFREQ));
+#endif
 }
 
 // statustoa
--- wafhelpers/bin_test.py.orig	2025-04-18 12:54:14.000000000 -0700
+++ wafhelpers/bin_test.py	2025-04-20 16:24:35.000000000 -0700
@@ -88,8 +88,11 @@ def cmd_bin_test(ctx):
         (BIN, NTPCLIENTS, "ntpleapfetch", "--version"),
         (SBIN, NTPD, "ntpd", "--version"),
         (BIN, NTPFROB, "ntpfrob", "-V"),
-        (BIN, NTPTIME, "ntptime", "-V"),
     ]
+    # Only include ntptime if it was built
+    if ctx.env.HEADER_SYS_TIMEX_H:
+        cmd_list.append((BIN, NTPTIME, "ntptime", "-V"))
+
     cmd_list_python = [
         (BIN, NTPCLIENTS, "ntpdig", "--version"),
         (BIN, NTPCLIENTS, "ntpkeygen", "--version"),
--- wafhelpers/options.py.orig	2025-04-18 12:54:14.000000000 -0700
+++ wafhelpers/options.py	2025-04-20 16:24:35.000000000 -0700
@@ -24,6 +24,8 @@ def options_cmd(ctx, config):
                    help="Droproot earlier (breaks SHM and NetBSD).")
     grp.add_option('--enable-seccomp', action='store_true',
                    default=False, help="Enable seccomp (restricts syscalls).")
+    grp.add_option('--disable-kernel-pll', action='store_true',
+                   default=False, help="Disable kernel PLL.")
     grp.add_option('--disable-mdns-registration', action='store_true',
                    default=False, help="Disable MDNS registration.")
     grp.add_option(
--- wscript.orig	2025-04-18 12:54:14.000000000 -0700
+++ wscript	2025-04-20 16:24:35.000000000 -0700
@@ -578,7 +578,7 @@ int main(int argc, char **argv) {
     structures = (
         ("struct if_laddrconf", ["sys/types.h", "net/if6.h"], False),
         ("struct if_laddrreq", ["sys/types.h", "net/if6.h"], False),
-        ("struct timex", ["sys/time.h", "sys/timex.h"], True),
+        ("struct timex", ["sys/time.h", "sys/timex.h"], False),
         ("struct ntptimeval", ["sys/time.h", "sys/timex.h"], False),
     )
     for (s, h, r) in structures:
@@ -809,6 +809,21 @@ int main(int argc, char **argv) {
     if ctx.options.disable_fuzz:
         pprint("YELLOW", "--disable-fuzz is now standard.  Clock fuzzing is gone.")
 
+    # Does the kernel implement a phase-locked loop for timing?
+    # All modern Unixes (in particular Linux and *BSD) have this.
+    #
+    # The README for the (now deleted) kernel directory says this:
+    # "If the precision-time kernel (KERNEL_PLL define) is
+    # configured, the installation process requires the header
+    # file /usr/include/sys/timex.h for the particular
+    # architecture to be in place."
+    #
+    if ((ctx.get_define("HAVE_SYS_TIMEX_H") and
+            not ctx.options.disable_kernel_pll)):
+        ctx.define("HAVE_KERNEL_PLL", 1,
+                   comment="Whether phase-locked loop for timing "
+                   "exists and is enabled")
+
     # SO_REUSEADDR socket option is needed to open a socket on an
     # interface when the port number is already in use on another
     # interface. Linux needs this, NetBSD does not, status on
