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