1 /* $NetBSD: clock.c,v 1.32 2001/11/23 01:04:11 simonb 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.32 2001/11/23 01:04:11 simonb Exp $"); 48 49 #include <sys/param.h> 50 #include <sys/kernel.h> 51 #include <sys/systm.h> 52 53 #include <dev/clock_subr.h> 54 55 #include <dev/dec/clockvar.h> 56 57 #include "opt_ntp.h" 58 59 #define MINYEAR 1998 /* "today" */ 60 61 struct device *clockdev; 62 const struct clockfns *clockfns; 63 int clockinitted; 64 65 #ifdef NTP 66 extern int fixtick; /* XXX */ 67 #endif 68 69 void 70 clockattach(dev, fns) 71 struct device *dev; 72 const struct clockfns *fns; 73 { 74 75 /* 76 * Just bookkeeping. 77 */ 78 printf("\n"); 79 80 if (clockfns != NULL) 81 panic("clockattach: multiple clocks"); 82 clockdev = dev; 83 clockfns = fns; 84 #ifdef EVCNT_COUNTERS 85 evcnt_attach_dynamic(&clock_intr_evcnt, EVCNT_TYPE_INTR, NULL, 86 dev->dv_xname, "intr"); 87 #endif 88 } 89 90 /* 91 * Machine-dependent clock routines. 92 * 93 * Startrtclock restarts the real-time clock, which provides 94 * hardclock interrupts to kern_clock.c. 95 * 96 * Inittodr initializes the time of day hardware which provides 97 * date functions. Its primary function is to use some file 98 * system information in case the hardare clock lost state. 99 * 100 * Resettodr restores the time of day hardware after a time change. 101 */ 102 103 /* 104 * Start the real-time and statistics clocks. Leave stathz 0 since there 105 * are no other timers available. 106 */ 107 void 108 cpu_initclocks() 109 { 110 111 if (clockfns == NULL) 112 panic("cpu_initclocks: no clock attached"); 113 114 /* 115 * Establish the clock interrupt; it's a special case. 116 * 117 * We establish the clock interrupt this late because if 118 * we do it at clock attach time, we may have never been at 119 * spl0() since taking over the system. Some versions of 120 * PALcode save a clock interrupt, which would get delivered 121 * when we spl0() in autoconf.c. If established the clock 122 * interrupt handler earlier, that interrupt would go to 123 * hardclock, which would then fall over because p->p_stats 124 * isn't set at that time. 125 */ 126 127 /* 128 * Get the clock started. 129 */ 130 (*clockfns->cf_init)(clockdev); 131 132 /* 133 * Set hz-related variables after the clock is initialised in 134 * case the initialisation routines adjusted hz. 135 */ 136 tick = 1000000 / hz; /* number of microseconds between interrupts */ 137 tickfix = 1000000 - (hz * tick); 138 #ifdef NTP 139 fixtick = tickfix; 140 #endif 141 if (tickfix) { 142 int ftp; 143 144 ftp = min(ffs(tickfix), ffs(hz)); 145 tickfix >>= (ftp - 1); 146 tickfixinterval = hz >> (ftp - 1); 147 } 148 } 149 150 /* 151 * We assume newhz is either stathz or profhz, and that neither will 152 * change after being set up above. Could recalculate intervals here 153 * but that would be a drag. 154 */ 155 void 156 setstatclockrate(newhz) 157 int newhz; 158 { 159 160 /* nothing we can do */ 161 } 162 163 /* 164 * Experiments (and passing years) show that Decstation PROMS 165 * assume the kernel uses the clock chip as a time-of-year clock. 166 * The PROM assumes the clock is always set to 1972 or 1973, and contains 167 * time-of-year in seconds. The PROM checks the clock at boot time, 168 * and if it's outside that range, sets it to 1972-01-01. 169 * 170 * XXX should be at the mc146818 layer? 171 */ 172 173 /* 174 * Initialze the time of day register, based on the time base which is, e.g. 175 * from a filesystem. Base provides the time to within six months, 176 * and the time of year clock (if any) provides the rest. 177 */ 178 void 179 inittodr(base) 180 time_t base; 181 { 182 struct clocktime ct; 183 struct clock_ymdhms dt; 184 time_t yearsecs; 185 time_t deltat; 186 int badbase; 187 188 if (base < (MINYEAR-1970)*SECYR) { 189 printf("WARNING: preposterous time in file system"); 190 /* read the system clock anyway */ 191 base = (MINYEAR-1970)*SECYR; 192 badbase = 1; 193 } else 194 badbase = 0; 195 196 (*clockfns->cf_get)(clockdev, base, &ct); 197 #ifdef DEBUG 198 printf("readclock: %d/%d/%d/%d/%d/%d", ct.year, ct.mon, ct.day, 199 ct.hour, ct.min, ct.sec); 200 #endif 201 clockinitted = 1; 202 203 /* simple sanity checks */ 204 if (ct.year < 70 || ct.mon < 1 || ct.mon > 12 || ct.day < 1 || 205 ct.day > 31 || ct.hour > 23 || ct.min > 59 || ct.sec > 59) { 206 /* 207 * Believe the time in the file system for lack of 208 * anything better, resetting the TODR. 209 */ 210 time.tv_sec = base; 211 if (!badbase) { 212 printf("WARNING: preposterous clock chip time\n"); 213 resettodr(); 214 } 215 goto bad; 216 } 217 218 /* 219 * The clock lives in 1972 (leapyear!); 220 * calculate seconds relative to this year. 221 */ 222 dt.dt_year = 1972; 223 dt.dt_mon = ct.mon; 224 dt.dt_day = ct.day; 225 dt.dt_hour = ct.hour; 226 dt.dt_min = ct.min; 227 dt.dt_sec = ct.sec; 228 yearsecs = clock_ymdhms_to_secs(&dt) - (72 - 70) * SECYR; 229 230 /* 231 * Take the actual year from the filesystem if possible; 232 * allow for 2 days of clock loss and 363 days of clock gain. 233 */ 234 dt.dt_year = 1972; /* or MINYEAR or base/SECYR+1970 ... */ 235 dt.dt_mon = 1; 236 dt.dt_day = 1; 237 dt.dt_hour = 0; 238 dt.dt_min = 0; 239 dt.dt_sec = 0; 240 for(;;) { 241 time.tv_sec = yearsecs + clock_ymdhms_to_secs(&dt); 242 if (badbase || (time.tv_sec > base - 2 * SECDAY)) 243 break; 244 dt.dt_year++; 245 } 246 #ifdef DEBUG 247 printf("=>%ld (%ld)\n", time.tv_sec, base); 248 #endif 249 250 if (!badbase) { 251 /* 252 * See if we gained/lost two or more days; 253 * if so, assume something is amiss. 254 */ 255 deltat = time.tv_sec - base; 256 if (deltat < 0) 257 deltat = -deltat; 258 if (deltat < 2 * SECDAY) 259 return; 260 printf("WARNING: clock %s %d days", 261 time.tv_sec < base ? "lost" : "gained", 262 (int) (deltat / SECDAY)); 263 } 264 bad: 265 printf(" -- CHECK AND RESET THE DATE!\n"); 266 } 267 268 /* 269 * Reset the TODR based on the time value; used when the TODR 270 * has a preposterous value and also when the time is reset 271 * by the stime system call. Also called when the TODR goes past 272 * TODRZERO + 100*(SECYEAR+2*SECDAY) (e.g. on Jan 2 just after midnight) 273 * to wrap the TODR around. 274 */ 275 void 276 resettodr() 277 { 278 time_t yearsecs; 279 struct clock_ymdhms dt; 280 struct clocktime ct; 281 282 if (!clockinitted) 283 return; 284 285 /* 286 * calculate seconds relative to this year 287 */ 288 clock_secs_to_ymdhms(time.tv_sec, &dt); /* get the year */ 289 dt.dt_mon = 1; 290 dt.dt_day = 1; 291 dt.dt_hour = 0; 292 dt.dt_min = 0; 293 dt.dt_sec = 0; 294 yearsecs = time.tv_sec - clock_ymdhms_to_secs(&dt); 295 296 /* 297 * The clock lives in 1972 (leapyear!); calc fictious date. 298 */ 299 #define first72 ((72 - 70) * SECYR) 300 clock_secs_to_ymdhms(first72 + yearsecs, &dt); 301 302 #ifdef DEBUG 303 if (dt.dt_year != 1972) 304 printf("resettodr: botch (%ld, %ld)\n", yearsecs, time.tv_sec); 305 #endif 306 ct.year = dt.dt_year % 100; /* rt clock wants 2 digits */ 307 ct.mon = dt.dt_mon; 308 ct.day = dt.dt_day; 309 ct.hour = dt.dt_hour; 310 ct.min = dt.dt_min; 311 ct.sec = dt.dt_sec; 312 ct.dow = dt.dt_wday; 313 #ifdef DEBUG 314 printf("setclock: %d/%d/%d/%d/%d/%d\n", ct.year, ct.mon, ct.day, 315 ct.hour, ct.min, ct.sec); 316 #endif 317 318 (*clockfns->cf_set)(clockdev, &ct); 319 } 320