xref: /netbsd/sys/arch/vax/vax/clock.c (revision bf9ec67e)
1 /*	$NetBSD: clock.c,v 1.40 2001/05/16 05:36:55 matt Exp $	 */
2 /*
3  * Copyright (c) 1995 Ludd, University of Lule}, Sweden.
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. All advertising materials mentioning features or use of this software
15  *    must display the following acknowledgement:
16  *     This product includes software developed at Ludd, University of Lule}.
17  * 4. The name of the author may not be used to endorse or promote products
18  *    derived from this software without specific prior written permission
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 
33 #include <sys/param.h>
34 #include <sys/kernel.h>
35 #include <sys/systm.h>
36 #include <sys/device.h>
37 
38 #include <dev/clock_subr.h>
39 
40 #include <machine/mtpr.h>
41 #include <machine/sid.h>
42 #include <machine/clock.h>
43 #include <machine/cpu.h>
44 #include <machine/uvax.h>
45 
46 #include "opt_cputype.h"
47 
48 struct evcnt clock_intrcnt =
49 	EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, "clock", "intr");
50 
51 /*
52  * microtime() should return number of usecs in struct timeval.
53  * We may get wrap-arounds, but that will be fixed with lasttime
54  * check. This may fault within 10 msecs.
55  */
56 void
57 microtime(tvp)
58 	struct timeval *tvp;
59 {
60 	int s, i;
61 	static struct timeval lasttime;
62 
63 	s = splhigh();
64 	bcopy((caddr_t)&time, tvp, sizeof(struct timeval));
65 
66 	switch (vax_boardtype) {
67 #if VAX46 || VAXANY
68 	case VAX_BTYP_46: {
69 		extern struct vs_cpu *ka46_cpu;
70 		i = *(volatile int *)(&ka46_cpu->vc_diagtimu);
71 		i = (i >> 16) * 1024 + (i & 0x3ff);
72 		break;
73 		}
74 #endif
75 #if VAX48 || VAXANY
76 	case VAX_BTYP_48: {
77 		/*
78 		 * PR_ICR doesn't exist.  We could use the vc_diagtimu
79 		 * counter, saving the value on the timer interrupt and
80 		 * subtracting that from the current value.
81 		 */
82 		i = 0;
83 		break;
84 		}
85 #endif
86 	default:
87 		i = mfpr(PR_ICR);
88 		break;
89 	}
90 	i += tick; /* Get current interval count */
91 	tvp->tv_usec += i;
92 	while (tvp->tv_usec >= 1000000) {
93 		tvp->tv_sec++;
94 		tvp->tv_usec -= 1000000;
95 	}
96 	if (tvp->tv_sec == lasttime.tv_sec &&
97 	    tvp->tv_usec <= lasttime.tv_usec &&
98 	    (tvp->tv_usec = lasttime.tv_usec + 1) >= 1000000) {
99 		tvp->tv_sec++;
100 		tvp->tv_usec -= 1000000;
101 	}
102 	bcopy(tvp, &lasttime, sizeof(struct timeval));
103 	splx(s);
104 }
105 
106 /*
107  * Sets year to the year in fs_time and then calculates the number of
108  * 100th of seconds in the current year and saves that info in year_len.
109  * fs_time contains the time set in the superblock in the root filesystem.
110  * If the clock is started, it then checks if the time is valid
111  * compared with the time in fs_time. If the clock is stopped, an
112  * alert is printed and the time is temporary set to the time in fs_time.
113  */
114 
115 void
116 inittodr(fs_time)
117 	time_t fs_time;
118 {
119 	int rv;
120 
121 	rv = (*dep_call->cpu_clkread) (fs_time);
122 	switch (rv) {
123 
124 	case CLKREAD_BAD: /* No useable information from system clock */
125 		time.tv_sec = fs_time;
126 		resettodr();
127 		break;
128 
129 	case CLKREAD_WARN: /* Just give the warning */
130 		break;
131 
132 	default: /* System clock OK, no warning if we don't want to. */
133 		if (time.tv_sec > fs_time + 3 * SEC_PER_DAY) {
134 			printf("Clock has gained %ld days",
135 			    (time.tv_sec - fs_time) / SEC_PER_DAY);
136 			rv = CLKREAD_WARN;
137 		} else if (time.tv_sec + SEC_PER_DAY < fs_time) {
138 			printf("Clock has lost %ld day(s)",
139 			    (fs_time - time.tv_sec) / SEC_PER_DAY);
140 			rv = CLKREAD_WARN;
141 		}
142 		break;
143 	}
144 
145 	if (rv < CLKREAD_OK)
146 		printf(" - CHECK AND RESET THE DATE.\n");
147 }
148 
149 /*
150  * Resettodr restores the time of day hardware after a time change.
151  */
152 
153 void
154 resettodr()
155 {
156 	(*dep_call->cpu_clkwrite)();
157 }
158 /*
159  * A delayloop that delays about the number of milliseconds that is
160  * given as argument.
161  */
162 void
163 delay(i)
164 	int i;
165 {
166 	asm ("1: sobgtr %0, 1b" : : "r" (dep_call->cpu_vups * i));
167 }
168 
169 /*
170  * On all VAXen there are a microsecond clock that should
171  * be used for interval interrupts. Some CPUs don't use the ICR interval
172  * register but it doesn't hurt to load it anyway.
173  */
174 void
175 cpu_initclocks()
176 {
177 	mtpr(-10000, PR_NICR); /* Load in count register */
178 	mtpr(0x800000d1, PR_ICCS); /* Start clock and enable interrupt */
179 	evcnt_attach_static(&clock_intrcnt);
180 }
181 
182 /*
183  * There are two types of real-time battery-backed up clocks on
184  * VAX computers, one with a register that counts up every 1/100 second,
185  * one with a clock chip that delivers time. For the register clock
186  * we have a generic version, and for the chip clock there are
187  * support routines for time conversion.
188  */
189 /*
190  * Converts a year to corresponding number of ticks.
191  */
192 int
193 yeartonum(y)
194 	int y;
195 {
196 	int n;
197 
198 	for (n = 0, y -= 1; y > 69; y--)
199 		n += SECPERYEAR(y);
200 	return n;
201 }
202 
203 /*
204  * Converts tick number to a year 70 ->
205  */
206 int
207 numtoyear(num)
208 	int num;
209 {
210 	int y = 70, j;
211 	while(num >= (j = SECPERYEAR(y))) {
212 		y++;
213 		num -= j;
214 	}
215 	return y;
216 }
217 
218 #if VAX750 || VAX780 || VAX8600 || VAX650 || \
219     VAX660 || VAX670 || VAX680 || VAX53 || VAXANY
220 /*
221  * Reads the TODR register; returns a (probably) true tick value,
222  * or CLKREAD_BAD if failed. The year is based on the argument
223  * year; the TODR doesn't hold years.
224  */
225 int
226 generic_clkread(base)
227 	time_t base;
228 {
229 	unsigned klocka = mfpr(PR_TODR);
230 
231 	/*
232 	 * Sanity check.
233 	 */
234 	if (klocka < TODRBASE) {
235 		if (klocka == 0)
236 			printf("TODR stopped");
237 		else
238 			printf("TODR too small");
239 		return CLKREAD_BAD;
240 	}
241 
242 	time.tv_sec = yeartonum(numtoyear(base)) + (klocka - TODRBASE) / 100;
243 	return CLKREAD_OK;
244 }
245 
246 /*
247  * Takes the current system time and writes it to the TODR.
248  */
249 void
250 generic_clkwrite()
251 {
252 	unsigned tid = time.tv_sec, bastid;
253 
254 	bastid = tid - yeartonum(numtoyear(tid));
255 	mtpr((bastid * 100) + TODRBASE, PR_TODR);
256 }
257 #endif
258 
259 #if VAX630 || VAX410 || VAX43 || VAX8200 || VAX46 || VAX48 || VAX49 || VAXANY
260 
261 volatile short *clk_page;	/* where the chip is mapped in virtual memory */
262 int	clk_adrshift;	/* how much to multiply the in-page address with */
263 int	clk_tweak;	/* Offset of time into word. */
264 
265 #define	REGPEEK(off)	(clk_page[off << clk_adrshift] >> clk_tweak)
266 #define	REGPOKE(off, v)	(clk_page[off << clk_adrshift] = ((v) << clk_tweak))
267 
268 int
269 chip_clkread(base)
270 	time_t base;
271 {
272 	struct clock_ymdhms c;
273 	int timeout = 1<<15, s;
274 
275 #ifdef DIAGNOSTIC
276 	if (clk_page == 0)
277 		panic("trying to use unset chip clock page");
278 #endif
279 
280 	if ((REGPEEK(CSRD_OFF) & CSRD_VRT) == 0) {
281 		printf("WARNING: TOY clock not marked valid");
282 		return CLKREAD_BAD;
283 	}
284 	while (REGPEEK(CSRA_OFF) & CSRA_UIP)
285 		if (--timeout == 0) {
286 			printf ("TOY clock timed out");
287 			return CLKREAD_BAD;
288 		}
289 
290 	s = splhigh();
291 	c.dt_year = ((u_char)REGPEEK(YR_OFF)) + 1970;
292 	c.dt_mon = REGPEEK(MON_OFF);
293 	c.dt_day = REGPEEK(DAY_OFF);
294 	c.dt_wday = REGPEEK(WDAY_OFF);
295 	c.dt_hour = REGPEEK(HR_OFF);
296 	c.dt_min = REGPEEK(MIN_OFF);
297 	c.dt_sec = REGPEEK(SEC_OFF);
298 	splx(s);
299 
300 	time.tv_sec = clock_ymdhms_to_secs(&c);
301 	return CLKREAD_OK;
302 }
303 
304 void
305 chip_clkwrite()
306 {
307 	struct clock_ymdhms c;
308 
309 #ifdef DIAGNOSTIC
310 	if (clk_page == 0)
311 		panic("trying to use unset chip clock page");
312 #endif
313 
314 	REGPOKE(CSRB_OFF, CSRB_SET);
315 
316 	clock_secs_to_ymdhms(time.tv_sec, &c);
317 
318 	REGPOKE(YR_OFF, ((u_char)(c.dt_year - 1970)));
319 	REGPOKE(MON_OFF, c.dt_mon);
320 	REGPOKE(DAY_OFF, c.dt_day);
321 	REGPOKE(WDAY_OFF, c.dt_wday);
322 	REGPOKE(HR_OFF, c.dt_hour);
323 	REGPOKE(MIN_OFF, c.dt_min);
324 	REGPOKE(SEC_OFF, c.dt_sec);
325 
326 	REGPOKE(CSRB_OFF, CSRB_DM|CSRB_24);
327 };
328 #endif
329