1 /* $NetBSD: clock.c,v 1.22 1996/10/14 18:40:15 is 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 <sys/systm.h> 49 #include <machine/psl.h> 50 #include <machine/cpu.h> 51 #include <amiga/amiga/device.h> 52 #include <amiga/amiga/custom.h> 53 #include <amiga/amiga/cia.h> 54 #ifdef DRACO 55 #include <amiga/amiga/drcustom.h> 56 #endif 57 #include <amiga/dev/rtc.h> 58 #include <amiga/dev/zbusvar.h> 59 60 #if defined(PROF) && defined(PROFTIMER) 61 #include <sys/PROF.h> 62 #endif 63 64 /* the clocks run at NTSC: 715.909kHz or PAL: 709.379kHz. 65 We're using a 100 Hz clock. */ 66 67 #define CLK_INTERVAL amiga_clk_interval 68 int amiga_clk_interval; 69 int eclockfreq; 70 struct CIA *clockcia; 71 72 /* 73 * Machine-dependent clock routines. 74 * 75 * Startrtclock restarts the real-time clock, which provides 76 * hardclock interrupts to kern_clock.c. 77 * 78 * Inittodr initializes the time of day hardware which provides 79 * date functions. 80 * 81 * Resettodr restores the time of day hardware after a time change. 82 * 83 * A note on the real-time clock: 84 * We actually load the clock with CLK_INTERVAL-1 instead of CLK_INTERVAL. 85 * This is because the counter decrements to zero after N+1 enabled clock 86 * periods where N is the value loaded into the counter. 87 */ 88 89 int clockmatch __P((struct device *, void *, void *)); 90 void clockattach __P((struct device *, struct device *, void *)); 91 void cpu_initclocks __P((void)); 92 void calibrate_delay __P((void)); 93 94 struct cfattach clock_ca = { 95 sizeof(struct device), clockmatch, clockattach 96 }; 97 98 struct cfdriver clock_cd = { 99 NULL, "clock", DV_DULL, NULL, 0 }; 100 101 int 102 clockmatch(pdp, match, auxp) 103 struct device *pdp; 104 void *match, *auxp; 105 { 106 if (matchname("clock", auxp)) 107 return(1); 108 return(0); 109 } 110 111 /* 112 * Start the real-time clock. 113 */ 114 void 115 clockattach(pdp, dp, auxp) 116 struct device *pdp, *dp; 117 void *auxp; 118 { 119 char *clockchip; 120 unsigned short interval; 121 #ifdef DRACO 122 u_char dracorev; 123 #endif 124 125 if (eclockfreq == 0) 126 eclockfreq = 715909; /* guess NTSC */ 127 128 CLK_INTERVAL = (eclockfreq / 100); 129 130 #ifdef DRACO 131 dracorev = is_draco(); 132 if (dracorev >= 4) { 133 CLK_INTERVAL = (eclockfreq / 700); 134 clockchip = "QuickLogic"; 135 } else if (dracorev) { 136 clockcia = (struct CIA *)CIAAbase; 137 clockchip = "CIA A"; 138 } else 139 #endif 140 { 141 clockcia = (struct CIA *)CIABbase; 142 clockchip = "CIA B"; 143 } 144 145 printf(": %s system hz %d hardware hz %d\n", clockchip, hz, 146 #ifdef DRACO 147 dracorev >= 4 ? eclockfreq / 7 : eclockfreq); 148 #else 149 eclockfreq); 150 #endif 151 152 #ifdef DRACO 153 if (dracorev >= 4) { 154 /* 155 * can't preload anything beforehand, timer is free_running; 156 * but need this for delay calibration. 157 */ 158 159 draco_ioct->io_timerlo = CLK_INTERVAL & 0xff; 160 draco_ioct->io_timerhi = CLK_INTERVAL >> 8; 161 162 calibrate_delay(); 163 164 return; 165 } 166 #endif 167 /* 168 * stop timer A 169 */ 170 clockcia->cra = clockcia->cra & 0xc0; 171 clockcia->icr = 1 << 0; /* disable timer A interrupt */ 172 interval = clockcia->icr; /* and make sure it's clear */ 173 174 /* 175 * load interval into registers. 176 * the clocks run at NTSC: 715.909kHz or PAL: 709.379kHz 177 * supprort for PAL WHEN?!?! XXX 178 */ 179 interval = CLK_INTERVAL - 1; 180 181 /* 182 * order of setting is important ! 183 */ 184 clockcia->talo = interval & 0xff; 185 clockcia->tahi = interval >> 8; 186 /* 187 * start timer A in continuous mode 188 */ 189 clockcia->cra = (clockcia->cra & 0xc0) | 1; 190 191 calibrate_delay(); 192 } 193 194 /* 195 * Calibrate delay loop. 196 * We use two iterations because we don't have enough bits to do a factor of 197 * 8 with better than 1%. 198 * 199 * XXX Note that we MUST stay below 1 tick if using clkread(), even for 200 * underestimated values of delaydivisor. 201 * 202 * XXX the "ns" below is only correct for a shift of 10 bits, and even then 203 * off by 2.4% 204 */ 205 206 void calibrate_delay() 207 { 208 unsigned long t1, t2; 209 extern u_int32_t delaydivisor; 210 /* XXX this should be defined elsewhere */ 211 212 printf("Calibrating delay loop... "); 213 214 do { 215 t1 = clkread(); 216 delay(1024); 217 t2 = clkread(); 218 } while (t2 <= t1); 219 t2 -= t1; 220 delaydivisor = (delaydivisor * t2 + 1023) >> 10; 221 #ifdef DIAGNOSTIC 222 printf("\ndiff %ld us, new divisor %u ns\n", t2, delaydivisor); 223 do { 224 t1 = clkread(); 225 delay(1024); 226 t2 = clkread(); 227 } while (t2 <= t1); 228 t2 -= t1; 229 delaydivisor = (delaydivisor * t2 + 1023) >> 10; 230 printf("diff %ld us, new divisor %u ns\n", t2, delaydivisor); 231 #endif 232 do { 233 t1 = clkread(); 234 delay(1024); 235 t2 = clkread(); 236 } while (t2 <= t1); 237 t2 -= t1; 238 delaydivisor = (delaydivisor * t2 + 1023) >> 10; 239 #ifdef DIAGNOSTIC 240 printf("diff %ld us, new divisor ", t2); 241 #endif 242 printf("%u ns\n", delaydivisor); 243 } 244 245 void 246 cpu_initclocks() 247 { 248 #ifdef DRACO 249 unsigned char dracorev; 250 dracorev = is_draco(); 251 if (dracorev >= 4) { 252 draco_ioct->io_timerlo = CLK_INTERVAL & 0xFF; 253 draco_ioct->io_timerhi = CLK_INTERVAL >> 8; 254 draco_ioct->io_timerrst = 0; /* any value resets */ 255 draco_ioct->io_status2 |= DRSTAT2_TMRINTENA; 256 257 return; 258 } 259 #endif 260 /* 261 * enable interrupts for timer A 262 */ 263 clockcia->icr = (1<<7) | (1<<0); 264 265 /* 266 * start timer A in continuous shot mode 267 */ 268 clockcia->cra = (clockcia->cra & 0xc0) | 1; 269 270 /* 271 * and globally enable interrupts for ciab 272 */ 273 #ifdef DRACO 274 if (dracorev) /* we use cia a on DraCo */ 275 *draco_intena |= DRIRQ_INT2; 276 else 277 #endif 278 custom.intena = INTF_SETCLR | INTF_EXTER; 279 280 } 281 282 void 283 setstatclockrate(hz) 284 int hz; 285 { 286 } 287 288 /* 289 * Returns number of usec since last recorded clock "tick" 290 * (i.e. clock interrupt). 291 */ 292 u_long 293 clkread() 294 { 295 u_int interval; 296 u_char hi, hi2, lo; 297 298 #ifdef DRACO 299 if (is_draco() >= 4) { 300 hi2 = draco_ioct->io_chiprev; /* latch timer */ 301 hi = draco_ioct->io_timerhi; 302 lo = draco_ioct->io_timerlo; 303 interval = ((hi<<8) | lo); 304 if (interval > CLK_INTERVAL) /* timer underflow */ 305 interval = 65536 + CLK_INTERVAL - interval; 306 else 307 interval = CLK_INTERVAL - interval; 308 309 } else 310 #endif 311 { 312 hi = clockcia->tahi; 313 lo = clockcia->talo; 314 hi2 = clockcia->tahi; 315 if (hi != hi2) { 316 lo = clockcia->talo; 317 hi = hi2; 318 } 319 320 interval = (CLK_INTERVAL - 1) - ((hi<<8) | lo); 321 322 /* 323 * should read ICR and if there's an int pending, adjust 324 * interval. However, since reading ICR clears the interrupt, 325 * we'd lose a hardclock int, and this is not tolerable. 326 */ 327 } 328 329 return((interval * tick) / CLK_INTERVAL); 330 } 331 332 #if notyet 333 334 /* implement this later. I'd suggest using both timers in CIA-A, they're 335 not yet used. */ 336 337 #include "clock.h" 338 #if NCLOCK > 0 339 /* 340 * /dev/clock: mappable high resolution timer. 341 * 342 * This code implements a 32-bit recycling counter (with a 4 usec period) 343 * using timers 2 & 3 on the 6840 clock chip. The counter can be mapped 344 * RO into a user's address space to achieve low overhead (no system calls), 345 * high-precision timing. 346 * 347 * Note that timer 3 is also used for the high precision profiling timer 348 * (PROFTIMER code above). Care should be taken when both uses are 349 * configured as only a token effort is made to avoid conflicting use. 350 */ 351 #include <sys/proc.h> 352 #include <sys/resourcevar.h> 353 #include <sys/ioctl.h> 354 #include <sys/malloc.h> 355 #include <vm/vm.h> 356 #include <amiga/amiga/clockioctl.h> 357 #include <sys/specdev.h> 358 #include <sys/vnode.h> 359 #include <sys/mman.h> 360 361 int clockon = 0; /* non-zero if high-res timer enabled */ 362 #ifdef PROFTIMER 363 int profprocs = 0; /* # of procs using profiling timer */ 364 #endif 365 #ifdef DEBUG 366 int clockdebug = 0; 367 #endif 368 369 /*ARGSUSED*/ 370 clockopen(dev, flags) 371 dev_t dev; 372 { 373 #ifdef PROFTIMER 374 #ifdef PROF 375 /* 376 * Kernel profiling enabled, give up. 377 */ 378 if (profiling) 379 return(EBUSY); 380 #endif 381 /* 382 * If any user processes are profiling, give up. 383 */ 384 if (profprocs) 385 return(EBUSY); 386 #endif 387 if (!clockon) { 388 startclock(); 389 clockon++; 390 } 391 return(0); 392 } 393 394 /*ARGSUSED*/ 395 clockclose(dev, flags) 396 dev_t dev; 397 { 398 (void) clockunmmap(dev, (caddr_t)0, curproc); /* XXX */ 399 stopclock(); 400 clockon = 0; 401 return(0); 402 } 403 404 /*ARGSUSED*/ 405 clockioctl(dev, cmd, data, flag, p) 406 dev_t dev; 407 u_long cmd; 408 caddr_t data; 409 struct proc *p; 410 { 411 int error = 0; 412 413 switch (cmd) { 414 415 case CLOCKMAP: 416 error = clockmmap(dev, (caddr_t *)data, p); 417 break; 418 419 case CLOCKUNMAP: 420 error = clockunmmap(dev, *(caddr_t *)data, p); 421 break; 422 423 case CLOCKGETRES: 424 *(int *)data = CLK_RESOLUTION; 425 break; 426 427 default: 428 error = EINVAL; 429 break; 430 } 431 return(error); 432 } 433 434 /*ARGSUSED*/ 435 clockmap(dev, off, prot) 436 dev_t dev; 437 { 438 return((off + (INTIOBASE+CLKBASE+CLKSR-1)) >> PGSHIFT); 439 } 440 441 clockmmap(dev, addrp, p) 442 dev_t dev; 443 caddr_t *addrp; 444 struct proc *p; 445 { 446 int error; 447 struct vnode vn; 448 struct specinfo si; 449 int flags; 450 451 flags = MAP_FILE|MAP_SHARED; 452 if (*addrp) 453 flags |= MAP_FIXED; 454 else 455 *addrp = (caddr_t)0x1000000; /* XXX */ 456 vn.v_type = VCHR; /* XXX */ 457 vn.v_specinfo = &si; /* XXX */ 458 vn.v_rdev = dev; /* XXX */ 459 error = vm_mmap(&p->p_vmspace->vm_map, (vm_offset_t *)addrp, 460 PAGE_SIZE, VM_PROT_ALL, flags, (caddr_t)&vn, 0); 461 return(error); 462 } 463 464 clockunmmap(dev, addr, p) 465 dev_t dev; 466 caddr_t addr; 467 struct proc *p; 468 { 469 int rv; 470 471 if (addr == 0) 472 return(EINVAL); /* XXX: how do we deal with this? */ 473 rv = vm_deallocate(p->p_vmspace->vm_map, (vm_offset_t)addr, PAGE_SIZE); 474 return(rv == KERN_SUCCESS ? 0 : EINVAL); 475 } 476 477 startclock() 478 { 479 register struct clkreg *clk = (struct clkreg *)clkstd[0]; 480 481 clk->clk_msb2 = -1; clk->clk_lsb2 = -1; 482 clk->clk_msb3 = -1; clk->clk_lsb3 = -1; 483 484 clk->clk_cr2 = CLK_CR3; 485 clk->clk_cr3 = CLK_OENAB|CLK_8BIT; 486 clk->clk_cr2 = CLK_CR1; 487 clk->clk_cr1 = CLK_IENAB; 488 } 489 490 stopclock() 491 { 492 register struct clkreg *clk = (struct clkreg *)clkstd[0]; 493 494 clk->clk_cr2 = CLK_CR3; 495 clk->clk_cr3 = 0; 496 clk->clk_cr2 = CLK_CR1; 497 clk->clk_cr1 = CLK_IENAB; 498 } 499 #endif 500 501 #endif 502 503 504 #ifdef PROFTIMER 505 /* 506 * This code allows the amiga kernel to use one of the extra timers on 507 * the clock chip for profiling, instead of the regular system timer. 508 * The advantage of this is that the profiling timer can be turned up to 509 * a higher interrupt rate, giving finer resolution timing. The profclock 510 * routine is called from the lev6intr in locore, and is a specialized 511 * routine that calls addupc. The overhead then is far less than if 512 * hardclock/softclock was called. Further, the context switch code in 513 * locore has been changed to turn the profile clock on/off when switching 514 * into/out of a process that is profiling (startprofclock/stopprofclock). 515 * This reduces the impact of the profiling clock on other users, and might 516 * possibly increase the accuracy of the profiling. 517 */ 518 int profint = PRF_INTERVAL; /* Clock ticks between interrupts */ 519 int profscale = 0; /* Scale factor from sys clock to prof clock */ 520 char profon = 0; /* Is profiling clock on? */ 521 522 /* profon values - do not change, locore.s assumes these values */ 523 #define PRF_NONE 0x00 524 #define PRF_USER 0x01 525 #define PRF_KERNEL 0x80 526 527 initprofclock() 528 { 529 #if NCLOCK > 0 530 struct proc *p = curproc; /* XXX */ 531 532 /* 533 * If the high-res timer is running, force profiling off. 534 * Unfortunately, this gets reflected back to the user not as 535 * an error but as a lack of results. 536 */ 537 if (clockon) { 538 p->p_stats->p_prof.pr_scale = 0; 539 return; 540 } 541 /* 542 * Keep track of the number of user processes that are profiling 543 * by checking the scale value. 544 * 545 * XXX: this all assumes that the profiling code is well behaved; 546 * i.e. profil() is called once per process with pcscale non-zero 547 * to turn it on, and once with pcscale zero to turn it off. 548 * Also assumes you don't do any forks or execs. Oh well, there 549 * is always adb... 550 */ 551 if (p->p_stats->p_prof.pr_scale) 552 profprocs++; 553 else 554 profprocs--; 555 #endif 556 /* 557 * The profile interrupt interval must be an even divisor 558 * of the CLK_INTERVAL so that scaling from a system clock 559 * tick to a profile clock tick is possible using integer math. 560 */ 561 if (profint > CLK_INTERVAL || (CLK_INTERVAL % profint) != 0) 562 profint = CLK_INTERVAL; 563 profscale = CLK_INTERVAL / profint; 564 } 565 566 startprofclock() 567 { 568 unsigned short interval; 569 570 /* stop timer B */ 571 clockcia->crb = clockcia->crb & 0xc0; 572 573 /* load interval into registers. 574 the clocks run at NTSC: 715.909kHz or PAL: 709.379kHz */ 575 576 interval = profint - 1; 577 578 /* order of setting is important ! */ 579 clockcia->tblo = interval & 0xff; 580 clockcia->tbhi = interval >> 8; 581 582 /* enable interrupts for timer B */ 583 clockcia->icr = (1<<7) | (1<<1); 584 585 /* start timer B in continuous shot mode */ 586 clockcia->crb = (clockcia->crb & 0xc0) | 1; 587 } 588 589 stopprofclock() 590 { 591 /* stop timer B */ 592 clockcia->crb = clockcia->crb & 0xc0; 593 } 594 595 #ifdef PROF 596 /* 597 * profclock() is expanded in line in lev6intr() unless profiling kernel. 598 * Assumes it is called with clock interrupts blocked. 599 */ 600 profclock(pc, ps) 601 caddr_t pc; 602 int ps; 603 { 604 /* 605 * Came from user mode. 606 * If this process is being profiled record the tick. 607 */ 608 if (USERMODE(ps)) { 609 if (p->p_stats.p_prof.pr_scale) 610 addupc(pc, &curproc->p_stats.p_prof, 1); 611 } 612 /* 613 * Came from kernel (supervisor) mode. 614 * If we are profiling the kernel, record the tick. 615 */ 616 else if (profiling < 2) { 617 register int s = pc - s_lowpc; 618 619 if (s < s_textsize) 620 kcount[s / (HISTFRACTION * sizeof (*kcount))]++; 621 } 622 /* 623 * Kernel profiling was on but has been disabled. 624 * Mark as no longer profiling kernel and if all profiling done, 625 * disable the clock. 626 */ 627 if (profiling && (profon & PRF_KERNEL)) { 628 profon &= ~PRF_KERNEL; 629 if (profon == PRF_NONE) 630 stopprofclock(); 631 } 632 } 633 #endif 634 #endif 635 636 /* this is a hook set by a clock driver for the configured realtime clock, 637 returning plain current unix-time */ 638 long (*gettod) __P((void)); 639 int (*settod) __P((long)); 640 void *clockaddr; 641 642 long a3gettod __P((void)); 643 long a2gettod __P((void)); 644 int a3settod __P((long)); 645 int a2settod __P((long)); 646 int rtcinit __P((void)); 647 648 /* 649 * Initialize the time of day register, based on the time base which is, e.g. 650 * from a filesystem. 651 */ 652 void 653 inittodr(base) 654 time_t base; 655 { 656 u_long timbuf = base; /* assume no battery clock exists */ 657 658 if (gettod == NULL && rtcinit() == 0) 659 printf("WARNING: no battery clock\n"); 660 else 661 timbuf = gettod(); 662 663 if (timbuf < base) { 664 printf("WARNING: bad date in battery clock\n"); 665 timbuf = base; 666 } 667 668 /* Battery clock does not store usec's, so forget about it. */ 669 time.tv_sec = timbuf; 670 } 671 672 void 673 resettodr() 674 { 675 if (settod && settod(time.tv_sec) == 0) 676 printf("Cannot set battery backed clock\n"); 677 } 678 679 int 680 rtcinit() 681 { 682 clockaddr = (void *)ztwomap(0xdc0000); 683 #ifdef DRACO 684 if (is_draco()) { 685 /* XXX to be done */ 686 gettod = (void *)0; 687 settod = (void *)0; 688 return 0; 689 } else 690 #endif 691 if (is_a3000() || is_a4000()) { 692 if (a3gettod() == 0) 693 return(0); 694 gettod = a3gettod; 695 settod = a3settod; 696 } else { 697 if (a2gettod() == 0) 698 return(0); 699 gettod = a2gettod; 700 settod = a2settod; 701 } 702 return(1); 703 } 704 705 static int month_days[12] = { 706 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 707 }; 708 709 long 710 a3gettod() 711 { 712 struct rtclock3000 *rt; 713 int i, year, month, day, wday, hour, min, sec; 714 u_long tmp; 715 716 rt = clockaddr; 717 718 /* hold clock */ 719 rt->control1 = A3CONTROL1_HOLD_CLOCK; 720 721 /* read it */ 722 sec = rt->second1 * 10 + rt->second2; 723 min = rt->minute1 * 10 + rt->minute2; 724 hour = rt->hour1 * 10 + rt->hour2; 725 wday = rt->weekday; 726 day = rt->day1 * 10 + rt->day2; 727 month = rt->month1 * 10 + rt->month2; 728 year = rt->year1 * 10 + rt->year2 + 1900; 729 730 /* let it run again.. */ 731 rt->control1 = A3CONTROL1_FREE_CLOCK; 732 733 if (range_test(hour, 0, 23)) 734 return(0); 735 if (range_test(wday, 0, 6)) 736 return(0); 737 if (range_test(day, 1, 31)) 738 return(0); 739 if (range_test(month, 1, 12)) 740 return(0); 741 if (range_test(year, STARTOFTIME, 2000)) 742 return(0); 743 744 tmp = 0; 745 746 for (i = STARTOFTIME; i < year; i++) 747 tmp += days_in_year(i); 748 if (leapyear(year) && month > FEBRUARY) 749 tmp++; 750 751 for (i = 1; i < month; i++) 752 tmp += days_in_month(i); 753 754 tmp += (day - 1); 755 tmp = ((tmp * 24 + hour) * 60 + min) * 60 + sec; 756 757 return(tmp); 758 } 759 760 int 761 a3settod(tim) 762 long tim; 763 { 764 register int i; 765 register long hms, day; 766 u_char sec1, sec2; 767 u_char min1, min2; 768 u_char hour1, hour2; 769 /* u_char wday; */ 770 u_char day1, day2; 771 u_char mon1, mon2; 772 u_char year1, year2; 773 struct rtclock3000 *rt; 774 775 rt = clockaddr; 776 /* 777 * there seem to be problems with the bitfield addressing 778 * currently used.. 779 */ 780 781 if (! rt) 782 return 0; 783 784 /* prepare values to be written to clock */ 785 day = tim / SECDAY; 786 hms = tim % SECDAY; 787 788 hour2 = hms / 3600; 789 hour1 = hour2 / 10; 790 hour2 %= 10; 791 792 min2 = (hms % 3600) / 60; 793 min1 = min2 / 10; 794 min2 %= 10; 795 796 797 sec2 = (hms % 3600) % 60; 798 sec1 = sec2 / 10; 799 sec2 %= 10; 800 801 /* Number of years in days */ 802 for (i = STARTOFTIME - 1900; day >= days_in_year(i); i++) 803 day -= days_in_year(i); 804 year1 = i / 10; 805 year2 = i % 10; 806 807 /* Number of months in days left */ 808 if (leapyear(i)) 809 days_in_month(FEBRUARY) = 29; 810 for (i = 1; day >= days_in_month(i); i++) 811 day -= days_in_month(i); 812 days_in_month(FEBRUARY) = 28; 813 814 mon1 = i / 10; 815 mon2 = i % 10; 816 817 /* Days are what is left over (+1) from all that. */ 818 day ++; 819 day1 = day / 10; 820 day2 = day % 10; 821 822 rt->control1 = A3CONTROL1_HOLD_CLOCK; 823 rt->second1 = sec1; 824 rt->second2 = sec2; 825 rt->minute1 = min1; 826 rt->minute2 = min2; 827 rt->hour1 = hour1; 828 rt->hour2 = hour2; 829 /* rt->weekday = wday; */ 830 rt->day1 = day1; 831 rt->day2 = day2; 832 rt->month1 = mon1; 833 rt->month2 = mon2; 834 rt->year1 = year1; 835 rt->year2 = year2; 836 rt->control1 = A3CONTROL1_FREE_CLOCK; 837 838 return 1; 839 } 840 841 long 842 a2gettod() 843 { 844 struct rtclock2000 *rt; 845 int i, year, month, day, hour, min, sec; 846 u_long tmp; 847 848 rt = clockaddr; 849 850 /* 851 * hold clock 852 */ 853 rt->control1 |= A2CONTROL1_HOLD; 854 i = 0x1000; 855 while (rt->control1 & A2CONTROL1_BUSY && i--) 856 ; 857 if (rt->control1 & A2CONTROL1_BUSY) 858 return (0); /* Give up and say it's not there */ 859 860 /* 861 * read it 862 */ 863 sec = rt->second1 * 10 + rt->second2; 864 min = rt->minute1 * 10 + rt->minute2; 865 hour = (rt->hour1 & 3) * 10 + rt->hour2; 866 day = rt->day1 * 10 + rt->day2; 867 month = rt->month1 * 10 + rt->month2; 868 year = rt->year1 * 10 + rt->year2 + 1900; 869 870 if ((rt->control3 & A2CONTROL3_24HMODE) == 0) { 871 if ((rt->hour1 & A2HOUR1_PM) == 0 && hour == 12) 872 hour = 0; 873 else if ((rt->hour1 & A2HOUR1_PM) && hour != 12) 874 hour += 12; 875 } 876 877 /* 878 * release the clock 879 */ 880 rt->control1 &= ~A2CONTROL1_HOLD; 881 882 if (range_test(hour, 0, 23)) 883 return(0); 884 if (range_test(day, 1, 31)) 885 return(0); 886 if (range_test(month, 1, 12)) 887 return(0); 888 if (range_test(year, STARTOFTIME, 2000)) 889 return(0); 890 891 tmp = 0; 892 893 for (i = STARTOFTIME; i < year; i++) 894 tmp += days_in_year(i); 895 if (leapyear(year) && month > FEBRUARY) 896 tmp++; 897 898 for (i = 1; i < month; i++) 899 tmp += days_in_month(i); 900 901 tmp += (day - 1); 902 tmp = ((tmp * 24 + hour) * 60 + min) * 60 + sec; 903 904 return(tmp); 905 } 906 907 /* 908 * there is some question as to whether this works 909 * I guess 910 */ 911 int 912 a2settod(tim) 913 long tim; 914 { 915 916 int i; 917 long hms, day; 918 u_char sec1, sec2; 919 u_char min1, min2; 920 u_char hour1, hour2; 921 u_char day1, day2; 922 u_char mon1, mon2; 923 u_char year1, year2; 924 struct rtclock2000 *rt; 925 926 rt = clockaddr; 927 /* 928 * there seem to be problems with the bitfield addressing 929 * currently used.. 930 * 931 * XXX Check out the above where we (hour1 & 3) 932 */ 933 if (! rt) 934 return 0; 935 936 /* prepare values to be written to clock */ 937 day = tim / SECDAY; 938 hms = tim % SECDAY; 939 940 hour2 = hms / 3600; 941 hour1 = hour2 / 10; 942 hour2 %= 10; 943 944 min2 = (hms % 3600) / 60; 945 min1 = min2 / 10; 946 min2 %= 10; 947 948 949 sec2 = (hms % 3600) % 60; 950 sec1 = sec2 / 10; 951 sec2 %= 10; 952 953 /* Number of years in days */ 954 for (i = STARTOFTIME - 1900; day >= days_in_year(i); i++) 955 day -= days_in_year(i); 956 year1 = i / 10; 957 year2 = i % 10; 958 959 /* Number of months in days left */ 960 if (leapyear(i)) 961 days_in_month(FEBRUARY) = 29; 962 for (i = 1; day >= days_in_month(i); i++) 963 day -= days_in_month(i); 964 days_in_month(FEBRUARY) = 28; 965 966 mon1 = i / 10; 967 mon2 = i % 10; 968 969 /* Days are what is left over (+1) from all that. */ 970 day ++; 971 day1 = day / 10; 972 day2 = day % 10; 973 974 /* 975 * XXXX spin wait as with reading??? 976 */ 977 rt->control1 |= A2CONTROL1_HOLD; 978 rt->second1 = sec1; 979 rt->second2 = sec2; 980 rt->minute1 = min1; 981 rt->minute2 = min2; 982 rt->hour1 = hour1; 983 rt->hour2 = hour2; 984 rt->day1 = day1; 985 rt->day2 = day2; 986 rt->month1 = mon1; 987 rt->month2 = mon2; 988 rt->year1 = year1; 989 rt->year2 = year2; 990 rt->control2 &= ~A2CONTROL1_HOLD; 991 992 return 1; 993 } 994