1 /* $NetBSD: clock.c,v 1.31 2001/05/27 13:53:24 sommerfeld Exp $ */ 2 3 /* 4 * Copyright (c) 1988 University of Utah. 5 * Copyright (c) 1992, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * This code is derived from software contributed to Berkeley by 9 * the Systems Programming Group of the University of Utah Computer 10 * Science Department and Ralph Campbell. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 3. All advertising materials mentioning features or use of this software 21 * must display the following acknowledgement: 22 * This product includes software developed by the University of 23 * California, Berkeley and its contributors. 24 * 4. Neither the name of the University nor the names of its contributors 25 * may be used to endorse or promote products derived from this software 26 * without specific prior written permission. 27 * 28 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 29 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 31 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 32 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 33 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 34 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 36 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 37 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 38 * SUCH DAMAGE. 39 * 40 * from: Utah Hdr: clock.c 1.18 91/01/21 41 * 42 * @(#)clock.c 8.1 (Berkeley) 6/10/93 43 */ 44 45 #include <sys/cdefs.h> /* RCS ID & Copyright macro defns */ 46 47 __KERNEL_RCSID(0, "$NetBSD: clock.c,v 1.31 2001/05/27 13:53:24 sommerfeld Exp $"); 48 49 #include <sys/param.h> 50 #include <sys/kernel.h> 51 #include <sys/systm.h> 52 #include <sys/device.h> 53 #include <sys/sched.h> 54 55 #include <dev/clock_subr.h> 56 57 #include <machine/autoconf.h> 58 #include <machine/cpuconf.h> 59 60 #include <dev/dec/clockvar.h> 61 62 #include "opt_clock_compat_osf1.h" 63 #include "opt_ntp.h" 64 65 #define MINYEAR 1998 /* "today" */ 66 #ifdef CLOCK_COMPAT_OSF1 67 /* 68 * According to OSF/1's /usr/sys/include/arch/alpha/clock.h, 69 * the console adjusts the RTC years 13..19 to 93..99 and 70 * 20..40 to 00..20. (historical reasons?) 71 * DEC Unix uses an offset to the year to stay outside 72 * the dangerous area for the next couple of years. 73 */ 74 #define UNIX_YEAR_OFFSET 52 /* 41=>1993, 12=>2064 */ 75 #else 76 #define UNIX_YEAR_OFFSET 0 77 #endif 78 79 struct device *clockdev; 80 const struct clockfns *clockfns; 81 int clockinitted; 82 83 void 84 clockattach(dev, fns) 85 struct device *dev; 86 const struct clockfns *fns; 87 { 88 89 /* 90 * Just bookkeeping. 91 */ 92 printf("\n"); 93 94 if (clockfns != NULL) 95 panic("clockattach: multiple clocks"); 96 clockdev = dev; 97 clockfns = fns; 98 } 99 100 /* 101 * Machine-dependent clock routines. 102 * 103 * Startrtclock restarts the real-time clock, which provides 104 * hardclock interrupts to kern_clock.c. 105 * 106 * Inittodr initializes the time of day hardware which provides 107 * date functions. Its primary function is to use some file 108 * system information in case the hardare clock lost state. 109 * 110 * Resettodr restores the time of day hardware after a time change. 111 */ 112 113 /* 114 * Start the real-time and statistics clocks. Leave stathz 0 since there 115 * are no other timers available. 116 */ 117 void 118 cpu_initclocks() 119 { 120 if (clockfns == NULL) 121 panic("cpu_initclocks: no clock attached"); 122 123 tick = 1000000 / hz; /* number of microseconds between interrupts */ 124 tickfix = 1000000 - (hz * tick); 125 if (tickfix) { 126 int ftp; 127 128 ftp = min(ffs(tickfix), ffs(hz)); 129 tickfix >>= (ftp - 1); 130 tickfixinterval = hz >> (ftp - 1); 131 } 132 133 /* 134 * Establish the clock interrupt; it's a special case. 135 * 136 * We establish the clock interrupt this late because if 137 * we do it at clock attach time, we may have never been at 138 * spl0() since taking over the system. Some versions of 139 * PALcode save a clock interrupt, which would get delivered 140 * when we spl0() in autoconf.c. If established the clock 141 * interrupt handler earlier, that interrupt would go to 142 * hardclock, which would then fall over because p->p_stats 143 * isn't set at that time. 144 */ 145 platform.clockintr = hardclock; 146 schedhz = 16; 147 148 /* 149 * Get the clock started. 150 */ 151 (*clockfns->cf_init)(clockdev); 152 } 153 154 /* 155 * We assume newhz is either stathz or profhz, and that neither will 156 * change after being set up above. Could recalculate intervals here 157 * but that would be a drag. 158 */ 159 void 160 setstatclockrate(newhz) 161 int newhz; 162 { 163 164 /* nothing we can do */ 165 } 166 167 /* 168 * Initialze the time of day register, based on the time base which is, e.g. 169 * from a filesystem. Base provides the time to within six months, 170 * and the time of year clock (if any) provides the rest. 171 */ 172 void 173 inittodr(base) 174 time_t base; 175 { 176 struct clocktime ct; 177 int year; 178 struct clock_ymdhms dt; 179 time_t deltat; 180 int badbase; 181 182 if (base < (MINYEAR-1970)*SECYR) { 183 printf("WARNING: preposterous time in file system"); 184 /* read the system clock anyway */ 185 base = (MINYEAR-1970)*SECYR; 186 badbase = 1; 187 } else 188 badbase = 0; 189 190 (*clockfns->cf_get)(clockdev, base, &ct); 191 #ifdef DEBUG 192 printf("readclock: %d/%d/%d/%d/%d/%d", ct.year, ct.mon, ct.day, 193 ct.hour, ct.min, ct.sec); 194 #endif 195 clockinitted = 1; 196 197 year = 1900 + UNIX_YEAR_OFFSET + ct.year; 198 if (year < 1970) 199 year += 100; 200 /* simple sanity checks (2037 = time_t overflow) */ 201 if (year < MINYEAR || year > 2037 || 202 ct.mon < 1 || ct.mon > 12 || ct.day < 1 || 203 ct.day > 31 || ct.hour > 23 || ct.min > 59 || ct.sec > 59) { 204 /* 205 * Believe the time in the file system for lack of 206 * anything better, resetting the TODR. 207 */ 208 time.tv_sec = base; 209 if (!badbase) { 210 printf("WARNING: preposterous clock chip time\n"); 211 resettodr(); 212 } 213 goto bad; 214 } 215 216 dt.dt_year = year; 217 dt.dt_mon = ct.mon; 218 dt.dt_day = ct.day; 219 dt.dt_hour = ct.hour; 220 dt.dt_min = ct.min; 221 dt.dt_sec = ct.sec; 222 time.tv_sec = clock_ymdhms_to_secs(&dt); 223 #ifdef DEBUG 224 printf("=>%ld (%d)\n", time.tv_sec, base); 225 #endif 226 microset_time = time; 227 microset(curcpu(), NULL); 228 229 if (!badbase) { 230 /* 231 * See if we gained/lost two or more days; 232 * if so, assume something is amiss. 233 */ 234 deltat = time.tv_sec - base; 235 if (deltat < 0) 236 deltat = -deltat; 237 if (deltat < 2 * SECDAY) 238 return; 239 printf("WARNING: clock %s %ld days", 240 time.tv_sec < base ? "lost" : "gained", 241 (long)deltat / SECDAY); 242 } 243 bad: 244 printf(" -- CHECK AND RESET THE DATE!\n"); 245 } 246 247 /* 248 * Reset the TODR based on the time value; used when the TODR 249 * has a preposterous value and also when the time is reset 250 * by the stime system call. Also called when the TODR goes past 251 * TODRZERO + 100*(SECYEAR+2*SECDAY) (e.g. on Jan 2 just after midnight) 252 * to wrap the TODR around. 253 */ 254 void 255 resettodr() 256 { 257 struct clock_ymdhms dt; 258 struct clocktime ct; 259 260 if (!clockinitted) 261 return; 262 263 microset_time = time; 264 #if defined(MULTIPROCESSOR) 265 alpha_multicast_ipi(cpus_running, ALPHA_IPI_MICROSET); 266 #endif 267 microset(curcpu(), NULL); 268 269 clock_secs_to_ymdhms(time.tv_sec, &dt); 270 271 /* rt clock wants 2 digits */ 272 ct.year = (dt.dt_year - UNIX_YEAR_OFFSET) % 100; 273 ct.mon = dt.dt_mon; 274 ct.day = dt.dt_day; 275 ct.hour = dt.dt_hour; 276 ct.min = dt.dt_min; 277 ct.sec = dt.dt_sec; 278 ct.dow = dt.dt_wday; 279 #ifdef DEBUG 280 printf("setclock: %d/%d/%d/%d/%d/%d\n", ct.year, ct.mon, ct.day, 281 ct.hour, ct.min, ct.sec); 282 #endif 283 284 (*clockfns->cf_set)(clockdev, &ct); 285 } 286