1 /* $NetBSD: clock.c,v 1.24 1995/04/07 04:30:13 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 for the Intersil 7170: 48 * Original by Adam Glass; partially rewritten by Gordon Ross. 49 */ 50 51 #include <sys/param.h> 52 #include <sys/systm.h> 53 #include <sys/time.h> 54 #include <sys/kernel.h> 55 #include <sys/device.h> 56 57 #include <machine/autoconf.h> 58 #include <machine/psl.h> 59 #include <machine/cpu.h> 60 61 #include <machine/mon.h> 62 #include <machine/obio.h> 63 #include <machine/control.h> 64 65 #include "intersil7170.h" 66 #include "interreg.h" 67 68 extern volatile u_char *interrupt_reg; 69 volatile char *clock_va; 70 71 #define intersil_clock ((volatile struct intersil7170 *) clock_va) 72 73 #define intersil_command(run, interrupt) \ 74 (run | interrupt | INTERSIL_CMD_FREQ_32K | INTERSIL_CMD_24HR_MODE | \ 75 INTERSIL_CMD_NORMAL_MODE) 76 77 #define intersil_clear() (void)intersil_clock->clk_intr_reg 78 79 int clockmatch __P((struct device *, void *vcf, void *args)); 80 void clockattach __P((struct device *, struct device *, void *)); 81 82 struct cfdriver clockcd = { 83 NULL, "clock", clockmatch, clockattach, 84 DV_DULL, sizeof(struct device), 0 }; 85 86 int clockmatch(parent, vcf, args) 87 struct device *parent; 88 void *vcf, *args; 89 { 90 struct cfdata *cf = vcf; 91 struct confargs *ca = args; 92 93 /* This driver only supports one unit. */ 94 if (cf->cf_unit != 0) 95 return (0); 96 if (ca->ca_paddr == -1) 97 ca->ca_paddr = OBIO_CLOCK; 98 if (ca->ca_intpri == -1) 99 ca->ca_intpri = 5; 100 return (1); 101 } 102 103 extern void level5intr_clock(); 104 void clockattach(parent, self, args) 105 struct device *parent; 106 struct device *self; 107 void *args; 108 { 109 struct confargs *ca = args; 110 111 printf("\n"); 112 if (ca->ca_intpri != 5) 113 panic("clock: level != 5"); 114 /* 115 * Can not hook up the ISR until cpu_initclock() 116 * because hardclock is not ready until then. 117 */ 118 } 119 120 /* 121 * Set and/or clear the desired clock bits in the interrupt 122 * register. We have to be extremely careful that we do it 123 * in such a manner that we don't get ourselves lost. 124 */ 125 set_clk_mode(on, off, enable) 126 u_char on, off; 127 int enable; 128 { 129 register u_char interreg; 130 register int s; 131 132 s = getsr(); 133 if ((s & PSL_IPL) < PSL_IPL7) 134 panic("set_clk_mode: ipl"); 135 136 if (!intersil_clock) 137 panic("set_clk_mode: map"); 138 139 /* 140 * make sure that we are only playing w/ 141 * clock interrupt register bits 142 */ 143 on &= (IREG_CLOCK_ENAB_7 | IREG_CLOCK_ENAB_5); 144 off &= (IREG_CLOCK_ENAB_7 | IREG_CLOCK_ENAB_5); 145 146 /* 147 * Get a copy of current interrupt register, 148 * turning off any undesired bits (aka `off') 149 */ 150 interreg = *interrupt_reg & ~(off | IREG_ALL_ENAB); 151 *interrupt_reg &= ~IREG_ALL_ENAB; 152 153 /* 154 * Next we turns off the CLK5 and CLK7 bits to clear 155 * the flip-flops, then we disable clock interrupts. 156 * Now we can read the clock's interrupt register 157 * to clear any pending signals there. 158 */ 159 *interrupt_reg &= ~(IREG_CLOCK_ENAB_7 | IREG_CLOCK_ENAB_5); 160 intersil_clock->clk_cmd_reg = 161 intersil_command(INTERSIL_CMD_RUN, INTERSIL_CMD_IDISABLE); 162 intersil_clear(); 163 164 /* 165 * Now we set all the desired bits 166 * in the interrupt register, then 167 * we turn the clock back on and 168 * finally we can enable all interrupts. 169 */ 170 *interrupt_reg |= (interreg | on); /* enable flip-flops */ 171 172 if (enable) 173 intersil_clock->clk_cmd_reg = 174 intersil_command(INTERSIL_CMD_RUN, INTERSIL_CMD_IENABLE); 175 176 *interrupt_reg |= IREG_ALL_ENAB; /* enable interrupts */ 177 } 178 179 /* Called very early by internal_configure. */ 180 void clock_init() 181 { 182 clock_va = obio_find_mapping(OBIO_CLOCK, OBIO_CLOCK_SIZE); 183 184 if (!clock_va) 185 mon_panic("clock_init: clock_va\n"); 186 if (!interrupt_reg) 187 mon_panic("clock_init: interrupt_reg\n"); 188 189 /* Turn off clock interrupts until cpu_initclocks() */ 190 /* isr_init() already set the interrupt reg to zero. */ 191 intersil_clock->clk_cmd_reg = 192 intersil_command(INTERSIL_CMD_RUN, INTERSIL_CMD_IDISABLE); 193 intersil_clear(); 194 } 195 196 #ifdef DIAGNOSTIC 197 static int clk_intr_ready; 198 #endif 199 200 /* 201 * Set up the real-time clock (enable clock interrupts). 202 * Leave stathz 0 since there is no secondary clock available. 203 * Note that clock interrupts MUST STAY DISABLED until here. 204 */ 205 void 206 cpu_initclocks(void) 207 { 208 int s; 209 210 if (!intersil_clock) 211 panic("cpu_initclocks"); 212 s = splhigh(); 213 214 isr_add_custom(5, level5intr_clock); 215 #ifdef DIAGNOSTIC 216 clk_intr_ready = 1; 217 #endif 218 219 /* Set the clock to interrupt 100 time per second. */ 220 intersil_clock->clk_intr_reg = INTERSIL_INTER_CSECONDS; 221 222 *interrupt_reg |= IREG_CLOCK_ENAB_5; /* enable clock */ 223 intersil_clock->clk_cmd_reg = 224 intersil_command(INTERSIL_CMD_RUN, INTERSIL_CMD_IENABLE); 225 *interrupt_reg |= IREG_ALL_ENAB; /* enable interrupts */ 226 splx(s); 227 } 228 229 /* 230 * This doesn't need to do anything, as we have only one timer and 231 * profhz==stathz==hz. 232 */ 233 void 234 setstatclockrate(newhz) 235 int newhz; 236 { 237 /* nothing */ 238 } 239 240 /* 241 * This is is called by the "custom" interrupt handler 242 * after it has reset the pending bit in the clock. 243 */ 244 int clock_count = 0; 245 void clock_intr(frame) 246 struct clockframe *frame; 247 { 248 static unsigned char led_pattern = 0xFE; 249 250 #ifdef DIAGNOSTIC 251 if (!clk_intr_ready) 252 panic("clock_intr"); 253 #endif 254 255 /* XXX - Move this LED frobbing to the idle loop? */ 256 clock_count++; 257 if ((clock_count & 7) == 0) { 258 led_pattern = (led_pattern << 1) | 1; 259 if (led_pattern == 0xFF) 260 led_pattern = 0xFE; 261 set_control_byte((char *) DIAG_REG, led_pattern); 262 } 263 hardclock(frame); 264 } 265 266 267 /* 268 * Return the best possible estimate of the time in the timeval 269 * to which tvp points. We do this by returning the current time 270 * plus the amount of time since the last clock interrupt. 271 * 272 * Check that this time is no less than any previously-reported time, 273 * which could happen around the time of a clock adjustment. Just for 274 * fun, we guarantee that the time will be greater than the value 275 * obtained by a previous call. 276 */ 277 void 278 microtime(tvp) 279 register struct timeval *tvp; 280 { 281 int s = splhigh(); 282 static struct timeval lasttime; 283 284 *tvp = time; 285 tvp->tv_usec; 286 while (tvp->tv_usec > 1000000) { 287 tvp->tv_sec++; 288 tvp->tv_usec -= 1000000; 289 } 290 if (tvp->tv_sec == lasttime.tv_sec && 291 tvp->tv_usec <= lasttime.tv_usec && 292 (tvp->tv_usec = lasttime.tv_usec + 1) > 1000000) { 293 tvp->tv_sec++; 294 tvp->tv_usec -= 1000000; 295 } 296 lasttime = *tvp; 297 splx(s); 298 } 299 300 301 /* 302 * Machine-dependent clock routines. 303 * 304 * Inittodr initializes the time of day hardware which provides 305 * date functions. 306 * 307 * Resettodr restores the time of day hardware after a time change. 308 */ 309 #define SECDAY 86400L 310 #define SECYR (SECDAY * 365) 311 312 static long clk_get_secs(void); 313 static void clk_set_secs(long); 314 315 /* 316 * Initialize the time of day register, based on the time base 317 * which is, e.g. from a filesystem. 318 */ 319 void inittodr(fs_time) 320 time_t fs_time; 321 { 322 long diff, clk_time; 323 long long_ago = (5 * SECYR); 324 int clk_bad = 0; 325 326 /* 327 * Sanity check time from file system. 328 * If it is zero,assume filesystem time is just unknown 329 * instead of preposterous. Don't bark. 330 */ 331 if (fs_time < long_ago) { 332 /* 333 * If fs_time is zero, assume filesystem time is just 334 * unknown instead of preposterous. Don't bark. 335 */ 336 if (fs_time != 0) 337 printf("WARNING: preposterous time in file system\n"); 338 /* 1991/07/01 12:00:00 */ 339 fs_time = 21*SECYR + 186*SECDAY + SECDAY/2; 340 } 341 342 clk_time = clk_get_secs(); 343 344 /* Sanity check time from clock. */ 345 if (clk_time < long_ago) { 346 printf("WARNING: bad date in battery clock"); 347 clk_bad = 1; 348 clk_time = fs_time; 349 } else { 350 /* Does the clock time jive with the file system? */ 351 diff = clk_time - fs_time; 352 if (diff < 0) 353 diff = -diff; 354 if (diff >= (SECDAY*2)) { 355 printf("WARNING: clock %s %d days", 356 (clk_time < fs_time) ? "lost" : "gained", 357 diff / SECDAY); 358 clk_bad = 1; 359 } 360 } 361 if (clk_bad) 362 printf(" -- CHECK AND RESET THE DATE!\n"); 363 time.tv_sec = clk_time; 364 } 365 366 /* 367 * Resettodr restores the time of day hardware after a time change. 368 */ 369 void resettodr() 370 { 371 clk_set_secs(time.tv_sec); 372 } 373 374 /* 375 * Machine dependent base year: 376 * Note: must be < 1970 377 */ 378 #define CLOCK_BASE_YEAR 1968 379 380 381 /* 382 * Routine to copy state into and out of the clock. 383 * The clock registers have to be read or written 384 * in sequential order (or so it appears). -gwr 385 */ 386 static void clk_get_dt(struct date_time *dt) 387 { 388 int s; 389 register volatile char *src, *dst; 390 391 src = (char *) &intersil_clock->counters; 392 393 s = splhigh(); 394 intersil_clock->clk_cmd_reg = 395 intersil_command(INTERSIL_CMD_STOP, INTERSIL_CMD_IENABLE); 396 397 dst = (char *) dt; 398 dt++; /* end marker */ 399 do { 400 *dst++ = *src++; 401 } while (dst < (char*)dt); 402 403 intersil_clock->clk_cmd_reg = 404 intersil_command(INTERSIL_CMD_RUN, INTERSIL_CMD_IENABLE); 405 splx(s); 406 } 407 408 static void clk_set_dt(struct date_time *dt) 409 { 410 int s; 411 register volatile char *src, *dst; 412 413 dst = (char *) &intersil_clock->counters; 414 415 s = splhigh(); 416 intersil_clock->clk_cmd_reg = 417 intersil_command(INTERSIL_CMD_STOP, INTERSIL_CMD_IENABLE); 418 419 src = (char *) dt; 420 dt++; /* end marker */ 421 do { 422 *dst++ = *src++; 423 } while (src < (char *)dt); 424 425 intersil_clock->clk_cmd_reg = 426 intersil_command(INTERSIL_CMD_RUN, INTERSIL_CMD_IENABLE); 427 splx(s); 428 } 429 430 431 432 /* 433 * Generic routines to convert to or from a POSIX date 434 * (seconds since 1/1/1970) and yr/mo/day/hr/min/sec 435 * 436 * These are organized this way mostly to so the code 437 * can easily be tested in an independent user program. 438 * (These are derived from the hp300 code.) 439 */ 440 441 /* Traditional UNIX base year */ 442 #define POSIX_BASE_YEAR 1970 443 #define FEBRUARY 2 444 445 #define leapyear(year) ((year) % 4 == 0) 446 #define days_in_year(a) (leapyear(a) ? 366 : 365) 447 #define days_in_month(a) (month_days[(a) - 1]) 448 449 static int month_days[12] = { 450 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 451 }; 452 453 void gmt_to_dt(long *tp, struct date_time *dt) 454 { 455 register int i; 456 register long days, secs; 457 458 days = *tp / SECDAY; 459 secs = *tp % SECDAY; 460 461 /* Hours, minutes, seconds are easy */ 462 dt->dt_hour = secs / 3600; 463 secs = secs % 3600; 464 dt->dt_min = secs / 60; 465 secs = secs % 60; 466 dt->dt_sec = secs; 467 468 /* Day of week (Note: 1/1/1970 was a Thursday) */ 469 dt->dt_dow = (days + 4) % 7; 470 471 /* Number of years in days */ 472 i = POSIX_BASE_YEAR; 473 while (days >= days_in_year(i)) { 474 days -= days_in_year(i); 475 i++; 476 } 477 dt->dt_year = i - CLOCK_BASE_YEAR; 478 479 /* Number of months in days left */ 480 if (leapyear(i)) 481 days_in_month(FEBRUARY) = 29; 482 for (i = 1; days >= days_in_month(i); i++) 483 days -= days_in_month(i); 484 days_in_month(FEBRUARY) = 28; 485 dt->dt_month = i; 486 487 /* Days are what is left over (+1) from all that. */ 488 dt->dt_day = days + 1; 489 } 490 491 void dt_to_gmt(struct date_time *dt, long *tp) 492 { 493 register int i; 494 register long tmp; 495 int year; 496 497 /* 498 * Hours are different for some reason. Makes no sense really. 499 */ 500 501 tmp = 0; 502 503 if (dt->dt_hour >= 24) goto out; 504 if (dt->dt_day > 31) goto out; 505 if (dt->dt_month > 12) goto out; 506 507 year = dt->dt_year + CLOCK_BASE_YEAR; 508 509 /* 510 * Compute days since start of time 511 * First from years, then from months. 512 */ 513 for (i = POSIX_BASE_YEAR; i < year; i++) 514 tmp += days_in_year(i); 515 if (leapyear(year) && dt->dt_month > FEBRUARY) 516 tmp++; 517 518 /* Months */ 519 for (i = 1; i < dt->dt_month; i++) 520 tmp += days_in_month(i); 521 tmp += (dt->dt_day - 1); 522 523 /* Now do hours */ 524 tmp = tmp * 24 + dt->dt_hour; 525 526 /* Now do minutes */ 527 tmp = tmp * 60 + dt->dt_min; 528 529 /* Now do seconds */ 530 tmp = tmp * 60 + dt->dt_sec; 531 532 out: 533 *tp = tmp; 534 } 535 536 /* 537 * Now routines to get and set clock as POSIX time. 538 */ 539 540 static long clk_get_secs() 541 { 542 struct date_time dt; 543 long gmt; 544 545 clk_get_dt(&dt); 546 dt_to_gmt(&dt, &gmt); 547 return (gmt); 548 } 549 550 static void clk_set_secs(long secs) 551 { 552 struct date_time dt; 553 long gmt; 554 555 gmt = secs; 556 gmt_to_dt(&gmt, &dt); 557 clk_set_dt(&dt); 558 } 559 560 561 #ifdef DEBUG 562 /* Call this from DDB or whatever... */ 563 int clkdebug() 564 { 565 struct date_time dt; 566 long gmt; 567 long *lp; 568 569 bzero((char*)&dt, sizeof(dt)); 570 clk_get_dt(&dt); 571 lp = (long*)&dt; 572 printf("clkdebug: dt=[%x,%x]\n", lp[0], lp[1]); 573 574 dt_to_gmt(&dt, &gmt); 575 printf("clkdebug: gmt=%x\n", gmt); 576 } 577 #endif 578