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