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