1 /* $NetBSD: clock.c,v 1.9 1997/03/05 22:22:11 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 Mostek48t02 48 */ 49 50 #include <sys/param.h> 51 #include <sys/systm.h> 52 #include <sys/time.h> 53 #include <sys/kernel.h> 54 #include <sys/device.h> 55 56 #include <m68k/asm_single.h> 57 58 #include <machine/autoconf.h> 59 #include <machine/cpu.h> 60 #include <machine/mon.h> 61 #include <machine/obio.h> 62 #include <machine/machdep.h> 63 64 #include <dev/clock_subr.h> 65 66 #include <sun3/sun3/interreg.h> 67 #include "mostek48t02.h" 68 69 #define CLOCK_PRI 5 70 #define IREG_CLK_BITS (IREG_CLOCK_ENAB_7 | IREG_CLOCK_ENAB_5) 71 72 void _isr_clock __P((void)); /* in locore.s */ 73 void clock_intr __P((struct clockframe)); 74 75 static volatile void *clock_va; 76 77 static int clock_match __P((struct device *, struct cfdata *, void *args)); 78 static void clock_attach __P((struct device *, struct device *, void *)); 79 80 struct cfattach clock_ca = { 81 sizeof(struct device), clock_match, clock_attach 82 }; 83 84 struct cfdriver clock_cd = { 85 NULL, "clock", DV_DULL 86 }; 87 88 89 /* 90 * This is called very early (by obio_init()) but after 91 * intreg_init() has found the PROM mapping for the 92 * interrupt register and cleared it. 93 */ 94 void 95 clock_init() 96 { 97 /* Yes, use the EEPROM address. It is the same H/W device. */ 98 clock_va = obio_find_mapping(OBIO_EEPROM, sizeof(struct clockreg)); 99 if (!clock_va) { 100 mon_printf("clock_init\n"); 101 sunmon_abort(); 102 } 103 } 104 105 /* 106 * XXX Need to determine which type of clock we have! 107 * XXX The Sun3/80 always has the MK4802, while the 108 * XXX Sun3/470 can (reportedly) have that or the old 109 * XXX intersil7170. Should have two clock drivers... 110 */ 111 static int 112 clock_match(parent, cf, args) 113 struct device *parent; 114 struct cfdata *cf; 115 void *args; 116 { 117 struct confargs *ca = args; 118 119 /* This driver only supports one unit. */ 120 if (cf->cf_unit != 0) 121 return (0); 122 123 /* Validate the given address. */ 124 if (ca->ca_paddr != OBIO_CLOCK2) 125 return (0); 126 127 /* Default interrupt priority. */ 128 if (ca->ca_intpri == -1) 129 ca->ca_intpri = CLOCK_PRI; 130 131 return (1); 132 } 133 134 static void 135 clock_attach(parent, self, args) 136 struct device *parent; 137 struct device *self; 138 void *args; 139 { 140 141 printf("\n"); 142 143 /* 144 * Can not hook up the ISR until cpu_initclocks() 145 * because hardclock is not ready until then. 146 * For now, the handler is _isr_autovec(), which 147 * will complain if it gets clock interrupts. 148 */ 149 } 150 151 /* 152 * Set and/or clear the desired clock bits in the interrupt 153 * register. We have to be extremely careful that we do it 154 * in such a manner that we don't get ourselves lost. 155 * XXX: Watch out! It's really easy to break this! 156 */ 157 void 158 set_clk_mode(on, off, enable_clk) 159 u_char on, off; 160 int enable_clk; 161 { 162 register u_char interreg; 163 164 /* 165 * If we have not yet mapped the register, 166 * then we do not want to do any of this... 167 */ 168 if (!interrupt_reg) 169 return; 170 171 #ifdef DIAGNOSTIC 172 /* Assertion: were are at splhigh! */ 173 if ((getsr() & PSL_IPL) < PSL_IPL7) 174 panic("set_clk_mode: bad ipl"); 175 #endif 176 177 /* 178 * make sure that we are only playing w/ 179 * clock interrupt register bits 180 */ 181 on &= IREG_CLK_BITS; 182 off &= IREG_CLK_BITS; 183 184 /* First, turn off the "master" enable bit. */ 185 single_inst_bclr_b(*interrupt_reg, IREG_ALL_ENAB); 186 187 /* 188 * Save the current interrupt register clock bits, 189 * and turn off/on the requested bits in the copy. 190 */ 191 interreg = *interrupt_reg & IREG_CLK_BITS; 192 interreg &= ~off; 193 interreg |= on; 194 195 /* Clear the CLK5 and CLK7 bits to clear the flip-flops. */ 196 single_inst_bclr_b(*interrupt_reg, IREG_CLK_BITS); 197 198 #ifdef SUN3_470 199 if (intersil_va) { 200 /* 201 * Then disable clock interrupts, and read the clock's 202 * interrupt register to clear any pending signals there. 203 */ 204 intersil_clock->clk_cmd_reg = 205 intersil_command(INTERSIL_CMD_RUN, INTERSIL_CMD_IDISABLE); 206 intersil_clear(); 207 } 208 #endif /* SUN3_470 */ 209 210 /* Set the requested bits in the interrupt register. */ 211 single_inst_bset_b(*interrupt_reg, interreg); 212 213 #ifdef SUN3_470 214 /* Turn the clock back on (maybe) */ 215 if (intersil_va && enable_clk) 216 intersil_clock->clk_cmd_reg = 217 intersil_command(INTERSIL_CMD_RUN, INTERSIL_CMD_IENABLE); 218 #endif /* SUN3_470 */ 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 /* Pulse the clock intr. enable low. */ 269 single_inst_bclr_b(*interrupt_reg, IREG_CLOCK_ENAB_5); 270 single_inst_bset_b(*interrupt_reg, IREG_CLOCK_ENAB_5); 271 272 /* Call common clock interrupt handler. */ 273 hardclock(&cf); 274 275 /* No LED frobbing on the 3/80 */ 276 } 277 278 279 /* 280 * Return the best possible estimate of the time in the timeval 281 * to which tvp points. We do this by returning the current time 282 * plus the amount of time since the last clock interrupt. 283 * 284 * Check that this time is no less than any previously-reported time, 285 * which could happen around the time of a clock adjustment. Just for 286 * fun, we guarantee that the time will be greater than the value 287 * obtained by a previous call. 288 */ 289 void 290 microtime(tvp) 291 register struct timeval *tvp; 292 { 293 int s = splhigh(); 294 static struct timeval lasttime; 295 296 *tvp = time; 297 tvp->tv_usec++; /* XXX */ 298 while (tvp->tv_usec > 1000000) { 299 tvp->tv_sec++; 300 tvp->tv_usec -= 1000000; 301 } 302 if (tvp->tv_sec == lasttime.tv_sec && 303 tvp->tv_usec <= lasttime.tv_usec && 304 (tvp->tv_usec = lasttime.tv_usec + 1) > 1000000) 305 { 306 tvp->tv_sec++; 307 tvp->tv_usec -= 1000000; 308 } 309 lasttime = *tvp; 310 splx(s); 311 } 312 313 314 /* 315 * Machine-dependent clock routines. 316 * 317 * Inittodr initializes the time of day hardware which provides 318 * date functions. 319 * 320 * Resettodr restores the time of day hardware after a time change. 321 */ 322 323 static long clk_get_secs(void); 324 static void clk_set_secs(long); 325 326 /* 327 * Initialize the time of day register, based on the time base 328 * which is, e.g. from a filesystem. 329 */ 330 void inittodr(fs_time) 331 time_t fs_time; 332 { 333 long diff, clk_time; 334 long long_ago = (5 * SECYR); 335 int clk_bad = 0; 336 337 /* 338 * Sanity check time from file system. 339 * If it is zero,assume filesystem time is just unknown 340 * instead of preposterous. Don't bark. 341 */ 342 if (fs_time < long_ago) { 343 /* 344 * If fs_time is zero, assume filesystem time is just 345 * unknown instead of preposterous. Don't bark. 346 */ 347 if (fs_time != 0) 348 printf("WARNING: preposterous time in file system\n"); 349 /* 1991/07/01 12:00:00 */ 350 fs_time = 21*SECYR + 186*SECDAY + SECDAY/2; 351 } 352 353 clk_time = clk_get_secs(); 354 355 /* Sanity check time from clock. */ 356 if (clk_time < long_ago) { 357 printf("WARNING: bad date in battery clock"); 358 clk_bad = 1; 359 clk_time = fs_time; 360 } else { 361 /* Does the clock time jive with the file system? */ 362 diff = clk_time - fs_time; 363 if (diff < 0) 364 diff = -diff; 365 if (diff >= (SECDAY*2)) { 366 printf("WARNING: clock %s %d days", 367 (clk_time < fs_time) ? "lost" : "gained", 368 (int) (diff / SECDAY)); 369 clk_bad = 1; 370 } 371 } 372 if (clk_bad) 373 printf(" -- CHECK AND RESET THE DATE!\n"); 374 time.tv_sec = clk_time; 375 } 376 377 /* 378 * Resettodr restores the time of day hardware after a time change. 379 */ 380 void resettodr() 381 { 382 clk_set_secs(time.tv_sec); 383 } 384 385 386 /* 387 * Routines to copy state into and out of the clock. 388 * The clock CSR has to be set for read or write. 389 */ 390 static void 391 clk_get_dt(struct clock_ymdhms *dt) 392 { 393 volatile struct clockreg *cl = clock_va; 394 int s; 395 396 s = splhigh(); 397 398 /* enable read (stop time) */ 399 cl->cl_csr |= CLK_READ; 400 401 /* Copy the info */ 402 dt->dt_sec = cl->cl_sec; 403 dt->dt_min = cl->cl_min; 404 dt->dt_hour = cl->cl_hour; 405 dt->dt_wday = cl->cl_wday; 406 dt->dt_day = cl->cl_mday; 407 dt->dt_mon = cl->cl_month; 408 dt->dt_year = cl->cl_year; 409 410 /* Done reading (time wears on) */ 411 cl->cl_csr &= ~CLK_READ; 412 splx(s); 413 } 414 415 static void 416 clk_set_dt(struct clock_ymdhms *dt) 417 { 418 volatile struct clockreg *cl = clock_va; 419 int s; 420 421 s = splhigh(); 422 /* enable write */ 423 cl->cl_csr |= CLK_WRITE; 424 425 /* Copy the info */ 426 cl->cl_sec = dt->dt_sec; 427 cl->cl_min = dt->dt_min; 428 cl->cl_hour = dt->dt_hour; 429 cl->cl_wday = dt->dt_wday; 430 cl->cl_mday = dt->dt_day; 431 cl->cl_month = dt->dt_mon; 432 cl->cl_year = dt->dt_year; 433 434 /* load them up */ 435 cl->cl_csr &= ~CLK_WRITE; 436 splx(s); 437 } 438 439 440 /* 441 * Now routines to get and set clock as POSIX time. 442 * Our clock keeps "years since 1/1/1968". 443 */ 444 #define CLOCK_BASE_YEAR 1968 445 446 static long 447 clk_get_secs() 448 { 449 struct clock_ymdhms dt; 450 long secs; 451 452 clk_get_dt(&dt); 453 454 /* Convert BCD values to binary. */ 455 dt.dt_sec = FROMBCD(dt.dt_sec); 456 dt.dt_min = FROMBCD(dt.dt_min); 457 dt.dt_hour = FROMBCD(dt.dt_hour); 458 dt.dt_day = FROMBCD(dt.dt_day); 459 dt.dt_mon = FROMBCD(dt.dt_mon); 460 dt.dt_year = FROMBCD(dt.dt_year); 461 462 if ((dt.dt_hour > 24) || 463 (dt.dt_day > 31) || 464 (dt.dt_mon > 12)) 465 return (0); 466 467 dt.dt_year += CLOCK_BASE_YEAR; 468 secs = clock_ymdhms_to_secs(&dt); 469 return (secs); 470 } 471 472 static void 473 clk_set_secs(secs) 474 long secs; 475 { 476 struct clock_ymdhms dt; 477 478 clock_secs_to_ymdhms(secs, &dt); 479 dt.dt_year -= CLOCK_BASE_YEAR; 480 481 /* Convert binary values to BCD. */ 482 dt.dt_sec = TOBCD(dt.dt_sec); 483 dt.dt_min = TOBCD(dt.dt_min); 484 dt.dt_hour = TOBCD(dt.dt_hour); 485 dt.dt_day = TOBCD(dt.dt_day); 486 dt.dt_mon = TOBCD(dt.dt_mon); 487 dt.dt_year = TOBCD(dt.dt_year); 488 489 clk_set_dt(&dt); 490 } 491