1*471aeecfSnaddy /* $OpenBSD: timekeeper.c,v 1.11 2022/04/06 18:59:26 naddy 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
67*471aeecfSnaddy const 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
clock_match(struct device * parent,void * match,void * aux)8980bc78ddSaoyama clock_match(struct device *parent, void *match, void *aux)
9002b01b5eSaoyama {
9102b01b5eSaoyama struct mainbus_attach_args *ma = aux;
9202b01b5eSaoyama
9302b01b5eSaoyama if (strcmp(ma->ma_name, clock_cd.cd_name))
9402b01b5eSaoyama return 0;
9502b01b5eSaoyama return 1;
9602b01b5eSaoyama }
9702b01b5eSaoyama
9802b01b5eSaoyama extern int machtype; /* in machdep.c */
9902b01b5eSaoyama
10002b01b5eSaoyama void
clock_attach(struct device * parent,struct device * self,void * aux)10180bc78ddSaoyama clock_attach(struct device *parent, struct device *self, void *aux)
10202b01b5eSaoyama {
10302b01b5eSaoyama struct timekeeper_softc *sc = (void *)self;
10402b01b5eSaoyama struct mainbus_attach_args *ma = aux;
10502b01b5eSaoyama const struct clockfns *clockwork;
10602b01b5eSaoyama
10702b01b5eSaoyama switch (machtype) {
10802b01b5eSaoyama default:
10902b01b5eSaoyama case LUNA_88K: /* Mostek MK48T02 */
110b5e88448Saoyama sc->sc_clock = (void *)(ma->ma_addr + MK_NVRAM_SPACE);
11102b01b5eSaoyama sc->sc_nvram = (void *)ma->ma_addr;
112b5e88448Saoyama sc->sc_nvramsize = MK_NVRAM_SPACE;
11302b01b5eSaoyama clockwork = &mkclock_clockfns;
11402b01b5eSaoyama printf(": MK48T02\n");
11502b01b5eSaoyama break;
11602b01b5eSaoyama case LUNA_88K2: /* Dallas DS1397 */
11702b01b5eSaoyama sc->sc_clock = (void *)ma->ma_addr;
11802b01b5eSaoyama sc->sc_nvram = (void *)(ma->ma_addr + 50);
11902b01b5eSaoyama sc->sc_nvramsize = 50;
12002b01b5eSaoyama clockwork = &dsclock_clockfns;
12102b01b5eSaoyama printf(": DS1397\n");
12202b01b5eSaoyama break;
12302b01b5eSaoyama }
1240781ad23Saoyama
1254667bebbSmatthew evcount_attach(&sc->sc_count, self->dv_xname, &ma->ma_ilvl);
1260781ad23Saoyama
1270781ad23Saoyama clockattach(&sc->sc_dev, clockwork, &sc->sc_count);
12802b01b5eSaoyama }
12902b01b5eSaoyama
13002b01b5eSaoyama /*
131b5e88448Saoyama * On LUNA-88K, NVRAM contents and Timekeeper registers are mapped on the
132b5e88448Saoyama * most significant byte of each 32bit word. (i.e. 4-bytes stride)
133b5e88448Saoyama *
13402b01b5eSaoyama * Get the time of day, based on the clock's value and/or the base value.
13502b01b5eSaoyama */
13602b01b5eSaoyama void
mkclock_get(struct device * dev,time_t base,struct clock_ymdhms * dt)13780bc78ddSaoyama mkclock_get(struct device *dev, time_t base, struct clock_ymdhms *dt)
13802b01b5eSaoyama {
13902b01b5eSaoyama struct timekeeper_softc *sc = (void *)dev;
140b5e88448Saoyama volatile u_int32_t *chiptime = (void *)sc->sc_clock;
14102b01b5eSaoyama int s;
14202b01b5eSaoyama
14302b01b5eSaoyama s = splclock();
144b5e88448Saoyama
145b5e88448Saoyama /* enable read (stop time) */
146b5e88448Saoyama chiptime[MK_CSR] |= (MK_CSR_READ << 24);
147b5e88448Saoyama
148b5e88448Saoyama dt->dt_sec = FROMBCD(chiptime[MK_SEC] >> 24);
149b5e88448Saoyama dt->dt_min = FROMBCD(chiptime[MK_MIN] >> 24);
150b5e88448Saoyama dt->dt_hour = FROMBCD(chiptime[MK_HOUR] >> 24);
151b5e88448Saoyama dt->dt_wday = FROMBCD(chiptime[MK_DOW] >> 24);
152b5e88448Saoyama dt->dt_day = FROMBCD(chiptime[MK_DOM] >> 24);
153b5e88448Saoyama dt->dt_mon = FROMBCD(chiptime[MK_MONTH] >> 24);
154b5e88448Saoyama dt->dt_year = FROMBCD(chiptime[MK_YEAR] >> 24);
155b5e88448Saoyama
156b5e88448Saoyama chiptime[MK_CSR] &= (~MK_CSR_READ << 24); /* time wears on */
157b5e88448Saoyama
158b5e88448Saoyama /* UniOS-Mach doesn't set the correct BCD year after Y2K */
159b5e88448Saoyama if (dt->dt_year > 100) dt->dt_year -= (MK_YEAR0 % 100);
160b5e88448Saoyama
161b5e88448Saoyama dt->dt_year += MK_YEAR0;
16202b01b5eSaoyama splx(s);
16302b01b5eSaoyama #ifdef TIMEKEEPER_DEBUG
164b5e88448Saoyama printf("get %02d/%02d/%02d %02d:%02d:%02d\n",
16502b01b5eSaoyama dt->dt_year, dt->dt_mon, dt->dt_day,
16602b01b5eSaoyama dt->dt_hour, dt->dt_min, dt->dt_sec);
16702b01b5eSaoyama #endif
16802b01b5eSaoyama }
16902b01b5eSaoyama
17002b01b5eSaoyama /*
17102b01b5eSaoyama * Reset the TODR based on the time value.
17202b01b5eSaoyama */
17302b01b5eSaoyama void
mkclock_set(struct device * dev,struct clock_ymdhms * dt)17480bc78ddSaoyama mkclock_set(struct device *dev, struct clock_ymdhms *dt)
17502b01b5eSaoyama {
17602b01b5eSaoyama struct timekeeper_softc *sc = (void *)dev;
177b5e88448Saoyama volatile u_int32_t *chiptime = (void *)sc->sc_clock;
178b5e88448Saoyama volatile u_int32_t *stamp = (void *)(sc->sc_nvram + (4 * 0x10));
17902b01b5eSaoyama int s;
18002b01b5eSaoyama
18102b01b5eSaoyama s = splclock();
182b5e88448Saoyama chiptime[MK_CSR] |= (MK_CSR_WRITE << 24); /* enable write */
183b5e88448Saoyama
184b5e88448Saoyama chiptime[MK_SEC] = TOBCD(dt->dt_sec) << 24;
185b5e88448Saoyama chiptime[MK_MIN] = TOBCD(dt->dt_min) << 24;
186b5e88448Saoyama chiptime[MK_HOUR] = TOBCD(dt->dt_hour) << 24;
187b5e88448Saoyama chiptime[MK_DOW] = TOBCD(dt->dt_wday) << 24;
188b5e88448Saoyama chiptime[MK_DOM] = TOBCD(dt->dt_day) << 24;
189b5e88448Saoyama chiptime[MK_MONTH] = TOBCD(dt->dt_mon) << 24;
190b5e88448Saoyama /* XXX: We don't consider UniOS-Mach Y2K problem */
191b5e88448Saoyama chiptime[MK_YEAR] = TOBCD(dt->dt_year - MK_YEAR0) << 24;
192b5e88448Saoyama
193b5e88448Saoyama chiptime[MK_CSR] &= (~MK_CSR_WRITE << 24); /* load them up */
19402b01b5eSaoyama splx(s);
19502b01b5eSaoyama #ifdef TIMEKEEPER_DEBUG
196b5e88448Saoyama printf("set %02d/%02d/%02d %02d:%02d:%02d\n",
19702b01b5eSaoyama dt->dt_year, dt->dt_mon, dt->dt_day,
19802b01b5eSaoyama dt->dt_hour, dt->dt_min, dt->dt_sec);
19902b01b5eSaoyama #endif
20002b01b5eSaoyama
201b5e88448Saoyama /* Write a stamp at NVRAM address 0x10-0x13 */
202b5e88448Saoyama stamp[0] = 'R' << 24; stamp[1] = 'T' << 24;
203b5e88448Saoyama stamp[2] = 'C' << 24; stamp[3] = '\0' << 24;
20402b01b5eSaoyama }
20502b01b5eSaoyama
20602b01b5eSaoyama #define _DS_GET(off, data) \
207805fad4dSmiod do { *chiptime = (off); (data) = (*chipdata); } while (0)
20802b01b5eSaoyama #define _DS_SET(off, data) \
20902b01b5eSaoyama do { *chiptime = (off); *chipdata = (u_int8_t)(data); } while (0)
21002b01b5eSaoyama #define _DS_GET_BCD(off, data) \
21102b01b5eSaoyama do { \
21202b01b5eSaoyama u_int8_t c; \
21302b01b5eSaoyama *chiptime = (off); \
214805fad4dSmiod c = *chipdata; (data) = FROMBCD(c); \
21502b01b5eSaoyama } while (0)
21602b01b5eSaoyama #define _DS_SET_BCD(off, data) \
21702b01b5eSaoyama do { \
21802b01b5eSaoyama *chiptime = (off); \
21902b01b5eSaoyama *chipdata = TOBCD((u_int8_t)(data)); \
22002b01b5eSaoyama } while (0)
22102b01b5eSaoyama
22202b01b5eSaoyama /*
22302b01b5eSaoyama * Get the time of day, based on the clock's value and/or the base value.
22402b01b5eSaoyama */
22502b01b5eSaoyama void
dsclock_get(struct device * dev,time_t base,struct clock_ymdhms * dt)22680bc78ddSaoyama dsclock_get(struct device *dev, time_t base, struct clock_ymdhms *dt)
22702b01b5eSaoyama {
22802b01b5eSaoyama struct timekeeper_softc *sc = (void *)dev;
22902b01b5eSaoyama volatile u_int8_t *chiptime = (void *)sc->sc_clock;
23002b01b5eSaoyama volatile u_int8_t *chipdata = (void *)(sc->sc_clock + 1);
23102b01b5eSaoyama int s;
23202b01b5eSaoyama u_int8_t c;
23302b01b5eSaoyama
23402b01b5eSaoyama s = splclock();
23502b01b5eSaoyama
23602b01b5eSaoyama /* specify 24hr and BCD mode */
23702b01b5eSaoyama _DS_GET(DS_REGB, c);
23802b01b5eSaoyama c |= DS_REGB_24HR;
23902b01b5eSaoyama c &= ~DS_REGB_BINARY;
24002b01b5eSaoyama _DS_SET(DS_REGB, c);
24102b01b5eSaoyama
24202b01b5eSaoyama /* update in progress; spin loop */
2434253d18cSaoyama for (;;) {
24402b01b5eSaoyama *chiptime = DS_REGA;
2454253d18cSaoyama if ((*chipdata & DS_REGA_UIP) == 0)
2464253d18cSaoyama break;
2474253d18cSaoyama }
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
257b5e88448Saoyama /* UniOS-Mach doesn't set the correct BCD year after Y2K */
258b5e88448Saoyama if (dt->dt_year > 100) dt->dt_year -= (DS_YEAR0 % 100);
259b5e88448Saoyama
260b5e88448Saoyama dt->dt_year += DS_YEAR0;
26102b01b5eSaoyama splx(s);
26202b01b5eSaoyama
26302b01b5eSaoyama #ifdef TIMEKEEPER_DEBUG
264b5e88448Saoyama printf("get %02d/%02d/%02d %02d:%02d:%02d\n",
26502b01b5eSaoyama dt->dt_year, dt->dt_mon, dt->dt_day,
26602b01b5eSaoyama dt->dt_hour, dt->dt_min, dt->dt_sec);
26702b01b5eSaoyama #endif
26802b01b5eSaoyama }
26902b01b5eSaoyama
27002b01b5eSaoyama /*
27102b01b5eSaoyama * Reset the TODR based on the time value.
27202b01b5eSaoyama */
27302b01b5eSaoyama void
dsclock_set(struct device * dev,struct clock_ymdhms * dt)27480bc78ddSaoyama dsclock_set(struct device *dev, struct clock_ymdhms *dt)
27502b01b5eSaoyama {
27602b01b5eSaoyama struct timekeeper_softc *sc = (void *)dev;
27702b01b5eSaoyama volatile u_int8_t *chiptime = (void *)sc->sc_clock;
27802b01b5eSaoyama volatile u_int8_t *chipdata = (void *)(sc->sc_clock + 1);
27902b01b5eSaoyama int s;
28002b01b5eSaoyama u_int8_t c;
28102b01b5eSaoyama
28202b01b5eSaoyama s = splclock();
28302b01b5eSaoyama
28402b01b5eSaoyama /* enable write */
28502b01b5eSaoyama _DS_GET(DS_REGB, c);
28602b01b5eSaoyama c |= DS_REGB_SET;
28702b01b5eSaoyama _DS_SET(DS_REGB, c);
28802b01b5eSaoyama
28902b01b5eSaoyama _DS_SET_BCD(DS_SEC, dt->dt_sec);
29002b01b5eSaoyama _DS_SET_BCD(DS_MIN, dt->dt_min);
29102b01b5eSaoyama _DS_SET_BCD(DS_HOUR, dt->dt_hour);
29202b01b5eSaoyama _DS_SET_BCD(DS_DOW, dt->dt_wday);
29302b01b5eSaoyama _DS_SET_BCD(DS_DOM, dt->dt_day);
29402b01b5eSaoyama _DS_SET_BCD(DS_MONTH, dt->dt_mon);
295b5e88448Saoyama /* XXX: We don't consider UniOS-Mach Y2K problem */
29602b01b5eSaoyama _DS_SET_BCD(DS_YEAR, dt->dt_year - DS_YEAR0);
29702b01b5eSaoyama
29802b01b5eSaoyama _DS_GET(DS_REGB, c);
29902b01b5eSaoyama c &= ~DS_REGB_SET;
30002b01b5eSaoyama _DS_SET(DS_REGB, c);
30102b01b5eSaoyama
30202b01b5eSaoyama splx(s);
30302b01b5eSaoyama
30402b01b5eSaoyama #ifdef TIMEKEEPER_DEBUG
305b5e88448Saoyama printf("set %02d/%02d/%02d %02d:%02d:%02d\n",
30602b01b5eSaoyama dt->dt_year, dt->dt_mon, dt->dt_day,
30702b01b5eSaoyama dt->dt_hour, dt->dt_min, dt->dt_sec);
30802b01b5eSaoyama #endif
30902b01b5eSaoyama }
310