1 /* $NetBSD: clock.c,v 1.60 2019/06/29 16:37:49 tsutsui Exp $ */ 2 3 /* 4 * Copyright (c) 1988 University of Utah. 5 * Copyright (c) 1982, 1990 The Regents of the University of California. 6 * All rights reserved. 7 * 8 * This code is derived from software contributed to Berkeley by 9 * the Systems Programming Group of the University of Utah Computer 10 * Science Department. 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 * from: Utah $Hdr: clock.c 1.18 91/01/21$ 37 * 38 * @(#)clock.c 7.6 (Berkeley) 5/7/91 39 */ 40 41 #include <sys/cdefs.h> 42 __KERNEL_RCSID(0, "$NetBSD: clock.c,v 1.60 2019/06/29 16:37:49 tsutsui Exp $"); 43 44 #include <sys/param.h> 45 #include <sys/kernel.h> 46 #include <sys/systm.h> 47 #include <sys/device.h> 48 #include <sys/uio.h> 49 #include <sys/conf.h> 50 #include <sys/proc.h> 51 #include <sys/event.h> 52 #include <sys/timetc.h> 53 54 #include <dev/clock_subr.h> 55 56 #include <machine/psl.h> 57 #include <machine/cpu.h> 58 #include <machine/iomap.h> 59 #include <machine/mfp.h> 60 #include <atari/dev/clockreg.h> 61 #include <atari/dev/clockvar.h> 62 #include <atari/atari/device.h> 63 64 #if defined(GPROF) && defined(PROFTIMER) 65 #include <machine/profile.h> 66 #endif 67 68 #include "ioconf.h" 69 70 static int atari_rtc_get(todr_chip_handle_t, struct clock_ymdhms *); 71 static int atari_rtc_set(todr_chip_handle_t, struct clock_ymdhms *); 72 73 /* 74 * The MFP clock runs at 2457600Hz. We use a {system,stat,prof}clock divider 75 * of 200. Therefore the timer runs at an effective rate of: 76 * 2457600/200 = 12288Hz. 77 */ 78 #define CLOCK_HZ 12288 79 80 static u_int clk_getcounter(struct timecounter *); 81 82 static struct timecounter clk_timecounter = { 83 clk_getcounter, /* get_timecount */ 84 0, /* no poll_pps */ 85 ~0u, /* counter_mask */ 86 CLOCK_HZ, /* frequency */ 87 "clock", /* name, overriden later */ 88 100, /* quality */ 89 NULL, /* prev */ 90 NULL, /* next */ 91 }; 92 93 /* 94 * Machine-dependent clock routines. 95 * 96 * Inittodr initializes the time of day hardware which provides 97 * date functions. 98 * 99 * Resettodr restores the time of day hardware after a time change. 100 */ 101 102 struct clock_softc { 103 device_t sc_dev; 104 int sc_flags; 105 struct todr_chip_handle sc_handle; 106 }; 107 108 /* 109 * 'sc_flags' state info. Only used by the rtc-device functions. 110 */ 111 #define RTC_OPEN 1 112 113 dev_type_open(rtcopen); 114 dev_type_close(rtcclose); 115 dev_type_read(rtcread); 116 dev_type_write(rtcwrite); 117 118 static void clockattach(device_t, device_t, void *); 119 static int clockmatch(device_t, cfdata_t, void *); 120 121 CFATTACH_DECL_NEW(clock, sizeof(struct clock_softc), 122 clockmatch, clockattach, NULL, NULL); 123 124 const struct cdevsw rtc_cdevsw = { 125 .d_open = rtcopen, 126 .d_close = rtcclose, 127 .d_read = rtcread, 128 .d_write = rtcwrite, 129 .d_ioctl = noioctl, 130 .d_stop = nostop, 131 .d_tty = notty, 132 .d_poll = nopoll, 133 .d_mmap = nommap, 134 .d_kqfilter = nokqfilter, 135 .d_discard = nodiscard, 136 .d_flag = 0 137 }; 138 139 void statintr(struct clockframe); 140 141 static int twodigits(char *, int); 142 143 static int divisor; /* Systemclock divisor */ 144 145 /* 146 * Statistics and profile clock intervals and variances. Variance must 147 * be a power of 2. Since this gives us an even number, not an odd number, 148 * we discard one case and compensate. That is, a variance of 64 would 149 * give us offsets in [0..63]. Instead, we take offsets in [1..63]. 150 * This is symmetric around the point 32, or statvar/2, and thus averages 151 * to that value (assuming uniform random numbers). 152 */ 153 #ifdef STATCLOCK 154 static int statvar = 32; /* {stat,prof}clock variance */ 155 static int statmin; /* statclock divisor - variance/2 */ 156 static int profmin; /* profclock divisor - variance/2 */ 157 static int clk2min; /* current, from above choices */ 158 #endif 159 160 static int 161 clockmatch(device_t parent, cfdata_t cf, void *aux) 162 { 163 164 if (!strcmp("clock", aux)) 165 return 1; 166 return 0; 167 } 168 169 /* 170 * Start the real-time clock. 171 */ 172 static void 173 clockattach(device_t parent, device_t self, void *aux) 174 { 175 struct clock_softc *sc = device_private(self); 176 struct todr_chip_handle *tch; 177 178 sc->sc_dev = self; 179 tch = &sc->sc_handle; 180 tch->todr_gettime_ymdhms = atari_rtc_get; 181 tch->todr_settime_ymdhms = atari_rtc_set; 182 tch->todr_setwen = NULL; 183 184 todr_attach(tch); 185 186 sc->sc_flags = 0; 187 188 /* 189 * Initialize Timer-A in the ST-MFP. We use a divisor of 200. 190 * The MFP clock runs at 2457600Hz. Therefore the timer runs 191 * at an effective rate of: 2457600/200 = 12288Hz. The 192 * following expression works for 48, 64 or 96 hz. 193 */ 194 divisor = CLOCK_HZ/hz; 195 MFP->mf_tacr = 0; /* Stop timer */ 196 MFP->mf_iera &= ~IA_TIMA; /* Disable timer interrupts */ 197 MFP->mf_tadr = divisor; /* Set divisor */ 198 199 clk_timecounter.tc_frequency = CLOCK_HZ; 200 201 if (hz != 48 && hz != 64 && hz != 96) { /* XXX */ 202 printf (": illegal value %d for systemclock, reset to %d\n\t", 203 hz, 64); 204 hz = 64; 205 } 206 printf(": system hz %d timer-A divisor 200/%d\n", hz, divisor); 207 tc_init(&clk_timecounter); 208 209 #ifdef STATCLOCK 210 if ((stathz == 0) || (stathz > hz) || (CLOCK_HZ % stathz)) 211 stathz = hz; 212 if ((profhz == 0) || (profhz > (hz << 1)) || (CLOCK_HZ % profhz)) 213 profhz = hz << 1; 214 215 MFP->mf_tcdcr &= 0x7; /* Stop timer */ 216 MFP->mf_ierb &= ~IB_TIMC; /* Disable timer inter. */ 217 MFP->mf_tcdr = CLOCK_HZ/stathz; /* Set divisor */ 218 219 statmin = (CLOCK_HZ/stathz) - (statvar >> 1); 220 profmin = (CLOCK_HZ/profhz) - (statvar >> 1); 221 clk2min = statmin; 222 #endif /* STATCLOCK */ 223 } 224 225 void 226 cpu_initclocks(void) 227 { 228 229 MFP->mf_tacr = T_Q200; /* Start timer */ 230 MFP->mf_ipra = (u_int8_t)~IA_TIMA;/* Clear pending interrupts */ 231 MFP->mf_iera |= IA_TIMA; /* Enable timer interrupts */ 232 MFP->mf_imra |= IA_TIMA; /* ..... */ 233 234 #ifdef STATCLOCK 235 MFP->mf_tcdcr = (MFP->mf_tcdcr & 0x7) | (T_Q200<<4); /* Start */ 236 MFP->mf_iprb = (u_int8_t)~IB_TIMC;/* Clear pending interrupts */ 237 MFP->mf_ierb |= IB_TIMC; /* Enable timer interrupts */ 238 MFP->mf_imrb |= IB_TIMC; /* ..... */ 239 #endif /* STATCLOCK */ 240 } 241 242 void 243 setstatclockrate(int newhz) 244 { 245 246 #ifdef STATCLOCK 247 if (newhz == stathz) 248 clk2min = statmin; 249 else clk2min = profmin; 250 #endif /* STATCLOCK */ 251 } 252 253 #ifdef STATCLOCK 254 void 255 statintr(struct clockframe frame) 256 { 257 register int var, r; 258 259 var = statvar - 1; 260 do { 261 r = random() & var; 262 } while (r == 0); 263 264 /* 265 * Note that we are always lagging behind as the new divisor 266 * value will not be loaded until the next interrupt. This 267 * shouldn't disturb the median frequency (I think ;-) ) as 268 * only the value used when switching frequencies is used 269 * twice. This shouldn't happen very often. 270 */ 271 MFP->mf_tcdr = clk2min + r; 272 273 statclock(&frame); 274 } 275 #endif /* STATCLOCK */ 276 277 static u_int 278 clk_getcounter(struct timecounter *tc) 279 { 280 uint32_t delta, count, cur_hardclock; 281 uint8_t ipra, tadr; 282 int s; 283 static uint32_t lastcount; 284 285 s = splhigh(); 286 cur_hardclock = hardclock_ticks; 287 ipra = MFP->mf_ipra; 288 tadr = MFP->mf_tadr; 289 delta = divisor - tadr; 290 291 if (ipra & IA_TIMA) 292 delta += divisor; 293 splx(s); 294 295 count = (divisor * cur_hardclock) + delta; 296 if ((int32_t)(count - lastcount) < 0) { 297 /* XXX wrapped; maybe hardclock() is blocked more than 2/HZ */ 298 count = lastcount + 1; 299 } 300 lastcount = count; 301 302 return count; 303 } 304 305 #define TIMB_FREQ 614400 306 #define TIMB_LIMIT 256 307 308 void 309 init_delay(void) 310 { 311 312 /* 313 * Initialize Timer-B in the ST-MFP. This timer is used by 314 * the 'delay' function below. This timer is setup to be 315 * continueously counting from 255 back to zero at a 316 * frequency of 614400Hz. We do this *early* in the 317 * initialisation process. 318 */ 319 MFP->mf_tbcr = 0; /* Stop timer */ 320 MFP->mf_iera &= ~IA_TIMB; /* Disable timer interrupts */ 321 MFP->mf_tbdr = 0; 322 MFP->mf_tbcr = T_Q004; /* Start timer */ 323 } 324 325 /* 326 * Wait "n" microseconds. 327 * Relies on MFP-Timer B counting down from TIMB_LIMIT at TIMB_FREQ Hz. 328 * Note: timer had better have been programmed before this is first used! 329 */ 330 void 331 delay(unsigned int n) 332 { 333 int ticks, otick, remaining; 334 335 /* 336 * Read the counter first, so that the rest of the setup overhead is 337 * counted. 338 */ 339 otick = MFP->mf_tbdr; 340 341 if (n <= UINT_MAX / TIMB_FREQ) { 342 /* 343 * For unsigned arithmetic, division can be replaced with 344 * multiplication with the inverse and a shift. 345 */ 346 remaining = n * TIMB_FREQ / 1000000; 347 } else { 348 /* This is a very long delay. 349 * Being slow here doesn't matter. 350 */ 351 remaining = (unsigned long long) n * TIMB_FREQ / 1000000; 352 } 353 354 while (remaining > 0) { 355 ticks = MFP->mf_tbdr; 356 if (ticks > otick) 357 remaining -= TIMB_LIMIT - (ticks - otick); 358 else 359 remaining -= otick - ticks; 360 otick = ticks; 361 } 362 } 363 364 #ifdef GPROF 365 /* 366 * profclock() is expanded in line in lev6intr() unless profiling kernel. 367 * Assumes it is called with clock interrupts blocked. 368 */ 369 profclock(void *pc, int ps) 370 { 371 372 /* 373 * Came from user mode. 374 * If this process is being profiled record the tick. 375 */ 376 if (USERMODE(ps)) { 377 if (p->p_stats.p_prof.pr_scale) 378 addupc(pc, &curproc->p_stats.p_prof, 1); 379 } 380 /* 381 * Came from kernel (supervisor) mode. 382 * If we are profiling the kernel, record the tick. 383 */ 384 else if (profiling < 2) { 385 register int s = pc - s_lowpc; 386 387 if (s < s_textsize) 388 kcount[s / (HISTFRACTION * sizeof(*kcount))]++; 389 } 390 /* 391 * Kernel profiling was on but has been disabled. 392 * Mark as no longer profiling kernel and if all profiling done, 393 * disable the clock. 394 */ 395 if (profiling && (profon & PRF_KERNEL)) { 396 profon &= ~PRF_KERNEL; 397 if (profon == PRF_NONE) 398 stopprofclock(); 399 } 400 } 401 #endif 402 403 /*********************************************************************** 404 * Real Time Clock support * 405 ***********************************************************************/ 406 407 u_int mc146818_read(void *cookie, u_int regno) 408 { 409 struct rtc *rtc = cookie; 410 411 rtc->rtc_regno = regno; 412 return rtc->rtc_data & 0xff; 413 } 414 415 void mc146818_write(void *cookie, u_int regno, u_int value) 416 { 417 struct rtc *rtc = cookie; 418 419 rtc->rtc_regno = regno; 420 rtc->rtc_data = value; 421 } 422 423 static int 424 atari_rtc_get(todr_chip_handle_t todr, struct clock_ymdhms *dtp) 425 { 426 int sps; 427 mc_todregs clkregs; 428 u_int regb; 429 430 sps = splhigh(); 431 regb = mc146818_read(RTC, MC_REGB); 432 MC146818_GETTOD(RTC, &clkregs); 433 splx(sps); 434 435 regb &= MC_REGB_24HR|MC_REGB_BINARY; 436 if (regb != (MC_REGB_24HR|MC_REGB_BINARY)) { 437 printf("Error: Nonstandard RealTimeClock Configuration -" 438 " value ignored\n" 439 " A write to /dev/rtc will correct this.\n"); 440 return 0; 441 } 442 if (clkregs[MC_SEC] > 59) 443 return -1; 444 if (clkregs[MC_MIN] > 59) 445 return -1; 446 if (clkregs[MC_HOUR] > 23) 447 return -1; 448 if (range_test(clkregs[MC_DOM], 1, 31)) 449 return -1; 450 if (range_test(clkregs[MC_MONTH], 1, 12)) 451 return -1; 452 if (clkregs[MC_YEAR] > 99) 453 return -1; 454 455 dtp->dt_year = clkregs[MC_YEAR] + GEMSTARTOFTIME; 456 dtp->dt_mon = clkregs[MC_MONTH]; 457 dtp->dt_day = clkregs[MC_DOM]; 458 dtp->dt_hour = clkregs[MC_HOUR]; 459 dtp->dt_min = clkregs[MC_MIN]; 460 dtp->dt_sec = clkregs[MC_SEC]; 461 462 return 0; 463 } 464 465 static int 466 atari_rtc_set(todr_chip_handle_t todr, struct clock_ymdhms *dtp) 467 { 468 int s; 469 mc_todregs clkregs; 470 471 clkregs[MC_YEAR] = dtp->dt_year - GEMSTARTOFTIME; 472 clkregs[MC_MONTH] = dtp->dt_mon; 473 clkregs[MC_DOM] = dtp->dt_day; 474 clkregs[MC_HOUR] = dtp->dt_hour; 475 clkregs[MC_MIN] = dtp->dt_min; 476 clkregs[MC_SEC] = dtp->dt_sec; 477 478 s = splclock(); 479 MC146818_PUTTOD(RTC, &clkregs); 480 splx(s); 481 482 return 0; 483 } 484 485 /*********************************************************************** 486 * RTC-device support * 487 ***********************************************************************/ 488 int 489 rtcopen(dev_t dev, int flag, int mode, struct lwp *l) 490 { 491 int unit = minor(dev); 492 struct clock_softc *sc; 493 494 sc = device_lookup_private(&clock_cd, unit); 495 if (sc == NULL) 496 return ENXIO; 497 if (sc->sc_flags & RTC_OPEN) 498 return EBUSY; 499 500 sc->sc_flags = RTC_OPEN; 501 return 0; 502 } 503 504 int 505 rtcclose(dev_t dev, int flag, int mode, struct lwp *l) 506 { 507 int unit = minor(dev); 508 struct clock_softc *sc = device_lookup_private(&clock_cd, unit); 509 510 sc->sc_flags = 0; 511 return 0; 512 } 513 514 int 515 rtcread(dev_t dev, struct uio *uio, int flags) 516 { 517 mc_todregs clkregs; 518 int s, length; 519 char buffer[16 + 1]; 520 521 s = splhigh(); 522 MC146818_GETTOD(RTC, &clkregs); 523 splx(s); 524 525 snprintf(buffer, sizeof(buffer), "%4d%02d%02d%02d%02d.%02d\n", 526 clkregs[MC_YEAR] + GEMSTARTOFTIME, 527 clkregs[MC_MONTH], clkregs[MC_DOM], 528 clkregs[MC_HOUR], clkregs[MC_MIN], clkregs[MC_SEC]); 529 530 if (uio->uio_offset > strlen(buffer)) 531 return 0; 532 533 length = strlen(buffer) - uio->uio_offset; 534 if (length > uio->uio_resid) 535 length = uio->uio_resid; 536 537 return uiomove((void *)buffer, length, uio); 538 } 539 540 static int 541 twodigits(char *buffer, int pos) 542 { 543 int result = 0; 544 545 if (buffer[pos] >= '0' && buffer[pos] <= '9') 546 result = (buffer[pos] - '0') * 10; 547 if (buffer[pos+1] >= '0' && buffer[pos+1] <= '9') 548 result += (buffer[pos+1] - '0'); 549 return result; 550 } 551 552 int 553 rtcwrite(dev_t dev, struct uio *uio, int flags) 554 { 555 mc_todregs clkregs; 556 int s, length, error; 557 char buffer[16]; 558 559 /* 560 * We require atomic updates! 561 */ 562 length = uio->uio_resid; 563 if (uio->uio_offset || (length != sizeof(buffer) 564 && length != sizeof(buffer) - 1)) 565 return EINVAL; 566 567 if ((error = uiomove((void *)buffer, sizeof(buffer), uio))) 568 return error; 569 570 if (length == sizeof(buffer) && buffer[sizeof(buffer) - 1] != '\n') 571 return EINVAL; 572 573 s = splclock(); 574 mc146818_write(RTC, MC_REGB, 575 mc146818_read(RTC, MC_REGB) | MC_REGB_24HR | MC_REGB_BINARY); 576 MC146818_GETTOD(RTC, &clkregs); 577 splx(s); 578 579 clkregs[MC_SEC] = twodigits(buffer, 13); 580 clkregs[MC_MIN] = twodigits(buffer, 10); 581 clkregs[MC_HOUR] = twodigits(buffer, 8); 582 clkregs[MC_DOM] = twodigits(buffer, 6); 583 clkregs[MC_MONTH] = twodigits(buffer, 4); 584 s = twodigits(buffer, 0) * 100 + twodigits(buffer, 2); 585 clkregs[MC_YEAR] = s - GEMSTARTOFTIME; 586 587 s = splclock(); 588 MC146818_PUTTOD(RTC, &clkregs); 589 splx(s); 590 591 return 0; 592 } 593