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