clock.c revision 1.8 1 /* $NetBSD: clock.c,v 1.8 1997/02/19 23:38:46 gwr Exp $ */
2
3 /*
4 * Copyright (c) 1994 Gordon W. Ross
5 * Copyright (c) 1993 Adam Glass
6 * Copyright (c) 1988 University of Utah.
7 * Copyright (c) 1982, 1990, 1993
8 * The Regents of the University of California. All rights reserved.
9 *
10 * This code is derived from software contributed to Berkeley by
11 * the Systems Programming Group of the University of Utah Computer
12 * Science Department.
13 *
14 * Redistribution and use in source and binary forms, with or without
15 * modification, are permitted provided that the following conditions
16 * are met:
17 * 1. Redistributions of source code must retain the above copyright
18 * notice, this list of conditions and the following disclaimer.
19 * 2. Redistributions in binary form must reproduce the above copyright
20 * notice, this list of conditions and the following disclaimer in the
21 * documentation and/or other materials provided with the distribution.
22 * 3. All advertising materials mentioning features or use of this software
23 * must display the following acknowledgement:
24 * This product includes software developed by the University of
25 * California, Berkeley and its contributors.
26 * 4. Neither the name of the University nor the names of its contributors
27 * may be used to endorse or promote products derived from this software
28 * without specific prior written permission.
29 *
30 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
31 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
32 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
34 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
35 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
36 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
37 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
38 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
39 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
40 * SUCH DAMAGE.
41 *
42 * from: Utah Hdr: clock.c 1.18 91/01/21$
43 * from: @(#)clock.c 8.2 (Berkeley) 1/12/94
44 */
45
46 /*
47 * Machine-dependent clock routines for the Mostek48t02
48 */
49
50 #include <sys/param.h>
51 #include <sys/systm.h>
52 #include <sys/time.h>
53 #include <sys/kernel.h>
54 #include <sys/device.h>
55
56 #include <machine/autoconf.h>
57 #include <machine/cpu.h>
58 #include <machine/mon.h>
59 #include <machine/obio.h>
60 #include <machine/machdep.h>
61
62 #include <dev/clock_subr.h>
63
64 #include <sun3/sun3/interreg.h>
65 #include "mostek48t02.h"
66
67 #define CLOCK_PRI 5
68
69 void _isr_clock __P((void)); /* in locore.s */
70 void clock_intr __P((struct clockframe));
71
72 /* Note: this is used by locore.s:__isr_clock */
73 static volatile void *clock_va;
74
75 static int clock_match __P((struct device *, struct cfdata *, void *args));
76 static void clock_attach __P((struct device *, struct device *, void *));
77
78 struct cfattach clock_ca = {
79 sizeof(struct device), clock_match, clock_attach
80 };
81
82 struct cfdriver clock_cd = {
83 NULL, "clock", DV_DULL
84 };
85
86 /*
87 * XXX Need to determine which type of clock we have!
88 * XXX The Sun3/80 always has the MK4802, while the
89 * XXX Sun3/470 can (reportedly) have that or the old
90 * XXX intersil7170. Should have two clock drivers...
91 */
92 static int
93 clock_match(parent, cf, args)
94 struct device *parent;
95 struct cfdata *cf;
96 void *args;
97 {
98 struct confargs *ca = args;
99
100 /* This driver only supports one unit. */
101 if (cf->cf_unit != 0)
102 return (0);
103
104 /* Validate the given address. */
105 if (ca->ca_paddr != OBIO_CLOCK2)
106 return (0);
107
108 /* Default interrupt priority. */
109 if (ca->ca_intpri == -1)
110 ca->ca_intpri = CLOCK_PRI;
111
112 return (1);
113 }
114
115 static void
116 clock_attach(parent, self, args)
117 struct device *parent;
118 struct device *self;
119 void *args;
120 {
121
122 printf("\n");
123
124 /*
125 * Can not hook up the ISR until cpu_initclocks()
126 * because hardclock is not ready until then.
127 * For now, the handler is _isr_autovec(), which
128 * will complain if it gets clock interrupts.
129 */
130 }
131
132 /*
133 * Set and/or clear the desired clock bits in the interrupt
134 * register. We have to be extremely careful that we do it
135 * in such a manner that we don't get ourselves lost.
136 */
137 void
138 set_clk_mode(on, off, enable)
139 u_char on, off;
140 int enable;
141 {
142 register u_char interreg;
143 register int s;
144
145 /* If we don't have this, we must not have touched it! */
146 if (!interrupt_reg)
147 return;
148
149 s = getsr();
150 if ((s & PSL_IPL) < PSL_IPL7)
151 panic("set_clk_mode: ipl");
152
153 /*
154 * make sure that we are only playing w/
155 * clock interrupt register bits
156 */
157 on &= (IREG_CLOCK_ENAB_7 | IREG_CLOCK_ENAB_5);
158 off &= (IREG_CLOCK_ENAB_7 | IREG_CLOCK_ENAB_5);
159
160 /*
161 * Get a copy of current interrupt register,
162 * turning off any undesired bits (aka `off')
163 */
164 interreg = *interrupt_reg & ~(off | IREG_ALL_ENAB);
165 *interrupt_reg &= ~IREG_ALL_ENAB;
166
167 /*
168 * Next we turns off the CLK5 and CLK7 bits to clear
169 * the flip-flops, then we disable clock interrupts.
170 * Now we can read the clock's interrupt register
171 * to clear any pending signals there.
172 */
173 *interrupt_reg &= ~(IREG_CLOCK_ENAB_7 | IREG_CLOCK_ENAB_5);
174
175 /* XXX - hit the clock? */
176
177 /*
178 * Now we set all the desired bits
179 * in the interrupt register, then
180 * we turn the clock back on and
181 * finally we can enable all interrupts.
182 */
183 *interrupt_reg |= (interreg | on); /* enable flip-flops */
184
185 /* XXX - hit the clock? */
186
187 *interrupt_reg |= IREG_ALL_ENAB; /* enable interrupts */
188 }
189
190 /* Called very early by internal_configure. */
191 void clock_init()
192 {
193 /* XXX - Yes, use the EEPROM address. Same H/W device. */
194 clock_va = obio_find_mapping(OBIO_EEPROM, sizeof(struct clockreg));
195
196 if (!clock_va || !interrupt_reg) {
197 mon_printf("clock_init\n");
198 sunmon_abort();
199 }
200
201 /* Turn off clock interrupts until cpu_initclocks() */
202 /* intreg_init() already cleared the interrupt register. */
203 }
204
205 /*
206 * Set up the real-time clock (enable clock interrupts).
207 * Leave stathz 0 since there is no secondary clock available.
208 * Note that clock interrupts MUST STAY DISABLED until here.
209 */
210 void
211 cpu_initclocks(void)
212 {
213 int s;
214
215 if (!clock_va)
216 panic("cpu_initclocks");
217 s = splhigh();
218
219 /* Install isr (in locore.s) that calls clock_intr(). */
220 isr_add_custom(5, (void*)_isr_clock);
221
222 /* Set the clock to interrupt 100 time per second. */
223 /* XXX - Hard wired? */
224
225 *interrupt_reg |= IREG_CLOCK_ENAB_5; /* enable clock */
226
227 /* XXX enable the clock? */
228
229 *interrupt_reg |= IREG_ALL_ENAB; /* enable interrupts */
230 splx(s);
231 }
232
233 /*
234 * This doesn't need to do anything, as we have only one timer and
235 * profhz==stathz==hz.
236 */
237 void
238 setstatclockrate(newhz)
239 int newhz;
240 {
241 /* nothing */
242 }
243
244 /*
245 * This is is called by the "custom" interrupt handler.
246 */
247 void
248 clock_intr(cf)
249 struct clockframe cf;
250 {
251 /* volatile struct clockreg *clk = clock_va; */
252
253 #if 1 /* XXX - Needed? */
254 /* Pulse the clock intr. enable low. */
255 *interrupt_reg &= ~IREG_CLOCK_ENAB_5;
256 *interrupt_reg |= IREG_CLOCK_ENAB_5;
257 #endif
258
259 hardclock(&cf);
260 }
261
262 /*
263 * Return the best possible estimate of the time in the timeval
264 * to which tvp points. We do this by returning the current time
265 * plus the amount of time since the last clock interrupt.
266 *
267 * Check that this time is no less than any previously-reported time,
268 * which could happen around the time of a clock adjustment. Just for
269 * fun, we guarantee that the time will be greater than the value
270 * obtained by a previous call.
271 */
272 void
273 microtime(tvp)
274 register struct timeval *tvp;
275 {
276 int s = splhigh();
277 static struct timeval lasttime;
278
279 *tvp = time;
280 tvp->tv_usec++; /* XXX */
281 while (tvp->tv_usec > 1000000) {
282 tvp->tv_sec++;
283 tvp->tv_usec -= 1000000;
284 }
285 if (tvp->tv_sec == lasttime.tv_sec &&
286 tvp->tv_usec <= lasttime.tv_usec &&
287 (tvp->tv_usec = lasttime.tv_usec + 1) > 1000000)
288 {
289 tvp->tv_sec++;
290 tvp->tv_usec -= 1000000;
291 }
292 lasttime = *tvp;
293 splx(s);
294 }
295
296
297 /*
298 * Machine-dependent clock routines.
299 *
300 * Inittodr initializes the time of day hardware which provides
301 * date functions.
302 *
303 * Resettodr restores the time of day hardware after a time change.
304 */
305
306 static long clk_get_secs(void);
307 static void clk_set_secs(long);
308
309 /*
310 * Initialize the time of day register, based on the time base
311 * which is, e.g. from a filesystem.
312 */
313 void inittodr(fs_time)
314 time_t fs_time;
315 {
316 long diff, clk_time;
317 long long_ago = (5 * SECYR);
318 int clk_bad = 0;
319
320 /*
321 * Sanity check time from file system.
322 * If it is zero,assume filesystem time is just unknown
323 * instead of preposterous. Don't bark.
324 */
325 if (fs_time < long_ago) {
326 /*
327 * If fs_time is zero, assume filesystem time is just
328 * unknown instead of preposterous. Don't bark.
329 */
330 if (fs_time != 0)
331 printf("WARNING: preposterous time in file system\n");
332 /* 1991/07/01 12:00:00 */
333 fs_time = 21*SECYR + 186*SECDAY + SECDAY/2;
334 }
335
336 clk_time = clk_get_secs();
337
338 /* Sanity check time from clock. */
339 if (clk_time < long_ago) {
340 printf("WARNING: bad date in battery clock");
341 clk_bad = 1;
342 clk_time = fs_time;
343 } else {
344 /* Does the clock time jive with the file system? */
345 diff = clk_time - fs_time;
346 if (diff < 0)
347 diff = -diff;
348 if (diff >= (SECDAY*2)) {
349 printf("WARNING: clock %s %d days",
350 (clk_time < fs_time) ? "lost" : "gained",
351 (int) (diff / SECDAY));
352 clk_bad = 1;
353 }
354 }
355 if (clk_bad)
356 printf(" -- CHECK AND RESET THE DATE!\n");
357 time.tv_sec = clk_time;
358 }
359
360 /*
361 * Resettodr restores the time of day hardware after a time change.
362 */
363 void resettodr()
364 {
365 clk_set_secs(time.tv_sec);
366 }
367
368
369 /*
370 * Routines to copy state into and out of the clock.
371 * The clock CSR has to be set for read or write.
372 */
373 static void
374 clk_get_dt(struct clock_ymdhms *dt)
375 {
376 volatile struct clockreg *cl = clock_va;
377 int s;
378
379 s = splhigh();
380
381 /* enable read (stop time) */
382 cl->cl_csr |= CLK_READ;
383
384 /* Copy the info */
385 dt->dt_sec = cl->cl_sec;
386 dt->dt_min = cl->cl_min;
387 dt->dt_hour = cl->cl_hour;
388 dt->dt_wday = cl->cl_wday;
389 dt->dt_day = cl->cl_mday;
390 dt->dt_mon = cl->cl_month;
391 dt->dt_year = cl->cl_year;
392
393 /* Done reading (time wears on) */
394 cl->cl_csr &= ~CLK_READ;
395 splx(s);
396 }
397
398 static void
399 clk_set_dt(struct clock_ymdhms *dt)
400 {
401 volatile struct clockreg *cl = clock_va;
402 int s;
403
404 s = splhigh();
405 /* enable write */
406 cl->cl_csr |= CLK_WRITE;
407
408 /* Copy the info */
409 cl->cl_sec = dt->dt_sec;
410 cl->cl_min = dt->dt_min;
411 cl->cl_hour = dt->dt_hour;
412 cl->cl_wday = dt->dt_wday;
413 cl->cl_mday = dt->dt_day;
414 cl->cl_month = dt->dt_mon;
415 cl->cl_year = dt->dt_year;
416
417 /* load them up */
418 cl->cl_csr &= ~CLK_WRITE;
419 splx(s);
420 }
421
422
423 /*
424 * Now routines to get and set clock as POSIX time.
425 * Our clock keeps "years since 1/1/1968".
426 */
427 #define CLOCK_BASE_YEAR 1968
428
429 static long
430 clk_get_secs()
431 {
432 struct clock_ymdhms dt;
433 long secs;
434
435 clk_get_dt(&dt);
436
437 /* Convert BCD values to binary. */
438 dt.dt_sec = FROMBCD(dt.dt_sec);
439 dt.dt_min = FROMBCD(dt.dt_min);
440 dt.dt_hour = FROMBCD(dt.dt_hour);
441 dt.dt_day = FROMBCD(dt.dt_day);
442 dt.dt_mon = FROMBCD(dt.dt_mon);
443 dt.dt_year = FROMBCD(dt.dt_year);
444
445 if ((dt.dt_hour > 24) ||
446 (dt.dt_day > 31) ||
447 (dt.dt_mon > 12))
448 return (0);
449
450 dt.dt_year += CLOCK_BASE_YEAR;
451 secs = clock_ymdhms_to_secs(&dt);
452 return (secs);
453 }
454
455 static void
456 clk_set_secs(secs)
457 long secs;
458 {
459 struct clock_ymdhms dt;
460
461 clock_secs_to_ymdhms(secs, &dt);
462 dt.dt_year -= CLOCK_BASE_YEAR;
463
464 /* Convert binary values to BCD. */
465 dt.dt_sec = TOBCD(dt.dt_sec);
466 dt.dt_min = TOBCD(dt.dt_min);
467 dt.dt_hour = TOBCD(dt.dt_hour);
468 dt.dt_day = TOBCD(dt.dt_day);
469 dt.dt_mon = TOBCD(dt.dt_mon);
470 dt.dt_year = TOBCD(dt.dt_year);
471
472 clk_set_dt(&dt);
473 }
474