1 /* $OpenBSD: clock.c,v 1.35 2021/02/23 04:44:30 cheloha Exp $ */ 2 /* $NetBSD: clock.c,v 1.1 2003/04/26 18:39:50 fvdl Exp $ */ 3 4 /*- 5 * Copyright (c) 1993, 1994 Charles M. Hannum. 6 * Copyright (c) 1990 The Regents of the University of California. 7 * All rights reserved. 8 * 9 * This code is derived from software contributed to Berkeley by 10 * William Jolitz and Don Ahn. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 3. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 * 36 * @(#)clock.c 7.2 (Berkeley) 5/12/91 37 */ 38 /* 39 * Mach Operating System 40 * Copyright (c) 1991,1990,1989 Carnegie Mellon University 41 * All Rights Reserved. 42 * 43 * Permission to use, copy, modify and distribute this software and its 44 * documentation is hereby granted, provided that both the copyright 45 * notice and this permission notice appear in all copies of the 46 * software, derivative works or modified versions, and any portions 47 * thereof, and that both notices appear in supporting documentation. 48 * 49 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" 50 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR 51 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. 52 * 53 * Carnegie Mellon requests users of this software to return to 54 * 55 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU 56 * School of Computer Science 57 * Carnegie Mellon University 58 * Pittsburgh PA 15213-3890 59 * 60 * any improvements or extensions that they make and grant Carnegie Mellon 61 * the rights to redistribute these changes. 62 */ 63 /* 64 Copyright 1988, 1989 by Intel Corporation, Santa Clara, California. 65 66 All Rights Reserved 67 68 Permission to use, copy, modify, and distribute this software and 69 its documentation for any purpose and without fee is hereby 70 granted, provided that the above copyright notice appears in all 71 copies and that both the copyright notice and this permission notice 72 appear in supporting documentation, and that the name of Intel 73 not be used in advertising or publicity pertaining to distribution 74 of the software without specific, written prior permission. 75 76 INTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE 77 INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, 78 IN NO EVENT SHALL INTEL BE LIABLE FOR ANY SPECIAL, INDIRECT, OR 79 CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 80 LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT, 81 NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION 82 WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 83 */ 84 85 /* 86 * Primitive clock interrupt routines. 87 */ 88 89 /* #define CLOCK_DEBUG */ 90 91 #include <sys/param.h> 92 #include <sys/systm.h> 93 #include <sys/time.h> 94 #include <sys/kernel.h> 95 #include <sys/timeout.h> 96 #include <sys/timetc.h> 97 98 #include <machine/cpu.h> 99 #include <machine/intr.h> 100 #include <machine/pio.h> 101 #include <machine/cpufunc.h> 102 103 #include <dev/clock_subr.h> 104 #include <dev/isa/isareg.h> 105 #include <dev/isa/isavar.h> 106 #include <dev/ic/mc146818reg.h> 107 #include <dev/ic/i8253reg.h> 108 #include <amd64/isa/nvram.h> 109 110 /* Timecounter on the i8254 */ 111 u_int32_t i8254_lastcount; 112 u_int32_t i8254_offset; 113 int i8254_ticked; 114 u_int i8254_get_timecount(struct timecounter *tc); 115 116 u_int i8254_simple_get_timecount(struct timecounter *tc); 117 118 static struct timecounter i8254_timecounter = { 119 .tc_get_timecount = i8254_get_timecount, 120 .tc_poll_pps = NULL, 121 .tc_counter_mask = ~0u, 122 .tc_frequency = TIMER_FREQ, 123 .tc_name = "i8254", 124 .tc_quality = 0, 125 .tc_priv = NULL, 126 .tc_user = 0, 127 }; 128 129 int clockintr(void *); 130 int rtcintr(void *); 131 int gettick(void); 132 void rtcdrain(void *v); 133 int rtcget(mc_todregs *); 134 void rtcput(mc_todregs *); 135 int bcdtobin(int); 136 int bintobcd(int); 137 138 u_int mc146818_read(void *, u_int); 139 void mc146818_write(void *, u_int, u_int); 140 141 u_int 142 mc146818_read(void *sc, u_int reg) 143 { 144 outb(IO_RTC, reg); 145 DELAY(1); 146 return (inb(IO_RTC+1)); 147 } 148 149 void 150 mc146818_write(void *sc, u_int reg, u_int datum) 151 { 152 outb(IO_RTC, reg); 153 DELAY(1); 154 outb(IO_RTC+1, datum); 155 DELAY(1); 156 } 157 158 struct mutex timer_mutex = MUTEX_INITIALIZER(IPL_HIGH); 159 160 u_long rtclock_tval; 161 162 void 163 startclocks(void) 164 { 165 mtx_enter(&timer_mutex); 166 rtclock_tval = TIMER_DIV(hz); 167 i8254_startclock(); 168 mtx_leave(&timer_mutex); 169 } 170 171 int 172 clockintr(void *arg) 173 { 174 struct clockframe *frame = arg; 175 176 if (timecounter->tc_get_timecount == i8254_get_timecount) { 177 if (i8254_ticked) { 178 i8254_ticked = 0; 179 } else { 180 i8254_offset += rtclock_tval; 181 i8254_lastcount = 0; 182 } 183 } 184 185 hardclock(frame); 186 187 return 1; 188 } 189 190 int 191 rtcintr(void *arg) 192 { 193 struct clockframe *frame = arg; 194 u_int stat = 0; 195 196 /* 197 * If rtcintr is 'late', next intr may happen immediately. 198 * Get them all. (Also, see comment in cpu_initclocks().) 199 */ 200 while (mc146818_read(NULL, MC_REGC) & MC_REGC_PF) { 201 statclock(frame); 202 stat = 1; 203 } 204 205 return (stat); 206 } 207 208 int 209 gettick(void) 210 { 211 u_long s; 212 u_char lo, hi; 213 214 /* Don't want someone screwing with the counter while we're here. */ 215 mtx_enter(&timer_mutex); 216 s = intr_disable(); 217 /* Select counter 0 and latch it. */ 218 outb(IO_TIMER1+TIMER_MODE, TIMER_SEL0 | TIMER_LATCH); 219 lo = inb(IO_TIMER1+TIMER_CNTR0); 220 hi = inb(IO_TIMER1+TIMER_CNTR0); 221 intr_restore(s); 222 mtx_leave(&timer_mutex); 223 return ((hi << 8) | lo); 224 } 225 226 /* 227 * Wait "n" microseconds. 228 * Relies on timer 1 counting down from (TIMER_FREQ / hz) at TIMER_FREQ Hz. 229 * Note: timer had better have been programmed before this is first used! 230 * (Note that we use `rate generator' mode, which counts at 1:1; `square 231 * wave' mode counts at 2:1). 232 */ 233 void 234 i8254_delay(int n) 235 { 236 int limit, tick, otick; 237 static const int delaytab[26] = { 238 0, 2, 3, 4, 5, 6, 7, 9, 10, 11, 239 12, 13, 15, 16, 17, 18, 19, 21, 22, 23, 240 24, 25, 27, 28, 29, 30, 241 }; 242 243 /* 244 * Read the counter first, so that the rest of the setup overhead is 245 * counted. 246 */ 247 otick = gettick(); 248 249 if (n <= 25) 250 n = delaytab[n]; 251 else { 252 /* Force 64-bit math to avoid 32-bit overflow if possible. */ 253 n = (int64_t)n * TIMER_FREQ / 1000000; 254 } 255 256 limit = TIMER_FREQ / hz; 257 258 while (n > 0) { 259 tick = gettick(); 260 if (tick > otick) 261 n -= limit - (tick - otick); 262 else 263 n -= otick - tick; 264 otick = tick; 265 } 266 } 267 268 void 269 rtcdrain(void *v) 270 { 271 struct timeout *to = (struct timeout *)v; 272 273 if (to != NULL) 274 timeout_del(to); 275 276 /* Drain any un-acknowledged RTC interrupts. */ 277 while (mc146818_read(NULL, MC_REGC) & MC_REGC_PF) 278 ; /* Nothing. */ 279 } 280 281 void 282 i8254_initclocks(void) 283 { 284 stathz = 128; 285 profhz = 1024; 286 287 /* 288 * While the clock interrupt handler isn't really MPSAFE, the 289 * i8254 can't really be used as a clock on a true MP system. 290 */ 291 isa_intr_establish(NULL, 0, IST_PULSE, IPL_CLOCK | IPL_MPSAFE, 292 clockintr, 0, "clock"); 293 isa_intr_establish(NULL, 8, IST_PULSE, IPL_STATCLOCK | IPL_MPSAFE, 294 rtcintr, 0, "rtc"); 295 296 rtcstart(); /* start the mc146818 clock */ 297 298 i8254_inittimecounter(); /* hook the interrupt-based i8254 tc */ 299 } 300 301 void 302 rtcstart(void) 303 { 304 static struct timeout rtcdrain_timeout; 305 306 mc146818_write(NULL, MC_REGA, MC_BASE_32_KHz | MC_RATE_128_Hz); 307 mc146818_write(NULL, MC_REGB, MC_REGB_24HR | MC_REGB_PIE); 308 309 /* 310 * On a number of i386 systems, the rtc will fail to start when booting 311 * the system. This is due to us missing to acknowledge an interrupt 312 * during early stages of the boot process. If we do not acknowledge 313 * the interrupt, the rtc clock will not generate further interrupts. 314 * To solve this, once interrupts are enabled, use a timeout (once) 315 * to drain any un-acknowledged rtc interrupt(s). 316 */ 317 timeout_set(&rtcdrain_timeout, rtcdrain, (void *)&rtcdrain_timeout); 318 timeout_add(&rtcdrain_timeout, 1); 319 } 320 321 void 322 rtcstop(void) 323 { 324 mc146818_write(NULL, MC_REGB, MC_REGB_24HR); 325 } 326 327 int 328 rtcget(mc_todregs *regs) 329 { 330 if ((mc146818_read(NULL, MC_REGD) & MC_REGD_VRT) == 0) /* XXX softc */ 331 return (-1); 332 MC146818_GETTOD(NULL, regs); /* XXX softc */ 333 return (0); 334 } 335 336 void 337 rtcput(mc_todregs *regs) 338 { 339 MC146818_PUTTOD(NULL, regs); /* XXX softc */ 340 } 341 342 int 343 bcdtobin(int n) 344 { 345 return (((n >> 4) & 0x0f) * 10 + (n & 0x0f)); 346 } 347 348 int 349 bintobcd(int n) 350 { 351 return ((u_char)(((n / 10) << 4) & 0xf0) | ((n % 10) & 0x0f)); 352 } 353 354 /* 355 * check whether the CMOS layout is "standard"-like (ie, not PS/2-like), 356 * to be called at splclock() 357 */ 358 static int cmoscheck(void); 359 static int 360 cmoscheck(void) 361 { 362 int i; 363 unsigned short cksum = 0; 364 365 for (i = 0x10; i <= 0x2d; i++) 366 cksum += mc146818_read(NULL, i); /* XXX softc */ 367 368 return (cksum == (mc146818_read(NULL, 0x2e) << 8) 369 + mc146818_read(NULL, 0x2f)); 370 } 371 372 /* 373 * patchable to control century byte handling: 374 * 1: always update 375 * -1: never touch 376 * 0: try to figure out itself 377 */ 378 int rtc_update_century = 0; 379 380 /* 381 * Expand a two-digit year as read from the clock chip 382 * into full width. 383 * Being here, deal with the CMOS century byte. 384 */ 385 static int centb = NVRAM_CENTURY; 386 static int clock_expandyear(int); 387 static int 388 clock_expandyear(int clockyear) 389 { 390 int s, clockcentury, cmoscentury; 391 392 clockcentury = (clockyear < 70) ? 20 : 19; 393 clockyear += 100 * clockcentury; 394 395 if (rtc_update_century < 0) 396 return (clockyear); 397 398 s = splclock(); 399 if (cmoscheck()) 400 cmoscentury = mc146818_read(NULL, NVRAM_CENTURY); 401 else 402 cmoscentury = 0; 403 splx(s); 404 if (!cmoscentury) 405 return (clockyear); 406 407 cmoscentury = bcdtobin(cmoscentury); 408 409 if (cmoscentury != clockcentury) { 410 /* XXX note: saying "century is 20" might confuse the naive. */ 411 printf("WARNING: NVRAM century is %d but RTC year is %d\n", 412 cmoscentury, clockyear); 413 414 /* Kludge to roll over century. */ 415 if ((rtc_update_century > 0) || 416 ((cmoscentury == 19) && (clockcentury == 20) && 417 (clockyear == 2000))) { 418 printf("WARNING: Setting NVRAM century to %d\n", 419 clockcentury); 420 s = splclock(); 421 mc146818_write(NULL, centb, bintobcd(clockcentury)); 422 splx(s); 423 } 424 } else if (cmoscentury == 19 && rtc_update_century == 0) 425 rtc_update_century = 1; /* will update later in resettodr() */ 426 427 return (clockyear); 428 } 429 430 int 431 rtcgettime(struct todr_chip_handle *handle, struct timeval *tv) 432 { 433 mc_todregs rtclk; 434 struct clock_ymdhms dt; 435 int s; 436 437 s = splclock(); 438 if (rtcget(&rtclk)) { 439 splx(s); 440 return EINVAL; 441 } 442 splx(s); 443 444 #ifdef CLOCK_DEBUG 445 printf("readclock: %x/%x/%x %x:%x:%x\n", rtclk[MC_YEAR], 446 rtclk[MC_MONTH], rtclk[MC_DOM], rtclk[MC_HOUR], rtclk[MC_MIN], 447 rtclk[MC_SEC]); 448 #endif 449 450 dt.dt_sec = bcdtobin(rtclk[MC_SEC]); 451 dt.dt_min = bcdtobin(rtclk[MC_MIN]); 452 dt.dt_hour = bcdtobin(rtclk[MC_HOUR]); 453 dt.dt_day = bcdtobin(rtclk[MC_DOM]); 454 dt.dt_mon = bcdtobin(rtclk[MC_MONTH]); 455 dt.dt_year = clock_expandyear(bcdtobin(rtclk[MC_YEAR])); 456 457 tv->tv_sec = clock_ymdhms_to_secs(&dt) - utc_offset; 458 tv->tv_usec = 0; 459 return 0; 460 } 461 462 int 463 rtcsettime(struct todr_chip_handle *handle, struct timeval *tv) 464 { 465 mc_todregs rtclk; 466 struct clock_ymdhms dt; 467 int century, s; 468 469 s = splclock(); 470 if (rtcget(&rtclk)) 471 memset(&rtclk, 0, sizeof(rtclk)); 472 splx(s); 473 474 clock_secs_to_ymdhms(tv->tv_sec + utc_offset, &dt); 475 476 rtclk[MC_SEC] = bintobcd(dt.dt_sec); 477 rtclk[MC_MIN] = bintobcd(dt.dt_min); 478 rtclk[MC_HOUR] = bintobcd(dt.dt_hour); 479 rtclk[MC_DOW] = dt.dt_wday + 1; 480 rtclk[MC_YEAR] = bintobcd(dt.dt_year % 100); 481 rtclk[MC_MONTH] = bintobcd(dt.dt_mon); 482 rtclk[MC_DOM] = bintobcd(dt.dt_day); 483 484 #ifdef CLOCK_DEBUG 485 printf("setclock: %x/%x/%x %x:%x:%x\n", rtclk[MC_YEAR], rtclk[MC_MONTH], 486 rtclk[MC_DOM], rtclk[MC_HOUR], rtclk[MC_MIN], rtclk[MC_SEC]); 487 #endif 488 489 s = splclock(); 490 rtcput(&rtclk); 491 if (rtc_update_century > 0) { 492 century = bintobcd(dt.dt_year / 100); 493 mc146818_write(NULL, centb, century); /* XXX softc */ 494 } 495 splx(s); 496 return 0; 497 } 498 499 extern todr_chip_handle_t todr_handle; 500 struct todr_chip_handle rtc_todr; 501 502 void 503 rtcinit(void) 504 { 505 rtc_todr.todr_gettime = rtcgettime; 506 rtc_todr.todr_settime = rtcsettime; 507 todr_handle = &rtc_todr; 508 } 509 510 void 511 setstatclockrate(int arg) 512 { 513 if (initclock_func == i8254_initclocks) { 514 if (arg == stathz) 515 mc146818_write(NULL, MC_REGA, 516 MC_BASE_32_KHz | MC_RATE_128_Hz); 517 else 518 mc146818_write(NULL, MC_REGA, 519 MC_BASE_32_KHz | MC_RATE_1024_Hz); 520 } 521 } 522 523 void 524 i8254_inittimecounter(void) 525 { 526 tc_init(&i8254_timecounter); 527 } 528 529 /* 530 * If we're using lapic to drive hardclock, we can use a simpler 531 * algorithm for the i8254 timecounters. 532 */ 533 void 534 i8254_inittimecounter_simple(void) 535 { 536 i8254_timecounter.tc_get_timecount = i8254_simple_get_timecount; 537 i8254_timecounter.tc_counter_mask = 0x7fff; 538 i8254_timecounter.tc_frequency = TIMER_FREQ; 539 540 mtx_enter(&timer_mutex); 541 rtclock_tval = 0x8000; 542 i8254_startclock(); 543 mtx_leave(&timer_mutex); 544 545 tc_init(&i8254_timecounter); 546 } 547 548 void 549 i8254_startclock(void) 550 { 551 u_long tval = rtclock_tval; 552 553 outb(IO_TIMER1 + TIMER_MODE, TIMER_SEL0 | TIMER_RATEGEN | TIMER_16BIT); 554 outb(IO_TIMER1 + TIMER_CNTR0, tval & 0xff); 555 outb(IO_TIMER1 + TIMER_CNTR0, tval >> 8); 556 } 557 558 u_int 559 i8254_simple_get_timecount(struct timecounter *tc) 560 { 561 return (rtclock_tval - gettick()); 562 } 563 564 u_int 565 i8254_get_timecount(struct timecounter *tc) 566 { 567 u_char hi, lo; 568 u_int count; 569 u_long s; 570 571 s = intr_disable(); 572 573 outb(IO_TIMER1+TIMER_MODE, TIMER_SEL0 | TIMER_LATCH); 574 lo = inb(IO_TIMER1+TIMER_CNTR0); 575 hi = inb(IO_TIMER1+TIMER_CNTR0); 576 577 count = rtclock_tval - ((hi << 8) | lo); 578 579 if (count < i8254_lastcount) { 580 i8254_ticked = 1; 581 i8254_offset += rtclock_tval; 582 } 583 i8254_lastcount = count; 584 count += i8254_offset; 585 586 intr_restore(s); 587 588 return (count); 589 } 590