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.2 (Berkeley) 06/25/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 * Start the real-time clock. 41 */ 42 startrtclock() 43 { 44 45 *(char *)ITIMER = IOCLOCK / 6144 / 100 - 1; 46 } 47 48 /* 49 * Enable the real-time clock. 50 */ 51 enablertclock() 52 { 53 54 *(char *)INTEN0 |= (char)INTEN0_TIMINT; 55 } 56 57 /* 58 * This code is defunct after 2099. 59 * Will Unix still be here then?? 60 */ 61 static short dayyr[12] = { 62 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 63 }; 64 65 #define bcd_to_int(BCD) (i = BCD, (((i) >> 4) & 0xf) * 10 + ((i) & 0xf)) 66 #define int_to_bcd(INT) (i = INT, ((((i) / 10) % 10) << 4) + (i) % 10) 67 68 /* 69 * Initialze the time of day register, based on the time base which is, e.g. 70 * from a filesystem. Base provides the time to within six months, 71 * and the time of year clock (if any) provides the rest. 72 */ 73 inittodr(base) 74 time_t base; 75 { 76 register volatile u_char *rtc_port = (u_char *)RTC_PORT; 77 register volatile u_char *rtc_data = (u_char *)DATA_PORT; 78 register int days, yr; 79 int sec, min, hour, week, day, mon, year; 80 long deltat, badbase = 0; 81 register u_int i; 82 83 if (base < 5*SECYR) { 84 printf("WARNING: preposterous time in file system\n"); 85 /* read the system clock anyway */ 86 base = 6*SECYR + 186*SECDAY + SECDAY/2; 87 badbase = 1; 88 } 89 90 *rtc_port = READ_CLOCK; 91 sec = bcd_to_int(*rtc_data++); 92 min = bcd_to_int(*rtc_data++); 93 hour = bcd_to_int(*rtc_data++); 94 week = bcd_to_int(*rtc_data++); 95 day = bcd_to_int(*rtc_data++); 96 mon = bcd_to_int(*rtc_data++); 97 year = bcd_to_int(*rtc_data++); 98 *rtc_port = 0; 99 100 /* simple sanity checks */ 101 if (year < 70 || mon < 1 || mon > 12 || day < 1 || day > 31 || 102 hour > 23 || min > 59 || sec > 59) { 103 printf("WARNING: preposterous clock chip time\n"); 104 /* 105 * Believe the time in the file system for lack of 106 * anything better, resetting the TODR. 107 */ 108 time.tv_sec = base; 109 if (!badbase) 110 resettodr(); 111 return (0); 112 } 113 days = 0; 114 for (yr = 70; yr < year; yr++) 115 days += LEAPYEAR(yr) ? 366 : 365; 116 days += dayyr[mon - 1] + day - 1; 117 if (LEAPYEAR(yr) && mon > 2) 118 days++; 119 /* now have days since Jan 1, 1970; the rest is easy... */ 120 time.tv_sec = days * SECDAY + hour * 3600 + min * 60 + sec; 121 122 if (!badbase) { 123 /* 124 * See if we gained/lost two or more days; 125 * if so, assume something is amiss. 126 */ 127 deltat = time.tv_sec - base; 128 if (deltat < 0) 129 deltat = -deltat; 130 if (deltat < 2 * SECDAY) 131 return; 132 printf("WARNING: clock %s %d days", 133 time.tv_sec < base ? "lost" : "gained", deltat / SECDAY); 134 } 135 printf(" -- CHECK AND RESET THE DATE!\n"); 136 } 137 138 /* 139 * Reset the TODR based on the time value; used when the TODR 140 * has a preposterous value and also when the time is reset 141 * by the stime system call. Also called when the TODR goes past 142 * TODRZERO + 100*(SECYEAR+2*SECDAY) (e.g. on Jan 2 just after midnight) 143 * to wrap the TODR around. 144 */ 145 resettodr() 146 { 147 register volatile u_char *rtc_port = (u_char *)RTC_PORT; 148 register volatile u_char *rtc_data = (u_char *)DATA_PORT; 149 int sec, min, hour, week, day, mon, year; 150 register int t, t2, t3; 151 register int i; 152 153 /* compute the year */ 154 t2 = time.tv_sec / SECDAY; 155 t = 69; 156 while (t2 >= 0) { /* whittle off years */ 157 t3 = t2; 158 t++; 159 t2 -= LEAPYEAR(t) ? 366 : 365; 160 } 161 162 year = t; 163 164 /* t3 = month + day; separate */ 165 t = LEAPYEAR(t); 166 for (t2 = 1; t2 < 12; t2++) 167 if (t3 < dayyr[t2] + (t && t2 > 1)) 168 break; 169 170 /* t2 is month */ 171 mon = t2; 172 t3 = t3 - dayyr[t2 - 1] + 1; 173 if (t && t2 > 2) 174 t3--; 175 day = t3; 176 177 week = 0; 178 179 /* the rest is easy */ 180 t = time.tv_sec % SECDAY; 181 hour = t / 3600; 182 t %= 3600; 183 min = t / 60; 184 sec = t % 60; 185 186 *rtc_port = SET_CLOCK; 187 *rtc_data++ = int_to_bcd(sec); 188 *rtc_data++ = int_to_bcd(min); 189 *rtc_data++ = int_to_bcd(hour); 190 *rtc_data++ = int_to_bcd(week); 191 *rtc_data++ = int_to_bcd(day); 192 *rtc_data++ = int_to_bcd(mon); 193 *rtc_data = int_to_bcd(year); 194 *rtc_port = 0; 195 } 196