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