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 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 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 */ 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 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 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 */ 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 */ 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 */ 227 resettodr() 228 { 229 } 230 231 /* 232 * Wire clock interrupt in. 233 */ 234 #define V(s) __CONCAT(V, s) 235 extern V(clk)(); 236 enablertclock() { 237 INTREN(IRQ0); 238 setidt(ICU_OFFSET+0, &V(clk), SDT_SYS386IGT, SEL_KPL); 239 splnone(); 240 } 241 242 243 244 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