Index: src/sys/conf/files
===================================================================
RCS file: /cvsroot/src/sys/conf/files,v
retrieving revision 1.1320
diff -u -r1.1320 files
--- src/sys/conf/files	10 Jun 2026 00:09:43 -0000	1.1320
+++ src/sys/conf/files	18 Jun 2026 15:03:15 -0000
@@ -1105,7 +1105,7 @@
 
 # Mostek time-of-day clock and NVRAM
 #
-define	mk48txx
+define	mk48txx: sysmon_envsys
 file	dev/ic/mk48txx.c		mk48txx
 
 # OKI MSM6242B
Index: src/sys/dev/ic/mk48txx.c
===================================================================
RCS file: /cvsroot/src/sys/dev/ic/mk48txx.c,v
retrieving revision 1.29
diff -u -r1.29 mk48txx.c
--- src/sys/dev/ic/mk48txx.c	7 Sep 2025 21:45:16 -0000	1.29
+++ src/sys/dev/ic/mk48txx.c	18 Jun 2026 15:03:15 -0000
@@ -39,6 +39,7 @@
 #include <sys/systm.h>
 #include <sys/device.h>
 #include <sys/errno.h>
+#include <sys/sysctl.h>
 
 #include <sys/bus.h>
 #include <dev/clock_subr.h>
@@ -49,13 +50,14 @@
 int mk48txx_settime_ymdhms(todr_chip_handle_t, struct clock_ymdhms *);
 uint8_t mk48txx_def_nvrd(struct mk48txx_softc *, int);
 void mk48txx_def_nvwr(struct mk48txx_softc *, int, uint8_t);
+void mk48txx_refresh(struct sysmon_envsys *, envsys_data_t *);
+static int sysctl_mk48txx_osc(SYSCTLFN_ARGS);
 
 const struct {
 	const char *name;
 	bus_size_t nvramsz;
 	bus_size_t clkoff;
 	int flags;
-#define MK48TXX_EXT_REGISTERS	1	/* Has extended register set */
 } mk48txx_models[] = {
 	{ "mk48t02", MK48T02_CLKSZ, MK48T02_CLKOFF, 0 },
 	{ "mk48t08", MK48T08_CLKSZ, MK48T08_CLKOFF, 0 },
@@ -68,7 +70,9 @@
 mk48txx_attach(struct mk48txx_softc *sc)
 {
 	todr_chip_handle_t handle;
+	const struct sysctlnode *me = NULL, *node = NULL;
 	int i;
+	uint8_t csr, flgs;
 
 	aprint_normal(": %s", sc->sc_model);
 
@@ -82,6 +86,7 @@
 
 	sc->sc_nvramsz = mk48txx_models[i].nvramsz;
 	sc->sc_clkoffset = mk48txx_models[i].clkoff;
+	sc->sc_flag |= mk48txx_models[i].flags;
 
 	handle = &sc->sc_handle;
 	KASSERT(sc->sc_dev != NULL);
@@ -94,6 +99,70 @@
 	if (sc->sc_nvwr == NULL)
 		sc->sc_nvwr = mk48txx_def_nvwr;
 
+	csr = (*sc->sc_nvrd)(sc, sc->sc_clkoffset + MK48TXX_ISEC);
+	if (csr & MK48TXX_SEC_STOP) {
+		aprint_normal("\n");
+		aprint_error_dev(sc->sc_dev,
+		    "WARNING: oscillator is stopped (0x%02x)", csr);
+		sc->sc_osc_stp = 1;
+	} else
+		sc->sc_osc_stp = 0;
+
+	/* Setup envsys if the chip has the battery low flag */
+	if (sc->sc_flag & MK48TXX_EXT_REGISTERS) {
+		flgs = (*sc->sc_nvrd)(sc, sc->sc_clkoffset + MK48TXX_IFLAGS);
+		if (flgs &= MK48TXX_FLAGS_BATTLOW) {
+			aprint_normal("\n");
+			aprint_error_dev(sc->sc_dev,
+			    "WARNING: battery is low (0x%02x)", flgs);
+		}
+
+		sc->sc_sme = sysmon_envsys_create();
+		sc->sc_sensor.units = ENVSYS_INDICATOR;
+		sc->sc_sensor.state = ENVSYS_SINVALID;
+		sc->sc_sensor.value_cur = 0;
+		sc->sc_sensor.flags |= ENVSYS_FMONCRITICAL;
+		(void)strlcpy(sc->sc_sensor.desc, "battery low",
+		    sizeof(sc->sc_sensor.desc));
+		if (sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor)) {
+			sysmon_envsys_destroy(sc->sc_sme);
+			sc->sc_sme = NULL;
+			aprint_error_dev(sc->sc_dev,
+			    "unable to attach sensor to sysmon\n");
+		} else {
+			sc->sc_sme->sme_name = device_xname(sc->sc_dev);
+			sc->sc_sme->sme_cookie = sc;
+			sc->sc_sme->sme_refresh = mk48txx_refresh;
+			if (sysmon_envsys_register(sc->sc_sme)) {
+				sysmon_envsys_destroy(sc->sc_sme);
+				sc->sc_sme = NULL;
+				aprint_error_dev(sc->sc_dev,
+				    "unable to register with sysmon\n");
+				sysmon_envsys_destroy(sc->sc_sme);
+			}
+		}
+	}
+
+	/* Setup sysctl for the oscillator control */
+	sysctl_createv(NULL, 0, NULL, &me,
+	    CTLFLAG_READWRITE,
+	    CTLTYPE_NODE, device_xname(sc->sc_dev), NULL,
+	    NULL, 0, NULL, 0,
+	    CTL_HW, CTL_CREATE, CTL_EOL);
+ 
+	if (me == NULL)
+		aprint_error_dev(sc->sc_dev, "unable to add sysctl root\n");
+	else {
+		sysctl_createv(NULL, 0, NULL, &node,
+		    CTLFLAG_READWRITE | CTLFLAG_OWNDESC,
+		    CTLTYPE_INT, "stop_oscillator", "Stop the chip oscillator",
+		    sysctl_mk48txx_osc, 1, (void *)sc, 0,
+		    CTL_HW, me->sysctl_num, CTL_CREATE, CTL_EOL);
+		if (node == NULL)
+			aprint_error_dev(sc->sc_dev,
+			    "unable to add sysctl node\n");
+	}
+ 
 	todr_attach(handle);
 }
 
@@ -223,3 +292,89 @@
 
 	bus_space_write_1(sc->sc_bst, sc->sc_bsh, off, v);
 }
+
+void
+mk48txx_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
+{
+	struct mk48txx_softc *sc = sme->sme_cookie;
+	uint8_t	flgs;
+
+	/*
+	 * The chip checks the battery every 24h, so we change the
+	 * monitoring timeout after the first refresh @ 30s.
+	 */
+	sme->sme_events_timeout = 86400;
+
+	flgs = (*sc->sc_nvrd)(sc, sc->sc_clkoffset + MK48TXX_IFLAGS);
+	if (flgs &= MK48TXX_FLAGS_BATTLOW) {
+		edata->value_cur = 1;
+		edata->state = ENVSYS_SCRITICAL;
+	} else {
+		edata->value_cur = 0;
+		edata->state = ENVSYS_SVALID;
+	}
+
+}
+
+static int
+sysctl_mk48txx_osc(SYSCTLFN_ARGS)
+{
+	struct sysctlnode node = *rnode;
+	struct mk48txx_softc *sc = node.sysctl_data;
+	todr_chip_handle_t handle = &sc->sc_handle;
+	bus_size_t clkoff;
+	int stp;
+	uint8_t sec;
+
+	if (newp) {
+		/* write */
+		clkoff = sc->sc_clkoffset;
+
+		stp = sc->sc_osc_stp;
+		node.sysctl_data = &stp;
+		if (sysctl_lookup(SYSCTLFN_CALL(&node)) == 0) {
+			if (stp != 0 && stp != 1)
+				return EINVAL;
+
+			if (stp != sc->sc_osc_stp) {
+				sc->sc_osc_stp = stp;
+				/*
+				 * We ignore any existing seconds values,
+				 * because we're either stopping or starting
+				 * the oscillator and the seconds
+				 * soon will be or are already incorrect.
+				 */
+				if (stp)
+					sec = MK48TXX_SEC_STOP;
+				else
+					sec = 0;
+				if (handle->todr_setwen != NULL)
+					handle->todr_setwen(handle, 1);
+				(*sc->sc_nvwr)(sc, clkoff + MK48TXX_ISEC,
+				     sec);
+				if (handle->todr_setwen != NULL)
+					handle->todr_setwen(handle, 0);
+			}
+
+			return 0;
+		}
+		return EINVAL;
+	} else {
+
+		node.sysctl_data = &sc->sc_osc_stp;
+		node.sysctl_size = 4;
+		return (sysctl_lookup(SYSCTLFN_CALL(&node)));
+	}
+
+	return 0;
+}
+
+SYSCTL_SETUP(sysctl_mk48txx_setup, "sysctl mk48txx subtree setup")
+{
+
+	sysctl_createv(NULL, 0, NULL, NULL,
+		       CTLFLAG_PERMANENT,
+		       CTLTYPE_NODE, "hw", NULL,
+		       NULL, 0, NULL, 0,
+		       CTL_HW, CTL_EOL);
+}
Index: src/sys/dev/ic/mk48txxvar.h
===================================================================
RCS file: /cvsroot/src/sys/dev/ic/mk48txxvar.h,v
retrieving revision 1.7
diff -u -r1.7 mk48txxvar.h
--- src/sys/dev/ic/mk48txxvar.h	4 Jan 2011 01:28:15 -0000	1.7
+++ src/sys/dev/ic/mk48txxvar.h	18 Jun 2026 15:03:15 -0000
@@ -28,6 +28,8 @@
  * POSSIBILITY OF SUCH DAMAGE.
  */
 
+#include <dev/sysmon/sysmonvar.h>
+
 struct mk48txx_softc;
 
 typedef uint8_t (*mk48txx_nvrd_t)(struct mk48txx_softc *, int);
@@ -49,9 +51,15 @@
 	u_int		sc_flag;
 #define MK48TXX_NO_CENT_ADJUST	0x0001
 #define MK48TXX_HAVE_CENT_REG	0x0002
+#define MK48TXX_EXT_REGISTERS	0x0004	/* Has extended register set */
 
 	mk48txx_nvrd_t	sc_nvrd;	/* NVRAM/RTC read function */
 	mk48txx_nvwr_t	sc_nvwr;	/* NVRAM/RTC write function */
+
+	struct sysmon_envsys	*sc_sme;	/* envsys config. */
+	envsys_data_t	sc_sensor;
+
+	int		sc_osc_stp;	/* Oscillator stop (sysctl) */
 };
 
 /* Chip attach function */
Index: src/share/man/man4/mk48txx.4
===================================================================
RCS file: /cvsroot/src/share/man/man4/mk48txx.4,v
retrieving revision 1.16
diff -u -r1.16 mk48txx.4
--- src/share/man/man4/mk48txx.4	10 Apr 2009 17:14:07 -0000	1.16
+++ src/share/man/man4/mk48txx.4	18 Jun 2026 15:03:15 -0000
@@ -52,6 +52,40 @@
 interface defined in
 .Xr todr 9 .
 .Pp
+M48T59 and compatible variants of the chip check the battery on power-up
+and every 24 hours, as long as the oscillator is running.
+They set an internal battery low flag if the battery voltage is less
+than approximately 2.5 V.
+The status of this flag is reported through the
+.Xr envstat 8
+interface.
+.Bl -column ".Li battery low" "TRUE/FALSE" -offset indent
+.It Sy Sensor     Ta Sy Units  Ta Sy Description
+.It Li battery low      Ta TRUE/FALSE    Ta Battery low alert
+.El
+.Pp
+The chip also supports stopping the oscillator to conserve battery life
+if the computer will be stored for an extended period.
+.Nm
+driver
+makes the oscillator control available via
+.Xr sysctl 8 .
+For example:
+.Bd -literal -offset indent
+hw.clock0.stop_oscillator = 0
+.Ed
+.Pp
+A value of
+.Dq 0
+means that the oscillator is running when the computer is powered off,
+and a value of
+.Dq 1
+means that the oscillator is stopped.
+If the oscillator is stopped, then the driver will output a warning message
+at attach time, but does not automatically restart the oscillator.
+Note, that on some computers, the firmware will restart the oscillator
+at power-on or reset time.
+.Pp
 To tie an instance of this device to the system, use the
 .Fn mk48txx_attach
 function and the mk48txx_softc structure defined as follows:
