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