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