clock.c revision 1.3 1 /* $NetBSD: clock.c,v 1.3 1997/01/23 22:30:15 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 <sun3/sun3/interreg.h>
63 #include "mostek48t02.h"
64
65 #define CLOCK_PRI 5
66
67 void _isr_clock __P((void)); /* in locore.s */
68 void clock_intr __P((struct clockframe));
69
70 /* Note: this is used by locore.s:__isr_clock */
71 static volatile void *clock_va;
72
73 static int clock_match __P((struct device *, struct cfdata *, void *args));
74 static void clock_attach __P((struct device *, struct device *, void *));
75
76 struct cfattach clock_ca = {
77 sizeof(struct device), clock_match, clock_attach
78 };
79
80 struct cfdriver clock_cd = {
81 NULL, "clock", DV_DULL
82 };
83
84 /*
85 * XXX - Need to determine which type of clock we have!
86 */
87 static int
88 clock_match(parent, cf, args)
89 struct device *parent;
90 struct cfdata *cf;
91 void *args;
92 {
93 struct confargs *ca = args;
94
95 /* This driver only supports one unit. */
96 if (cf->cf_unit != 0)
97 return (0);
98
99 /* Validate the given address. */
100 if (ca->ca_paddr != OBIO_CLOCK2)
101 return (0);
102
103 /* Default interrupt priority. */
104 if (ca->ca_intpri == -1)
105 ca->ca_intpri = CLOCK_PRI;
106
107 return (1);
108 }
109
110 static void
111 clock_attach(parent, self, args)
112 struct device *parent;
113 struct device *self;
114 void *args;
115 {
116
117 printf("\n");
118
119 /*
120 * Can not hook up the ISR until cpu_initclocks()
121 * because hardclock is not ready until then.
122 * For now, the handler is _isr_autovec(), which
123 * will complain if it gets clock interrupts.
124 */
125 }
126
127 /*
128 * Set and/or clear the desired clock bits in the interrupt
129 * register. We have to be extremely careful that we do it
130 * in such a manner that we don't get ourselves lost.
131 */
132 void
133 set_clk_mode(on, off, enable)
134 u_char on, off;
135 int enable;
136 {
137 register u_char interreg;
138 register int s;
139
140 s = getsr();
141 if ((s & PSL_IPL) < PSL_IPL7)
142 panic("set_clk_mode: ipl");
143
144 if (!clock_va)
145 panic("set_clk_mode: map");
146
147 /*
148 * make sure that we are only playing w/
149 * clock interrupt register bits
150 */
151 on &= (IREG_CLOCK_ENAB_7 | IREG_CLOCK_ENAB_5);
152 off &= (IREG_CLOCK_ENAB_7 | IREG_CLOCK_ENAB_5);
153
154 /*
155 * Get a copy of current interrupt register,
156 * turning off any undesired bits (aka `off')
157 */
158 interreg = *interrupt_reg & ~(off | IREG_ALL_ENAB);
159 *interrupt_reg &= ~IREG_ALL_ENAB;
160
161 /*
162 * Next we turns off the CLK5 and CLK7 bits to clear
163 * the flip-flops, then we disable clock interrupts.
164 * Now we can read the clock's interrupt register
165 * to clear any pending signals there.
166 */
167 *interrupt_reg &= ~(IREG_CLOCK_ENAB_7 | IREG_CLOCK_ENAB_5);
168
169 /* XXX - hit the clock? */
170
171 /*
172 * Now we set all the desired bits
173 * in the interrupt register, then
174 * we turn the clock back on and
175 * finally we can enable all interrupts.
176 */
177 *interrupt_reg |= (interreg | on); /* enable flip-flops */
178
179 /* XXX - hit the clock? */
180
181 *interrupt_reg |= IREG_ALL_ENAB; /* enable interrupts */
182 }
183
184 /* Called very early by internal_configure. */
185 void clock_init()
186 {
187 /* XXX - Yes, use the EEPROM address. Same H/W device. */
188 clock_va = obio_find_mapping(OBIO_EEPROM, sizeof(struct clockreg));
189
190 if (!clock_va)
191 mon_panic("clock_init: clock_va\n");
192 if (!interrupt_reg)
193 mon_panic("clock_init: interrupt_reg\n");
194
195 /* Turn off clock interrupts until cpu_initclocks() */
196 /* isr_init() already set the interrupt reg to zero. */
197 }
198
199 /*
200 * Set up the real-time clock (enable clock interrupts).
201 * Leave stathz 0 since there is no secondary clock available.
202 * Note that clock interrupts MUST STAY DISABLED until here.
203 */
204 void
205 cpu_initclocks(void)
206 {
207 int s;
208
209 if (!clock_va)
210 panic("cpu_initclocks");
211 s = splhigh();
212
213 /* Install isr (in locore.s) that calls clock_intr(). */
214 isr_add_custom(5, (void*)_isr_clock);
215
216 /* Set the clock to interrupt 100 time per second. */
217 /* XXX - Hard wired? */
218
219 *interrupt_reg |= IREG_CLOCK_ENAB_5; /* enable clock */
220
221 /* XXX enable the clock? */
222
223 *interrupt_reg |= IREG_ALL_ENAB; /* enable interrupts */
224 splx(s);
225 }
226
227 /*
228 * This doesn't need to do anything, as we have only one timer and
229 * profhz==stathz==hz.
230 */
231 void
232 setstatclockrate(newhz)
233 int newhz;
234 {
235 /* nothing */
236 }
237
238 /*
239 * This is is called by the "custom" interrupt handler.
240 */
241 void
242 clock_intr(cf)
243 struct clockframe cf;
244 {
245 /* volatile struct clockreg *clk = clock_va; */
246
247 #if 1 /* XXX - Needed? */
248 /* Pulse the clock intr. enable low. */
249 *interrupt_reg &= ~IREG_CLOCK_ENAB_5;
250 *interrupt_reg |= IREG_CLOCK_ENAB_5;
251 #endif
252
253 /* XXX - Need to do anything? */
254 hardclock(&cf);
255 }
256
257 /*
258 * Return the best possible estimate of the time in the timeval
259 * to which tvp points. We do this by returning the current time
260 * plus the amount of time since the last clock interrupt.
261 *
262 * Check that this time is no less than any previously-reported time,
263 * which could happen around the time of a clock adjustment. Just for
264 * fun, we guarantee that the time will be greater than the value
265 * obtained by a previous call.
266 */
267 void
268 microtime(tvp)
269 register struct timeval *tvp;
270 {
271 int s = splhigh();
272 static struct timeval lasttime;
273
274 *tvp = time;
275 tvp->tv_usec++; /* XXX */
276 while (tvp->tv_usec > 1000000) {
277 tvp->tv_sec++;
278 tvp->tv_usec -= 1000000;
279 }
280 if (tvp->tv_sec == lasttime.tv_sec &&
281 tvp->tv_usec <= lasttime.tv_usec &&
282 (tvp->tv_usec = lasttime.tv_usec + 1) > 1000000)
283 {
284 tvp->tv_sec++;
285 tvp->tv_usec -= 1000000;
286 }
287 lasttime = *tvp;
288 splx(s);
289 }
290
291
292 /*
293 * Machine-dependent clock routines.
294 *
295 * Inittodr initializes the time of day hardware which provides
296 * date functions.
297 *
298 * Resettodr restores the time of day hardware after a time change.
299 */
300 #define SECDAY 86400L
301 #define SECYR (SECDAY * 365)
302
303 static long clk_get_secs(void);
304 static void clk_set_secs(long);
305
306 /*
307 * Initialize the time of day register, based on the time base
308 * which is, e.g. from a filesystem.
309 */
310 void inittodr(fs_time)
311 time_t fs_time;
312 {
313 long diff, clk_time;
314 long long_ago = (5 * SECYR);
315 int clk_bad = 0;
316
317 /*
318 * Sanity check time from file system.
319 * If it is zero,assume filesystem time is just unknown
320 * instead of preposterous. Don't bark.
321 */
322 if (fs_time < long_ago) {
323 /*
324 * If fs_time is zero, assume filesystem time is just
325 * unknown instead of preposterous. Don't bark.
326 */
327 if (fs_time != 0)
328 printf("WARNING: preposterous time in file system\n");
329 /* 1991/07/01 12:00:00 */
330 fs_time = 21*SECYR + 186*SECDAY + SECDAY/2;
331 }
332
333 clk_time = clk_get_secs();
334
335 /* Sanity check time from clock. */
336 if (clk_time < long_ago) {
337 printf("WARNING: bad date in battery clock");
338 clk_bad = 1;
339 clk_time = fs_time;
340 } else {
341 /* Does the clock time jive with the file system? */
342 diff = clk_time - fs_time;
343 if (diff < 0)
344 diff = -diff;
345 if (diff >= (SECDAY*2)) {
346 printf("WARNING: clock %s %d days",
347 (clk_time < fs_time) ? "lost" : "gained",
348 (int) (diff / SECDAY));
349 clk_bad = 1;
350 }
351 }
352 if (clk_bad)
353 printf(" -- CHECK AND RESET THE DATE!\n");
354 time.tv_sec = clk_time;
355 }
356
357 /*
358 * Resettodr restores the time of day hardware after a time change.
359 */
360 void resettodr()
361 {
362 clk_set_secs(time.tv_sec);
363 }
364
365
366 /*
368 * XXX - Todo: take one of the implementations of
369 * "POSIX time" to/from "YY/MM/DD/hh/mm/ss"
370 * and put that in libkern (or somewhere).
371 * Also put this stuct in some header...
372 */
373 struct date_time {
374 u_char dt_year; /* since POSIX_BASE_YEAR (1970) */
375 u_char dt_mon;
376 u_char dt_day;
377 u_char dt_hour;
378 u_char dt_min;
379 u_char dt_sec;
380 u_char dt_csec; /* hundredths of a second */
381 u_char dt_wday; /* Day of week (needed?) */
382 };
383 void gmt_to_dt __P((long gmt, struct date_time *dt));
384 long dt_to_gmt __P((struct date_time *dt));
385 /* Traditional UNIX base year */
386 #define POSIX_BASE_YEAR 1970
387 /*
388 * XXX - End of stuff that should move to a header.
389 */
390
391
392 /*
393 * Routines to copy state into and out of the clock.
394 * The clock CSR has to be set for read or write.
395 */
396
397 static void
398 clk_get_dt(struct date_time *dt)
399 {
400 volatile struct clockreg *cl = clock_va;
401 int s;
402
403 s = splhigh();
404 /* enable read (stop time) */
405 cl->cl_csr |= CLK_READ;
406
407 /* Copy the info */
408 dt->dt_sec = cl->cl_sec;
409 dt->dt_min = cl->cl_min;
410 dt->dt_hour = cl->cl_hour;
411 dt->dt_wday = cl->cl_wday;
412 dt->dt_day = cl->cl_mday;
413 dt->dt_mon = cl->cl_month;
414 dt->dt_year = cl->cl_year;
415
416 /* Done reading (time wears on) */
417 cl->cl_csr &= ~CLK_READ;
418 splx(s);
419 }
420
421 static void
422 clk_set_dt(struct date_time *dt)
423 {
424 volatile struct clockreg *cl = clock_va;
425 int s;
426
427 s = splhigh();
428 /* enable write */
429 cl->cl_csr |= CLK_WRITE;
430
431 /* Copy the info */
432 cl->cl_sec = dt->dt_sec;
433 cl->cl_min = dt->dt_min;
434 cl->cl_hour = dt->dt_hour;
435 cl->cl_wday = dt->dt_wday;
436 cl->cl_mday = dt->dt_day;
437 cl->cl_month = dt->dt_mon;
438 cl->cl_year = dt->dt_year;
439
440 /* load them up */
441 cl->cl_csr &= ~CLK_WRITE;
442 splx(s);
443 }
444
445
446 /*
447 * Now routines to get and set clock as POSIX time.
448 * Our clock keeps "years since 1/1/1968", so we must
449 * convert to/from "years since 1/1/1970" before the
450 * common time conversion functions are used.
451 */
452 #define CLOCK_YEAR_ADJUST (POSIX_BASE_YEAR - 1968)
453 static long
454 clk_get_secs()
455 {
456 struct date_time dt;
457 long gmt;
458
459 clk_get_dt(&dt);
460 dt.dt_year -= CLOCK_YEAR_ADJUST;
461 gmt = dt_to_gmt(&dt);
462 return (gmt);
463 }
464 static void
465 clk_set_secs(secs)
466 long secs;
467 {
468 struct date_time dt;
469 long gmt;
470
471 gmt = secs;
472 gmt_to_dt(gmt, &dt);
473 dt.dt_year += CLOCK_YEAR_ADJUST;
474 clk_set_dt(&dt);
475 }
476
477
478
479 /*****************************************************************
481 *
482 * Generic routines to convert to or from a POSIX date
483 * (seconds since 1/1/1970) and yr/mo/day/hr/min/sec
484 *
485 * These are organized this way mostly to so the code
486 * can easily be tested in an independent user program.
487 * (These are derived from the hp300 code.)
488 *
489 * XXX - Should move these to libkern or somewhere...
490 */
491 static inline int leapyear __P((int year));
492 #define FEBRUARY 2
493 #define days_in_year(a) (leapyear(a) ? 366 : 365)
494 #define days_in_month(a) (month_days[(a) - 1])
495
496 /*
497 * Note: This array may be modified by gmt_to_dt(),
498 * but these functions DO NOT need to be reentrant.
499 * If we ever DO need reentrance, we should just make
500 * gmt_to_dt() copy this to a local before use. -gwr
501 */
502 static char month_days[12] = {
503 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
504 };
505
506 /* Use an inline to make the logic more obvious. */
507 static inline int
508 leapyear(year)
509 int year;
510 {
511 int rv = 0;
512
513 if ((year % 4) == 0) {
514 rv = 1;
515 if ((year % 100) == 0) {
516 rv = 0;
517 if ((year % 400) == 0)
518 rv = 1;
519 }
520 }
521 return rv;
522 }
523
524 void gmt_to_dt(long gmt, struct date_time *dt)
525 {
526 long secs;
527 int i, days;
528
529 days = gmt / SECDAY;
530 secs = gmt % SECDAY;
531
532 /* Hours, minutes, seconds are easy */
533 dt->dt_hour = secs / 3600;
534 secs = secs % 3600;
535 dt->dt_min = secs / 60;
536 secs = secs % 60;
537 dt->dt_sec = secs;
538
539 /* Day of week (Note: 1/1/1970 was a Thursday) */
540 dt->dt_wday = (days + 4) % 7;
541
542 /* Subtract out whole years... */
543 i = POSIX_BASE_YEAR;
544 while (days >= days_in_year(i)) {
545 days -= days_in_year(i);
546 i++;
547 }
548 dt->dt_year = i - POSIX_BASE_YEAR;
549
550 /* Subtract out whole months... */
551 /* XXX - Note temporary change to month_days */
552 if (leapyear(i))
553 days_in_month(FEBRUARY) = 29;
554 for (i = 1; days >= days_in_month(i); i++)
555 days -= days_in_month(i);
556 /* XXX - Undo temporary change to month_days */
557 days_in_month(FEBRUARY) = 28;
558 dt->dt_mon = i;
559
560 /* Days are what is left over (+1) from all that. */
561 dt->dt_day = days + 1;
562 }
563
564 long dt_to_gmt(struct date_time *dt)
565 {
566 long gmt;
567 int i, year;
568
569 /*
570 * Hours are different for some reason. Makes no sense really.
571 */
572
573 gmt = 0;
574
575 if (dt->dt_hour >= 24) goto out;
576 if (dt->dt_day > 31) goto out;
577 if (dt->dt_mon > 12) goto out;
578
579 year = dt->dt_year + POSIX_BASE_YEAR;
580
581 /*
582 * Compute days since start of time
583 * First from years, then from months.
584 */
585 for (i = POSIX_BASE_YEAR; i < year; i++)
586 gmt += days_in_year(i);
587 if (leapyear(year) && dt->dt_mon > FEBRUARY)
588 gmt++;
589
590 /* Months */
591 for (i = 1; i < dt->dt_mon; i++)
592 gmt += days_in_month(i);
593 gmt += (dt->dt_day - 1);
594
595 /* Now do hours */
596 gmt = gmt * 24 + dt->dt_hour;
597
598 /* Now do minutes */
599 gmt = gmt * 60 + dt->dt_min;
600
601 /* Now do seconds */
602 gmt = gmt * 60 + dt->dt_sec;
603
604 out:
605 return gmt;
606 }
607