clock.c revision 1.4 1 /* $NetBSD: clock.c,v 1.4 1997/01/25 21:46:19 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 <sun3/sun3/sunmon.h>
64
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 */
89 static int
90 clock_match(parent, cf, args)
91 struct device *parent;
92 struct cfdata *cf;
93 void *args;
94 {
95 struct confargs *ca = args;
96
97 /* This driver only supports one unit. */
98 if (cf->cf_unit != 0)
99 return (0);
100
101 /* Validate the given address. */
102 if (ca->ca_paddr != OBIO_CLOCK2)
103 return (0);
104
105 /* Default interrupt priority. */
106 if (ca->ca_intpri == -1)
107 ca->ca_intpri = CLOCK_PRI;
108
109 return (1);
110 }
111
112 static void
113 clock_attach(parent, self, args)
114 struct device *parent;
115 struct device *self;
116 void *args;
117 {
118
119 printf("\n");
120
121 /*
122 * Can not hook up the ISR until cpu_initclocks()
123 * because hardclock is not ready until then.
124 * For now, the handler is _isr_autovec(), which
125 * will complain if it gets clock interrupts.
126 */
127 }
128
129 /*
130 * Set and/or clear the desired clock bits in the interrupt
131 * register. We have to be extremely careful that we do it
132 * in such a manner that we don't get ourselves lost.
133 */
134 void
135 set_clk_mode(on, off, enable)
136 u_char on, off;
137 int enable;
138 {
139 register u_char interreg;
140 register int s;
141
142 /* If we don't have this, we must not have touched it! */
143 if (!clock_va)
144 return;
145
146 s = getsr();
147 if ((s & PSL_IPL) < PSL_IPL7)
148 panic("set_clk_mode: ipl");
149
150 /*
151 * make sure that we are only playing w/
152 * clock interrupt register bits
153 */
154 on &= (IREG_CLOCK_ENAB_7 | IREG_CLOCK_ENAB_5);
155 off &= (IREG_CLOCK_ENAB_7 | IREG_CLOCK_ENAB_5);
156
157 /*
158 * Get a copy of current interrupt register,
159 * turning off any undesired bits (aka `off')
160 */
161 interreg = *interrupt_reg & ~(off | IREG_ALL_ENAB);
162 *interrupt_reg &= ~IREG_ALL_ENAB;
163
164 /*
165 * Next we turns off the CLK5 and CLK7 bits to clear
166 * the flip-flops, then we disable clock interrupts.
167 * Now we can read the clock's interrupt register
168 * to clear any pending signals there.
169 */
170 *interrupt_reg &= ~(IREG_CLOCK_ENAB_7 | IREG_CLOCK_ENAB_5);
171
172 /* XXX - hit the clock? */
173
174 /*
175 * Now we set all the desired bits
176 * in the interrupt register, then
177 * we turn the clock back on and
178 * finally we can enable all interrupts.
179 */
180 *interrupt_reg |= (interreg | on); /* enable flip-flops */
181
182 /* XXX - hit the clock? */
183
184 *interrupt_reg |= IREG_ALL_ENAB; /* enable interrupts */
185 }
186
187 /* Called very early by internal_configure. */
188 void clock_init()
189 {
190 /* XXX - Yes, use the EEPROM address. Same H/W device. */
191 clock_va = obio_find_mapping(OBIO_EEPROM, sizeof(struct clockreg));
192
193 if (!clock_va || !interrupt_reg) {
194 mon_printf("clock_init\n");
195 sunmon_abort();
196 }
197
198 /* Turn off clock interrupts until cpu_initclocks() */
199 /* isr_init() already set the interrupt reg to zero. */
200 }
201
202 /*
203 * Set up the real-time clock (enable clock interrupts).
204 * Leave stathz 0 since there is no secondary clock available.
205 * Note that clock interrupts MUST STAY DISABLED until here.
206 */
207 void
208 cpu_initclocks(void)
209 {
210 int s;
211
212 if (!clock_va)
213 panic("cpu_initclocks");
214 s = splhigh();
215
216 /* Install isr (in locore.s) that calls clock_intr(). */
217 isr_add_custom(5, (void*)_isr_clock);
218
219 /* Set the clock to interrupt 100 time per second. */
220 /* XXX - Hard wired? */
221
222 *interrupt_reg |= IREG_CLOCK_ENAB_5; /* enable clock */
223
224 /* XXX enable the clock? */
225
226 *interrupt_reg |= IREG_ALL_ENAB; /* enable interrupts */
227 splx(s);
228 }
229
230 /*
231 * This doesn't need to do anything, as we have only one timer and
232 * profhz==stathz==hz.
233 */
234 void
235 setstatclockrate(newhz)
236 int newhz;
237 {
238 /* nothing */
239 }
240
241 /*
242 * This is is called by the "custom" interrupt handler.
243 */
244 void
245 clock_intr(cf)
246 struct clockframe cf;
247 {
248 /* volatile struct clockreg *clk = clock_va; */
249
250 #if 1 /* XXX - Needed? */
251 /* Pulse the clock intr. enable low. */
252 *interrupt_reg &= ~IREG_CLOCK_ENAB_5;
253 *interrupt_reg |= IREG_CLOCK_ENAB_5;
254 #endif
255
256 /* XXX - Need to do anything? */
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
400 static void
401 clk_get_dt(struct date_time *dt)
402 {
403 volatile struct clockreg *cl = clock_va;
404 int s;
405
406 s = splhigh();
407 /* enable read (stop time) */
408 cl->cl_csr |= CLK_READ;
409
410 /* Copy the info */
411 dt->dt_sec = cl->cl_sec;
412 dt->dt_min = cl->cl_min;
413 dt->dt_hour = cl->cl_hour;
414 dt->dt_wday = cl->cl_wday;
415 dt->dt_day = cl->cl_mday;
416 dt->dt_mon = cl->cl_month;
417 dt->dt_year = cl->cl_year;
418
419 /* Done reading (time wears on) */
420 cl->cl_csr &= ~CLK_READ;
421 splx(s);
422 }
423
424 static void
425 clk_set_dt(struct date_time *dt)
426 {
427 volatile struct clockreg *cl = clock_va;
428 int s;
429
430 s = splhigh();
431 /* enable write */
432 cl->cl_csr |= CLK_WRITE;
433
434 /* Copy the info */
435 cl->cl_sec = dt->dt_sec;
436 cl->cl_min = dt->dt_min;
437 cl->cl_hour = dt->dt_hour;
438 cl->cl_wday = dt->dt_wday;
439 cl->cl_mday = dt->dt_day;
440 cl->cl_month = dt->dt_mon;
441 cl->cl_year = dt->dt_year;
442
443 /* load them up */
444 cl->cl_csr &= ~CLK_WRITE;
445 splx(s);
446 }
447
448
449 /*
450 * Now routines to get and set clock as POSIX time.
451 * Our clock keeps "years since 1/1/1968", so we must
452 * convert to/from "years since 1/1/1970" before the
453 * common time conversion functions are used.
454 */
455 #define CLOCK_YEAR_ADJUST (POSIX_BASE_YEAR - 1968)
456 static long
457 clk_get_secs()
458 {
459 struct date_time dt;
460 long gmt;
461
462 clk_get_dt(&dt);
463 dt.dt_year -= CLOCK_YEAR_ADJUST;
464 gmt = dt_to_gmt(&dt);
465 return (gmt);
466 }
467 static void
468 clk_set_secs(secs)
469 long secs;
470 {
471 struct date_time dt;
472 long gmt;
473
474 gmt = secs;
475 gmt_to_dt(gmt, &dt);
476 dt.dt_year += CLOCK_YEAR_ADJUST;
477 clk_set_dt(&dt);
478 }
479
480
481
482 /*****************************************************************
484 *
485 * Generic routines to convert to or from a POSIX date
486 * (seconds since 1/1/1970) and yr/mo/day/hr/min/sec
487 *
488 * These are organized this way mostly to so the code
489 * can easily be tested in an independent user program.
490 * (These are derived from the hp300 code.)
491 *
492 * XXX - Should move these to libkern or somewhere...
493 */
494 static inline int leapyear __P((int year));
495 #define FEBRUARY 2
496 #define days_in_year(a) (leapyear(a) ? 366 : 365)
497 #define days_in_month(a) (month_days[(a) - 1])
498
499 /*
500 * Note: This array may be modified by gmt_to_dt(),
501 * but these functions DO NOT need to be reentrant.
502 * If we ever DO need reentrance, we should just make
503 * gmt_to_dt() copy this to a local before use. -gwr
504 */
505 static int month_days[12] = {
506 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
507 };
508
509 /* Use an inline to make the logic more obvious. */
510 static inline int
511 leapyear(year)
512 int year;
513 {
514 int rv = 0;
515
516 if ((year % 4) == 0) {
517 rv = 1;
518 if ((year % 100) == 0) {
519 rv = 0;
520 if ((year % 400) == 0)
521 rv = 1;
522 }
523 }
524 return rv;
525 }
526
527 void gmt_to_dt(long gmt, struct date_time *dt)
528 {
529 long secs;
530 int i, days;
531
532 days = gmt / SECDAY;
533 secs = gmt % SECDAY;
534
535 /* Hours, minutes, seconds are easy */
536 dt->dt_hour = secs / 3600;
537 secs = secs % 3600;
538 dt->dt_min = secs / 60;
539 secs = secs % 60;
540 dt->dt_sec = secs;
541
542 /* Day of week (Note: 1/1/1970 was a Thursday) */
543 dt->dt_wday = (days + 4) % 7;
544
545 /* Subtract out whole years... */
546 i = POSIX_BASE_YEAR;
547 while (days >= days_in_year(i)) {
548 days -= days_in_year(i);
549 i++;
550 }
551 dt->dt_year = i - POSIX_BASE_YEAR;
552
553 /* Subtract out whole months... */
554 /* XXX - Note temporary change to month_days */
555 if (leapyear(i))
556 days_in_month(FEBRUARY) = 29;
557 for (i = 1; days >= days_in_month(i); i++)
558 days -= days_in_month(i);
559 /* XXX - Undo temporary change to month_days */
560 days_in_month(FEBRUARY) = 28;
561 dt->dt_mon = i;
562
563 /* Days are what is left over (+1) from all that. */
564 dt->dt_day = days + 1;
565 }
566
567 long dt_to_gmt(struct date_time *dt)
568 {
569 long gmt;
570 int i, year;
571
572 /*
573 * Hours are different for some reason. Makes no sense really.
574 */
575
576 gmt = 0;
577
578 if (dt->dt_hour >= 24) goto out;
579 if (dt->dt_day > 31) goto out;
580 if (dt->dt_mon > 12) goto out;
581
582 year = dt->dt_year + POSIX_BASE_YEAR;
583
584 /*
585 * Compute days since start of time
586 * First from years, then from months.
587 */
588 for (i = POSIX_BASE_YEAR; i < year; i++)
589 gmt += days_in_year(i);
590 if (leapyear(year) && dt->dt_mon > FEBRUARY)
591 gmt++;
592
593 /* Months */
594 for (i = 1; i < dt->dt_mon; i++)
595 gmt += days_in_month(i);
596 gmt += (dt->dt_day - 1);
597
598 /* Now do hours */
599 gmt = gmt * 24 + dt->dt_hour;
600
601 /* Now do minutes */
602 gmt = gmt * 60 + dt->dt_min;
603
604 /* Now do seconds */
605 gmt = gmt * 60 + dt->dt_sec;
606
607 out:
608 return gmt;
609 }
610