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