1*0781ad23Saoyama /* $OpenBSD: timekeeper.c,v 1.3 2004/08/18 13:29:46 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 * 3. All advertising materials mentioning features or use of this software 2002b01b5eSaoyama * must display the following acknowledgement: 2102b01b5eSaoyama * This product includes software developed by the NetBSD 2202b01b5eSaoyama * Foundation, Inc. and its contributors. 2302b01b5eSaoyama * 4. Neither the name of The NetBSD Foundation nor the names of its 2402b01b5eSaoyama * contributors may be used to endorse or promote products derived 2502b01b5eSaoyama * from this software without specific prior written permission. 2602b01b5eSaoyama * 2702b01b5eSaoyama * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 2802b01b5eSaoyama * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 2902b01b5eSaoyama * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 3002b01b5eSaoyama * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 3102b01b5eSaoyama * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 3202b01b5eSaoyama * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 3302b01b5eSaoyama * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 3402b01b5eSaoyama * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 3502b01b5eSaoyama * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 3602b01b5eSaoyama * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 3702b01b5eSaoyama * POSSIBILITY OF SUCH DAMAGE. 3802b01b5eSaoyama */ 3902b01b5eSaoyama 4002b01b5eSaoyama #include <sys/param.h> 4102b01b5eSaoyama #include <sys/systm.h> 4202b01b5eSaoyama #include <sys/device.h> 4302b01b5eSaoyama #include <sys/kernel.h> 44*0781ad23Saoyama #include <sys/evcount.h> 4502b01b5eSaoyama 46110e3210Saoyama #include <machine/autoconf.h> 4702b01b5eSaoyama #include <machine/board.h> /* machtype value */ 4802b01b5eSaoyama #include <machine/cpu.h> 4902b01b5eSaoyama 5002b01b5eSaoyama #include <dev/clock_subr.h> 51110e3210Saoyama 5202b01b5eSaoyama #include <luna88k/luna88k/clockvar.h> 5302b01b5eSaoyama #include <luna88k/dev/timekeeper.h> 5402b01b5eSaoyama 5502b01b5eSaoyama #define MK_YEAR0 1970 /* year offset of MK */ 5602b01b5eSaoyama #define DS_YEAR0 1990 /* year offset of DS */ 5702b01b5eSaoyama 5802b01b5eSaoyama struct timekeeper_softc { 5902b01b5eSaoyama struct device sc_dev; 6002b01b5eSaoyama void *sc_clock, *sc_nvram; 6102b01b5eSaoyama int sc_nvramsize; 62*0781ad23Saoyama struct evcount sc_count; 6302b01b5eSaoyama }; 6402b01b5eSaoyama 6502b01b5eSaoyama /* 6602b01b5eSaoyama * BCD to decimal and decimal to BCD. 6702b01b5eSaoyama */ 6802b01b5eSaoyama #define FROMBCD(x) (((x) >> 4) * 10 + ((x) & 0xf)) 6902b01b5eSaoyama #define TOBCD(x) (((x) / 10 * 16) + ((x) % 10)) 7002b01b5eSaoyama 7102b01b5eSaoyama int clock_match(struct device *, void *, void *); 7202b01b5eSaoyama void clock_attach(struct device *, struct device *, void *); 7302b01b5eSaoyama 7402b01b5eSaoyama struct cfattach clock_ca = { 7502b01b5eSaoyama sizeof (struct timekeeper_softc), clock_match, clock_attach 7602b01b5eSaoyama }; 7702b01b5eSaoyama 7802b01b5eSaoyama struct cfdriver clock_cd = { 7902b01b5eSaoyama NULL, "clock", DV_DULL 8002b01b5eSaoyama }; 8102b01b5eSaoyama 8202b01b5eSaoyama void mkclock_get(struct device *, time_t, struct clock_ymdhms *); 8302b01b5eSaoyama void mkclock_set(struct device *, struct clock_ymdhms *); 8402b01b5eSaoyama void dsclock_get(struct device *, time_t, struct clock_ymdhms *); 8502b01b5eSaoyama void dsclock_set(struct device *, struct clock_ymdhms *); 8602b01b5eSaoyama 8702b01b5eSaoyama const struct clockfns mkclock_clockfns = { 8802b01b5eSaoyama NULL /* never used */, mkclock_get, mkclock_set, 8902b01b5eSaoyama }; 9002b01b5eSaoyama 9102b01b5eSaoyama const struct clockfns dsclock_clockfns = { 9202b01b5eSaoyama NULL /* never used */, dsclock_get, dsclock_set, 9302b01b5eSaoyama }; 9402b01b5eSaoyama 9502b01b5eSaoyama int 9602b01b5eSaoyama clock_match(parent, match, aux) 9702b01b5eSaoyama struct device *parent; 9802b01b5eSaoyama void *match, *aux; 9902b01b5eSaoyama { 10002b01b5eSaoyama struct mainbus_attach_args *ma = aux; 10102b01b5eSaoyama 10202b01b5eSaoyama if (strcmp(ma->ma_name, clock_cd.cd_name)) 10302b01b5eSaoyama return 0; 10402b01b5eSaoyama return 1; 10502b01b5eSaoyama } 10602b01b5eSaoyama 10702b01b5eSaoyama extern int machtype; /* in machdep.c */ 10802b01b5eSaoyama 10902b01b5eSaoyama void 11002b01b5eSaoyama clock_attach(parent, self, aux) 11102b01b5eSaoyama struct device *parent, *self; 11202b01b5eSaoyama void *aux; 11302b01b5eSaoyama { 11402b01b5eSaoyama struct timekeeper_softc *sc = (void *)self; 11502b01b5eSaoyama struct mainbus_attach_args *ma = aux; 11602b01b5eSaoyama const struct clockfns *clockwork; 11702b01b5eSaoyama 11802b01b5eSaoyama switch (machtype) { 11902b01b5eSaoyama default: 12002b01b5eSaoyama case LUNA_88K: /* Mostek MK48T02 */ 12102b01b5eSaoyama sc->sc_clock = (void *)(ma->ma_addr + 2040); 12202b01b5eSaoyama sc->sc_nvram = (void *)ma->ma_addr; 12302b01b5eSaoyama sc->sc_nvramsize = 2040; 12402b01b5eSaoyama clockwork = &mkclock_clockfns; 12502b01b5eSaoyama printf(": MK48T02\n"); 12602b01b5eSaoyama break; 12702b01b5eSaoyama case LUNA_88K2: /* Dallas DS1397 */ 12802b01b5eSaoyama sc->sc_clock = (void *)ma->ma_addr; 12902b01b5eSaoyama sc->sc_nvram = (void *)(ma->ma_addr + 50); 13002b01b5eSaoyama sc->sc_nvramsize = 50; 13102b01b5eSaoyama clockwork = &dsclock_clockfns; 13202b01b5eSaoyama printf(": DS1397\n"); 13302b01b5eSaoyama break; 13402b01b5eSaoyama } 135*0781ad23Saoyama 136*0781ad23Saoyama evcount_attach(&sc->sc_count, self->dv_xname, (void *)&ma->ma_ilvl, &evcount_intr); 137*0781ad23Saoyama 138*0781ad23Saoyama clockattach(&sc->sc_dev, clockwork, &sc->sc_count); 13902b01b5eSaoyama } 14002b01b5eSaoyama 14102b01b5eSaoyama /* 14202b01b5eSaoyama * Get the time of day, based on the clock's value and/or the base value. 14302b01b5eSaoyama */ 14402b01b5eSaoyama void 14502b01b5eSaoyama mkclock_get(dev, base, dt) 14602b01b5eSaoyama struct device *dev; 14702b01b5eSaoyama time_t base; 14802b01b5eSaoyama struct clock_ymdhms *dt; 14902b01b5eSaoyama { 15002b01b5eSaoyama struct timekeeper_softc *sc = (void *)dev; 15102b01b5eSaoyama volatile u_int8_t *chiptime = (void *)sc->sc_clock; 15202b01b5eSaoyama int s; 15302b01b5eSaoyama 15402b01b5eSaoyama s = splclock(); 15502b01b5eSaoyama chiptime[MK_CSR] |= MK_CSR_READ; /* enable read (stop time) */ 15602b01b5eSaoyama dt->dt_sec = FROMBCD(chiptime[MK_SEC]); 15702b01b5eSaoyama dt->dt_min = FROMBCD(chiptime[MK_MIN]); 15802b01b5eSaoyama dt->dt_hour = FROMBCD(chiptime[MK_HOUR]); 15902b01b5eSaoyama dt->dt_wday = FROMBCD(chiptime[MK_DOW]); 16002b01b5eSaoyama dt->dt_day = FROMBCD(chiptime[MK_DOM]); 16102b01b5eSaoyama dt->dt_mon = FROMBCD(chiptime[MK_MONTH]); 16202b01b5eSaoyama dt->dt_year = FROMBCD(chiptime[MK_YEAR]) + MK_YEAR0; 16302b01b5eSaoyama chiptime[MK_CSR] &= ~MK_CSR_READ; /* time wears on */ 16402b01b5eSaoyama splx(s); 16502b01b5eSaoyama #ifdef TIMEKEEPER_DEBUG 16602b01b5eSaoyama printf("get %d/%d/%d %d:%d:%d\n", 16702b01b5eSaoyama dt->dt_year, dt->dt_mon, dt->dt_day, 16802b01b5eSaoyama dt->dt_hour, dt->dt_min, dt->dt_sec); 16902b01b5eSaoyama #endif 17002b01b5eSaoyama } 17102b01b5eSaoyama 17202b01b5eSaoyama /* 17302b01b5eSaoyama * Reset the TODR based on the time value. 17402b01b5eSaoyama */ 17502b01b5eSaoyama void 17602b01b5eSaoyama mkclock_set(dev, dt) 17702b01b5eSaoyama struct device *dev; 17802b01b5eSaoyama struct clock_ymdhms *dt; 17902b01b5eSaoyama { 18002b01b5eSaoyama struct timekeeper_softc *sc = (void *)dev; 18102b01b5eSaoyama volatile u_int8_t *chiptime = (void *)sc->sc_clock; 18202b01b5eSaoyama volatile u_int8_t *stamp = (u_int8_t *)sc->sc_nvram + 0x10; 18302b01b5eSaoyama int s; 18402b01b5eSaoyama 18502b01b5eSaoyama s = splclock(); 18602b01b5eSaoyama chiptime[MK_CSR] |= MK_CSR_WRITE; /* enable write */ 18702b01b5eSaoyama chiptime[MK_SEC] = TOBCD(dt->dt_sec); 18802b01b5eSaoyama chiptime[MK_MIN] = TOBCD(dt->dt_min); 18902b01b5eSaoyama chiptime[MK_HOUR] = TOBCD(dt->dt_hour); 19002b01b5eSaoyama chiptime[MK_DOW] = TOBCD(dt->dt_wday); 19102b01b5eSaoyama chiptime[MK_DOM] = TOBCD(dt->dt_day); 19202b01b5eSaoyama chiptime[MK_MONTH] = TOBCD(dt->dt_mon); 19302b01b5eSaoyama chiptime[MK_YEAR] = TOBCD(dt->dt_year - MK_YEAR0); 19402b01b5eSaoyama chiptime[MK_CSR] &= ~MK_CSR_WRITE; /* load them up */ 19502b01b5eSaoyama splx(s); 19602b01b5eSaoyama #ifdef TIMEKEEPER_DEBUG 19702b01b5eSaoyama printf("set %d/%d/%d %d:%d:%d\n", 19802b01b5eSaoyama dt->dt_year, dt->dt_mon, dt->dt_day, 19902b01b5eSaoyama dt->dt_hour, dt->dt_min, dt->dt_sec); 20002b01b5eSaoyama #endif 20102b01b5eSaoyama 20202b01b5eSaoyama stamp[0] = 'R'; stamp[1] = 'T'; stamp[2] = 'C'; stamp[3] = '\0'; 20302b01b5eSaoyama } 20402b01b5eSaoyama 20502b01b5eSaoyama #define _DS_GET(off, data) \ 20602b01b5eSaoyama do { *chiptime = (off); (u_int8_t)(data) = (*chipdata); } while (0) 20702b01b5eSaoyama #define _DS_SET(off, data) \ 20802b01b5eSaoyama do { *chiptime = (off); *chipdata = (u_int8_t)(data); } while (0) 20902b01b5eSaoyama #define _DS_GET_BCD(off, data) \ 21002b01b5eSaoyama do { \ 21102b01b5eSaoyama u_int8_t c; \ 21202b01b5eSaoyama *chiptime = (off); \ 21302b01b5eSaoyama c = *chipdata; (u_int8_t)(data) = FROMBCD(c); \ 21402b01b5eSaoyama } while (0) 21502b01b5eSaoyama #define _DS_SET_BCD(off, data) \ 21602b01b5eSaoyama do { \ 21702b01b5eSaoyama *chiptime = (off); \ 21802b01b5eSaoyama *chipdata = TOBCD((u_int8_t)(data)); \ 21902b01b5eSaoyama } while (0) 22002b01b5eSaoyama 22102b01b5eSaoyama /* 22202b01b5eSaoyama * Get the time of day, based on the clock's value and/or the base value. 22302b01b5eSaoyama */ 22402b01b5eSaoyama void 22502b01b5eSaoyama dsclock_get(dev, base, dt) 22602b01b5eSaoyama struct device *dev; 22702b01b5eSaoyama time_t base; 22802b01b5eSaoyama struct clock_ymdhms *dt; 22902b01b5eSaoyama { 23002b01b5eSaoyama struct timekeeper_softc *sc = (void *)dev; 23102b01b5eSaoyama volatile u_int8_t *chiptime = (void *)sc->sc_clock; 23202b01b5eSaoyama volatile u_int8_t *chipdata = (void *)(sc->sc_clock + 1); 23302b01b5eSaoyama int s; 23402b01b5eSaoyama u_int8_t c; 23502b01b5eSaoyama 23602b01b5eSaoyama s = splclock(); 23702b01b5eSaoyama 23802b01b5eSaoyama /* specify 24hr and BCD mode */ 23902b01b5eSaoyama _DS_GET(DS_REGB, c); 24002b01b5eSaoyama c |= DS_REGB_24HR; 24102b01b5eSaoyama c &= ~DS_REGB_BINARY; 24202b01b5eSaoyama _DS_SET(DS_REGB, c); 24302b01b5eSaoyama 24402b01b5eSaoyama /* update in progress; spin loop */ 24502b01b5eSaoyama *chiptime = DS_REGA; 24602b01b5eSaoyama while (*chipdata & DS_REGA_UIP) 24702b01b5eSaoyama ; 24802b01b5eSaoyama 24902b01b5eSaoyama _DS_GET_BCD(DS_SEC, dt->dt_sec); 25002b01b5eSaoyama _DS_GET_BCD(DS_MIN, dt->dt_min); 25102b01b5eSaoyama _DS_GET_BCD(DS_HOUR, dt->dt_hour); 25202b01b5eSaoyama _DS_GET_BCD(DS_DOW, dt->dt_wday); 25302b01b5eSaoyama _DS_GET_BCD(DS_DOM, dt->dt_day); 25402b01b5eSaoyama _DS_GET_BCD(DS_MONTH, dt->dt_mon); 25502b01b5eSaoyama _DS_GET_BCD(DS_YEAR, dt->dt_year); 25602b01b5eSaoyama dt->dt_year += DS_YEAR0; 25702b01b5eSaoyama 25802b01b5eSaoyama splx(s); 25902b01b5eSaoyama 26002b01b5eSaoyama #ifdef TIMEKEEPER_DEBUG 26102b01b5eSaoyama printf("get %d/%d/%d %d:%d:%d\n", 26202b01b5eSaoyama dt->dt_year, dt->dt_mon, dt->dt_day, 26302b01b5eSaoyama dt->dt_hour, dt->dt_min, dt->dt_sec); 26402b01b5eSaoyama #endif 26502b01b5eSaoyama } 26602b01b5eSaoyama 26702b01b5eSaoyama /* 26802b01b5eSaoyama * Reset the TODR based on the time value. 26902b01b5eSaoyama */ 27002b01b5eSaoyama void 27102b01b5eSaoyama dsclock_set(dev, dt) 27202b01b5eSaoyama struct device *dev; 27302b01b5eSaoyama struct clock_ymdhms *dt; 27402b01b5eSaoyama { 27502b01b5eSaoyama struct timekeeper_softc *sc = (void *)dev; 27602b01b5eSaoyama volatile u_int8_t *chiptime = (void *)sc->sc_clock; 27702b01b5eSaoyama volatile u_int8_t *chipdata = (void *)(sc->sc_clock + 1); 27802b01b5eSaoyama int s; 27902b01b5eSaoyama u_int8_t c; 28002b01b5eSaoyama 28102b01b5eSaoyama s = splclock(); 28202b01b5eSaoyama 28302b01b5eSaoyama /* enable write */ 28402b01b5eSaoyama _DS_GET(DS_REGB, c); 28502b01b5eSaoyama c |= DS_REGB_SET; 28602b01b5eSaoyama _DS_SET(DS_REGB, c); 28702b01b5eSaoyama 28802b01b5eSaoyama _DS_SET_BCD(DS_SEC, dt->dt_sec); 28902b01b5eSaoyama _DS_SET_BCD(DS_MIN, dt->dt_min); 29002b01b5eSaoyama _DS_SET_BCD(DS_HOUR, dt->dt_hour); 29102b01b5eSaoyama _DS_SET_BCD(DS_DOW, dt->dt_wday); 29202b01b5eSaoyama _DS_SET_BCD(DS_DOM, dt->dt_day); 29302b01b5eSaoyama _DS_SET_BCD(DS_MONTH, dt->dt_mon); 29402b01b5eSaoyama _DS_SET_BCD(DS_YEAR, dt->dt_year - DS_YEAR0); 29502b01b5eSaoyama 29602b01b5eSaoyama _DS_GET(DS_REGB, c); 29702b01b5eSaoyama c &= ~DS_REGB_SET; 29802b01b5eSaoyama _DS_SET(DS_REGB, c); 29902b01b5eSaoyama 30002b01b5eSaoyama splx(s); 30102b01b5eSaoyama 30202b01b5eSaoyama #ifdef TIMEKEEPER_DEBUG 30302b01b5eSaoyama printf("set %d/%d/%d %d:%d:%d\n", 30402b01b5eSaoyama dt->dt_year, dt->dt_mon, dt->dt_day, 30502b01b5eSaoyama dt->dt_hour, dt->dt_min, dt->dt_sec); 30602b01b5eSaoyama #endif 30702b01b5eSaoyama } 308