Home | History | Annotate | Line # | Download | only in sun3x
clock.c revision 1.8
      1 /*	$NetBSD: clock.c,v 1.8 1997/02/19 23:38:46 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 <dev/clock_subr.h>
     63 
     64 #include <sun3/sun3/interreg.h>
     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  * XXX  The Sun3/80 always has the MK4802, while the
     89  * XXX  Sun3/470 can (reportedly) have that or the old
     90  * XXX  intersil7170.  Should have two clock drivers...
     91  */
     92 static int
     93 clock_match(parent, cf, args)
     94     struct device *parent;
     95 	struct cfdata *cf;
     96     void *args;
     97 {
     98 	struct confargs *ca = args;
     99 
    100 	/* This driver only supports one unit. */
    101 	if (cf->cf_unit != 0)
    102 		return (0);
    103 
    104 	/* Validate the given address. */
    105 	if (ca->ca_paddr != OBIO_CLOCK2)
    106 		return (0);
    107 
    108 	/* Default interrupt priority. */
    109 	if (ca->ca_intpri == -1)
    110 		ca->ca_intpri = CLOCK_PRI;
    111 
    112 	return (1);
    113 }
    114 
    115 static void
    116 clock_attach(parent, self, args)
    117 	struct device *parent;
    118 	struct device *self;
    119 	void *args;
    120 {
    121 
    122 	printf("\n");
    123 
    124 	/*
    125 	 * Can not hook up the ISR until cpu_initclocks()
    126 	 * because hardclock is not ready until then.
    127 	 * For now, the handler is _isr_autovec(), which
    128 	 * will complain if it gets clock interrupts.
    129 	 */
    130 }
    131 
    132 /*
    133  * Set and/or clear the desired clock bits in the interrupt
    134  * register.  We have to be extremely careful that we do it
    135  * in such a manner that we don't get ourselves lost.
    136  */
    137 void
    138 set_clk_mode(on, off, enable)
    139 	u_char on, off;
    140 	int enable;
    141 {
    142 	register u_char interreg;
    143 	register int s;
    144 
    145 	/* If we don't have this, we must not have touched it! */
    146 	if (!interrupt_reg)
    147 		return;
    148 
    149 	s = getsr();
    150 	if ((s & PSL_IPL) < PSL_IPL7)
    151 		panic("set_clk_mode: ipl");
    152 
    153 	/*
    154 	 * make sure that we are only playing w/
    155 	 * clock interrupt register bits
    156 	 */
    157 	on &= (IREG_CLOCK_ENAB_7 | IREG_CLOCK_ENAB_5);
    158 	off &= (IREG_CLOCK_ENAB_7 | IREG_CLOCK_ENAB_5);
    159 
    160 	/*
    161 	 * Get a copy of current interrupt register,
    162 	 * turning off any undesired bits (aka `off')
    163 	 */
    164 	interreg = *interrupt_reg & ~(off | IREG_ALL_ENAB);
    165 	*interrupt_reg &= ~IREG_ALL_ENAB;
    166 
    167 	/*
    168 	 * Next we turns off the CLK5 and CLK7 bits to clear
    169 	 * the flip-flops, then we disable clock interrupts.
    170 	 * Now we can read the clock's interrupt register
    171 	 * to clear any pending signals there.
    172 	 */
    173 	*interrupt_reg &= ~(IREG_CLOCK_ENAB_7 | IREG_CLOCK_ENAB_5);
    174 
    175 	/* XXX - hit the clock? */
    176 
    177 	/*
    178 	 * Now we set all the desired bits
    179 	 * in the interrupt register, then
    180 	 * we turn the clock back on and
    181 	 * finally we can enable all interrupts.
    182 	 */
    183 	*interrupt_reg |= (interreg | on);		/* enable flip-flops */
    184 
    185 	/* XXX - hit the clock? */
    186 
    187 	*interrupt_reg |= IREG_ALL_ENAB;		/* enable interrupts */
    188 }
    189 
    190 /* Called very early by internal_configure. */
    191 void clock_init()
    192 {
    193 	/* XXX - Yes, use the EEPROM address.  Same H/W device. */
    194 	clock_va = obio_find_mapping(OBIO_EEPROM, sizeof(struct clockreg));
    195 
    196 	if (!clock_va || !interrupt_reg) {
    197 		mon_printf("clock_init\n");
    198 		sunmon_abort();
    199 	}
    200 
    201 	/* Turn off clock interrupts until cpu_initclocks() */
    202 	/* intreg_init() already cleared the interrupt register. */
    203 }
    204 
    205 /*
    206  * Set up the real-time clock (enable clock interrupts).
    207  * Leave stathz 0 since there is no secondary clock available.
    208  * Note that clock interrupts MUST STAY DISABLED until here.
    209  */
    210 void
    211 cpu_initclocks(void)
    212 {
    213 	int s;
    214 
    215 	if (!clock_va)
    216 		panic("cpu_initclocks");
    217 	s = splhigh();
    218 
    219 	/* Install isr (in locore.s) that calls clock_intr(). */
    220 	isr_add_custom(5, (void*)_isr_clock);
    221 
    222 	/* Set the clock to interrupt 100 time per second. */
    223 	/* XXX - Hard wired? */
    224 
    225 	*interrupt_reg |= IREG_CLOCK_ENAB_5;	/* enable clock */
    226 
    227 	/* XXX enable the clock? */
    228 
    229 	*interrupt_reg |= IREG_ALL_ENAB;		/* enable interrupts */
    230 	splx(s);
    231 }
    232 
    233 /*
    234  * This doesn't need to do anything, as we have only one timer and
    235  * profhz==stathz==hz.
    236  */
    237 void
    238 setstatclockrate(newhz)
    239 	int newhz;
    240 {
    241 	/* nothing */
    242 }
    243 
    244 /*
    245  * This is is called by the "custom" interrupt handler.
    246  */
    247 void
    248 clock_intr(cf)
    249 	struct clockframe cf;
    250 {
    251 	/* volatile struct clockreg *clk = clock_va; */
    252 
    253 #if 1	/* XXX - Needed? */
    254 	/* Pulse the clock intr. enable low. */
    255 	*interrupt_reg &= ~IREG_CLOCK_ENAB_5;
    256 	*interrupt_reg |=  IREG_CLOCK_ENAB_5;
    257 #endif
    258 
    259 	hardclock(&cf);
    260 }
    261 
    262 /*
    263  * Return the best possible estimate of the time in the timeval
    264  * to which tvp points.  We do this by returning the current time
    265  * plus the amount of time since the last clock interrupt.
    266  *
    267  * Check that this time is no less than any previously-reported time,
    268  * which could happen around the time of a clock adjustment.  Just for
    269  * fun, we guarantee that the time will be greater than the value
    270  * obtained by a previous call.
    271  */
    272 void
    273 microtime(tvp)
    274 	register struct timeval *tvp;
    275 {
    276 	int s = splhigh();
    277 	static struct timeval lasttime;
    278 
    279 	*tvp = time;
    280 	tvp->tv_usec++; 	/* XXX */
    281 	while (tvp->tv_usec > 1000000) {
    282 		tvp->tv_sec++;
    283 		tvp->tv_usec -= 1000000;
    284 	}
    285 	if (tvp->tv_sec == lasttime.tv_sec &&
    286 		tvp->tv_usec <= lasttime.tv_usec &&
    287 		(tvp->tv_usec = lasttime.tv_usec + 1) > 1000000)
    288 	{
    289 		tvp->tv_sec++;
    290 		tvp->tv_usec -= 1000000;
    291 	}
    292 	lasttime = *tvp;
    293 	splx(s);
    294 }
    295 
    296 
    297 /*
    298  * Machine-dependent clock routines.
    299  *
    300  * Inittodr initializes the time of day hardware which provides
    301  * date functions.
    302  *
    303  * Resettodr restores the time of day hardware after a time change.
    304  */
    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 /*
    370  * Routines to copy state into and out of the clock.
    371  * The clock CSR has to be set for read or write.
    372  */
    373 static void
    374 clk_get_dt(struct clock_ymdhms *dt)
    375 {
    376 	volatile struct clockreg *cl = clock_va;
    377 	int s;
    378 
    379 	s = splhigh();
    380 
    381 	/* enable read (stop time) */
    382 	cl->cl_csr |= CLK_READ;
    383 
    384 	/* Copy the info */
    385 	dt->dt_sec  = cl->cl_sec;
    386 	dt->dt_min  = cl->cl_min;
    387 	dt->dt_hour = cl->cl_hour;
    388 	dt->dt_wday = cl->cl_wday;
    389 	dt->dt_day  = cl->cl_mday;
    390 	dt->dt_mon  = cl->cl_month;
    391 	dt->dt_year = cl->cl_year;
    392 
    393 	/* Done reading (time wears on) */
    394 	cl->cl_csr &= ~CLK_READ;
    395 	splx(s);
    396 }
    397 
    398 static void
    399 clk_set_dt(struct clock_ymdhms *dt)
    400 {
    401 	volatile struct clockreg *cl = clock_va;
    402 	int s;
    403 
    404 	s = splhigh();
    405 	/* enable write */
    406 	cl->cl_csr |= CLK_WRITE;
    407 
    408 	/* Copy the info */
    409 	cl->cl_sec = dt->dt_sec;
    410 	cl->cl_min = dt->dt_min;
    411 	cl->cl_hour = dt->dt_hour;
    412 	cl->cl_wday = dt->dt_wday;
    413 	cl->cl_mday = dt->dt_day;
    414 	cl->cl_month = dt->dt_mon;
    415 	cl->cl_year = dt->dt_year;
    416 
    417 	/* load them up */
    418 	cl->cl_csr &= ~CLK_WRITE;
    419 	splx(s);
    420 }
    421 
    422 
    423 /*
    424  * Now routines to get and set clock as POSIX time.
    425  * Our clock keeps "years since 1/1/1968".
    426  */
    427 #define	CLOCK_BASE_YEAR 1968
    428 
    429 static long
    430 clk_get_secs()
    431 {
    432 	struct clock_ymdhms dt;
    433 	long secs;
    434 
    435 	clk_get_dt(&dt);
    436 
    437 	/* Convert BCD values to binary. */
    438 	dt.dt_sec  = FROMBCD(dt.dt_sec);
    439 	dt.dt_min  = FROMBCD(dt.dt_min);
    440 	dt.dt_hour = FROMBCD(dt.dt_hour);
    441 	dt.dt_day  = FROMBCD(dt.dt_day);
    442 	dt.dt_mon  = FROMBCD(dt.dt_mon);
    443 	dt.dt_year = FROMBCD(dt.dt_year);
    444 
    445 	if ((dt.dt_hour > 24) ||
    446 		(dt.dt_day  > 31) ||
    447 		(dt.dt_mon  > 12))
    448 		return (0);
    449 
    450 	dt.dt_year += CLOCK_BASE_YEAR;
    451 	secs = clock_ymdhms_to_secs(&dt);
    452 	return (secs);
    453 }
    454 
    455 static void
    456 clk_set_secs(secs)
    457 	long secs;
    458 {
    459 	struct clock_ymdhms dt;
    460 
    461 	clock_secs_to_ymdhms(secs, &dt);
    462 	dt.dt_year -= CLOCK_BASE_YEAR;
    463 
    464 	/* Convert binary values to BCD. */
    465 	dt.dt_sec  = TOBCD(dt.dt_sec);
    466 	dt.dt_min  = TOBCD(dt.dt_min);
    467 	dt.dt_hour = TOBCD(dt.dt_hour);
    468 	dt.dt_day  = TOBCD(dt.dt_day);
    469 	dt.dt_mon  = TOBCD(dt.dt_mon);
    470 	dt.dt_year = TOBCD(dt.dt_year);
    471 
    472 	clk_set_dt(&dt);
    473 }
    474