diff --git a/sys/arch/pc532/pc532/clock.c b/sys/arch/pc532/pc532/clock.c index 3834b68b591c..13e38b2d1cf2 100644 --- a/sys/arch/pc532/pc532/clock.c +++ b/sys/arch/pc532/pc532/clock.c @@ -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 #include #include #include +#include +#include +#include + +#include #include +#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); }