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