xref: /openbsd-src/sys/arch/luna88k/dev/timekeeper.c (revision 471aeecfc619bc9b69519928152daf993376c2a1)
1 /* $OpenBSD: timekeeper.c,v 1.11 2022/04/06 18:59:26 naddy 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 
51 struct 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 
64 int  clock_match(struct device *, void *, void *);
65 void clock_attach(struct device *, struct device *, void *);
66 
67 const struct cfattach clock_ca = {
68 	sizeof (struct timekeeper_softc), clock_match, clock_attach
69 };
70 
71 struct cfdriver clock_cd = {
72 	NULL, "clock", DV_DULL
73 };
74 
75 void mkclock_get(struct device *, time_t, struct clock_ymdhms *);
76 void mkclock_set(struct device *, struct clock_ymdhms *);
77 void dsclock_get(struct device *, time_t, struct clock_ymdhms *);
78 void dsclock_set(struct device *, struct clock_ymdhms *);
79 
80 const struct clockfns mkclock_clockfns = {
81 	NULL /* never used */, mkclock_get, mkclock_set,
82 };
83 
84 const struct clockfns dsclock_clockfns = {
85 	NULL /* never used */, dsclock_get, dsclock_set,
86 };
87 
88 int
clock_match(struct device * parent,void * match,void * aux)89 clock_match(struct device *parent, void *match, void *aux)
90 {
91 	struct mainbus_attach_args *ma = aux;
92 
93 	if (strcmp(ma->ma_name, clock_cd.cd_name))
94 		return 0;
95 	return 1;
96 }
97 
98 extern int machtype; /* in machdep.c */
99 
100 void
clock_attach(struct device * parent,struct device * self,void * aux)101 clock_attach(struct device *parent, struct device *self, void *aux)
102 {
103 	struct timekeeper_softc *sc = (void *)self;
104 	struct mainbus_attach_args *ma = aux;
105 	const struct clockfns *clockwork;
106 
107 	switch (machtype) {
108 	default:
109 	case LUNA_88K:	/* Mostek MK48T02 */
110 		sc->sc_clock = (void *)(ma->ma_addr + MK_NVRAM_SPACE);
111 		sc->sc_nvram = (void *)ma->ma_addr;
112 		sc->sc_nvramsize = MK_NVRAM_SPACE;
113 		clockwork = &mkclock_clockfns;
114 		printf(": MK48T02\n");
115 		break;
116 	case LUNA_88K2: /* Dallas DS1397 */
117 		sc->sc_clock = (void *)ma->ma_addr;
118 		sc->sc_nvram = (void *)(ma->ma_addr + 50);
119 		sc->sc_nvramsize = 50;
120 		clockwork = &dsclock_clockfns;
121 		printf(": DS1397\n");
122 		break;
123 	}
124 
125 	evcount_attach(&sc->sc_count, self->dv_xname, &ma->ma_ilvl);
126 
127 	clockattach(&sc->sc_dev, clockwork, &sc->sc_count);
128 }
129 
130 /*
131  * On LUNA-88K, NVRAM contents and Timekeeper registers are mapped on the
132  * most significant byte of each 32bit word. (i.e. 4-bytes stride)
133  *
134  * Get the time of day, based on the clock's value and/or the base value.
135  */
136 void
mkclock_get(struct device * dev,time_t base,struct clock_ymdhms * dt)137 mkclock_get(struct device *dev, time_t base, struct clock_ymdhms *dt)
138 {
139 	struct timekeeper_softc *sc = (void *)dev;
140 	volatile u_int32_t *chiptime = (void *)sc->sc_clock;
141 	int s;
142 
143 	s = splclock();
144 
145 	/* enable read (stop time) */
146 	chiptime[MK_CSR] |= (MK_CSR_READ << 24);
147 
148 	dt->dt_sec  = FROMBCD(chiptime[MK_SEC]   >> 24);
149 	dt->dt_min  = FROMBCD(chiptime[MK_MIN]   >> 24);
150 	dt->dt_hour = FROMBCD(chiptime[MK_HOUR]  >> 24);
151 	dt->dt_wday = FROMBCD(chiptime[MK_DOW]   >> 24);
152 	dt->dt_day  = FROMBCD(chiptime[MK_DOM]   >> 24);
153 	dt->dt_mon  = FROMBCD(chiptime[MK_MONTH] >> 24);
154 	dt->dt_year = FROMBCD(chiptime[MK_YEAR]  >> 24);
155 
156 	chiptime[MK_CSR] &= (~MK_CSR_READ << 24);	/* time wears on */
157 
158 	/* UniOS-Mach doesn't set the correct BCD year after Y2K */
159 	if (dt->dt_year > 100) dt->dt_year -= (MK_YEAR0 % 100);
160 
161 	dt->dt_year += MK_YEAR0;
162 	splx(s);
163 #ifdef TIMEKEEPER_DEBUG
164 	printf("get %02d/%02d/%02d %02d:%02d:%02d\n",
165 	    dt->dt_year, dt->dt_mon, dt->dt_day,
166 	    dt->dt_hour, dt->dt_min, dt->dt_sec);
167 #endif
168 }
169 
170 /*
171  * Reset the TODR based on the time value.
172  */
173 void
mkclock_set(struct device * dev,struct clock_ymdhms * dt)174 mkclock_set(struct device *dev, struct clock_ymdhms *dt)
175 {
176 	struct timekeeper_softc *sc = (void *)dev;
177 	volatile u_int32_t *chiptime = (void *)sc->sc_clock;
178 	volatile u_int32_t *stamp = (void *)(sc->sc_nvram + (4 * 0x10));
179 	int s;
180 
181 	s = splclock();
182 	chiptime[MK_CSR]  |= (MK_CSR_WRITE << 24);	/* enable write */
183 
184 	chiptime[MK_SEC]   = TOBCD(dt->dt_sec)  << 24;
185 	chiptime[MK_MIN]   = TOBCD(dt->dt_min)  << 24;
186 	chiptime[MK_HOUR]  = TOBCD(dt->dt_hour) << 24;
187 	chiptime[MK_DOW]   = TOBCD(dt->dt_wday) << 24;
188 	chiptime[MK_DOM]   = TOBCD(dt->dt_day)  << 24;
189 	chiptime[MK_MONTH] = TOBCD(dt->dt_mon)  << 24;
190 	/* XXX: We don't consider UniOS-Mach Y2K problem */
191 	chiptime[MK_YEAR]  = TOBCD(dt->dt_year - MK_YEAR0) << 24;
192 
193 	chiptime[MK_CSR]  &= (~MK_CSR_WRITE << 24);	/* load them up */
194 	splx(s);
195 #ifdef TIMEKEEPER_DEBUG
196 	printf("set %02d/%02d/%02d %02d:%02d:%02d\n",
197 	    dt->dt_year, dt->dt_mon, dt->dt_day,
198 	    dt->dt_hour, dt->dt_min, dt->dt_sec);
199 #endif
200 
201 	/* Write a stamp at NVRAM address 0x10-0x13 */
202 	stamp[0] = 'R' << 24; stamp[1] = 'T' << 24;
203 	stamp[2] = 'C' << 24; stamp[3] = '\0' << 24;
204 }
205 
206 #define _DS_GET(off, data) \
207 	do { *chiptime = (off); (data) = (*chipdata); } while (0)
208 #define _DS_SET(off, data) \
209 	do { *chiptime = (off); *chipdata = (u_int8_t)(data); } while (0)
210 #define _DS_GET_BCD(off, data) \
211 	do { \
212 		u_int8_t c; \
213 		*chiptime = (off); \
214 		c = *chipdata; (data) = FROMBCD(c); \
215 	} while (0)
216 #define _DS_SET_BCD(off, data) \
217 	do { \
218 		*chiptime = (off); \
219 		*chipdata = TOBCD((u_int8_t)(data)); \
220 	} while (0)
221 
222 /*
223  * Get the time of day, based on the clock's value and/or the base value.
224  */
225 void
dsclock_get(struct device * dev,time_t base,struct clock_ymdhms * dt)226 dsclock_get(struct device *dev, time_t base, struct clock_ymdhms *dt)
227 {
228 	struct timekeeper_softc *sc = (void *)dev;
229 	volatile u_int8_t *chiptime = (void *)sc->sc_clock;
230 	volatile u_int8_t *chipdata = (void *)(sc->sc_clock + 1);
231 	int s;
232 	u_int8_t c;
233 
234 	s = splclock();
235 
236 	/* specify 24hr and BCD mode */
237 	_DS_GET(DS_REGB, c);
238 	c |= DS_REGB_24HR;
239 	c &= ~DS_REGB_BINARY;
240 	_DS_SET(DS_REGB, c);
241 
242 	/* update in progress; spin loop */
243 	for (;;) {
244 		*chiptime = DS_REGA;
245 		if ((*chipdata & DS_REGA_UIP) == 0)
246 			break;
247 	}
248 
249 	_DS_GET_BCD(DS_SEC,   dt->dt_sec);
250 	_DS_GET_BCD(DS_MIN,   dt->dt_min);
251 	_DS_GET_BCD(DS_HOUR,  dt->dt_hour);
252 	_DS_GET_BCD(DS_DOW,   dt->dt_wday);
253 	_DS_GET_BCD(DS_DOM,   dt->dt_day);
254 	_DS_GET_BCD(DS_MONTH, dt->dt_mon);
255 	_DS_GET_BCD(DS_YEAR,  dt->dt_year);
256 
257 	/* UniOS-Mach doesn't set the correct BCD year after Y2K */
258 	if (dt->dt_year > 100) dt->dt_year -= (DS_YEAR0 % 100);
259 
260 	dt->dt_year += DS_YEAR0;
261 	splx(s);
262 
263 #ifdef TIMEKEEPER_DEBUG
264 	printf("get %02d/%02d/%02d %02d:%02d:%02d\n",
265 	    dt->dt_year, dt->dt_mon, dt->dt_day,
266 	    dt->dt_hour, dt->dt_min, dt->dt_sec);
267 #endif
268 }
269 
270 /*
271  * Reset the TODR based on the time value.
272  */
273 void
dsclock_set(struct device * dev,struct clock_ymdhms * dt)274 dsclock_set(struct device *dev, struct clock_ymdhms *dt)
275 {
276 	struct timekeeper_softc *sc = (void *)dev;
277 	volatile u_int8_t *chiptime = (void *)sc->sc_clock;
278 	volatile u_int8_t *chipdata = (void *)(sc->sc_clock + 1);
279 	int s;
280 	u_int8_t c;
281 
282 	s = splclock();
283 
284 	/* enable write */
285 	_DS_GET(DS_REGB, c);
286 	c |= DS_REGB_SET;
287 	_DS_SET(DS_REGB, c);
288 
289 	_DS_SET_BCD(DS_SEC,   dt->dt_sec);
290 	_DS_SET_BCD(DS_MIN,   dt->dt_min);
291 	_DS_SET_BCD(DS_HOUR,  dt->dt_hour);
292 	_DS_SET_BCD(DS_DOW,   dt->dt_wday);
293 	_DS_SET_BCD(DS_DOM,   dt->dt_day);
294 	_DS_SET_BCD(DS_MONTH, dt->dt_mon);
295 	/* XXX: We don't consider UniOS-Mach Y2K problem */
296 	_DS_SET_BCD(DS_YEAR,  dt->dt_year - DS_YEAR0);
297 
298 	_DS_GET(DS_REGB, c);
299 	c &= ~DS_REGB_SET;
300 	_DS_SET(DS_REGB, c);
301 
302 	splx(s);
303 
304 #ifdef TIMEKEEPER_DEBUG
305 	printf("set %02d/%02d/%02d %02d:%02d:%02d\n",
306 	    dt->dt_year, dt->dt_mon, dt->dt_day,
307 	    dt->dt_hour, dt->dt_min, dt->dt_sec);
308 #endif
309 }
310