xref: /netbsd/sys/arch/pmax/pmax/clock.c (revision bf9ec67e)
1 /* $NetBSD: clock.c,v 1.32 2001/11/23 01:04:11 simonb Exp $ */
2 
3 /*
4  * Copyright (c) 1988 University of Utah.
5  * Copyright (c) 1992, 1993
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * This code is derived from software contributed to Berkeley by
9  * the Systems Programming Group of the University of Utah Computer
10  * Science Department and Ralph Campbell.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  * 3. All advertising materials mentioning features or use of this software
21  *    must display the following acknowledgement:
22  *	This product includes software developed by the University of
23  *	California, Berkeley and its contributors.
24  * 4. Neither the name of the University nor the names of its contributors
25  *    may be used to endorse or promote products derived from this software
26  *    without specific prior written permission.
27  *
28  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
29  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
32  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38  * SUCH DAMAGE.
39  *
40  * from: Utah Hdr: clock.c 1.18 91/01/21
41  *
42  *	@(#)clock.c	8.1 (Berkeley) 6/10/93
43  */
44 
45 #include <sys/cdefs.h>			/* RCS ID & Copyright macro defns */
46 
47 __KERNEL_RCSID(0, "$NetBSD: clock.c,v 1.32 2001/11/23 01:04:11 simonb Exp $");
48 
49 #include <sys/param.h>
50 #include <sys/kernel.h>
51 #include <sys/systm.h>
52 
53 #include <dev/clock_subr.h>
54 
55 #include <dev/dec/clockvar.h>
56 
57 #include "opt_ntp.h"
58 
59 #define MINYEAR 1998 /* "today" */
60 
61 struct device *clockdev;
62 const struct clockfns *clockfns;
63 int clockinitted;
64 
65 #ifdef NTP
66 extern int fixtick;		/* XXX */
67 #endif
68 
69 void
70 clockattach(dev, fns)
71 	struct device *dev;
72 	const struct clockfns *fns;
73 {
74 
75 	/*
76 	 * Just bookkeeping.
77 	 */
78 	printf("\n");
79 
80 	if (clockfns != NULL)
81 		panic("clockattach: multiple clocks");
82 	clockdev = dev;
83 	clockfns = fns;
84 #ifdef EVCNT_COUNTERS
85 	evcnt_attach_dynamic(&clock_intr_evcnt, EVCNT_TYPE_INTR, NULL,
86 	    dev->dv_xname, "intr");
87 #endif
88 }
89 
90 /*
91  * Machine-dependent clock routines.
92  *
93  * Startrtclock restarts the real-time clock, which provides
94  * hardclock interrupts to kern_clock.c.
95  *
96  * Inittodr initializes the time of day hardware which provides
97  * date functions.  Its primary function is to use some file
98  * system information in case the hardare clock lost state.
99  *
100  * Resettodr restores the time of day hardware after a time change.
101  */
102 
103 /*
104  * Start the real-time and statistics clocks. Leave stathz 0 since there
105  * are no other timers available.
106  */
107 void
108 cpu_initclocks()
109 {
110 
111 	if (clockfns == NULL)
112 		panic("cpu_initclocks: no clock attached");
113 
114 	/*
115 	 * Establish the clock interrupt; it's a special case.
116 	 *
117 	 * We establish the clock interrupt this late because if
118 	 * we do it at clock attach time, we may have never been at
119 	 * spl0() since taking over the system.  Some versions of
120 	 * PALcode save a clock interrupt, which would get delivered
121 	 * when we spl0() in autoconf.c.  If established the clock
122 	 * interrupt handler earlier, that interrupt would go to
123 	 * hardclock, which would then fall over because p->p_stats
124 	 * isn't set at that time.
125 	 */
126 
127 	/*
128 	 * Get the clock started.
129 	 */
130 	(*clockfns->cf_init)(clockdev);
131 
132 	/*
133 	 * Set hz-related variables after the clock is initialised in
134 	 * case the initialisation routines adjusted hz.
135 	 */
136 	tick = 1000000 / hz;	/* number of microseconds between interrupts */
137 	tickfix = 1000000 - (hz * tick);
138 #ifdef NTP
139 	fixtick = tickfix;
140 #endif
141 	if (tickfix) {
142 		int ftp;
143 
144 		ftp = min(ffs(tickfix), ffs(hz));
145 		tickfix >>= (ftp - 1);
146 		tickfixinterval = hz >> (ftp - 1);
147         }
148 }
149 
150 /*
151  * We assume newhz is either stathz or profhz, and that neither will
152  * change after being set up above.  Could recalculate intervals here
153  * but that would be a drag.
154  */
155 void
156 setstatclockrate(newhz)
157 	int newhz;
158 {
159 
160 	/* nothing we can do */
161 }
162 
163 /*
164  * Experiments (and  passing years) show that Decstation PROMS
165  * assume the kernel uses the clock chip as a time-of-year clock.
166  * The PROM assumes the clock is always set to 1972 or 1973, and contains
167  * time-of-year in seconds.   The PROM checks the clock at boot time,
168  * and if it's outside that range, sets it to 1972-01-01.
169  *
170  * XXX should be at the mc146818 layer?
171 */
172 
173 /*
174  * Initialze the time of day register, based on the time base which is, e.g.
175  * from a filesystem.  Base provides the time to within six months,
176  * and the time of year clock (if any) provides the rest.
177  */
178 void
179 inittodr(base)
180 	time_t base;
181 {
182 	struct clocktime ct;
183 	struct clock_ymdhms dt;
184 	time_t yearsecs;
185 	time_t deltat;
186 	int badbase;
187 
188 	if (base < (MINYEAR-1970)*SECYR) {
189 		printf("WARNING: preposterous time in file system");
190 		/* read the system clock anyway */
191 		base = (MINYEAR-1970)*SECYR;
192 		badbase = 1;
193 	} else
194 		badbase = 0;
195 
196 	(*clockfns->cf_get)(clockdev, base, &ct);
197 #ifdef DEBUG
198 	printf("readclock: %d/%d/%d/%d/%d/%d", ct.year, ct.mon, ct.day,
199 	       ct.hour, ct.min, ct.sec);
200 #endif
201 	clockinitted = 1;
202 
203 	/* simple sanity checks */
204 	if (ct.year < 70 || ct.mon < 1 || ct.mon > 12 || ct.day < 1 ||
205 	    ct.day > 31 || ct.hour > 23 || ct.min > 59 || ct.sec > 59) {
206 		/*
207 		 * Believe the time in the file system for lack of
208 		 * anything better, resetting the TODR.
209 		 */
210 		time.tv_sec = base;
211 		if (!badbase) {
212 			printf("WARNING: preposterous clock chip time\n");
213 			resettodr();
214 		}
215 		goto bad;
216 	}
217 
218 	/*
219 	 * The clock lives in 1972 (leapyear!);
220 	 * calculate seconds relative to this year.
221 	 */
222 	dt.dt_year = 1972;
223 	dt.dt_mon = ct.mon;
224 	dt.dt_day = ct.day;
225 	dt.dt_hour = ct.hour;
226 	dt.dt_min = ct.min;
227 	dt.dt_sec = ct.sec;
228 	yearsecs = clock_ymdhms_to_secs(&dt) - (72 - 70) * SECYR;
229 
230 	/*
231 	 * Take the actual year from the filesystem if possible;
232 	 * allow for 2 days of clock loss and 363 days of clock gain.
233 	 */
234 	dt.dt_year = 1972; /* or MINYEAR or base/SECYR+1970 ... */
235 	dt.dt_mon = 1;
236 	dt.dt_day = 1;
237 	dt.dt_hour = 0;
238 	dt.dt_min = 0;
239 	dt.dt_sec = 0;
240 	for(;;) {
241 		time.tv_sec = yearsecs + clock_ymdhms_to_secs(&dt);
242 		if (badbase || (time.tv_sec > base - 2 * SECDAY))
243 			break;
244 		dt.dt_year++;
245 	}
246 #ifdef DEBUG
247 	printf("=>%ld (%ld)\n", time.tv_sec, base);
248 #endif
249 
250 	if (!badbase) {
251 		/*
252 		 * See if we gained/lost two or more days;
253 		 * if so, assume something is amiss.
254 		 */
255 		deltat = time.tv_sec - base;
256 		if (deltat < 0)
257 			deltat = -deltat;
258 		if (deltat < 2 * SECDAY)
259 			return;
260 		printf("WARNING: clock %s %d days",
261 		    time.tv_sec < base ? "lost" : "gained",
262 		       (int) (deltat / SECDAY));
263 	}
264 bad:
265 	printf(" -- CHECK AND RESET THE DATE!\n");
266 }
267 
268 /*
269  * Reset the TODR based on the time value; used when the TODR
270  * has a preposterous value and also when the time is reset
271  * by the stime system call.  Also called when the TODR goes past
272  * TODRZERO + 100*(SECYEAR+2*SECDAY) (e.g. on Jan 2 just after midnight)
273  * to wrap the TODR around.
274  */
275 void
276 resettodr()
277 {
278 	time_t yearsecs;
279 	struct clock_ymdhms dt;
280 	struct clocktime ct;
281 
282 	if (!clockinitted)
283 		return;
284 
285 	/*
286 	 * calculate seconds relative to this year
287 	 */
288 	clock_secs_to_ymdhms(time.tv_sec, &dt); /* get the year */
289 	dt.dt_mon = 1;
290 	dt.dt_day = 1;
291 	dt.dt_hour = 0;
292 	dt.dt_min = 0;
293 	dt.dt_sec = 0;
294 	yearsecs = time.tv_sec - clock_ymdhms_to_secs(&dt);
295 
296 	/*
297 	 * The clock lives in 1972 (leapyear!); calc fictious date.
298 	 */
299 #define first72 ((72 - 70) * SECYR)
300 	clock_secs_to_ymdhms(first72 + yearsecs, &dt);
301 
302 #ifdef DEBUG
303 	if (dt.dt_year != 1972)
304 		printf("resettodr: botch (%ld, %ld)\n", yearsecs, time.tv_sec);
305 #endif
306 	ct.year = dt.dt_year % 100; /* rt clock wants 2 digits */
307 	ct.mon = dt.dt_mon;
308 	ct.day = dt.dt_day;
309 	ct.hour = dt.dt_hour;
310 	ct.min = dt.dt_min;
311 	ct.sec = dt.dt_sec;
312 	ct.dow = dt.dt_wday;
313 #ifdef DEBUG
314 	printf("setclock: %d/%d/%d/%d/%d/%d\n", ct.year, ct.mon, ct.day,
315 	       ct.hour, ct.min, ct.sec);
316 #endif
317 
318 	(*clockfns->cf_set)(clockdev, &ct);
319 }
320