1 /* $NetBSD: clock.c,v 1.1.1.1 1995/03/26 07:12:13 leo 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. All advertising materials mentioning features or use of this software 21 * must display the following acknowledgement: 22 * This product includes software developed by the University of 23 * California, Berkeley and its contributors. 24 * 4. Neither the name of the University nor the names of its contributors 25 * may be used to endorse or promote products derived from this software 26 * without specific prior written permission. 27 * 28 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 29 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 31 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 32 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 33 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 34 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 36 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 37 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 38 * SUCH DAMAGE. 39 * 40 * from: Utah $Hdr: clock.c 1.18 91/01/21$ 41 * 42 * @(#)clock.c 7.6 (Berkeley) 5/7/91 43 */ 44 45 #include <sys/param.h> 46 #include <sys/kernel.h> 47 #include <sys/device.h> 48 #include <machine/psl.h> 49 #include <machine/cpu.h> 50 #include <machine/iomap.h> 51 #include <machine/mfp.h> 52 #include <atari/dev/clockreg.h> 53 54 #if defined(PROF) && defined(PROFTIMER) 55 #include <sys/PROF.h> 56 #endif 57 58 59 /* 60 * Machine-dependent clock routines. 61 * 62 * Startrtclock restarts the real-time clock, which provides 63 * hardclock interrupts to kern_clock.c. 64 * 65 * Inittodr initializes the time of day hardware which provides 66 * date functions. 67 * 68 * Resettodr restores the time of day hardware after a time change. 69 * 70 * A note on the real-time clock: 71 * We actually load the clock with CLK_INTERVAL-1 instead of CLK_INTERVAL. 72 * This is because the counter decrements to zero after N+1 enabled clock 73 * periods where N is the value loaded into the counter. 74 */ 75 76 int clockmatch __P((struct device *, struct cfdata *, void *)); 77 void clockattach __P((struct device *, struct device *, void *)); 78 79 struct cfdriver clockcd = { 80 NULL, "clock", (cfmatch_t)clockmatch, clockattach, 81 DV_DULL, sizeof(struct device), NULL, 0 82 }; 83 84 static u_long gettod __P((void)); 85 static int settod __P((u_long)); 86 87 static int divisor; 88 89 int 90 clockmatch(pdp, cfp, auxp) 91 struct device *pdp; 92 struct cfdata *cfp; 93 void *auxp; 94 { 95 if(!strcmp("clock", auxp)) 96 return(1); 97 return(0); 98 } 99 100 /* 101 * Start the real-time clock. 102 */ 103 void clockattach(pdp, dp, auxp) 104 struct device *pdp, *dp; 105 void *auxp; 106 { 107 /* 108 * Initialize Timer-A in the TT-MFP. An exact reduce to HZ is not 109 * possible by hardware. We use a divisor of 64 and reduce by software 110 * with a factor of 4. The MFP clock runs at 2457600Hz. Therefore the 111 * timer runs at an effective rate of: 2457600/(64*4) = 9600Hz. The 112 * following expression works for all 'normal' values of hz. 113 */ 114 divisor = 9600/hz; 115 MFP2->mf_tacr = 0; /* Stop timer */ 116 MFP2->mf_iera &= ~IA_TIMA2; /* Disable timer interrupts */ 117 MFP2->mf_tadr = divisor; /* Set divisor */ 118 119 printf(": system hz %d timer-A divisor %d\n", hz, divisor); 120 121 /* 122 * Initialize Timer-B in the TT-MFP. This timer is used by the 'delay' 123 * function below. This time is setup to be continueously counting from 124 * 255 back to zero at a frequency of 614400Hz. 125 */ 126 MFP2->mf_tbcr = 0; /* Stop timer */ 127 MFP2->mf_iera &= ~IA_TIMB2; /* Disable timer interrupts */ 128 MFP2->mf_tbdr = 0; 129 MFP2->mf_tbcr = T_Q004; /* Start timer */ 130 131 } 132 133 void cpu_initclocks() 134 { 135 MFP2->mf_tacr = T_Q064; /* Start timer */ 136 MFP2->mf_ipra &= ~IA_TIMA2; /* Clear pending interrupts */ 137 MFP2->mf_iera |= IA_TIMA2; /* Enable timer interrupts */ 138 MFP2->mf_imra |= IA_TIMA2; /* ..... */ 139 } 140 141 setstatclockrate(hz) 142 int hz; 143 { 144 } 145 146 /* 147 * Returns number of usec since last recorded clock "tick" 148 * (i.e. clock interrupt). 149 */ 150 clkread() 151 { 152 extern short clk_div; 153 u_int delta, elapsed; 154 155 elapsed = (divisor - MFP2->mf_tadr) + ((4 - clk_div) * divisor); 156 delta = (elapsed * tick) / (divisor << 2); 157 158 /* 159 * Account for pending clock interrupts 160 */ 161 if(MFP2->mf_iera & IA_TIMA2) 162 return(delta + tick); 163 return(delta); 164 } 165 166 #define TIMB2_FREQ 614400 167 #define TIMB2_LIMIT 256 168 169 /* 170 * Wait "n" microseconds. 171 * Relies on MFP2-Timer B counting down from TIMB2_LIMIT at TIMB2_FREQ Hz. 172 * Note: timer had better have been programmed before this is first used! 173 */ 174 void delay(n) 175 int n; 176 { 177 int tick, otick; 178 179 /* 180 * Read the counter first, so that the rest of the setup overhead is 181 * counted. 182 */ 183 otick = MFP2->mf_tbdr; 184 185 /* 186 * Calculate ((n * TIMER_FREQ) / 1e6) using explicit assembler code so 187 * we can take advantage of the intermediate 64-bit quantity to prevent 188 * loss of significance. 189 */ 190 n -= 5; 191 if(n < 0) 192 return; 193 { 194 u_int temp; 195 196 __asm __volatile ("mulul %2,%1:%0" : "=d" (n), "=d" (temp) 197 : "d" (TIMB2_FREQ)); 198 __asm __volatile ("divul %1,%2:%0" : "=d" (n) 199 : "d"(1000000),"d"(temp),"0"(n)); 200 } 201 202 while(n > 0) { 203 tick = MFP2->mf_tbdr; 204 if(tick > otick) 205 n -= TIMB2_LIMIT - (tick - otick); 206 else n -= otick - tick; 207 otick = tick; 208 } 209 } 210 211 #ifdef notyet 212 /* 213 * Needs to be calibrated for use, its way off most of the time 214 */ 215 void 216 DELAY(mic) 217 int mic; 218 { 219 u_long n; 220 short hpos; 221 222 /* 223 * this function uses HSync pulses as base units. The custom chips 224 * display only deals with 31.6kHz/2 refresh, this gives us a 225 * resolution of 1/15800 s, which is ~63us (add some fuzz so we really 226 * wait awhile, even if using small timeouts) 227 */ 228 n = mic/63 + 2; 229 do { 230 hpos = custom.vhposr & 0xff00; 231 while (hpos == (custom.vhposr & 0xff00)) 232 ; 233 } while (n--); 234 } 235 #endif /* notyet */ 236 237 #if notyet 238 239 /* implement this later. I'd suggest using both timers in CIA-A, they're 240 not yet used. */ 241 242 #include "clock.h" 243 #if NCLOCK > 0 244 /* 245 * /dev/clock: mappable high resolution timer. 246 * 247 * This code implements a 32-bit recycling counter (with a 4 usec period) 248 * using timers 2 & 3 on the 6840 clock chip. The counter can be mapped 249 * RO into a user's address space to achieve low overhead (no system calls), 250 * high-precision timing. 251 * 252 * Note that timer 3 is also used for the high precision profiling timer 253 * (PROFTIMER code above). Care should be taken when both uses are 254 * configured as only a token effort is made to avoid conflicting use. 255 */ 256 #include <sys/proc.h> 257 #include <sys/resourcevar.h> 258 #include <sys/ioctl.h> 259 #include <sys/malloc.h> 260 #include <vm/vm.h> 261 #include <amiga/amiga/clockioctl.h> 262 #include <sys/specdev.h> 263 #include <sys/vnode.h> 264 #include <sys/mman.h> 265 266 int clockon = 0; /* non-zero if high-res timer enabled */ 267 #ifdef PROFTIMER 268 int profprocs = 0; /* # of procs using profiling timer */ 269 #endif 270 #ifdef DEBUG 271 int clockdebug = 0; 272 #endif 273 274 /*ARGSUSED*/ 275 clockopen(dev, flags) 276 dev_t dev; 277 { 278 #ifdef PROFTIMER 279 #ifdef PROF 280 /* 281 * Kernel profiling enabled, give up. 282 */ 283 if (profiling) 284 return(EBUSY); 285 #endif 286 /* 287 * If any user processes are profiling, give up. 288 */ 289 if (profprocs) 290 return(EBUSY); 291 #endif 292 if (!clockon) { 293 startclock(); 294 clockon++; 295 } 296 return(0); 297 } 298 299 /*ARGSUSED*/ 300 clockclose(dev, flags) 301 dev_t dev; 302 { 303 (void) clockunmmap(dev, (caddr_t)0, curproc); /* XXX */ 304 stopclock(); 305 clockon = 0; 306 return(0); 307 } 308 309 /*ARGSUSED*/ 310 clockioctl(dev, cmd, data, flag, p) 311 dev_t dev; 312 u_long cmd; 313 caddr_t data; 314 struct proc *p; 315 { 316 int error = 0; 317 318 switch (cmd) { 319 320 case CLOCKMAP: 321 error = clockmmap(dev, (caddr_t *)data, p); 322 break; 323 324 case CLOCKUNMAP: 325 error = clockunmmap(dev, *(caddr_t *)data, p); 326 break; 327 328 case CLOCKGETRES: 329 *(int *)data = CLK_RESOLUTION; 330 break; 331 332 default: 333 error = EINVAL; 334 break; 335 } 336 return(error); 337 } 338 339 /*ARGSUSED*/ 340 clockmap(dev, off, prot) 341 dev_t dev; 342 { 343 return((off + (INTIOBASE+CLKBASE+CLKSR-1)) >> PGSHIFT); 344 } 345 346 clockmmap(dev, addrp, p) 347 dev_t dev; 348 caddr_t *addrp; 349 struct proc *p; 350 { 351 int error; 352 struct vnode vn; 353 struct specinfo si; 354 int flags; 355 356 flags = MAP_FILE|MAP_SHARED; 357 if (*addrp) 358 flags |= MAP_FIXED; 359 else 360 *addrp = (caddr_t)0x1000000; /* XXX */ 361 vn.v_type = VCHR; /* XXX */ 362 vn.v_specinfo = &si; /* XXX */ 363 vn.v_rdev = dev; /* XXX */ 364 error = vm_mmap(&p->p_vmspace->vm_map, (vm_offset_t *)addrp, 365 PAGE_SIZE, VM_PROT_ALL, flags, (caddr_t)&vn, 0); 366 return(error); 367 } 368 369 clockunmmap(dev, addr, p) 370 dev_t dev; 371 caddr_t addr; 372 struct proc *p; 373 { 374 int rv; 375 376 if (addr == 0) 377 return(EINVAL); /* XXX: how do we deal with this? */ 378 rv = vm_deallocate(p->p_vmspace->vm_map, (vm_offset_t)addr, PAGE_SIZE); 379 return(rv == KERN_SUCCESS ? 0 : EINVAL); 380 } 381 382 startclock() 383 { 384 register struct clkreg *clk = (struct clkreg *)clkstd[0]; 385 386 clk->clk_msb2 = -1; clk->clk_lsb2 = -1; 387 clk->clk_msb3 = -1; clk->clk_lsb3 = -1; 388 389 clk->clk_cr2 = CLK_CR3; 390 clk->clk_cr3 = CLK_OENAB|CLK_8BIT; 391 clk->clk_cr2 = CLK_CR1; 392 clk->clk_cr1 = CLK_IENAB; 393 } 394 395 stopclock() 396 { 397 register struct clkreg *clk = (struct clkreg *)clkstd[0]; 398 399 clk->clk_cr2 = CLK_CR3; 400 clk->clk_cr3 = 0; 401 clk->clk_cr2 = CLK_CR1; 402 clk->clk_cr1 = CLK_IENAB; 403 } 404 #endif 405 406 #endif 407 408 409 #ifdef PROFTIMER 410 /* 411 * This code allows the amiga kernel to use one of the extra timers on 412 * the clock chip for profiling, instead of the regular system timer. 413 * The advantage of this is that the profiling timer can be turned up to 414 * a higher interrupt rate, giving finer resolution timing. The profclock 415 * routine is called from the lev6intr in locore, and is a specialized 416 * routine that calls addupc. The overhead then is far less than if 417 * hardclock/softclock was called. Further, the context switch code in 418 * locore has been changed to turn the profile clock on/off when switching 419 * into/out of a process that is profiling (startprofclock/stopprofclock). 420 * This reduces the impact of the profiling clock on other users, and might 421 * possibly increase the accuracy of the profiling. 422 */ 423 int profint = PRF_INTERVAL; /* Clock ticks between interrupts */ 424 int profscale = 0; /* Scale factor from sys clock to prof clock */ 425 char profon = 0; /* Is profiling clock on? */ 426 427 /* profon values - do not change, locore.s assumes these values */ 428 #define PRF_NONE 0x00 429 #define PRF_USER 0x01 430 #define PRF_KERNEL 0x80 431 432 initprofclock() 433 { 434 #if NCLOCK > 0 435 struct proc *p = curproc; /* XXX */ 436 437 /* 438 * If the high-res timer is running, force profiling off. 439 * Unfortunately, this gets reflected back to the user not as 440 * an error but as a lack of results. 441 */ 442 if (clockon) { 443 p->p_stats->p_prof.pr_scale = 0; 444 return; 445 } 446 /* 447 * Keep track of the number of user processes that are profiling 448 * by checking the scale value. 449 * 450 * XXX: this all assumes that the profiling code is well behaved; 451 * i.e. profil() is called once per process with pcscale non-zero 452 * to turn it on, and once with pcscale zero to turn it off. 453 * Also assumes you don't do any forks or execs. Oh well, there 454 * is always adb... 455 */ 456 if (p->p_stats->p_prof.pr_scale) 457 profprocs++; 458 else 459 profprocs--; 460 #endif 461 /* 462 * The profile interrupt interval must be an even divisor 463 * of the CLK_INTERVAL so that scaling from a system clock 464 * tick to a profile clock tick is possible using integer math. 465 */ 466 if (profint > CLK_INTERVAL || (CLK_INTERVAL % profint) != 0) 467 profint = CLK_INTERVAL; 468 profscale = CLK_INTERVAL / profint; 469 } 470 471 startprofclock() 472 { 473 unsigned short interval; 474 475 /* stop timer B */ 476 ciab.crb = ciab.crb & 0xc0; 477 478 /* load interval into registers. 479 the clocks run at NTSC: 715.909kHz or PAL: 709.379kHz */ 480 481 interval = profint - 1; 482 483 /* order of setting is important ! */ 484 ciab.tblo = interval & 0xff; 485 ciab.tbhi = interval >> 8; 486 487 /* enable interrupts for timer B */ 488 ciab.icr = (1<<7) | (1<<1); 489 490 /* start timer B in continuous shot mode */ 491 ciab.crb = (ciab.crb & 0xc0) | 1; 492 } 493 494 stopprofclock() 495 { 496 /* stop timer B */ 497 ciab.crb = ciab.crb & 0xc0; 498 } 499 500 #ifdef PROF 501 /* 502 * profclock() is expanded in line in lev6intr() unless profiling kernel. 503 * Assumes it is called with clock interrupts blocked. 504 */ 505 profclock(pc, ps) 506 caddr_t pc; 507 int ps; 508 { 509 /* 510 * Came from user mode. 511 * If this process is being profiled record the tick. 512 */ 513 if (USERMODE(ps)) { 514 if (p->p_stats.p_prof.pr_scale) 515 addupc(pc, &curproc->p_stats.p_prof, 1); 516 } 517 /* 518 * Came from kernel (supervisor) mode. 519 * If we are profiling the kernel, record the tick. 520 */ 521 else if (profiling < 2) { 522 register int s = pc - s_lowpc; 523 524 if (s < s_textsize) 525 kcount[s / (HISTFRACTION * sizeof (*kcount))]++; 526 } 527 /* 528 * Kernel profiling was on but has been disabled. 529 * Mark as no longer profiling kernel and if all profiling done, 530 * disable the clock. 531 */ 532 if (profiling && (profon & PRF_KERNEL)) { 533 profon &= ~PRF_KERNEL; 534 if (profon == PRF_NONE) 535 stopprofclock(); 536 } 537 } 538 #endif 539 #endif 540 541 /* 542 * Initialize the time of day register, based on the time base which is, e.g. 543 * from a filesystem. 544 */ 545 inittodr(base) 546 time_t base; 547 { 548 u_long timbuf = base; /* assume no battery clock exists */ 549 550 timbuf = gettod(); 551 552 if(timbuf < base) { 553 printf("WARNING: bad date in battery clock\n"); 554 timbuf = base; 555 } 556 557 /* Battery clock does not store usec's, so forget about it. */ 558 time.tv_sec = timbuf; 559 } 560 561 resettodr() 562 { 563 if(settod(time.tv_sec) == 1) 564 return; 565 printf("Cannot set battery backed clock\n"); 566 } 567 568 static char dmsize[12] = 569 { 570 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 571 }; 572 573 static char ldmsize[12] = 574 { 575 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 576 }; 577 578 static __inline__ int rtc_getclkreg(regno) 579 int regno; 580 { 581 RTC->rtc_regno = RTC_REGA; 582 RTC->rtc_regno = regno; 583 return(RTC->rtc_data & 0377); 584 } 585 586 static __inline__ void rtc_setclkreg(regno, value) 587 int regno, value; 588 { 589 RTC->rtc_regno = regno; 590 RTC->rtc_data = value; 591 } 592 593 static u_long 594 gettod() 595 { 596 int i, year, mon, day, hour, min, sec; 597 u_long new_time = 0; 598 char *msize; 599 600 /* 601 * Hold clock 602 */ 603 rtc_setclkreg(RTC_REGB, rtc_getclkreg(RTC_REGB) | RTC_B_SET); 604 605 /* 606 * Read clock 607 */ 608 sec = rtc_getclkreg(RTC_SEC); 609 min = rtc_getclkreg(RTC_MIN); 610 hour = rtc_getclkreg(RTC_HOUR); 611 day = rtc_getclkreg(RTC_DAY) - 1; 612 mon = rtc_getclkreg(RTC_MONTH) - 1; 613 year = rtc_getclkreg(RTC_YEAR) + STARTOFTIME; 614 615 /* 616 * Let it run again.. 617 */ 618 rtc_setclkreg(RTC_REGB, rtc_getclkreg(RTC_REGB) & ~RTC_B_SET); 619 620 if(range_test(hour, 0, 23)) 621 return(0); 622 if(range_test(day, 0, 30)) 623 return(0); 624 if (range_test(mon, 0, 11)) 625 return(0); 626 if(range_test(year, STARTOFTIME, 2000)) 627 return(0); 628 629 for(i = STARTOFTIME; i < year; i++) { 630 if(is_leap(i)) 631 new_time += 366; 632 else new_time += 365; 633 } 634 635 msize = is_leap(year) ? ldmsize : dmsize; 636 for(i = 0; i < mon; i++) 637 new_time += msize[i]; 638 new_time += day; 639 return((new_time * SECS_DAY) + (hour * 3600) + (min * 60) + sec); 640 } 641 642 static int 643 settod(newtime) 644 u_long newtime; 645 { 646 register long days, rem, year; 647 register char *ml; 648 int sec, min, hour, month; 649 650 /* Number of days since Jan. 1 1970 */ 651 days = newtime / SECS_DAY; 652 rem = newtime % SECS_DAY; 653 654 /* 655 * Calculate sec, min, hour 656 */ 657 hour = rem / SECS_HOUR; 658 rem %= SECS_HOUR; 659 min = rem / 60; 660 sec = rem % 60; 661 662 /* 663 * Figure out the year. Day in year is left in 'days'. 664 */ 665 year = STARTOFTIME; 666 while(days >= (rem = is_leap(year) ? 366 : 365)) { 667 ++year; 668 days -= rem; 669 } 670 while(days < 0) { 671 --year; 672 days += is_leap(year) ? 366 : 365; 673 } 674 675 /* 676 * Determine the month 677 */ 678 ml = is_leap(year) ? ldmsize : dmsize; 679 for(month = 0; days >= ml[month]; ++month) 680 days -= ml[month]; 681 682 /* 683 * Now that everything is calculated, program the RTC 684 */ 685 rtc_setclkreg(RTC_REGB, RTC_B_SET); 686 rtc_setclkreg(RTC_REGA, RTC_A_DV1|RTC_A_RS2|RTC_A_RS3); 687 rtc_setclkreg(RTC_REGB, RTC_B_SET|RTC_B_SQWE|RTC_B_DM|RTC_B_24_12); 688 rtc_setclkreg(RTC_SEC, sec); 689 rtc_setclkreg(RTC_MIN, min); 690 rtc_setclkreg(RTC_HOUR, hour); 691 rtc_setclkreg(RTC_DAY, days+1); 692 rtc_setclkreg(RTC_MONTH, month+1); 693 rtc_setclkreg(RTC_YEAR, year-1970); 694 rtc_setclkreg(RTC_REGB, RTC_B_SQWE|RTC_B_DM|RTC_B_24_12); 695 696 return(1); 697 } 698