1 /* $NetBSD: clock.c,v 1.20 2001/09/05 14:18:10 tsutsui 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 (intersil_va) 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 (mostek_clk_va) 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 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 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 memset(&dt, 0, 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