clock.c revision 1.15 1 /* $NetBSD: clock.c,v 1.15 1998/02/05 04:57:54 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. Sun3X machines may have
48 * either the Mostek 48T02 or the Intersil 7170 clock.
49 *
50 * It is tricky to determine which you have, because there is
51 * always something responding at the address where the Mostek
52 * clock might be found: either a Mostek or plain-old EEPROM.
53 * Therefore, we cheat. If we find an Intersil clock, assume
54 * that what responds at the end of the EEPROM space is just
55 * plain-old EEPROM (not a Mostek clock). Worse, there are
56 * H/W problems with probing for an Intersil on the 3/80, so
57 * on that machine we "know" there is a Mostek clock.
58 *
59 * Note that the probing algorithm described above requires
60 * that we probe the intersil before we probe the mostek!
61 */
62
63 #include <sys/param.h>
64 #include <sys/systm.h>
65 #include <sys/time.h>
66 #include <sys/kernel.h>
67 #include <sys/device.h>
68
69 #include <m68k/asm_single.h>
70
71 #include <machine/autoconf.h>
72 #include <machine/cpu.h>
73 #include <machine/idprom.h>
74 #include <machine/leds.h>
75
76 #include <dev/clock_subr.h>
77 #include <dev/ic/intersil7170.h>
78
79 #include <sun3/sun3/machdep.h>
80 #include <sun3/sun3/interreg.h>
81
82 #include <sun3/sun3x/mk48t02.h>
83
84 #define SUN3_470 Yes
85
86 #define CLOCK_PRI 5
87 #define IREG_CLK_BITS (IREG_CLOCK_ENAB_7 | IREG_CLOCK_ENAB_5)
88
89 /*
90 * Only one of these two variables should be non-zero after
91 * autoconfiguration determines which clock we have.
92 */
93 static volatile void *intersil_va;
94 static volatile void *mostek_clk_va;
95
96 void _isr_clock __P((void)); /* in locore.s */
97 void clock_intr __P((struct clockframe));
98
99
100 static int clock_match __P((struct device *, struct cfdata *, void *args));
101 static void clock_attach __P((struct device *, struct device *, void *));
102
103 struct cfattach clock_ca = {
104 sizeof(struct device), clock_match, clock_attach
105 };
106
107 #ifdef SUN3_470
108
109 #define intersil_clock ((volatile struct intersil7170 *) intersil_va)
110
111 #define intersil_command(run, interrupt) \
112 (run | interrupt | INTERSIL_CMD_FREQ_32K | INTERSIL_CMD_24HR_MODE | \
113 INTERSIL_CMD_NORMAL_MODE)
114
115 #define intersil_clear() (void)intersil_clock->clk_intr_reg
116
117 static int oclock_match __P((struct device *, struct cfdata *, void *args));
118 static void oclock_attach __P((struct device *, struct device *, void *));
119
120 struct cfattach oclock_ca = {
121 sizeof(struct device), oclock_match, oclock_attach
122 };
123
124 /*
125 * Is there an intersil clock?
126 */
127 static int
128 oclock_match(parent, cf, args)
129 struct device *parent;
130 struct cfdata *cf;
131 void *args;
132 {
133 struct confargs *ca = args;
134
135 /* This driver only supports one unit. */
136 if (cf->cf_unit != 0)
137 return (0);
138
139 /*
140 * The 3/80 can not probe the Intersil absent,
141 * but it never has one, so "just say no."
142 */
143 if (cpu_machine_id == SUN3X_MACH_80)
144 return (0);
145
146 /* OK, really probe for the Intersil. */
147 if (bus_peek(ca->ca_bustype, ca->ca_paddr, 1) == -1)
148 return (0);
149
150 return (1);
151 }
152
153 /*
154 * Attach the intersil clock.
155 */
156 static void
157 oclock_attach(parent, self, args)
158 struct device *parent;
159 struct device *self;
160 void *args;
161 {
162 struct confargs *ca = args;
163 caddr_t va;
164
165 printf("\n");
166
167 /* Get a mapping for it. */
168 va = bus_mapin(ca->ca_bustype,
169 ca->ca_paddr, sizeof(struct intersil7170));
170 if (!va)
171 panic("oclock_attach");
172 intersil_va = va;
173
174 #ifdef DIAGNOSTIC
175 /* Verify correct probe order... */
176 if (mostek_clk_va) {
177 mostek_clk_va = 0;
178 printf("%s: warning - mostek found also!\n",
179 self->dv_xname);
180 }
181 #endif
182
183 /*
184 * Set the clock to the correct interrupt rate, but
185 * do not enable the interrupt until cpu_initclocks.
186 * XXX: Actually, the interrupt_reg should be zero
187 * at this point, so the clock interrupts should not
188 * affect us, but we need to set the rate...
189 */
190 intersil_clock->clk_cmd_reg =
191 intersil_command(INTERSIL_CMD_RUN, INTERSIL_CMD_IDISABLE);
192 intersil_clear();
193
194 /* Set the clock to 100 Hz, but do not enable it yet. */
195 intersil_clock->clk_intr_reg = INTERSIL_INTER_CSECONDS;
196
197 /*
198 * Can not hook up the ISR until cpu_initclocks()
199 * because hardclock is not ready until then.
200 * For now, the handler is _isr_autovec(), which
201 * will complain if it gets clock interrupts.
202 */
203 }
204 #endif /* SUN3_470 */
205
206
207 /*
208 * Is there a Mostek clock? Hard to tell...
209 * (See comment at top of this file.)
210 */
211 static int
212 clock_match(parent, cf, args)
213 struct device *parent;
214 struct cfdata *cf;
215 void *args;
216 {
217
218 /* This driver only supports one unit. */
219 if (cf->cf_unit != 0)
220 return (0);
221
222 /* If intersil was found, use that. */
223 if (intersil_va)
224 return (0);
225
226 /* Assume a Mostek is there... */
227 return (1);
228 }
229
230 /*
231 * Attach the mostek clock.
232 */
233 static void
234 clock_attach(parent, self, args)
235 struct device *parent;
236 struct device *self;
237 void *args;
238 {
239 struct confargs *ca = args;
240 caddr_t va;
241
242 printf("\n");
243
244 /* Get a mapping for it. */
245 va = bus_mapin(ca->ca_bustype,
246 ca->ca_paddr, sizeof(struct mostek_clkreg));
247 if (!va)
248 panic("clock_attach");
249 mostek_clk_va = va;
250
251 /*
252 * Can not hook up the ISR until cpu_initclocks()
253 * because hardclock is not ready until then.
254 * For now, the handler is _isr_autovec(), which
255 * will complain if it gets clock interrupts.
256 */
257 }
258
259 /*
260 * Set and/or clear the desired clock bits in the interrupt
261 * register. We have to be extremely careful that we do it
262 * in such a manner that we don't get ourselves lost.
263 * XXX: Watch out! It's really easy to break this!
264 */
265 void
266 set_clk_mode(on, off, enable_clk)
267 u_char on, off;
268 int enable_clk;
269 {
270 register u_char interreg;
271
272 /*
273 * If we have not yet mapped the register,
274 * then we do not want to do any of this...
275 */
276 if (!interrupt_reg)
277 return;
278
279 #ifdef DIAGNOSTIC
280 /* Assertion: were are at splhigh! */
281 if ((getsr() & PSL_IPL) < PSL_IPL7)
282 panic("set_clk_mode: bad ipl");
283 #endif
284
285 /*
286 * make sure that we are only playing w/
287 * clock interrupt register bits
288 */
289 on &= IREG_CLK_BITS;
290 off &= IREG_CLK_BITS;
291
292 /* First, turn off the "master" enable bit. */
293 single_inst_bclr_b(*interrupt_reg, IREG_ALL_ENAB);
294
295 /*
296 * Save the current interrupt register clock bits,
297 * and turn off/on the requested bits in the copy.
298 */
299 interreg = *interrupt_reg & IREG_CLK_BITS;
300 interreg &= ~off;
301 interreg |= on;
302
303 /* Clear the CLK5 and CLK7 bits to clear the flip-flops. */
304 single_inst_bclr_b(*interrupt_reg, IREG_CLK_BITS);
305
306 #ifdef SUN3_470
307 if (intersil_va) {
308 /*
309 * Then disable clock interrupts, and read the clock's
310 * interrupt register to clear any pending signals there.
311 */
312 intersil_clock->clk_cmd_reg =
313 intersil_command(INTERSIL_CMD_RUN, INTERSIL_CMD_IDISABLE);
314 intersil_clear();
315 }
316 #endif /* SUN3_470 */
317
318 /* Set the requested bits in the interrupt register. */
319 single_inst_bset_b(*interrupt_reg, interreg);
320
321 #ifdef SUN3_470
322 /* Turn the clock back on (maybe) */
323 if (intersil_va && enable_clk)
324 intersil_clock->clk_cmd_reg =
325 intersil_command(INTERSIL_CMD_RUN, INTERSIL_CMD_IENABLE);
326 #endif /* SUN3_470 */
327
328 /* Finally, turn the "master" enable back on. */
329 single_inst_bset_b(*interrupt_reg, IREG_ALL_ENAB);
330 }
331
332 /*
333 * Set up the real-time clock (enable clock interrupts).
334 * Leave stathz 0 since there is no secondary clock available.
335 * Note that clock interrupts MUST STAY DISABLED until here.
336 */
337 void
338 cpu_initclocks(void)
339 {
340 int s;
341
342 s = splhigh();
343
344 /* Install isr (in locore.s) that calls clock_intr(). */
345 isr_add_custom(5, (void*)_isr_clock);
346
347 /* Now enable the clock at level 5 in the interrupt reg. */
348 set_clk_mode(IREG_CLOCK_ENAB_5, 0, 1);
349
350 splx(s);
351 }
352
353 /*
354 * This doesn't need to do anything, as we have only one timer and
355 * profhz==stathz==hz.
356 */
357 void
358 setstatclockrate(newhz)
359 int newhz;
360 {
361 /* nothing */
362 }
363
364 /*
365 * Clock interrupt handler (for both Intersil and Mostek).
366 * XXX - Is it worth the trouble to save a few cycles here
367 * by making two separate interrupt handlers?
368 *
369 * This is is called by the "custom" interrupt handler.
370 * Note that we can get ZS interrupts while this runs,
371 * and zshard may touch the interrupt_reg, so we must
372 * be careful to use the single_inst_* macros to modify
373 * the interrupt register atomically.
374 */
375 void
376 clock_intr(cf)
377 struct clockframe cf;
378 {
379 extern char _Idle[]; /* locore.s */
380
381 #ifdef SUN3_470
382 if (intersil_va) {
383 /* Read the clock interrupt register. */
384 intersil_clear();
385 }
386 #endif /* SUN3_470 */
387
388 /* Pulse the clock intr. enable low. */
389 single_inst_bclr_b(*interrupt_reg, IREG_CLOCK_ENAB_5);
390 single_inst_bset_b(*interrupt_reg, IREG_CLOCK_ENAB_5);
391
392 #ifdef SUN3_470
393 if (intersil_va) {
394 /* Read the clock intr. reg. AGAIN! */
395 intersil_clear();
396 }
397 #endif /* SUN3_470 */
398
399 /* Entertainment! */
400 if (cf.cf_pc == (long)_Idle)
401 leds_intr();
402
403 /* Call common clock interrupt handler. */
404 hardclock(&cf);
405 }
406
407
408 /*
409 * Return the best possible estimate of the time in the timeval
410 * to which tvp points. We do this by returning the current time
411 * plus the amount of time since the last clock interrupt.
412 *
413 * Check that this time is no less than any previously-reported time,
414 * which could happen around the time of a clock adjustment. Just for
415 * fun, we guarantee that the time will be greater than the value
416 * obtained by a previous call.
417 */
418 void
419 microtime(tvp)
420 register struct timeval *tvp;
421 {
422 int s = splhigh();
423 static struct timeval lasttime;
424
425 *tvp = time;
426 tvp->tv_usec++; /* XXX */
427 while (tvp->tv_usec > 1000000) {
428 tvp->tv_sec++;
429 tvp->tv_usec -= 1000000;
430 }
431 if (tvp->tv_sec == lasttime.tv_sec &&
432 tvp->tv_usec <= lasttime.tv_usec &&
433 (tvp->tv_usec = lasttime.tv_usec + 1) > 1000000)
434 {
435 tvp->tv_sec++;
436 tvp->tv_usec -= 1000000;
437 }
438 lasttime = *tvp;
439 splx(s);
440 }
441
442
443 /*
444 * Machine-dependent clock routines.
445 *
446 * Inittodr initializes the time of day hardware which provides
447 * date functions.
448 *
449 * Resettodr restores the time of day hardware after a time change.
450 */
451
452 static long clk_get_secs __P((void));
453 static void clk_set_secs __P((long));
454
455 /*
456 * Initialize the time of day register, based on the time base
457 * which is, e.g. from a filesystem.
458 */
459 void inittodr(fs_time)
460 time_t fs_time;
461 {
462 long diff, clk_time;
463 long long_ago = (5 * SECYR);
464 int clk_bad = 0;
465
466 /*
467 * Sanity check time from file system.
468 * If it is zero,assume filesystem time is just unknown
469 * instead of preposterous. Don't bark.
470 */
471 if (fs_time < long_ago) {
472 /*
473 * If fs_time is zero, assume filesystem time is just
474 * unknown instead of preposterous. Don't bark.
475 */
476 if (fs_time != 0)
477 printf("WARNING: preposterous time in file system\n");
478 /* 1991/07/01 12:00:00 */
479 fs_time = 21*SECYR + 186*SECDAY + SECDAY/2;
480 }
481
482 clk_time = clk_get_secs();
483
484 /* Sanity check time from clock. */
485 if (clk_time < long_ago) {
486 printf("WARNING: bad date in battery clock");
487 clk_bad = 1;
488 clk_time = fs_time;
489 } else {
490 /* Does the clock time jive with the file system? */
491 diff = clk_time - fs_time;
492 if (diff < 0)
493 diff = -diff;
494 if (diff >= (SECDAY*2)) {
495 printf("WARNING: clock %s %d days",
496 (clk_time < fs_time) ? "lost" : "gained",
497 (int) (diff / SECDAY));
498 clk_bad = 1;
499 }
500 }
501 if (clk_bad)
502 printf(" -- CHECK AND RESET THE DATE!\n");
503 time.tv_sec = clk_time;
504 }
505
506 /*
507 * Resettodr restores the time of day hardware after a time change.
508 */
509 void resettodr()
510 {
511 clk_set_secs(time.tv_sec);
512 }
513
514
515 /*
516 * Now routines to get and set clock as POSIX time.
517 * Our clock keeps "years since 1/1/1968".
518 */
519 #define CLOCK_BASE_YEAR 1968
520 #ifdef SUN3_470
521 static void intersil_get_dt __P((struct clock_ymdhms *));
522 static void intersil_set_dt __P((struct clock_ymdhms *));
523 #endif /* SUN3_470 */
524 static void mostek_get_dt __P((struct clock_ymdhms *));
525 static void mostek_set_dt __P((struct clock_ymdhms *));
526
527 static long
528 clk_get_secs()
529 {
530 struct clock_ymdhms dt;
531 long secs;
532
533 bzero(&dt, sizeof(dt));
534
535 #ifdef SUN3_470
536 if (intersil_va)
537 intersil_get_dt(&dt);
538 #endif /* SUN3_470 */
539 if (mostek_clk_va) {
540 /* Read the Mostek. */
541 mostek_get_dt(&dt);
542 /* Convert BCD values to binary. */
543 dt.dt_sec = FROMBCD(dt.dt_sec);
544 dt.dt_min = FROMBCD(dt.dt_min);
545 dt.dt_hour = FROMBCD(dt.dt_hour);
546 dt.dt_day = FROMBCD(dt.dt_day);
547 dt.dt_mon = FROMBCD(dt.dt_mon);
548 dt.dt_year = FROMBCD(dt.dt_year);
549 }
550
551 if ((dt.dt_hour > 24) ||
552 (dt.dt_day > 31) ||
553 (dt.dt_mon > 12))
554 return (0);
555
556 dt.dt_year += CLOCK_BASE_YEAR;
557 secs = clock_ymdhms_to_secs(&dt);
558 return (secs);
559 }
560
561 static void
562 clk_set_secs(secs)
563 long secs;
564 {
565 struct clock_ymdhms dt;
566
567 clock_secs_to_ymdhms(secs, &dt);
568 dt.dt_year -= CLOCK_BASE_YEAR;
569
570 #ifdef SUN3_470
571 if (intersil_va)
572 intersil_set_dt(&dt);
573 #endif /* SUN3_470 */
574
575 if (mostek_clk_va) {
576 /* Convert binary values to BCD. */
577 dt.dt_sec = TOBCD(dt.dt_sec);
578 dt.dt_min = TOBCD(dt.dt_min);
579 dt.dt_hour = TOBCD(dt.dt_hour);
580 dt.dt_day = TOBCD(dt.dt_day);
581 dt.dt_mon = TOBCD(dt.dt_mon);
582 dt.dt_year = TOBCD(dt.dt_year);
583 /* Write the Mostek. */
584 mostek_set_dt(&dt);
585 }
586 }
587
588 #ifdef SUN3_470
589
590 /*
591 * Routines to copy state into and out of the clock.
592 * The intersil registers have to be read or written
593 * in sequential order (or so it appears). -gwr
594 */
595 static void
596 intersil_get_dt(struct clock_ymdhms *dt)
597 {
598 volatile struct intersil_dt *isdt;
599 int s;
600
601 isdt = &intersil_clock->counters;
602 s = splhigh();
603
604 /* Enable read (stop time) */
605 intersil_clock->clk_cmd_reg =
606 intersil_command(INTERSIL_CMD_STOP, INTERSIL_CMD_IENABLE);
607
608 /* Copy the info. Careful about the order! */
609 dt->dt_sec = isdt->dt_csec; /* throw-away */
610 dt->dt_hour = isdt->dt_hour;
611 dt->dt_min = isdt->dt_min;
612 dt->dt_sec = isdt->dt_sec;
613 dt->dt_mon = isdt->dt_month;
614 dt->dt_day = isdt->dt_day;
615 dt->dt_year = isdt->dt_year;
616 dt->dt_wday = isdt->dt_dow;
617
618 /* Done reading (time wears on) */
619 intersil_clock->clk_cmd_reg =
620 intersil_command(INTERSIL_CMD_RUN, INTERSIL_CMD_IENABLE);
621 splx(s);
622 }
623
624 static void
625 intersil_set_dt(struct clock_ymdhms *dt)
626 {
627 volatile struct intersil_dt *isdt;
628 int s;
629
630 isdt = &intersil_clock->counters;
631 s = splhigh();
632
633 /* Enable write (stop time) */
634 intersil_clock->clk_cmd_reg =
635 intersil_command(INTERSIL_CMD_STOP, INTERSIL_CMD_IENABLE);
636
637 /* Copy the info. Careful about the order! */
638 isdt->dt_csec = 0;
639 isdt->dt_hour = dt->dt_hour;
640 isdt->dt_min = dt->dt_min;
641 isdt->dt_sec = dt->dt_sec;
642 isdt->dt_month= dt->dt_mon;
643 isdt->dt_day = dt->dt_day;
644 isdt->dt_year = dt->dt_year;
645 isdt->dt_dow = dt->dt_wday;
646
647 /* Done writing (time wears on) */
648 intersil_clock->clk_cmd_reg =
649 intersil_command(INTERSIL_CMD_RUN, INTERSIL_CMD_IENABLE);
650 splx(s);
651 }
652
653 #endif /* SUN3_470 */
654
655
656 /*
657 * Routines to copy state into and out of the clock.
658 * The clock CSR has to be set for read or write.
659 */
660 static void
661 mostek_get_dt(struct clock_ymdhms *dt)
662 {
663 volatile struct mostek_clkreg *cl = mostek_clk_va;
664 int s;
665
666 s = splhigh();
667
668 /* enable read (stop time) */
669 cl->cl_csr |= CLK_READ;
670
671 /* Copy the info */
672 dt->dt_sec = cl->cl_sec;
673 dt->dt_min = cl->cl_min;
674 dt->dt_hour = cl->cl_hour;
675 dt->dt_wday = cl->cl_wday;
676 dt->dt_day = cl->cl_mday;
677 dt->dt_mon = cl->cl_month;
678 dt->dt_year = cl->cl_year;
679
680 /* Done reading (time wears on) */
681 cl->cl_csr &= ~CLK_READ;
682 splx(s);
683 }
684
685 static void
686 mostek_set_dt(struct clock_ymdhms *dt)
687 {
688 volatile struct mostek_clkreg *cl = mostek_clk_va;
689 int s;
690
691 s = splhigh();
692 /* enable write */
693 cl->cl_csr |= CLK_WRITE;
694
695 /* Copy the info */
696 cl->cl_sec = dt->dt_sec;
697 cl->cl_min = dt->dt_min;
698 cl->cl_hour = dt->dt_hour;
699 cl->cl_wday = dt->dt_wday;
700 cl->cl_mday = dt->dt_day;
701 cl->cl_month = dt->dt_mon;
702 cl->cl_year = dt->dt_year;
703
704 /* load them up */
705 cl->cl_csr &= ~CLK_WRITE;
706 splx(s);
707 }
708
709