xref: /netbsd-src/sys/arch/vax/vax/clock.c (revision ef06fc5cad694e5a786cb13b2d8ab7ada0633f9d)
1 /*	$NetBSD: clock.c,v 1.60 2020/07/03 16:23:03 maxv 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  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26 
27 #include <sys/cdefs.h>
28 __KERNEL_RCSID(0, "$NetBSD: clock.c,v 1.60 2020/07/03 16:23:03 maxv Exp $");
29 
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/cpu.h>
33 #include <sys/device.h>
34 #include <sys/timetc.h>
35 #include <sys/kernel.h>
36 
37 #include <machine/sid.h>
38 #include <machine/clock.h>
39 
40 #include "opt_cputype.h"
41 
42 struct evcnt clock_misscnt =
43 	EVCNT_INITIALIZER(EVCNT_TYPE_MISC, NULL, "clock", "intr miss");
44 
45 EVCNT_ATTACH_STATIC(clock_misscnt);
46 
47 struct evcnt clock_intrcnt =
48 	EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, "clock", "intr");
49 
50 EVCNT_ATTACH_STATIC(clock_intrcnt);
51 
52 static int vax_gettime(todr_chip_handle_t, struct timeval *);
53 static int vax_settime(todr_chip_handle_t, struct timeval *);
54 
55 static struct todr_chip_handle todr_handle = {
56 	.todr_gettime = vax_gettime,
57 	.todr_settime = vax_settime,
58 };
59 
60 #if VAX46 || VAXANY
61 static u_int
vax_diag_get_counter(struct timecounter * tc)62 vax_diag_get_counter(struct timecounter *tc)
63 {
64 	extern struct vs_cpu *ka46_cpu;
65 	int cur_hardclock;
66 	u_int counter;
67 
68 	do {
69 		cur_hardclock = getticks();
70 		counter = *(volatile u_int *)&ka46_cpu->vc_diagtimu;
71 	} while (cur_hardclock != getticks());
72 
73 	counter = (counter & 0x3ff) + (counter >> 16) * 1024;
74 
75 	return counter + getticks() * tick;
76 }
77 #endif
78 
79 static u_int
vax_mfpr_get_counter(struct timecounter * tc)80 vax_mfpr_get_counter(struct timecounter *tc)
81 {
82 	int cur_hardclock;
83 	u_int counter;
84 	static int prev_count, prev_hardclock;
85 
86 	do {
87 		cur_hardclock = getticks();
88 		counter = mfpr(PR_ICR) + tick;
89 	} while (cur_hardclock != getticks());
90 
91 	/*
92 	 * Handle interval counter wrapping with interrupts blocked.
93 	 * If the current getticks() is less than what we saw
94 	 *   previously, use the previous value.
95 	 * If the interval counter is smaller, assume it has wrapped,
96 	 *   and if the [adjusted] current hardclock ticks is the same
97 	 *   as what we saw previously, increment the local copy of
98 	 *   the hardclock ticks.
99 	 */
100 	if (cur_hardclock < prev_hardclock)
101 		cur_hardclock = prev_hardclock;
102 	if (counter < prev_count && cur_hardclock == prev_hardclock)
103 		cur_hardclock++;
104 
105 	prev_count = counter;
106 	prev_hardclock=cur_hardclock;
107 
108 	return counter + cur_hardclock * tick;
109 }
110 
111 #if VAX46 || VAXANY
112 static struct timecounter vax_diag_tc = {
113 	.tc_get_timecount = vax_diag_get_counter,
114 	.tc_counter_mask = ~0u,
115 	.tc_frequency = 1000000,
116 	.tc_name = "diagtimer",
117 	.tc_quality = 100,
118 };
119 #endif
120 
121 static struct timecounter vax_mfpr_tc = {
122 	.tc_get_timecount = vax_mfpr_get_counter,
123 	.tc_counter_mask = ~0u,
124 	.tc_frequency = 1000000,
125 	.tc_name = "mfpr",
126 	.tc_quality = 100,
127 };
128 
129 /*
130  * A delayloop that delays about the number of milliseconds that is
131  * given as argument.
132  */
133 void
delay(int i)134 delay(int i)
135 {
136 	__asm ("1: sobgtr %0, 1b" : : "r" (dep_call->cpu_vups * i));
137 }
138 
139 /*
140  * On all VAXen there are a microsecond clock that should
141  * be used for interval interrupts. Some CPUs don't use the ICR interval
142  * register but it doesn't hurt to load it anyway.
143  */
144 void
cpu_initclocks(void)145 cpu_initclocks(void)
146 {
147 	mtpr(-10000, PR_NICR); /* Load in count register */
148 	mtpr(0x800000d1, PR_ICCS); /* Start clock and enable interrupt */
149 
150 	todr_attach(&todr_handle);
151 
152 #if VAX46 || VAXANY
153 	if (vax_boardtype == VAX_BTYP_46)
154 		tc_init(&vax_diag_tc);
155 #endif
156 	if (vax_boardtype != VAX_BTYP_46 && vax_boardtype != VAX_BTYP_48)
157 		tc_init(&vax_mfpr_tc);
158 }
159 
160 int
vax_gettime(todr_chip_handle_t handle,struct timeval * tvp)161 vax_gettime(todr_chip_handle_t handle, struct timeval *tvp)
162 {
163 	tvp->tv_sec = handle->base_time;
164 	return (*dep_call->cpu_gettime)(tvp);
165 }
166 
167 int
vax_settime(todr_chip_handle_t handle,struct timeval * tvp)168 vax_settime(todr_chip_handle_t handle, struct timeval *tvp)
169 {
170 	(*dep_call->cpu_settime)(tvp);
171 	return 0;
172 }
173 
174 /*
175  * There are two types of real-time battery-backed up clocks on
176  * VAX computers, one with a register that counts up every 1/100 second,
177  * one with a clock chip that delivers time. For the register clock
178  * we have a generic version, and for the chip clock there are
179  * support routines for time conversion.
180  */
181 /*
182  * Converts a year to corresponding number of ticks.
183  */
184 int
yeartonum(int y)185 yeartonum(int y)
186 {
187 	int n;
188 
189 	for (n = 0, y -= 1; y > 1969; y--)
190  		n += days_per_year(y) * SECS_PER_DAY;
191 	return n;
192 }
193 
194 /*
195  * Converts tick number to a year 1970 ->
196  */
197 int
numtoyear(int num)198 numtoyear(int num)
199 {
200 	int y = 1970, j;
201 	while(num >= (j = days_per_year(y) * SECS_PER_DAY)) {
202 		y++;
203 		num -= j;
204 	}
205 	return y;
206 }
207 
208 #if VAX750 || VAX780 || VAX8600 || VAX650 || \
209     VAX660 || VAX670 || VAX680 || VAX53 || VAXANY
210 /*
211  * Reads the TODR register; returns a (probably) true tick value, and 0 is
212  * success or EINVAL if failed.  The year is based on the argument
213  * year; the TODR doesn't hold years.
214  */
215 int
generic_gettime(struct timeval * tvp)216 generic_gettime(struct timeval *tvp)
217 {
218 	unsigned klocka = mfpr(PR_TODR);
219 
220 	/*
221 	 * Sanity check.
222 	 */
223 	if (klocka < TODRBASE) {
224 		if (klocka == 0)
225 			printf("TODR stopped");
226 		else
227 			printf("TODR too small");
228 		return EINVAL;
229 	}
230 
231 	tvp->tv_sec = yeartonum(numtoyear(tvp->tv_sec)) + (klocka - TODRBASE) / 100;
232 	return 0;
233 }
234 
235 /*
236  * Takes the current system time and writes it to the TODR.
237  */
238 void
generic_settime(struct timeval * tvp)239 generic_settime(struct timeval *tvp)
240 {
241 	unsigned tid = tvp->tv_sec, bastid;
242 
243 	bastid = tid - yeartonum(numtoyear(tid));
244 	mtpr((bastid * 100) + TODRBASE, PR_TODR);
245 }
246 #endif
247 
248 #if VAX630 || VAX410 || VAX43 || VAX8200 || VAX46 || VAX48 || VAX49 || VAXANY
249 
250 volatile short *clk_page;	/* where the chip is mapped in virtual memory */
251 int	clk_adrshift;	/* how much to multiply the in-page address with */
252 int	clk_tweak;	/* Offset of time into word. */
253 
254 #define	REGPEEK(off)	(clk_page[off << clk_adrshift] >> clk_tweak)
255 #define	REGPOKE(off, v)	(clk_page[off << clk_adrshift] = ((v) << clk_tweak))
256 
257 int
chip_gettime(struct timeval * tvp)258 chip_gettime(struct timeval *tvp)
259 {
260 	struct clock_ymdhms c;
261 	int timeout = 1<<15, s;
262 
263 #ifdef DIAGNOSTIC
264 	if (clk_page == 0)
265 		panic("trying to use unset chip clock page");
266 #endif
267 
268 	if ((REGPEEK(CSRD_OFF) & CSRD_VRT) == 0) {
269 		printf("WARNING: TOY clock not marked valid\n");
270 		return EINVAL;
271 	}
272 	while (REGPEEK(CSRA_OFF) & CSRA_UIP) {
273 		if (--timeout == 0) {
274 			printf ("TOY clock timed out");
275 			return ETIMEDOUT;
276 		}
277 	}
278 
279 	s = splhigh();
280 	c.dt_year = ((u_char)REGPEEK(YR_OFF)) + 1970;
281 	c.dt_mon = REGPEEK(MON_OFF);
282 	c.dt_day = REGPEEK(DAY_OFF);
283 	c.dt_wday = REGPEEK(WDAY_OFF);
284 	c.dt_hour = REGPEEK(HR_OFF);
285 	c.dt_min = REGPEEK(MIN_OFF);
286 	c.dt_sec = REGPEEK(SEC_OFF);
287 	splx(s);
288 
289 	tvp->tv_sec = clock_ymdhms_to_secs(&c);
290 	tvp->tv_usec = 0;
291 	return 0;
292 }
293 
294 void
chip_settime(struct timeval * tvp)295 chip_settime(struct timeval *tvp)
296 {
297 	struct clock_ymdhms c;
298 
299 #ifdef DIAGNOSTIC
300 	if (clk_page == 0)
301 		panic("trying to use unset chip clock page");
302 #endif
303 
304 	REGPOKE(CSRB_OFF, CSRB_SET);
305 
306 	clock_secs_to_ymdhms(tvp->tv_sec, &c);
307 
308 	REGPOKE(YR_OFF, ((u_char)(c.dt_year - 1970)));
309 	REGPOKE(MON_OFF, c.dt_mon);
310 	REGPOKE(DAY_OFF, c.dt_day);
311 	REGPOKE(WDAY_OFF, c.dt_wday);
312 	REGPOKE(HR_OFF, c.dt_hour);
313 	REGPOKE(MIN_OFF, c.dt_min);
314 	REGPOKE(SEC_OFF, c.dt_sec);
315 
316 	REGPOKE(CSRB_OFF, CSRB_DM|CSRB_24);
317 };
318 #endif
319