xref: /netbsd/sys/arch/alpha/alpha/clock.c (revision bf9ec67e)
1 /* $NetBSD: clock.c,v 1.31 2001/05/27 13:53:24 sommerfeld 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.31 2001/05/27 13:53:24 sommerfeld Exp $");
48 
49 #include <sys/param.h>
50 #include <sys/kernel.h>
51 #include <sys/systm.h>
52 #include <sys/device.h>
53 #include <sys/sched.h>
54 
55 #include <dev/clock_subr.h>
56 
57 #include <machine/autoconf.h>
58 #include <machine/cpuconf.h>
59 
60 #include <dev/dec/clockvar.h>
61 
62 #include "opt_clock_compat_osf1.h"
63 #include "opt_ntp.h"
64 
65 #define MINYEAR 1998 /* "today" */
66 #ifdef CLOCK_COMPAT_OSF1
67 /*
68  * According to OSF/1's /usr/sys/include/arch/alpha/clock.h,
69  * the console adjusts the RTC years 13..19 to 93..99 and
70  * 20..40 to 00..20. (historical reasons?)
71  * DEC Unix uses an offset to the year to stay outside
72  * the dangerous area for the next couple of years.
73  */
74 #define UNIX_YEAR_OFFSET 52 /* 41=>1993, 12=>2064 */
75 #else
76 #define UNIX_YEAR_OFFSET 0
77 #endif
78 
79 struct device *clockdev;
80 const struct clockfns *clockfns;
81 int clockinitted;
82 
83 void
84 clockattach(dev, fns)
85 	struct device *dev;
86 	const struct clockfns *fns;
87 {
88 
89 	/*
90 	 * Just bookkeeping.
91 	 */
92 	printf("\n");
93 
94 	if (clockfns != NULL)
95 		panic("clockattach: multiple clocks");
96 	clockdev = dev;
97 	clockfns = fns;
98 }
99 
100 /*
101  * Machine-dependent clock routines.
102  *
103  * Startrtclock restarts the real-time clock, which provides
104  * hardclock interrupts to kern_clock.c.
105  *
106  * Inittodr initializes the time of day hardware which provides
107  * date functions.  Its primary function is to use some file
108  * system information in case the hardare clock lost state.
109  *
110  * Resettodr restores the time of day hardware after a time change.
111  */
112 
113 /*
114  * Start the real-time and statistics clocks. Leave stathz 0 since there
115  * are no other timers available.
116  */
117 void
118 cpu_initclocks()
119 {
120 	if (clockfns == NULL)
121 		panic("cpu_initclocks: no clock attached");
122 
123 	tick = 1000000 / hz;	/* number of microseconds between interrupts */
124 	tickfix = 1000000 - (hz * tick);
125 	if (tickfix) {
126 		int ftp;
127 
128 		ftp = min(ffs(tickfix), ffs(hz));
129 		tickfix >>= (ftp - 1);
130 		tickfixinterval = hz >> (ftp - 1);
131         }
132 
133 	/*
134 	 * Establish the clock interrupt; it's a special case.
135 	 *
136 	 * We establish the clock interrupt this late because if
137 	 * we do it at clock attach time, we may have never been at
138 	 * spl0() since taking over the system.  Some versions of
139 	 * PALcode save a clock interrupt, which would get delivered
140 	 * when we spl0() in autoconf.c.  If established the clock
141 	 * interrupt handler earlier, that interrupt would go to
142 	 * hardclock, which would then fall over because p->p_stats
143 	 * isn't set at that time.
144 	 */
145 	platform.clockintr = hardclock;
146 	schedhz = 16;
147 
148 	/*
149 	 * Get the clock started.
150 	 */
151 	(*clockfns->cf_init)(clockdev);
152 }
153 
154 /*
155  * We assume newhz is either stathz or profhz, and that neither will
156  * change after being set up above.  Could recalculate intervals here
157  * but that would be a drag.
158  */
159 void
160 setstatclockrate(newhz)
161 	int newhz;
162 {
163 
164 	/* nothing we can do */
165 }
166 
167 /*
168  * Initialze the time of day register, based on the time base which is, e.g.
169  * from a filesystem.  Base provides the time to within six months,
170  * and the time of year clock (if any) provides the rest.
171  */
172 void
173 inittodr(base)
174 	time_t base;
175 {
176 	struct clocktime ct;
177 	int year;
178 	struct clock_ymdhms dt;
179 	time_t deltat;
180 	int badbase;
181 
182 	if (base < (MINYEAR-1970)*SECYR) {
183 		printf("WARNING: preposterous time in file system");
184 		/* read the system clock anyway */
185 		base = (MINYEAR-1970)*SECYR;
186 		badbase = 1;
187 	} else
188 		badbase = 0;
189 
190 	(*clockfns->cf_get)(clockdev, base, &ct);
191 #ifdef DEBUG
192 	printf("readclock: %d/%d/%d/%d/%d/%d", ct.year, ct.mon, ct.day,
193 	       ct.hour, ct.min, ct.sec);
194 #endif
195 	clockinitted = 1;
196 
197 	year = 1900 + UNIX_YEAR_OFFSET + ct.year;
198 	if (year < 1970)
199 		year += 100;
200 	/* simple sanity checks (2037 = time_t overflow) */
201 	if (year < MINYEAR || year > 2037 ||
202 	    ct.mon < 1 || ct.mon > 12 || ct.day < 1 ||
203 	    ct.day > 31 || ct.hour > 23 || ct.min > 59 || ct.sec > 59) {
204 		/*
205 		 * Believe the time in the file system for lack of
206 		 * anything better, resetting the TODR.
207 		 */
208 		time.tv_sec = base;
209 		if (!badbase) {
210 			printf("WARNING: preposterous clock chip time\n");
211 			resettodr();
212 		}
213 		goto bad;
214 	}
215 
216 	dt.dt_year = year;
217 	dt.dt_mon = ct.mon;
218 	dt.dt_day = ct.day;
219 	dt.dt_hour = ct.hour;
220 	dt.dt_min = ct.min;
221 	dt.dt_sec = ct.sec;
222 	time.tv_sec = clock_ymdhms_to_secs(&dt);
223 #ifdef DEBUG
224 	printf("=>%ld (%d)\n", time.tv_sec, base);
225 #endif
226 	microset_time = time;
227 	microset(curcpu(), NULL);
228 
229 	if (!badbase) {
230 		/*
231 		 * See if we gained/lost two or more days;
232 		 * if so, assume something is amiss.
233 		 */
234 		deltat = time.tv_sec - base;
235 		if (deltat < 0)
236 			deltat = -deltat;
237 		if (deltat < 2 * SECDAY)
238 			return;
239 		printf("WARNING: clock %s %ld days",
240 		    time.tv_sec < base ? "lost" : "gained",
241 		    (long)deltat / SECDAY);
242 	}
243 bad:
244 	printf(" -- CHECK AND RESET THE DATE!\n");
245 }
246 
247 /*
248  * Reset the TODR based on the time value; used when the TODR
249  * has a preposterous value and also when the time is reset
250  * by the stime system call.  Also called when the TODR goes past
251  * TODRZERO + 100*(SECYEAR+2*SECDAY) (e.g. on Jan 2 just after midnight)
252  * to wrap the TODR around.
253  */
254 void
255 resettodr()
256 {
257 	struct clock_ymdhms dt;
258 	struct clocktime ct;
259 
260 	if (!clockinitted)
261 		return;
262 
263 	microset_time = time;
264 #if defined(MULTIPROCESSOR)
265 	alpha_multicast_ipi(cpus_running, ALPHA_IPI_MICROSET);
266 #endif
267 	microset(curcpu(), NULL);
268 
269 	clock_secs_to_ymdhms(time.tv_sec, &dt);
270 
271 	/* rt clock wants 2 digits */
272 	ct.year = (dt.dt_year - UNIX_YEAR_OFFSET) % 100;
273 	ct.mon = dt.dt_mon;
274 	ct.day = dt.dt_day;
275 	ct.hour = dt.dt_hour;
276 	ct.min = dt.dt_min;
277 	ct.sec = dt.dt_sec;
278 	ct.dow = dt.dt_wday;
279 #ifdef DEBUG
280 	printf("setclock: %d/%d/%d/%d/%d/%d\n", ct.year, ct.mon, ct.day,
281 	       ct.hour, ct.min, ct.sec);
282 #endif
283 
284 	(*clockfns->cf_set)(clockdev, &ct);
285 }
286