xref: /csrg-svn/sys/i386/isa/clock.c (revision 63364)
141052Swilliam /*-
2*63364Sbostic  * Copyright (c) 1990, 1993
3*63364Sbostic  *	The Regents of the University of California.  All rights reserved.
441052Swilliam  *
541052Swilliam  * This code is derived from software contributed to Berkeley by
649617Sbostic  * William Jolitz and Don Ahn.
741052Swilliam  *
849617Sbostic  * %sccs.include.redist.c%
943590Sdonahn  *
1063177Scgd  *	from: @(#)clock.c	7.2 (Berkeley) 5/12/91
1163177Scgd  *	from NetBSD: Id: clock.c,v 1.6 1993/05/22 08:01:07 cgd Exp
1263177Scgd  *
13*63364Sbostic  *	@(#)clock.c	8.1 (Berkeley) 06/11/93
1463177Scgd  *
1541052Swilliam  */
1641052Swilliam 
1741052Swilliam /*
1841052Swilliam  * Primitive clock interrupt routines.
1941052Swilliam  */
2056513Sbostic #include <sys/param.h>
2156513Sbostic #include <sys/time.h>
2256513Sbostic #include <sys/kernel.h>
2356513Sbostic #include <machine/segments.h>
2456513Sbostic #include <i386/isa/icu.h>
2556513Sbostic #include <i386/isa/isa.h>
2656513Sbostic #include <i386/isa/rtc.h>
2741052Swilliam 
2863177Scgd /* these should go elsewere (timerreg.h) but to avoid admin overhead... */
2963177Scgd /*
3063177Scgd  * Macros for specifying values to be written into a mode register.
3163177Scgd  */
3263177Scgd #define TIMER_CNTR0     (IO_TIMER1 + 0) /* timer 0 counter port */
3363177Scgd #define TIMER_CNTR1     (IO_TIMER1 + 1) /* timer 1 counter port */
3463177Scgd #define TIMER_CNTR2     (IO_TIMER1 + 2) /* timer 2 counter port */
3563177Scgd #define TIMER_MODE      (IO_TIMER1 + 3) /* timer mode port */
3663177Scgd #define         TIMER_SEL0      0x00    /* select counter 0 */
3763177Scgd #define         TIMER_SEL1      0x40    /* select counter 1 */
3863177Scgd #define         TIMER_SEL2      0x80    /* select counter 2 */
3963177Scgd #define         TIMER_INTTC     0x00    /* mode 0, intr on terminal cnt */
4063177Scgd #define         TIMER_ONESHOT   0x02    /* mode 1, one shot */
4163177Scgd #define         TIMER_RATEGEN   0x04    /* mode 2, rate generator */
4263177Scgd #define         TIMER_SQWAVE    0x06    /* mode 3, square wave */
4363177Scgd #define         TIMER_SWSTROBE  0x08    /* mode 4, s/w triggered strobe */
4463177Scgd #define         TIMER_HWSTROBE  0x0a    /* mode 5, h/w triggered strobe */
4563177Scgd #define         TIMER_LATCH     0x00    /* latch counter for reading */
4663177Scgd #define         TIMER_LSB       0x10    /* r/w counter LSB */
4763177Scgd #define         TIMER_MSB       0x20    /* r/w counter MSB */
4863177Scgd #define         TIMER_16BIT     0x30    /* r/w counter 16 bits, LSB first */
4963177Scgd #define         TIMER_BCD       0x01    /* count in BCD */
5063177Scgd 
5143590Sdonahn #define DAYST 119
5243590Sdonahn #define DAYEN 303
5343590Sdonahn 
5463177Scgd #ifndef	XTALSPEED
5563177Scgd #define XTALSPEED 1193182
5663177Scgd #endif
5763177Scgd 
startrtclock()5841052Swilliam startrtclock() {
5949573Swilliam 	int s;
6041052Swilliam 
6163177Scgd 	findcpuspeed();		/* use the clock (while it's free)
6263177Scgd 					to find the cpu speed */
6341052Swilliam 	/* initialize 8253 clock */
6463177Scgd 	outb(TIMER_MODE, TIMER_SEL0|TIMER_RATEGEN|TIMER_16BIT);
6549573Swilliam 
6663177Scgd 	/* Correct rounding will buy us a better precision in timekeeping */
6763177Scgd 	outb (IO_TIMER1, (XTALSPEED+hz/2)/hz);
6863177Scgd 	outb (IO_TIMER1, ((XTALSPEED+hz/2)/hz)/256);
6963177Scgd 
7049573Swilliam 	/* initialize brain-dead battery powered clock */
7149573Swilliam 	outb (IO_RTC, RTC_STATUSA);
7249573Swilliam 	outb (IO_RTC+1, 0x26);
7349573Swilliam 	outb (IO_RTC, RTC_STATUSB);
7449573Swilliam 	outb (IO_RTC+1, 2);
7549573Swilliam 
7649573Swilliam 	outb (IO_RTC, RTC_DIAG);
7749573Swilliam 	if (s = inb (IO_RTC+1))
7849573Swilliam 		printf("RTC BIOS diagnostic error %b\n", s, RTCDG_BITS);
7949573Swilliam 	outb (IO_RTC, RTC_DIAG);
8049573Swilliam 	outb (IO_RTC+1, 0);
8141052Swilliam }
8241052Swilliam 
8363177Scgd unsigned int delaycount;	/* calibrated loop variable (1 millisecond) */
8463177Scgd 
8563177Scgd #define FIRST_GUESS	0x2000
findcpuspeed()8663177Scgd findcpuspeed()
8763177Scgd {
8863177Scgd 	unsigned char low;
8963177Scgd 	unsigned int remainder;
9063177Scgd 
9163177Scgd 	/* Put counter in count down mode */
9263177Scgd 	outb(IO_TIMER1+3, 0x34);
9363177Scgd 	outb(IO_TIMER1, 0xff);
9463177Scgd 	outb(IO_TIMER1, 0xff);
9563177Scgd 	delaycount = FIRST_GUESS;
9663177Scgd 	spinwait(1);
9763177Scgd 	/* Read the value left in the counter */
9863177Scgd 	low 	= inb(IO_TIMER1);	/* least siginifcant */
9963177Scgd 	remainder = inb(IO_TIMER1);	/* most significant */
10063177Scgd 	remainder = (remainder<<8) + low ;
10163177Scgd 	/* Formula for delaycount is :
10263177Scgd 	 *  (loopcount * timer clock speed)/ (counter ticks * 1000)
10363177Scgd 	 */
10463177Scgd 	delaycount = (FIRST_GUESS * (XTALSPEED/1000)) / (0xffff-remainder);
10563177Scgd }
10663177Scgd 
10763177Scgd 
10863177Scgd 
10943590Sdonahn /* convert 2 digit BCD number */
bcd(i)11043590Sdonahn bcd(i)
11143590Sdonahn int i;
11243590Sdonahn {
11343590Sdonahn 	return ((i/16)*10 + (i%16));
11443590Sdonahn }
11543590Sdonahn 
11643590Sdonahn /* convert years to seconds (from 1970) */
11743590Sdonahn unsigned long
ytos(y)11843590Sdonahn ytos(y)
11943590Sdonahn int y;
12043590Sdonahn {
12143590Sdonahn 	int i;
12243590Sdonahn 	unsigned long ret;
12343590Sdonahn 
12463177Scgd 	ret = 0;
12563177Scgd 	for(i = 1970; i < y; i++) {
12649573Swilliam 		if (i % 4) ret += 365*24*60*60;
12749573Swilliam 		else ret += 366*24*60*60;
12843590Sdonahn 	}
12943590Sdonahn 	return ret;
13043590Sdonahn }
13143590Sdonahn 
13243590Sdonahn /* convert months to seconds */
13343590Sdonahn unsigned long
mtos(m,leap)13443590Sdonahn mtos(m,leap)
13543590Sdonahn int m,leap;
13643590Sdonahn {
13743590Sdonahn 	int i;
13843590Sdonahn 	unsigned long ret;
13943590Sdonahn 
14043590Sdonahn 	ret = 0;
14143590Sdonahn 	for(i=1;i<m;i++) {
14243590Sdonahn 		switch(i){
14343590Sdonahn 		case 1: case 3: case 5: case 7: case 8: case 10: case 12:
14449573Swilliam 			ret += 31*24*60*60; break;
14543590Sdonahn 		case 4: case 6: case 9: case 11:
14649573Swilliam 			ret += 30*24*60*60; break;
14743590Sdonahn 		case 2:
14849573Swilliam 			if (leap) ret += 29*24*60*60;
14949573Swilliam 			else ret += 28*24*60*60;
15043590Sdonahn 		}
15143590Sdonahn 	}
15243590Sdonahn 	return ret;
15343590Sdonahn }
15443590Sdonahn 
15543590Sdonahn 
15641052Swilliam /*
15745532Sbill  * Initialize the time of day register, based on the time base which is, e.g.
15841052Swilliam  * from a filesystem.
15941052Swilliam  */
inittodr(base)16041052Swilliam inittodr(base)
16141052Swilliam 	time_t base;
16241052Swilliam {
16343590Sdonahn 	unsigned long sec;
16443590Sdonahn 	int leap,day_week,t,yd;
16549699Swilliam 	int sa,s;
16641052Swilliam 
16749573Swilliam 	/* do we have a realtime clock present? (otherwise we loop below) */
16849573Swilliam 	sa = rtcin(RTC_STATUSA);
16949573Swilliam 	if (sa == 0xff || sa == 0) return;
17043590Sdonahn 
17149573Swilliam 	/* ready for a read? */
17249573Swilliam 	while ((sa&RTCSA_TUP) == RTCSA_TUP)
17349573Swilliam 		sa = rtcin(RTC_STATUSA);
17449573Swilliam 
17563177Scgd 	sec = bcd(rtcin(RTC_YEAR)) + 1900;
17663177Scgd 	if (sec < 1970)
17763177Scgd 		sec += 100;
17863177Scgd 	leap = !(sec % 4); sec = ytos(sec); /* year    */
17949573Swilliam 	yd = mtos(bcd(rtcin(RTC_MONTH)),leap); sec += yd;	/* month   */
18049573Swilliam 	t = (bcd(rtcin(RTC_DAY))-1) * 24*60*60; sec += t; yd += t; /* date    */
18149573Swilliam 	day_week = rtcin(RTC_WDAY);				/* day     */
18249573Swilliam 	sec += bcd(rtcin(RTC_HRS)) * 60*60;			/* hour    */
18349573Swilliam 	sec += bcd(rtcin(RTC_MIN)) * 60;			/* minutes */
18449573Swilliam 	sec += bcd(rtcin(RTC_SEC));				/* seconds */
18549573Swilliam 
18643590Sdonahn 	/* XXX off by one? Need to calculate DST on SUNDAY */
18743590Sdonahn 	/* Perhaps we should have the RTC hold GMT time to save */
18843590Sdonahn 	/* us the bother of converting. */
18963177Scgd 	yd = yd / (24*60*60);
19043590Sdonahn 	if ((yd >= DAYST) && ( yd <= DAYEN)) {
19149573Swilliam 		sec -= 60*60;
19243590Sdonahn 	}
19343590Sdonahn 	sec += tz.tz_minuteswest * 60;
19443590Sdonahn 
19543590Sdonahn 	time.tv_sec = sec;
19643590Sdonahn }
19743590Sdonahn 
19845532Sbill #ifdef garbage
19943590Sdonahn /*
20043590Sdonahn  * Initialze the time of day register, based on the time base which is, e.g.
20143590Sdonahn  * from a filesystem.
20243590Sdonahn  */
test_inittodr(base)20343590Sdonahn test_inittodr(base)
20443590Sdonahn 	time_t base;
20543590Sdonahn {
20643590Sdonahn 
20745532Sbill 	outb(IO_RTC,9); /* year    */
20845532Sbill 	printf("%d ",bcd(inb(IO_RTC+1)));
20945532Sbill 	outb(IO_RTC,8); /* month   */
21045532Sbill 	printf("%d ",bcd(inb(IO_RTC+1)));
21145532Sbill 	outb(IO_RTC,7); /* day     */
21245532Sbill 	printf("%d ",bcd(inb(IO_RTC+1)));
21345532Sbill 	outb(IO_RTC,4); /* hour    */
21445532Sbill 	printf("%d ",bcd(inb(IO_RTC+1)));
21545532Sbill 	outb(IO_RTC,2); /* minutes */
21645532Sbill 	printf("%d ",bcd(inb(IO_RTC+1)));
21745532Sbill 	outb(IO_RTC,0); /* seconds */
21845532Sbill 	printf("%d\n",bcd(inb(IO_RTC+1)));
21943590Sdonahn 
22041052Swilliam 	time.tv_sec = base;
22141052Swilliam }
22245532Sbill #endif
22341052Swilliam 
22445532Sbill /*
22541052Swilliam  * Restart the clock.
22641052Swilliam  */
resettodr()22741052Swilliam resettodr()
22841052Swilliam {
22941052Swilliam }
23041052Swilliam 
23149699Swilliam /*
23249699Swilliam  * Wire clock interrupt in.
23349699Swilliam  */
23449573Swilliam #define V(s)	__CONCAT(V, s)
23545624Sbill extern V(clk)();
enablertclock()23641052Swilliam enablertclock() {
23741052Swilliam 	INTREN(IRQ0);
23845624Sbill 	setidt(ICU_OFFSET+0, &V(clk), SDT_SYS386IGT, SEL_KPL);
23941052Swilliam 	splnone();
24041052Swilliam }
24163177Scgd 
24263177Scgd 
24363177Scgd 
24463177Scgd 
spinwait(millisecs)24563177Scgd spinwait(millisecs)
24663177Scgd int millisecs;		/* number of milliseconds to delay */
24763177Scgd {
24863177Scgd 	int i, j;
24963177Scgd 
25063177Scgd 	for (i=0;i<millisecs;i++)
25163177Scgd 		for (j=0;j<delaycount;j++)
25263177Scgd 			;
25363177Scgd }
25463177Scgd 
255