xref: /netbsd/sys/arch/sun3/sun3/clock.c (revision bf9ec67e)
1 /*	$NetBSD: clock.c,v 1.46 2001/09/05 13:21:09 tsutsui 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 Intersil 7170:
48  * Original by Adam Glass;  partially rewritten by Gordon Ross.
49  */
50 
51 #include <sys/param.h>
52 #include <sys/systm.h>
53 #include <sys/time.h>
54 #include <sys/kernel.h>
55 #include <sys/device.h>
56 
57 #include <m68k/asm_single.h>
58 
59 #include <machine/autoconf.h>
60 #include <machine/cpu.h>
61 #include <machine/leds.h>
62 
63 #include <sun3/sun3/control.h>
64 #include <sun3/sun3/interreg.h>
65 #include <sun3/sun3/machdep.h>
66 
67 #include <dev/clock_subr.h>
68 #include <dev/ic/intersil7170.h>
69 
70 #define	CLOCK_PRI	5
71 #define IREG_CLK_BITS	(IREG_CLOCK_ENAB_7 | IREG_CLOCK_ENAB_5)
72 
73 void _isr_clock __P((void));	/* in locore.s */
74 void clock_intr __P((struct clockframe));
75 
76 static volatile void *intersil_va;
77 
78 #define intersil_clock ((volatile struct intersil7170 *) intersil_va)
79 
80 #define intersil_command(run, interrupt) \
81 	(run | interrupt | INTERSIL_CMD_FREQ_32K | INTERSIL_CMD_24HR_MODE | \
82 	 INTERSIL_CMD_NORMAL_MODE)
83 
84 #define intersil_clear() (void)intersil_clock->clk_intr_reg
85 
86 static int  clock_match __P((struct device *, struct cfdata *, void *args));
87 static void clock_attach __P((struct device *, struct device *, void *));
88 
89 struct cfattach clock_ca = {
90 	sizeof(struct device), clock_match, clock_attach
91 };
92 
93 static int
94 clock_match(parent, cf, args)
95     struct device *parent;
96 	struct cfdata *cf;
97     void *args;
98 {
99 	struct confargs *ca = args;
100 
101 	/* This driver only supports one unit. */
102 	if (intersil_va)
103 		return (0);
104 
105 	/* Make sure there is something there... */
106 	if (bus_peek(ca->ca_bustype, ca->ca_paddr, 1) == -1)
107 		return (0);
108 
109 	/* Default interrupt priority. */
110 	if (ca->ca_intpri == -1)
111 		ca->ca_intpri = CLOCK_PRI;
112 
113 	return (1);
114 }
115 
116 static void
117 clock_attach(parent, self, args)
118 	struct device *parent;
119 	struct device *self;
120 	void *args;
121 {
122 	struct confargs *ca = args;
123 	caddr_t va;
124 
125 	printf("\n");
126 
127 	/* Get a mapping for it. */
128 	va = bus_mapin(ca->ca_bustype,
129 	    ca->ca_paddr, sizeof(struct intersil7170));
130 	if (!va)
131 		panic("clock_attach");
132 	intersil_va = va;
133 
134 	/*
135 	 * Set the clock to the correct interrupt rate, but
136 	 * do not enable the interrupt until cpu_initclocks.
137 	 * XXX: Actually, the interrupt_reg should be zero
138 	 * at this point, so the clock interrupts should not
139 	 * affect us, but we need to set the rate...
140 	 */
141 	intersil_clock->clk_cmd_reg =
142 		intersil_command(INTERSIL_CMD_RUN, INTERSIL_CMD_IDISABLE);
143 	intersil_clear();
144 
145 	/* Set the clock to 100 Hz, but do not enable it yet. */
146 	intersil_clock->clk_intr_reg = INTERSIL_INTER_CSECONDS;
147 
148 	/*
149 	 * Can not hook up the ISR until cpu_initclocks()
150 	 * because hardclock is not ready until then.
151 	 * For now, the handler is _isr_autovec(), which
152 	 * will complain if it gets clock interrupts.
153 	 */
154 }
155 
156 /*
157  * Set and/or clear the desired clock bits in the interrupt
158  * register.  We have to be extremely careful that we do it
159  * in such a manner that we don't get ourselves lost.
160  * XXX:  Watch out!  It's really easy to break this!
161  */
162 void
163 set_clk_mode(on, off, enable_clk)
164 	u_char on, off;
165 	int enable_clk;
166 {
167 	u_char interreg;
168 
169 	/*
170 	 * If we have not yet mapped the register,
171 	 * then we do not want to do any of this...
172 	 */
173 	if (!interrupt_reg)
174 		return;
175 
176 #ifdef	DIAGNOSTIC
177 	/* Assertion: were are at splhigh! */
178 	if ((getsr() & PSL_IPL) < PSL_IPL7)
179 		panic("set_clk_mode: bad ipl");
180 #endif
181 
182 	/*
183 	 * make sure that we are only playing w/
184 	 * clock interrupt register bits
185 	 */
186 	on  &= IREG_CLK_BITS;
187 	off &= IREG_CLK_BITS;
188 
189 	/* First, turn off the "master" enable bit. */
190 	single_inst_bclr_b(*interrupt_reg, IREG_ALL_ENAB);
191 
192 	/*
193 	 * Save the current interrupt register clock bits,
194 	 * and turn off/on the requested bits in the copy.
195 	 */
196 	interreg = *interrupt_reg & IREG_CLK_BITS;
197 	interreg &= ~off;
198 	interreg |= on;
199 
200 	/* Clear the CLK5 and CLK7 bits to clear the flip-flops. */
201 	single_inst_bclr_b(*interrupt_reg, IREG_CLK_BITS);
202 
203 	if (intersil_va) {
204 		/*
205 		 * Then disable clock interrupts, and read the clock's
206 		 * interrupt register to clear any pending signals there.
207 		 */
208 		intersil_clock->clk_cmd_reg =
209 			intersil_command(INTERSIL_CMD_RUN, INTERSIL_CMD_IDISABLE);
210 		intersil_clear();
211 	}
212 
213 	/* Set the requested bits in the interrupt register. */
214 	single_inst_bset_b(*interrupt_reg, interreg);
215 
216 	/* Turn the clock back on (maybe) */
217 	if (intersil_va && enable_clk)
218 		intersil_clock->clk_cmd_reg =
219 			intersil_command(INTERSIL_CMD_RUN, INTERSIL_CMD_IENABLE);
220 
221 	/* Finally, turn the "master" enable back on. */
222 	single_inst_bset_b(*interrupt_reg, IREG_ALL_ENAB);
223 }
224 
225 /*
226  * Set up the real-time clock (enable clock interrupts).
227  * Leave stathz 0 since there is no secondary clock available.
228  * Note that clock interrupts MUST STAY DISABLED until here.
229  */
230 void
231 cpu_initclocks(void)
232 {
233 	int s;
234 
235 	s = splhigh();
236 
237 	/* Install isr (in locore.s) that calls clock_intr(). */
238 	isr_add_custom(5, (void*)_isr_clock);
239 
240 	/* Now enable the clock at level 5 in the interrupt reg. */
241 	set_clk_mode(IREG_CLOCK_ENAB_5, 0, 1);
242 
243 	splx(s);
244 }
245 
246 /*
247  * This doesn't need to do anything, as we have only one timer and
248  * profhz==stathz==hz.
249  */
250 void
251 setstatclockrate(newhz)
252 	int newhz;
253 {
254 	/* nothing */
255 }
256 
257 /*
258  * This is is called by the "custom" interrupt handler.
259  * Note that we can get ZS interrupts while this runs,
260  * and zshard may touch the interrupt_reg, so we must
261  * be careful to use the single_inst_* macros to modify
262  * the interrupt register atomically.
263  */
264 void
265 clock_intr(cf)
266 	struct clockframe cf;
267 {
268 
269 	/* Read the clock interrupt register. */
270 	intersil_clear();
271 
272 	/* Pulse the clock intr. enable low. */
273 	single_inst_bclr_b(*interrupt_reg, IREG_CLOCK_ENAB_5);
274 	single_inst_bset_b(*interrupt_reg, IREG_CLOCK_ENAB_5);
275 
276 	/* Read the clock intr. reg. AGAIN! */
277 	intersil_clear();
278 
279 
280 	{ /* Entertainment! */
281 #ifdef	LED_IDLE_CHECK
282 		/* With this option, LEDs move only when CPU is idle. */
283 		extern char _Idle[];	/* locore.s */
284 		if (cf.cf_pc == (long)_Idle)
285 #endif
286 			leds_intr();
287 	}
288 
289 	/* Call common clock interrupt handler. */
290 	hardclock(&cf);
291 }
292 
293 
294 /*
295  * Return the best possible estimate of the time in the timeval
296  * to which tvp points.  We do this by returning the current time
297  * plus the amount of time since the last clock interrupt.
298  *
299  * Check that this time is no less than any previously-reported time,
300  * which could happen around the time of a clock adjustment.  Just for
301  * fun, we guarantee that the time will be greater than the value
302  * obtained by a previous call.
303  */
304 void
305 microtime(tvp)
306 	struct timeval *tvp;
307 {
308 	int s = splhigh();
309 	static struct timeval lasttime;
310 
311 	*tvp = time;
312 	tvp->tv_usec++; 	/* XXX */
313 	while (tvp->tv_usec >= 1000000) {
314 		tvp->tv_sec++;
315 		tvp->tv_usec -= 1000000;
316 	}
317 	if (tvp->tv_sec == lasttime.tv_sec &&
318 		tvp->tv_usec <= lasttime.tv_usec &&
319 		(tvp->tv_usec = lasttime.tv_usec + 1) >= 1000000)
320 	{
321 		tvp->tv_sec++;
322 		tvp->tv_usec -= 1000000;
323 	}
324 	lasttime = *tvp;
325 	splx(s);
326 }
327 
328 
329 /*
330  * Machine-dependent clock routines.
331  *
332  * Inittodr initializes the time of day hardware which provides
333  * date functions.
334  *
335  * Resettodr restores the time of day hardware after a time change.
336  */
337 
338 static long clk_get_secs __P((void));
339 static void clk_set_secs __P((long));
340 
341 /*
342  * Initialize the time of day register, based on the time base
343  * which is, e.g. from a filesystem.
344  */
345 void inittodr(fs_time)
346 	time_t fs_time;
347 {
348 	long diff, clk_time;
349 	long long_ago = (5 * SECYR);
350 	int clk_bad = 0;
351 
352 	/*
353 	 * Sanity check time from file system.
354 	 * If it is zero,assume filesystem time is just unknown
355 	 * instead of preposterous.  Don't bark.
356 	 */
357 	if (fs_time < long_ago) {
358 		/*
359 		 * If fs_time is zero, assume filesystem time is just
360 		 * unknown instead of preposterous.  Don't bark.
361 		 */
362 		if (fs_time != 0)
363 			printf("WARNING: preposterous time in file system\n");
364 		/* 1991/07/01  12:00:00 */
365 		fs_time = 21*SECYR + 186*SECDAY + SECDAY/2;
366 	}
367 
368 	clk_time = clk_get_secs();
369 
370 	/* Sanity check time from clock. */
371 	if (clk_time < long_ago) {
372 		printf("WARNING: bad date in battery clock");
373 		clk_bad = 1;
374 		clk_time = fs_time;
375 	} else {
376 		/* Does the clock time jive with the file system? */
377 		diff = clk_time - fs_time;
378 		if (diff < 0)
379 			diff = -diff;
380 		if (diff >= (SECDAY*2)) {
381 			printf("WARNING: clock %s %d days",
382 				   (clk_time < fs_time) ? "lost" : "gained",
383 				   (int) (diff / SECDAY));
384 			clk_bad = 1;
385 		}
386 	}
387 	if (clk_bad)
388 		printf(" -- CHECK AND RESET THE DATE!\n");
389 	time.tv_sec = clk_time;
390 }
391 
392 /*
393  * Resettodr restores the time of day hardware after a time change.
394  */
395 void resettodr()
396 {
397 	clk_set_secs(time.tv_sec);
398 }
399 
400 
401 /*
402  * Now routines to get and set clock as POSIX time.
403  * Our clock keeps "years since 1/1/1968".
404  */
405 #define	CLOCK_BASE_YEAR 1968
406 static void intersil_get_dt __P((struct clock_ymdhms *));
407 static void intersil_set_dt __P((struct clock_ymdhms *));
408 
409 static long
410 clk_get_secs()
411 {
412 	struct clock_ymdhms dt;
413 	long secs;
414 
415 	intersil_get_dt(&dt);
416 
417 	if ((dt.dt_hour > 24) ||
418 		(dt.dt_day  > 31) ||
419 		(dt.dt_mon  > 12))
420 		return (0);
421 
422 	dt.dt_year += CLOCK_BASE_YEAR;
423 	secs = clock_ymdhms_to_secs(&dt);
424 	return (secs);
425 }
426 
427 static void
428 clk_set_secs(secs)
429 	long secs;
430 {
431 	struct clock_ymdhms dt;
432 
433 	clock_secs_to_ymdhms(secs, &dt);
434 	dt.dt_year -= CLOCK_BASE_YEAR;
435 
436 	intersil_set_dt(&dt);
437 }
438 
439 
440 /*
441  * Routines to copy state into and out of the clock.
442  * The intersil registers have to be read or written
443  * in sequential order (or so it appears). -gwr
444  */
445 static void
446 intersil_get_dt(struct clock_ymdhms *dt)
447 {
448 	volatile struct intersil_dt *isdt;
449 	int s;
450 
451 	isdt = &intersil_clock->counters;
452 	s = splhigh();
453 
454 	/* Enable read (stop time) */
455 	intersil_clock->clk_cmd_reg =
456 		intersil_command(INTERSIL_CMD_STOP, INTERSIL_CMD_IENABLE);
457 
458 	/* Copy the info.  Careful about the order! */
459 	dt->dt_sec  = isdt->dt_csec;  /* throw-away */
460 	dt->dt_hour = isdt->dt_hour;
461 	dt->dt_min  = isdt->dt_min;
462 	dt->dt_sec  = isdt->dt_sec;
463 	dt->dt_mon  = isdt->dt_month;
464 	dt->dt_day  = isdt->dt_day;
465 	dt->dt_year = isdt->dt_year;
466 	dt->dt_wday = isdt->dt_dow;
467 
468 	/* Done reading (time wears on) */
469 	intersil_clock->clk_cmd_reg =
470 		intersil_command(INTERSIL_CMD_RUN, INTERSIL_CMD_IENABLE);
471 	splx(s);
472 }
473 
474 static void
475 intersil_set_dt(struct clock_ymdhms *dt)
476 {
477 	volatile struct intersil_dt *isdt;
478 	int s;
479 
480 	isdt = &intersil_clock->counters;
481 	s = splhigh();
482 
483 	/* Enable write (stop time) */
484 	intersil_clock->clk_cmd_reg =
485 		intersil_command(INTERSIL_CMD_STOP, INTERSIL_CMD_IENABLE);
486 
487 	/* Copy the info.  Careful about the order! */
488 	isdt->dt_csec = 0;
489 	isdt->dt_hour = dt->dt_hour;
490 	isdt->dt_min  = dt->dt_min;
491 	isdt->dt_sec  = dt->dt_sec;
492 	isdt->dt_month= dt->dt_mon;
493 	isdt->dt_day  = dt->dt_day;
494 	isdt->dt_year = dt->dt_year;
495 	isdt->dt_dow  = dt->dt_wday;
496 
497 	/* Done writing (time wears on) */
498 	intersil_clock->clk_cmd_reg =
499 		intersil_command(INTERSIL_CMD_RUN, INTERSIL_CMD_IENABLE);
500 	splx(s);
501 }
502