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