1*4667bebbSmatthew /* $OpenBSD: timekeeper.c,v 1.5 2010/09/20 06:33:47 matthew 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 */ 11402b01b5eSaoyama sc->sc_clock = (void *)(ma->ma_addr + 2040); 11502b01b5eSaoyama sc->sc_nvram = (void *)ma->ma_addr; 11602b01b5eSaoyama sc->sc_nvramsize = 2040; 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 129*4667bebbSmatthew evcount_attach(&sc->sc_count, self->dv_xname, &ma->ma_ilvl); 1300781ad23Saoyama 1310781ad23Saoyama clockattach(&sc->sc_dev, clockwork, &sc->sc_count); 13202b01b5eSaoyama } 13302b01b5eSaoyama 13402b01b5eSaoyama /* 13502b01b5eSaoyama * Get the time of day, based on the clock's value and/or the base value. 13602b01b5eSaoyama */ 13702b01b5eSaoyama void 13802b01b5eSaoyama mkclock_get(dev, base, dt) 13902b01b5eSaoyama struct device *dev; 14002b01b5eSaoyama time_t base; 14102b01b5eSaoyama struct clock_ymdhms *dt; 14202b01b5eSaoyama { 14302b01b5eSaoyama struct timekeeper_softc *sc = (void *)dev; 14402b01b5eSaoyama volatile u_int8_t *chiptime = (void *)sc->sc_clock; 14502b01b5eSaoyama int s; 14602b01b5eSaoyama 14702b01b5eSaoyama s = splclock(); 14802b01b5eSaoyama chiptime[MK_CSR] |= MK_CSR_READ; /* enable read (stop time) */ 14902b01b5eSaoyama dt->dt_sec = FROMBCD(chiptime[MK_SEC]); 15002b01b5eSaoyama dt->dt_min = FROMBCD(chiptime[MK_MIN]); 15102b01b5eSaoyama dt->dt_hour = FROMBCD(chiptime[MK_HOUR]); 15202b01b5eSaoyama dt->dt_wday = FROMBCD(chiptime[MK_DOW]); 15302b01b5eSaoyama dt->dt_day = FROMBCD(chiptime[MK_DOM]); 15402b01b5eSaoyama dt->dt_mon = FROMBCD(chiptime[MK_MONTH]); 15502b01b5eSaoyama dt->dt_year = FROMBCD(chiptime[MK_YEAR]) + MK_YEAR0; 15602b01b5eSaoyama chiptime[MK_CSR] &= ~MK_CSR_READ; /* time wears on */ 15702b01b5eSaoyama splx(s); 15802b01b5eSaoyama #ifdef TIMEKEEPER_DEBUG 15902b01b5eSaoyama printf("get %d/%d/%d %d:%d:%d\n", 16002b01b5eSaoyama dt->dt_year, dt->dt_mon, dt->dt_day, 16102b01b5eSaoyama dt->dt_hour, dt->dt_min, dt->dt_sec); 16202b01b5eSaoyama #endif 16302b01b5eSaoyama } 16402b01b5eSaoyama 16502b01b5eSaoyama /* 16602b01b5eSaoyama * Reset the TODR based on the time value. 16702b01b5eSaoyama */ 16802b01b5eSaoyama void 16902b01b5eSaoyama mkclock_set(dev, dt) 17002b01b5eSaoyama struct device *dev; 17102b01b5eSaoyama struct clock_ymdhms *dt; 17202b01b5eSaoyama { 17302b01b5eSaoyama struct timekeeper_softc *sc = (void *)dev; 17402b01b5eSaoyama volatile u_int8_t *chiptime = (void *)sc->sc_clock; 17502b01b5eSaoyama volatile u_int8_t *stamp = (u_int8_t *)sc->sc_nvram + 0x10; 17602b01b5eSaoyama int s; 17702b01b5eSaoyama 17802b01b5eSaoyama s = splclock(); 17902b01b5eSaoyama chiptime[MK_CSR] |= MK_CSR_WRITE; /* enable write */ 18002b01b5eSaoyama chiptime[MK_SEC] = TOBCD(dt->dt_sec); 18102b01b5eSaoyama chiptime[MK_MIN] = TOBCD(dt->dt_min); 18202b01b5eSaoyama chiptime[MK_HOUR] = TOBCD(dt->dt_hour); 18302b01b5eSaoyama chiptime[MK_DOW] = TOBCD(dt->dt_wday); 18402b01b5eSaoyama chiptime[MK_DOM] = TOBCD(dt->dt_day); 18502b01b5eSaoyama chiptime[MK_MONTH] = TOBCD(dt->dt_mon); 18602b01b5eSaoyama chiptime[MK_YEAR] = TOBCD(dt->dt_year - MK_YEAR0); 18702b01b5eSaoyama chiptime[MK_CSR] &= ~MK_CSR_WRITE; /* load them up */ 18802b01b5eSaoyama splx(s); 18902b01b5eSaoyama #ifdef TIMEKEEPER_DEBUG 19002b01b5eSaoyama printf("set %d/%d/%d %d:%d:%d\n", 19102b01b5eSaoyama dt->dt_year, dt->dt_mon, dt->dt_day, 19202b01b5eSaoyama dt->dt_hour, dt->dt_min, dt->dt_sec); 19302b01b5eSaoyama #endif 19402b01b5eSaoyama 19502b01b5eSaoyama stamp[0] = 'R'; stamp[1] = 'T'; stamp[2] = 'C'; stamp[3] = '\0'; 19602b01b5eSaoyama } 19702b01b5eSaoyama 19802b01b5eSaoyama #define _DS_GET(off, data) \ 19902b01b5eSaoyama do { *chiptime = (off); (u_int8_t)(data) = (*chipdata); } while (0) 20002b01b5eSaoyama #define _DS_SET(off, data) \ 20102b01b5eSaoyama do { *chiptime = (off); *chipdata = (u_int8_t)(data); } while (0) 20202b01b5eSaoyama #define _DS_GET_BCD(off, data) \ 20302b01b5eSaoyama do { \ 20402b01b5eSaoyama u_int8_t c; \ 20502b01b5eSaoyama *chiptime = (off); \ 20602b01b5eSaoyama c = *chipdata; (u_int8_t)(data) = FROMBCD(c); \ 20702b01b5eSaoyama } while (0) 20802b01b5eSaoyama #define _DS_SET_BCD(off, data) \ 20902b01b5eSaoyama do { \ 21002b01b5eSaoyama *chiptime = (off); \ 21102b01b5eSaoyama *chipdata = TOBCD((u_int8_t)(data)); \ 21202b01b5eSaoyama } while (0) 21302b01b5eSaoyama 21402b01b5eSaoyama /* 21502b01b5eSaoyama * Get the time of day, based on the clock's value and/or the base value. 21602b01b5eSaoyama */ 21702b01b5eSaoyama void 21802b01b5eSaoyama dsclock_get(dev, base, dt) 21902b01b5eSaoyama struct device *dev; 22002b01b5eSaoyama time_t base; 22102b01b5eSaoyama struct clock_ymdhms *dt; 22202b01b5eSaoyama { 22302b01b5eSaoyama struct timekeeper_softc *sc = (void *)dev; 22402b01b5eSaoyama volatile u_int8_t *chiptime = (void *)sc->sc_clock; 22502b01b5eSaoyama volatile u_int8_t *chipdata = (void *)(sc->sc_clock + 1); 22602b01b5eSaoyama int s; 22702b01b5eSaoyama u_int8_t c; 22802b01b5eSaoyama 22902b01b5eSaoyama s = splclock(); 23002b01b5eSaoyama 23102b01b5eSaoyama /* specify 24hr and BCD mode */ 23202b01b5eSaoyama _DS_GET(DS_REGB, c); 23302b01b5eSaoyama c |= DS_REGB_24HR; 23402b01b5eSaoyama c &= ~DS_REGB_BINARY; 23502b01b5eSaoyama _DS_SET(DS_REGB, c); 23602b01b5eSaoyama 23702b01b5eSaoyama /* update in progress; spin loop */ 23802b01b5eSaoyama *chiptime = DS_REGA; 23902b01b5eSaoyama while (*chipdata & DS_REGA_UIP) 24002b01b5eSaoyama ; 24102b01b5eSaoyama 24202b01b5eSaoyama _DS_GET_BCD(DS_SEC, dt->dt_sec); 24302b01b5eSaoyama _DS_GET_BCD(DS_MIN, dt->dt_min); 24402b01b5eSaoyama _DS_GET_BCD(DS_HOUR, dt->dt_hour); 24502b01b5eSaoyama _DS_GET_BCD(DS_DOW, dt->dt_wday); 24602b01b5eSaoyama _DS_GET_BCD(DS_DOM, dt->dt_day); 24702b01b5eSaoyama _DS_GET_BCD(DS_MONTH, dt->dt_mon); 24802b01b5eSaoyama _DS_GET_BCD(DS_YEAR, dt->dt_year); 24902b01b5eSaoyama dt->dt_year += DS_YEAR0; 25002b01b5eSaoyama 25102b01b5eSaoyama splx(s); 25202b01b5eSaoyama 25302b01b5eSaoyama #ifdef TIMEKEEPER_DEBUG 25402b01b5eSaoyama printf("get %d/%d/%d %d:%d:%d\n", 25502b01b5eSaoyama dt->dt_year, dt->dt_mon, dt->dt_day, 25602b01b5eSaoyama dt->dt_hour, dt->dt_min, dt->dt_sec); 25702b01b5eSaoyama #endif 25802b01b5eSaoyama } 25902b01b5eSaoyama 26002b01b5eSaoyama /* 26102b01b5eSaoyama * Reset the TODR based on the time value. 26202b01b5eSaoyama */ 26302b01b5eSaoyama void 26402b01b5eSaoyama dsclock_set(dev, dt) 26502b01b5eSaoyama struct device *dev; 26602b01b5eSaoyama struct clock_ymdhms *dt; 26702b01b5eSaoyama { 26802b01b5eSaoyama struct timekeeper_softc *sc = (void *)dev; 26902b01b5eSaoyama volatile u_int8_t *chiptime = (void *)sc->sc_clock; 27002b01b5eSaoyama volatile u_int8_t *chipdata = (void *)(sc->sc_clock + 1); 27102b01b5eSaoyama int s; 27202b01b5eSaoyama u_int8_t c; 27302b01b5eSaoyama 27402b01b5eSaoyama s = splclock(); 27502b01b5eSaoyama 27602b01b5eSaoyama /* enable write */ 27702b01b5eSaoyama _DS_GET(DS_REGB, c); 27802b01b5eSaoyama c |= DS_REGB_SET; 27902b01b5eSaoyama _DS_SET(DS_REGB, c); 28002b01b5eSaoyama 28102b01b5eSaoyama _DS_SET_BCD(DS_SEC, dt->dt_sec); 28202b01b5eSaoyama _DS_SET_BCD(DS_MIN, dt->dt_min); 28302b01b5eSaoyama _DS_SET_BCD(DS_HOUR, dt->dt_hour); 28402b01b5eSaoyama _DS_SET_BCD(DS_DOW, dt->dt_wday); 28502b01b5eSaoyama _DS_SET_BCD(DS_DOM, dt->dt_day); 28602b01b5eSaoyama _DS_SET_BCD(DS_MONTH, dt->dt_mon); 28702b01b5eSaoyama _DS_SET_BCD(DS_YEAR, dt->dt_year - DS_YEAR0); 28802b01b5eSaoyama 28902b01b5eSaoyama _DS_GET(DS_REGB, c); 29002b01b5eSaoyama c &= ~DS_REGB_SET; 29102b01b5eSaoyama _DS_SET(DS_REGB, c); 29202b01b5eSaoyama 29302b01b5eSaoyama splx(s); 29402b01b5eSaoyama 29502b01b5eSaoyama #ifdef TIMEKEEPER_DEBUG 29602b01b5eSaoyama printf("set %d/%d/%d %d:%d:%d\n", 29702b01b5eSaoyama dt->dt_year, dt->dt_mon, dt->dt_day, 29802b01b5eSaoyama dt->dt_hour, dt->dt_min, dt->dt_sec); 29902b01b5eSaoyama #endif 30002b01b5eSaoyama } 301