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