xref: /netbsd-src/sys/arch/sparc/sparc/clock.c (revision 38e52ce4ec6dae1c7ea45074e89bcb37955cddb2)
1 /*	$NetBSD: clock.c,v 1.104 2020/11/22 03:55:33 thorpej Exp $ */
2 
3 /*
4  * Copyright (c) 1992, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  * Copyright (c) 1994 Gordon W. Ross
7  * Copyright (c) 1993 Adam Glass
8  * Copyright (c) 1996 Paul Kranenburg
9  * Copyright (c) 1996
10  * 	The President and Fellows of Harvard College. All rights reserved.
11  *
12  * This software was developed by the Computer Systems Engineering group
13  * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
14  * contributed to Berkeley.
15  *
16  * All advertising materials mentioning features or use of this software
17  * must display the following acknowledgement:
18  *	This product includes software developed by Harvard University.
19  *	This product includes software developed by the University of
20  *	California, Lawrence Berkeley Laboratory.
21  *
22  * Redistribution and use in source and binary forms, with or without
23  * modification, are permitted provided that the following conditions
24  * are met:
25  *
26  * 1. Redistributions of source code must retain the above copyright
27  *    notice, this list of conditions and the following disclaimer.
28  * 2. Redistributions in binary form must reproduce the above copyright
29  *    notice, this list of conditions and the following disclaimer in the
30  *    documentation and/or other materials provided with the distribution.
31  * 3. All advertising materials mentioning features or use of this software
32  *    must display the following acknowledgement:
33  *	This product includes software developed by the University of
34  *	California, Berkeley and its contributors.
35  *	This product includes software developed by Paul Kranenburg.
36  *	This product includes software developed by Harvard University.
37  * 4. Neither the name of the University nor the names of its contributors
38  *    may be used to endorse or promote products derived from this software
39  *    without specific prior written permission.
40  *
41  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
42  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
43  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
44  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
45  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
46  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
47  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
48  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
49  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
50  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
51  * SUCH DAMAGE.
52  *
53  *	@(#)clock.c	8.1 (Berkeley) 6/11/93
54  *
55  */
56 
57 /*
58  * Common timer, clock and eeprom routines.
59  *
60  * Overview of timer and time-of-day devices on sparc machines:
61  *
62  * sun4/100 & sun4/200
63  *	have the Intersil 7170 time-of-day and timer chip (oclock.c)
64  *	eeprom device in OBIO space (eeprom.c)
65  *
66  * sun4/300 & sun4/400
67  *	Mostek MK48T02 clock/nvram device, includes eeprom (mkclock.c)
68  *	2 system timers (timer.c)
69  *
70  * sun4c
71  *	Mostek MK48T02 or MK48T08 clock/nvram device (mkclock.c)
72  *	system timer in OBIO space
73  *	2 system timers (timer.c)
74  *
75  * sun4m
76  *	Mostek MK48T08 clock/nvram device (mkclock.c)
77  *	1 global system timer (timer.c)
78  *	1 configurable counter/timer per CPU (timer.c)
79  *
80  * microSPARC-IIep:
81  *	DS1287A time-of-day chip on EBUS (dev/rtc.c)
82  *	the system timer is part of the PCI controller (timer.c)
83  *
84  * All system use the timer interrupt (at IPL 10) to drive hardclock().
85  * The second or per-CPU timer interrupt (at IPL 14) is used to drive
86  * statclock() (except on sun4/100 and sun4/200 machines, which don't
87  * have a spare timer device).
88  */
89 
90 #include <sys/cdefs.h>
91 __KERNEL_RCSID(0, "$NetBSD: clock.c,v 1.104 2020/11/22 03:55:33 thorpej Exp $");
92 
93 #include "opt_sparc_arch.h"
94 
95 #include <sys/param.h>
96 #include <sys/kernel.h>
97 #include <sys/device.h>
98 #include <sys/proc.h>
99 #include <sys/kmem.h>
100 #include <sys/systm.h>
101 #include <sys/timetc.h>
102 
103 #include <sys/bus.h>
104 #include <machine/autoconf.h>
105 #include <machine/eeprom.h>
106 #include <machine/cpu.h>
107 
108 #include <dev/clock_subr.h>
109 
110 /* Variables shared with timer.c, mkclock.c, oclock.c */
111 int timerblurb = 10;	/* Guess a value; used before clock is attached */
112 int oldclk = 0;
113 
114 void	(*timer_init)(void);	/* Called from cpu_initclocks() */
115 int	(*eeprom_nvram_wenable)(int);
116 
117 /*
118  * Statistics clock interval and variance, in usec.  Variance must be a
119  * power of two.  Since this gives us an even number, not an odd number,
120  * we discard one case and compensate.  That is, a variance of 1024 would
121  * give us offsets in [0..1023].  Instead, we take offsets in [1..1023].
122  * This is symmetric about the point 512, or statvar/2, and thus averages
123  * to that value (assuming uniform random numbers).
124  */
125 /* XXX fix comment to match value */
126 int statvar = 8192;
127 int statmin;			/* statclock interval - 1/2*variance */
128 int statint;
129 
130 
131 /*
132  * Common eeprom I/O routines.
133  */
134 char		*eeprom_va = NULL;
135 #if defined(SUN4)
136 static int	eeprom_busy = 0;
137 static int	eeprom_wanted = 0;
138 static int	eeprom_take(void);
139 static void	eeprom_give(void);
140 static int	eeprom_update(char *, int, int);
141 #endif
142 
143 /*
144  * Set up the real-time and statistics clocks.
145  * Leave stathz 0 only if no alternative timer is available.
146  *
147  * The frequencies of these clocks must be an even number of microseconds.
148  */
149 void
cpu_initclocks(void)150 cpu_initclocks(void)
151 {
152 	int minint;
153 
154 	if (1000000 % hz) {
155 		printf("cannot get %d Hz clock; using 100 Hz\n", hz);
156 		hz = 100;
157 		tick = 1000000 / hz;
158 	}
159 	if (stathz == 0)
160 		stathz = hz;
161 	if (1000000 % stathz) {
162 		printf("cannot get %d Hz statclock; using 100 Hz\n", stathz);
163 		stathz = 100;
164 	}
165 	profhz = stathz;		/* always */
166 
167 	statint = 1000000 / stathz;
168 	minint = statint / 2 + 100;
169 	while (statvar > minint)
170 		statvar >>= 1;
171 	statmin = statint - (statvar >> 1);
172 
173 	if (timer_init != NULL)
174 		(*timer_init)();
175 
176 	/*
177 	 * The scheduler clock runs every 8 statclock ticks,
178 	 * assuming stathz == 100. If it's not, compute a mask
179 	 * for use in the various statintr() functions approx.
180 	 * like this:
181 	 *	mask = round_power2(stathz / schedhz) - 1
182 	 */
183 	schedhz = 12;
184 }
185 
186 /*
187  * Dummy setstatclockrate(), since we know profhz==hz.
188  */
189 /* ARGSUSED */
190 void
setstatclockrate(int newhz)191 setstatclockrate(int newhz)
192 {
193 	/* nothing */
194 }
195 
196 
197 /*
198  * Scheduler pseudo-clock interrupt handler.
199  * Runs off a soft interrupt at IPL_SCHED, scheduled by statintr().
200  */
201 void
schedintr(void * v)202 schedintr(void *v)
203 {
204 
205 	schedclock(curlwp);
206 }
207 
208 /*
209  * XXX: these may actually belong somewhere else, but since the
210  * EEPROM is so closely tied to the clock on some models, perhaps
211  * it needs to stay here...
212  */
213 int
eeprom_uio(struct uio * uio)214 eeprom_uio(struct uio *uio)
215 {
216 #if defined(SUN4)
217 	int error;
218 	int off;	/* NOT off_t */
219 	u_int cnt, bcnt;
220 	char *buf = NULL;
221 
222 	if (!CPU_ISSUN4)
223 		return (ENODEV);
224 
225 	if (eeprom_va == NULL) {
226 		error = ENXIO;
227 		goto out;
228 	}
229 
230 	off = uio->uio_offset;
231 	if (off > EEPROM_SIZE)
232 		return (EFAULT);
233 
234 	cnt = uio->uio_resid;
235 	if (cnt > (EEPROM_SIZE - off))
236 		cnt = (EEPROM_SIZE - off);
237 
238 	if ((error = eeprom_take()) != 0)
239 		return (error);
240 
241 	/*
242 	 * The EEPROM can only be accessed one byte at a time, yet
243 	 * uiomove() will attempt long-word access.  To circumvent
244 	 * this, we byte-by-byte copy the eeprom contents into a
245 	 * temporary buffer.
246 	 */
247 	buf = kmem_alloc(EEPROM_SIZE, KM_SLEEP);
248 
249 	if (uio->uio_rw == UIO_READ)
250 		for (bcnt = 0; bcnt < EEPROM_SIZE; ++bcnt)
251 			buf[bcnt] = eeprom_va[bcnt];
252 
253 	if ((error = uiomove(buf + off, (int)cnt, uio)) != 0)
254 		goto out;
255 
256 	if (uio->uio_rw != UIO_READ)
257 		error = eeprom_update(buf, off, cnt);
258 
259  out:
260 	kmem_free(buf, EEPROM_SIZE);
261 	eeprom_give();
262 	return (error);
263 #else /* ! SUN4 */
264 	return (ENODEV);
265 #endif /* SUN4 */
266 }
267 
268 #if defined(SUN4)
269 /*
270  * Update the EEPROM from the passed buf.
271  */
272 static int
eeprom_update(char * buf,int off,int cnt)273 eeprom_update(char *buf, int off, int cnt)
274 {
275 	int error = 0;
276 	volatile char *ep;
277 	char *bp;
278 
279 	if (eeprom_va == NULL)
280 		return (ENXIO);
281 
282 	ep = eeprom_va + off;
283 	bp = buf + off;
284 
285 	if (eeprom_nvram_wenable != NULL)
286 		(*eeprom_nvram_wenable)(1);
287 
288 	while (cnt > 0) {
289 		/*
290 		 * DO NOT WRITE IT UNLESS WE HAVE TO because the
291 		 * EEPROM has a limited number of write cycles.
292 		 * After some number of writes it just fails!
293 		 */
294 		if (*ep != *bp) {
295 			*ep = *bp;
296 			/*
297 			 * We have written the EEPROM, so now we must
298 			 * sleep for at least 10 milliseconds while
299 			 * holding the lock to prevent all access to
300 			 * the EEPROM while it recovers.
301 			 */
302 			(void)tsleep(eeprom_va, PZERO - 1, "eeprom", hz/50);
303 		}
304 		/* Make sure the write worked. */
305 		if (*ep != *bp) {
306 			error = EIO;
307 			goto out;
308 		}
309 		++ep;
310 		++bp;
311 		--cnt;
312 	}
313  out:
314 	if (eeprom_nvram_wenable != NULL)
315 		(*eeprom_nvram_wenable)(0);
316 
317 	return (error);
318 }
319 
320 /* Take a lock on the eeprom. */
321 static int
eeprom_take(void)322 eeprom_take(void)
323 {
324 	int error = 0;
325 
326 	while (eeprom_busy) {
327 		eeprom_wanted = 1;
328 		error = tsleep(&eeprom_busy, PZERO | PCATCH, "eeprom", 0);
329 		eeprom_wanted = 0;
330 		if (error)	/* interrupted */
331 			goto out;
332 	}
333 	eeprom_busy = 1;
334  out:
335 	return (error);
336 }
337 
338 /* Give a lock on the eeprom away. */
339 static void
eeprom_give(void)340 eeprom_give(void)
341 {
342 
343 	eeprom_busy = 0;
344 	if (eeprom_wanted) {
345 		eeprom_wanted = 0;
346 		wakeup(&eeprom_busy);
347 	}
348 }
349 #endif /* SUN4 */
350