1 /* $NetBSD: clock.c,v 1.49 2002/10/02 16:02:28 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 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 <m68k/asm_single.h> 58 59 #include <machine/autoconf.h> 60 #include <machine/cpu.h> 61 #include <machine/leds.h> 62 63 #include <sun3/sun3/control.h> 64 #include <sun3/sun3/interreg.h> 65 #include <sun3/sun3/machdep.h> 66 67 #include <dev/clock_subr.h> 68 #include <dev/ic/intersil7170.h> 69 70 #define CLOCK_PRI 5 71 #define IREG_CLK_BITS (IREG_CLOCK_ENAB_7 | IREG_CLOCK_ENAB_5) 72 73 void _isr_clock __P((void)); /* in locore.s */ 74 void clock_intr __P((struct clockframe)); 75 76 static volatile void *intersil_va; 77 78 #define intersil_clock ((volatile struct intersil7170 *) intersil_va) 79 80 #define intersil_command(run, interrupt) \ 81 (run | interrupt | INTERSIL_CMD_FREQ_32K | INTERSIL_CMD_24HR_MODE | \ 82 INTERSIL_CMD_NORMAL_MODE) 83 84 #define intersil_clear() (void)intersil_clock->clk_intr_reg 85 86 static int clock_match __P((struct device *, struct cfdata *, void *args)); 87 static void clock_attach __P((struct device *, struct device *, void *)); 88 89 CFATTACH_DECL(clock, sizeof(struct device), 90 clock_match, clock_attach, NULL, NULL); 91 92 static int 93 clock_match(parent, cf, args) 94 struct device *parent; 95 struct cfdata *cf; 96 void *args; 97 { 98 struct confargs *ca = args; 99 100 /* This driver only supports one unit. */ 101 if (intersil_va) 102 return (0); 103 104 /* Make sure there is something there... */ 105 if (bus_peek(ca->ca_bustype, ca->ca_paddr, 1) == -1) 106 return (0); 107 108 /* Default interrupt priority. */ 109 if (ca->ca_intpri == -1) 110 ca->ca_intpri = CLOCK_PRI; 111 112 return (1); 113 } 114 115 static void 116 clock_attach(parent, self, args) 117 struct device *parent; 118 struct device *self; 119 void *args; 120 { 121 struct confargs *ca = args; 122 caddr_t va; 123 124 printf("\n"); 125 126 /* Get a mapping for it. */ 127 va = bus_mapin(ca->ca_bustype, 128 ca->ca_paddr, sizeof(struct intersil7170)); 129 if (!va) 130 panic("clock_attach"); 131 intersil_va = va; 132 133 /* 134 * Set the clock to the correct interrupt rate, but 135 * do not enable the interrupt until cpu_initclocks. 136 * XXX: Actually, the interrupt_reg should be zero 137 * at this point, so the clock interrupts should not 138 * affect us, but we need to set the rate... 139 */ 140 intersil_clock->clk_cmd_reg = 141 intersil_command(INTERSIL_CMD_RUN, INTERSIL_CMD_IDISABLE); 142 intersil_clear(); 143 144 /* Set the clock to 100 Hz, but do not enable it yet. */ 145 intersil_clock->clk_intr_reg = INTERSIL_INTER_CSECONDS; 146 147 /* 148 * Can not hook up the ISR until cpu_initclocks() 149 * because hardclock is not ready until then. 150 * For now, the handler is _isr_autovec(), which 151 * will complain if it gets clock interrupts. 152 */ 153 } 154 155 /* 156 * Set and/or clear the desired clock bits in the interrupt 157 * register. We have to be extremely careful that we do it 158 * in such a manner that we don't get ourselves lost. 159 * XXX: Watch out! It's really easy to break this! 160 */ 161 void 162 set_clk_mode(on, off, enable_clk) 163 u_char on, off; 164 int enable_clk; 165 { 166 u_char interreg; 167 168 /* 169 * If we have not yet mapped the register, 170 * then we do not want to do any of this... 171 */ 172 if (!interrupt_reg) 173 return; 174 175 #ifdef DIAGNOSTIC 176 /* Assertion: were are at splhigh! */ 177 if ((getsr() & PSL_IPL) < PSL_IPL7) 178 panic("set_clk_mode: bad ipl"); 179 #endif 180 181 /* 182 * make sure that we are only playing w/ 183 * clock interrupt register bits 184 */ 185 on &= IREG_CLK_BITS; 186 off &= IREG_CLK_BITS; 187 188 /* First, turn off the "master" enable bit. */ 189 single_inst_bclr_b(*interrupt_reg, IREG_ALL_ENAB); 190 191 /* 192 * Save the current interrupt register clock bits, 193 * and turn off/on the requested bits in the copy. 194 */ 195 interreg = *interrupt_reg & IREG_CLK_BITS; 196 interreg &= ~off; 197 interreg |= on; 198 199 /* Clear the CLK5 and CLK7 bits to clear the flip-flops. */ 200 single_inst_bclr_b(*interrupt_reg, IREG_CLK_BITS); 201 202 if (intersil_va) { 203 /* 204 * Then disable clock interrupts, and read the clock's 205 * interrupt register to clear any pending signals there. 206 */ 207 intersil_clock->clk_cmd_reg = 208 intersil_command(INTERSIL_CMD_RUN, INTERSIL_CMD_IDISABLE); 209 intersil_clear(); 210 } 211 212 /* Set the requested bits in the interrupt register. */ 213 single_inst_bset_b(*interrupt_reg, interreg); 214 215 /* Turn the clock back on (maybe) */ 216 if (intersil_va && enable_clk) 217 intersil_clock->clk_cmd_reg = 218 intersil_command(INTERSIL_CMD_RUN, INTERSIL_CMD_IENABLE); 219 220 /* Finally, turn the "master" enable back on. */ 221 single_inst_bset_b(*interrupt_reg, IREG_ALL_ENAB); 222 } 223 224 /* 225 * Set up the real-time clock (enable clock interrupts). 226 * Leave stathz 0 since there is no secondary clock available. 227 * Note that clock interrupts MUST STAY DISABLED until here. 228 */ 229 void 230 cpu_initclocks(void) 231 { 232 int s; 233 234 s = splhigh(); 235 236 /* Install isr (in locore.s) that calls clock_intr(). */ 237 isr_add_custom(5, (void*)_isr_clock); 238 239 /* Now enable the clock at level 5 in the interrupt reg. */ 240 set_clk_mode(IREG_CLOCK_ENAB_5, 0, 1); 241 242 splx(s); 243 } 244 245 /* 246 * This doesn't need to do anything, as we have only one timer and 247 * profhz==stathz==hz. 248 */ 249 void 250 setstatclockrate(newhz) 251 int newhz; 252 { 253 /* nothing */ 254 } 255 256 /* 257 * This is is called by the "custom" interrupt handler. 258 * Note that we can get ZS interrupts while this runs, 259 * and zshard may touch the interrupt_reg, so we must 260 * be careful to use the single_inst_* macros to modify 261 * the interrupt register atomically. 262 */ 263 void 264 clock_intr(cf) 265 struct clockframe cf; 266 { 267 268 /* Read the clock interrupt register. */ 269 intersil_clear(); 270 271 /* Pulse the clock intr. enable low. */ 272 single_inst_bclr_b(*interrupt_reg, IREG_CLOCK_ENAB_5); 273 single_inst_bset_b(*interrupt_reg, IREG_CLOCK_ENAB_5); 274 275 /* Read the clock intr. reg. AGAIN! */ 276 intersil_clear(); 277 278 279 { /* Entertainment! */ 280 #ifdef LED_IDLE_CHECK 281 /* With this option, LEDs move only when CPU is idle. */ 282 extern char _Idle[]; /* locore.s */ 283 if (cf.cf_pc == (long)_Idle) 284 #endif 285 leds_intr(); 286 } 287 288 /* Call common clock interrupt handler. */ 289 hardclock(&cf); 290 } 291 292 293 /* 294 * Return the best possible estimate of the time in the timeval 295 * to which tvp points. We do this by returning the current time 296 * plus the amount of time since the last clock interrupt. 297 * 298 * Check that this time is no less than any previously-reported time, 299 * which could happen around the time of a clock adjustment. Just for 300 * fun, we guarantee that the time will be greater than the value 301 * obtained by a previous call. 302 */ 303 void 304 microtime(tvp) 305 struct timeval *tvp; 306 { 307 int s = splhigh(); 308 static struct timeval lasttime; 309 310 *tvp = time; 311 tvp->tv_usec++; /* XXX */ 312 while (tvp->tv_usec >= 1000000) { 313 tvp->tv_sec++; 314 tvp->tv_usec -= 1000000; 315 } 316 if (tvp->tv_sec == lasttime.tv_sec && 317 tvp->tv_usec <= lasttime.tv_usec && 318 (tvp->tv_usec = lasttime.tv_usec + 1) >= 1000000) 319 { 320 tvp->tv_sec++; 321 tvp->tv_usec -= 1000000; 322 } 323 lasttime = *tvp; 324 splx(s); 325 } 326 327 328 /* 329 * Machine-dependent clock routines. 330 * 331 * Inittodr initializes the time of day hardware which provides 332 * date functions. 333 * 334 * Resettodr restores the time of day hardware after a time change. 335 */ 336 337 static long clk_get_secs __P((void)); 338 static void clk_set_secs __P((long)); 339 340 /* 341 * Initialize the time of day register, based on the time base 342 * which is, e.g. from a filesystem. 343 */ 344 void inittodr(fs_time) 345 time_t fs_time; 346 { 347 long diff, clk_time; 348 long long_ago = (5 * SECYR); 349 int clk_bad = 0; 350 351 /* 352 * Sanity check time from file system. 353 * If it is zero,assume filesystem time is just unknown 354 * instead of preposterous. Don't bark. 355 */ 356 if (fs_time < long_ago) { 357 /* 358 * If fs_time is zero, assume filesystem time is just 359 * unknown instead of preposterous. Don't bark. 360 */ 361 if (fs_time != 0) 362 printf("WARNING: preposterous time in file system\n"); 363 /* 1991/07/01 12:00:00 */ 364 fs_time = 21*SECYR + 186*SECDAY + SECDAY/2; 365 } 366 367 clk_time = clk_get_secs(); 368 369 /* Sanity check time from clock. */ 370 if (clk_time < long_ago) { 371 printf("WARNING: bad date in battery clock"); 372 clk_bad = 1; 373 clk_time = fs_time; 374 } else { 375 /* Does the clock time jive with the file system? */ 376 diff = clk_time - fs_time; 377 if (diff < 0) 378 diff = -diff; 379 if (diff >= (SECDAY*2)) { 380 printf("WARNING: clock %s %d days", 381 (clk_time < fs_time) ? "lost" : "gained", 382 (int) (diff / SECDAY)); 383 clk_bad = 1; 384 } 385 } 386 if (clk_bad) 387 printf(" -- CHECK AND RESET THE DATE!\n"); 388 time.tv_sec = clk_time; 389 } 390 391 /* 392 * Resettodr restores the time of day hardware after a time change. 393 */ 394 void resettodr() 395 { 396 clk_set_secs(time.tv_sec); 397 } 398 399 400 /* 401 * Now routines to get and set clock as POSIX time. 402 * Our clock keeps "years since 1/1/1968". 403 */ 404 #define CLOCK_BASE_YEAR 1968 405 static void intersil_get_dt __P((struct clock_ymdhms *)); 406 static void intersil_set_dt __P((struct clock_ymdhms *)); 407 408 static long 409 clk_get_secs() 410 { 411 struct clock_ymdhms dt; 412 long secs; 413 414 intersil_get_dt(&dt); 415 416 if ((dt.dt_hour > 24) || 417 (dt.dt_day > 31) || 418 (dt.dt_mon > 12)) 419 return (0); 420 421 dt.dt_year += CLOCK_BASE_YEAR; 422 secs = clock_ymdhms_to_secs(&dt); 423 return (secs); 424 } 425 426 static void 427 clk_set_secs(secs) 428 long secs; 429 { 430 struct clock_ymdhms dt; 431 432 clock_secs_to_ymdhms(secs, &dt); 433 dt.dt_year -= CLOCK_BASE_YEAR; 434 435 intersil_set_dt(&dt); 436 } 437 438 439 /* 440 * Routines to copy state into and out of the clock. 441 * The intersil registers have to be read or written 442 * in sequential order (or so it appears). -gwr 443 */ 444 static void 445 intersil_get_dt(struct clock_ymdhms *dt) 446 { 447 volatile struct intersil_dt *isdt; 448 int s; 449 450 isdt = &intersil_clock->counters; 451 s = splhigh(); 452 453 /* Enable read (stop time) */ 454 intersil_clock->clk_cmd_reg = 455 intersil_command(INTERSIL_CMD_STOP, INTERSIL_CMD_IENABLE); 456 457 /* Copy the info. Careful about the order! */ 458 dt->dt_sec = isdt->dt_csec; /* throw-away */ 459 dt->dt_hour = isdt->dt_hour; 460 dt->dt_min = isdt->dt_min; 461 dt->dt_sec = isdt->dt_sec; 462 dt->dt_mon = isdt->dt_month; 463 dt->dt_day = isdt->dt_day; 464 dt->dt_year = isdt->dt_year; 465 dt->dt_wday = isdt->dt_dow; 466 467 /* Done reading (time wears on) */ 468 intersil_clock->clk_cmd_reg = 469 intersil_command(INTERSIL_CMD_RUN, INTERSIL_CMD_IENABLE); 470 splx(s); 471 } 472 473 static void 474 intersil_set_dt(struct clock_ymdhms *dt) 475 { 476 volatile struct intersil_dt *isdt; 477 int s; 478 479 isdt = &intersil_clock->counters; 480 s = splhigh(); 481 482 /* Enable write (stop time) */ 483 intersil_clock->clk_cmd_reg = 484 intersil_command(INTERSIL_CMD_STOP, INTERSIL_CMD_IENABLE); 485 486 /* Copy the info. Careful about the order! */ 487 isdt->dt_csec = 0; 488 isdt->dt_hour = dt->dt_hour; 489 isdt->dt_min = dt->dt_min; 490 isdt->dt_sec = dt->dt_sec; 491 isdt->dt_month= dt->dt_mon; 492 isdt->dt_day = dt->dt_day; 493 isdt->dt_year = dt->dt_year; 494 isdt->dt_dow = dt->dt_wday; 495 496 /* Done writing (time wears on) */ 497 intersil_clock->clk_cmd_reg = 498 intersil_command(INTERSIL_CMD_RUN, INTERSIL_CMD_IENABLE); 499 splx(s); 500 } 501