Home | History | Annotate | Line # | Download | only in sun3x
clock.c revision 1.7
      1 /*	$NetBSD: clock.c,v 1.7 1997/02/12 16:00:31 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 Mostek48t02
     48  */
     49 
     50 #include <sys/param.h>
     51 #include <sys/systm.h>
     52 #include <sys/time.h>
     53 #include <sys/kernel.h>
     54 #include <sys/device.h>
     55 
     56 #include <machine/autoconf.h>
     57 #include <machine/cpu.h>
     58 #include <machine/mon.h>
     59 #include <machine/obio.h>
     60 #include <machine/machdep.h>
     61 
     62 #include <sun3/sun3/interreg.h>
     63 #include "mostek48t02.h"
     64 
     65 #define	CLOCK_PRI	5
     66 
     67 void _isr_clock __P((void));	/* in locore.s */
     68 void clock_intr __P((struct clockframe));
     69 
     70 /* Note: this is used by locore.s:__isr_clock */
     71 static volatile void *clock_va;
     72 
     73 static int  clock_match __P((struct device *, struct cfdata *, void *args));
     74 static void clock_attach __P((struct device *, struct device *, void *));
     75 
     76 struct cfattach clock_ca = {
     77 	sizeof(struct device), clock_match, clock_attach
     78 };
     79 
     80 struct cfdriver clock_cd = {
     81 	NULL, "clock", DV_DULL
     82 };
     83 
     84 /*
     85  * XXX  Need to determine which type of clock we have!
     86  * XXX  The Sun3/80 always has the MK4802, while the
     87  * XXX  Sun3/470 can (reportedly) have that or the old
     88  * XXX  intersil7170.  Should have two clock drivers...
     89  */
     90 static int
     91 clock_match(parent, cf, args)
     92     struct device *parent;
     93 	struct cfdata *cf;
     94     void *args;
     95 {
     96 	struct confargs *ca = args;
     97 
     98 	/* This driver only supports one unit. */
     99 	if (cf->cf_unit != 0)
    100 		return (0);
    101 
    102 	/* Validate the given address. */
    103 	if (ca->ca_paddr != OBIO_CLOCK2)
    104 		return (0);
    105 
    106 	/* Default interrupt priority. */
    107 	if (ca->ca_intpri == -1)
    108 		ca->ca_intpri = CLOCK_PRI;
    109 
    110 	return (1);
    111 }
    112 
    113 static void
    114 clock_attach(parent, self, args)
    115 	struct device *parent;
    116 	struct device *self;
    117 	void *args;
    118 {
    119 
    120 	printf("\n");
    121 
    122 	/*
    123 	 * Can not hook up the ISR until cpu_initclocks()
    124 	 * because hardclock is not ready until then.
    125 	 * For now, the handler is _isr_autovec(), which
    126 	 * will complain if it gets clock interrupts.
    127 	 */
    128 }
    129 
    130 /*
    131  * Set and/or clear the desired clock bits in the interrupt
    132  * register.  We have to be extremely careful that we do it
    133  * in such a manner that we don't get ourselves lost.
    134  */
    135 void
    136 set_clk_mode(on, off, enable)
    137 	u_char on, off;
    138 	int enable;
    139 {
    140 	register u_char interreg;
    141 	register int s;
    142 
    143 	/* If we don't have this, we must not have touched it! */
    144 	if (!interrupt_reg)
    145 		return;
    146 
    147 	s = getsr();
    148 	if ((s & PSL_IPL) < PSL_IPL7)
    149 		panic("set_clk_mode: ipl");
    150 
    151 	/*
    152 	 * make sure that we are only playing w/
    153 	 * clock interrupt register bits
    154 	 */
    155 	on &= (IREG_CLOCK_ENAB_7 | IREG_CLOCK_ENAB_5);
    156 	off &= (IREG_CLOCK_ENAB_7 | IREG_CLOCK_ENAB_5);
    157 
    158 	/*
    159 	 * Get a copy of current interrupt register,
    160 	 * turning off any undesired bits (aka `off')
    161 	 */
    162 	interreg = *interrupt_reg & ~(off | IREG_ALL_ENAB);
    163 	*interrupt_reg &= ~IREG_ALL_ENAB;
    164 
    165 	/*
    166 	 * Next we turns off the CLK5 and CLK7 bits to clear
    167 	 * the flip-flops, then we disable clock interrupts.
    168 	 * Now we can read the clock's interrupt register
    169 	 * to clear any pending signals there.
    170 	 */
    171 	*interrupt_reg &= ~(IREG_CLOCK_ENAB_7 | IREG_CLOCK_ENAB_5);
    172 
    173 	/* XXX - hit the clock? */
    174 
    175 	/*
    176 	 * Now we set all the desired bits
    177 	 * in the interrupt register, then
    178 	 * we turn the clock back on and
    179 	 * finally we can enable all interrupts.
    180 	 */
    181 	*interrupt_reg |= (interreg | on);		/* enable flip-flops */
    182 
    183 	/* XXX - hit the clock? */
    184 
    185 	*interrupt_reg |= IREG_ALL_ENAB;		/* enable interrupts */
    186 }
    187 
    188 /* Called very early by internal_configure. */
    189 void clock_init()
    190 {
    191 	/* XXX - Yes, use the EEPROM address.  Same H/W device. */
    192 	clock_va = obio_find_mapping(OBIO_EEPROM, sizeof(struct clockreg));
    193 
    194 	if (!clock_va || !interrupt_reg) {
    195 		mon_printf("clock_init\n");
    196 		sunmon_abort();
    197 	}
    198 
    199 	/* Turn off clock interrupts until cpu_initclocks() */
    200 	/* intreg_init() already cleared the interrupt register. */
    201 }
    202 
    203 /*
    204  * Set up the real-time clock (enable clock interrupts).
    205  * Leave stathz 0 since there is no secondary clock available.
    206  * Note that clock interrupts MUST STAY DISABLED until here.
    207  */
    208 void
    209 cpu_initclocks(void)
    210 {
    211 	int s;
    212 
    213 	if (!clock_va)
    214 		panic("cpu_initclocks");
    215 	s = splhigh();
    216 
    217 	/* Install isr (in locore.s) that calls clock_intr(). */
    218 	isr_add_custom(5, (void*)_isr_clock);
    219 
    220 	/* Set the clock to interrupt 100 time per second. */
    221 	/* XXX - Hard wired? */
    222 
    223 	*interrupt_reg |= IREG_CLOCK_ENAB_5;	/* enable clock */
    224 
    225 	/* XXX enable the clock? */
    226 
    227 	*interrupt_reg |= IREG_ALL_ENAB;		/* enable interrupts */
    228 	splx(s);
    229 }
    230 
    231 /*
    232  * This doesn't need to do anything, as we have only one timer and
    233  * profhz==stathz==hz.
    234  */
    235 void
    236 setstatclockrate(newhz)
    237 	int newhz;
    238 {
    239 	/* nothing */
    240 }
    241 
    242 /*
    243  * This is is called by the "custom" interrupt handler.
    244  */
    245 void
    246 clock_intr(cf)
    247 	struct clockframe cf;
    248 {
    249 	/* volatile struct clockreg *clk = clock_va; */
    250 
    251 #if 1	/* XXX - Needed? */
    252 	/* Pulse the clock intr. enable low. */
    253 	*interrupt_reg &= ~IREG_CLOCK_ENAB_5;
    254 	*interrupt_reg |=  IREG_CLOCK_ENAB_5;
    255 #endif
    256 
    257 	hardclock(&cf);
    258 }
    259 
    260 /*
    261  * Return the best possible estimate of the time in the timeval
    262  * to which tvp points.  We do this by returning the current time
    263  * plus the amount of time since the last clock interrupt.
    264  *
    265  * Check that this time is no less than any previously-reported time,
    266  * which could happen around the time of a clock adjustment.  Just for
    267  * fun, we guarantee that the time will be greater than the value
    268  * obtained by a previous call.
    269  */
    270 void
    271 microtime(tvp)
    272 	register struct timeval *tvp;
    273 {
    274 	int s = splhigh();
    275 	static struct timeval lasttime;
    276 
    277 	*tvp = time;
    278 	tvp->tv_usec++; 	/* XXX */
    279 	while (tvp->tv_usec > 1000000) {
    280 		tvp->tv_sec++;
    281 		tvp->tv_usec -= 1000000;
    282 	}
    283 	if (tvp->tv_sec == lasttime.tv_sec &&
    284 		tvp->tv_usec <= lasttime.tv_usec &&
    285 		(tvp->tv_usec = lasttime.tv_usec + 1) > 1000000)
    286 	{
    287 		tvp->tv_sec++;
    288 		tvp->tv_usec -= 1000000;
    289 	}
    290 	lasttime = *tvp;
    291 	splx(s);
    292 }
    293 
    294 
    295 /*
    296  * Machine-dependent clock routines.
    297  *
    298  * Inittodr initializes the time of day hardware which provides
    299  * date functions.
    300  *
    301  * Resettodr restores the time of day hardware after a time change.
    302  */
    303 #define SECDAY		86400L
    304 #define SECYR		(SECDAY * 365)
    305 
    306 static long clk_get_secs(void);
    307 static void clk_set_secs(long);
    308 
    309 /*
    310  * Initialize the time of day register, based on the time base
    311  * which is, e.g. from a filesystem.
    312  */
    313 void inittodr(fs_time)
    314 	time_t fs_time;
    315 {
    316 	long diff, clk_time;
    317 	long long_ago = (5 * SECYR);
    318 	int clk_bad = 0;
    319 
    320 	/*
    321 	 * Sanity check time from file system.
    322 	 * If it is zero,assume filesystem time is just unknown
    323 	 * instead of preposterous.  Don't bark.
    324 	 */
    325 	if (fs_time < long_ago) {
    326 		/*
    327 		 * If fs_time is zero, assume filesystem time is just
    328 		 * unknown instead of preposterous.  Don't bark.
    329 		 */
    330 		if (fs_time != 0)
    331 			printf("WARNING: preposterous time in file system\n");
    332 		/* 1991/07/01  12:00:00 */
    333 		fs_time = 21*SECYR + 186*SECDAY + SECDAY/2;
    334 	}
    335 
    336 	clk_time = clk_get_secs();
    337 
    338 	/* Sanity check time from clock. */
    339 	if (clk_time < long_ago) {
    340 		printf("WARNING: bad date in battery clock");
    341 		clk_bad = 1;
    342 		clk_time = fs_time;
    343 	} else {
    344 		/* Does the clock time jive with the file system? */
    345 		diff = clk_time - fs_time;
    346 		if (diff < 0)
    347 			diff = -diff;
    348 		if (diff >= (SECDAY*2)) {
    349 			printf("WARNING: clock %s %d days",
    350 				   (clk_time < fs_time) ? "lost" : "gained",
    351 				   (int) (diff / SECDAY));
    352 			clk_bad = 1;
    353 		}
    354 	}
    355 	if (clk_bad)
    356 		printf(" -- CHECK AND RESET THE DATE!\n");
    357 	time.tv_sec = clk_time;
    358 }
    359 
    360 /*
    361  * Resettodr restores the time of day hardware after a time change.
    362  */
    363 void resettodr()
    364 {
    365 	clk_set_secs(time.tv_sec);
    366 }
    367 
    368 
    369 /*
    371  * XXX - Todo: take one of the implementations of
    372  * "POSIX time" to/from "YY/MM/DD/hh/mm/ss"
    373  * and put that in libkern (or somewhere).
    374  * Also put this stuct in some header...
    375  */
    376 struct date_time {
    377     u_char dt_year;	/* since POSIX_BASE_YEAR (1970) */
    378     u_char dt_mon;
    379     u_char dt_day;
    380     u_char dt_hour;
    381     u_char dt_min;
    382     u_char dt_sec;
    383 	u_char dt_csec;	/* hundredths of a second */
    384 	u_char dt_wday;	/* Day of week (needed?) */
    385 };
    386 void gmt_to_dt __P((long gmt, struct date_time *dt));
    387 long dt_to_gmt __P((struct date_time *dt));
    388 /* Traditional UNIX base year */
    389 #define	POSIX_BASE_YEAR	1970
    390 /*
    391  * XXX - End of stuff that should move to a header.
    392  */
    393 
    394 
    395 /*
    396  * Routines to copy state into and out of the clock.
    397  * The clock CSR has to be set for read or write.
    398  */
    399 static void
    400 clk_get_dt(struct date_time *dt)
    401 {
    402 	volatile struct clockreg *cl = clock_va;
    403 	int s;
    404 
    405 	s = splhigh();
    406 
    407 	/* XXX - Wait for the end of this second? */
    408 	dt->dt_csec = 0;
    409 
    410 	/* enable read (stop time) */
    411 	cl->cl_csr |= CLK_READ;
    412 
    413 	/* Copy the info */
    414 	dt->dt_sec  = cl->cl_sec;
    415 	dt->dt_min  = cl->cl_min;
    416 	dt->dt_hour = cl->cl_hour;
    417 	dt->dt_wday = cl->cl_wday;
    418 	dt->dt_day  = cl->cl_mday;
    419 	dt->dt_mon  = cl->cl_month;
    420 	dt->dt_year = cl->cl_year;
    421 
    422 	/* Done reading (time wears on) */
    423 	cl->cl_csr &= ~CLK_READ;
    424 	splx(s);
    425 }
    426 
    427 static void
    428 clk_set_dt(struct date_time *dt)
    429 {
    430 	volatile struct clockreg *cl = clock_va;
    431 	int s;
    432 
    433 	s = splhigh();
    434 	/* enable write */
    435 	cl->cl_csr |= CLK_WRITE;
    436 
    437 	/* Copy the info */
    438 	cl->cl_sec = dt->dt_sec;
    439 	cl->cl_min = dt->dt_min;
    440 	cl->cl_hour = dt->dt_hour;
    441 	cl->cl_wday = dt->dt_wday;
    442 	cl->cl_mday = dt->dt_day;
    443 	cl->cl_month = dt->dt_mon;
    444 	cl->cl_year = dt->dt_year;
    445 
    446 	/* load them up */
    447 	cl->cl_csr &= ~CLK_WRITE;
    448 	splx(s);
    449 }
    450 
    451 
    452 /*
    453  * BCD to decimal and decimal to BCD.
    454  */
    455 #define	FROMBCD(x)	(((x) >> 4) * 10 + ((x) & 0xf))
    456 #define	TOBCD(x)	(((x) / 10 * 16) + ((x) % 10))
    457 
    458 /*
    459  * Now routines to get and set clock as POSIX time.
    460  * Our clock keeps "years since 1/1/1968", so we must
    461  * convert to/from "years since 1/1/1970" before the
    462  * common time conversion functions are used.
    463  */
    464 #define	CLOCK_YEAR_ADJUST (POSIX_BASE_YEAR - 1968)
    465 static long
    466 clk_get_secs()
    467 {
    468 	struct date_time dt;
    469 	long gmt;
    470 
    471 	clk_get_dt(&dt);
    472 
    473 	/* Convert BCD values to binary. */
    474 	dt.dt_sec  = FROMBCD(dt.dt_sec);
    475 	dt.dt_min  = FROMBCD(dt.dt_min);
    476 	dt.dt_hour = FROMBCD(dt.dt_hour);
    477 	dt.dt_day  = FROMBCD(dt.dt_day);
    478 	dt.dt_mon  = FROMBCD(dt.dt_mon);
    479 	dt.dt_year = FROMBCD(dt.dt_year);
    480 
    481 	dt.dt_year -= CLOCK_YEAR_ADJUST;
    482 	gmt = dt_to_gmt(&dt);
    483 	return (gmt);
    484 }
    485 
    486 static void
    487 clk_set_secs(secs)
    488 	long secs;
    489 {
    490 	struct date_time dt;
    491 	long gmt;
    492 
    493 	gmt = secs;
    494 	gmt_to_dt(gmt, &dt);
    495 	dt.dt_year += CLOCK_YEAR_ADJUST;
    496 
    497 	/* Convert binary values to BCD. */
    498 	dt.dt_sec  = TOBCD(dt.dt_sec);
    499 	dt.dt_min  = TOBCD(dt.dt_min);
    500 	dt.dt_hour = TOBCD(dt.dt_hour);
    501 	dt.dt_day  = TOBCD(dt.dt_day);
    502 	dt.dt_mon  = TOBCD(dt.dt_mon);
    503 	dt.dt_year = TOBCD(dt.dt_year);
    504 
    505 	clk_set_dt(&dt);
    506 }
    507 
    508 
    509 
    510 /*****************************************************************
    512  *
    513  * Generic routines to convert to or from a POSIX date
    514  * (seconds since 1/1/1970) and  yr/mo/day/hr/min/sec
    515  *
    516  * These are organized this way mostly to so the code
    517  * can easily be tested in an independent user program.
    518  * (These are derived from the hp300 code.)
    519  *
    520  * XXX - Should move these to libkern or somewhere...
    521  */
    522 static inline int leapyear __P((int year));
    523 #define FEBRUARY	2
    524 #define	days_in_year(a) 	(leapyear(a) ? 366 : 365)
    525 #define	days_in_month(a) 	(month_days[(a) - 1])
    526 
    527 /*
    528  * Note:  This array may be modified by gmt_to_dt(),
    529  * but these functions DO NOT need to be reentrant.
    530  * If we ever DO need reentrance, we should just make
    531  * gmt_to_dt() copy this to a local before use. -gwr
    532  */
    533 static int month_days[12] = {
    534 	31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
    535 };
    536 
    537 /* Use an inline to make the logic more obvious. */
    538 static inline int
    539 leapyear(year)
    540 	int year;
    541 {
    542 	int rv = 0;
    543 
    544 	if ((year % 4) == 0) {
    545 		rv = 1;
    546 		if ((year % 100) == 0) {
    547 			rv = 0;
    548 			if ((year % 400) == 0)
    549 				rv = 1;
    550 		}
    551 	}
    552 	return rv;
    553 }
    554 
    555 void gmt_to_dt(long gmt, struct date_time *dt)
    556 {
    557 	long secs;
    558 	int i, days;
    559 
    560 	days = gmt / SECDAY;
    561 	secs = gmt % SECDAY;
    562 
    563 	/* Hours, minutes, seconds are easy */
    564 	dt->dt_hour = secs / 3600;
    565 	secs = secs % 3600;
    566 	dt->dt_min  = secs / 60;
    567 	secs = secs % 60;
    568 	dt->dt_sec  = secs;
    569 
    570 	/* Day of week (Note: 1/1/1970 was a Thursday) */
    571 	dt->dt_wday = (days + 4) % 7;
    572 
    573 	/* Subtract out whole years... */
    574 	i = POSIX_BASE_YEAR;
    575 	while (days >= days_in_year(i)) {
    576 		days -= days_in_year(i);
    577 		i++;
    578 	}
    579 	dt->dt_year = i - POSIX_BASE_YEAR;
    580 
    581 	/* Subtract out whole months... */
    582 	/* XXX - Note temporary change to month_days */
    583 	if (leapyear(i))
    584 		days_in_month(FEBRUARY) = 29;
    585 	for (i = 1; days >= days_in_month(i); i++)
    586 		days -= days_in_month(i);
    587 	/* XXX - Undo temporary change to month_days */
    588 	days_in_month(FEBRUARY) = 28;
    589 	dt->dt_mon = i;
    590 
    591 	/* Days are what is left over (+1) from all that. */
    592 	dt->dt_day = days + 1;
    593 }
    594 
    595 long dt_to_gmt(struct date_time *dt)
    596 {
    597 	long gmt;
    598 	int i, year;
    599 
    600 	/*
    601 	 * Hours are different for some reason. Makes no sense really.
    602 	 */
    603 
    604 	gmt = 0;
    605 
    606 	if (dt->dt_hour >= 24) goto out;
    607 	if (dt->dt_day  >  31) goto out;
    608 	if (dt->dt_mon   > 12) goto out;
    609 
    610 	year = dt->dt_year + POSIX_BASE_YEAR;
    611 
    612 	/*
    613 	 * Compute days since start of time
    614 	 * First from years, then from months.
    615 	 */
    616 	for (i = POSIX_BASE_YEAR; i < year; i++)
    617 		gmt += days_in_year(i);
    618 	if (leapyear(year) && dt->dt_mon > FEBRUARY)
    619 		gmt++;
    620 
    621 	/* Months */
    622 	for (i = 1; i < dt->dt_mon; i++)
    623 	  	gmt += days_in_month(i);
    624 	gmt += (dt->dt_day - 1);
    625 
    626 	/* Now do hours */
    627 	gmt = gmt * 24 + dt->dt_hour;
    628 
    629 	/* Now do minutes */
    630 	gmt = gmt * 60 + dt->dt_min;
    631 
    632 	/* Now do seconds */
    633 	gmt = gmt * 60 + dt->dt_sec;
    634 
    635  out:
    636 	return gmt;
    637 }
    638