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