1*b5e88448Saoyama /* $OpenBSD: timekeeper.c,v 1.7 2013/08/10 22:27:13 aoyama Exp $ */ 202b01b5eSaoyama /* $NetBSD: timekeeper.c,v 1.1 2000/01/05 08:48:56 nisimura Exp $ */ 302b01b5eSaoyama 402b01b5eSaoyama /*- 502b01b5eSaoyama * Copyright (c) 2000 The NetBSD Foundation, Inc. 602b01b5eSaoyama * All rights reserved. 702b01b5eSaoyama * 802b01b5eSaoyama * This code is derived from software contributed to The NetBSD Foundation 902b01b5eSaoyama * by Tohru Nishimura. 1002b01b5eSaoyama * 1102b01b5eSaoyama * Redistribution and use in source and binary forms, with or without 1202b01b5eSaoyama * modification, are permitted provided that the following conditions 1302b01b5eSaoyama * are met: 1402b01b5eSaoyama * 1. Redistributions of source code must retain the above copyright 1502b01b5eSaoyama * notice, this list of conditions and the following disclaimer. 1602b01b5eSaoyama * 2. Redistributions in binary form must reproduce the above copyright 1702b01b5eSaoyama * notice, this list of conditions and the following disclaimer in the 1802b01b5eSaoyama * documentation and/or other materials provided with the distribution. 1902b01b5eSaoyama * 2002b01b5eSaoyama * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 2102b01b5eSaoyama * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 2202b01b5eSaoyama * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 2302b01b5eSaoyama * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 2402b01b5eSaoyama * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 2502b01b5eSaoyama * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 2602b01b5eSaoyama * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 2702b01b5eSaoyama * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 2802b01b5eSaoyama * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 2902b01b5eSaoyama * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 3002b01b5eSaoyama * POSSIBILITY OF SUCH DAMAGE. 3102b01b5eSaoyama */ 3202b01b5eSaoyama 3302b01b5eSaoyama #include <sys/param.h> 3402b01b5eSaoyama #include <sys/systm.h> 3502b01b5eSaoyama #include <sys/device.h> 3602b01b5eSaoyama #include <sys/kernel.h> 370781ad23Saoyama #include <sys/evcount.h> 3802b01b5eSaoyama 39110e3210Saoyama #include <machine/autoconf.h> 4002b01b5eSaoyama #include <machine/board.h> /* machtype value */ 4102b01b5eSaoyama #include <machine/cpu.h> 4202b01b5eSaoyama 4302b01b5eSaoyama #include <dev/clock_subr.h> 44110e3210Saoyama 4502b01b5eSaoyama #include <luna88k/luna88k/clockvar.h> 4602b01b5eSaoyama #include <luna88k/dev/timekeeper.h> 4702b01b5eSaoyama 4802b01b5eSaoyama #define MK_YEAR0 1970 /* year offset of MK */ 4902b01b5eSaoyama #define DS_YEAR0 1990 /* year offset of DS */ 5002b01b5eSaoyama 5102b01b5eSaoyama struct timekeeper_softc { 5202b01b5eSaoyama struct device sc_dev; 5302b01b5eSaoyama void *sc_clock, *sc_nvram; 5402b01b5eSaoyama int sc_nvramsize; 550781ad23Saoyama struct evcount sc_count; 5602b01b5eSaoyama }; 5702b01b5eSaoyama 5802b01b5eSaoyama /* 5902b01b5eSaoyama * BCD to decimal and decimal to BCD. 6002b01b5eSaoyama */ 6102b01b5eSaoyama #define FROMBCD(x) (((x) >> 4) * 10 + ((x) & 0xf)) 6202b01b5eSaoyama #define TOBCD(x) (((x) / 10 * 16) + ((x) % 10)) 6302b01b5eSaoyama 6402b01b5eSaoyama int clock_match(struct device *, void *, void *); 6502b01b5eSaoyama void clock_attach(struct device *, struct device *, void *); 6602b01b5eSaoyama 6702b01b5eSaoyama struct cfattach clock_ca = { 6802b01b5eSaoyama sizeof (struct timekeeper_softc), clock_match, clock_attach 6902b01b5eSaoyama }; 7002b01b5eSaoyama 7102b01b5eSaoyama struct cfdriver clock_cd = { 7202b01b5eSaoyama NULL, "clock", DV_DULL 7302b01b5eSaoyama }; 7402b01b5eSaoyama 7502b01b5eSaoyama void mkclock_get(struct device *, time_t, struct clock_ymdhms *); 7602b01b5eSaoyama void mkclock_set(struct device *, struct clock_ymdhms *); 7702b01b5eSaoyama void dsclock_get(struct device *, time_t, struct clock_ymdhms *); 7802b01b5eSaoyama void dsclock_set(struct device *, struct clock_ymdhms *); 7902b01b5eSaoyama 8002b01b5eSaoyama const struct clockfns mkclock_clockfns = { 8102b01b5eSaoyama NULL /* never used */, mkclock_get, mkclock_set, 8202b01b5eSaoyama }; 8302b01b5eSaoyama 8402b01b5eSaoyama const struct clockfns dsclock_clockfns = { 8502b01b5eSaoyama NULL /* never used */, dsclock_get, dsclock_set, 8602b01b5eSaoyama }; 8702b01b5eSaoyama 8802b01b5eSaoyama int 8902b01b5eSaoyama clock_match(parent, match, aux) 9002b01b5eSaoyama struct device *parent; 9102b01b5eSaoyama void *match, *aux; 9202b01b5eSaoyama { 9302b01b5eSaoyama struct mainbus_attach_args *ma = aux; 9402b01b5eSaoyama 9502b01b5eSaoyama if (strcmp(ma->ma_name, clock_cd.cd_name)) 9602b01b5eSaoyama return 0; 9702b01b5eSaoyama return 1; 9802b01b5eSaoyama } 9902b01b5eSaoyama 10002b01b5eSaoyama extern int machtype; /* in machdep.c */ 10102b01b5eSaoyama 10202b01b5eSaoyama void 10302b01b5eSaoyama clock_attach(parent, self, aux) 10402b01b5eSaoyama struct device *parent, *self; 10502b01b5eSaoyama void *aux; 10602b01b5eSaoyama { 10702b01b5eSaoyama struct timekeeper_softc *sc = (void *)self; 10802b01b5eSaoyama struct mainbus_attach_args *ma = aux; 10902b01b5eSaoyama const struct clockfns *clockwork; 11002b01b5eSaoyama 11102b01b5eSaoyama switch (machtype) { 11202b01b5eSaoyama default: 11302b01b5eSaoyama case LUNA_88K: /* Mostek MK48T02 */ 114*b5e88448Saoyama sc->sc_clock = (void *)(ma->ma_addr + MK_NVRAM_SPACE); 11502b01b5eSaoyama sc->sc_nvram = (void *)ma->ma_addr; 116*b5e88448Saoyama sc->sc_nvramsize = MK_NVRAM_SPACE; 11702b01b5eSaoyama clockwork = &mkclock_clockfns; 11802b01b5eSaoyama printf(": MK48T02\n"); 11902b01b5eSaoyama break; 12002b01b5eSaoyama case LUNA_88K2: /* Dallas DS1397 */ 12102b01b5eSaoyama sc->sc_clock = (void *)ma->ma_addr; 12202b01b5eSaoyama sc->sc_nvram = (void *)(ma->ma_addr + 50); 12302b01b5eSaoyama sc->sc_nvramsize = 50; 12402b01b5eSaoyama clockwork = &dsclock_clockfns; 12502b01b5eSaoyama printf(": DS1397\n"); 12602b01b5eSaoyama break; 12702b01b5eSaoyama } 1280781ad23Saoyama 1294667bebbSmatthew evcount_attach(&sc->sc_count, self->dv_xname, &ma->ma_ilvl); 1300781ad23Saoyama 1310781ad23Saoyama clockattach(&sc->sc_dev, clockwork, &sc->sc_count); 13202b01b5eSaoyama } 13302b01b5eSaoyama 13402b01b5eSaoyama /* 135*b5e88448Saoyama * On LUNA-88K, NVRAM contents and Timekeeper registers are mapped on the 136*b5e88448Saoyama * most significant byte of each 32bit word. (i.e. 4-bytes stride) 137*b5e88448Saoyama * 13802b01b5eSaoyama * Get the time of day, based on the clock's value and/or the base value. 13902b01b5eSaoyama */ 14002b01b5eSaoyama void 14102b01b5eSaoyama mkclock_get(dev, base, dt) 14202b01b5eSaoyama struct device *dev; 14302b01b5eSaoyama time_t base; 14402b01b5eSaoyama struct clock_ymdhms *dt; 14502b01b5eSaoyama { 14602b01b5eSaoyama struct timekeeper_softc *sc = (void *)dev; 147*b5e88448Saoyama volatile u_int32_t *chiptime = (void *)sc->sc_clock; 14802b01b5eSaoyama int s; 14902b01b5eSaoyama 15002b01b5eSaoyama s = splclock(); 151*b5e88448Saoyama 152*b5e88448Saoyama /* enable read (stop time) */ 153*b5e88448Saoyama chiptime[MK_CSR] |= (MK_CSR_READ << 24); 154*b5e88448Saoyama 155*b5e88448Saoyama dt->dt_sec = FROMBCD(chiptime[MK_SEC] >> 24); 156*b5e88448Saoyama dt->dt_min = FROMBCD(chiptime[MK_MIN] >> 24); 157*b5e88448Saoyama dt->dt_hour = FROMBCD(chiptime[MK_HOUR] >> 24); 158*b5e88448Saoyama dt->dt_wday = FROMBCD(chiptime[MK_DOW] >> 24); 159*b5e88448Saoyama dt->dt_day = FROMBCD(chiptime[MK_DOM] >> 24); 160*b5e88448Saoyama dt->dt_mon = FROMBCD(chiptime[MK_MONTH] >> 24); 161*b5e88448Saoyama dt->dt_year = FROMBCD(chiptime[MK_YEAR] >> 24); 162*b5e88448Saoyama 163*b5e88448Saoyama chiptime[MK_CSR] &= (~MK_CSR_READ << 24); /* time wears on */ 164*b5e88448Saoyama 165*b5e88448Saoyama /* UniOS-Mach doesn't set the correct BCD year after Y2K */ 166*b5e88448Saoyama if (dt->dt_year > 100) dt->dt_year -= (MK_YEAR0 % 100); 167*b5e88448Saoyama 168*b5e88448Saoyama dt->dt_year += MK_YEAR0; 16902b01b5eSaoyama splx(s); 17002b01b5eSaoyama #ifdef TIMEKEEPER_DEBUG 171*b5e88448Saoyama printf("get %02d/%02d/%02d %02d:%02d:%02d\n", 17202b01b5eSaoyama dt->dt_year, dt->dt_mon, dt->dt_day, 17302b01b5eSaoyama dt->dt_hour, dt->dt_min, dt->dt_sec); 17402b01b5eSaoyama #endif 17502b01b5eSaoyama } 17602b01b5eSaoyama 17702b01b5eSaoyama /* 17802b01b5eSaoyama * Reset the TODR based on the time value. 17902b01b5eSaoyama */ 18002b01b5eSaoyama void 18102b01b5eSaoyama mkclock_set(dev, dt) 18202b01b5eSaoyama struct device *dev; 18302b01b5eSaoyama struct clock_ymdhms *dt; 18402b01b5eSaoyama { 18502b01b5eSaoyama struct timekeeper_softc *sc = (void *)dev; 186*b5e88448Saoyama volatile u_int32_t *chiptime = (void *)sc->sc_clock; 187*b5e88448Saoyama volatile u_int32_t *stamp = (void *)(sc->sc_nvram + (4 * 0x10)); 18802b01b5eSaoyama int s; 18902b01b5eSaoyama 19002b01b5eSaoyama s = splclock(); 191*b5e88448Saoyama chiptime[MK_CSR] |= (MK_CSR_WRITE << 24); /* enable write */ 192*b5e88448Saoyama 193*b5e88448Saoyama chiptime[MK_SEC] = TOBCD(dt->dt_sec) << 24; 194*b5e88448Saoyama chiptime[MK_MIN] = TOBCD(dt->dt_min) << 24; 195*b5e88448Saoyama chiptime[MK_HOUR] = TOBCD(dt->dt_hour) << 24; 196*b5e88448Saoyama chiptime[MK_DOW] = TOBCD(dt->dt_wday) << 24; 197*b5e88448Saoyama chiptime[MK_DOM] = TOBCD(dt->dt_day) << 24; 198*b5e88448Saoyama chiptime[MK_MONTH] = TOBCD(dt->dt_mon) << 24; 199*b5e88448Saoyama /* XXX: We don't consider UniOS-Mach Y2K problem */ 200*b5e88448Saoyama chiptime[MK_YEAR] = TOBCD(dt->dt_year - MK_YEAR0) << 24; 201*b5e88448Saoyama 202*b5e88448Saoyama chiptime[MK_CSR] &= (~MK_CSR_WRITE << 24); /* load them up */ 20302b01b5eSaoyama splx(s); 20402b01b5eSaoyama #ifdef TIMEKEEPER_DEBUG 205*b5e88448Saoyama printf("set %02d/%02d/%02d %02d:%02d:%02d\n", 20602b01b5eSaoyama dt->dt_year, dt->dt_mon, dt->dt_day, 20702b01b5eSaoyama dt->dt_hour, dt->dt_min, dt->dt_sec); 20802b01b5eSaoyama #endif 20902b01b5eSaoyama 210*b5e88448Saoyama /* Write a stamp at NVRAM address 0x10-0x13 */ 211*b5e88448Saoyama stamp[0] = 'R' << 24; stamp[1] = 'T' << 24; 212*b5e88448Saoyama stamp[2] = 'C' << 24; stamp[3] = '\0' << 24; 21302b01b5eSaoyama } 21402b01b5eSaoyama 21502b01b5eSaoyama #define _DS_GET(off, data) \ 216805fad4dSmiod do { *chiptime = (off); (data) = (*chipdata); } while (0) 21702b01b5eSaoyama #define _DS_SET(off, data) \ 21802b01b5eSaoyama do { *chiptime = (off); *chipdata = (u_int8_t)(data); } while (0) 21902b01b5eSaoyama #define _DS_GET_BCD(off, data) \ 22002b01b5eSaoyama do { \ 22102b01b5eSaoyama u_int8_t c; \ 22202b01b5eSaoyama *chiptime = (off); \ 223805fad4dSmiod c = *chipdata; (data) = FROMBCD(c); \ 22402b01b5eSaoyama } while (0) 22502b01b5eSaoyama #define _DS_SET_BCD(off, data) \ 22602b01b5eSaoyama do { \ 22702b01b5eSaoyama *chiptime = (off); \ 22802b01b5eSaoyama *chipdata = TOBCD((u_int8_t)(data)); \ 22902b01b5eSaoyama } while (0) 23002b01b5eSaoyama 23102b01b5eSaoyama /* 23202b01b5eSaoyama * Get the time of day, based on the clock's value and/or the base value. 23302b01b5eSaoyama */ 23402b01b5eSaoyama void 23502b01b5eSaoyama dsclock_get(dev, base, dt) 23602b01b5eSaoyama struct device *dev; 23702b01b5eSaoyama time_t base; 23802b01b5eSaoyama struct clock_ymdhms *dt; 23902b01b5eSaoyama { 24002b01b5eSaoyama struct timekeeper_softc *sc = (void *)dev; 24102b01b5eSaoyama volatile u_int8_t *chiptime = (void *)sc->sc_clock; 24202b01b5eSaoyama volatile u_int8_t *chipdata = (void *)(sc->sc_clock + 1); 24302b01b5eSaoyama int s; 24402b01b5eSaoyama u_int8_t c; 24502b01b5eSaoyama 24602b01b5eSaoyama s = splclock(); 24702b01b5eSaoyama 24802b01b5eSaoyama /* specify 24hr and BCD mode */ 24902b01b5eSaoyama _DS_GET(DS_REGB, c); 25002b01b5eSaoyama c |= DS_REGB_24HR; 25102b01b5eSaoyama c &= ~DS_REGB_BINARY; 25202b01b5eSaoyama _DS_SET(DS_REGB, c); 25302b01b5eSaoyama 25402b01b5eSaoyama /* update in progress; spin loop */ 25502b01b5eSaoyama *chiptime = DS_REGA; 25602b01b5eSaoyama while (*chipdata & DS_REGA_UIP) 25702b01b5eSaoyama ; 25802b01b5eSaoyama 25902b01b5eSaoyama _DS_GET_BCD(DS_SEC, dt->dt_sec); 26002b01b5eSaoyama _DS_GET_BCD(DS_MIN, dt->dt_min); 26102b01b5eSaoyama _DS_GET_BCD(DS_HOUR, dt->dt_hour); 26202b01b5eSaoyama _DS_GET_BCD(DS_DOW, dt->dt_wday); 26302b01b5eSaoyama _DS_GET_BCD(DS_DOM, dt->dt_day); 26402b01b5eSaoyama _DS_GET_BCD(DS_MONTH, dt->dt_mon); 26502b01b5eSaoyama _DS_GET_BCD(DS_YEAR, dt->dt_year); 26602b01b5eSaoyama 267*b5e88448Saoyama /* UniOS-Mach doesn't set the correct BCD year after Y2K */ 268*b5e88448Saoyama if (dt->dt_year > 100) dt->dt_year -= (DS_YEAR0 % 100); 269*b5e88448Saoyama 270*b5e88448Saoyama dt->dt_year += DS_YEAR0; 27102b01b5eSaoyama splx(s); 27202b01b5eSaoyama 27302b01b5eSaoyama #ifdef TIMEKEEPER_DEBUG 274*b5e88448Saoyama printf("get %02d/%02d/%02d %02d:%02d:%02d\n", 27502b01b5eSaoyama dt->dt_year, dt->dt_mon, dt->dt_day, 27602b01b5eSaoyama dt->dt_hour, dt->dt_min, dt->dt_sec); 27702b01b5eSaoyama #endif 27802b01b5eSaoyama } 27902b01b5eSaoyama 28002b01b5eSaoyama /* 28102b01b5eSaoyama * Reset the TODR based on the time value. 28202b01b5eSaoyama */ 28302b01b5eSaoyama void 28402b01b5eSaoyama dsclock_set(dev, dt) 28502b01b5eSaoyama struct device *dev; 28602b01b5eSaoyama struct clock_ymdhms *dt; 28702b01b5eSaoyama { 28802b01b5eSaoyama struct timekeeper_softc *sc = (void *)dev; 28902b01b5eSaoyama volatile u_int8_t *chiptime = (void *)sc->sc_clock; 29002b01b5eSaoyama volatile u_int8_t *chipdata = (void *)(sc->sc_clock + 1); 29102b01b5eSaoyama int s; 29202b01b5eSaoyama u_int8_t c; 29302b01b5eSaoyama 29402b01b5eSaoyama s = splclock(); 29502b01b5eSaoyama 29602b01b5eSaoyama /* enable write */ 29702b01b5eSaoyama _DS_GET(DS_REGB, c); 29802b01b5eSaoyama c |= DS_REGB_SET; 29902b01b5eSaoyama _DS_SET(DS_REGB, c); 30002b01b5eSaoyama 30102b01b5eSaoyama _DS_SET_BCD(DS_SEC, dt->dt_sec); 30202b01b5eSaoyama _DS_SET_BCD(DS_MIN, dt->dt_min); 30302b01b5eSaoyama _DS_SET_BCD(DS_HOUR, dt->dt_hour); 30402b01b5eSaoyama _DS_SET_BCD(DS_DOW, dt->dt_wday); 30502b01b5eSaoyama _DS_SET_BCD(DS_DOM, dt->dt_day); 30602b01b5eSaoyama _DS_SET_BCD(DS_MONTH, dt->dt_mon); 307*b5e88448Saoyama /* XXX: We don't consider UniOS-Mach Y2K problem */ 30802b01b5eSaoyama _DS_SET_BCD(DS_YEAR, dt->dt_year - DS_YEAR0); 30902b01b5eSaoyama 31002b01b5eSaoyama _DS_GET(DS_REGB, c); 31102b01b5eSaoyama c &= ~DS_REGB_SET; 31202b01b5eSaoyama _DS_SET(DS_REGB, c); 31302b01b5eSaoyama 31402b01b5eSaoyama splx(s); 31502b01b5eSaoyama 31602b01b5eSaoyama #ifdef TIMEKEEPER_DEBUG 317*b5e88448Saoyama printf("set %02d/%02d/%02d %02d:%02d:%02d\n", 31802b01b5eSaoyama dt->dt_year, dt->dt_mon, dt->dt_day, 31902b01b5eSaoyama dt->dt_hour, dt->dt_min, dt->dt_sec); 32002b01b5eSaoyama #endif 32102b01b5eSaoyama } 322