Home | History | Annotate | Line # | Download | only in dev
clock.c revision 1.10
      1 /*	$NetBSD: clock.c,v 1.10 1996/03/17 01:26:44 thorpej 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/systm.h>
     48 #include <sys/device.h>
     49 #include <sys/cpu.h>
     50 #include <machine/psl.h>
     51 #include <machine/cpu.h>
     52 #include <machine/iomap.h>
     53 #include <machine/mfp.h>
     54 #include <atari/dev/clockreg.h>
     55 
     56 #if defined(GPROF) && defined(PROFTIMER)
     57 #include <machine/profile.h>
     58 #endif
     59 
     60 /*
     61  * The MFP clock runs at 2457600Hz. We use a {system,stat,prof}clock divider
     62  * of 200. Therefore the timer runs at an effective rate of:
     63  * 2457600/200 = 12288Hz.
     64  */
     65 #define CLOCK_HZ	12288
     66 
     67 /*
     68  * Machine-dependent clock routines.
     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 
     76 int	clockmatch __P((struct device *, void *, void *));
     77 void	clockattach __P((struct device *, struct device *, void *));
     78 
     79 struct cfattach clock_ca = {
     80 	sizeof(struct device), clockmatch, clockattach
     81 };
     82 
     83 struct cfdriver clock_cd = {
     84 	NULL, "clock", DV_DULL, NULL, 0
     85 };
     86 
     87 void statintr __P((struct clockframe *));
     88 
     89 static u_long	gettod __P((void));
     90 static int	settod __P((u_long));
     91 
     92 static int	divisor;	/* Systemclock divisor	*/
     93 
     94 /*
     95  * Statistics and profile clock intervals and variances. Variance must
     96  * be a power of 2. Since this gives us an even number, not an odd number,
     97  * we discard one case and compensate. That is, a variance of 64 would
     98  * give us offsets in [0..63]. Instead, we take offsets in [1..63].
     99  * This is symetric around the point 32, or statvar/2, and thus averages
    100  * to that value (assuming uniform random numbers).
    101  */
    102 #ifdef STATCLOCK
    103 static int	statvar = 32;	/* {stat,prof}clock variance		*/
    104 static int	statmin;	/* statclock divisor - variance/2	*/
    105 static int	profmin;	/* profclock divisor - variance/2	*/
    106 static int	clk2min;	/* current, from above choises		*/
    107 #endif
    108 
    109 int
    110 clockmatch(pdp, match, auxp)
    111 struct device *pdp;
    112 void *match, *auxp;
    113 {
    114 	if(!strcmp("clock", auxp))
    115 		return(1);
    116 	return(0);
    117 }
    118 
    119 /*
    120  * Start the real-time clock.
    121  */
    122 void clockattach(pdp, dp, auxp)
    123 struct device	*pdp, *dp;
    124 void			*auxp;
    125 {
    126 	/*
    127 	 * Initialize Timer-A in the ST-MFP. We use a divisor of 200.
    128 	 * The MFP clock runs at 2457600Hz. Therefore the timer runs
    129 	 * at an effective rate of: 2457600/200 = 12288Hz. The
    130 	 * following expression works for 48, 64 or 96 hz.
    131 	 */
    132 	divisor       = CLOCK_HZ/hz;
    133 	MFP->mf_tacr  = 0;		/* Stop timer			*/
    134 	MFP->mf_iera &= ~IA_TIMA;	/* Disable timer interrupts	*/
    135 	MFP->mf_tadr  = divisor;	/* Set divisor			*/
    136 
    137 	if (hz != 48 && hz != 64 && hz != 96) { /* XXX */
    138 		printf (": illegal value %d for systemclock, reset to %d\n\t",
    139 								hz, 64);
    140 		hz = 64;
    141 	}
    142 	printf(": system hz %d timer-A divisor 200/%d\n", hz, divisor);
    143 
    144 #ifdef STATCLOCK
    145 	if ((stathz == 0) || (stathz > hz) || (CLOCK_HZ % stathz))
    146 		stathz = hz;
    147 	if ((profhz == 0) || (profhz > (hz << 1)) || (CLOCK_HZ % profhz))
    148 		profhz = hz << 1;
    149 
    150 	MFP->mf_tcdcr &= 0x7;			/* Stop timer		*/
    151 	MFP->mf_ierb  &= ~IB_TIMC;		/* Disable timer inter.	*/
    152 	MFP->mf_tcdr   = CLOCK_HZ/stathz;	/* Set divisor		*/
    153 
    154 	statmin  = (CLOCK_HZ/stathz) - (statvar >> 1);
    155 	profmin  = (CLOCK_HZ/profhz) - (statvar >> 1);
    156 	clk2min  = statmin;
    157 #endif /* STATCLOCK */
    158 
    159 	/*
    160 	 * Initialize Timer-B in the ST-MFP. This timer is used by the 'delay'
    161 	 * function below. This time is setup to be continueously counting from
    162 	 * 255 back to zero at a frequency of 614400Hz.
    163 	 */
    164 	MFP->mf_tbcr  = 0;		/* Stop timer			*/
    165 	MFP->mf_iera &= ~IA_TIMB;	/* Disable timer interrupts	*/
    166 	MFP->mf_tbdr  = 0;
    167 	MFP->mf_tbcr  = T_Q004;	/* Start timer			*/
    168 
    169 }
    170 
    171 void cpu_initclocks()
    172 {
    173 	MFP->mf_tacr  = T_Q200;		/* Start timer			*/
    174 	MFP->mf_ipra &= ~IA_TIMA;	/* Clear pending interrupts	*/
    175 	MFP->mf_iera |= IA_TIMA;	/* Enable timer interrupts	*/
    176 	MFP->mf_imra |= IA_TIMA;	/*    .....			*/
    177 
    178 #ifdef STATCLOCK
    179 	MFP->mf_tcdcr = (MFP->mf_tcdcr & 0x7) | (T_Q200<<4); /* Start	*/
    180 	MFP->mf_iprb &= ~IB_TIMC;	/* Clear pending interrupts	*/
    181 	MFP->mf_ierb |= IB_TIMC;	/* Enable timer interrupts	*/
    182 	MFP->mf_imrb |= IB_TIMC;	/*    .....			*/
    183 #endif /* STATCLOCK */
    184 }
    185 
    186 void
    187 setstatclockrate(newhz)
    188 	int newhz;
    189 {
    190 #ifdef STATCLOCK
    191 	if (newhz == stathz)
    192 		clk2min = statmin;
    193 	else clk2min = profmin;
    194 #endif /* STATCLOCK */
    195 }
    196 
    197 #ifdef STATCLOCK
    198 void
    199 statintr(frame)
    200 	register struct clockframe *frame;
    201 {
    202 	register int	var, r;
    203 
    204 	var = statvar - 1;
    205 	do {
    206 		r = random() & var;
    207 	} while(r == 0);
    208 
    209 	/*
    210 	 * Note that we are always lagging behind as the new divisor
    211 	 * value will not be loaded until the next interrupt. This
    212 	 * shouldn't disturb the median frequency (I think ;-) ) as
    213 	 * only the value used when switching frequencies is used
    214 	 * twice. This shouldn't happen very often.
    215 	 */
    216 	MFP->mf_tcdr = clk2min + r;
    217 
    218 	statclock(frame);
    219 }
    220 #endif /* STATCLOCK */
    221 
    222 /*
    223  * Returns number of usec since last recorded clock "tick"
    224  * (i.e. clock interrupt).
    225  */
    226 long
    227 clkread()
    228 {
    229 	u_int	delta;
    230 
    231 	delta = ((divisor - MFP->mf_tadr) * tick) / divisor;
    232 	/*
    233 	 * Account for pending clock interrupts
    234 	 */
    235 	if(MFP->mf_iera & IA_TIMA)
    236 		return(delta + tick);
    237 	return(delta);
    238 }
    239 
    240 #define TIMB_FREQ	614400
    241 #define TIMB_LIMIT	256
    242 
    243 /*
    244  * Wait "n" microseconds.
    245  * Relies on MFP-Timer B counting down from TIMB_LIMIT at TIMB_FREQ Hz.
    246  * Note: timer had better have been programmed before this is first used!
    247  */
    248 void delay(n)
    249 int	n;
    250 {
    251 	int	tick, otick;
    252 
    253 	/*
    254 	 * Read the counter first, so that the rest of the setup overhead is
    255 	 * counted.
    256 	 */
    257 	otick = MFP->mf_tbdr;
    258 
    259 	/*
    260 	 * Calculate ((n * TIMER_FREQ) / 1e6) using explicit assembler code so
    261 	 * we can take advantage of the intermediate 64-bit quantity to prevent
    262 	 * loss of significance.
    263 	 */
    264 	n -= 5;
    265 	if(n < 0)
    266 		return;
    267 	{
    268 	    u_int	temp;
    269 
    270 	    __asm __volatile ("mulul %2,%1:%0" : "=d" (n), "=d" (temp)
    271 					       : "d" (TIMB_FREQ));
    272 	    __asm __volatile ("divul %1,%2:%0" : "=d" (n)
    273 					       : "d"(1000000),"d"(temp),"0"(n));
    274 	}
    275 
    276 	while(n > 0) {
    277 		tick = MFP->mf_tbdr;
    278 		if(tick > otick)
    279 			n -= TIMB_LIMIT - (tick - otick);
    280 		else n -= otick - tick;
    281 		otick = tick;
    282 	}
    283 }
    284 
    285 #ifdef GPROF
    286 /*
    287  * profclock() is expanded in line in lev6intr() unless profiling kernel.
    288  * Assumes it is called with clock interrupts blocked.
    289  */
    290 profclock(pc, ps)
    291 	caddr_t pc;
    292 	int ps;
    293 {
    294 	/*
    295 	 * Came from user mode.
    296 	 * If this process is being profiled record the tick.
    297 	 */
    298 	if (USERMODE(ps)) {
    299 		if (p->p_stats.p_prof.pr_scale)
    300 			addupc(pc, &curproc->p_stats.p_prof, 1);
    301 	}
    302 	/*
    303 	 * Came from kernel (supervisor) mode.
    304 	 * If we are profiling the kernel, record the tick.
    305 	 */
    306 	else if (profiling < 2) {
    307 		register int s = pc - s_lowpc;
    308 
    309 		if (s < s_textsize)
    310 			kcount[s / (HISTFRACTION * sizeof (*kcount))]++;
    311 	}
    312 	/*
    313 	 * Kernel profiling was on but has been disabled.
    314 	 * Mark as no longer profiling kernel and if all profiling done,
    315 	 * disable the clock.
    316 	 */
    317 	if (profiling && (profon & PRF_KERNEL)) {
    318 		profon &= ~PRF_KERNEL;
    319 		if (profon == PRF_NONE)
    320 			stopprofclock();
    321 	}
    322 }
    323 #endif
    324 
    325 /***********************************************************************
    326  *                   Real Time Clock support                           *
    327  ***********************************************************************/
    328 
    329 u_int mc146818_read(rtc, regno)
    330 void	*rtc;
    331 u_int	regno;
    332 {
    333 	((struct rtc *)rtc)->rtc_regno = regno;
    334 	return(((struct rtc *)rtc)->rtc_data & 0377);
    335 }
    336 
    337 void mc146818_write(rtc, regno, value)
    338 void	*rtc;
    339 u_int	regno, value;
    340 {
    341 	((struct rtc *)rtc)->rtc_regno = regno;
    342 	((struct rtc *)rtc)->rtc_data  = value;
    343 }
    344 
    345 /*
    346  * Initialize the time of day register, based on the time base which is, e.g.
    347  * from a filesystem.
    348  */
    349 void
    350 inittodr(base)
    351 time_t base;
    352 {
    353 	u_long timbuf = base;	/* assume no battery clock exists */
    354 
    355 	timbuf = gettod();
    356 
    357 	if(timbuf < base) {
    358 		printf("WARNING: bad date in battery clock\n");
    359 		timbuf = base;
    360 	}
    361 
    362 	/* Battery clock does not store usec's, so forget about it. */
    363 	time.tv_sec = timbuf;
    364 }
    365 
    366 void
    367 resettodr()
    368 {
    369 	if(settod(time.tv_sec) == 1)
    370 		return;
    371 	printf("Cannot set battery backed clock\n");
    372 }
    373 
    374 static	char	dmsize[12] =
    375 {
    376 	31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
    377 };
    378 
    379 static	char	ldmsize[12] =
    380 {
    381 	31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
    382 };
    383 
    384 static u_long
    385 gettod()
    386 {
    387 	int		i, sps;
    388 	u_long		new_time = 0;
    389 	char		*msize;
    390 	mc_todregs	clkregs;
    391 
    392 	sps = splhigh();
    393 	MC146818_GETTOD(RTC, &clkregs);
    394 	splx(sps);
    395 
    396 	if(clkregs[MC_SEC] > 59)
    397 		return(0);
    398 	if(clkregs[MC_MIN] > 59)
    399 		return(0);
    400 	if(clkregs[MC_HOUR] > 23)
    401 		return(0);
    402 	if(range_test(clkregs[MC_DOM], 1, 31))
    403 		return(0);
    404 	if (range_test(clkregs[MC_MONTH], 1, 12))
    405 		return(0);
    406 	if(clkregs[MC_YEAR] > (2000 - GEMSTARTOFTIME))
    407 		return(0);
    408 	clkregs[MC_YEAR] += GEMSTARTOFTIME;
    409 
    410 	for(i = BSDSTARTOFTIME; i < clkregs[MC_YEAR]; i++) {
    411 		if(is_leap(i))
    412 			new_time += 366;
    413 		else new_time += 365;
    414 	}
    415 
    416 	msize = is_leap(clkregs[MC_YEAR]) ? ldmsize : dmsize;
    417 	for(i = 0; i < (clkregs[MC_MONTH] - 1); i++)
    418 		new_time += msize[i];
    419 	new_time += clkregs[MC_DOM] - 1;
    420 	new_time *= SECS_DAY;
    421 	new_time += (clkregs[MC_HOUR] * 3600) + (clkregs[MC_MIN] * 60);
    422 	return(new_time + clkregs[MC_SEC]);
    423 }
    424 
    425 static int
    426 settod(newtime)
    427 u_long	newtime;
    428 {
    429 	register long	days, rem, year;
    430 	register char	*ml;
    431 		 int	sps, sec, min, hour, month;
    432 	mc_todregs	clkregs;
    433 
    434 	/* Number of days since Jan. 1 'BSDSTARTOFTIME'	*/
    435 	days = newtime / SECS_DAY;
    436 	rem  = newtime % SECS_DAY;
    437 
    438 	/*
    439 	 * Calculate sec, min, hour
    440 	 */
    441 	hour = rem / SECS_HOUR;
    442 	rem %= SECS_HOUR;
    443 	min  = rem / 60;
    444 	sec  = rem % 60;
    445 
    446 	/*
    447 	 * Figure out the year. Day in year is left in 'days'.
    448 	 */
    449 	year = BSDSTARTOFTIME;
    450 	while(days >= (rem = is_leap(year) ? 366 : 365)) {
    451 		++year;
    452 		days -= rem;
    453 	}
    454 
    455 	/*
    456 	 * Determine the month
    457 	 */
    458 	ml = is_leap(year) ? ldmsize : dmsize;
    459 	for(month = 0; days >= ml[month]; ++month)
    460 		days -= ml[month];
    461 
    462 	/*
    463 	 * Now that everything is calculated, program the RTC
    464 	 */
    465 	mc146818_write(RTC, MC_REGA, MC_BASE_32_KHz);
    466 	mc146818_write(RTC, MC_REGB, MC_REGB_24HR | MC_REGB_BINARY);
    467 	sps = splhigh();
    468 	MC146818_GETTOD(RTC, &clkregs);
    469 	clkregs[MC_SEC]   = sec;
    470 	clkregs[MC_MIN]   = min;
    471 	clkregs[MC_HOUR]  = hour;
    472 	clkregs[MC_DOM]   = days+1;
    473 	clkregs[MC_MONTH] = month+1;
    474 	clkregs[MC_YEAR]  = year - GEMSTARTOFTIME;
    475 	MC146818_PUTTOD(RTC, &clkregs);
    476 	splx(sps);
    477 
    478 	return(1);
    479 }
    480