Home | History | Annotate | Line # | Download | only in dev
clock.c revision 1.13
      1 /*	$NetBSD: clock.c,v 1.13 1996/10/13 04:10:51 christos 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 <machine/psl.h>
     50 #include <machine/cpu.h>
     51 #include <machine/iomap.h>
     52 #include <machine/mfp.h>
     53 #include <atari/dev/clockreg.h>
     54 
     55 #if defined(GPROF) && defined(PROFTIMER)
     56 #include <machine/profile.h>
     57 #endif
     58 
     59 /*
     60  * The MFP clock runs at 2457600Hz. We use a {system,stat,prof}clock divider
     61  * of 200. Therefore the timer runs at an effective rate of:
     62  * 2457600/200 = 12288Hz.
     63  */
     64 #define CLOCK_HZ	12288
     65 
     66 /*
     67  * Machine-dependent clock routines.
     68  *
     69  * Inittodr initializes the time of day hardware which provides
     70  * date functions.
     71  *
     72  * Resettodr restores the time of day hardware after a time change.
     73  */
     74 
     75 int	clockmatch __P((struct device *, void *, void *));
     76 void	clockattach __P((struct device *, struct device *, void *));
     77 
     78 struct cfattach clock_ca = {
     79 	sizeof(struct device), clockmatch, clockattach
     80 };
     81 
     82 struct cfdriver clock_cd = {
     83 	NULL, "clock", DV_DULL, NULL, 0
     84 };
     85 
     86 void statintr __P((struct clockframe *));
     87 
     88 static u_long	gettod __P((void));
     89 static int	settod __P((u_long));
     90 
     91 static int	divisor;	/* Systemclock divisor	*/
     92 
     93 /*
     94  * Statistics and profile clock intervals and variances. Variance must
     95  * be a power of 2. Since this gives us an even number, not an odd number,
     96  * we discard one case and compensate. That is, a variance of 64 would
     97  * give us offsets in [0..63]. Instead, we take offsets in [1..63].
     98  * This is symetric around the point 32, or statvar/2, and thus averages
     99  * to that value (assuming uniform random numbers).
    100  */
    101 #ifdef STATCLOCK
    102 static int	statvar = 32;	/* {stat,prof}clock variance		*/
    103 static int	statmin;	/* statclock divisor - variance/2	*/
    104 static int	profmin;	/* profclock divisor - variance/2	*/
    105 static int	clk2min;	/* current, from above choises		*/
    106 #endif
    107 
    108 int
    109 clockmatch(pdp, match, auxp)
    110 struct device *pdp;
    111 void *match, *auxp;
    112 {
    113 	if(!strcmp("clock", auxp))
    114 		return(1);
    115 	return(0);
    116 }
    117 
    118 /*
    119  * Start the real-time clock.
    120  */
    121 void clockattach(pdp, dp, auxp)
    122 struct device	*pdp, *dp;
    123 void			*auxp;
    124 {
    125 	/*
    126 	 * Initialize Timer-A in the ST-MFP. We use a divisor of 200.
    127 	 * The MFP clock runs at 2457600Hz. Therefore the timer runs
    128 	 * at an effective rate of: 2457600/200 = 12288Hz. The
    129 	 * following expression works for 48, 64 or 96 hz.
    130 	 */
    131 	divisor       = CLOCK_HZ/hz;
    132 	MFP->mf_tacr  = 0;		/* Stop timer			*/
    133 	MFP->mf_iera &= ~IA_TIMA;	/* Disable timer interrupts	*/
    134 	MFP->mf_tadr  = divisor;	/* Set divisor			*/
    135 
    136 	if (hz != 48 && hz != 64 && hz != 96) { /* XXX */
    137 		printf (": illegal value %d for systemclock, reset to %d\n\t",
    138 								hz, 64);
    139 		hz = 64;
    140 	}
    141 	printf(": system hz %d timer-A divisor 200/%d\n", hz, divisor);
    142 
    143 #ifdef STATCLOCK
    144 	if ((stathz == 0) || (stathz > hz) || (CLOCK_HZ % stathz))
    145 		stathz = hz;
    146 	if ((profhz == 0) || (profhz > (hz << 1)) || (CLOCK_HZ % profhz))
    147 		profhz = hz << 1;
    148 
    149 	MFP->mf_tcdcr &= 0x7;			/* Stop timer		*/
    150 	MFP->mf_ierb  &= ~IB_TIMC;		/* Disable timer inter.	*/
    151 	MFP->mf_tcdr   = CLOCK_HZ/stathz;	/* Set divisor		*/
    152 
    153 	statmin  = (CLOCK_HZ/stathz) - (statvar >> 1);
    154 	profmin  = (CLOCK_HZ/profhz) - (statvar >> 1);
    155 	clk2min  = statmin;
    156 #endif /* STATCLOCK */
    157 
    158 	/*
    159 	 * Initialize Timer-B in the ST-MFP. This timer is used by the 'delay'
    160 	 * function below. This time is setup to be continueously counting from
    161 	 * 255 back to zero at a frequency of 614400Hz.
    162 	 */
    163 	MFP->mf_tbcr  = 0;		/* Stop timer			*/
    164 	MFP->mf_iera &= ~IA_TIMB;	/* Disable timer interrupts	*/
    165 	MFP->mf_tbdr  = 0;
    166 	MFP->mf_tbcr  = T_Q004;	/* Start timer			*/
    167 
    168 }
    169 
    170 void cpu_initclocks()
    171 {
    172 	MFP->mf_tacr  = T_Q200;		/* Start timer			*/
    173 	MFP->mf_ipra &= ~IA_TIMA;	/* Clear pending interrupts	*/
    174 	MFP->mf_iera |= IA_TIMA;	/* Enable timer interrupts	*/
    175 	MFP->mf_imra |= IA_TIMA;	/*    .....			*/
    176 
    177 #ifdef STATCLOCK
    178 	MFP->mf_tcdcr = (MFP->mf_tcdcr & 0x7) | (T_Q200<<4); /* Start	*/
    179 	MFP->mf_iprb &= ~IB_TIMC;	/* Clear pending interrupts	*/
    180 	MFP->mf_ierb |= IB_TIMC;	/* Enable timer interrupts	*/
    181 	MFP->mf_imrb |= IB_TIMC;	/*    .....			*/
    182 #endif /* STATCLOCK */
    183 }
    184 
    185 void
    186 setstatclockrate(newhz)
    187 	int newhz;
    188 {
    189 #ifdef STATCLOCK
    190 	if (newhz == stathz)
    191 		clk2min = statmin;
    192 	else clk2min = profmin;
    193 #endif /* STATCLOCK */
    194 }
    195 
    196 #ifdef STATCLOCK
    197 void
    198 statintr(frame)
    199 	register struct clockframe *frame;
    200 {
    201 	register int	var, r;
    202 
    203 	var = statvar - 1;
    204 	do {
    205 		r = random() & var;
    206 	} while(r == 0);
    207 
    208 	/*
    209 	 * Note that we are always lagging behind as the new divisor
    210 	 * value will not be loaded until the next interrupt. This
    211 	 * shouldn't disturb the median frequency (I think ;-) ) as
    212 	 * only the value used when switching frequencies is used
    213 	 * twice. This shouldn't happen very often.
    214 	 */
    215 	MFP->mf_tcdr = clk2min + r;
    216 
    217 	statclock(frame);
    218 }
    219 #endif /* STATCLOCK */
    220 
    221 /*
    222  * Returns number of usec since last recorded clock "tick"
    223  * (i.e. clock interrupt).
    224  */
    225 long
    226 clkread()
    227 {
    228 	u_int	delta;
    229 
    230 	delta = ((divisor - MFP->mf_tadr) * tick) / divisor;
    231 	/*
    232 	 * Account for pending clock interrupts
    233 	 */
    234 	if(MFP->mf_iera & IA_TIMA)
    235 		return(delta + tick);
    236 	return(delta);
    237 }
    238 
    239 #define TIMB_FREQ	614400
    240 #define TIMB_LIMIT	256
    241 
    242 /*
    243  * Wait "n" microseconds.
    244  * Relies on MFP-Timer B counting down from TIMB_LIMIT at TIMB_FREQ Hz.
    245  * Note: timer had better have been programmed before this is first used!
    246  */
    247 void delay(n)
    248 int	n;
    249 {
    250 	int	tick, otick;
    251 
    252 	/*
    253 	 * Read the counter first, so that the rest of the setup overhead is
    254 	 * counted.
    255 	 */
    256 	otick = MFP->mf_tbdr;
    257 
    258 	/*
    259 	 * Calculate ((n * TIMER_FREQ) / 1e6) using explicit assembler code so
    260 	 * we can take advantage of the intermediate 64-bit quantity to prevent
    261 	 * loss of significance.
    262 	 */
    263 	n -= 5;
    264 	if(n < 0)
    265 		return;
    266 	{
    267 	    u_int	temp;
    268 
    269 	    __asm __volatile ("mulul %2,%1:%0" : "=d" (n), "=d" (temp)
    270 					       : "d" (TIMB_FREQ));
    271 	    __asm __volatile ("divul %1,%2:%0" : "=d" (n)
    272 					       : "d"(1000000),"d"(temp),"0"(n));
    273 	}
    274 
    275 	while(n > 0) {
    276 		tick = MFP->mf_tbdr;
    277 		if(tick > otick)
    278 			n -= TIMB_LIMIT - (tick - otick);
    279 		else n -= otick - tick;
    280 		otick = tick;
    281 	}
    282 }
    283 
    284 #ifdef GPROF
    285 /*
    286  * profclock() is expanded in line in lev6intr() unless profiling kernel.
    287  * Assumes it is called with clock interrupts blocked.
    288  */
    289 profclock(pc, ps)
    290 	caddr_t pc;
    291 	int ps;
    292 {
    293 	/*
    294 	 * Came from user mode.
    295 	 * If this process is being profiled record the tick.
    296 	 */
    297 	if (USERMODE(ps)) {
    298 		if (p->p_stats.p_prof.pr_scale)
    299 			addupc(pc, &curproc->p_stats.p_prof, 1);
    300 	}
    301 	/*
    302 	 * Came from kernel (supervisor) mode.
    303 	 * If we are profiling the kernel, record the tick.
    304 	 */
    305 	else if (profiling < 2) {
    306 		register int s = pc - s_lowpc;
    307 
    308 		if (s < s_textsize)
    309 			kcount[s / (HISTFRACTION * sizeof (*kcount))]++;
    310 	}
    311 	/*
    312 	 * Kernel profiling was on but has been disabled.
    313 	 * Mark as no longer profiling kernel and if all profiling done,
    314 	 * disable the clock.
    315 	 */
    316 	if (profiling && (profon & PRF_KERNEL)) {
    317 		profon &= ~PRF_KERNEL;
    318 		if (profon == PRF_NONE)
    319 			stopprofclock();
    320 	}
    321 }
    322 #endif
    323 
    324 /***********************************************************************
    325  *                   Real Time Clock support                           *
    326  ***********************************************************************/
    327 
    328 u_int mc146818_read(rtc, regno)
    329 void	*rtc;
    330 u_int	regno;
    331 {
    332 	((struct rtc *)rtc)->rtc_regno = regno;
    333 	return(((struct rtc *)rtc)->rtc_data & 0377);
    334 }
    335 
    336 void mc146818_write(rtc, regno, value)
    337 void	*rtc;
    338 u_int	regno, value;
    339 {
    340 	((struct rtc *)rtc)->rtc_regno = regno;
    341 	((struct rtc *)rtc)->rtc_data  = value;
    342 }
    343 
    344 /*
    345  * Initialize the time of day register, based on the time base which is, e.g.
    346  * from a filesystem.
    347  */
    348 void
    349 inittodr(base)
    350 time_t base;
    351 {
    352 	u_long timbuf = base;	/* assume no battery clock exists */
    353 
    354 	timbuf = gettod();
    355 
    356 	if(timbuf < base) {
    357 		printf("WARNING: bad date in battery clock\n");
    358 		timbuf = base;
    359 	}
    360 
    361 	/* Battery clock does not store usec's, so forget about it. */
    362 	time.tv_sec  = timbuf;
    363 	time.tv_usec = 0;
    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