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