xref: /openbsd/sys/arch/luna88k/dev/timekeeper.c (revision b5e88448)
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