1 /*-
2 * Copyright (c) 1990, 1993
3 * The Regents of the University of California. All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * William Jolitz and Don Ahn.
7 *
8 * %sccs.include.redist.c%
9 *
10 * from: @(#)clock.c 7.2 (Berkeley) 5/12/91
11 * from NetBSD: Id: clock.c,v 1.6 1993/05/22 08:01:07 cgd Exp
12 *
13 * @(#)clock.c 8.1 (Berkeley) 06/11/93
14 *
15 */
16
17 /*
18 * Primitive clock interrupt routines.
19 */
20 #include <sys/param.h>
21 #include <sys/time.h>
22 #include <sys/kernel.h>
23 #include <machine/segments.h>
24 #include <i386/isa/icu.h>
25 #include <i386/isa/isa.h>
26 #include <i386/isa/rtc.h>
27
28 /* these should go elsewere (timerreg.h) but to avoid admin overhead... */
29 /*
30 * Macros for specifying values to be written into a mode register.
31 */
32 #define TIMER_CNTR0 (IO_TIMER1 + 0) /* timer 0 counter port */
33 #define TIMER_CNTR1 (IO_TIMER1 + 1) /* timer 1 counter port */
34 #define TIMER_CNTR2 (IO_TIMER1 + 2) /* timer 2 counter port */
35 #define TIMER_MODE (IO_TIMER1 + 3) /* timer mode port */
36 #define TIMER_SEL0 0x00 /* select counter 0 */
37 #define TIMER_SEL1 0x40 /* select counter 1 */
38 #define TIMER_SEL2 0x80 /* select counter 2 */
39 #define TIMER_INTTC 0x00 /* mode 0, intr on terminal cnt */
40 #define TIMER_ONESHOT 0x02 /* mode 1, one shot */
41 #define TIMER_RATEGEN 0x04 /* mode 2, rate generator */
42 #define TIMER_SQWAVE 0x06 /* mode 3, square wave */
43 #define TIMER_SWSTROBE 0x08 /* mode 4, s/w triggered strobe */
44 #define TIMER_HWSTROBE 0x0a /* mode 5, h/w triggered strobe */
45 #define TIMER_LATCH 0x00 /* latch counter for reading */
46 #define TIMER_LSB 0x10 /* r/w counter LSB */
47 #define TIMER_MSB 0x20 /* r/w counter MSB */
48 #define TIMER_16BIT 0x30 /* r/w counter 16 bits, LSB first */
49 #define TIMER_BCD 0x01 /* count in BCD */
50
51 #define DAYST 119
52 #define DAYEN 303
53
54 #ifndef XTALSPEED
55 #define XTALSPEED 1193182
56 #endif
57
startrtclock()58 startrtclock() {
59 int s;
60
61 findcpuspeed(); /* use the clock (while it's free)
62 to find the cpu speed */
63 /* initialize 8253 clock */
64 outb(TIMER_MODE, TIMER_SEL0|TIMER_RATEGEN|TIMER_16BIT);
65
66 /* Correct rounding will buy us a better precision in timekeeping */
67 outb (IO_TIMER1, (XTALSPEED+hz/2)/hz);
68 outb (IO_TIMER1, ((XTALSPEED+hz/2)/hz)/256);
69
70 /* initialize brain-dead battery powered clock */
71 outb (IO_RTC, RTC_STATUSA);
72 outb (IO_RTC+1, 0x26);
73 outb (IO_RTC, RTC_STATUSB);
74 outb (IO_RTC+1, 2);
75
76 outb (IO_RTC, RTC_DIAG);
77 if (s = inb (IO_RTC+1))
78 printf("RTC BIOS diagnostic error %b\n", s, RTCDG_BITS);
79 outb (IO_RTC, RTC_DIAG);
80 outb (IO_RTC+1, 0);
81 }
82
83 unsigned int delaycount; /* calibrated loop variable (1 millisecond) */
84
85 #define FIRST_GUESS 0x2000
findcpuspeed()86 findcpuspeed()
87 {
88 unsigned char low;
89 unsigned int remainder;
90
91 /* Put counter in count down mode */
92 outb(IO_TIMER1+3, 0x34);
93 outb(IO_TIMER1, 0xff);
94 outb(IO_TIMER1, 0xff);
95 delaycount = FIRST_GUESS;
96 spinwait(1);
97 /* Read the value left in the counter */
98 low = inb(IO_TIMER1); /* least siginifcant */
99 remainder = inb(IO_TIMER1); /* most significant */
100 remainder = (remainder<<8) + low ;
101 /* Formula for delaycount is :
102 * (loopcount * timer clock speed)/ (counter ticks * 1000)
103 */
104 delaycount = (FIRST_GUESS * (XTALSPEED/1000)) / (0xffff-remainder);
105 }
106
107
108
109 /* convert 2 digit BCD number */
bcd(i)110 bcd(i)
111 int i;
112 {
113 return ((i/16)*10 + (i%16));
114 }
115
116 /* convert years to seconds (from 1970) */
117 unsigned long
ytos(y)118 ytos(y)
119 int y;
120 {
121 int i;
122 unsigned long ret;
123
124 ret = 0;
125 for(i = 1970; i < y; i++) {
126 if (i % 4) ret += 365*24*60*60;
127 else ret += 366*24*60*60;
128 }
129 return ret;
130 }
131
132 /* convert months to seconds */
133 unsigned long
mtos(m,leap)134 mtos(m,leap)
135 int m,leap;
136 {
137 int i;
138 unsigned long ret;
139
140 ret = 0;
141 for(i=1;i<m;i++) {
142 switch(i){
143 case 1: case 3: case 5: case 7: case 8: case 10: case 12:
144 ret += 31*24*60*60; break;
145 case 4: case 6: case 9: case 11:
146 ret += 30*24*60*60; break;
147 case 2:
148 if (leap) ret += 29*24*60*60;
149 else ret += 28*24*60*60;
150 }
151 }
152 return ret;
153 }
154
155
156 /*
157 * Initialize the time of day register, based on the time base which is, e.g.
158 * from a filesystem.
159 */
inittodr(base)160 inittodr(base)
161 time_t base;
162 {
163 unsigned long sec;
164 int leap,day_week,t,yd;
165 int sa,s;
166
167 /* do we have a realtime clock present? (otherwise we loop below) */
168 sa = rtcin(RTC_STATUSA);
169 if (sa == 0xff || sa == 0) return;
170
171 /* ready for a read? */
172 while ((sa&RTCSA_TUP) == RTCSA_TUP)
173 sa = rtcin(RTC_STATUSA);
174
175 sec = bcd(rtcin(RTC_YEAR)) + 1900;
176 if (sec < 1970)
177 sec += 100;
178 leap = !(sec % 4); sec = ytos(sec); /* year */
179 yd = mtos(bcd(rtcin(RTC_MONTH)),leap); sec += yd; /* month */
180 t = (bcd(rtcin(RTC_DAY))-1) * 24*60*60; sec += t; yd += t; /* date */
181 day_week = rtcin(RTC_WDAY); /* day */
182 sec += bcd(rtcin(RTC_HRS)) * 60*60; /* hour */
183 sec += bcd(rtcin(RTC_MIN)) * 60; /* minutes */
184 sec += bcd(rtcin(RTC_SEC)); /* seconds */
185
186 /* XXX off by one? Need to calculate DST on SUNDAY */
187 /* Perhaps we should have the RTC hold GMT time to save */
188 /* us the bother of converting. */
189 yd = yd / (24*60*60);
190 if ((yd >= DAYST) && ( yd <= DAYEN)) {
191 sec -= 60*60;
192 }
193 sec += tz.tz_minuteswest * 60;
194
195 time.tv_sec = sec;
196 }
197
198 #ifdef garbage
199 /*
200 * Initialze the time of day register, based on the time base which is, e.g.
201 * from a filesystem.
202 */
test_inittodr(base)203 test_inittodr(base)
204 time_t base;
205 {
206
207 outb(IO_RTC,9); /* year */
208 printf("%d ",bcd(inb(IO_RTC+1)));
209 outb(IO_RTC,8); /* month */
210 printf("%d ",bcd(inb(IO_RTC+1)));
211 outb(IO_RTC,7); /* day */
212 printf("%d ",bcd(inb(IO_RTC+1)));
213 outb(IO_RTC,4); /* hour */
214 printf("%d ",bcd(inb(IO_RTC+1)));
215 outb(IO_RTC,2); /* minutes */
216 printf("%d ",bcd(inb(IO_RTC+1)));
217 outb(IO_RTC,0); /* seconds */
218 printf("%d\n",bcd(inb(IO_RTC+1)));
219
220 time.tv_sec = base;
221 }
222 #endif
223
224 /*
225 * Restart the clock.
226 */
resettodr()227 resettodr()
228 {
229 }
230
231 /*
232 * Wire clock interrupt in.
233 */
234 #define V(s) __CONCAT(V, s)
235 extern V(clk)();
enablertclock()236 enablertclock() {
237 INTREN(IRQ0);
238 setidt(ICU_OFFSET+0, &V(clk), SDT_SYS386IGT, SEL_KPL);
239 splnone();
240 }
241
242
243
244
spinwait(millisecs)245 spinwait(millisecs)
246 int millisecs; /* number of milliseconds to delay */
247 {
248 int i, j;
249
250 for (i=0;i<millisecs;i++)
251 for (j=0;j<delaycount;j++)
252 ;
253 }
254
255