timekeeper.c revision 1.8
1/* $OpenBSD: timekeeper.c,v 1.8 2013/11/12 13:56:23 aoyama Exp $ */
2/* $NetBSD: timekeeper.c,v 1.1 2000/01/05 08:48:56 nisimura Exp $ */
3
4/*-
5 * Copyright (c) 2000 The NetBSD Foundation, Inc.
6 * All rights reserved.
7 *
8 * This code is derived from software contributed to The NetBSD Foundation
9 * by Tohru Nishimura.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 *    notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 *    notice, this list of conditions and the following disclaimer in the
18 *    documentation and/or other materials provided with the distribution.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 */
32
33#include <sys/param.h>
34#include <sys/systm.h>
35#include <sys/device.h>
36#include <sys/kernel.h>
37#include <sys/evcount.h>
38
39#include <machine/autoconf.h>
40#include <machine/board.h>	/* machtype value */
41#include <machine/cpu.h>
42
43#include <dev/clock_subr.h>
44
45#include <luna88k/luna88k/clockvar.h>
46#include <luna88k/dev/timekeeper.h>
47
48#define	MK_YEAR0	1970	/* year offset of MK */
49#define	DS_YEAR0	1990	/* year offset of DS */
50
51struct timekeeper_softc {
52	struct device sc_dev;
53	void *sc_clock, *sc_nvram;
54	int sc_nvramsize;
55	struct evcount sc_count;
56};
57
58/*
59 * BCD to decimal and decimal to BCD.
60 */
61#define FROMBCD(x)      (((x) >> 4) * 10 + ((x) & 0xf))
62#define TOBCD(x)        (((x) / 10 * 16) + ((x) % 10))
63
64int  clock_match(struct device *, void *, void *);
65void clock_attach(struct device *, struct device *, void *);
66
67struct cfattach clock_ca = {
68	sizeof (struct timekeeper_softc), clock_match, clock_attach
69};
70
71struct cfdriver clock_cd = {
72        NULL, "clock", DV_DULL
73};
74
75void mkclock_get(struct device *, time_t, struct clock_ymdhms *);
76void mkclock_set(struct device *, struct clock_ymdhms *);
77void dsclock_get(struct device *, time_t, struct clock_ymdhms *);
78void dsclock_set(struct device *, struct clock_ymdhms *);
79
80const struct clockfns mkclock_clockfns = {
81	NULL /* never used */, mkclock_get, mkclock_set,
82};
83
84const struct clockfns dsclock_clockfns = {
85	NULL /* never used */, dsclock_get, dsclock_set,
86};
87
88int
89clock_match(parent, match, aux)
90        struct device *parent;
91        void *match, *aux;
92{
93	struct mainbus_attach_args *ma = aux;
94
95	if (strcmp(ma->ma_name, clock_cd.cd_name))
96		return 0;
97	return 1;
98}
99
100extern int machtype; /* in machdep.c */
101
102void
103clock_attach(parent, self, aux)
104        struct device *parent, *self;
105        void *aux;
106{
107	struct timekeeper_softc *sc = (void *)self;
108	struct mainbus_attach_args *ma = aux;
109	const struct clockfns *clockwork;
110
111	switch (machtype) {
112	default:
113	case LUNA_88K:	/* Mostek MK48T02 */
114		sc->sc_clock = (void *)(ma->ma_addr + MK_NVRAM_SPACE);
115		sc->sc_nvram = (void *)ma->ma_addr;
116		sc->sc_nvramsize = MK_NVRAM_SPACE;
117		clockwork = &mkclock_clockfns;
118		printf(": MK48T02\n");
119		break;
120	case LUNA_88K2: /* Dallas DS1397 */
121		sc->sc_clock = (void *)ma->ma_addr;
122		sc->sc_nvram = (void *)(ma->ma_addr + 50);
123		sc->sc_nvramsize = 50;
124		clockwork = &dsclock_clockfns;
125		printf(": DS1397\n");
126		break;
127	}
128
129	evcount_attach(&sc->sc_count, self->dv_xname, &ma->ma_ilvl);
130
131	clockattach(&sc->sc_dev, clockwork, &sc->sc_count);
132}
133
134/*
135 * On LUNA-88K, NVRAM contents and Timekeeper registers are mapped on the
136 * most significant byte of each 32bit word. (i.e. 4-bytes stride)
137 *
138 * Get the time of day, based on the clock's value and/or the base value.
139 */
140void
141mkclock_get(dev, base, dt)
142	struct device *dev;
143	time_t base;
144	struct clock_ymdhms *dt;
145{
146	struct timekeeper_softc *sc = (void *)dev;
147	volatile u_int32_t *chiptime = (void *)sc->sc_clock;
148	int s;
149
150	s = splclock();
151
152	/* enable read (stop time) */
153	chiptime[MK_CSR] |= (MK_CSR_READ << 24);
154
155	dt->dt_sec  = FROMBCD(chiptime[MK_SEC]   >> 24);
156	dt->dt_min  = FROMBCD(chiptime[MK_MIN]   >> 24);
157	dt->dt_hour = FROMBCD(chiptime[MK_HOUR]  >> 24);
158	dt->dt_wday = FROMBCD(chiptime[MK_DOW]   >> 24);
159	dt->dt_day  = FROMBCD(chiptime[MK_DOM]   >> 24);
160	dt->dt_mon  = FROMBCD(chiptime[MK_MONTH] >> 24);
161	dt->dt_year = FROMBCD(chiptime[MK_YEAR]  >> 24);
162
163	chiptime[MK_CSR] &= (~MK_CSR_READ << 24);	/* time wears on */
164
165	/* UniOS-Mach doesn't set the correct BCD year after Y2K */
166	if (dt->dt_year > 100) dt->dt_year -= (MK_YEAR0 % 100);
167
168	dt->dt_year += MK_YEAR0;
169	splx(s);
170#ifdef TIMEKEEPER_DEBUG
171	printf("get %02d/%02d/%02d %02d:%02d:%02d\n",
172	    dt->dt_year, dt->dt_mon, dt->dt_day,
173	    dt->dt_hour, dt->dt_min, dt->dt_sec);
174#endif
175}
176
177/*
178 * Reset the TODR based on the time value.
179 */
180void
181mkclock_set(dev, dt)
182	struct device *dev;
183	struct clock_ymdhms *dt;
184{
185	struct timekeeper_softc *sc = (void *)dev;
186	volatile u_int32_t *chiptime = (void *)sc->sc_clock;
187	volatile u_int32_t *stamp = (void *)(sc->sc_nvram + (4 * 0x10));
188	int s;
189
190	s = splclock();
191	chiptime[MK_CSR]  |= (MK_CSR_WRITE << 24);	/* enable write */
192
193	chiptime[MK_SEC]   = TOBCD(dt->dt_sec)  << 24;
194	chiptime[MK_MIN]   = TOBCD(dt->dt_min)  << 24;
195	chiptime[MK_HOUR]  = TOBCD(dt->dt_hour) << 24;
196	chiptime[MK_DOW]   = TOBCD(dt->dt_wday) << 24;
197	chiptime[MK_DOM]   = TOBCD(dt->dt_day)  << 24;
198	chiptime[MK_MONTH] = TOBCD(dt->dt_mon)  << 24;
199	/* XXX: We don't consider UniOS-Mach Y2K problem */
200	chiptime[MK_YEAR]  = TOBCD(dt->dt_year - MK_YEAR0) << 24;
201
202	chiptime[MK_CSR]  &= (~MK_CSR_WRITE << 24);	/* load them up */
203	splx(s);
204#ifdef TIMEKEEPER_DEBUG
205	printf("set %02d/%02d/%02d %02d:%02d:%02d\n",
206	    dt->dt_year, dt->dt_mon, dt->dt_day,
207	    dt->dt_hour, dt->dt_min, dt->dt_sec);
208#endif
209
210	/* Write a stamp at NVRAM address 0x10-0x13 */
211	stamp[0] = 'R' << 24; stamp[1] = 'T' << 24;
212	stamp[2] = 'C' << 24; stamp[3] = '\0' << 24;
213}
214
215#define _DS_GET(off, data) \
216	do { *chiptime = (off); (data) = (*chipdata); } while (0)
217#define _DS_SET(off, data) \
218	do { *chiptime = (off); *chipdata = (u_int8_t)(data); } while (0)
219#define _DS_GET_BCD(off, data) \
220	do { \
221		u_int8_t c; \
222		*chiptime = (off); \
223		c = *chipdata; (data) = FROMBCD(c); \
224	} while (0)
225#define _DS_SET_BCD(off, data) \
226	do { \
227		*chiptime = (off); \
228		*chipdata = TOBCD((u_int8_t)(data)); \
229	} while (0)
230
231/*
232 * Get the time of day, based on the clock's value and/or the base value.
233 */
234void
235dsclock_get(dev, base, dt)
236	struct device *dev;
237	time_t base;
238	struct clock_ymdhms *dt;
239{
240	struct timekeeper_softc *sc = (void *)dev;
241	volatile u_int8_t *chiptime = (void *)sc->sc_clock;
242	volatile u_int8_t *chipdata = (void *)(sc->sc_clock + 1);
243	int s;
244	u_int8_t c;
245
246	s = splclock();
247
248	/* specify 24hr and BCD mode */
249	_DS_GET(DS_REGB, c);
250	c |= DS_REGB_24HR;
251	c &= ~DS_REGB_BINARY;
252	_DS_SET(DS_REGB, c);
253
254	/* update in progress; spin loop */
255	for (;;) {
256		*chiptime = DS_REGA;
257		if ((*chipdata & DS_REGA_UIP) == 0)
258			break;
259	}
260
261	_DS_GET_BCD(DS_SEC,   dt->dt_sec);
262	_DS_GET_BCD(DS_MIN,   dt->dt_min);
263	_DS_GET_BCD(DS_HOUR,  dt->dt_hour);
264	_DS_GET_BCD(DS_DOW,   dt->dt_wday);
265	_DS_GET_BCD(DS_DOM,   dt->dt_day);
266	_DS_GET_BCD(DS_MONTH, dt->dt_mon);
267	_DS_GET_BCD(DS_YEAR,  dt->dt_year);
268
269	/* UniOS-Mach doesn't set the correct BCD year after Y2K */
270	if (dt->dt_year > 100) dt->dt_year -= (DS_YEAR0 % 100);
271
272	dt->dt_year += DS_YEAR0;
273	splx(s);
274
275#ifdef TIMEKEEPER_DEBUG
276	printf("get %02d/%02d/%02d %02d:%02d:%02d\n",
277	    dt->dt_year, dt->dt_mon, dt->dt_day,
278	    dt->dt_hour, dt->dt_min, dt->dt_sec);
279#endif
280}
281
282/*
283 * Reset the TODR based on the time value.
284 */
285void
286dsclock_set(dev, dt)
287	struct device *dev;
288	struct clock_ymdhms *dt;
289{
290	struct timekeeper_softc *sc = (void *)dev;
291	volatile u_int8_t *chiptime = (void *)sc->sc_clock;
292	volatile u_int8_t *chipdata = (void *)(sc->sc_clock + 1);
293	int s;
294	u_int8_t c;
295
296	s = splclock();
297
298	/* enable write */
299	_DS_GET(DS_REGB, c);
300	c |= DS_REGB_SET;
301	_DS_SET(DS_REGB, c);
302
303	_DS_SET_BCD(DS_SEC,   dt->dt_sec);
304	_DS_SET_BCD(DS_MIN,   dt->dt_min);
305	_DS_SET_BCD(DS_HOUR,  dt->dt_hour);
306	_DS_SET_BCD(DS_DOW,   dt->dt_wday);
307	_DS_SET_BCD(DS_DOM,   dt->dt_day);
308	_DS_SET_BCD(DS_MONTH, dt->dt_mon);
309	/* XXX: We don't consider UniOS-Mach Y2K problem */
310	_DS_SET_BCD(DS_YEAR,  dt->dt_year - DS_YEAR0);
311
312	_DS_GET(DS_REGB, c);
313	c &= ~DS_REGB_SET;
314	_DS_SET(DS_REGB, c);
315
316	splx(s);
317
318#ifdef TIMEKEEPER_DEBUG
319	printf("set %02d/%02d/%02d %02d:%02d:%02d\n",
320	    dt->dt_year, dt->dt_mon, dt->dt_day,
321	    dt->dt_hour, dt->dt_min, dt->dt_sec);
322#endif
323}
324