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