1 /* 2 * Copyright (c) 1988 University of Utah. 3 * Copyright (c) 1982, 1990 The Regents of the University of California. 4 * All rights reserved. 5 * 6 * This code is derived from software contributed to Berkeley by 7 * the Systems Programming Group of the University of Utah Computer 8 * Science Department. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the University of 21 * California, Berkeley and its contributors. 22 * 4. Neither the name of the University nor the names of its contributors 23 * may be used to endorse or promote products derived from this software 24 * without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 29 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 36 * SUCH DAMAGE. 37 * 38 * from: Utah $Hdr: clock.c 1.18 91/01/21$ 39 * 40 * @(#)clock.c 7.6 (Berkeley) 5/7/91 41 * $Id: clock.c,v 1.5 1994/06/16 14:28:45 chopps Exp $ 42 */ 43 44 #include <sys/param.h> 45 #include <sys/kernel.h> 46 #include <sys/device.h> 47 #include <machine/psl.h> 48 #include <machine/cpu.h> 49 #include <amiga/amiga/device.h> 50 #include <amiga/amiga/custom.h> 51 #include <amiga/amiga/cia.h> 52 #include <amiga/dev/rtc.h> 53 #include <amiga/dev/ztwobusvar.h> 54 55 #if defined(PROF) && defined(PROFTIMER) 56 #include <sys/PROF.h> 57 #endif 58 59 /* the clocks run at NTSC: 715.909kHz or PAL: 709.379kHz. 60 We're using a 100 Hz clock. */ 61 62 #define CLK_INTERVAL amiga_clk_interval 63 int amiga_clk_interval; 64 int eclockfreq; 65 66 /* 67 * Machine-dependent clock routines. 68 * 69 * Startrtclock restarts the real-time clock, which provides 70 * hardclock interrupts to kern_clock.c. 71 * 72 * Inittodr initializes the time of day hardware which provides 73 * date functions. 74 * 75 * Resettodr restores the time of day hardware after a time change. 76 * 77 * A note on the real-time clock: 78 * We actually load the clock with CLK_INTERVAL-1 instead of CLK_INTERVAL. 79 * This is because the counter decrements to zero after N+1 enabled clock 80 * periods where N is the value loaded into the counter. 81 */ 82 83 int clockmatch __P((struct device *, struct cfdata *, void *)); 84 void clockattach __P((struct device *, struct device *, void *)); 85 86 struct cfdriver clockcd = { 87 NULL, "clock", clockmatch, clockattach, 88 DV_DULL, sizeof(struct device), NULL, 0 }; 89 90 int 91 clockmatch(pdp, cfp, auxp) 92 struct device *pdp; 93 struct cfdata *cfp; 94 void *auxp; 95 { 96 if (matchname("clock", auxp)) 97 return(1); 98 return(0); 99 } 100 101 /* 102 * Start the real-time clock. 103 */ 104 void 105 clockattach(pdp, dp, auxp) 106 struct device *pdp, *dp; 107 void *auxp; 108 { 109 unsigned short interval; 110 111 if (eclockfreq == 0) 112 eclockfreq = 715909; /* guess NTSC */ 113 114 CLK_INTERVAL = (eclockfreq / 100); 115 116 printf(": system hz %d hardware hz %d\n", hz, eclockfreq); 117 118 /* 119 * stop timer A 120 */ 121 ciab.cra = ciab.cra & 0xc0; 122 ciab.icr = 1 << 0; /* disable timer A interrupt */ 123 interval = ciab.icr; /* and make sure it's clear */ 124 125 /* 126 * load interval into registers. 127 * the clocks run at NTSC: 715.909kHz or PAL: 709.379kHz 128 * supprort for PAL WHEN?!?! XXX 129 */ 130 interval = CLK_INTERVAL - 1; 131 132 /* 133 * order of setting is important ! 134 */ 135 ciab.talo = interval & 0xff; 136 ciab.tahi = interval >> 8; 137 } 138 139 void 140 cpu_initclocks() 141 { 142 /* 143 * enable interrupts for timer A 144 */ 145 ciab.icr = (1<<7) | (1<<0); 146 147 /* 148 * start timer A in continuous shot mode 149 */ 150 ciab.cra = (ciab.cra & 0xc0) | 1; 151 152 /* 153 * and globally enable interrupts for ciab 154 */ 155 custom.intena = INTF_SETCLR | INTF_EXTER; 156 } 157 158 setstatclockrate(hz) 159 int hz; 160 { 161 } 162 163 /* 164 * Returns number of usec since last recorded clock "tick" 165 * (i.e. clock interrupt). 166 */ 167 clkread() 168 { 169 u_char hi, hi2, lo; 170 u_int interval; 171 172 hi = ciab.tahi; 173 lo = ciab.talo; 174 hi2 = ciab.tahi; 175 if (hi != hi2) { 176 lo = ciab.talo; 177 hi = hi2; 178 } 179 180 interval = (CLK_INTERVAL - 1) - ((hi<<8) | lo); 181 182 /* 183 * should read ICR and if there's an int pending, adjust interval. 184 * However, * since reading ICR clears the interrupt, we'd lose a 185 * hardclock int, and * this is not tolerable. 186 */ 187 188 return((interval * tick) / CLK_INTERVAL); 189 } 190 191 u_int micspertick; 192 193 /* 194 * we set up as much of the CIAa as possible 195 * as all access to chip memory are very slow. 196 */ 197 void 198 setmicspertick() 199 { 200 micspertick = (1000000ULL << 20) / 715909; 201 202 /* 203 * disable interrupts (just in case.) 204 */ 205 ciaa.icr = 0x3; 206 207 /* 208 * stop both timers if not already 209 */ 210 ciaa.cra &= ~1; 211 ciaa.crb &= ~1; 212 213 /* 214 * set timer B in "count timer A underflows" mode 215 * set tiemr A in one-shot mode 216 */ 217 ciaa.crb = (ciaa.crb & 0x80) | 0x48; 218 ciaa.cra = (ciaa.cra & 0xc0) | 0x08; 219 } 220 221 /* 222 * this function assumes that on any entry beyond the first 223 * the following condintions exist: 224 * Interrupts for Timers A and B are disabled. 225 * Timers A and B are stoped. 226 * Timers A and B are in one-shot mode with B counting timer A underflows 227 * 228 */ 229 void 230 delay(mic) 231 int mic; 232 { 233 u_int temp; 234 int s; 235 236 if (micspertick == 0) 237 setmicspertick(); 238 239 if (mic <= 1) 240 return; 241 242 /* 243 * basically this is going to do an integer 244 * usec / (1000000 / 715909) with no loss of 245 * precision 246 */ 247 temp = mic >> 12; 248 asm("divul %3,%1:%0" : "=d" (temp) : "d" (mic >> 12), "0" (mic << 20), 249 "d" (micspertick)); 250 251 if ((temp & 0xffff0000) > 0x10000) { 252 mic = (temp >> 16) - 1; 253 temp &= 0xffff; 254 255 /* 256 * set timer A in continous mode 257 */ 258 ciaa.cra = (ciaa.cra & 0xc0) | 0x00; 259 260 /* 261 * latch/load/start "counts of timer A underflows" in B 262 */ 263 ciaa.tblo = mic & 0xff; 264 ciaa.tbhi = mic >> 8; 265 266 /* 267 * timer A latches 0xffff 268 * and start it. 269 */ 270 ciaa.talo = 0xff; 271 ciaa.tahi = 0xff; 272 ciaa.cra |= 1; 273 274 while (ciaa.crb & 1) 275 ; 276 277 /* 278 * stop timer A 279 */ 280 ciaa.cra &= ~1; 281 282 /* 283 * set timer A in one shot mode 284 */ 285 ciaa.cra = (ciaa.cra & 0xc0) | 0x08; 286 } else if ((temp & 0xffff0000) == 0x10000) { 287 temp &= 0xffff; 288 289 /* 290 * timer A is in one shot latch/load/start 1 full turn 291 */ 292 ciaa.talo = 0xff; 293 ciaa.tahi = 0xff; 294 while (ciaa.cra & 1) 295 ; 296 } 297 if (temp < 1) 298 return; 299 300 /* 301 * temp is now residual ammount, latch/load/start it. 302 */ 303 ciaa.talo = temp & 0xff; 304 ciaa.tahi = temp >> 8; 305 while (ciaa.cra & 1) 306 ; 307 } 308 309 /* 310 * Needs to be calibrated for use, its way off most of the time 311 */ 312 void 313 DELAY(mic) 314 int mic; 315 { 316 u_long n; 317 short hpos; 318 319 /* 320 * this function uses HSync pulses as base units. The custom chips 321 * display only deals with 31.6kHz/2 refresh, this gives us a 322 * resolution of 1/15800 s, which is ~63us (add some fuzz so we really 323 * wait awhile, even if using small timeouts) 324 */ 325 n = mic/63 + 2; 326 do { 327 hpos = custom.vhposr & 0xff00; 328 while (hpos == (custom.vhposr & 0xff00)) 329 ; 330 } while (n--); 331 } 332 333 #if notyet 334 335 /* implement this later. I'd suggest using both timers in CIA-A, they're 336 not yet used. */ 337 338 #include "clock.h" 339 #if NCLOCK > 0 340 /* 341 * /dev/clock: mappable high resolution timer. 342 * 343 * This code implements a 32-bit recycling counter (with a 4 usec period) 344 * using timers 2 & 3 on the 6840 clock chip. The counter can be mapped 345 * RO into a user's address space to achieve low overhead (no system calls), 346 * high-precision timing. 347 * 348 * Note that timer 3 is also used for the high precision profiling timer 349 * (PROFTIMER code above). Care should be taken when both uses are 350 * configured as only a token effort is made to avoid conflicting use. 351 */ 352 #include <sys/proc.h> 353 #include <sys/resourcevar.h> 354 #include <sys/ioctl.h> 355 #include <sys/malloc.h> 356 #include <vm/vm.h> 357 #include <amiga/amiga/clockioctl.h> 358 #include <sys/specdev.h> 359 #include <sys/vnode.h> 360 #include <sys/mman.h> 361 362 int clockon = 0; /* non-zero if high-res timer enabled */ 363 #ifdef PROFTIMER 364 int profprocs = 0; /* # of procs using profiling timer */ 365 #endif 366 #ifdef DEBUG 367 int clockdebug = 0; 368 #endif 369 370 /*ARGSUSED*/ 371 clockopen(dev, flags) 372 dev_t dev; 373 { 374 #ifdef PROFTIMER 375 #ifdef PROF 376 /* 377 * Kernel profiling enabled, give up. 378 */ 379 if (profiling) 380 return(EBUSY); 381 #endif 382 /* 383 * If any user processes are profiling, give up. 384 */ 385 if (profprocs) 386 return(EBUSY); 387 #endif 388 if (!clockon) { 389 startclock(); 390 clockon++; 391 } 392 return(0); 393 } 394 395 /*ARGSUSED*/ 396 clockclose(dev, flags) 397 dev_t dev; 398 { 399 (void) clockunmmap(dev, (caddr_t)0, curproc); /* XXX */ 400 stopclock(); 401 clockon = 0; 402 return(0); 403 } 404 405 /*ARGSUSED*/ 406 clockioctl(dev, cmd, data, flag, p) 407 dev_t dev; 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 ciab.crb = ciab.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 ciab.tblo = interval & 0xff; 580 ciab.tbhi = interval >> 8; 581 582 /* enable interrupts for timer B */ 583 ciab.icr = (1<<7) | (1<<1); 584 585 /* start timer B in continuous shot mode */ 586 ciab.crb = (ciab.crb & 0xc0) | 1; 587 } 588 589 stopprofclock() 590 { 591 /* stop timer B */ 592 ciab.crb = ciab.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 inittodr(base) 653 time_t base; 654 { 655 u_long timbuf = base; /* assume no battery clock exists */ 656 657 if (gettod == NULL && rtcinit() == 0) 658 printf("WARNING: no battery clock\n"); 659 else 660 timbuf = gettod(); 661 662 if (timbuf < base) { 663 printf("WARNING: bad date in battery clock\n"); 664 timbuf = base; 665 } 666 667 /* Battery clock does not store usec's, so forget about it. */ 668 time.tv_sec = timbuf; 669 } 670 671 resettodr() 672 { 673 if (settod && settod(time.tv_sec) == 1) 674 return; 675 printf("Cannot set battery backed clock\n"); 676 } 677 678 int 679 rtcinit() 680 { 681 clockaddr = (void *)ztwomap(0xdc0000); 682 if (is_a3000() || is_a4000()) { 683 if (a3gettod() == 0) 684 return(0); 685 gettod = a3gettod; 686 settod = a3settod; 687 } else { 688 if (a2gettod() == 0) 689 return(0); 690 gettod = a2gettod; 691 settod = a2settod; 692 } 693 return(1); 694 } 695 696 static int month_days[12] = { 697 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 698 }; 699 700 long 701 a3gettod() 702 { 703 struct rtclock3000 *rt; 704 int i, year, month, day, hour, min, sec; 705 u_long tmp; 706 707 rt = clockaddr; 708 709 /* hold clock */ 710 rt->control1 = A3CONTROL1_HOLD_CLOCK; 711 712 /* read it */ 713 sec = rt->second1 * 10 + rt->second2; 714 min = rt->minute1 * 10 + rt->minute2; 715 hour = rt->hour1 * 10 + rt->hour2; 716 day = rt->day1 * 10 + rt->day2; 717 month = rt->month1 * 10 + rt->month2; 718 year = rt->year1 * 10 + rt->year2 + 1900; 719 720 /* let it run again.. */ 721 rt->control1 = A3CONTROL1_FREE_CLOCK; 722 723 if (range_test(hour, 0, 23)) 724 return(0); 725 if (range_test(day, 1, 31)) 726 return(0); 727 if (range_test(month, 1, 12)) 728 return(0); 729 if (range_test(year, STARTOFTIME, 2000)) 730 return(0); 731 732 tmp = 0; 733 734 for (i = STARTOFTIME; i < year; i++) 735 tmp += days_in_year(i); 736 if (leapyear(year) && month > FEBRUARY) 737 tmp++; 738 739 for (i = 1; i < month; i++) 740 tmp += days_in_month(i); 741 742 tmp += (day - 1); 743 tmp = ((tmp * 24 + hour) * 60 + min) * 60 + sec; 744 745 return(tmp); 746 } 747 748 int 749 a3settod(tim) 750 long tim; 751 { 752 register int i; 753 register long hms, day; 754 u_char sec1, sec2; 755 u_char min1, min2; 756 u_char hour1, hour2; 757 u_char day1, day2; 758 u_char mon1, mon2; 759 u_char year1, year2; 760 struct rtclock3000 *rt; 761 762 rt = clockaddr; 763 /* 764 * there seem to be problems with the bitfield addressing 765 * currently used.. 766 */ 767 return(0); 768 #if not_yet 769 if (rt) 770 return 0; 771 772 /* prepare values to be written to clock */ 773 day = tim / SECDAY; 774 hms = tim % SECDAY; 775 776 hour2 = hms / 3600; 777 hour1 = hour2 / 10; 778 hour2 %= 10; 779 780 min2 = (hms % 3600) / 60; 781 min1 = min2 / 10; 782 min2 %= 10; 783 784 785 sec2 = (hms % 3600) % 60; 786 sec1 = sec2 / 10; 787 sec2 %= 10; 788 789 /* Number of years in days */ 790 for (i = STARTOFTIME - 1900; day >= days_in_year(i); i++) 791 day -= days_in_year(i); 792 year1 = i / 10; 793 year2 = i % 10; 794 795 /* Number of months in days left */ 796 if (leapyear(i)) 797 days_in_month(FEBRUARY) = 29; 798 for (i = 1; day >= days_in_month(i); i++) 799 day -= days_in_month(i); 800 days_in_month(FEBRUARY) = 28; 801 802 mon1 = i / 10; 803 mon2 = i % 10; 804 805 /* Days are what is left over (+1) from all that. */ 806 day ++; 807 day1 = day / 10; 808 day2 = day % 10; 809 810 rt->control1 = CONTROL1_HOLD_CLOCK; 811 rt->second1 = sec1; 812 rt->second2 = sec2; 813 rt->minute1 = min1; 814 rt->minute2 = min2; 815 rt->hour1 = hour1; 816 rt->hour2 = hour2; 817 rt->day1 = day1; 818 rt->day2 = day2; 819 rt->month1 = mon1; 820 rt->month2 = mon2; 821 rt->year1 = year1; 822 rt->year2 = year2; 823 rt->control2 = CONTROL1_FREE_CLOCK; 824 825 return 1; 826 #endif 827 } 828 829 long 830 a2gettod() 831 { 832 struct rtclock2000 *rt; 833 int i, year, month, day, hour, min, sec; 834 u_long tmp; 835 836 rt = clockaddr; 837 838 /* 839 * hold clock 840 */ 841 rt->control1 |= A2CONTROL1_HOLD; 842 while (rt->control1 & A2CONTROL1_BUSY) 843 ; 844 845 /* 846 * read it 847 */ 848 sec = rt->second1 * 10 + rt->second2; 849 min = rt->minute1 * 10 + rt->minute2; 850 hour = (rt->hour1 & 3) * 10 + rt->hour2; 851 day = rt->day1 * 10 + rt->day2; 852 month = rt->month1 * 10 + rt->month2; 853 year = rt->year1 * 10 + rt->year2 + 1900; 854 855 if ((rt->control3 & A2CONTROL3_24HMODE) == 0) { 856 if ((rt->hour1 & A2HOUR1_PM) == 0 && hour == 12) 857 hour = 0; 858 else if ((rt->hour1 & A2HOUR1_PM) && hour != 12) 859 hour += 12; 860 } 861 862 /* 863 * release the clock 864 */ 865 rt->control1 &= ~A2CONTROL1_HOLD; 866 867 if (range_test(hour, 0, 23)) 868 return(0); 869 if (range_test(day, 1, 31)) 870 return(0); 871 if (range_test(month, 1, 12)) 872 return(0); 873 if (range_test(year, STARTOFTIME, 2000)) 874 return(0); 875 876 tmp = 0; 877 878 for (i = STARTOFTIME; i < year; i++) 879 tmp += days_in_year(i); 880 if (leapyear(year) && month > FEBRUARY) 881 tmp++; 882 883 for (i = 1; i < month; i++) 884 tmp += days_in_month(i); 885 886 tmp += (day - 1); 887 tmp = ((tmp * 24 + hour) * 60 + min) * 60 + sec; 888 889 return(tmp); 890 } 891 892 /* 893 * there is some question as to whether this works 894 * I guess 895 */ 896 int 897 a2settod(tim) 898 long tim; 899 { 900 901 int i; 902 long hms, day; 903 u_char sec1, sec2; 904 u_char min1, min2; 905 u_char hour1, hour2; 906 u_char day1, day2; 907 u_char mon1, mon2; 908 u_char year1, year2; 909 struct rtclock2000 *rt; 910 911 rt = clockaddr; 912 /* 913 * there seem to be problems with the bitfield addressing 914 * currently used.. 915 * 916 * XXX Check out the above where we (hour1 & 3) 917 */ 918 return(0); 919 #if not_yet 920 if (! rt) 921 return 0; 922 923 /* prepare values to be written to clock */ 924 day = tim / SECDAY; 925 hms = tim % SECDAY; 926 927 hour2 = hms / 3600; 928 hour1 = hour2 / 10; 929 hour2 %= 10; 930 931 min2 = (hms % 3600) / 60; 932 min1 = min2 / 10; 933 min2 %= 10; 934 935 936 sec2 = (hms % 3600) % 60; 937 sec1 = sec2 / 10; 938 sec2 %= 10; 939 940 /* Number of years in days */ 941 for (i = STARTOFTIME - 1900; day >= days_in_year(i); i++) 942 day -= days_in_year(i); 943 year1 = i / 10; 944 year2 = i % 10; 945 946 /* Number of months in days left */ 947 if (leapyear(i)) 948 days_in_month(FEBRUARY) = 29; 949 for (i = 1; day >= days_in_month(i); i++) 950 day -= days_in_month(i); 951 days_in_month(FEBRUARY) = 28; 952 953 mon1 = i / 10; 954 mon2 = i % 10; 955 956 /* Days are what is left over (+1) from all that. */ 957 day ++; 958 day1 = day / 10; 959 day2 = day % 10; 960 961 /* 962 * XXXX spin wait as with reading??? 963 */ 964 rt->control1 = A2CONTROL1_HOLD_CLOCK; 965 rt->second1 = sec1; 966 rt->second2 = sec2; 967 rt->minute1 = min1; 968 rt->minute2 = min2; 969 rt->hour1 = hour1; 970 rt->hour2 = hour2; 971 rt->day1 = day1; 972 rt->day2 = day2; 973 rt->month1 = mon1; 974 rt->month2 = mon2; 975 rt->year1 = year1; 976 rt->year2 = year2; 977 rt->control2 = CONTROL1_FREE_CLOCK; 978 979 return 1; 980 #endif 981 } 982