1 /* 2 * Copyright (c) 1988 University of Utah. 3 * Copyright (c) 1992 The Regents of the University of California. 4 * All rights reserved. 5 * 6 * This code is derived from software contributed to Berkeley by 7 * the Systems Programming Group of the University of Utah Computer 8 * Science Department, Ralph Campbell, and Kazumasa Utashiro of 9 * Software Research Associates, Inc. 10 * 11 * %sccs.include.redist.c% 12 * 13 * from: Utah $Hdr: clock.c 1.18 91/01/21$ 14 * 15 * @(#)clock.c 7.3 (Berkeley) 07/28/92 16 */ 17 18 #include "../include/fix_machine_type.h" 19 #include "../include/adrsmap.h" 20 21 #include "param.h" 22 #include "kernel.h" 23 24 #include "clockreg.h" 25 26 /* 27 * Machine-dependent clock routines. 28 * 29 * Startrtclock restarts the real-time clock, which provides 30 * hardclock interrupts to kern_clock.c. 31 * 32 * Inittodr initializes the time of day hardware which provides 33 * date functions. Its primary function is to use some file 34 * system information in case the hardare clock lost state. 35 * 36 * Resettodr restores the time of day hardware after a time change. 37 */ 38 39 /* 40 * Set up the real-time and statistics clocks. Leave stathz 0 only if 41 * no alternative timer is available. 42 * 43 */ 44 cpu_initclocks() 45 { 46 47 startrtclock(); 48 enablertclock(); 49 } 50 51 /* 52 * We assume newhz is either stathz or profhz, and that neither will 53 * change after being set up above. Could recalculate intervals here 54 * but that would be a drag. 55 */ 56 void 57 setstatclockrate(newhz) 58 int newhz; 59 { 60 61 /* KU:XXX do something! */ 62 } 63 64 /* 65 * Start the real-time clock. 66 */ 67 static void 68 startrtclock() 69 { 70 71 *(char *)ITIMER = IOCLOCK / 6144 / 100 - 1; 72 } 73 74 /* 75 * Enable the real-time clock. 76 */ 77 static void 78 enablertclock() 79 { 80 81 *(char *)INTEN0 |= (char)INTEN0_TIMINT; 82 } 83 84 /* 85 * This code is defunct after 2099. 86 * Will Unix still be here then?? 87 */ 88 static short dayyr[12] = { 89 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 90 }; 91 92 #define bcd_to_int(BCD) (i = BCD, (((i) >> 4) & 0xf) * 10 + ((i) & 0xf)) 93 #define int_to_bcd(INT) (i = INT, ((((i) / 10) % 10) << 4) + (i) % 10) 94 95 /* 96 * Initialze the time of day register, based on the time base which is, e.g. 97 * from a filesystem. Base provides the time to within six months, 98 * and the time of year clock (if any) provides the rest. 99 */ 100 inittodr(base) 101 time_t base; 102 { 103 register volatile u_char *rtc_port = (u_char *)RTC_PORT; 104 register volatile u_char *rtc_data = (u_char *)DATA_PORT; 105 register int days, yr; 106 int sec, min, hour, week, day, mon, year; 107 long deltat, badbase = 0; 108 register u_int i; 109 110 if (base < 5*SECYR) { 111 printf("WARNING: preposterous time in file system\n"); 112 /* read the system clock anyway */ 113 base = 6*SECYR + 186*SECDAY + SECDAY/2; 114 badbase = 1; 115 } 116 117 *rtc_port = READ_CLOCK; 118 sec = bcd_to_int(*rtc_data++); 119 min = bcd_to_int(*rtc_data++); 120 hour = bcd_to_int(*rtc_data++); 121 week = bcd_to_int(*rtc_data++); 122 day = bcd_to_int(*rtc_data++); 123 mon = bcd_to_int(*rtc_data++); 124 year = bcd_to_int(*rtc_data++); 125 *rtc_port = 0; 126 127 /* simple sanity checks */ 128 if (year < 70 || mon < 1 || mon > 12 || day < 1 || day > 31 || 129 hour > 23 || min > 59 || sec > 59) { 130 printf("WARNING: preposterous clock chip time\n"); 131 /* 132 * Believe the time in the file system for lack of 133 * anything better, resetting the TODR. 134 */ 135 time.tv_sec = base; 136 if (!badbase) 137 resettodr(); 138 return (0); 139 } 140 days = 0; 141 for (yr = 70; yr < year; yr++) 142 days += LEAPYEAR(yr) ? 366 : 365; 143 days += dayyr[mon - 1] + day - 1; 144 if (LEAPYEAR(yr) && mon > 2) 145 days++; 146 /* now have days since Jan 1, 1970; the rest is easy... */ 147 time.tv_sec = days * SECDAY + hour * 3600 + min * 60 + sec; 148 149 if (!badbase) { 150 /* 151 * See if we gained/lost two or more days; 152 * if so, assume something is amiss. 153 */ 154 deltat = time.tv_sec - base; 155 if (deltat < 0) 156 deltat = -deltat; 157 if (deltat < 2 * SECDAY) 158 return; 159 printf("WARNING: clock %s %d days", 160 time.tv_sec < base ? "lost" : "gained", deltat / SECDAY); 161 } 162 printf(" -- CHECK AND RESET THE DATE!\n"); 163 } 164 165 /* 166 * Reset the TODR based on the time value; used when the TODR 167 * has a preposterous value and also when the time is reset 168 * by the stime system call. Also called when the TODR goes past 169 * TODRZERO + 100*(SECYEAR+2*SECDAY) (e.g. on Jan 2 just after midnight) 170 * to wrap the TODR around. 171 */ 172 resettodr() 173 { 174 register volatile u_char *rtc_port = (u_char *)RTC_PORT; 175 register volatile u_char *rtc_data = (u_char *)DATA_PORT; 176 int sec, min, hour, week, day, mon, year; 177 register int t, t2, t3; 178 register int i; 179 180 /* compute the year */ 181 t2 = time.tv_sec / SECDAY; 182 t = 69; 183 while (t2 >= 0) { /* whittle off years */ 184 t3 = t2; 185 t++; 186 t2 -= LEAPYEAR(t) ? 366 : 365; 187 } 188 189 year = t; 190 191 /* t3 = month + day; separate */ 192 t = LEAPYEAR(t); 193 for (t2 = 1; t2 < 12; t2++) 194 if (t3 < dayyr[t2] + (t && t2 > 1)) 195 break; 196 197 /* t2 is month */ 198 mon = t2; 199 t3 = t3 - dayyr[t2 - 1] + 1; 200 if (t && t2 > 2) 201 t3--; 202 day = t3; 203 204 week = 0; 205 206 /* the rest is easy */ 207 t = time.tv_sec % SECDAY; 208 hour = t / 3600; 209 t %= 3600; 210 min = t / 60; 211 sec = t % 60; 212 213 *rtc_port = SET_CLOCK; 214 *rtc_data++ = int_to_bcd(sec); 215 *rtc_data++ = int_to_bcd(min); 216 *rtc_data++ = int_to_bcd(hour); 217 *rtc_data++ = int_to_bcd(week); 218 *rtc_data++ = int_to_bcd(day); 219 *rtc_data++ = int_to_bcd(mon); 220 *rtc_data = int_to_bcd(year); 221 *rtc_port = 0; 222 } 223