Home | History | Annotate | Line # | Download | only in sun3x
clock.c revision 1.4
      1 /*	$NetBSD: clock.c,v 1.4 1997/01/25 21:46:19 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 <sun3/sun3/sunmon.h>
     64 
     65 #include "mostek48t02.h"
     66 
     67 #define	CLOCK_PRI	5
     68 
     69 void _isr_clock __P((void));	/* in locore.s */
     70 void clock_intr __P((struct clockframe));
     71 
     72 /* Note: this is used by locore.s:__isr_clock */
     73 static volatile void *clock_va;
     74 
     75 static int  clock_match __P((struct device *, struct cfdata *, void *args));
     76 static void clock_attach __P((struct device *, struct device *, void *));
     77 
     78 struct cfattach clock_ca = {
     79 	sizeof(struct device), clock_match, clock_attach
     80 };
     81 
     82 struct cfdriver clock_cd = {
     83 	NULL, "clock", DV_DULL
     84 };
     85 
     86 /*
     87  * XXX - Need to determine which type of clock we have!
     88  */
     89 static int
     90 clock_match(parent, cf, args)
     91     struct device *parent;
     92 	struct cfdata *cf;
     93     void *args;
     94 {
     95 	struct confargs *ca = args;
     96 
     97 	/* This driver only supports one unit. */
     98 	if (cf->cf_unit != 0)
     99 		return (0);
    100 
    101 	/* Validate the given address. */
    102 	if (ca->ca_paddr != OBIO_CLOCK2)
    103 		return (0);
    104 
    105 	/* Default interrupt priority. */
    106 	if (ca->ca_intpri == -1)
    107 		ca->ca_intpri = CLOCK_PRI;
    108 
    109 	return (1);
    110 }
    111 
    112 static void
    113 clock_attach(parent, self, args)
    114 	struct device *parent;
    115 	struct device *self;
    116 	void *args;
    117 {
    118 
    119 	printf("\n");
    120 
    121 	/*
    122 	 * Can not hook up the ISR until cpu_initclocks()
    123 	 * because hardclock is not ready until then.
    124 	 * For now, the handler is _isr_autovec(), which
    125 	 * will complain if it gets clock interrupts.
    126 	 */
    127 }
    128 
    129 /*
    130  * Set and/or clear the desired clock bits in the interrupt
    131  * register.  We have to be extremely careful that we do it
    132  * in such a manner that we don't get ourselves lost.
    133  */
    134 void
    135 set_clk_mode(on, off, enable)
    136 	u_char on, off;
    137 	int enable;
    138 {
    139 	register u_char interreg;
    140 	register int s;
    141 
    142 	/* If we don't have this, we must not have touched it! */
    143 	if (!clock_va)
    144 		return;
    145 
    146 	s = getsr();
    147 	if ((s & PSL_IPL) < PSL_IPL7)
    148 		panic("set_clk_mode: ipl");
    149 
    150 	/*
    151 	 * make sure that we are only playing w/
    152 	 * clock interrupt register bits
    153 	 */
    154 	on &= (IREG_CLOCK_ENAB_7 | IREG_CLOCK_ENAB_5);
    155 	off &= (IREG_CLOCK_ENAB_7 | IREG_CLOCK_ENAB_5);
    156 
    157 	/*
    158 	 * Get a copy of current interrupt register,
    159 	 * turning off any undesired bits (aka `off')
    160 	 */
    161 	interreg = *interrupt_reg & ~(off | IREG_ALL_ENAB);
    162 	*interrupt_reg &= ~IREG_ALL_ENAB;
    163 
    164 	/*
    165 	 * Next we turns off the CLK5 and CLK7 bits to clear
    166 	 * the flip-flops, then we disable clock interrupts.
    167 	 * Now we can read the clock's interrupt register
    168 	 * to clear any pending signals there.
    169 	 */
    170 	*interrupt_reg &= ~(IREG_CLOCK_ENAB_7 | IREG_CLOCK_ENAB_5);
    171 
    172 	/* XXX - hit the clock? */
    173 
    174 	/*
    175 	 * Now we set all the desired bits
    176 	 * in the interrupt register, then
    177 	 * we turn the clock back on and
    178 	 * finally we can enable all interrupts.
    179 	 */
    180 	*interrupt_reg |= (interreg | on);		/* enable flip-flops */
    181 
    182 	/* XXX - hit the clock? */
    183 
    184 	*interrupt_reg |= IREG_ALL_ENAB;		/* enable interrupts */
    185 }
    186 
    187 /* Called very early by internal_configure. */
    188 void clock_init()
    189 {
    190 	/* XXX - Yes, use the EEPROM address.  Same H/W device. */
    191 	clock_va = obio_find_mapping(OBIO_EEPROM, sizeof(struct clockreg));
    192 
    193 	if (!clock_va || !interrupt_reg) {
    194 		mon_printf("clock_init\n");
    195 		sunmon_abort();
    196 	}
    197 
    198 	/* Turn off clock interrupts until cpu_initclocks() */
    199 	/* isr_init() already set the interrupt reg to zero. */
    200 }
    201 
    202 /*
    203  * Set up the real-time clock (enable clock interrupts).
    204  * Leave stathz 0 since there is no secondary clock available.
    205  * Note that clock interrupts MUST STAY DISABLED until here.
    206  */
    207 void
    208 cpu_initclocks(void)
    209 {
    210 	int s;
    211 
    212 	if (!clock_va)
    213 		panic("cpu_initclocks");
    214 	s = splhigh();
    215 
    216 	/* Install isr (in locore.s) that calls clock_intr(). */
    217 	isr_add_custom(5, (void*)_isr_clock);
    218 
    219 	/* Set the clock to interrupt 100 time per second. */
    220 	/* XXX - Hard wired? */
    221 
    222 	*interrupt_reg |= IREG_CLOCK_ENAB_5;	/* enable clock */
    223 
    224 	/* XXX enable the clock? */
    225 
    226 	*interrupt_reg |= IREG_ALL_ENAB;		/* enable interrupts */
    227 	splx(s);
    228 }
    229 
    230 /*
    231  * This doesn't need to do anything, as we have only one timer and
    232  * profhz==stathz==hz.
    233  */
    234 void
    235 setstatclockrate(newhz)
    236 	int newhz;
    237 {
    238 	/* nothing */
    239 }
    240 
    241 /*
    242  * This is is called by the "custom" interrupt handler.
    243  */
    244 void
    245 clock_intr(cf)
    246 	struct clockframe cf;
    247 {
    248 	/* volatile struct clockreg *clk = clock_va; */
    249 
    250 #if 1	/* XXX - Needed? */
    251 	/* Pulse the clock intr. enable low. */
    252 	*interrupt_reg &= ~IREG_CLOCK_ENAB_5;
    253 	*interrupt_reg |=  IREG_CLOCK_ENAB_5;
    254 #endif
    255 
    256 	/* XXX - Need to do anything? */
    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 
    400 static void
    401 clk_get_dt(struct date_time *dt)
    402 {
    403 	volatile struct clockreg *cl = clock_va;
    404 	int s;
    405 
    406 	s = splhigh();
    407 	/* enable read (stop time) */
    408 	cl->cl_csr |= CLK_READ;
    409 
    410 	/* Copy the info */
    411 	dt->dt_sec  = cl->cl_sec;
    412 	dt->dt_min  = cl->cl_min;
    413 	dt->dt_hour = cl->cl_hour;
    414 	dt->dt_wday = cl->cl_wday;
    415 	dt->dt_day  = cl->cl_mday;
    416 	dt->dt_mon  = cl->cl_month;
    417 	dt->dt_year = cl->cl_year;
    418 
    419 	/* Done reading (time wears on) */
    420 	cl->cl_csr &= ~CLK_READ;
    421 	splx(s);
    422 }
    423 
    424 static void
    425 clk_set_dt(struct date_time *dt)
    426 {
    427 	volatile struct clockreg *cl = clock_va;
    428 	int s;
    429 
    430 	s = splhigh();
    431 	/* enable write */
    432 	cl->cl_csr |= CLK_WRITE;
    433 
    434 	/* Copy the info */
    435 	cl->cl_sec = dt->dt_sec;
    436 	cl->cl_min = dt->dt_min;
    437 	cl->cl_hour = dt->dt_hour;
    438 	cl->cl_wday = dt->dt_wday;
    439 	cl->cl_mday = dt->dt_day;
    440 	cl->cl_month = dt->dt_mon;
    441 	cl->cl_year = dt->dt_year;
    442 
    443 	/* load them up */
    444 	cl->cl_csr &= ~CLK_WRITE;
    445 	splx(s);
    446 }
    447 
    448 
    449 /*
    450  * Now routines to get and set clock as POSIX time.
    451  * Our clock keeps "years since 1/1/1968", so we must
    452  * convert to/from "years since 1/1/1970" before the
    453  * common time conversion functions are used.
    454  */
    455 #define	CLOCK_YEAR_ADJUST (POSIX_BASE_YEAR - 1968)
    456 static long
    457 clk_get_secs()
    458 {
    459 	struct date_time dt;
    460 	long gmt;
    461 
    462 	clk_get_dt(&dt);
    463 	dt.dt_year -= CLOCK_YEAR_ADJUST;
    464 	gmt = dt_to_gmt(&dt);
    465 	return (gmt);
    466 }
    467 static void
    468 clk_set_secs(secs)
    469 	long secs;
    470 {
    471 	struct date_time dt;
    472 	long gmt;
    473 
    474 	gmt = secs;
    475 	gmt_to_dt(gmt, &dt);
    476 	dt.dt_year += CLOCK_YEAR_ADJUST;
    477 	clk_set_dt(&dt);
    478 }
    479 
    480 
    481 
    482 /*****************************************************************
    484  *
    485  * Generic routines to convert to or from a POSIX date
    486  * (seconds since 1/1/1970) and  yr/mo/day/hr/min/sec
    487  *
    488  * These are organized this way mostly to so the code
    489  * can easily be tested in an independent user program.
    490  * (These are derived from the hp300 code.)
    491  *
    492  * XXX - Should move these to libkern or somewhere...
    493  */
    494 static inline int leapyear __P((int year));
    495 #define FEBRUARY	2
    496 #define	days_in_year(a) 	(leapyear(a) ? 366 : 365)
    497 #define	days_in_month(a) 	(month_days[(a) - 1])
    498 
    499 /*
    500  * Note:  This array may be modified by gmt_to_dt(),
    501  * but these functions DO NOT need to be reentrant.
    502  * If we ever DO need reentrance, we should just make
    503  * gmt_to_dt() copy this to a local before use. -gwr
    504  */
    505 static int month_days[12] = {
    506 	31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
    507 };
    508 
    509 /* Use an inline to make the logic more obvious. */
    510 static inline int
    511 leapyear(year)
    512 	int year;
    513 {
    514 	int rv = 0;
    515 
    516 	if ((year % 4) == 0) {
    517 		rv = 1;
    518 		if ((year % 100) == 0) {
    519 			rv = 0;
    520 			if ((year % 400) == 0)
    521 				rv = 1;
    522 		}
    523 	}
    524 	return rv;
    525 }
    526 
    527 void gmt_to_dt(long gmt, struct date_time *dt)
    528 {
    529 	long secs;
    530 	int i, days;
    531 
    532 	days = gmt / SECDAY;
    533 	secs = gmt % SECDAY;
    534 
    535 	/* Hours, minutes, seconds are easy */
    536 	dt->dt_hour = secs / 3600;
    537 	secs = secs % 3600;
    538 	dt->dt_min  = secs / 60;
    539 	secs = secs % 60;
    540 	dt->dt_sec  = secs;
    541 
    542 	/* Day of week (Note: 1/1/1970 was a Thursday) */
    543 	dt->dt_wday = (days + 4) % 7;
    544 
    545 	/* Subtract out whole years... */
    546 	i = POSIX_BASE_YEAR;
    547 	while (days >= days_in_year(i)) {
    548 		days -= days_in_year(i);
    549 		i++;
    550 	}
    551 	dt->dt_year = i - POSIX_BASE_YEAR;
    552 
    553 	/* Subtract out whole months... */
    554 	/* XXX - Note temporary change to month_days */
    555 	if (leapyear(i))
    556 		days_in_month(FEBRUARY) = 29;
    557 	for (i = 1; days >= days_in_month(i); i++)
    558 		days -= days_in_month(i);
    559 	/* XXX - Undo temporary change to month_days */
    560 	days_in_month(FEBRUARY) = 28;
    561 	dt->dt_mon = i;
    562 
    563 	/* Days are what is left over (+1) from all that. */
    564 	dt->dt_day = days + 1;
    565 }
    566 
    567 long dt_to_gmt(struct date_time *dt)
    568 {
    569 	long gmt;
    570 	int i, year;
    571 
    572 	/*
    573 	 * Hours are different for some reason. Makes no sense really.
    574 	 */
    575 
    576 	gmt = 0;
    577 
    578 	if (dt->dt_hour >= 24) goto out;
    579 	if (dt->dt_day  >  31) goto out;
    580 	if (dt->dt_mon   > 12) goto out;
    581 
    582 	year = dt->dt_year + POSIX_BASE_YEAR;
    583 
    584 	/*
    585 	 * Compute days since start of time
    586 	 * First from years, then from months.
    587 	 */
    588 	for (i = POSIX_BASE_YEAR; i < year; i++)
    589 		gmt += days_in_year(i);
    590 	if (leapyear(year) && dt->dt_mon > FEBRUARY)
    591 		gmt++;
    592 
    593 	/* Months */
    594 	for (i = 1; i < dt->dt_mon; i++)
    595 	  	gmt += days_in_month(i);
    596 	gmt += (dt->dt_day - 1);
    597 
    598 	/* Now do hours */
    599 	gmt = gmt * 24 + dt->dt_hour;
    600 
    601 	/* Now do minutes */
    602 	gmt = gmt * 60 + dt->dt_min;
    603 
    604 	/* Now do seconds */
    605 	gmt = gmt * 60 + dt->dt_sec;
    606 
    607  out:
    608 	return gmt;
    609 }
    610