1 /* $NetBSD: clock.c,v 1.59 2014/07/25 08:10:32 dholland 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.59 2014/07/25 08:10:32 dholland 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 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 void clockattach(device_t parent, device_t self, void *aux) 173 { 174 struct clock_softc *sc = device_private(self); 175 struct todr_chip_handle *tch; 176 177 sc->sc_dev = self; 178 tch = &sc->sc_handle; 179 tch->todr_gettime_ymdhms = atari_rtc_get; 180 tch->todr_settime_ymdhms = atari_rtc_set; 181 tch->todr_setwen = NULL; 182 183 todr_attach(tch); 184 185 sc->sc_flags = 0; 186 187 /* 188 * Initialize Timer-A in the ST-MFP. We use a divisor of 200. 189 * The MFP clock runs at 2457600Hz. Therefore the timer runs 190 * at an effective rate of: 2457600/200 = 12288Hz. The 191 * following expression works for 48, 64 or 96 hz. 192 */ 193 divisor = CLOCK_HZ/hz; 194 MFP->mf_tacr = 0; /* Stop timer */ 195 MFP->mf_iera &= ~IA_TIMA; /* Disable timer interrupts */ 196 MFP->mf_tadr = divisor; /* Set divisor */ 197 198 clk_timecounter.tc_frequency = CLOCK_HZ; 199 200 if (hz != 48 && hz != 64 && hz != 96) { /* XXX */ 201 printf (": illegal value %d for systemclock, reset to %d\n\t", 202 hz, 64); 203 hz = 64; 204 } 205 printf(": system hz %d timer-A divisor 200/%d\n", hz, divisor); 206 tc_init(&clk_timecounter); 207 208 #ifdef STATCLOCK 209 if ((stathz == 0) || (stathz > hz) || (CLOCK_HZ % stathz)) 210 stathz = hz; 211 if ((profhz == 0) || (profhz > (hz << 1)) || (CLOCK_HZ % profhz)) 212 profhz = hz << 1; 213 214 MFP->mf_tcdcr &= 0x7; /* Stop timer */ 215 MFP->mf_ierb &= ~IB_TIMC; /* Disable timer inter. */ 216 MFP->mf_tcdr = CLOCK_HZ/stathz; /* Set divisor */ 217 218 statmin = (CLOCK_HZ/stathz) - (statvar >> 1); 219 profmin = (CLOCK_HZ/profhz) - (statvar >> 1); 220 clk2min = statmin; 221 #endif /* STATCLOCK */ 222 } 223 224 void cpu_initclocks(void) 225 { 226 227 MFP->mf_tacr = T_Q200; /* Start timer */ 228 MFP->mf_ipra = (u_int8_t)~IA_TIMA;/* Clear pending interrupts */ 229 MFP->mf_iera |= IA_TIMA; /* Enable timer interrupts */ 230 MFP->mf_imra |= IA_TIMA; /* ..... */ 231 232 #ifdef STATCLOCK 233 MFP->mf_tcdcr = (MFP->mf_tcdcr & 0x7) | (T_Q200<<4); /* Start */ 234 MFP->mf_iprb = (u_int8_t)~IB_TIMC;/* Clear pending interrupts */ 235 MFP->mf_ierb |= IB_TIMC; /* Enable timer interrupts */ 236 MFP->mf_imrb |= IB_TIMC; /* ..... */ 237 #endif /* STATCLOCK */ 238 } 239 240 void 241 setstatclockrate(int newhz) 242 { 243 244 #ifdef STATCLOCK 245 if (newhz == stathz) 246 clk2min = statmin; 247 else clk2min = profmin; 248 #endif /* STATCLOCK */ 249 } 250 251 #ifdef STATCLOCK 252 void 253 statintr(struct clockframe frame) 254 { 255 register int var, r; 256 257 var = statvar - 1; 258 do { 259 r = random() & var; 260 } while (r == 0); 261 262 /* 263 * Note that we are always lagging behind as the new divisor 264 * value will not be loaded until the next interrupt. This 265 * shouldn't disturb the median frequency (I think ;-) ) as 266 * only the value used when switching frequencies is used 267 * twice. This shouldn't happen very often. 268 */ 269 MFP->mf_tcdr = clk2min + r; 270 271 statclock(&frame); 272 } 273 #endif /* STATCLOCK */ 274 275 static u_int 276 clk_getcounter(struct timecounter *tc) 277 { 278 uint32_t delta, count, cur_hardclock; 279 uint8_t ipra, tadr; 280 int s; 281 static uint32_t lastcount; 282 283 s = splhigh(); 284 cur_hardclock = hardclock_ticks; 285 ipra = MFP->mf_ipra; 286 tadr = MFP->mf_tadr; 287 delta = divisor - tadr; 288 289 if (ipra & IA_TIMA) 290 delta += divisor; 291 splx(s); 292 293 count = (divisor * cur_hardclock) + delta; 294 if ((int32_t)(count - lastcount) < 0) { 295 /* XXX wrapped; maybe hardclock() is blocked more than 2/HZ */ 296 count = lastcount + 1; 297 } 298 lastcount = count; 299 300 return count; 301 } 302 303 #define TIMB_FREQ 614400 304 #define TIMB_LIMIT 256 305 306 void 307 init_delay(void) 308 { 309 310 /* 311 * Initialize Timer-B in the ST-MFP. This timer is used by 312 * the 'delay' function below. This timer is setup to be 313 * continueously counting from 255 back to zero at a 314 * frequency of 614400Hz. We do this *early* in the 315 * initialisation process. 316 */ 317 MFP->mf_tbcr = 0; /* Stop timer */ 318 MFP->mf_iera &= ~IA_TIMB; /* Disable timer interrupts */ 319 MFP->mf_tbdr = 0; 320 MFP->mf_tbcr = T_Q004; /* Start timer */ 321 } 322 323 /* 324 * Wait "n" microseconds. 325 * Relies on MFP-Timer B counting down from TIMB_LIMIT at TIMB_FREQ Hz. 326 * Note: timer had better have been programmed before this is first used! 327 */ 328 void 329 delay(unsigned int n) 330 { 331 int ticks, otick, remaining; 332 333 /* 334 * Read the counter first, so that the rest of the setup overhead is 335 * counted. 336 */ 337 otick = MFP->mf_tbdr; 338 339 if (n <= UINT_MAX / TIMB_FREQ) { 340 /* 341 * For unsigned arithmetic, division can be replaced with 342 * multiplication with the inverse and a shift. 343 */ 344 remaining = n * TIMB_FREQ / 1000000; 345 } else { 346 /* This is a very long delay. 347 * Being slow here doesn't matter. 348 */ 349 remaining = (unsigned long long) n * TIMB_FREQ / 1000000; 350 } 351 352 while (remaining > 0) { 353 ticks = MFP->mf_tbdr; 354 if (ticks > otick) 355 remaining -= TIMB_LIMIT - (ticks - otick); 356 else 357 remaining -= otick - ticks; 358 otick = ticks; 359 } 360 } 361 362 #ifdef GPROF 363 /* 364 * profclock() is expanded in line in lev6intr() unless profiling kernel. 365 * Assumes it is called with clock interrupts blocked. 366 */ 367 profclock(void *pc, int ps) 368 { 369 370 /* 371 * Came from user mode. 372 * If this process is being profiled record the tick. 373 */ 374 if (USERMODE(ps)) { 375 if (p->p_stats.p_prof.pr_scale) 376 addupc(pc, &curproc->p_stats.p_prof, 1); 377 } 378 /* 379 * Came from kernel (supervisor) mode. 380 * If we are profiling the kernel, record the tick. 381 */ 382 else if (profiling < 2) { 383 register int s = pc - s_lowpc; 384 385 if (s < s_textsize) 386 kcount[s / (HISTFRACTION * sizeof(*kcount))]++; 387 } 388 /* 389 * Kernel profiling was on but has been disabled. 390 * Mark as no longer profiling kernel and if all profiling done, 391 * disable the clock. 392 */ 393 if (profiling && (profon & PRF_KERNEL)) { 394 profon &= ~PRF_KERNEL; 395 if (profon == PRF_NONE) 396 stopprofclock(); 397 } 398 } 399 #endif 400 401 /*********************************************************************** 402 * Real Time Clock support * 403 ***********************************************************************/ 404 405 u_int mc146818_read(void *cookie, u_int regno) 406 { 407 struct rtc *rtc = cookie; 408 409 rtc->rtc_regno = regno; 410 return rtc->rtc_data & 0xff; 411 } 412 413 void mc146818_write(void *cookie, u_int regno, u_int value) 414 { 415 struct rtc *rtc = cookie; 416 417 rtc->rtc_regno = regno; 418 rtc->rtc_data = value; 419 } 420 421 static int 422 atari_rtc_get(todr_chip_handle_t todr, struct clock_ymdhms *dtp) 423 { 424 int sps; 425 mc_todregs clkregs; 426 u_int regb; 427 428 sps = splhigh(); 429 regb = mc146818_read(RTC, MC_REGB); 430 MC146818_GETTOD(RTC, &clkregs); 431 splx(sps); 432 433 regb &= MC_REGB_24HR|MC_REGB_BINARY; 434 if (regb != (MC_REGB_24HR|MC_REGB_BINARY)) { 435 printf("Error: Nonstandard RealTimeClock Configuration -" 436 " value ignored\n" 437 " A write to /dev/rtc will correct this.\n"); 438 return 0; 439 } 440 if (clkregs[MC_SEC] > 59) 441 return -1; 442 if (clkregs[MC_MIN] > 59) 443 return -1; 444 if (clkregs[MC_HOUR] > 23) 445 return -1; 446 if (range_test(clkregs[MC_DOM], 1, 31)) 447 return -1; 448 if (range_test(clkregs[MC_MONTH], 1, 12)) 449 return -1; 450 if (clkregs[MC_YEAR] > 99) 451 return -1; 452 453 dtp->dt_year = clkregs[MC_YEAR] + GEMSTARTOFTIME; 454 dtp->dt_mon = clkregs[MC_MONTH]; 455 dtp->dt_day = clkregs[MC_DOM]; 456 dtp->dt_hour = clkregs[MC_HOUR]; 457 dtp->dt_min = clkregs[MC_MIN]; 458 dtp->dt_sec = clkregs[MC_SEC]; 459 460 return 0; 461 } 462 463 static int 464 atari_rtc_set(todr_chip_handle_t todr, struct clock_ymdhms *dtp) 465 { 466 int s; 467 mc_todregs clkregs; 468 469 clkregs[MC_YEAR] = dtp->dt_year - GEMSTARTOFTIME; 470 clkregs[MC_MONTH] = dtp->dt_mon; 471 clkregs[MC_DOM] = dtp->dt_day; 472 clkregs[MC_HOUR] = dtp->dt_hour; 473 clkregs[MC_MIN] = dtp->dt_min; 474 clkregs[MC_SEC] = dtp->dt_sec; 475 476 s = splclock(); 477 MC146818_PUTTOD(RTC, &clkregs); 478 splx(s); 479 480 return 0; 481 } 482 483 /*********************************************************************** 484 * RTC-device support * 485 ***********************************************************************/ 486 int 487 rtcopen(dev_t dev, int flag, int mode, struct lwp *l) 488 { 489 int unit = minor(dev); 490 struct clock_softc *sc; 491 492 sc = device_lookup_private(&clock_cd, unit); 493 if (sc == NULL) 494 return ENXIO; 495 if (sc->sc_flags & RTC_OPEN) 496 return EBUSY; 497 498 sc->sc_flags = RTC_OPEN; 499 return 0; 500 } 501 502 int 503 rtcclose(dev_t dev, int flag, int mode, struct lwp *l) 504 { 505 int unit = minor(dev); 506 struct clock_softc *sc = device_lookup_private(&clock_cd, unit); 507 508 sc->sc_flags = 0; 509 return 0; 510 } 511 512 int 513 rtcread(dev_t dev, struct uio *uio, int flags) 514 { 515 mc_todregs clkregs; 516 int s, length; 517 char buffer[16 + 1]; 518 519 s = splhigh(); 520 MC146818_GETTOD(RTC, &clkregs); 521 splx(s); 522 523 snprintf(buffer, sizeof(buffer), "%4d%02d%02d%02d%02d.%02d\n", 524 clkregs[MC_YEAR] + GEMSTARTOFTIME, 525 clkregs[MC_MONTH], clkregs[MC_DOM], 526 clkregs[MC_HOUR], clkregs[MC_MIN], clkregs[MC_SEC]); 527 528 if (uio->uio_offset > strlen(buffer)) 529 return 0; 530 531 length = strlen(buffer) - uio->uio_offset; 532 if (length > uio->uio_resid) 533 length = uio->uio_resid; 534 535 return uiomove((void *)buffer, length, uio); 536 } 537 538 static int 539 twodigits(char *buffer, int pos) 540 { 541 int result = 0; 542 543 if (buffer[pos] >= '0' && buffer[pos] <= '9') 544 result = (buffer[pos] - '0') * 10; 545 if (buffer[pos+1] >= '0' && buffer[pos+1] <= '9') 546 result += (buffer[pos+1] - '0'); 547 return result; 548 } 549 550 int 551 rtcwrite(dev_t dev, struct uio *uio, int flags) 552 { 553 mc_todregs clkregs; 554 int s, length, error; 555 char buffer[16]; 556 557 /* 558 * We require atomic updates! 559 */ 560 length = uio->uio_resid; 561 if (uio->uio_offset || (length != sizeof(buffer) 562 && length != sizeof(buffer) - 1)) 563 return EINVAL; 564 565 if ((error = uiomove((void *)buffer, sizeof(buffer), uio))) 566 return error; 567 568 if (length == sizeof(buffer) && buffer[sizeof(buffer) - 1] != '\n') 569 return EINVAL; 570 571 s = splclock(); 572 mc146818_write(RTC, MC_REGB, 573 mc146818_read(RTC, MC_REGB) | MC_REGB_24HR | MC_REGB_BINARY); 574 MC146818_GETTOD(RTC, &clkregs); 575 splx(s); 576 577 clkregs[MC_SEC] = twodigits(buffer, 13); 578 clkregs[MC_MIN] = twodigits(buffer, 10); 579 clkregs[MC_HOUR] = twodigits(buffer, 8); 580 clkregs[MC_DOM] = twodigits(buffer, 6); 581 clkregs[MC_MONTH] = twodigits(buffer, 4); 582 s = twodigits(buffer, 0) * 100 + twodigits(buffer, 2); 583 clkregs[MC_YEAR] = s - GEMSTARTOFTIME; 584 585 s = splclock(); 586 MC146818_PUTTOD(RTC, &clkregs); 587 splx(s); 588 589 return 0; 590 } 591