clock.c revision 1.9 1 /* $NetBSD: clock.c,v 1.9 1997/03/05 22:22:11 gwr Exp $ */
2
3 /*
4 * Copyright (c) 1994 Gordon W. Ross
5 * Copyright (c) 1993 Adam Glass
6 * Copyright (c) 1988 University of Utah.
7 * Copyright (c) 1982, 1990, 1993
8 * The Regents of the University of California. All rights reserved.
9 *
10 * This code is derived from software contributed to Berkeley by
11 * the Systems Programming Group of the University of Utah Computer
12 * Science Department.
13 *
14 * Redistribution and use in source and binary forms, with or without
15 * modification, are permitted provided that the following conditions
16 * are met:
17 * 1. Redistributions of source code must retain the above copyright
18 * notice, this list of conditions and the following disclaimer.
19 * 2. Redistributions in binary form must reproduce the above copyright
20 * notice, this list of conditions and the following disclaimer in the
21 * documentation and/or other materials provided with the distribution.
22 * 3. All advertising materials mentioning features or use of this software
23 * must display the following acknowledgement:
24 * This product includes software developed by the University of
25 * California, Berkeley and its contributors.
26 * 4. Neither the name of the University nor the names of its contributors
27 * may be used to endorse or promote products derived from this software
28 * without specific prior written permission.
29 *
30 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
31 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
32 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
34 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
35 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
36 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
37 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
38 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
39 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
40 * SUCH DAMAGE.
41 *
42 * from: Utah Hdr: clock.c 1.18 91/01/21$
43 * from: @(#)clock.c 8.2 (Berkeley) 1/12/94
44 */
45
46 /*
47 * Machine-dependent clock routines for the Mostek48t02
48 */
49
50 #include <sys/param.h>
51 #include <sys/systm.h>
52 #include <sys/time.h>
53 #include <sys/kernel.h>
54 #include <sys/device.h>
55
56 #include <m68k/asm_single.h>
57
58 #include <machine/autoconf.h>
59 #include <machine/cpu.h>
60 #include <machine/mon.h>
61 #include <machine/obio.h>
62 #include <machine/machdep.h>
63
64 #include <dev/clock_subr.h>
65
66 #include <sun3/sun3/interreg.h>
67 #include "mostek48t02.h"
68
69 #define CLOCK_PRI 5
70 #define IREG_CLK_BITS (IREG_CLOCK_ENAB_7 | IREG_CLOCK_ENAB_5)
71
72 void _isr_clock __P((void)); /* in locore.s */
73 void clock_intr __P((struct clockframe));
74
75 static volatile void *clock_va;
76
77 static int clock_match __P((struct device *, struct cfdata *, void *args));
78 static void clock_attach __P((struct device *, struct device *, void *));
79
80 struct cfattach clock_ca = {
81 sizeof(struct device), clock_match, clock_attach
82 };
83
84 struct cfdriver clock_cd = {
85 NULL, "clock", DV_DULL
86 };
87
88
89 /*
90 * This is called very early (by obio_init()) but after
91 * intreg_init() has found the PROM mapping for the
92 * interrupt register and cleared it.
93 */
94 void
95 clock_init()
96 {
97 /* Yes, use the EEPROM address. It is the same H/W device. */
98 clock_va = obio_find_mapping(OBIO_EEPROM, sizeof(struct clockreg));
99 if (!clock_va) {
100 mon_printf("clock_init\n");
101 sunmon_abort();
102 }
103 }
104
105 /*
106 * XXX Need to determine which type of clock we have!
107 * XXX The Sun3/80 always has the MK4802, while the
108 * XXX Sun3/470 can (reportedly) have that or the old
109 * XXX intersil7170. Should have two clock drivers...
110 */
111 static int
112 clock_match(parent, cf, args)
113 struct device *parent;
114 struct cfdata *cf;
115 void *args;
116 {
117 struct confargs *ca = args;
118
119 /* This driver only supports one unit. */
120 if (cf->cf_unit != 0)
121 return (0);
122
123 /* Validate the given address. */
124 if (ca->ca_paddr != OBIO_CLOCK2)
125 return (0);
126
127 /* Default interrupt priority. */
128 if (ca->ca_intpri == -1)
129 ca->ca_intpri = CLOCK_PRI;
130
131 return (1);
132 }
133
134 static void
135 clock_attach(parent, self, args)
136 struct device *parent;
137 struct device *self;
138 void *args;
139 {
140
141 printf("\n");
142
143 /*
144 * Can not hook up the ISR until cpu_initclocks()
145 * because hardclock is not ready until then.
146 * For now, the handler is _isr_autovec(), which
147 * will complain if it gets clock interrupts.
148 */
149 }
150
151 /*
152 * Set and/or clear the desired clock bits in the interrupt
153 * register. We have to be extremely careful that we do it
154 * in such a manner that we don't get ourselves lost.
155 * XXX: Watch out! It's really easy to break this!
156 */
157 void
158 set_clk_mode(on, off, enable_clk)
159 u_char on, off;
160 int enable_clk;
161 {
162 register u_char interreg;
163
164 /*
165 * If we have not yet mapped the register,
166 * then we do not want to do any of this...
167 */
168 if (!interrupt_reg)
169 return;
170
171 #ifdef DIAGNOSTIC
172 /* Assertion: were are at splhigh! */
173 if ((getsr() & PSL_IPL) < PSL_IPL7)
174 panic("set_clk_mode: bad ipl");
175 #endif
176
177 /*
178 * make sure that we are only playing w/
179 * clock interrupt register bits
180 */
181 on &= IREG_CLK_BITS;
182 off &= IREG_CLK_BITS;
183
184 /* First, turn off the "master" enable bit. */
185 single_inst_bclr_b(*interrupt_reg, IREG_ALL_ENAB);
186
187 /*
188 * Save the current interrupt register clock bits,
189 * and turn off/on the requested bits in the copy.
190 */
191 interreg = *interrupt_reg & IREG_CLK_BITS;
192 interreg &= ~off;
193 interreg |= on;
194
195 /* Clear the CLK5 and CLK7 bits to clear the flip-flops. */
196 single_inst_bclr_b(*interrupt_reg, IREG_CLK_BITS);
197
198 #ifdef SUN3_470
199 if (intersil_va) {
200 /*
201 * Then disable clock interrupts, and read the clock's
202 * interrupt register to clear any pending signals there.
203 */
204 intersil_clock->clk_cmd_reg =
205 intersil_command(INTERSIL_CMD_RUN, INTERSIL_CMD_IDISABLE);
206 intersil_clear();
207 }
208 #endif /* SUN3_470 */
209
210 /* Set the requested bits in the interrupt register. */
211 single_inst_bset_b(*interrupt_reg, interreg);
212
213 #ifdef SUN3_470
214 /* Turn the clock back on (maybe) */
215 if (intersil_va && enable_clk)
216 intersil_clock->clk_cmd_reg =
217 intersil_command(INTERSIL_CMD_RUN, INTERSIL_CMD_IENABLE);
218 #endif /* SUN3_470 */
219
220 /* Finally, turn the "master" enable back on. */
221 single_inst_bset_b(*interrupt_reg, IREG_ALL_ENAB);
222 }
223
224 /*
225 * Set up the real-time clock (enable clock interrupts).
226 * Leave stathz 0 since there is no secondary clock available.
227 * Note that clock interrupts MUST STAY DISABLED until here.
228 */
229 void
230 cpu_initclocks(void)
231 {
232 int s;
233
234 s = splhigh();
235
236 /* Install isr (in locore.s) that calls clock_intr(). */
237 isr_add_custom(5, (void*)_isr_clock);
238
239 /* Now enable the clock at level 5 in the interrupt reg. */
240 set_clk_mode(IREG_CLOCK_ENAB_5, 0, 1);
241
242 splx(s);
243 }
244
245 /*
246 * This doesn't need to do anything, as we have only one timer and
247 * profhz==stathz==hz.
248 */
249 void
250 setstatclockrate(newhz)
251 int newhz;
252 {
253 /* nothing */
254 }
255
256 /*
257 * This is is called by the "custom" interrupt handler.
258 * Note that we can get ZS interrupts while this runs,
259 * and zshard may touch the interrupt_reg, so we must
260 * be careful to use the single_inst_* macros to modify
261 * the interrupt register atomically.
262 */
263 void
264 clock_intr(cf)
265 struct clockframe cf;
266 {
267
268 /* Pulse the clock intr. enable low. */
269 single_inst_bclr_b(*interrupt_reg, IREG_CLOCK_ENAB_5);
270 single_inst_bset_b(*interrupt_reg, IREG_CLOCK_ENAB_5);
271
272 /* Call common clock interrupt handler. */
273 hardclock(&cf);
274
275 /* No LED frobbing on the 3/80 */
276 }
277
278
279 /*
280 * Return the best possible estimate of the time in the timeval
281 * to which tvp points. We do this by returning the current time
282 * plus the amount of time since the last clock interrupt.
283 *
284 * Check that this time is no less than any previously-reported time,
285 * which could happen around the time of a clock adjustment. Just for
286 * fun, we guarantee that the time will be greater than the value
287 * obtained by a previous call.
288 */
289 void
290 microtime(tvp)
291 register struct timeval *tvp;
292 {
293 int s = splhigh();
294 static struct timeval lasttime;
295
296 *tvp = time;
297 tvp->tv_usec++; /* XXX */
298 while (tvp->tv_usec > 1000000) {
299 tvp->tv_sec++;
300 tvp->tv_usec -= 1000000;
301 }
302 if (tvp->tv_sec == lasttime.tv_sec &&
303 tvp->tv_usec <= lasttime.tv_usec &&
304 (tvp->tv_usec = lasttime.tv_usec + 1) > 1000000)
305 {
306 tvp->tv_sec++;
307 tvp->tv_usec -= 1000000;
308 }
309 lasttime = *tvp;
310 splx(s);
311 }
312
313
314 /*
315 * Machine-dependent clock routines.
316 *
317 * Inittodr initializes the time of day hardware which provides
318 * date functions.
319 *
320 * Resettodr restores the time of day hardware after a time change.
321 */
322
323 static long clk_get_secs(void);
324 static void clk_set_secs(long);
325
326 /*
327 * Initialize the time of day register, based on the time base
328 * which is, e.g. from a filesystem.
329 */
330 void inittodr(fs_time)
331 time_t fs_time;
332 {
333 long diff, clk_time;
334 long long_ago = (5 * SECYR);
335 int clk_bad = 0;
336
337 /*
338 * Sanity check time from file system.
339 * If it is zero,assume filesystem time is just unknown
340 * instead of preposterous. Don't bark.
341 */
342 if (fs_time < long_ago) {
343 /*
344 * If fs_time is zero, assume filesystem time is just
345 * unknown instead of preposterous. Don't bark.
346 */
347 if (fs_time != 0)
348 printf("WARNING: preposterous time in file system\n");
349 /* 1991/07/01 12:00:00 */
350 fs_time = 21*SECYR + 186*SECDAY + SECDAY/2;
351 }
352
353 clk_time = clk_get_secs();
354
355 /* Sanity check time from clock. */
356 if (clk_time < long_ago) {
357 printf("WARNING: bad date in battery clock");
358 clk_bad = 1;
359 clk_time = fs_time;
360 } else {
361 /* Does the clock time jive with the file system? */
362 diff = clk_time - fs_time;
363 if (diff < 0)
364 diff = -diff;
365 if (diff >= (SECDAY*2)) {
366 printf("WARNING: clock %s %d days",
367 (clk_time < fs_time) ? "lost" : "gained",
368 (int) (diff / SECDAY));
369 clk_bad = 1;
370 }
371 }
372 if (clk_bad)
373 printf(" -- CHECK AND RESET THE DATE!\n");
374 time.tv_sec = clk_time;
375 }
376
377 /*
378 * Resettodr restores the time of day hardware after a time change.
379 */
380 void resettodr()
381 {
382 clk_set_secs(time.tv_sec);
383 }
384
385
386 /*
387 * Routines to copy state into and out of the clock.
388 * The clock CSR has to be set for read or write.
389 */
390 static void
391 clk_get_dt(struct clock_ymdhms *dt)
392 {
393 volatile struct clockreg *cl = clock_va;
394 int s;
395
396 s = splhigh();
397
398 /* enable read (stop time) */
399 cl->cl_csr |= CLK_READ;
400
401 /* Copy the info */
402 dt->dt_sec = cl->cl_sec;
403 dt->dt_min = cl->cl_min;
404 dt->dt_hour = cl->cl_hour;
405 dt->dt_wday = cl->cl_wday;
406 dt->dt_day = cl->cl_mday;
407 dt->dt_mon = cl->cl_month;
408 dt->dt_year = cl->cl_year;
409
410 /* Done reading (time wears on) */
411 cl->cl_csr &= ~CLK_READ;
412 splx(s);
413 }
414
415 static void
416 clk_set_dt(struct clock_ymdhms *dt)
417 {
418 volatile struct clockreg *cl = clock_va;
419 int s;
420
421 s = splhigh();
422 /* enable write */
423 cl->cl_csr |= CLK_WRITE;
424
425 /* Copy the info */
426 cl->cl_sec = dt->dt_sec;
427 cl->cl_min = dt->dt_min;
428 cl->cl_hour = dt->dt_hour;
429 cl->cl_wday = dt->dt_wday;
430 cl->cl_mday = dt->dt_day;
431 cl->cl_month = dt->dt_mon;
432 cl->cl_year = dt->dt_year;
433
434 /* load them up */
435 cl->cl_csr &= ~CLK_WRITE;
436 splx(s);
437 }
438
439
440 /*
441 * Now routines to get and set clock as POSIX time.
442 * Our clock keeps "years since 1/1/1968".
443 */
444 #define CLOCK_BASE_YEAR 1968
445
446 static long
447 clk_get_secs()
448 {
449 struct clock_ymdhms dt;
450 long secs;
451
452 clk_get_dt(&dt);
453
454 /* Convert BCD values to binary. */
455 dt.dt_sec = FROMBCD(dt.dt_sec);
456 dt.dt_min = FROMBCD(dt.dt_min);
457 dt.dt_hour = FROMBCD(dt.dt_hour);
458 dt.dt_day = FROMBCD(dt.dt_day);
459 dt.dt_mon = FROMBCD(dt.dt_mon);
460 dt.dt_year = FROMBCD(dt.dt_year);
461
462 if ((dt.dt_hour > 24) ||
463 (dt.dt_day > 31) ||
464 (dt.dt_mon > 12))
465 return (0);
466
467 dt.dt_year += CLOCK_BASE_YEAR;
468 secs = clock_ymdhms_to_secs(&dt);
469 return (secs);
470 }
471
472 static void
473 clk_set_secs(secs)
474 long secs;
475 {
476 struct clock_ymdhms dt;
477
478 clock_secs_to_ymdhms(secs, &dt);
479 dt.dt_year -= CLOCK_BASE_YEAR;
480
481 /* Convert binary values to BCD. */
482 dt.dt_sec = TOBCD(dt.dt_sec);
483 dt.dt_min = TOBCD(dt.dt_min);
484 dt.dt_hour = TOBCD(dt.dt_hour);
485 dt.dt_day = TOBCD(dt.dt_day);
486 dt.dt_mon = TOBCD(dt.dt_mon);
487 dt.dt_year = TOBCD(dt.dt_year);
488
489 clk_set_dt(&dt);
490 }
491