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