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