[BACK]Return to clock.c CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / src / sys / arch / pc532 / pc532

File: [cvs.NetBSD.org] / src / sys / arch / pc532 / pc532 / Attic / clock.c (download)

Revision 1.22, Mon Jan 12 19:55:44 1998 UTC (23 years, 1 month ago) by thorpej
Branch: MAIN
CVS Tags: wrstuden-devbsize-base, wrstuden-devbsize-19991221, wrstuden-devbsize, thorpej_scsipi_nbase, thorpej_scsipi_beforemerge, thorpej_scsipi_base, thorpej_scsipi, thorpej-mips-cache-base, thorpej-mips-cache, thorpej-devvp-base3, thorpej-devvp-base2, thorpej-devvp-base, thorpej-devvp, pre-chs-ubcperf, post-chs-ubcperf, newlock-base, newlock, netbsd-1-6-base, netbsd-1-6-RELEASE, netbsd-1-6-RC3, netbsd-1-6-RC2, netbsd-1-6-RC1, netbsd-1-6-PATCH002-RELEASE, netbsd-1-6-PATCH002-RC4, netbsd-1-6-PATCH002-RC3, netbsd-1-6-PATCH002-RC2, netbsd-1-6-PATCH002-RC1, netbsd-1-6-PATCH002, netbsd-1-6-PATCH001-RELEASE, netbsd-1-6-PATCH001-RC3, netbsd-1-6-PATCH001-RC2, netbsd-1-6-PATCH001-RC1, netbsd-1-6-PATCH001, netbsd-1-6, netbsd-1-5-base, netbsd-1-5-RELEASE, netbsd-1-5-PATCH003, netbsd-1-5-PATCH002, netbsd-1-5-PATCH001, netbsd-1-5-BETA2, netbsd-1-5-BETA, netbsd-1-5-ALPHA2, netbsd-1-5, netbsd-1-4-base, netbsd-1-4-RELEASE, netbsd-1-4-PATCH003, netbsd-1-4-PATCH002, netbsd-1-4-PATCH001, netbsd-1-4, minoura-xpg4dl-base, minoura-xpg4dl, kenh-if-detach-base, kenh-if-detach, kame_14_19990705, kame_14_19990628, kame_141_19991130, kame, ifpoll-base, gehenna-devsw-base, gehenna-devsw, fvdl-softdep-base, fvdl-softdep, eeh-paddr_t-base, eeh-paddr_t, eeh-devprop-base, eeh-devprop, comdex-fall-1999-base, comdex-fall-1999, chs-ubc2-newbase, chs-ubc2-base, chs-ubc2, chs-ubc-base, chs-ubc
Branch point for: kqueue
Changes since 1.21: +1 -9 lines

Update for changes to config.

/*	$NetBSD: clock.c,v 1.22 1998/01/12 19:55:44 thorpej Exp $	*/

/*-
 * Copyright (c) 1990 The Regents of the University of California.
 * All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * William Jolitz and Don Ahn.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    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 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 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
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *	@(#)clock.c	7.2 (Berkeley) 5/12/91
 *
 */

#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
};

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
};

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
};

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;
{
	static u_char icu_table[] = {
		CICTL,	0,		/* Disable clock interrupt for now. */
		CIPTR,	IR_CLK << 4,	/* Select clock interrupt vector. */
		OCASN,	0,		/* No clock output. */
		CCTL,	0x1c,		/* Two clocks, prescale, output zero */
					/* detect of L-counter, run both */
					/* clocks. */
		0xff
	};

	printf("\n");
	icu_init(icu_table);

	/* Establish interrupt vector */
	intr_establish(IR_CLK, (void (*)(void *))hardclock, NULL,
		       self->dv_xname, IPL_CLOCK, IPL_CLOCK, FALLING_EDGE);

	/* Write the timer values to the ICU. */
	divisor = ICU_CLK_HZ / hz;
	ICUW(HCSV) = divisor;
	ICUW(HCCV) = divisor;
}

void
cpu_initclocks()
{
	/* Enable the clock interrupt. */
	ICUB(CICTL) = 0x30;
}

void
setstatclockrate(arg)
	int arg;
{
	printf("setstatclockrate\n");
}

void
microtime(tvp)
	register struct timeval *tvp;
{
	u_short count, delta, ipend;

	di();
	ICUB(MCTL) |= 0x88;		/* freeze HCCV, IPND */
	count = ICUW(HCCV);
	ipend = ICUW(IPND);
	*tvp = time;
	ICUB(MCTL) &= 0x77;		/* thaw ICU regs */
	ei();

	/* yields value 0..9999 */
	delta = ((divisor - count) * (4096 * 1000000UL / ICU_CLK_HZ)) >> 12;
	tvp->tv_usec += delta;

	/* check for timer overflow (ie; clock int disabled) */
	if (count > 0 && (ipend & (1 << IR_CLK)))
		tvp->tv_usec += tick;

	if (tvp->tv_usec >= 1000000) {
		tvp->tv_usec -= 1000000;
		tvp->tv_sec++;
	}

#ifdef DIAGNOSTIC
	if (tvp->tv_usec < 0 || tvp->tv_usec > 1000000)
		printf("microtime bad tv_usec: %ld\n", tvp->tv_usec);
#endif
}

/*
 * 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
rtc_match(parent, cf, aux)
	struct device *parent;
	struct cfdata *cf;
	void *aux;
{
	struct confargs *ca = aux;
	int rom_val, rom_cnt, i;

	/* 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);
}

static void
rtc_attach(parent, self, aux)
	struct device *parent;
	struct device *self;
	void *aux;
{
	printf("\n");
	rtc_attached = 1;
}

/*
 * Initialize the time of day register, based on the time base
 * which is, e.g. from a filesystem.
 */
void inittodr(fs_time)
	time_t fs_time;
{
	long diff, clk_time;
	long long_ago = (5 * SECYR);
	int clk_bad = 0;

	clockinitted = 1;

	/*
	 * 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;
	}

	clk_time = clk_get_secs();

	/* 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;
}

/*
 * Resettodr restores the time of day hardware after a time change.
 */
void resettodr()
{
	/*
	 * We might have been called by boot() due to a crash early
	 * on.  Don't reset the clock chip in this case.
	 */
	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;

	clock_secs_to_ymdhms(secs, &dt);
	dt.dt_year -= CLOCK_BASE_YEAR;
	if (dt.dt_year >= 100)
		dt.dt_year -= 100;

	rw_rtc(&dt, 1);
}