xref: /netbsd/sys/arch/mvme68k/mvme68k/clock.c (revision bf9ec67e)
1 /*      $NetBSD: clock.c,v 1.17 2002/02/12 20:38:36 scw Exp $	*/
2 
3 /*
4  * Copyright (c) 1992, 1993
5  *      The Regents of the University of California.  All rights reserved.
6  *
7  * This software was developed by the Computer Systems Engineering group
8  * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
9  * contributed to Berkeley.
10  *
11  * All advertising materials mentioning features or use of this software
12  * must display the following acknowledgement:
13  *      This product includes software developed by the University of
14  *      California, Lawrence Berkeley Laboratory.
15  *
16  * Redistribution and use in source and binary forms, with or without
17  * modification, are permitted provided that the following conditions
18  * are met:
19  * 1. Redistributions of source code must retain the above copyright
20  *    notice, this list of conditions and the following disclaimer.
21  * 2. Redistributions in binary form must reproduce the above copyright
22  *    notice, this list of conditions and the following disclaimer in the
23  *    documentation and/or other materials provided with the distribution.
24  * 3. All advertising materials mentioning features or use of this software
25  *    must display the following acknowledgement:
26  *      This product includes software developed by the University of
27  *      California, Berkeley and its contributors.
28  * 4. Neither the name of the University nor the names of its contributors
29  *    may be used to endorse or promote products derived from this software
30  *    without specific prior written permission.
31  *
32  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
33  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
34  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
35  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
36  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
37  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
38  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
39  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
40  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
41  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
42  * SUCH DAMAGE.
43  *
44  *      @(#)clock.c     8.1 (Berkeley) 6/11/93
45  */
46 
47 #include <sys/param.h>
48 #include <sys/kernel.h>
49 #include <sys/systm.h>
50 #include <sys/device.h>
51 
52 #include <machine/psl.h>
53 #include <machine/bus.h>
54 
55 #include <dev/mvme/clockvar.h>
56 
57 static	struct clock_attach_args *clock_args;
58 static	todr_chip_handle_t todr_handle;
59 
60 struct	evcnt clock_profcnt;
61 struct	evcnt clock_statcnt;
62 
63 /*
64  * Statistics clock interval and variance, in usec.  Variance must be a
65  * power of two.  Since this gives us an even number, not an odd number,
66  * we discard one case and compensate.  That is, a variance of 1024 would
67  * give us offsets in [0..1023].  Instead, we take offsets in [1..1023].
68  * This is symmetric about the point 512, or statvar/2, and thus averages
69  * to that value (assuming uniform random numbers).
70  */
71 /* XXX fix comment to match value */
72 int	clock_statvar = 8192;
73 int	clock_statmin;		/* statclock interval - (1/2 * variance) */
74 
75 
76 /*
77  * Common parts of clock autoconfiguration.
78  */
79 void
80 clock_config(dev, ca, ev)
81 	struct device *dev;
82 	struct clock_attach_args *ca;
83 	struct evcnt *ev;
84 {
85 	extern int delay_divisor;	/* from machdep.c */
86 
87 	/* Hook up that which we need. */
88 	clock_args = ca;
89 
90 	evcnt_attach_dynamic(&clock_profcnt, EVCNT_TYPE_INTR, ev,
91 	    dev->dv_xname, "profint");
92 	evcnt_attach_dynamic(&clock_statcnt, EVCNT_TYPE_INTR, ev,
93 	    dev->dv_xname, "statint");
94 
95 	/* Print info about the clock. */
96 	printf(": delay_divisor %d\n", delay_divisor);
97 }
98 
99 void
100 clock_rtc_config(todr)
101 	todr_chip_handle_t todr;
102 {
103 
104 	if (todr_handle)
105 		panic("clock_config: clock already configured");
106 
107 	todr_handle = todr;
108 }
109 
110 /*
111  * Set up the real-time and statistics clocks.  Leave stathz 0 only
112  * if no alternative timer is available.
113  *
114  * The frequencies of these clocks must be an even number of microseconds.
115  */
116 void
117 cpu_initclocks()
118 {
119 	int statint, minint;
120 
121 	if (clock_args == NULL)
122 		panic("clock not configured");
123 
124 	if (1000000 % hz) {
125 		printf("cannot get %d Hz clock; using 100 Hz\n", hz);
126 		hz = 100;
127 		tick = 1000000 / hz;
128 	}
129 	if (stathz == 0)
130 		stathz = hz;
131 	if (1000000 % stathz) {
132 		printf("cannot get %d Hz statclock; using 100 Hz\n", stathz);
133 		stathz = 100;
134 	}
135 	profhz = stathz;	/* always */
136 
137 	statint = 1000000 / stathz;
138 	minint = statint / 2 + 100;
139 	while (clock_statvar > minint)
140 		clock_statvar >>= 1;
141 
142 	clock_statmin = statint - (clock_statvar >> 1);
143 
144 	/* Call the machine-specific initclocks hook. */
145 	(*clock_args->ca_initfunc)(clock_args->ca_arg, tick, statint);
146 }
147 
148 void
149 setstatclockrate(newhz)
150 	int newhz;
151 {
152 
153 	/* XXX should we do something here? XXX */
154 }
155 
156 /*
157  * Return the best possible estimate of the time in the timeval
158  * to which tvp points.  We do this by returning the current time
159  * plus the amount of time, in uSec, since the last clock interrupt
160  * (clock_args->ca_microtime()) was handled.
161  *
162  * Check that this time is no less than any previously-reported time,
163  * which could happen around the time of a clock adjustment.  Just for fun,
164  * we guarantee that the time will be greater than the value obtained by a
165  * previous call.
166  */
167 
168 void
169 microtime(tvp)
170 	struct timeval *tvp;
171 {
172 	int s = splhigh();
173 	static struct timeval lasttime;
174 
175 	*tvp = time;
176 	tvp->tv_usec += (*clock_args->ca_microtime)(clock_args->ca_arg);
177 	while (tvp->tv_usec >= 1000000) {
178 		tvp->tv_sec++;
179 		tvp->tv_usec -= 1000000;
180 	}
181 	if (tvp->tv_sec == lasttime.tv_sec &&
182 	    tvp->tv_usec <= lasttime.tv_usec &&
183 	    (tvp->tv_usec = lasttime.tv_usec + 1) >= 1000000) {
184 		tvp->tv_sec++;
185 		tvp->tv_usec -= 1000000;
186 	}
187 	lasttime = *tvp;
188 	splx(s);
189 }
190 
191 /*
192  * Set up the system's time, given a `reasonable' time value.
193  */
194 void
195 inittodr(base)
196         time_t base;
197 {
198         int badbase = 0, waszero = (base == 0);
199 
200 	if (todr_handle == NULL)
201 		panic("todr not configured");
202 
203         if (base < 5 * SECYR) {
204                 /*
205                  * If base is 0, assume filesystem time is just unknown
206                  * in stead of preposterous. Don't bark.
207                  */
208                 if (base != 0)
209                         printf("WARNING: preposterous time in file system\n");
210                 /* not going to use it anyway, if the chip is readable */
211                 base = 21*SECYR + 186*SECDAY + SECDAY/2;
212                 badbase = 1;
213         }
214 
215         if (todr_gettime(todr_handle, (struct timeval *)&time) != 0 ||
216             time.tv_sec == 0) {
217                 printf("WARNING: bad date in battery clock");
218                 /*
219                  * Believe the time in the file system for lack of
220                  * anything better, resetting the clock.
221                  */
222                 time.tv_sec = base;
223                 if (!badbase)
224                         resettodr();
225         } else {
226                 int deltat = time.tv_sec - base;
227 
228                 if (deltat < 0)
229                         deltat = -deltat;
230                 if (waszero || deltat < 2 * SECDAY)
231                         return;
232                 printf("WARNING: clock %s %d days",
233                     time.tv_sec < base ? "lost" : "gained", deltat / SECDAY);
234         }
235         printf(" -- CHECK AND RESET THE DATE!\n");
236 }
237 
238 
239 /*
240  * Reset the clock based on the current time.
241  * Used when the current clock is preposterous, when the time is changed,
242  * and when rebooting.  Do nothing if the time is not yet known, e.g.,
243  * when crashing during autoconfig.
244  */
245 void
246 resettodr()
247 {
248 
249         if (!time.tv_sec)
250                 return;
251 
252         if (todr_settime(todr_handle, (struct timeval *)&time) != 0)
253                 printf("resettodr: failed to set time\n");
254 }
255