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