clock.c revision 1.6 1 /* $NetBSD: clock.c,v 1.6 1994/10/26 02:02:51 cgd 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/ztwobusvar.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", 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 caddr_t data;
410 struct proc *p;
411 {
412 int error = 0;
413
414 switch (cmd) {
415
416 case CLOCKMAP:
417 error = clockmmap(dev, (caddr_t *)data, p);
418 break;
419
420 case CLOCKUNMAP:
421 error = clockunmmap(dev, *(caddr_t *)data, p);
422 break;
423
424 case CLOCKGETRES:
425 *(int *)data = CLK_RESOLUTION;
426 break;
427
428 default:
429 error = EINVAL;
430 break;
431 }
432 return(error);
433 }
434
435 /*ARGSUSED*/
436 clockmap(dev, off, prot)
437 dev_t dev;
438 {
439 return((off + (INTIOBASE+CLKBASE+CLKSR-1)) >> PGSHIFT);
440 }
441
442 clockmmap(dev, addrp, p)
443 dev_t dev;
444 caddr_t *addrp;
445 struct proc *p;
446 {
447 int error;
448 struct vnode vn;
449 struct specinfo si;
450 int flags;
451
452 flags = MAP_FILE|MAP_SHARED;
453 if (*addrp)
454 flags |= MAP_FIXED;
455 else
456 *addrp = (caddr_t)0x1000000; /* XXX */
457 vn.v_type = VCHR; /* XXX */
458 vn.v_specinfo = &si; /* XXX */
459 vn.v_rdev = dev; /* XXX */
460 error = vm_mmap(&p->p_vmspace->vm_map, (vm_offset_t *)addrp,
461 PAGE_SIZE, VM_PROT_ALL, flags, (caddr_t)&vn, 0);
462 return(error);
463 }
464
465 clockunmmap(dev, addr, p)
466 dev_t dev;
467 caddr_t addr;
468 struct proc *p;
469 {
470 int rv;
471
472 if (addr == 0)
473 return(EINVAL); /* XXX: how do we deal with this? */
474 rv = vm_deallocate(p->p_vmspace->vm_map, (vm_offset_t)addr, PAGE_SIZE);
475 return(rv == KERN_SUCCESS ? 0 : EINVAL);
476 }
477
478 startclock()
479 {
480 register struct clkreg *clk = (struct clkreg *)clkstd[0];
481
482 clk->clk_msb2 = -1; clk->clk_lsb2 = -1;
483 clk->clk_msb3 = -1; clk->clk_lsb3 = -1;
484
485 clk->clk_cr2 = CLK_CR3;
486 clk->clk_cr3 = CLK_OENAB|CLK_8BIT;
487 clk->clk_cr2 = CLK_CR1;
488 clk->clk_cr1 = CLK_IENAB;
489 }
490
491 stopclock()
492 {
493 register struct clkreg *clk = (struct clkreg *)clkstd[0];
494
495 clk->clk_cr2 = CLK_CR3;
496 clk->clk_cr3 = 0;
497 clk->clk_cr2 = CLK_CR1;
498 clk->clk_cr1 = CLK_IENAB;
499 }
500 #endif
501
502 #endif
503
504
505 #ifdef PROFTIMER
506 /*
507 * This code allows the amiga kernel to use one of the extra timers on
508 * the clock chip for profiling, instead of the regular system timer.
509 * The advantage of this is that the profiling timer can be turned up to
510 * a higher interrupt rate, giving finer resolution timing. The profclock
511 * routine is called from the lev6intr in locore, and is a specialized
512 * routine that calls addupc. The overhead then is far less than if
513 * hardclock/softclock was called. Further, the context switch code in
514 * locore has been changed to turn the profile clock on/off when switching
515 * into/out of a process that is profiling (startprofclock/stopprofclock).
516 * This reduces the impact of the profiling clock on other users, and might
517 * possibly increase the accuracy of the profiling.
518 */
519 int profint = PRF_INTERVAL; /* Clock ticks between interrupts */
520 int profscale = 0; /* Scale factor from sys clock to prof clock */
521 char profon = 0; /* Is profiling clock on? */
522
523 /* profon values - do not change, locore.s assumes these values */
524 #define PRF_NONE 0x00
525 #define PRF_USER 0x01
526 #define PRF_KERNEL 0x80
527
528 initprofclock()
529 {
530 #if NCLOCK > 0
531 struct proc *p = curproc; /* XXX */
532
533 /*
534 * If the high-res timer is running, force profiling off.
535 * Unfortunately, this gets reflected back to the user not as
536 * an error but as a lack of results.
537 */
538 if (clockon) {
539 p->p_stats->p_prof.pr_scale = 0;
540 return;
541 }
542 /*
543 * Keep track of the number of user processes that are profiling
544 * by checking the scale value.
545 *
546 * XXX: this all assumes that the profiling code is well behaved;
547 * i.e. profil() is called once per process with pcscale non-zero
548 * to turn it on, and once with pcscale zero to turn it off.
549 * Also assumes you don't do any forks or execs. Oh well, there
550 * is always adb...
551 */
552 if (p->p_stats->p_prof.pr_scale)
553 profprocs++;
554 else
555 profprocs--;
556 #endif
557 /*
558 * The profile interrupt interval must be an even divisor
559 * of the CLK_INTERVAL so that scaling from a system clock
560 * tick to a profile clock tick is possible using integer math.
561 */
562 if (profint > CLK_INTERVAL || (CLK_INTERVAL % profint) != 0)
563 profint = CLK_INTERVAL;
564 profscale = CLK_INTERVAL / profint;
565 }
566
567 startprofclock()
568 {
569 unsigned short interval;
570
571 /* stop timer B */
572 ciab.crb = ciab.crb & 0xc0;
573
574 /* load interval into registers.
575 the clocks run at NTSC: 715.909kHz or PAL: 709.379kHz */
576
577 interval = profint - 1;
578
579 /* order of setting is important ! */
580 ciab.tblo = interval & 0xff;
581 ciab.tbhi = interval >> 8;
582
583 /* enable interrupts for timer B */
584 ciab.icr = (1<<7) | (1<<1);
585
586 /* start timer B in continuous shot mode */
587 ciab.crb = (ciab.crb & 0xc0) | 1;
588 }
589
590 stopprofclock()
591 {
592 /* stop timer B */
593 ciab.crb = ciab.crb & 0xc0;
594 }
595
596 #ifdef PROF
597 /*
598 * profclock() is expanded in line in lev6intr() unless profiling kernel.
599 * Assumes it is called with clock interrupts blocked.
600 */
601 profclock(pc, ps)
602 caddr_t pc;
603 int ps;
604 {
605 /*
606 * Came from user mode.
607 * If this process is being profiled record the tick.
608 */
609 if (USERMODE(ps)) {
610 if (p->p_stats.p_prof.pr_scale)
611 addupc(pc, &curproc->p_stats.p_prof, 1);
612 }
613 /*
614 * Came from kernel (supervisor) mode.
615 * If we are profiling the kernel, record the tick.
616 */
617 else if (profiling < 2) {
618 register int s = pc - s_lowpc;
619
620 if (s < s_textsize)
621 kcount[s / (HISTFRACTION * sizeof (*kcount))]++;
622 }
623 /*
624 * Kernel profiling was on but has been disabled.
625 * Mark as no longer profiling kernel and if all profiling done,
626 * disable the clock.
627 */
628 if (profiling && (profon & PRF_KERNEL)) {
629 profon &= ~PRF_KERNEL;
630 if (profon == PRF_NONE)
631 stopprofclock();
632 }
633 }
634 #endif
635 #endif
636
637 /* this is a hook set by a clock driver for the configured realtime clock,
638 returning plain current unix-time */
639 long (*gettod) __P((void));
640 int (*settod) __P((long));
641 void *clockaddr;
642
643 long a3gettod __P((void));
644 long a2gettod __P((void));
645 int a3settod __P((long));
646 int a2settod __P((long));
647 int rtcinit __P((void));
648
649 /*
650 * Initialize the time of day register, based on the time base which is, e.g.
651 * from a filesystem.
652 */
653 inittodr(base)
654 time_t base;
655 {
656 u_long timbuf = base; /* assume no battery clock exists */
657
658 if (gettod == NULL && rtcinit() == 0)
659 printf("WARNING: no battery clock\n");
660 else
661 timbuf = gettod();
662
663 if (timbuf < base) {
664 printf("WARNING: bad date in battery clock\n");
665 timbuf = base;
666 }
667
668 /* Battery clock does not store usec's, so forget about it. */
669 time.tv_sec = timbuf;
670 }
671
672 resettodr()
673 {
674 if (settod && settod(time.tv_sec) == 1)
675 return;
676 printf("Cannot set battery backed clock\n");
677 }
678
679 int
680 rtcinit()
681 {
682 clockaddr = (void *)ztwomap(0xdc0000);
683 if (is_a3000() || is_a4000()) {
684 if (a3gettod() == 0)
685 return(0);
686 gettod = a3gettod;
687 settod = a3settod;
688 } else {
689 if (a2gettod() == 0)
690 return(0);
691 gettod = a2gettod;
692 settod = a2settod;
693 }
694 return(1);
695 }
696
697 static int month_days[12] = {
698 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
699 };
700
701 long
702 a3gettod()
703 {
704 struct rtclock3000 *rt;
705 int i, year, month, day, hour, min, sec;
706 u_long tmp;
707
708 rt = clockaddr;
709
710 /* hold clock */
711 rt->control1 = A3CONTROL1_HOLD_CLOCK;
712
713 /* read it */
714 sec = rt->second1 * 10 + rt->second2;
715 min = rt->minute1 * 10 + rt->minute2;
716 hour = rt->hour1 * 10 + rt->hour2;
717 day = rt->day1 * 10 + rt->day2;
718 month = rt->month1 * 10 + rt->month2;
719 year = rt->year1 * 10 + rt->year2 + 1900;
720
721 /* let it run again.. */
722 rt->control1 = A3CONTROL1_FREE_CLOCK;
723
724 if (range_test(hour, 0, 23))
725 return(0);
726 if (range_test(day, 1, 31))
727 return(0);
728 if (range_test(month, 1, 12))
729 return(0);
730 if (range_test(year, STARTOFTIME, 2000))
731 return(0);
732
733 tmp = 0;
734
735 for (i = STARTOFTIME; i < year; i++)
736 tmp += days_in_year(i);
737 if (leapyear(year) && month > FEBRUARY)
738 tmp++;
739
740 for (i = 1; i < month; i++)
741 tmp += days_in_month(i);
742
743 tmp += (day - 1);
744 tmp = ((tmp * 24 + hour) * 60 + min) * 60 + sec;
745
746 return(tmp);
747 }
748
749 int
750 a3settod(tim)
751 long tim;
752 {
753 register int i;
754 register long hms, day;
755 u_char sec1, sec2;
756 u_char min1, min2;
757 u_char hour1, hour2;
758 u_char day1, day2;
759 u_char mon1, mon2;
760 u_char year1, year2;
761 struct rtclock3000 *rt;
762
763 rt = clockaddr;
764 /*
765 * there seem to be problems with the bitfield addressing
766 * currently used..
767 */
768 return(0);
769 #if not_yet
770 if (rt)
771 return 0;
772
773 /* prepare values to be written to clock */
774 day = tim / SECDAY;
775 hms = tim % SECDAY;
776
777 hour2 = hms / 3600;
778 hour1 = hour2 / 10;
779 hour2 %= 10;
780
781 min2 = (hms % 3600) / 60;
782 min1 = min2 / 10;
783 min2 %= 10;
784
785
786 sec2 = (hms % 3600) % 60;
787 sec1 = sec2 / 10;
788 sec2 %= 10;
789
790 /* Number of years in days */
791 for (i = STARTOFTIME - 1900; day >= days_in_year(i); i++)
792 day -= days_in_year(i);
793 year1 = i / 10;
794 year2 = i % 10;
795
796 /* Number of months in days left */
797 if (leapyear(i))
798 days_in_month(FEBRUARY) = 29;
799 for (i = 1; day >= days_in_month(i); i++)
800 day -= days_in_month(i);
801 days_in_month(FEBRUARY) = 28;
802
803 mon1 = i / 10;
804 mon2 = i % 10;
805
806 /* Days are what is left over (+1) from all that. */
807 day ++;
808 day1 = day / 10;
809 day2 = day % 10;
810
811 rt->control1 = CONTROL1_HOLD_CLOCK;
812 rt->second1 = sec1;
813 rt->second2 = sec2;
814 rt->minute1 = min1;
815 rt->minute2 = min2;
816 rt->hour1 = hour1;
817 rt->hour2 = hour2;
818 rt->day1 = day1;
819 rt->day2 = day2;
820 rt->month1 = mon1;
821 rt->month2 = mon2;
822 rt->year1 = year1;
823 rt->year2 = year2;
824 rt->control2 = CONTROL1_FREE_CLOCK;
825
826 return 1;
827 #endif
828 }
829
830 long
831 a2gettod()
832 {
833 struct rtclock2000 *rt;
834 int i, year, month, day, hour, min, sec;
835 u_long tmp;
836
837 rt = clockaddr;
838
839 /*
840 * hold clock
841 */
842 rt->control1 |= A2CONTROL1_HOLD;
843 while (rt->control1 & A2CONTROL1_BUSY)
844 ;
845
846 /*
847 * read it
848 */
849 sec = rt->second1 * 10 + rt->second2;
850 min = rt->minute1 * 10 + rt->minute2;
851 hour = (rt->hour1 & 3) * 10 + rt->hour2;
852 day = rt->day1 * 10 + rt->day2;
853 month = rt->month1 * 10 + rt->month2;
854 year = rt->year1 * 10 + rt->year2 + 1900;
855
856 if ((rt->control3 & A2CONTROL3_24HMODE) == 0) {
857 if ((rt->hour1 & A2HOUR1_PM) == 0 && hour == 12)
858 hour = 0;
859 else if ((rt->hour1 & A2HOUR1_PM) && hour != 12)
860 hour += 12;
861 }
862
863 /*
864 * release the clock
865 */
866 rt->control1 &= ~A2CONTROL1_HOLD;
867
868 if (range_test(hour, 0, 23))
869 return(0);
870 if (range_test(day, 1, 31))
871 return(0);
872 if (range_test(month, 1, 12))
873 return(0);
874 if (range_test(year, STARTOFTIME, 2000))
875 return(0);
876
877 tmp = 0;
878
879 for (i = STARTOFTIME; i < year; i++)
880 tmp += days_in_year(i);
881 if (leapyear(year) && month > FEBRUARY)
882 tmp++;
883
884 for (i = 1; i < month; i++)
885 tmp += days_in_month(i);
886
887 tmp += (day - 1);
888 tmp = ((tmp * 24 + hour) * 60 + min) * 60 + sec;
889
890 return(tmp);
891 }
892
893 /*
894 * there is some question as to whether this works
895 * I guess
896 */
897 int
898 a2settod(tim)
899 long tim;
900 {
901
902 int i;
903 long hms, day;
904 u_char sec1, sec2;
905 u_char min1, min2;
906 u_char hour1, hour2;
907 u_char day1, day2;
908 u_char mon1, mon2;
909 u_char year1, year2;
910 struct rtclock2000 *rt;
911
912 rt = clockaddr;
913 /*
914 * there seem to be problems with the bitfield addressing
915 * currently used..
916 *
917 * XXX Check out the above where we (hour1 & 3)
918 */
919 return(0);
920 #if not_yet
921 if (! rt)
922 return 0;
923
924 /* prepare values to be written to clock */
925 day = tim / SECDAY;
926 hms = tim % SECDAY;
927
928 hour2 = hms / 3600;
929 hour1 = hour2 / 10;
930 hour2 %= 10;
931
932 min2 = (hms % 3600) / 60;
933 min1 = min2 / 10;
934 min2 %= 10;
935
936
937 sec2 = (hms % 3600) % 60;
938 sec1 = sec2 / 10;
939 sec2 %= 10;
940
941 /* Number of years in days */
942 for (i = STARTOFTIME - 1900; day >= days_in_year(i); i++)
943 day -= days_in_year(i);
944 year1 = i / 10;
945 year2 = i % 10;
946
947 /* Number of months in days left */
948 if (leapyear(i))
949 days_in_month(FEBRUARY) = 29;
950 for (i = 1; day >= days_in_month(i); i++)
951 day -= days_in_month(i);
952 days_in_month(FEBRUARY) = 28;
953
954 mon1 = i / 10;
955 mon2 = i % 10;
956
957 /* Days are what is left over (+1) from all that. */
958 day ++;
959 day1 = day / 10;
960 day2 = day % 10;
961
962 /*
963 * XXXX spin wait as with reading???
964 */
965 rt->control1 = A2CONTROL1_HOLD_CLOCK;
966 rt->second1 = sec1;
967 rt->second2 = sec2;
968 rt->minute1 = min1;
969 rt->minute2 = min2;
970 rt->hour1 = hour1;
971 rt->hour2 = hour2;
972 rt->day1 = day1;
973 rt->day2 = day2;
974 rt->month1 = mon1;
975 rt->month2 = mon2;
976 rt->year1 = year1;
977 rt->year2 = year2;
978 rt->control2 = CONTROL1_FREE_CLOCK;
979
980 return 1;
981 #endif
982 }
983