V9/sys/sun3/rtclock.c

Compare this file to the similar file:
Show the results in this format:

/*
 * Copyright (c) 1985 by Sun Microsystems, Inc.
 */

#include "../h/param.h"
#include "../h/systm.h"
#include "../machine/clock.h"
#include "../machine/interreg.h"

/*
 * Machine-dependent clock routines.
 *
 * Clockstart restarts the real-time clock, which provides
 * hardclock interrupts to clock.c.
 *
 * Clkinit initializes the time of day hardware which provides
 * date functions.  Its primary function is to use some file
 * system information in case the hardware clock lost state.
 *
 * Clkset restores the time of day hardware after a time change.
 */

/*
 * Start the real-time clock.
 */
clkstart()
{

	/*
	 * We will set things up to interrupt every 1/100 of a second.
	 * locore.s currently only calls hardclock every other clock
	 * interrupt, thus assuming 50 hz operation.
	 */
	if (hz != 50)
		panic("clkstart");

	CLKADDR->clk_intrreg = CLK_INT_HSEC;	/* set 1/100 sec clock intr */
	set_clk_mode(IR_ENA_CLK5, 0);		/* turn on level 5 clock intr */
}

/*
 * Set and/or clear the desired clock bits in the interrupt
 * register.  We have to be extremely careful that we do it
 * in such a manner that we don't get ourselves lost.
 */
set_clk_mode(on, off)
	u_char on, off;
{
	register u_char interreg, dummy;

	/*
	 * make sure that we are only playing w/ 
	 * clock interrupt register bits
	 */
	on &= (IR_ENA_CLK7 | IR_ENA_CLK5);
	off &= (IR_ENA_CLK7 | IR_ENA_CLK5);

	/*
	 * Get a copy of current interrupt register,
	 * turning off any undesired bits (aka `off')
	 */
	interreg = *INTERREG & ~(off | IR_ENA_INT);
	*INTERREG &= ~IR_ENA_INT;

	/*
	 * Next we turns off the CLK5 and CLK7 bits to clear
	 * the flip-flops, then we disable clock interrupts.
	 * Now we can read the clock's interrupt register
	 * to clear any pending signals there.
	 */
	*INTERREG &= ~(IR_ENA_CLK7 | IR_ENA_CLK5);
	CLKADDR->clk_cmd = (CLK_CMD_NORMAL & ~CLK_CMD_INTRENA);
	dummy = CLKADDR->clk_intrreg;			/* clear clock */
#ifdef lint
	dummy = dummy;
#endif

	/*
	 * Now we set all the desired bits
	 * in the interrupt register, then
	 * we turn the clock back on and
	 * finally we can enable all interrupts.
	 */
	*INTERREG |= (interreg | on);			/* enable flip-flops */
	CLKADDR->clk_cmd = CLK_CMD_NORMAL;		/* enable clock intr */
	*INTERREG |= IR_ENA_INT;			/* enable interrupts */
}

#define ABS(x)	((x) < 0? -(x) : (x))

/*
 * Initialize the system time, based on the time base which is, e.g.
 * from a filesystem.
 */
clkinit(base)
	time_t base;
{
	register unsigned todr;
	register long deltat;
	int ticks;
	int s;

	if (base < (85 - YRREF) * SECYR) {	/* ~1985 */
		printf("WARNING: preposterous time in file system");
		goto check;
	}
	s = splclock();
	time = todget(&ticks);
	(void) splx(s);
	if (time < SECYR) {
		time = base;
		printf("WARNING: TOD clock not initialized");
		clkset();
		goto check;
	}
	deltat = time - base;
	if (deltat < 0)
		deltat = -deltat;
	if (deltat < 2*SECDAY)
		return;
	printf("WARNING: clock %s %d days",
	    time < base ? "lost" : "gained", deltat / SECDAY);
check:
	printf(" -- CHECK AND RESET THE DATE!\n");
}

/*
 * Dummy routine for version 9 compatability
 */
clkwrap()
{
	return(0);
}

clktrim()
{
	int ticks, sec;
	int s = splclock();

	/*
	 * Sync the software clock with the hardware clock
	 */
	sec = todget(&ticks);
	lbolt += hz * (sec - time) + (ticks - lbolt);
	(void) splx(s);
}

/*
 * Reset the TODR based on the time value; used when the TODR
 * has a preposterous value and also when the time is reset
 * by the settimeofday system call.  We call tod_set at splclock()
 * to avoid synctodr() from running and getting confused.
 */
clkset()
{
	int s;

	s = splclock();
	todset();
	(void) splx(s);
}

/*
 * For Sun-3, we use the Intersil ICM7170 for both the
 * real time clock and the time-of-day device.
 */

static u_int monthsec[12] = {
	31 * SECDAY,	/* Jan */
	28 * SECDAY,	/* Feb */
	31 * SECDAY,	/* Mar */
	30 * SECDAY,	/* Apr */
	31 * SECDAY,	/* May */
	30 * SECDAY,	/* Jun */
	31 * SECDAY,	/* Jul */
	31 * SECDAY,	/* Aug */
	30 * SECDAY,	/* Sep */
	31 * SECDAY,	/* Oct */
	30 * SECDAY,	/* Nov */
	31 * SECDAY	/* Dec */
};

#define	MONTHSEC(mon, yr)	\
	(((((yr) % 4) == 0) && ((mon) == 2))? 29*SECDAY : monthsec[(mon) - 1])

/*
 * Set the TOD based on the argument value; used when the TOD
 * has a preposterous value and also when the time is reset
 * by the settimeofday system call.
 */
todset()
{
	register int t = time;
	u_short hsec, sec, min, hour, day, mon, weekday, year;

	/*
	 * Figure out the (adjusted) year
	 */
	for (year = (YRREF - YRBASE); t > SECYEAR(year); year++)
		t -= SECYEAR(year);

	/*
	 * Figure out what month this is by subtracting off
	 * time per month, adjust for leap year if appropriate.
	 */
	for (mon = 1; t >= 0; mon++)
		t -= MONTHSEC(mon, year);

	t += MONTHSEC(--mon, year);	/* back off one month */

	sec = t % 60;			/* seconds */
	t /= 60;
	min = t % 60;			/* minutes */
	t /= 60;
	hour = t % 24;			/* hours (24 hour format) */
	day = t / 24;			/* day of the month */
	day++;				/* adjust to start at 1 */
	weekday = day % 7;		/* not right, but it doesn't matter */

	hsec = 0;

	CLKADDR->clk_cmd = (CLK_CMD_NORMAL & ~CLK_CMD_RUN);
	CLKADDR->clk_weekday = weekday;
	CLKADDR->clk_year = year;
	CLKADDR->clk_mon = mon;
	CLKADDR->clk_day = day;
	CLKADDR->clk_hour = hour;
	CLKADDR->clk_min = min;
	CLKADDR->clk_sec = sec;
	CLKADDR->clk_hsec = hsec;
	CLKADDR->clk_cmd = CLK_CMD_NORMAL;
}

/*
 * Read the current time from the clock chip and convert to UNIX form.
 * Assumes that the year in the counter chip is valid.
 */
todget(ticks)
int *ticks;
{
	u_char now[CLK_WEEKDAY];
	register int i;
	register u_char *cp = (u_char *)CLKADDR;
	register u_int t = 0;
	u_short year;

	for (i = CLK_HSEC; i < CLK_WEEKDAY; i++)	/* read counters */
		now[i] = *cp++;

	/*
	 * Add the number of seconds for each year onto our time t.
	 * We start at YRBASE, and count up to the year value given
	 * by the chip.  If the year is greater/equal to the difference
	 * between YRREF and YRBASE, then that time is added into
	 * the Unix time value we are calculating.
	 */
	for (year = 0; year < now[CLK_YEAR]; year++)
		if (year >= (YRREF - YRBASE))
			t += SECYEAR(year);

	/*
	 * Now add in the seconds for each month that has gone
	 * by this year, adjusting for leap year if appropriate.
	 */
	for (i = 1; i < now[CLK_MON]; i++)
		t += MONTHSEC(i, year);

	t += (now[CLK_DAY] - 1) * SECDAY;
	t += now[CLK_HOUR] * (60*60);
	t += now[CLK_MIN] * 60;
	t += now[CLK_SEC];

	*ticks = now[CLK_HSEC] * hz / 100;
	return (t);
}