1 /*
2 * Copyright (c) 1988 University of Utah.
3 * Copyright (c) 1992, 1993
4 * The Regents of the University of California. 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 and Ralph Campbell.
9 *
10 * %sccs.include.redist.c%
11 *
12 * from: Utah $Hdr: clock.c 1.18 91/01/21$
13 *
14 * @(#)clock.c 8.2 (Berkeley) 10/09/94
15 */
16
17 #include <sys/param.h>
18 #include <sys/kernel.h>
19
20 #include <machine/machConst.h>
21 #include <pmax/pmax/clockreg.h>
22
23 /*
24 * Machine-dependent clock routines.
25 *
26 * Startrtclock restarts the real-time clock, which provides
27 * hardclock interrupts to kern_clock.c.
28 *
29 * Inittodr initializes the time of day hardware which provides
30 * date functions. Its primary function is to use some file
31 * system information in case the hardare clock lost state.
32 *
33 * Resettodr restores the time of day hardware after a time change.
34 */
35
36 volatile struct chiptime *Mach_clock_addr;
37
38 /*
39 * Start the real-time and statistics clocks. Leave stathz 0 since there
40 * are no other timers available.
41 */
cpu_initclocks()42 cpu_initclocks()
43 {
44 register volatile struct chiptime *c;
45 extern int tickadj;
46
47 tick = 15625; /* number of micro-seconds between interrupts */
48 hz = 1000000 / 15625; /* 64 Hz */
49 tickadj = 240000 / (60000000 / 15625);
50 c = Mach_clock_addr;
51 c->rega = REGA_TIME_BASE | SELECTED_RATE;
52 c->regb = REGB_PER_INT_ENA | REGB_DATA_MODE | REGB_HOURS_FORMAT;
53 }
54
55 /*
56 * We assume newhz is either stathz or profhz, and that neither will
57 * change after being set up above. Could recalculate intervals here
58 * but that would be a drag.
59 */
60 void
setstatclockrate(newhz)61 setstatclockrate(newhz)
62 int newhz;
63 {
64 }
65
66 /*
67 * This is the amount to add to the value stored in the clock chip
68 * to get the current year.
69 */
70 #define YR_OFFSET 22
71
72 /*
73 * This code is defunct after 2099.
74 * Will Unix still be here then??
75 */
76 static short dayyr[12] = {
77 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
78 };
79
80 /*
81 * Initialze the time of day register, based on the time base which is, e.g.
82 * from a filesystem. Base provides the time to within six months,
83 * and the time of year clock (if any) provides the rest.
84 */
85 void
inittodr(base)86 inittodr(base)
87 time_t base;
88 {
89 register volatile struct chiptime *c;
90 register int days, yr;
91 int sec, min, hour, day, mon, year;
92 long deltat;
93 int badbase, s;
94
95 if (base < 5*SECYR) {
96 printf("WARNING: preposterous time in file system");
97 /* read the system clock anyway */
98 base = 6*SECYR + 186*SECDAY + SECDAY/2;
99 badbase = 1;
100 } else
101 badbase = 0;
102
103 c = Mach_clock_addr;
104 /* don't read clock registers while they are being updated */
105 s = splclock();
106 while ((c->rega & REGA_UIP) == 1)
107 ;
108 sec = c->sec;
109 min = c->min;
110 hour = c->hour;
111 day = c->day;
112 mon = c->mon;
113 year = c->year + YR_OFFSET;
114 splx(s);
115
116 /* simple sanity checks */
117 if (year < 70 || mon < 1 || mon > 12 || day < 1 || day > 31 ||
118 hour > 23 || min > 59 || sec > 59) {
119 /*
120 * Believe the time in the file system for lack of
121 * anything better, resetting the TODR.
122 */
123 time.tv_sec = base;
124 if (!badbase) {
125 printf("WARNING: preposterous clock chip time");
126 resettodr();
127 }
128 goto bad;
129 }
130 days = 0;
131 for (yr = 70; yr < year; yr++)
132 days += LEAPYEAR(yr) ? 366 : 365;
133 days += dayyr[mon - 1] + day - 1;
134 if (LEAPYEAR(yr) && mon > 2)
135 days++;
136 /* now have days since Jan 1, 1970; the rest is easy... */
137 time.tv_sec = days * SECDAY + hour * 3600 + min * 60 + sec;
138
139 if (!badbase) {
140 /*
141 * See if we gained/lost two or more days;
142 * if so, assume something is amiss.
143 */
144 deltat = time.tv_sec - base;
145 if (deltat < 0)
146 deltat = -deltat;
147 if (deltat < 2 * SECDAY)
148 return;
149 printf("WARNING: clock %s %d days",
150 time.tv_sec < base ? "lost" : "gained", deltat / SECDAY);
151 }
152 bad:
153 printf(" -- CHECK AND RESET THE DATE!\n");
154 }
155
156 /*
157 * Reset the TODR based on the time value; used when the TODR
158 * has a preposterous value and also when the time is reset
159 * by the stime system call. Also called when the TODR goes past
160 * TODRZERO + 100*(SECYEAR+2*SECDAY) (e.g. on Jan 2 just after midnight)
161 * to wrap the TODR around.
162 */
resettodr()163 resettodr()
164 {
165 register volatile struct chiptime *c;
166 register int t, t2;
167 int sec, min, hour, day, mon, year;
168 int s;
169
170 /* compute the year */
171 t2 = time.tv_sec / SECDAY;
172 year = 69;
173 while (t2 >= 0) { /* whittle off years */
174 t = t2;
175 year++;
176 t2 -= LEAPYEAR(year) ? 366 : 365;
177 }
178
179 /* t = month + day; separate */
180 t2 = LEAPYEAR(year);
181 for (mon = 1; mon < 12; mon++)
182 if (t < dayyr[mon] + (t2 && mon > 1))
183 break;
184
185 day = t - dayyr[mon - 1] + 1;
186 if (t2 && mon > 2)
187 day--;
188
189 /* the rest is easy */
190 t = time.tv_sec % SECDAY;
191 hour = t / 3600;
192 t %= 3600;
193 min = t / 60;
194 sec = t % 60;
195
196 c = Mach_clock_addr;
197 s = splclock();
198 t = c->regb;
199 c->regb = t | REGB_SET_TIME;
200 MachEmptyWriteBuffer();
201 c->sec = sec;
202 c->min = min;
203 c->hour = hour;
204 c->day = day;
205 c->mon = mon;
206 c->year = year - YR_OFFSET;
207 c->regb = t;
208 MachEmptyWriteBuffer();
209 splx(s);
210 }
211