* 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. * 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/param.h>
#include <sys/time.h> #include <sys/time.h>
#include <sys/kernel.h> #include <sys/kernel.h>
#include <sys/systm.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> #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 divisor;
static int clockinitted;
static int rtc_attached;
static u_char rtc_magic[8] = {
0xc5, 0x3a, 0xa3, 0x5c, 0xc5, 0x3a, 0xa3, 0x5c
};
void static long clk_get_secs __P((void));
startrtclock() 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; divisor = ICU_CLK_HZ / hz;
/* Write the timer values to the ICU. */ /* Disable clock interrupt for now. */
ICUW(HCSV) = divisor; ICUB(CICTL) = 0;
ICUW(HCCV) = divisor;
/* Select clock interrupt vector. */
ICUB(CIPTR) = ca->ca_irq << 4;
/* Establish interrupt vector */ /* Establish interrupt vector */
intr_establish(IR_CLK, (void (*)(void *))hardclock, NULL, "clock", intr_establish(IR_CLK, (void (*)(void *))hardclock, NULL, "clock",
IPL_CLOCK, IPL_CLOCK, FALLING_EDGE); 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 void
cpu_initclocks() cpu_initclocks()
{ {
/* Enable clock interrupt. */ /* Enable the clock interrupt. */
ICUB(CICTL) = 0x30; ICUB(CICTL) = 0x30;
} }
void void
setstatclockrate(int dummy) setstatclockrate(arg)
int arg;
{ {
printf ("setstatclockrate\n"); printf("setstatclockrate\n");
} }
void 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 static int
yeartoday(year) rtc_match(parent, cf, aux)
int year; 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 static void
hexdectodec(n) rtc_attach(parent, self, aux)
char n; struct device *parent;
struct device *self;
void *aux;
{ {
printf("\n");
return(((n >> 4) & 0x0F) * 10 + (n & 0x0F)); 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. * Initialize the time of day register, based on the time base
* from a filesystem. * which is, e.g. from a filesystem.
*/ */
void void inittodr(fs_time)
inittodr(base) time_t fs_time;
time_t base;
{ {
/* long diff, clk_time;
* We ignore the suggested time for now and go for the RTC long long_ago = (5 * SECYR);
* clock time stored in the CMOS RAM. int clk_bad = 0;
*/
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;
timeset = 1; clockinitted = 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;
/* /*
* Check to see if it was really the rtc * Sanity check time from file system.
* by checking for bad date info. * 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) { if (fs_time < long_ago) {
printf("inittodr: No clock found\n"); /*
time.tv_sec = base; * If fs_time is zero, assume filesystem time is just
return; * 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; clk_time = clk_get_secs();
n += (dom - 1) * 3600 * 24;
if (yeartoday(yr) == 366) /* Sanity check time from clock. */
month[1] = 29; if (clk_time < long_ago) {
for (i = mon - 2; i >= 0; i--) printf("WARNING: bad date in battery clock");
days += month[i]; clk_bad = 1;
month[1] = 28; clk_time = fs_time;
for (i = 70; i < yr; i++) } else {
days += yeartoday(i); /* Does the clock time jive with the file system? */
n += days * 3600 * 24; diff = clk_time - fs_time;
if (diff < 0)
n += rtc_offset * 60; diff = -diff;
s = splclock(); if (diff >= (SECDAY*2)) {
time.tv_sec = n; printf("WARNING: clock %s %d days",
time.tv_usec = csec * 10000; (clk_time < fs_time) ? "lost" : "gained",
splx(s); (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 void resettodr()
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 * We might have been called by boot() due to a crash early
* on. Don't reset the clock chip in this case. * 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; 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(); rw_rtc(&dt, 1);
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);
} }