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