Home | History | Annotate | Line # | Download | only in sun3x
clock.c revision 1.1
      1 /*	$NetBSD: clock.c,v 1.1 1997/01/14 20:57:08 gwr Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1994 Gordon W. Ross
      5  * Copyright (c) 1993 Adam Glass
      6  * Copyright (c) 1988 University of Utah.
      7  * Copyright (c) 1982, 1990, 1993
      8  *	The Regents of the University of California.  All rights reserved.
      9  *
     10  * This code is derived from software contributed to Berkeley by
     11  * the Systems Programming Group of the University of Utah Computer
     12  * Science Department.
     13  *
     14  * Redistribution and use in source and binary forms, with or without
     15  * modification, are permitted provided that the following conditions
     16  * are met:
     17  * 1. Redistributions of source code must retain the above copyright
     18  *    notice, this list of conditions and the following disclaimer.
     19  * 2. Redistributions in binary form must reproduce the above copyright
     20  *    notice, this list of conditions and the following disclaimer in the
     21  *    documentation and/or other materials provided with the distribution.
     22  * 3. All advertising materials mentioning features or use of this software
     23  *    must display the following acknowledgement:
     24  *	This product includes software developed by the University of
     25  *	California, Berkeley and its contributors.
     26  * 4. Neither the name of the University nor the names of its contributors
     27  *    may be used to endorse or promote products derived from this software
     28  *    without specific prior written permission.
     29  *
     30  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     31  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     32  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     33  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     34  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     35  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     36  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     37  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     38  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     39  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     40  * SUCH DAMAGE.
     41  *
     42  *	from: Utah Hdr: clock.c 1.18 91/01/21$
     43  *	from: @(#)clock.c	8.2 (Berkeley) 1/12/94
     44  */
     45 
     46 /*
     47  * Machine-dependent clock routines for the Intersil 7170:
     48  * Original by Adam Glass;  partially rewritten by Gordon Ross.
     49  */
     50 
     51 #include <sys/param.h>
     52 #include <sys/systm.h>
     53 #include <sys/time.h>
     54 #include <sys/kernel.h>
     55 #include <sys/device.h>
     56 
     57 #include <machine/autoconf.h>
     58 #include <machine/cpu.h>
     59 #include <machine/mon.h>
     60 #include <machine/obio.h>
     61 
     62 #include "intersil7170.h"
     63 #include "interreg.h"
     64 #include "machdep.h"
     65 
     66 #define	CLOCK_PRI	5
     67 
     68 void _isr_clock __P((void));	/* in locore.s */
     69 void clock_intr __P((struct clockframe));
     70 
     71 /* Note: this is used by locore.s:__isr_clock */
     72 static volatile char *clock_va;
     73 
     74 #define intersil_clock ((volatile struct intersil7170 *) clock_va)
     75 
     76 #define intersil_command(run, interrupt) \
     77 	(run | interrupt | INTERSIL_CMD_FREQ_32K | INTERSIL_CMD_24HR_MODE | \
     78 	 INTERSIL_CMD_NORMAL_MODE)
     79 
     80 #define intersil_clear() (void)intersil_clock->clk_intr_reg
     81 
     82 static int  clock_match __P((struct device *, struct cfdata *, void *args));
     83 static void clock_attach __P((struct device *, struct device *, void *));
     84 
     85 struct cfattach clock_ca = {
     86 	sizeof(struct device), clock_match, clock_attach
     87 };
     88 
     89 struct cfdriver clock_cd = {
     90 	NULL, "clock", DV_DULL
     91 };
     92 
     93 /*
     94  * XXX - Need to determine which type of clock we have!
     95  */
     96 static int
     97 clock_match(parent, cf, args)
     98     struct device *parent;
     99 	struct cfdata *cf;
    100     void *args;
    101 {
    102 	struct confargs *ca = args;
    103 
    104 	/* This driver only supports one unit. */
    105 	if (cf->cf_unit != 0)
    106 		return (0);
    107 
    108 	/* Validate the given address. */
    109 	if (ca->ca_paddr != OBIO_CLOCK2)
    110 		return (0);
    111 
    112 	/* Default interrupt priority. */
    113 	if (ca->ca_intpri == -1)
    114 		ca->ca_intpri = CLOCK_PRI;
    115 
    116 	return (1);
    117 }
    118 
    119 static void
    120 clock_attach(parent, self, args)
    121 	struct device *parent;
    122 	struct device *self;
    123 	void *args;
    124 {
    125 
    126 	printf("\n");
    127 
    128 	/*
    129 	 * Can not hook up the ISR until cpu_initclocks()
    130 	 * because hardclock is not ready until then.
    131 	 * For now, the handler is _isr_autovec(), which
    132 	 * will complain if it gets clock interrupts.
    133 	 */
    134 }
    135 
    136 /*
    137  * Set and/or clear the desired clock bits in the interrupt
    138  * register.  We have to be extremely careful that we do it
    139  * in such a manner that we don't get ourselves lost.
    140  */
    141 void
    142 set_clk_mode(on, off, enable)
    143 	u_char on, off;
    144 	int enable;
    145 {
    146 	register u_char interreg;
    147 	register int s;
    148 
    149 	s = getsr();
    150 	if ((s & PSL_IPL) < PSL_IPL7)
    151 		panic("set_clk_mode: ipl");
    152 
    153 	if (!intersil_clock)
    154 		panic("set_clk_mode: map");
    155 
    156 	/*
    157 	 * make sure that we are only playing w/
    158 	 * clock interrupt register bits
    159 	 */
    160 	on &= (IREG_CLOCK_ENAB_7 | IREG_CLOCK_ENAB_5);
    161 	off &= (IREG_CLOCK_ENAB_7 | IREG_CLOCK_ENAB_5);
    162 
    163 	/*
    164 	 * Get a copy of current interrupt register,
    165 	 * turning off any undesired bits (aka `off')
    166 	 */
    167 	interreg = *interrupt_reg & ~(off | IREG_ALL_ENAB);
    168 	*interrupt_reg &= ~IREG_ALL_ENAB;
    169 
    170 	/*
    171 	 * Next we turns off the CLK5 and CLK7 bits to clear
    172 	 * the flip-flops, then we disable clock interrupts.
    173 	 * Now we can read the clock's interrupt register
    174 	 * to clear any pending signals there.
    175 	 */
    176 	*interrupt_reg &= ~(IREG_CLOCK_ENAB_7 | IREG_CLOCK_ENAB_5);
    177 	intersil_clock->clk_cmd_reg =
    178 		intersil_command(INTERSIL_CMD_RUN, INTERSIL_CMD_IDISABLE);
    179 	intersil_clear();
    180 
    181 	/*
    182 	 * Now we set all the desired bits
    183 	 * in the interrupt register, then
    184 	 * we turn the clock back on and
    185 	 * finally we can enable all interrupts.
    186 	 */
    187 	*interrupt_reg |= (interreg | on);		/* enable flip-flops */
    188 
    189 	if (enable)
    190 		intersil_clock->clk_cmd_reg =
    191 			intersil_command(INTERSIL_CMD_RUN, INTERSIL_CMD_IENABLE);
    192 
    193 	*interrupt_reg |= IREG_ALL_ENAB;		/* enable interrupts */
    194 }
    195 
    196 /* Called very early by internal_configure. */
    197 void clock_init()
    198 {
    199 	clock_va = obio_find_mapping(OBIO_CLOCK2, sizeof(struct intersil7170));
    200 
    201 	if (!clock_va)
    202 		mon_panic("clock_init: clock_va\n");
    203 	if (!interrupt_reg)
    204 		mon_panic("clock_init: interrupt_reg\n");
    205 
    206 	/* Turn off clock interrupts until cpu_initclocks() */
    207 	/* isr_init() already set the interrupt reg to zero. */
    208 	intersil_clock->clk_cmd_reg =
    209 		intersil_command(INTERSIL_CMD_RUN, INTERSIL_CMD_IDISABLE);
    210 	intersil_clear();
    211 }
    212 
    213 /*
    214  * Set up the real-time clock (enable clock interrupts).
    215  * Leave stathz 0 since there is no secondary clock available.
    216  * Note that clock interrupts MUST STAY DISABLED until here.
    217  */
    218 void
    219 cpu_initclocks(void)
    220 {
    221 	int s;
    222 
    223 	if (!intersil_clock)
    224 		panic("cpu_initclocks");
    225 	s = splhigh();
    226 
    227 	/* Install isr (in locore.s) that calls clock_intr(). */
    228 	isr_add_custom(5, (void*)_isr_clock);
    229 
    230 	/* Set the clock to interrupt 100 time per second. */
    231 	intersil_clock->clk_intr_reg = INTERSIL_INTER_CSECONDS;
    232 
    233 	*interrupt_reg |= IREG_CLOCK_ENAB_5;	/* enable clock */
    234 	intersil_clock->clk_cmd_reg =
    235 		intersil_command(INTERSIL_CMD_RUN, INTERSIL_CMD_IENABLE);
    236 	*interrupt_reg |= IREG_ALL_ENAB;		/* enable interrupts */
    237 	splx(s);
    238 }
    239 
    240 /*
    241  * This doesn't need to do anything, as we have only one timer and
    242  * profhz==stathz==hz.
    243  */
    244 void
    245 setstatclockrate(newhz)
    246 	int newhz;
    247 {
    248 	/* nothing */
    249 }
    250 
    251 /*
    252  * This is is called by the "custom" interrupt handler
    253  * after it has reset the pending bit in the clock.
    254  */
    255 void
    256 clock_intr(cf)
    257 	struct clockframe cf;
    258 {
    259 	register volatile struct intersil7170 *clk = intersil_clock;
    260 
    261 	/* Read the clock interrupt register. */
    262 	(void) clk->clk_intr_reg;
    263 	/* Pulse the clock intr. enable low. */
    264 	*interrupt_reg &= ~IREG_CLOCK_ENAB_5;
    265 	*interrupt_reg |=  IREG_CLOCK_ENAB_5;
    266 	/* Read the clock intr. reg AGAIN! */
    267 	(void) clk->clk_intr_reg;
    268 
    269 	hardclock(&cf);
    270 }
    271 
    272 /*
    273  * Return the best possible estimate of the time in the timeval
    274  * to which tvp points.  We do this by returning the current time
    275  * plus the amount of time since the last clock interrupt.
    276  *
    277  * Check that this time is no less than any previously-reported time,
    278  * which could happen around the time of a clock adjustment.  Just for
    279  * fun, we guarantee that the time will be greater than the value
    280  * obtained by a previous call.
    281  */
    282 void
    283 microtime(tvp)
    284 	register struct timeval *tvp;
    285 {
    286 	int s = splhigh();
    287 	static struct timeval lasttime;
    288 
    289 	*tvp = time;
    290 	tvp->tv_usec++; 	/* XXX */
    291 	while (tvp->tv_usec > 1000000) {
    292 		tvp->tv_sec++;
    293 		tvp->tv_usec -= 1000000;
    294 	}
    295 	if (tvp->tv_sec == lasttime.tv_sec &&
    296 		tvp->tv_usec <= lasttime.tv_usec &&
    297 		(tvp->tv_usec = lasttime.tv_usec + 1) > 1000000)
    298 	{
    299 		tvp->tv_sec++;
    300 		tvp->tv_usec -= 1000000;
    301 	}
    302 	lasttime = *tvp;
    303 	splx(s);
    304 }
    305 
    306 
    307 /*
    308  * Machine-dependent clock routines.
    309  *
    310  * Inittodr initializes the time of day hardware which provides
    311  * date functions.
    312  *
    313  * Resettodr restores the time of day hardware after a time change.
    314  */
    315 #define SECDAY		86400L
    316 #define SECYR		(SECDAY * 365)
    317 
    318 static long clk_get_secs(void);
    319 static void clk_set_secs(long);
    320 
    321 /*
    322  * Initialize the time of day register, based on the time base
    323  * which is, e.g. from a filesystem.
    324  */
    325 void inittodr(fs_time)
    326 	time_t fs_time;
    327 {
    328 	long diff, clk_time;
    329 	long long_ago = (5 * SECYR);
    330 	int clk_bad = 0;
    331 
    332 	/*
    333 	 * Sanity check time from file system.
    334 	 * If it is zero,assume filesystem time is just unknown
    335 	 * instead of preposterous.  Don't bark.
    336 	 */
    337 	if (fs_time < long_ago) {
    338 		/*
    339 		 * If fs_time is zero, assume filesystem time is just
    340 		 * unknown instead of preposterous.  Don't bark.
    341 		 */
    342 		if (fs_time != 0)
    343 			printf("WARNING: preposterous time in file system\n");
    344 		/* 1991/07/01  12:00:00 */
    345 		fs_time = 21*SECYR + 186*SECDAY + SECDAY/2;
    346 	}
    347 
    348 	clk_time = clk_get_secs();
    349 
    350 	/* Sanity check time from clock. */
    351 	if (clk_time < long_ago) {
    352 		printf("WARNING: bad date in battery clock");
    353 		clk_bad = 1;
    354 		clk_time = fs_time;
    355 	} else {
    356 		/* Does the clock time jive with the file system? */
    357 		diff = clk_time - fs_time;
    358 		if (diff < 0)
    359 			diff = -diff;
    360 		if (diff >= (SECDAY*2)) {
    361 			printf("WARNING: clock %s %d days",
    362 				   (clk_time < fs_time) ? "lost" : "gained",
    363 				   (int) (diff / SECDAY));
    364 			clk_bad = 1;
    365 		}
    366 	}
    367 	if (clk_bad)
    368 		printf(" -- CHECK AND RESET THE DATE!\n");
    369 	time.tv_sec = clk_time;
    370 }
    371 
    372 /*
    373  * Resettodr restores the time of day hardware after a time change.
    374  */
    375 void resettodr()
    376 {
    377 	clk_set_secs(time.tv_sec);
    378 }
    379 
    380 /*
    381  * Machine dependent base year:
    382  * Note: must be < 1970
    383  */
    384 #define	CLOCK_BASE_YEAR	1968
    385 
    386 
    387 /*
    388  * Routine to copy state into and out of the clock.
    389  * The clock registers have to be read or written
    390  * in sequential order (or so it appears). -gwr
    391  */
    392 static void clk_get_dt(struct date_time *dt)
    393 {
    394 	int s;
    395 	register volatile char *src, *dst;
    396 
    397 	src = (char *) &intersil_clock->counters;
    398 
    399 	s = splhigh();
    400 	intersil_clock->clk_cmd_reg =
    401 		intersil_command(INTERSIL_CMD_STOP, INTERSIL_CMD_IENABLE);
    402 
    403 	dst = (char *) dt;
    404 	dt++;	/* end marker */
    405 	do {
    406 		*dst++ = *src++;
    407 	} while (dst < (char*)dt);
    408 
    409 	intersil_clock->clk_cmd_reg =
    410 		intersil_command(INTERSIL_CMD_RUN, INTERSIL_CMD_IENABLE);
    411 	splx(s);
    412 }
    413 
    414 static void clk_set_dt(struct date_time *dt)
    415 {
    416 	int s;
    417 	register volatile char *src, *dst;
    418 
    419 	dst = (char *) &intersil_clock->counters;
    420 
    421 	s = splhigh();
    422 	intersil_clock->clk_cmd_reg =
    423 		intersil_command(INTERSIL_CMD_STOP, INTERSIL_CMD_IENABLE);
    424 
    425 	src = (char *) dt;
    426 	dt++;	/* end marker */
    427 	do {
    428 		*dst++ = *src++;
    429 	} while (src < (char *)dt);
    430 
    431 	intersil_clock->clk_cmd_reg =
    432 		intersil_command(INTERSIL_CMD_RUN, INTERSIL_CMD_IENABLE);
    433 	splx(s);
    434 }
    435 
    436 
    437 
    438 /*
    440  * Generic routines to convert to or from a POSIX date
    441  * (seconds since 1/1/1970) and  yr/mo/day/hr/min/sec
    442  *
    443  * These are organized this way mostly to so the code
    444  * can easily be tested in an independent user program.
    445  * (These are derived from the hp300 code.)
    446  */
    447 
    448 /* Traditional UNIX base year */
    449 #define	POSIX_BASE_YEAR	1970
    450 #define FEBRUARY	2
    451 
    452 #define	leapyear(year)		((year) % 4 == 0)
    453 #define	days_in_year(a) 	(leapyear(a) ? 366 : 365)
    454 #define	days_in_month(a) 	(month_days[(a) - 1])
    455 
    456 static int month_days[12] = {
    457 	31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
    458 };
    459 
    460 void gmt_to_dt(long *tp, struct date_time *dt)
    461 {
    462 	register int i;
    463 	register long days, secs;
    464 
    465 	days = *tp / SECDAY;
    466 	secs = *tp % SECDAY;
    467 
    468 	/* Hours, minutes, seconds are easy */
    469 	dt->dt_hour = secs / 3600;
    470 	secs = secs % 3600;
    471 	dt->dt_min  = secs / 60;
    472 	secs = secs % 60;
    473 	dt->dt_sec  = secs;
    474 
    475 	/* Day of week (Note: 1/1/1970 was a Thursday) */
    476 	dt->dt_dow = (days + 4) % 7;
    477 
    478 	/* Number of years in days */
    479 	i = POSIX_BASE_YEAR;
    480 	while (days >= days_in_year(i)) {
    481 		days -= days_in_year(i);
    482 		i++;
    483 	}
    484 	dt->dt_year = i - CLOCK_BASE_YEAR;
    485 
    486 	/* Number of months in days left */
    487 	if (leapyear(i))
    488 		days_in_month(FEBRUARY) = 29;
    489 	for (i = 1; days >= days_in_month(i); i++)
    490 		days -= days_in_month(i);
    491 	days_in_month(FEBRUARY) = 28;
    492 	dt->dt_month = i;
    493 
    494 	/* Days are what is left over (+1) from all that. */
    495 	dt->dt_day = days + 1;
    496 }
    497 
    498 void dt_to_gmt(struct date_time *dt, long *tp)
    499 {
    500 	register int i;
    501 	register long tmp;
    502 	int year;
    503 
    504 	/*
    505 	 * Hours are different for some reason. Makes no sense really.
    506 	 */
    507 
    508 	tmp = 0;
    509 
    510 	if (dt->dt_hour >= 24) goto out;
    511 	if (dt->dt_day  >  31) goto out;
    512 	if (dt->dt_month > 12) goto out;
    513 
    514 	year = dt->dt_year + CLOCK_BASE_YEAR;
    515 
    516 	/*
    517 	 * Compute days since start of time
    518 	 * First from years, then from months.
    519 	 */
    520 	for (i = POSIX_BASE_YEAR; i < year; i++)
    521 		tmp += days_in_year(i);
    522 	if (leapyear(year) && dt->dt_month > FEBRUARY)
    523 		tmp++;
    524 
    525 	/* Months */
    526 	for (i = 1; i < dt->dt_month; i++)
    527 	  	tmp += days_in_month(i);
    528 	tmp += (dt->dt_day - 1);
    529 
    530 	/* Now do hours */
    531 	tmp = tmp * 24 + dt->dt_hour;
    532 
    533 	/* Now do minutes */
    534 	tmp = tmp * 60 + dt->dt_min;
    535 
    536 	/* Now do seconds */
    537 	tmp = tmp * 60 + dt->dt_sec;
    538 
    539  out:
    540 	*tp = tmp;
    541 }
    542 
    543 /*
    544  * Now routines to get and set clock as POSIX time.
    545  */
    546 
    547 static long clk_get_secs()
    548 {
    549 	struct date_time dt;
    550 	long gmt;
    551 
    552 	clk_get_dt(&dt);
    553 	dt_to_gmt(&dt, &gmt);
    554 	return (gmt);
    555 }
    556 
    557 static void clk_set_secs(long secs)
    558 {
    559 	struct date_time dt;
    560 	long gmt;
    561 
    562 	gmt = secs;
    563 	gmt_to_dt(&gmt, &dt);
    564 	clk_set_dt(&dt);
    565 }
    566 
    567 
    568 #ifdef	DEBUG
    569 /* Call this from DDB or whatever... */
    570 int clkdebug()
    571 {
    572 	struct date_time dt;
    573 	long gmt;
    574 	long *lp;
    575 
    576 	bzero((char*)&dt, sizeof(dt));
    577 	clk_get_dt(&dt);
    578 	lp = (long*)&dt;
    579 	printf("clkdebug: dt=[%x,%x]\n", lp[0], lp[1]);
    580 
    581 	dt_to_gmt(&dt, &gmt);
    582 	printf("clkdebug: gmt=%x\n", gmt);
    583 }
    584 #endif
    585