Fix routines to read/write the stupid intersil clock.

This commit is contained in:
gwr 1994-09-26 17:25:21 +00:00
parent 3e676d4221
commit 2905514717
2 changed files with 338 additions and 213 deletions

View File

@ -1,6 +1,13 @@
/*
* Copyright (c) 1994 Gordon W. Ross
* Copyright (c) 1993 Adam Glass
* All rights reserved.
* Copyright (c) 1988 University of Utah.
* Copyright (c) 1982, 1990, 1993
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* the Systems Programming Group of the University of Utah Computer
* Science Department.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@ -12,11 +19,13 @@
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Adam Glass.
* 4. The name of the Author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY Adam Glass ``AS IS'' AND
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
@ -28,15 +37,15 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $Id: clock.c,v 1.17 1994/09/20 16:52:22 gwr Exp $
* from: Utah $Hdr: clock.c 1.18 91/01/21$
*
* from: @(#)clock.c 8.2 (Berkeley) 1/12/94
* $Id: clock.c,v 1.18 1994/09/26 17:25:23 gwr Exp $
*/
/*
* machine-dependent clock routines; intersil7170
* by Adam Glass
*
* [this code suffered terribly when we were pursuing the fast clock interrupt
* problem. in particular, the level switching code is not legitimate]
*
* Machine-dependent clock routines for the Intersil 7170:
* Original by Adam Glass; partially rewritten by Gordon Ross.
*/
#include <sys/param.h>
@ -75,18 +84,6 @@ volatile char *clock_va;
#define intersil_clear() intersil_clock->clk_intr_reg
#define SECS_HOUR (60*60)
#define SECS_DAY (SECS_HOUR*24)
#define SECS_PER_YEAR (SECS_DAY*365)
#define SECS_PER_LEAP (SECS_DAY*366)
#define SECS_PER_MONTH(month, year) \
((month == 2) && INTERSIL_LEAP_YEAR(year) \
? 29*SECS_DAY : month_days[month-1]*SECS_DAY)
#define SECS_YEAR(year) \
(INTERSIL_LEAP_YEAR(year) ? SECS_PER_LEAP : SECS_PER_YEAR)
struct clock_softc {
struct device clock_dev;
caddr_t clock_va;
@ -95,10 +92,6 @@ struct clock_softc {
struct clock_softc *intersil_softc = NULL;
static int month_days[12] = {
31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};
void clockattach __P((struct device *, struct device *, void *));
int clockmatch __P((struct device *, struct cfdata *, void *args));
@ -106,6 +99,7 @@ struct cfdriver clockcd =
{ NULL, "clock", always_match, clockattach, DV_DULL,
sizeof(struct clock_softc), 0};
/* Called very earyl by internal_configure. */
void clock_init()
{
clock_va = obio_find_mapping(OBIO_CLOCK, OBIO_CLOCK_SIZE);
@ -179,12 +173,9 @@ set_clk_mode(on, off, enable)
* to clear any pending signals there.
*/
*interrupt_reg &= ~(IREG_CLOCK_ENAB_7 | IREG_CLOCK_ENAB_5);
intersil_clock->clk_cmd_reg = intersil_command(INTERSIL_CMD_RUN,
INTERSIL_CMD_IDISABLE);
intersil_clock->clk_cmd_reg =
intersil_command(INTERSIL_CMD_RUN, INTERSIL_CMD_IDISABLE);
dummy = intersil_clock->clk_intr_reg; /* clear clock */
#ifdef lint
dummy = dummy;
#endif
/*
* Now we set all the desired bits
@ -193,16 +184,17 @@ set_clk_mode(on, off, enable)
* finally we can enable all interrupts.
*/
*interrupt_reg |= (interreg | on); /* enable flip-flops */
if (enable)
intersil_clock->clk_cmd_reg =
intersil_command(INTERSIL_CMD_RUN,
INTERSIL_CMD_IENABLE);
intersil_clock->clk_cmd_reg =
intersil_command(INTERSIL_CMD_RUN, INTERSIL_CMD_IENABLE);
*interrupt_reg |= IREG_ALL_ENAB; /* enable interrupts */
}
/*
* Set up the real-time clock. Leave stathz 0 since there is no secondary
* clock available.
* Set up the real-time clock. Leave stathz 0 since there is no
* secondary clock available.
*/
void
cpu_initclocks(void)
@ -217,10 +209,10 @@ cpu_initclocks(void)
/* make sure irq5/7 stuff is resolved :) */
dummy = intersil_clear();
dummy++;
intersil_clock->clk_intr_reg = INTERSIL_INTER_CSECONDS;
intersil_clock->clk_cmd_reg = intersil_command(INTERSIL_CMD_RUN,
INTERSIL_CMD_IENABLE);
intersil_clock->clk_cmd_reg =
intersil_command(INTERSIL_CMD_RUN, INTERSIL_CMD_IENABLE);
}
/*
@ -234,16 +226,20 @@ setstatclockrate(newhz)
/* nothing */
}
/*
* Startrtclock restarts the real-time clock, which provides
* hardclock interrupts to kern_clock.c.
*/
void startrtclock()
{
char dummy;
if (!intersil_softc)
panic("clock: not initialized");
panic("clock: not initialized");
intersil_clock->clk_intr_reg = INTERSIL_INTER_CSECONDS;
intersil_clock->clk_cmd_reg = intersil_command(INTERSIL_CMD_RUN,
INTERSIL_CMD_IDISABLE);
intersil_clock->clk_cmd_reg =
intersil_command(INTERSIL_CMD_RUN, INTERSIL_CMD_IDISABLE);
dummy = intersil_clear();
}
@ -251,12 +247,11 @@ void enablertclock()
{
unsigned char dummy;
/* make sure irq5/7 stuff is resolved :) */
dummy = intersil_clear();
dummy++;
intersil_clock->clk_intr_reg = INTERSIL_INTER_CSECONDS;
intersil_clock->clk_cmd_reg = intersil_command(INTERSIL_CMD_RUN,
INTERSIL_CMD_IENABLE);
intersil_clock->clk_cmd_reg =
intersil_command(INTERSIL_CMD_RUN, INTERSIL_CMD_IENABLE);
}
int clock_count = 0;
@ -266,6 +261,7 @@ void clock_intr(frame)
{
static unsigned char led_pattern = 0xFE;
/* XXX - Move this LED frobbing to the idle loop? */
clock_count++;
if ((clock_count & 7) == 0) {
led_pattern = (led_pattern << 1) | 1;
@ -278,176 +274,312 @@ void clock_intr(frame)
/*
* Machine-dependent clock routines.
* Return the best possible estimate of the time in the timeval
* to which tvp points. We do this by returning the current time
* plus the amount of time since the last clock interrupt.
*
* Startrtclock restarts the real-time clock, which provides
* hardclock interrupts to kern_clock.c.
* Check that this time is no less than any previously-reported time,
* which could happen around the time of a clock adjustment. Just for
* fun, we guarantee that the time will be greater than the value
* obtained by a previous call.
*/
microtime(tvp)
register struct timeval *tvp;
{
int s = splhigh();
static struct timeval lasttime;
*tvp = time;
tvp->tv_usec;
while (tvp->tv_usec > 1000000) {
tvp->tv_sec++;
tvp->tv_usec -= 1000000;
}
if (tvp->tv_sec == lasttime.tv_sec &&
tvp->tv_usec <= lasttime.tv_usec &&
(tvp->tv_usec = lasttime.tv_usec + 1) > 1000000) {
tvp->tv_sec++;
tvp->tv_usec -= 1000000;
}
lasttime = *tvp;
splx(s);
}
/*
* Machine-dependent clock routines.
*
* Inittodr initializes the time of day hardware which provides
* date functions.
*
* Resettodr restores the time of day hardware after a time change.
*
* also microtime support
*/
#define SECDAY 86400L
#define SECYR (SECDAY * 365)
static long clk_get_secs(void);
static void clk_set_secs(long);
void intersil_counter_state(map)
struct intersil_map *map;
{
map->csecs = intersil_clock->counters.csecs;
map->hours = intersil_clock->counters.hours;
map->minutes = intersil_clock->counters.minutes;
map->seconds = intersil_clock->counters.seconds;
map->month = intersil_clock->counters.month;
map->date = intersil_clock->counters.date;
map->year = intersil_clock->counters.year;
map->day = intersil_clock->counters.day;
}
struct timeval intersil_to_timeval()
{
struct intersil_map now_state;
struct timeval now;
int i;
intersil_counter_state(&now_state);
now.tv_sec = 0;
now.tv_usec = 0;
#define range_check_high(field, high) \
now_state.field > high
#define range_check(field, low, high) \
now_state.field < low || now_state.field > high
if (range_check_high(csecs, 99) ||
range_check_high(hours, 23) ||
range_check_high(minutes, 59) ||
range_check_high(seconds, 59) ||
range_check(month, 1, 12) ||
range_check(date, 1, 32) ||
range_check_high(year, 99) ||
range_check_high(day, 6)) return now;
if ((INTERSIL_UNIX_BASE - INTERSIL_YEAR_BASE) > now_state.year)
return now;
for (i = INTERSIL_UNIX_BASE-INTERSIL_YEAR_BASE;
i < now_state.year; i++)
if (INTERSIL_LEAP_YEAR(INTERSIL_YEAR_BASE+now_state.year))
now.tv_sec += SECS_PER_LEAP;
else now.tv_sec += SECS_PER_YEAR;
for (i = 1; i < now_state.month; i++)
now.tv_sec += SECS_PER_MONTH(i, INTERSIL_YEAR_BASE+now_state.year);
now.tv_sec += SECS_DAY*(now_state.date-1);
now.tv_sec += SECS_HOUR * now_state.hours;
now.tv_sec += 60 * now_state.minutes;
now.tv_sec += 60 * now_state.seconds;
return now;
}
/*
* Initialize the time of day register, based on the time base which is, e.g.
* from a filesystem.
* Initialize the time of day register, based on the time base
* which is, e.g. from a filesystem.
*/
void inittodr(base)
time_t base;
void inittodr(fs_time)
time_t fs_time;
{
struct timeval clock_time;
long diff_time;
void resettodr();
long diff, clk_time;
long long_ago = (5 * SECYR);
int clk_bad = 0;
clock_time = intersil_to_timeval();
/*
* Sanity check time from file system.
* If it is zero,assume filesystem time is just unknown
* instead of preposterous. Don't bark.
*/
if (fs_time < long_ago) {
/*
* If fs_time is zero, assume filesystem time is just
* unknown instead of preposterous. Don't bark.
*/
if (fs_time != 0)
printf("WARNING: preposterous time in file system\n");
/* 1991/07/01 12:00:00 */
fs_time = 21*SECYR + 186*SECDAY + SECDAY/2;
}
if (!base) goto set_time;
if (!clock_time.tv_sec && (base <= 0)) goto set_time;
if (clock_time.tv_sec < base) {
printf("WARNING: real-time clock reports a time earlier than last\n");
printf(" write to root filesystem. Trusting filesystem...\n");
time.tv_sec = base;
resettodr();
return;
}
diff_time = clock_time.tv_sec - base;
if (diff_time < 0) diff_time = - diff_time;
if (diff_time >= (SECS_DAY*2))
printf("WARNING: clock %s %d days\n",
(clock_time.tv_sec <base ? "lost" : "gained"),
diff_time % SECS_DAY);
clk_time = clk_get_secs();
set_time:
time = clock_time;
/* Sanity check time from clock. */
if (clk_time < long_ago) {
printf("WARNING: bad date in battery clock");
clk_bad = 1;
clk_time = fs_time;
} else {
/* Does the clock time jive with the file system? */
diff = clk_time - fs_time;
if (diff < 0)
diff = -diff;
if (diff >= (SECDAY*2)) {
printf("WARNING: clock %s %d days",
(clk_time < fs_time) ? "lost" : "gained",
diff / SECDAY);
clk_bad = 1;
}
}
if (clk_bad)
printf(" -- CHECK AND RESET THE DATE!\n");
time.tv_sec = clk_time;
}
void timeval_to_intersil(now, map)
struct timeval now;
struct intersil_map *map;
{
for (map->year = INTERSIL_UNIX_BASE-INTERSIL_YEAR_BASE;
now.tv_sec > SECS_YEAR(map->year);
map->year++)
now.tv_sec -= SECS_YEAR(map->year);
for (map->month = 1; now.tv_sec >=0; map->month++)
now.tv_sec -= SECS_PER_MONTH(map->month, INTERSIL_YEAR_BASE+map->year);
map->month--;
now.tv_sec -= SECS_PER_MONTH(map->month, INTERSIL_YEAR_BASE+map->year);
map->date = now.tv_sec % SECS_DAY ;
now.tv_sec /= SECS_DAY;
map->minutes = now.tv_sec %60;
now.tv_sec /= 60;
map->seconds = now.tv_sec %60;
now.tv_sec /= 60;
map->day = map->date %7;
map->csecs = now.tv_usec / 10000;
}
/*
* Resettodr restores the time of day hardware after a time change.
*/
void resettodr()
{
struct intersil_map hdw_format;
timeval_to_intersil(time, &hdw_format);
intersil_clock->clk_cmd_reg = intersil_command(INTERSIL_CMD_STOP,
INTERSIL_CMD_IDISABLE);
intersil_clock->counters.csecs = hdw_format.csecs ;
intersil_clock->counters.hours = hdw_format.hours ;
intersil_clock->counters.minutes = hdw_format.minutes ;
intersil_clock->counters.seconds = hdw_format.seconds ;
intersil_clock->counters.month = hdw_format.month ;
intersil_clock->counters.date = hdw_format.date ;
intersil_clock->counters.year = hdw_format.year ;
intersil_clock->counters.day = hdw_format.day ;
intersil_clock->clk_cmd_reg = intersil_command(INTERSIL_CMD_RUN,
INTERSIL_CMD_IENABLE);
clk_set_secs(time.tv_sec);
}
void microtime(tvp)
register struct timeval *tvp;
/*
* Machine dependent base year:
* Note: must be < 1970
*/
#define CLOCK_BASE_YEAR 1968
/*
* Routine to copy state into and out of the clock.
* The clock registers have to be read or written
* in sequential order (or so it appears). -gwr
*/
static void clk_get_dt(struct date_time *dt)
{
int s = splhigh();
static struct timeval lasttime;
int s;
register volatile char *src, *dst;
/* as yet...... this makes little sense*/
*tvp = time;
if (tvp->tv_sec == lasttime.tv_sec &&
tvp->tv_usec <= lasttime.tv_usec &&
(tvp->tv_usec = lasttime.tv_usec + 1) > 1000000) {
tvp->tv_sec++;
tvp->tv_usec -= 1000000;
}
lasttime = *tvp;
splx(s);
src = (char *) &intersil_clock->counters;
s = splhigh();
intersil_clock->clk_cmd_reg =
intersil_command(INTERSIL_CMD_STOP, INTERSIL_CMD_IENABLE);
dst = (char *) dt;
dt++; /* end marker */
do {
*dst++ = *src++;
} while (dst < (char*)dt);
intersil_clock->clk_cmd_reg =
intersil_command(INTERSIL_CMD_RUN, INTERSIL_CMD_IENABLE);
splx(s);
}
static void clk_set_dt(struct date_time *dt)
{
int s;
register volatile char *src, *dst;
dst = (char *) &intersil_clock->counters;
s = splhigh();
intersil_clock->clk_cmd_reg =
intersil_command(INTERSIL_CMD_STOP, INTERSIL_CMD_IENABLE);
src = (char *) dt;
dt++; /* end marker */
do {
*dst++ = *src++;
} while (src < (char *)dt);
intersil_clock->clk_cmd_reg =
intersil_command(INTERSIL_CMD_RUN, INTERSIL_CMD_IENABLE);
splx(s);
}
/*
* Generic routines to convert to or from a POSIX date
* (seconds since 1/1/1970) and yr/mo/day/hr/min/sec
*
* These are organized this way mostly to so the code
* can easily be tested in an independent user program.
* (These are derived from the hp300 code.)
*/
/* Traditional UNIX base year */
#define POSIX_BASE_YEAR 1970
#define FEBRUARY 2
#define leapyear(year) ((year) % 4 == 0)
#define days_in_year(a) (leapyear(a) ? 366 : 365)
#define days_in_month(a) (month_days[(a) - 1])
static int month_days[12] = {
31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};
void gmt_to_dt(long *tp, struct date_time *dt)
{
register int i;
register long days, secs;
days = *tp / SECDAY;
secs = *tp % SECDAY;
/* Hours, minutes, seconds are easy */
dt->dt_hour = secs / 3600;
secs = secs % 3600;
dt->dt_min = secs / 60;
secs = secs % 60;
dt->dt_sec = secs;
/* Day of week (Note: 1/1/1970 was a Thursday) */
dt->dt_dow = (days + 4) % 7;
/* Number of years in days */
i = POSIX_BASE_YEAR;
while (days >= days_in_year(i)) {
days -= days_in_year(i);
i++;
}
dt->dt_year = i - CLOCK_BASE_YEAR;
/* Number of months in days left */
if (leapyear(i))
days_in_month(FEBRUARY) = 29;
for (i = 1; days >= days_in_month(i); i++)
days -= days_in_month(i);
days_in_month(FEBRUARY) = 28;
dt->dt_month = i;
/* Days are what is left over (+1) from all that. */
dt->dt_day = days + 1;
}
void dt_to_gmt(struct date_time *dt, long *tp)
{
register int i;
register long tmp;
int year;
/*
* Hours are different for some reason. Makes no sense really.
*/
tmp = 0;
if (dt->dt_hour >= 24) goto out;
if (dt->dt_day > 31) goto out;
if (dt->dt_month > 12) goto out;
year = dt->dt_year + CLOCK_BASE_YEAR;
/*
* Compute days since start of time
* First from years, then from months.
*/
for (i = POSIX_BASE_YEAR; i < year; i++)
tmp += days_in_year(i);
if (leapyear(year) && dt->dt_month > FEBRUARY)
tmp++;
/* Months */
for (i = 1; i < dt->dt_month; i++)
tmp += days_in_month(i);
tmp += (dt->dt_day - 1);
/* Now do hours */
tmp = tmp * 24 + dt->dt_hour;
/* Now do minutes */
tmp = tmp * 60 + dt->dt_min;
/* Now do seconds */
tmp = tmp * 60 + dt->dt_sec;
out:
*tp = tmp;
}
/*
* Now routines to get and set clock as POSIX time.
*/
static long clk_get_secs()
{
struct date_time dt;
long gmt;
clk_get_dt(&dt);
dt_to_gmt(&dt, &gmt);
return (gmt);
}
static void clk_set_secs(long secs)
{
struct date_time dt;
long gmt;
gmt = secs;
gmt_to_dt(&gmt, &dt);
clk_set_dt(&dt);
}
#ifdef DEBUG
/* Call this from DDB or whatever... */
int clkdebug()
{
struct date_time dt;
long gmt;
long *lp;
bzero((char*)&dt, sizeof(dt));
clk_get_dt(&dt);
lp = (long*)&dt;
printf("clkdebug: dt=[%x,%x]\n", lp[0], lp[1]);
dt_to_gmt(&dt, &gmt);
printf("clkdebug: gmt=%x\n", gmt);
}
#endif

View File

@ -28,7 +28,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $Id: intersil7170.h,v 1.5 1994/09/20 16:52:26 gwr Exp $
* $Id: intersil7170.h,v 1.6 1994/09/26 17:25:21 gwr Exp $
*/
/*
@ -39,20 +39,20 @@
* document #301680-005, Dec 85
*/
struct intersil_map { /* from p. 7 of 10 */
unsigned char csecs;
unsigned char hours;
unsigned char minutes;
unsigned char seconds;
unsigned char month;
unsigned char date;
unsigned char year;
unsigned char day;
struct date_time { /* from p. 7 of 10 */
unsigned char dt_csec;
unsigned char dt_hour;
unsigned char dt_min;
unsigned char dt_sec;
unsigned char dt_month;
unsigned char dt_day;
unsigned char dt_year;
unsigned char dt_dow;
};
struct intersil7170 {
struct intersil_map counters;
struct intersil_map clk_ram; /* should be ok as both are word aligned */
struct date_time counters;
struct date_time clk_ram; /* should be ok as both are word aligned */
unsigned char clk_intr_reg;
unsigned char clk_cmd_reg;
};
@ -88,10 +88,3 @@ struct intersil7170 {
#define INTERSIL_INTER_BITS "\20\10PENDING\7DAYS\6HRS\5MIN\4SCDS\3DSEC\2CSEC\1ALARM"
/* useful info */
#define INTERSIL_YEAR_BASE 68
#define INTERSIL_UNIX_BASE 70
#define INTERSIL_LEAP_YEAR(x) !((x) % 4)