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