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	19 Jun 2026 14:23:53 -0000
@@ -1125,7 +1125,7 @@
 
 # Motorola mc146818 (and compatible) time-of-day clock
 #
-define	mc146818
+define	mc146818: sysmon_envsys
 file	dev/ic/mc146818.c		mc146818
 
 # Ricoh RS5C313 time of-day-clock
Index: src/sys/dev/ic/mc146818.c
===================================================================
RCS file: /cvsroot/src/sys/dev/ic/mc146818.c,v
retrieving revision 1.22
diff -u -r1.22 mc146818.c
--- src/sys/dev/ic/mc146818.c	7 Sep 2025 21:45:16 -0000	1.22
+++ src/sys/dev/ic/mc146818.c	19 Jun 2026 14:23:53 -0000
@@ -35,6 +35,7 @@
 #include <sys/systm.h>
 #include <sys/device.h>
 #include <sys/errno.h>
+#include <sys/sysctl.h>
 
 #include <sys/bus.h>
 
@@ -43,10 +44,14 @@
 #include <dev/ic/mc146818reg.h>
 #include <dev/ic/mc146818var.h>
 
+static int	sysctl_mc146818_osc(SYSCTLFN_ARGS);
+
 void
 mc146818_attach(struct mc146818_softc *sc)
 {
 	todr_chip_handle_t handle;
+	const struct sysctlnode *me = NULL, *node = NULL;
+	u_int rega, regd;
 
 #ifdef DIAGNOSTIC
 	if (sc->sc_mcread == NULL ||
@@ -67,6 +72,72 @@
 		handle->todr_settime_ymdhms = mc146818_settime_ymdhms;
 	}
 
+	/* Setup envsys for the valid RAM and time bit */
+	regd = (*sc->sc_mcread)(sc, MC_REGD);
+	sc->sc_vrt = regd & MC_REGD_VRT;
+	if (!(sc->sc_vrt)) {
+		aprint_normal("\n");
+		aprint_error_dev(sc->sc_dev,
+		    "WARNING: battery is low (0x%02x)", regd);
+	}
+
+	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 = mc146818_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");
+		}
+	}
+
+	sc->sc_osc_stp = 0;
+	if (sc->sc_flag & MC146818_OSC_CTRL) {
+		/* Check the oscillator state */
+		rega = (*sc->sc_mcread)(sc, MC_REGA);
+		if ((rega & MC_REGA_OSMASK) == MC_OSC_OFF) {
+			aprint_normal("\n");
+			aprint_error_dev(sc->sc_dev,
+			    "WARNING: oscillator is stopped (0x%02x)", rega);
+			sc->sc_osc_stp = 1;
+		}
+
+		/* 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_mc146818_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);
 }
 
@@ -173,3 +244,74 @@
 
 	return 0;
 }
+
+/* Refresh for sysmon */
+void
+mc146818_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
+{
+	struct mc146818_softc *sc = sme->sme_cookie;
+	u_int regd = 0x0d;
+
+	/*
+	 * The valid RAM and time bit is only set at power-up,
+	 * so we always return the saved value.
+	 * Note, that we make vrt 0 into low battery 1.
+	 */
+	if (sc->sc_vrt) {
+		edata->value_cur = 0;
+		edata->state = ENVSYS_SVALID;
+	} else {
+		edata->value_cur = 1;
+		edata->state = ENVSYS_SCRITICAL;
+	}
+}
+
+/* Oscillator control routine for sysctl */
+static int
+sysctl_mc146818_osc(SYSCTLFN_ARGS)
+{
+	struct sysctlnode node = *rnode;
+	struct mc146818_softc *sc = node.sysctl_data;
+	int stp;
+	u_int rega;
+
+	if (newp) {
+		/* write */
+		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;
+				rega =  (*sc->sc_mcread)(sc, MC_REGA);
+				rega &= ~MC_REGA_OSMASK;
+				if (stp)
+					rega |= MC_OSC_OFF;
+				else
+					rega |= sc->sc_osc_on;
+				(*sc->sc_mcwrite)(sc, MC_REGA, rega);
+			}
+
+			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_mc146818_setup, "sysctl mc146818 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/mc146818reg.h
===================================================================
RCS file: /cvsroot/src/sys/dev/ic/mc146818reg.h,v
retrieving revision 1.9
diff -u -r1.9 mc146818reg.h
--- src/sys/dev/ic/mc146818reg.h	8 Mar 2006 23:46:25 -0000	1.9
+++ src/sys/dev/ic/mc146818reg.h	19 Jun 2026 14:23:53 -0000
@@ -36,6 +36,17 @@
  * The MC146818A has 16 registers.  The first 10 contain time-of-year
  * and alarm data.  The rest contain various control and status bits.
  *
+ * The Dallas DS1287A is compatible with the MC146818A but does not have
+ * the divider select.  These bits are used to turn the oscillator on or
+ * off and reset the countdown chain.
+ *
+ * ACPI-compatible chips like the NS PC87317 include a MC146818A-compatible
+ * RTC as part of a bank selectable region.  The banks are:
+ *  0 - combined general purpose
+ *  1 - real-time clock
+ *  2 - advanced power control block
+ * The divider select bits are used to select the bank.
+ *
  * To read or write the registers, one writes the register number to
  * the RTC's control port, then either reads from or writes the new
  * data to the RTC's data port.  Since the locations of these ports
@@ -86,6 +97,7 @@
 #define	 MC_REGA_RSMASK	0x0f	/* Interrupt rate select mask (see below) */
 #define	 MC_REGA_DVMASK	0x70	/* Divisor select mask (see below) */
 #define	 MC_REGA_UIP	0x80	/* Update in progress; read only. */
+#define	 MC_REGA_OSMASK MC_REGA_DVMASK	/* Oscillator ctrl mask ( see below) */
 
 #define	MC_REGB		0xb	/* Control register B */
 
@@ -118,6 +130,19 @@
 #define	MC_NVRAM_START	0xe	/* start of NVRAM: offset 14 */
 #define	MC_NVRAM_SIZE	50	/* 50 bytes of NVRAM */
 
+/* 
+ * ACPI Bank 2 registers, containing the address of the data.
+ * Bit 7 selects the bank (0x00 bank 0, 0x80 bank 1).
+ * Bits 6-0 contain the address in the bank.
+ * Note, only RTC-related registers are defined here.
+ */
+#define PC_BANK2_DADDR	0x4f	/* Day-of-Month Alarm Address */
+#define PC_BANK2_MADDR	0x50	/* Month Alarm Address */
+#define PC_BANK2_CADDR	0x51	/* Century Address */
+#define PC_CADDR_DEF	0xc8	/* Default CADDR (Century Address) */
+#define PC_CADDR_BANK1	0x80	/* CADDR is bank 1 (bank 0 if not set) */
+#define PC_CADDR_ADMSK	0x7f	/* CADDR address mask */
+
 /*
  * Periodic Interrupt Rate Select constants (Control register A)
  */
@@ -139,13 +164,22 @@
 #define	MC_RATE_2_Hz	0xf	/* 500 ms period */
 
 /*
- * Time base (divisor select) constants (Control register A)
+ * MC146818 Time base (divisor select) constants
+ * DS1287 oscillator constants
+ * ACPI-compatible bank switching constants
+ * (Control register A)
+ * 
  */
 #define	MC_BASE_4_MHz	0x00		/* 4 MHz crystal */
 #define	MC_BASE_1_MHz	0x10		/* 1 MHz crystal */
 #define	MC_BASE_32_KHz	0x20		/* 32 kHz crystal */
 #define	MC_BASE_NONE	0x60		/* actually, both of these reset */
 #define	MC_BASE_RESET	0x70
+#define	MC_OSC_OFF	0x00		/* Oscillator off (DS1287)*/
+#define	MC_OSC_ON	0x20		/* Oscillator on (DS1287)*/
+#define	MC_BANK_SEL0	0x20		/* Bank select 0 and osc. on (ACPI) */
+#define	MC_BANK_SEL1	0x30		/* Bank select 1 and osc. on (ACPI) */
+#define	MC_BANK_SEL2	0x40		/* Bank select 2 and osc. on (ACPI) */
 
 #ifndef USE_TODR_MCCLOCK
 /*
Index: src/sys/dev/ic/mc146818var.h
===================================================================
RCS file: /cvsroot/src/sys/dev/ic/mc146818var.h,v
retrieving revision 1.8
diff -u -r1.8 mc146818var.h
--- src/sys/dev/ic/mc146818var.h	6 Mar 2024 02:31:44 -0000	1.8
+++ src/sys/dev/ic/mc146818var.h	19 Jun 2026 14:23:53 -0000
@@ -27,6 +27,9 @@
 #ifndef _DEV_IC_MC146818VAR_H_
 #define	_DEV_IC_MC146818VAR_H_
 
+#include <dev/sysmon/sysmonvar.h>
+
+
 struct mc146818_softc {
 	device_t sc_dev;
 
@@ -39,6 +42,14 @@
 #define MC146818_NO_CENT_ADJUST	0x0001		/* don't adjust century */
 #define MC146818_BCD		0x0002		/* use BCD mode */
 #define MC146818_12HR		0x0004		/* use AM/PM mode */
+#define MC146818_OSC_CTRL	0x0008		/* Oscillator ctrl (DS1287) */
+	u_int sc_osc_on;			/* Oscillator on val */
+
+	struct sysmon_envsys    *sc_sme;	/* envsys config. */
+	envsys_data_t		sc_sensor;
+	int			sc_vrt;		/* Valid RAM and time bit */
+
+	int			sc_osc_stp;	/* Oscillator stop (sysctl) */
 
 	/* MD chip register read/write functions */
 	u_int (*sc_mcread)(struct mc146818_softc *, u_int);
@@ -51,5 +62,6 @@
 void	mc146818_attach(struct mc146818_softc *);
 int	mc146818_gettime_ymdhms(todr_chip_handle_t, struct clock_ymdhms *);
 int	mc146818_settime_ymdhms(todr_chip_handle_t, struct clock_ymdhms *);
+void	mc146818_refresh(struct sysmon_envsys *, envsys_data_t *);
 
 #endif /* _DEV_IC_MC146818VAR_H_ */
Index: src/share/man/man4/mcclock.4
===================================================================
RCS file: /cvsroot/src/share/man/man4/mcclock.4,v
retrieving revision 1.6
diff -u -r1.6 mcclock.4
--- src/share/man/man4/mcclock.4	30 Apr 2008 13:10:54 -0000	1.6
+++ src/share/man/man4/mcclock.4	19 Jun 2026 14:23:53 -0000
@@ -32,7 +32,7 @@
 .Os
 .Sh NAME
 .Nm mcclock
-.Nd DS1287 real-time clock
+.Nd MC146816, DS1287, and compatibles real-time clock
 .Sh SYNOPSIS
 .Ss algor
 .Cd "mcclock* at isa? port 0x70"
@@ -50,12 +50,59 @@
 .Cd "mcclock* at ioasic? offset ?"
 .Ss sgimips
 .Cd "mcclock* at mace0 offset 0x3a0000"
+.Ss sparc64
+.Cd "rtc0 at ebus0 addr 300070-300071 ipl 24: mc146818 compatible time-of-day clock: ds1287"
+.Cd "rtc0 at ebus0 addr 70-71: mc146818 compatible time-of-day clock: m5819p"
 .Sh DESCRIPTION
 The
 .Nm
-driver provides support for the DS1287 real-time clock (RTC).  Note
-that the kernel expects the RTC to run in UTC.
+driver provides support for the MC146818, DS1287 and compatibles
+real-time clock (RTC).
+Note, that the kernel expects the RTC to run in UTC.
+Access methods to retrieve and set date and time
+are provided through the
+.Em TODR
+interface defined in
+.Xr todr 9 .
+.Pp
+The chip checks the battery on power-up, and alters an internal
+.EM RAM and Time bit
+if the external battery source is exhausted.
+The status of this bit 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
+DS1287 and compatible chips also support stopping the oscillator to
+conserve battery life if the computer will be stored for an extended
+period.
+The
+.Nm
+driver
+makes the oscillator control available via
+.Xr sysctl 8 .
+For example:
+.Bd -literal -offset indent
+hw.mcclock0.stop_oscillator = 0
+hw.rtc0.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.
 .Sh SEE ALSO
 .Xr intro 4 ,
+.Xr ebus 4 ,
 .Xr ioasic 4 ,
 .Xr isa 4
+.Xr envstat 8 ,
+.Xr sysctl 8,
+.Xr todr 9
