Home | History | Annotate | Line # | Download | only in dev
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