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