1 /* $NetBSD: mk48txx.c,v 1.30 2026/09/03 05:52:43 jdc Exp $ */ 2 /*- 3 * Copyright (c) 2000 The NetBSD Foundation, Inc. 4 * All rights reserved. 5 * 6 * This code is derived from software contributed to The NetBSD Foundation 7 * by Paul Kranenburg. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 19 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 20 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 21 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 22 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 * POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 /* 32 * Mostek MK48T02, MK48T08, MK48T59 time-of-day chip subroutines. 33 */ 34 35 #include <sys/cdefs.h> 36 __KERNEL_RCSID(0, "$NetBSD: mk48txx.c,v 1.30 2026/09/03 05:52:43 jdc Exp $"); 37 38 #include <sys/param.h> 39 #include <sys/systm.h> 40 #include <sys/device.h> 41 #include <sys/errno.h> 42 #include <sys/sysctl.h> 43 44 #include <sys/bus.h> 45 #include <dev/clock_subr.h> 46 #include <dev/ic/mk48txxreg.h> 47 #include <dev/ic/mk48txxvar.h> 48 49 int mk48txx_gettime_ymdhms(todr_chip_handle_t, struct clock_ymdhms *); 50 int mk48txx_settime_ymdhms(todr_chip_handle_t, struct clock_ymdhms *); 51 uint8_t mk48txx_def_nvrd(struct mk48txx_softc *, int); 52 void mk48txx_def_nvwr(struct mk48txx_softc *, int, uint8_t); 53 void mk48txx_refresh(struct sysmon_envsys *, envsys_data_t *); 54 static int sysctl_mk48txx_osc(SYSCTLFN_ARGS); 55 56 const struct { 57 const char *name; 58 bus_size_t nvramsz; 59 bus_size_t clkoff; 60 int flags; 61 } mk48txx_models[] = { 62 { "mk48t02", MK48T02_CLKSZ, MK48T02_CLKOFF, 0 }, 63 { "mk48t08", MK48T08_CLKSZ, MK48T08_CLKOFF, 0 }, 64 { "mk48t18", MK48T18_CLKSZ, MK48T18_CLKOFF, 0 }, 65 { "mk48t59", MK48T59_CLKSZ, MK48T59_CLKOFF, MK48TXX_EXT_REGISTERS }, 66 { "ds1553", DS1553_CLKSZ, DS1553_CLKOFF, MK48TXX_EXT_REGISTERS }, 67 }; 68 69 void 70 mk48txx_attach(struct mk48txx_softc *sc) 71 { 72 todr_chip_handle_t handle; 73 const struct sysctlnode *me = NULL, *node = NULL; 74 int i; 75 uint8_t csr; 76 77 aprint_normal(": %s", sc->sc_model); 78 79 i = __arraycount(mk48txx_models); 80 while (--i >= 0) { 81 if (strcmp(sc->sc_model, mk48txx_models[i].name) == 0) 82 break; 83 } 84 if (i < 0) 85 panic("%s: unsupported model", __func__); 86 87 sc->sc_nvramsz = mk48txx_models[i].nvramsz; 88 sc->sc_clkoffset = mk48txx_models[i].clkoff; 89 sc->sc_flag |= mk48txx_models[i].flags; 90 91 handle = &sc->sc_handle; 92 KASSERT(sc->sc_dev != NULL); 93 handle->todr_dev = sc->sc_dev; 94 handle->todr_gettime_ymdhms = mk48txx_gettime_ymdhms; 95 handle->todr_settime_ymdhms = mk48txx_settime_ymdhms; 96 97 if (sc->sc_nvrd == NULL) 98 sc->sc_nvrd = mk48txx_def_nvrd; 99 if (sc->sc_nvwr == NULL) 100 sc->sc_nvwr = mk48txx_def_nvwr; 101 102 csr = (*sc->sc_nvrd)(sc, sc->sc_clkoffset + MK48TXX_ISEC); 103 if (csr & MK48TXX_SEC_STOP) { 104 aprint_normal("\n"); 105 aprint_error_dev(sc->sc_dev, 106 "WARNING: oscillator is stopped (0x%02x)", csr); 107 sc->sc_osc_stp = 1; 108 } else 109 sc->sc_osc_stp = 0; 110 111 /* Setup envsys if the chip has the battery low flag */ 112 if (sc->sc_flag & MK48TXX_EXT_REGISTERS) { 113 sc->sc_sme = sysmon_envsys_create(); 114 115 sc->sc_sensor.units = ENVSYS_INDICATOR; 116 sc->sc_sensor.state = ENVSYS_SINVALID; 117 sc->sc_sensor.value_cur = 0; 118 sc->sc_sensor.flags |= ENVSYS_FMONCRITICAL; 119 (void)strlcpy(sc->sc_sensor.desc, "battery low", 120 sizeof(sc->sc_sensor.desc)); 121 if (sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor)) { 122 sysmon_envsys_destroy(sc->sc_sme); 123 sc->sc_sme = NULL; 124 aprint_error_dev(sc->sc_dev, 125 "unable to attach sensor to sysmon\n"); 126 } else { 127 sc->sc_sme->sme_name = device_xname(sc->sc_dev); 128 sc->sc_sme->sme_cookie = sc; 129 sc->sc_sme->sme_refresh = mk48txx_refresh; 130 if (sysmon_envsys_register(sc->sc_sme)) { 131 sysmon_envsys_destroy(sc->sc_sme); 132 sc->sc_sme = NULL; 133 aprint_error_dev(sc->sc_dev, 134 "unable to register with sysmon\n"); 135 sysmon_envsys_destroy(sc->sc_sme); 136 } 137 } 138 } 139 140 /* Setup sysctl for the oscillator control */ 141 sysctl_createv(NULL, 0, NULL, &me, 142 CTLFLAG_READWRITE, 143 CTLTYPE_NODE, device_xname(sc->sc_dev), NULL, 144 NULL, 0, NULL, 0, 145 CTL_HW, CTL_CREATE, CTL_EOL); 146 147 if (me == NULL) 148 aprint_error_dev(sc->sc_dev, "unable to add sysctl root\n"); 149 else { 150 sysctl_createv(NULL, 0, NULL, &node, 151 CTLFLAG_READWRITE | CTLFLAG_OWNDESC, 152 CTLTYPE_INT, "stop_oscillator", "Stop the chip oscillator", 153 sysctl_mk48txx_osc, 1, (void *)sc, 0, 154 CTL_HW, me->sysctl_num, CTL_CREATE, CTL_EOL); 155 if (node == NULL) 156 aprint_error_dev(sc->sc_dev, 157 "unable to add sysctl node\n"); 158 } 159 160 todr_attach(handle); 161 } 162 163 /* 164 * Get time-of-day and convert to a `struct timeval' 165 * Return 0 on success; an error number otherwise. 166 */ 167 int 168 mk48txx_gettime_ymdhms(todr_chip_handle_t handle, struct clock_ymdhms *dt) 169 { 170 struct mk48txx_softc *sc = device_private(handle->todr_dev); 171 bus_size_t clkoff; 172 int year; 173 uint8_t csr; 174 175 clkoff = sc->sc_clkoffset; 176 177 /* enable read (stop time) */ 178 csr = (*sc->sc_nvrd)(sc, clkoff + MK48TXX_ICSR); 179 csr |= MK48TXX_CSR_READ; 180 (*sc->sc_nvwr)(sc, clkoff + MK48TXX_ICSR, csr); 181 182 dt->dt_sec = bcdtobin((*sc->sc_nvrd)(sc, clkoff + MK48TXX_ISEC)); 183 dt->dt_min = bcdtobin((*sc->sc_nvrd)(sc, clkoff + MK48TXX_IMIN)); 184 dt->dt_hour = bcdtobin((*sc->sc_nvrd)(sc, clkoff + MK48TXX_IHOUR)); 185 dt->dt_day = bcdtobin((*sc->sc_nvrd)(sc, clkoff + MK48TXX_IDAY)); 186 dt->dt_wday = bcdtobin((*sc->sc_nvrd)(sc, clkoff + MK48TXX_IWDAY)); 187 dt->dt_mon = bcdtobin((*sc->sc_nvrd)(sc, clkoff + MK48TXX_IMON)); 188 year = bcdtobin((*sc->sc_nvrd)(sc, clkoff + MK48TXX_IYEAR)); 189 190 if (sc->sc_flag & MK48TXX_HAVE_CENT_REG) { 191 year += 100*bcdtobin(csr & MK48TXX_CSR_CENT_MASK); 192 } else { 193 year += sc->sc_year0; 194 if (year < POSIX_BASE_YEAR && 195 (sc->sc_flag & MK48TXX_NO_CENT_ADJUST) == 0) 196 year += 100; 197 } 198 199 dt->dt_year = year; 200 201 /* time wears on */ 202 csr = (*sc->sc_nvrd)(sc, clkoff + MK48TXX_ICSR); 203 csr &= ~MK48TXX_CSR_READ; 204 (*sc->sc_nvwr)(sc, clkoff + MK48TXX_ICSR, csr); 205 206 return 0; 207 } 208 209 /* 210 * Set the time-of-day clock based on the value of the `struct timeval' arg. 211 * Return 0 on success; an error number otherwise. 212 */ 213 int 214 mk48txx_settime_ymdhms(todr_chip_handle_t handle, struct clock_ymdhms *dt) 215 { 216 struct mk48txx_softc *sc = device_private(handle->todr_dev); 217 bus_size_t clkoff; 218 uint8_t csr; 219 int year; 220 int cent; 221 222 clkoff = sc->sc_clkoffset; 223 224 if ((sc->sc_flag & MK48TXX_HAVE_CENT_REG) == 0) { 225 cent = 0; 226 year = dt->dt_year - sc->sc_year0; 227 if (year > 99 && 228 (sc->sc_flag & MK48TXX_NO_CENT_ADJUST) == 0) 229 year -= 100; 230 } else { 231 cent = dt->dt_year / 100; 232 year = dt->dt_year % 100; 233 } 234 235 /* enable write */ 236 csr = (*sc->sc_nvrd)(sc, clkoff + MK48TXX_ICSR); 237 csr |= MK48TXX_CSR_WRITE; 238 (*sc->sc_nvwr)(sc, clkoff + MK48TXX_ICSR, csr); 239 240 (*sc->sc_nvwr)(sc, clkoff + MK48TXX_ISEC, bintobcd(dt->dt_sec)); 241 (*sc->sc_nvwr)(sc, clkoff + MK48TXX_IMIN, bintobcd(dt->dt_min)); 242 (*sc->sc_nvwr)(sc, clkoff + MK48TXX_IHOUR, bintobcd(dt->dt_hour)); 243 (*sc->sc_nvwr)(sc, clkoff + MK48TXX_IWDAY, bintobcd(dt->dt_wday)); 244 (*sc->sc_nvwr)(sc, clkoff + MK48TXX_IDAY, bintobcd(dt->dt_day)); 245 (*sc->sc_nvwr)(sc, clkoff + MK48TXX_IMON, bintobcd(dt->dt_mon)); 246 (*sc->sc_nvwr)(sc, clkoff + MK48TXX_IYEAR, bintobcd(year)); 247 248 /* 249 * If we have a century register and the century has changed 250 * update it. 251 */ 252 if ((sc->sc_flag & MK48TXX_HAVE_CENT_REG) 253 && (csr & MK48TXX_CSR_CENT_MASK) != bintobcd(cent)) { 254 csr &= ~MK48TXX_CSR_CENT_MASK; 255 csr |= MK48TXX_CSR_CENT_MASK & bintobcd(cent); 256 (*sc->sc_nvwr)(sc, clkoff + MK48TXX_ICSR, csr); 257 } 258 259 /* load them up */ 260 csr = (*sc->sc_nvrd)(sc, clkoff + MK48TXX_ICSR); 261 csr &= ~MK48TXX_CSR_WRITE; 262 (*sc->sc_nvwr)(sc, clkoff + MK48TXX_ICSR, csr); 263 264 return 0; 265 } 266 267 int 268 mk48txx_get_nvram_size(todr_chip_handle_t handle, bus_size_t *vp) 269 { 270 struct mk48txx_softc *sc = device_private(handle->todr_dev); 271 272 *vp = sc->sc_nvramsz; 273 return 0; 274 } 275 276 uint8_t 277 mk48txx_def_nvrd(struct mk48txx_softc *sc, int off) 278 { 279 280 return bus_space_read_1(sc->sc_bst, sc->sc_bsh, off); 281 } 282 283 void 284 mk48txx_def_nvwr(struct mk48txx_softc *sc, int off, uint8_t v) 285 { 286 287 bus_space_write_1(sc->sc_bst, sc->sc_bsh, off, v); 288 } 289 290 void 291 mk48txx_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) 292 { 293 struct mk48txx_softc *sc = sme->sme_cookie; 294 uint8_t flgs; 295 296 /* 297 * The chip checks the battery every 24h, so we change the 298 * monitoring timeout after the first refresh @ 30s. 299 */ 300 sme->sme_events_timeout = 86400; 301 302 flgs = (*sc->sc_nvrd)(sc, sc->sc_clkoffset + MK48TXX_IFLAGS); 303 if (flgs &= MK48TXX_FLAGS_BATTLOW) { 304 edata->value_cur = 1; 305 edata->state = ENVSYS_SCRITICAL; 306 } else { 307 edata->value_cur = 0; 308 edata->state = ENVSYS_SVALID; 309 } 310 311 } 312 313 static int 314 sysctl_mk48txx_osc(SYSCTLFN_ARGS) 315 { 316 struct sysctlnode node = *rnode; 317 struct mk48txx_softc *sc = node.sysctl_data; 318 todr_chip_handle_t handle = &sc->sc_handle; 319 bus_size_t clkoff; 320 int stp; 321 uint8_t sec; 322 323 if (newp) { 324 /* write */ 325 clkoff = sc->sc_clkoffset; 326 327 stp = sc->sc_osc_stp; 328 node.sysctl_data = &stp; 329 if (sysctl_lookup(SYSCTLFN_CALL(&node)) == 0) { 330 if (stp != 0 && stp != 1) 331 return EINVAL; 332 333 if (stp != sc->sc_osc_stp) { 334 sc->sc_osc_stp = stp; 335 /* 336 * We ignore any existing seconds values, 337 * because we're either stopping or starting 338 * the oscillator and the seconds 339 * soon will be or are already incorrect. 340 */ 341 if (stp) 342 sec = MK48TXX_SEC_STOP; 343 else 344 sec = 0; 345 if (handle->todr_setwen != NULL) 346 handle->todr_setwen(handle, 1); 347 (*sc->sc_nvwr)(sc, clkoff + MK48TXX_ISEC, 348 sec); 349 if (handle->todr_setwen != NULL) 350 handle->todr_setwen(handle, 0); 351 } 352 353 return 0; 354 } 355 return EINVAL; 356 } else { 357 358 node.sysctl_data = &sc->sc_osc_stp; 359 node.sysctl_size = 4; 360 return (sysctl_lookup(SYSCTLFN_CALL(&node))); 361 } 362 363 return 0; 364 } 365