* Make the rtc and the clock real devices. Initialize the clock parts

of the ICU in clock_attach.
* Pull over (and reformat) rtc_rw from mem.c.
* Convert the rtc driver to use /sys/dev/clock_subr.c (partially by
  stealing code from the sun3 port).
This commit is contained in:
matthias 1997-03-20 12:00:33 +00:00
parent fa30cfc066
commit b677f20e5d
1 changed files with 332 additions and 136 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: clock.c,v 1.18 1997/01/15 01:28:56 perry Exp $ */
/* $NetBSD: clock.c,v 1.19 1997/03/20 12:00:33 matthias Exp $ */
/*-
* Copyright (c) 1990 The Regents of the University of California.
@ -39,48 +39,125 @@
*
*/
/*
* Primitive clock interrupt routines.
*
* Improved by Phil Budne ... 10/17/94.
* Pulled over code from i386/isa/clock.c (Matthias Pfaller 12/03/94).
* Phil Budne's better microtime added 09/02/96
*/
#include <sys/param.h>
#include <sys/time.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/conf.h>
#include <dev/clock_subr.h>
#include <machine/autoconf.h>
#include <machine/icu.h>
#define ROM_ORIGIN 0xFFF00000 /* Mapped origin! */
static volatile u_char * const rom = (u_char *)ROM_ORIGIN;
static int divisor;
static int clockinitted;
static int rtc_attached;
static u_char rtc_magic[8] = {
0xc5, 0x3a, 0xa3, 0x5c, 0xc5, 0x3a, 0xa3, 0x5c
};
void
startrtclock()
static long clk_get_secs __P((void));
static void clk_set_secs __P((long));
static u_char bcd2bin __P((u_char));
static u_char bin2bcd __P((u_char));
static void rw_rtc __P((struct clock_ymdhms *, int));
static void write_rtc __P((u_char *));
static int clock_match __P((struct device *, struct cfdata *, void *args));
static void clock_attach __P((struct device *, struct device *, void *));
struct cfattach clock_ca = {
sizeof(struct device), clock_match, clock_attach
};
struct cfdriver clock_cd = {
NULL, "clock", DV_DULL
};
static int rtc_match __P((struct device *, struct cfdata *, void *args));
static void rtc_attach __P((struct device *, struct device *, void *));
struct cfattach rtc_ca = {
sizeof(struct device), rtc_match, rtc_attach
};
struct cfdriver rtc_cd = {
NULL, "rtc", DV_DULL
};
static int
clock_match(parent, cf, aux)
struct device *parent;
struct cfdata *cf;
void *aux;
{
struct confargs *ca = aux;
/* This driver only supports one unit. */
if (cf->cf_unit != 0)
return(0);
if ((ca->ca_addr != -1 && ca->ca_addr != ICU_ADR) ||
(ca->ca_irq != -1 && ca->ca_irq != IR_CLK))
return(0);
ca->ca_addr = ICU_ADR;
ca->ca_irq = IR_CLK;
return(1);
}
static void
clock_attach(parent, self, aux)
struct device *parent;
struct device *self;
void *aux;
{
struct confargs *ca = aux;
printf("\n");
divisor = ICU_CLK_HZ / hz;
/* Write the timer values to the ICU. */
ICUW(HCSV) = divisor;
ICUW(HCCV) = divisor;
/* Disable clock interrupt for now. */
ICUB(CICTL) = 0;
/* Select clock interrupt vector. */
ICUB(CIPTR) = ca->ca_irq << 4;
/* Establish interrupt vector */
intr_establish(IR_CLK, (void (*)(void *))hardclock, NULL, "clock",
IPL_CLOCK, IPL_CLOCK, FALLING_EDGE);
/* No clock output. */
ICUB(OCASN) = 0;
/*
* Two clocks, prescale, output zero detect of L-counter,
* run both clocks.
*/
ICUB(CCTL) = 0x1c;
/* Write the timer values to the ICU. */
ICUW(HCSV) = divisor;
ICUW(HCCV) = divisor;
}
void
cpu_initclocks()
{
/* Enable clock interrupt. */
/* Enable the clock interrupt. */
ICUB(CICTL) = 0x30;
}
void
setstatclockrate(int dummy)
setstatclockrate(arg)
int arg;
{
printf ("setstatclockrate\n");
printf("setstatclockrate\n");
}
void
@ -111,154 +188,273 @@ microtime(tvp)
}
}
static int month[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
/*
* 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.
*/
static int
yeartoday(year)
int year;
rtc_match(parent, cf, aux)
struct device *parent;
struct cfdata *cf;
void *aux;
{
struct confargs *ca = aux;
int rom_val, rom_cnt, i;
return((year % 4) ? 365 : 366);
/* This driver only supports one unit. */
if (cf->cf_unit != 0)
return(0);
(void) rom[4]; /* Synchronize the comparison reg. */
rom_val = rom[4];
write_rtc(rtc_magic);
for (i = rom_cnt = 0; i < 64; i++)
if (rom[4] == rom_val)
rom_cnt++;
if (rom_cnt == 64)
return(0);
if ((ca->ca_addr != -1 && ca->ca_addr != ROM_ORIGIN) ||
ca->ca_irq != -1)
return(0);
ca->ca_addr = ROM_ORIGIN;
ca->ca_irq = -1;
return(1);
}
int
hexdectodec(n)
char n;
static void
rtc_attach(parent, self, aux)
struct device *parent;
struct device *self;
void *aux;
{
return(((n >> 4) & 0x0F) * 10 + (n & 0x0F));
printf("\n");
rtc_attached = 1;
}
char
dectohexdec(n)
int n;
{
return((char)(((n / 10) << 4) & 0xF0) | ((n % 10) & 0x0F));
}
static int timeset;
struct rtc_st {
unsigned char rtc_csec;
unsigned char rtc_sec;
unsigned char rtc_min;
unsigned char rtc_hr;
unsigned char rtc_dow;
unsigned char rtc_dom;
unsigned char rtc_mon;
unsigned char rtc_yr;
};
/*
* 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;
{
/*
* We ignore the suggested time for now and go for the RTC
* clock time stored in the CMOS RAM.
*/
struct rtc_st rtclk;
time_t n;
int csec, sec, min, hr, dom, mon, yr;
int i, days = 0;
int s;
extern int have_rtc;
long diff, clk_time;
long long_ago = (5 * SECYR);
int clk_bad = 0;
timeset = 1;
if (!have_rtc) {
time.tv_sec = base;
return;
}
rw_rtc((unsigned char *)&rtclk, 0);
csec = hexdectodec(rtclk.rtc_csec);
sec = hexdectodec(rtclk.rtc_sec);
min = hexdectodec(rtclk.rtc_min);
hr = hexdectodec(rtclk.rtc_hr);
dom = hexdectodec(rtclk.rtc_dom);
mon = hexdectodec(rtclk.rtc_mon);
yr = hexdectodec(rtclk.rtc_yr);
yr = (yr < 70) ? yr + 100 : yr;
clockinitted = 1;
/*
* Check to see if it was really the rtc
* by checking for bad date info.
* Sanity check time from file system.
* If it is zero,assume filesystem time is just unknown
* instead of preposterous. Don't bark.
*/
if (sec > 59 || min > 59 || hr > 23 || dom > 31 || mon > 12) {
printf("inittodr: No clock found\n");
time.tv_sec = base;
return;
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;
}
n = sec + 60 * min + 3600 * hr;
n += (dom - 1) * 3600 * 24;
clk_time = clk_get_secs();
if (yeartoday(yr) == 366)
month[1] = 29;
for (i = mon - 2; i >= 0; i--)
days += month[i];
month[1] = 28;
for (i = 70; i < yr; i++)
days += yeartoday(i);
n += days * 3600 * 24;
n += rtc_offset * 60;
s = splclock();
time.tv_sec = n;
time.tv_usec = csec * 10000;
splx(s);
/* 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",
(int) (diff / SECDAY));
clk_bad = 1;
}
}
if (clk_bad)
printf(" -- CHECK AND RESET THE DATE!\n");
time.tv_sec = clk_time;
}
/*
* Reset the clock.
* Resettodr restores the time of day hardware after a time change.
*/
void
resettodr()
void resettodr()
{
struct rtc_st rtclk;
time_t n;
int diff, i, j;
int s;
/*
* We might have been called by boot() due to a crash early
* on. Don't reset the clock chip in this case.
*/
if (!timeset)
if (clockinitted == 0)
return;
clk_set_secs(time.tv_sec);
}
/*
* Now routines to get and set clock as POSIX time.
* Our clock keeps "years since 1/1/1900".
*/
#define CLOCK_BASE_YEAR 1900
/*
* Do the actual reading and writing of the rtc. We have to read
* and write the entire contents at a time.
* rw = 0 => read,
* rw = 1 => write.
*/
static u_char
bcd2bin(n)
u_char n;
{
return(((n >> 4) & 0x0f) * 10 + (n & 0x0f));
}
static u_char
bin2bcd(n)
u_char n;
{
return((char)(((n / 10) << 4) & 0xf0) | ((n % 10) & 0x0f));
}
static void
write_rtc(p)
u_char *p;
{
u_char *q;
int i;
for (q = p + 8; p < q; p++) {
for (i = 0; i < 8; i++)
(void) rom[(*p >> i) & 0x01];
}
}
static void
rw_rtc(dt, rw)
struct clock_ymdhms *dt;
int rw;
{
struct {
u_char rtc_csec;
u_char rtc_sec;
u_char rtc_min;
u_char rtc_hour;
u_char rtc_wday;
u_char rtc_day;
u_char rtc_mon;
u_char rtc_year;
} rtcdt[1];
u_char *p;
int i;
/*
* Read or write to the real time chip. Address line A0 functions as
* data input, A2 is used as the /write signal. Accesses to the RTC
* are always done to one of the addresses (unmapped):
*
* 0x10000000 - write a '0' bit
* 0x10000001 - write a '1' bit
* 0x10000004 - read a bit
*
* Data is output from the RTC using D0. To read or write time
* information, the chip has to be activated first, to distinguish
* clock accesses from normal ROM reads. This is done by writing,
* bit by bit, a magic pattern to the chip. Before that, a dummy read
* assures that the chip's pattern comparison register pointer is
* reset. The RTC register file is always read or written wholly,
* even if we are only interested in a part of it.
*/
/* Activate the real time chip */
(void) rom[4]; /* Synchronize the comparison reg. */
write_rtc(rtc_magic);
if (rw == 0) {
/* Read the time from the RTC. */
for (p = (u_char *)rtcdt; p < (u_char *)(rtcdt + 1); p++) {
for (i = 0; i < 8; i++) {
*p >>= 1;
*p |= ((rom[4] & 0x01) ? 0x80 : 0x00);
}
}
dt->dt_sec = bcd2bin(rtcdt->rtc_sec);
dt->dt_min = bcd2bin(rtcdt->rtc_min);
dt->dt_hour = bcd2bin(rtcdt->rtc_hour);
dt->dt_day = bcd2bin(rtcdt->rtc_day);
dt->dt_mon = bcd2bin(rtcdt->rtc_mon);
dt->dt_year = bcd2bin(rtcdt->rtc_year);
dt->dt_wday = bcd2bin(rtcdt->rtc_wday);
} else {
/* Write the time to the RTC */
rtcdt->rtc_sec = bin2bcd(dt->dt_sec);
rtcdt->rtc_min = bin2bcd(dt->dt_min);
rtcdt->rtc_hour = bin2bcd(dt->dt_hour);
rtcdt->rtc_day = bin2bcd(dt->dt_day);
rtcdt->rtc_mon = bin2bcd(dt->dt_mon);
rtcdt->rtc_year = bin2bcd(dt->dt_year);
rtcdt->rtc_wday = bin2bcd(dt->dt_wday);
write_rtc((u_char *)rtcdt);
}
}
static long
clk_get_secs()
{
struct clock_ymdhms dt;
long secs;
if (rtc_attached == 0)
return(0);
rw_rtc(&dt, 0);
if ((dt.dt_sec > 59) ||
(dt.dt_min > 59) ||
(dt.dt_hour > 23) ||
(dt.dt_day > 31) ||
(dt.dt_mon > 12) ||
(dt.dt_year > 99))
return(0);
if (dt.dt_year < 70)
dt.dt_year += 100;
dt.dt_year += CLOCK_BASE_YEAR;
secs = clock_ymdhms_to_secs(&dt);
return(secs);
}
static void
clk_set_secs(secs)
long secs;
{
struct clock_ymdhms dt;
if (rtc_attached == 0)
return;
diff = rtc_offset * 60;
clock_secs_to_ymdhms(secs, &dt);
dt.dt_year -= CLOCK_BASE_YEAR;
if (dt.dt_year >= 100)
dt.dt_year -= 100;
s = splclock();
n = (time.tv_sec - diff) % (3600 * 24); /* hrs+mins+secs */
rtclk.rtc_csec = dectohexdec(time.tv_usec / 10000);
rtclk.rtc_sec = dectohexdec(n%60);
n /= 60;
rtclk.rtc_min = dectohexdec(n%60);
rtclk.rtc_hr = dectohexdec(n/60);
n = (time.tv_sec - diff) / (3600 * 24); /* days */
splx(s);
rtclk.rtc_dow = (n + 4) % 7; /* 1/1/70 is Thursday */
for (j = 1970, i = yeartoday(j); n >= i; j++, i = yeartoday(j))
n -= i;
rtclk.rtc_yr = dectohexdec(j - 1900);
if (i == 366)
month[1] = 29;
for (i = 0; n >= month[i]; i++)
n -= month[i];
month[1] = 28;
rtclk.rtc_mon = dectohexdec(++i);
rtclk.rtc_dom = dectohexdec(++n);
rw_rtc((unsigned char *)&rtclk, 1);
rw_rtc(&dt, 1);
}