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