1 /* $NetBSD: clock.c,v 1.40 2001/05/16 05:36:55 matt Exp $ */ 2 /* 3 * Copyright (c) 1995 Ludd, University of Lule}, Sweden. 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. All advertising materials mentioning features or use of this software 15 * must display the following acknowledgement: 16 * This product includes software developed at Ludd, University of Lule}. 17 * 4. The name of the author may not be used to endorse or promote products 18 * derived from this software without specific prior written permission 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 21 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 22 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 23 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 24 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 25 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 29 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 33 #include <sys/param.h> 34 #include <sys/kernel.h> 35 #include <sys/systm.h> 36 #include <sys/device.h> 37 38 #include <dev/clock_subr.h> 39 40 #include <machine/mtpr.h> 41 #include <machine/sid.h> 42 #include <machine/clock.h> 43 #include <machine/cpu.h> 44 #include <machine/uvax.h> 45 46 #include "opt_cputype.h" 47 48 struct evcnt clock_intrcnt = 49 EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, "clock", "intr"); 50 51 /* 52 * microtime() should return number of usecs in struct timeval. 53 * We may get wrap-arounds, but that will be fixed with lasttime 54 * check. This may fault within 10 msecs. 55 */ 56 void 57 microtime(tvp) 58 struct timeval *tvp; 59 { 60 int s, i; 61 static struct timeval lasttime; 62 63 s = splhigh(); 64 bcopy((caddr_t)&time, tvp, sizeof(struct timeval)); 65 66 switch (vax_boardtype) { 67 #if VAX46 || VAXANY 68 case VAX_BTYP_46: { 69 extern struct vs_cpu *ka46_cpu; 70 i = *(volatile int *)(&ka46_cpu->vc_diagtimu); 71 i = (i >> 16) * 1024 + (i & 0x3ff); 72 break; 73 } 74 #endif 75 #if VAX48 || VAXANY 76 case VAX_BTYP_48: { 77 /* 78 * PR_ICR doesn't exist. We could use the vc_diagtimu 79 * counter, saving the value on the timer interrupt and 80 * subtracting that from the current value. 81 */ 82 i = 0; 83 break; 84 } 85 #endif 86 default: 87 i = mfpr(PR_ICR); 88 break; 89 } 90 i += tick; /* Get current interval count */ 91 tvp->tv_usec += i; 92 while (tvp->tv_usec >= 1000000) { 93 tvp->tv_sec++; 94 tvp->tv_usec -= 1000000; 95 } 96 if (tvp->tv_sec == lasttime.tv_sec && 97 tvp->tv_usec <= lasttime.tv_usec && 98 (tvp->tv_usec = lasttime.tv_usec + 1) >= 1000000) { 99 tvp->tv_sec++; 100 tvp->tv_usec -= 1000000; 101 } 102 bcopy(tvp, &lasttime, sizeof(struct timeval)); 103 splx(s); 104 } 105 106 /* 107 * Sets year to the year in fs_time and then calculates the number of 108 * 100th of seconds in the current year and saves that info in year_len. 109 * fs_time contains the time set in the superblock in the root filesystem. 110 * If the clock is started, it then checks if the time is valid 111 * compared with the time in fs_time. If the clock is stopped, an 112 * alert is printed and the time is temporary set to the time in fs_time. 113 */ 114 115 void 116 inittodr(fs_time) 117 time_t fs_time; 118 { 119 int rv; 120 121 rv = (*dep_call->cpu_clkread) (fs_time); 122 switch (rv) { 123 124 case CLKREAD_BAD: /* No useable information from system clock */ 125 time.tv_sec = fs_time; 126 resettodr(); 127 break; 128 129 case CLKREAD_WARN: /* Just give the warning */ 130 break; 131 132 default: /* System clock OK, no warning if we don't want to. */ 133 if (time.tv_sec > fs_time + 3 * SEC_PER_DAY) { 134 printf("Clock has gained %ld days", 135 (time.tv_sec - fs_time) / SEC_PER_DAY); 136 rv = CLKREAD_WARN; 137 } else if (time.tv_sec + SEC_PER_DAY < fs_time) { 138 printf("Clock has lost %ld day(s)", 139 (fs_time - time.tv_sec) / SEC_PER_DAY); 140 rv = CLKREAD_WARN; 141 } 142 break; 143 } 144 145 if (rv < CLKREAD_OK) 146 printf(" - CHECK AND RESET THE DATE.\n"); 147 } 148 149 /* 150 * Resettodr restores the time of day hardware after a time change. 151 */ 152 153 void 154 resettodr() 155 { 156 (*dep_call->cpu_clkwrite)(); 157 } 158 /* 159 * A delayloop that delays about the number of milliseconds that is 160 * given as argument. 161 */ 162 void 163 delay(i) 164 int i; 165 { 166 asm ("1: sobgtr %0, 1b" : : "r" (dep_call->cpu_vups * i)); 167 } 168 169 /* 170 * On all VAXen there are a microsecond clock that should 171 * be used for interval interrupts. Some CPUs don't use the ICR interval 172 * register but it doesn't hurt to load it anyway. 173 */ 174 void 175 cpu_initclocks() 176 { 177 mtpr(-10000, PR_NICR); /* Load in count register */ 178 mtpr(0x800000d1, PR_ICCS); /* Start clock and enable interrupt */ 179 evcnt_attach_static(&clock_intrcnt); 180 } 181 182 /* 183 * There are two types of real-time battery-backed up clocks on 184 * VAX computers, one with a register that counts up every 1/100 second, 185 * one with a clock chip that delivers time. For the register clock 186 * we have a generic version, and for the chip clock there are 187 * support routines for time conversion. 188 */ 189 /* 190 * Converts a year to corresponding number of ticks. 191 */ 192 int 193 yeartonum(y) 194 int y; 195 { 196 int n; 197 198 for (n = 0, y -= 1; y > 69; y--) 199 n += SECPERYEAR(y); 200 return n; 201 } 202 203 /* 204 * Converts tick number to a year 70 -> 205 */ 206 int 207 numtoyear(num) 208 int num; 209 { 210 int y = 70, j; 211 while(num >= (j = SECPERYEAR(y))) { 212 y++; 213 num -= j; 214 } 215 return y; 216 } 217 218 #if VAX750 || VAX780 || VAX8600 || VAX650 || \ 219 VAX660 || VAX670 || VAX680 || VAX53 || VAXANY 220 /* 221 * Reads the TODR register; returns a (probably) true tick value, 222 * or CLKREAD_BAD if failed. The year is based on the argument 223 * year; the TODR doesn't hold years. 224 */ 225 int 226 generic_clkread(base) 227 time_t base; 228 { 229 unsigned klocka = mfpr(PR_TODR); 230 231 /* 232 * Sanity check. 233 */ 234 if (klocka < TODRBASE) { 235 if (klocka == 0) 236 printf("TODR stopped"); 237 else 238 printf("TODR too small"); 239 return CLKREAD_BAD; 240 } 241 242 time.tv_sec = yeartonum(numtoyear(base)) + (klocka - TODRBASE) / 100; 243 return CLKREAD_OK; 244 } 245 246 /* 247 * Takes the current system time and writes it to the TODR. 248 */ 249 void 250 generic_clkwrite() 251 { 252 unsigned tid = time.tv_sec, bastid; 253 254 bastid = tid - yeartonum(numtoyear(tid)); 255 mtpr((bastid * 100) + TODRBASE, PR_TODR); 256 } 257 #endif 258 259 #if VAX630 || VAX410 || VAX43 || VAX8200 || VAX46 || VAX48 || VAX49 || VAXANY 260 261 volatile short *clk_page; /* where the chip is mapped in virtual memory */ 262 int clk_adrshift; /* how much to multiply the in-page address with */ 263 int clk_tweak; /* Offset of time into word. */ 264 265 #define REGPEEK(off) (clk_page[off << clk_adrshift] >> clk_tweak) 266 #define REGPOKE(off, v) (clk_page[off << clk_adrshift] = ((v) << clk_tweak)) 267 268 int 269 chip_clkread(base) 270 time_t base; 271 { 272 struct clock_ymdhms c; 273 int timeout = 1<<15, s; 274 275 #ifdef DIAGNOSTIC 276 if (clk_page == 0) 277 panic("trying to use unset chip clock page"); 278 #endif 279 280 if ((REGPEEK(CSRD_OFF) & CSRD_VRT) == 0) { 281 printf("WARNING: TOY clock not marked valid"); 282 return CLKREAD_BAD; 283 } 284 while (REGPEEK(CSRA_OFF) & CSRA_UIP) 285 if (--timeout == 0) { 286 printf ("TOY clock timed out"); 287 return CLKREAD_BAD; 288 } 289 290 s = splhigh(); 291 c.dt_year = ((u_char)REGPEEK(YR_OFF)) + 1970; 292 c.dt_mon = REGPEEK(MON_OFF); 293 c.dt_day = REGPEEK(DAY_OFF); 294 c.dt_wday = REGPEEK(WDAY_OFF); 295 c.dt_hour = REGPEEK(HR_OFF); 296 c.dt_min = REGPEEK(MIN_OFF); 297 c.dt_sec = REGPEEK(SEC_OFF); 298 splx(s); 299 300 time.tv_sec = clock_ymdhms_to_secs(&c); 301 return CLKREAD_OK; 302 } 303 304 void 305 chip_clkwrite() 306 { 307 struct clock_ymdhms c; 308 309 #ifdef DIAGNOSTIC 310 if (clk_page == 0) 311 panic("trying to use unset chip clock page"); 312 #endif 313 314 REGPOKE(CSRB_OFF, CSRB_SET); 315 316 clock_secs_to_ymdhms(time.tv_sec, &c); 317 318 REGPOKE(YR_OFF, ((u_char)(c.dt_year - 1970))); 319 REGPOKE(MON_OFF, c.dt_mon); 320 REGPOKE(DAY_OFF, c.dt_day); 321 REGPOKE(WDAY_OFF, c.dt_wday); 322 REGPOKE(HR_OFF, c.dt_hour); 323 REGPOKE(MIN_OFF, c.dt_min); 324 REGPOKE(SEC_OFF, c.dt_sec); 325 326 REGPOKE(CSRB_OFF, CSRB_DM|CSRB_24); 327 }; 328 #endif 329