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