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