1 /* $OpenBSD: clock.c,v 1.22 2016/02/26 02:07:03 mmcc 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 the pointer 130 * to the virtual timers wasn'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 */ 216 if (year < MINYEAR || ct.mon < 1 || ct.mon > 12 || ct.day < 1 || 217 ct.day > 31 || ct.hour > 23 || ct.min > 59 || ct.sec > 59) { 218 /* 219 * Believe the time in the file system for lack of 220 * anything better, resetting the TODR. 221 */ 222 ts.tv_sec = base; 223 if (!badbase) { 224 printf("WARNING: preposterous clock chip time\n"); 225 resettodr(); 226 } 227 goto bad; 228 } 229 230 dt.dt_year = year; 231 dt.dt_mon = ct.mon; 232 dt.dt_day = ct.day; 233 dt.dt_hour = ct.hour; 234 dt.dt_min = ct.min; 235 dt.dt_sec = ct.sec; 236 ts.tv_sec = clock_ymdhms_to_secs(&dt); 237 #ifdef DEBUG 238 printf("=>%ld (%d)\n", ts.tv_sec, base); 239 #endif 240 241 if (!badbase) { 242 /* 243 * See if we gained/lost two or more days; 244 * if so, assume something is amiss. 245 */ 246 deltat = ts.tv_sec - base; 247 if (deltat < 0) 248 deltat = -deltat; 249 if (deltat < 2 * SECDAY) { 250 tc_setclock(&ts); 251 return; 252 } 253 printf("WARNING: clock %s %ld days", 254 ts.tv_sec < base ? "lost" : "gained", 255 (long)deltat / SECDAY); 256 } 257 bad: 258 tc_setclock(&ts); 259 printf(" -- CHECK AND RESET THE DATE!\n"); 260 } 261 262 /* 263 * Reset the TODR based on the time value; used when the TODR 264 * has a preposterous value and also when the time is reset 265 * by the stime system call. Also called when the TODR goes past 266 * TODRZERO + 100*(SECYEAR+2*SECDAY) (e.g. on Jan 2 just after midnight) 267 * to wrap the TODR around. 268 */ 269 void 270 resettodr() 271 { 272 struct clock_ymdhms dt; 273 struct clocktime ct; 274 275 if (!clockinitted) 276 return; 277 278 clock_secs_to_ymdhms(time_second, &dt); 279 280 /* rt clock wants 2 digits */ 281 ct.year = (dt.dt_year - UNIX_YEAR_OFFSET) % 100; 282 ct.mon = dt.dt_mon; 283 ct.day = dt.dt_day; 284 ct.hour = dt.dt_hour; 285 ct.min = dt.dt_min; 286 ct.sec = dt.dt_sec; 287 ct.dow = dt.dt_wday; 288 #ifdef DEBUG 289 printf("setclock: %d/%d/%d/%d/%d/%d\n", ct.year, ct.mon, ct.day, 290 ct.hour, ct.min, ct.sec); 291 #endif 292 293 (*clockfns->cf_set)(clockdev, &ct); 294 } 295 296 u_int 297 rpcc_get_timecount(struct timecounter *tc) 298 { 299 return alpha_rpcc(); 300 } 301