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