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