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