1 /* $OpenBSD: clock.c,v 1.42 2023/09/17 14:50:50 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/clockintr.h> 94 #include <sys/time.h> 95 #include <sys/kernel.h> 96 #include <sys/timeout.h> 97 #include <sys/timetc.h> 98 99 #include <machine/cpu.h> 100 #include <machine/intr.h> 101 #include <machine/pio.h> 102 #include <machine/cpufunc.h> 103 104 #include <dev/clock_subr.h> 105 #include <dev/isa/isareg.h> 106 #include <dev/isa/isavar.h> 107 #include <dev/ic/mc146818reg.h> 108 #include <dev/ic/i8253reg.h> 109 #include <amd64/isa/nvram.h> 110 111 /* Timecounter on the i8254 */ 112 u_int32_t i8254_lastcount; 113 u_int32_t i8254_offset; 114 int i8254_ticked; 115 u_int i8254_get_timecount(struct timecounter *tc); 116 117 u_int i8254_simple_get_timecount(struct timecounter *tc); 118 119 static struct timecounter i8254_timecounter = { 120 .tc_get_timecount = i8254_get_timecount, 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 *frame) 173 { 174 if (timecounter->tc_get_timecount == i8254_get_timecount) { 175 if (i8254_ticked) { 176 i8254_ticked = 0; 177 } else { 178 i8254_offset += rtclock_tval; 179 i8254_lastcount = 0; 180 } 181 } 182 183 clockintr_dispatch(frame); 184 185 return 1; 186 } 187 188 int 189 rtcintr(void *frame) 190 { 191 u_int stat = 0; 192 193 /* 194 * If rtcintr is 'late', next intr may happen immediately. 195 * Get them all. (Also, see comment in cpu_initclocks().) 196 */ 197 while (mc146818_read(NULL, MC_REGC) & MC_REGC_PF) 198 stat = 1; 199 200 if (stat) 201 clockintr_dispatch(frame); 202 203 return (stat); 204 } 205 206 int 207 gettick(void) 208 { 209 u_long s; 210 u_char lo, hi; 211 212 /* Don't want someone screwing with the counter while we're here. */ 213 mtx_enter(&timer_mutex); 214 s = intr_disable(); 215 /* Select counter 0 and latch it. */ 216 outb(IO_TIMER1+TIMER_MODE, TIMER_SEL0 | TIMER_LATCH); 217 lo = inb(IO_TIMER1+TIMER_CNTR0); 218 hi = inb(IO_TIMER1+TIMER_CNTR0); 219 intr_restore(s); 220 mtx_leave(&timer_mutex); 221 return ((hi << 8) | lo); 222 } 223 224 /* 225 * Wait "n" microseconds. 226 * Relies on timer 1 counting down from (TIMER_FREQ / hz) at TIMER_FREQ Hz. 227 * Note: timer had better have been programmed before this is first used! 228 * (Note that we use `rate generator' mode, which counts at 1:1; `square 229 * wave' mode counts at 2:1). 230 */ 231 void 232 i8254_delay(int n) 233 { 234 int limit, tick, otick; 235 static const int delaytab[26] = { 236 0, 2, 3, 4, 5, 6, 7, 9, 10, 11, 237 12, 13, 15, 16, 17, 18, 19, 21, 22, 23, 238 24, 25, 27, 28, 29, 30, 239 }; 240 241 /* 242 * Read the counter first, so that the rest of the setup overhead is 243 * counted. 244 */ 245 otick = gettick(); 246 247 if (n <= 25) 248 n = delaytab[n]; 249 else { 250 /* Force 64-bit math to avoid 32-bit overflow if possible. */ 251 n = (int64_t)n * TIMER_FREQ / 1000000; 252 } 253 254 limit = TIMER_FREQ / hz; 255 256 while (n > 0) { 257 tick = gettick(); 258 if (tick > otick) 259 n -= limit - (tick - otick); 260 else 261 n -= otick - tick; 262 otick = tick; 263 } 264 } 265 266 void 267 rtcdrain(void *v) 268 { 269 struct timeout *to = (struct timeout *)v; 270 271 if (to != NULL) 272 timeout_del(to); 273 274 /* Drain any un-acknowledged RTC interrupts. */ 275 while (mc146818_read(NULL, MC_REGC) & MC_REGC_PF) 276 ; /* Nothing. */ 277 } 278 279 void 280 i8254_initclocks(void) 281 { 282 i8254_inittimecounter(); /* hook the interrupt-based i8254 tc */ 283 284 stathz = 128; 285 profhz = 1024; /* XXX does not divide into 1 billion */ 286 } 287 288 void 289 i8254_start_both_clocks(void) 290 { 291 clockintr_cpu_init(NULL); 292 293 /* 294 * While the clock interrupt handler isn't really MPSAFE, the 295 * i8254 can't really be used as a clock on a true MP system. 296 */ 297 isa_intr_establish(NULL, 0, IST_PULSE, IPL_CLOCK | IPL_MPSAFE, 298 clockintr, 0, "clock"); 299 isa_intr_establish(NULL, 8, IST_PULSE, IPL_STATCLOCK | IPL_MPSAFE, 300 rtcintr, 0, "rtc"); 301 302 rtcstart(); /* start the mc146818 clock */ 303 } 304 305 void 306 rtcstart(void) 307 { 308 static struct timeout rtcdrain_timeout; 309 310 mc146818_write(NULL, MC_REGA, MC_BASE_32_KHz | MC_RATE_128_Hz); 311 mc146818_write(NULL, MC_REGB, MC_REGB_24HR | MC_REGB_PIE); 312 313 /* 314 * On a number of i386 systems, the rtc will fail to start when booting 315 * the system. This is due to us missing to acknowledge an interrupt 316 * during early stages of the boot process. If we do not acknowledge 317 * the interrupt, the rtc clock will not generate further interrupts. 318 * To solve this, once interrupts are enabled, use a timeout (once) 319 * to drain any un-acknowledged rtc interrupt(s). 320 */ 321 timeout_set(&rtcdrain_timeout, rtcdrain, (void *)&rtcdrain_timeout); 322 timeout_add(&rtcdrain_timeout, 1); 323 } 324 325 void 326 rtcstop(void) 327 { 328 mc146818_write(NULL, MC_REGB, MC_REGB_24HR); 329 } 330 331 int 332 rtcget(mc_todregs *regs) 333 { 334 if ((mc146818_read(NULL, MC_REGD) & MC_REGD_VRT) == 0) /* XXX softc */ 335 return (-1); 336 MC146818_GETTOD(NULL, regs); /* XXX softc */ 337 return (0); 338 } 339 340 void 341 rtcput(mc_todregs *regs) 342 { 343 MC146818_PUTTOD(NULL, regs); /* XXX softc */ 344 } 345 346 int 347 bcdtobin(int n) 348 { 349 return (((n >> 4) & 0x0f) * 10 + (n & 0x0f)); 350 } 351 352 int 353 bintobcd(int n) 354 { 355 return ((u_char)(((n / 10) << 4) & 0xf0) | ((n % 10) & 0x0f)); 356 } 357 358 /* 359 * check whether the CMOS layout is "standard"-like (ie, not PS/2-like), 360 * to be called at splclock() 361 */ 362 static int cmoscheck(void); 363 static int 364 cmoscheck(void) 365 { 366 int i; 367 unsigned short cksum = 0; 368 369 for (i = 0x10; i <= 0x2d; i++) 370 cksum += mc146818_read(NULL, i); /* XXX softc */ 371 372 return (cksum == (mc146818_read(NULL, 0x2e) << 8) 373 + mc146818_read(NULL, 0x2f)); 374 } 375 376 /* 377 * patchable to control century byte handling: 378 * 1: always update 379 * -1: never touch 380 * 0: try to figure out itself 381 */ 382 int rtc_update_century = 0; 383 384 /* 385 * Expand a two-digit year as read from the clock chip 386 * into full width. 387 * Being here, deal with the CMOS century byte. 388 */ 389 static int centb = NVRAM_CENTURY; 390 static int clock_expandyear(int); 391 static int 392 clock_expandyear(int clockyear) 393 { 394 int s, clockcentury, cmoscentury; 395 396 clockcentury = (clockyear < 70) ? 20 : 19; 397 clockyear += 100 * clockcentury; 398 399 if (rtc_update_century < 0) 400 return (clockyear); 401 402 s = splclock(); 403 if (cmoscheck()) 404 cmoscentury = mc146818_read(NULL, NVRAM_CENTURY); 405 else 406 cmoscentury = 0; 407 splx(s); 408 if (!cmoscentury) 409 return (clockyear); 410 411 cmoscentury = bcdtobin(cmoscentury); 412 413 if (cmoscentury != clockcentury) { 414 /* XXX note: saying "century is 20" might confuse the naive. */ 415 printf("WARNING: NVRAM century is %d but RTC year is %d\n", 416 cmoscentury, clockyear); 417 418 /* Kludge to roll over century. */ 419 if ((rtc_update_century > 0) || 420 ((cmoscentury == 19) && (clockcentury == 20) && 421 (clockyear == 2000))) { 422 printf("WARNING: Setting NVRAM century to %d\n", 423 clockcentury); 424 s = splclock(); 425 mc146818_write(NULL, centb, bintobcd(clockcentury)); 426 splx(s); 427 } 428 } else if (cmoscentury == 19 && rtc_update_century == 0) 429 rtc_update_century = 1; /* will update later in resettodr() */ 430 431 return (clockyear); 432 } 433 434 int 435 rtcgettime(struct todr_chip_handle *handle, struct timeval *tv) 436 { 437 mc_todregs rtclk; 438 struct clock_ymdhms dt; 439 int s; 440 441 s = splclock(); 442 if (rtcget(&rtclk)) { 443 splx(s); 444 return EINVAL; 445 } 446 splx(s); 447 448 #ifdef CLOCK_DEBUG 449 printf("readclock: %x/%x/%x %x:%x:%x\n", rtclk[MC_YEAR], 450 rtclk[MC_MONTH], rtclk[MC_DOM], rtclk[MC_HOUR], rtclk[MC_MIN], 451 rtclk[MC_SEC]); 452 #endif 453 454 dt.dt_sec = bcdtobin(rtclk[MC_SEC]); 455 dt.dt_min = bcdtobin(rtclk[MC_MIN]); 456 dt.dt_hour = bcdtobin(rtclk[MC_HOUR]); 457 dt.dt_day = bcdtobin(rtclk[MC_DOM]); 458 dt.dt_mon = bcdtobin(rtclk[MC_MONTH]); 459 dt.dt_year = clock_expandyear(bcdtobin(rtclk[MC_YEAR])); 460 461 tv->tv_sec = clock_ymdhms_to_secs(&dt) - utc_offset; 462 tv->tv_usec = 0; 463 return 0; 464 } 465 466 int 467 rtcsettime(struct todr_chip_handle *handle, struct timeval *tv) 468 { 469 mc_todregs rtclk; 470 struct clock_ymdhms dt; 471 int century, s; 472 473 s = splclock(); 474 if (rtcget(&rtclk)) 475 memset(&rtclk, 0, sizeof(rtclk)); 476 splx(s); 477 478 clock_secs_to_ymdhms(tv->tv_sec + utc_offset, &dt); 479 480 rtclk[MC_SEC] = bintobcd(dt.dt_sec); 481 rtclk[MC_MIN] = bintobcd(dt.dt_min); 482 rtclk[MC_HOUR] = bintobcd(dt.dt_hour); 483 rtclk[MC_DOW] = dt.dt_wday + 1; 484 rtclk[MC_YEAR] = bintobcd(dt.dt_year % 100); 485 rtclk[MC_MONTH] = bintobcd(dt.dt_mon); 486 rtclk[MC_DOM] = bintobcd(dt.dt_day); 487 488 #ifdef CLOCK_DEBUG 489 printf("setclock: %x/%x/%x %x:%x:%x\n", rtclk[MC_YEAR], rtclk[MC_MONTH], 490 rtclk[MC_DOM], rtclk[MC_HOUR], rtclk[MC_MIN], rtclk[MC_SEC]); 491 #endif 492 493 s = splclock(); 494 rtcput(&rtclk); 495 if (rtc_update_century > 0) { 496 century = bintobcd(dt.dt_year / 100); 497 mc146818_write(NULL, centb, century); /* XXX softc */ 498 } 499 splx(s); 500 return 0; 501 } 502 503 struct todr_chip_handle rtc_todr; 504 505 void 506 rtcinit(void) 507 { 508 rtc_todr.todr_gettime = rtcgettime; 509 rtc_todr.todr_settime = rtcsettime; 510 rtc_todr.todr_quality = 0; 511 todr_attach(&rtc_todr); 512 } 513 514 void 515 setstatclockrate(int arg) 516 { 517 if (initclock_func == i8254_initclocks) { 518 if (arg == stathz) 519 mc146818_write(NULL, MC_REGA, 520 MC_BASE_32_KHz | MC_RATE_128_Hz); 521 else 522 mc146818_write(NULL, MC_REGA, 523 MC_BASE_32_KHz | MC_RATE_1024_Hz); 524 } 525 } 526 527 void 528 i8254_inittimecounter(void) 529 { 530 tc_init(&i8254_timecounter); 531 } 532 533 /* 534 * If we're using lapic to drive hardclock, we can use a simpler 535 * algorithm for the i8254 timecounters. 536 */ 537 void 538 i8254_inittimecounter_simple(void) 539 { 540 i8254_timecounter.tc_get_timecount = i8254_simple_get_timecount; 541 i8254_timecounter.tc_counter_mask = 0x7fff; 542 i8254_timecounter.tc_frequency = TIMER_FREQ; 543 544 mtx_enter(&timer_mutex); 545 rtclock_tval = 0x8000; 546 i8254_startclock(); 547 mtx_leave(&timer_mutex); 548 549 tc_init(&i8254_timecounter); 550 } 551 552 void 553 i8254_startclock(void) 554 { 555 u_long tval = rtclock_tval; 556 557 outb(IO_TIMER1 + TIMER_MODE, TIMER_SEL0 | TIMER_RATEGEN | TIMER_16BIT); 558 outb(IO_TIMER1 + TIMER_CNTR0, tval & 0xff); 559 outb(IO_TIMER1 + TIMER_CNTR0, tval >> 8); 560 } 561 562 u_int 563 i8254_simple_get_timecount(struct timecounter *tc) 564 { 565 return (rtclock_tval - gettick()); 566 } 567 568 u_int 569 i8254_get_timecount(struct timecounter *tc) 570 { 571 u_char hi, lo; 572 u_int count; 573 u_long s; 574 575 s = intr_disable(); 576 577 outb(IO_TIMER1+TIMER_MODE, TIMER_SEL0 | TIMER_LATCH); 578 lo = inb(IO_TIMER1+TIMER_CNTR0); 579 hi = inb(IO_TIMER1+TIMER_CNTR0); 580 581 count = rtclock_tval - ((hi << 8) | lo); 582 583 if (count < i8254_lastcount) { 584 i8254_ticked = 1; 585 i8254_offset += rtclock_tval; 586 } 587 i8254_lastcount = count; 588 count += i8254_offset; 589 590 intr_restore(s); 591 592 return (count); 593 } 594