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