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