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