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