Index: src/sys/conf/files =================================================================== RCS file: /cvsroot/src/sys/conf/files,v retrieving revision 1.1317 diff -u -r1.1317 files --- src/sys/conf/files 24 Oct 2025 23:16:11 -0000 1.1317 +++ src/sys/conf/files 8 Jun 2026 16:12:31 -0000 @@ -1104,7 +1104,7 @@ # Mostek time-of-day clock and NVRAM # -define mk48txx +device 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 8 Jun 2026 16:12:31 -0000 @@ -39,6 +39,7 @@ #include #include #include +#include #include #include @@ -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; 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,7 +99,64 @@ 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_error_dev(sc->sc_dev, + "WARNING: oscillator is stopped (0x%02x)\n", csr); + sc->sc_osc_stp = 1; + } else + sc->sc_osc_stp = 0; + todr_attach(handle); + + /* Setup envsys if the chip has the battery low flag */ + if (sc->sc_flag & MK48TXX_EXT_REGISTERS) { + sc->sc_sme = sysmon_envsys_create(); + + sc->sc_sensor.units = ENVSYS_INDICATOR; + sc->sc_sensor.state = ENVSYS_SINVALID; + 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) + printf("%s: unable to add sysctl root\n", + device_xname(sc->sc_dev)); + 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) + printf("%s: unable to add sysctl node\n", + device_xname(sc->sc_dev)); + } } /* @@ -223,3 +285,76 @@ 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; + + flgs = (*sc->sc_nvrd)(sc, sc->sc_clkoffset + MK48TXX_ISEC); + if (flgs &= MK48TXX_FLAGS_BATTLOW) + edata->state = ENVSYS_SCRITICAL; + else + edata->state = ENVSYS_SVALID; + +} + +static int +sysctl_mk48txx_osc(SYSCTLFN_ARGS) +{ + struct sysctlnode node = *rnode; + struct mk48txx_softc *sc = node.sysctl_data; + 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; +printf("%s: set oscillator stop: %d (0x%02x)\n", device_xname(sc->sc_dev), stp, sec); + (*sc->sc_nvwr)(sc, clkoff + MK48TXX_ISEC, + MK48TXX_SEC_STOP); + } + + 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 8 Jun 2026 16:12:31 -0000 @@ -28,6 +28,8 @@ * POSSIBILITY OF SUCH DAMAGE. */ +#include + 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 8 Jun 2026 16:12:31 -0000 @@ -52,6 +52,38 @@ interface defined in .Xr todr 9 . .Pp +The chip checks the battery on power-up and every 24 hours, as long as +the oscillator is running. +It sets an internal battery low flag if the battery voltage is less +than approximately 2.5 V. +The status of the 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. +.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: