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