clock.c revision 1.4 1 /* $NetBSD: clock.c,v 1.4 1995/09/23 20:23:28 leo Exp $ */
2
3 /*
4 * Copyright (c) 1988 University of Utah.
5 * Copyright (c) 1982, 1990 The Regents of the University of California.
6 * All rights reserved.
7 *
8 * This code is derived from software contributed to Berkeley by
9 * the Systems Programming Group of the University of Utah Computer
10 * Science Department.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 * 3. All advertising materials mentioning features or use of this software
21 * must display the following acknowledgement:
22 * This product includes software developed by the University of
23 * California, Berkeley and its contributors.
24 * 4. Neither the name of the University nor the names of its contributors
25 * may be used to endorse or promote products derived from this software
26 * without specific prior written permission.
27 *
28 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
29 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
32 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38 * SUCH DAMAGE.
39 *
40 * from: Utah $Hdr: clock.c 1.18 91/01/21$
41 *
42 * @(#)clock.c 7.6 (Berkeley) 5/7/91
43 */
44
45 #include <sys/param.h>
46 #include <sys/kernel.h>
47 #include <sys/device.h>
48 #include <machine/psl.h>
49 #include <machine/cpu.h>
50 #include <machine/iomap.h>
51 #include <machine/mfp.h>
52 #include <atari/dev/clockreg.h>
53
54 #if defined(GPROF) && defined(PROFTIMER)
55 #include <machine/profile.h>
56 #endif
57
58 /*
59 * Machine-dependent clock routines.
60 *
61 * Startrtclock restarts the real-time clock, which provides
62 * hardclock interrupts to kern_clock.c.
63 *
64 * Inittodr initializes the time of day hardware which provides
65 * date functions.
66 *
67 * Resettodr restores the time of day hardware after a time change.
68 *
69 * A note on the real-time clock:
70 * We actually load the clock with CLK_INTERVAL-1 instead of CLK_INTERVAL.
71 * This is because the counter decrements to zero after N+1 enabled clock
72 * periods where N is the value loaded into the counter.
73 */
74
75 int clockmatch __P((struct device *, struct cfdata *, void *));
76 void clockattach __P((struct device *, struct device *, void *));
77
78 struct cfdriver clockcd = {
79 NULL, "clock", (cfmatch_t)clockmatch, clockattach,
80 DV_DULL, sizeof(struct device), NULL, 0
81 };
82
83 static u_long gettod __P((void));
84 static int settod __P((u_long));
85
86 static int divisor;
87
88 int
89 clockmatch(pdp, cfp, auxp)
90 struct device *pdp;
91 struct cfdata *cfp;
92 void *auxp;
93 {
94 if(!strcmp("clock", auxp))
95 return(1);
96 return(0);
97 }
98
99 /*
100 * Start the real-time clock.
101 */
102 void clockattach(pdp, dp, auxp)
103 struct device *pdp, *dp;
104 void *auxp;
105 {
106 /*
107 * Initialize Timer-A in the ST-MFP. We use a divisor of 200.
108 * The MFP clock runs at 2457600Hz. Therefore the timer runs
109 * at an effective rate of: 2457600/200 = 12288Hz. The
110 * following expression works for 48, 64 or 96 hz.
111 */
112 divisor = 12288/hz;
113 MFP->mf_tacr = 0; /* Stop timer */
114 MFP->mf_iera &= ~IA_TIMA; /* Disable timer interrupts */
115 MFP->mf_tadr = divisor; /* Set divisor */
116
117 printf(": system hz %d timer-A divisor 200/%d\n", hz, divisor);
118
119 /*
120 * Initialize Timer-B in the ST-MFP. This timer is used by the 'delay'
121 * function below. This time is setup to be continueously counting from
122 * 255 back to zero at a frequency of 614400Hz.
123 */
124 MFP->mf_tbcr = 0; /* Stop timer */
125 MFP->mf_iera &= ~IA_TIMB; /* Disable timer interrupts */
126 MFP->mf_tbdr = 0;
127 MFP->mf_tbcr = T_Q004; /* Start timer */
128
129 }
130
131 void cpu_initclocks()
132 {
133 MFP->mf_tacr = T_Q200; /* Start timer */
134 MFP->mf_ipra &= ~IA_TIMA; /* Clear pending interrupts */
135 MFP->mf_iera |= IA_TIMA; /* Enable timer interrupts */
136 MFP->mf_imra |= IA_TIMA; /* ..... */
137 }
138
139 setstatclockrate(hz)
140 int hz;
141 {
142 }
143
144 /*
145 * Returns number of usec since last recorded clock "tick"
146 * (i.e. clock interrupt).
147 */
148 clkread()
149 {
150 u_int delta;
151
152 delta = ((divisor - MFP->mf_tadr) * tick) / divisor;
153 /*
154 * Account for pending clock interrupts
155 */
156 if(MFP->mf_iera & IA_TIMA)
157 return(delta + tick);
158 return(delta);
159 }
160
161 #define TIMB_FREQ 614400
162 #define TIMB_LIMIT 256
163
164 /*
165 * Wait "n" microseconds.
166 * Relies on MFP-Timer B counting down from TIMB_LIMIT at TIMB_FREQ Hz.
167 * Note: timer had better have been programmed before this is first used!
168 */
169 void delay(n)
170 int n;
171 {
172 int tick, otick;
173
174 /*
175 * Read the counter first, so that the rest of the setup overhead is
176 * counted.
177 */
178 otick = MFP->mf_tbdr;
179
180 /*
181 * Calculate ((n * TIMER_FREQ) / 1e6) using explicit assembler code so
182 * we can take advantage of the intermediate 64-bit quantity to prevent
183 * loss of significance.
184 */
185 n -= 5;
186 if(n < 0)
187 return;
188 {
189 u_int temp;
190
191 __asm __volatile ("mulul %2,%1:%0" : "=d" (n), "=d" (temp)
192 : "d" (TIMB_FREQ));
193 __asm __volatile ("divul %1,%2:%0" : "=d" (n)
194 : "d"(1000000),"d"(temp),"0"(n));
195 }
196
197 while(n > 0) {
198 tick = MFP->mf_tbdr;
199 if(tick > otick)
200 n -= TIMB_LIMIT - (tick - otick);
201 else n -= otick - tick;
202 otick = tick;
203 }
204 }
205
206 #ifdef PROFTIMER
207 /*
208 * This code allows the amiga kernel to use one of the extra timers on
209 * the clock chip for profiling, instead of the regular system timer.
210 * The advantage of this is that the profiling timer can be turned up to
211 * a higher interrupt rate, giving finer resolution timing. The profclock
212 * routine is called from the lev6intr in locore, and is a specialized
213 * routine that calls addupc. The overhead then is far less than if
214 * hardclock/softclock was called. Further, the context switch code in
215 * locore has been changed to turn the profile clock on/off when switching
216 * into/out of a process that is profiling (startprofclock/stopprofclock).
217 * This reduces the impact of the profiling clock on other users, and might
218 * possibly increase the accuracy of the profiling.
219 */
220 int profint = PRF_INTERVAL; /* Clock ticks between interrupts */
221 int profscale = 0; /* Scale factor from sys clock to prof clock */
222 char profon = 0; /* Is profiling clock on? */
223
224 /* profon values - do not change, locore.s assumes these values */
225 #define PRF_NONE 0x00
226 #define PRF_USER 0x01
227 #define PRF_KERNEL 0x80
228
229 initprofclock()
230 {
231 #if NCLOCK > 0
232 struct proc *p = curproc; /* XXX */
233
234 /*
235 * If the high-res timer is running, force profiling off.
236 * Unfortunately, this gets reflected back to the user not as
237 * an error but as a lack of results.
238 */
239 if (clockon) {
240 p->p_stats->p_prof.pr_scale = 0;
241 return;
242 }
243 /*
244 * Keep track of the number of user processes that are profiling
245 * by checking the scale value.
246 *
247 * XXX: this all assumes that the profiling code is well behaved;
248 * i.e. profil() is called once per process with pcscale non-zero
249 * to turn it on, and once with pcscale zero to turn it off.
250 * Also assumes you don't do any forks or execs. Oh well, there
251 * is always adb...
252 */
253 if (p->p_stats->p_prof.pr_scale)
254 profprocs++;
255 else
256 profprocs--;
257 #endif
258 /*
259 * The profile interrupt interval must be an even divisor
260 * of the CLK_INTERVAL so that scaling from a system clock
261 * tick to a profile clock tick is possible using integer math.
262 */
263 if (profint > CLK_INTERVAL || (CLK_INTERVAL % profint) != 0)
264 profint = CLK_INTERVAL;
265 profscale = CLK_INTERVAL / profint;
266 }
267
268 startprofclock()
269 {
270 unsigned short interval;
271
272 /* stop timer B */
273 ciab.crb = ciab.crb & 0xc0;
274
275 /* load interval into registers.
276 the clocks run at NTSC: 715.909kHz or PAL: 709.379kHz */
277
278 interval = profint - 1;
279
280 /* order of setting is important ! */
281 ciab.tblo = interval & 0xff;
282 ciab.tbhi = interval >> 8;
283
284 /* enable interrupts for timer B */
285 ciab.icr = (1<<7) | (1<<1);
286
287 /* start timer B in continuous shot mode */
288 ciab.crb = (ciab.crb & 0xc0) | 1;
289 }
290
291 stopprofclock()
292 {
293 /* stop timer B */
294 ciab.crb = ciab.crb & 0xc0;
295 }
296
297 #ifdef GPROF
298 /*
299 * profclock() is expanded in line in lev6intr() unless profiling kernel.
300 * Assumes it is called with clock interrupts blocked.
301 */
302 profclock(pc, ps)
303 caddr_t pc;
304 int ps;
305 {
306 /*
307 * Came from user mode.
308 * If this process is being profiled record the tick.
309 */
310 if (USERMODE(ps)) {
311 if (p->p_stats.p_prof.pr_scale)
312 addupc(pc, &curproc->p_stats.p_prof, 1);
313 }
314 /*
315 * Came from kernel (supervisor) mode.
316 * If we are profiling the kernel, record the tick.
317 */
318 else if (profiling < 2) {
319 register int s = pc - s_lowpc;
320
321 if (s < s_textsize)
322 kcount[s / (HISTFRACTION * sizeof (*kcount))]++;
323 }
324 /*
325 * Kernel profiling was on but has been disabled.
326 * Mark as no longer profiling kernel and if all profiling done,
327 * disable the clock.
328 */
329 if (profiling && (profon & PRF_KERNEL)) {
330 profon &= ~PRF_KERNEL;
331 if (profon == PRF_NONE)
332 stopprofclock();
333 }
334 }
335 #endif
336 #endif
337
338 /*
339 * Initialize the time of day register, based on the time base which is, e.g.
340 * from a filesystem.
341 */
342 inittodr(base)
343 time_t base;
344 {
345 u_long timbuf = base; /* assume no battery clock exists */
346
347 timbuf = gettod();
348
349 if(timbuf < base) {
350 printf("WARNING: bad date in battery clock\n");
351 timbuf = base;
352 }
353
354 /* Battery clock does not store usec's, so forget about it. */
355 time.tv_sec = timbuf;
356 }
357
358 resettodr()
359 {
360 if(settod(time.tv_sec) == 1)
361 return;
362 printf("Cannot set battery backed clock\n");
363 }
364
365 static char dmsize[12] =
366 {
367 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
368 };
369
370 static char ldmsize[12] =
371 {
372 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
373 };
374
375 static u_long
376 gettod()
377 {
378 int i, sps;
379 u_long new_time = 0;
380 char *msize;
381 mc_todregs clkregs;
382
383 sps = splhigh();
384 MC146818_GETTOD(RTC, &clkregs);
385 splx(sps);
386
387 if(range_test(clkregs[MC_HOUR], 0, 23))
388 return(0);
389 if(range_test(clkregs[MC_DOM], 1, 31))
390 return(0);
391 if (range_test(clkregs[MC_MONTH], 1, 12))
392 return(0);
393 if(range_test(clkregs[MC_YEAR], 0, 2000 - GEMSTARTOFTIME))
394 return(0);
395 clkregs[MC_YEAR] += GEMSTARTOFTIME;
396
397 for(i = BSDSTARTOFTIME; i < clkregs[MC_YEAR]; i++) {
398 if(is_leap(i))
399 new_time += 366;
400 else new_time += 365;
401 }
402
403 msize = is_leap(clkregs[MC_YEAR]) ? ldmsize : dmsize;
404 for(i = 0; i < (clkregs[MC_MONTH] - 1); i++)
405 new_time += msize[i];
406 new_time += clkregs[MC_DOM] - 1;
407 new_time *= SECS_DAY;
408 new_time += (clkregs[MC_HOUR] * 3600) + (clkregs[MC_MIN] * 60);
409 return(new_time + clkregs[MC_SEC]);
410 }
411
412 static int
413 settod(newtime)
414 u_long newtime;
415 {
416 register long days, rem, year;
417 register char *ml;
418 int sps, sec, min, hour, month;
419 mc_todregs clkregs;
420
421 /* Number of days since Jan. 1 'BSDSTARTOFTIME' */
422 days = newtime / SECS_DAY;
423 rem = newtime % SECS_DAY;
424
425 /*
426 * Calculate sec, min, hour
427 */
428 hour = rem / SECS_HOUR;
429 rem %= SECS_HOUR;
430 min = rem / 60;
431 sec = rem % 60;
432
433 /*
434 * Figure out the year. Day in year is left in 'days'.
435 */
436 year = BSDSTARTOFTIME;
437 while(days >= (rem = is_leap(year) ? 366 : 365)) {
438 ++year;
439 days -= rem;
440 }
441
442 /*
443 * Determine the month
444 */
445 ml = is_leap(year) ? ldmsize : dmsize;
446 for(month = 0; days >= ml[month]; ++month)
447 days -= ml[month];
448
449 /*
450 * Now that everything is calculated, program the RTC
451 */
452 mc146818_write(RTC, MC_REGA, MC_BASE_32_KHz);
453 mc146818_write(RTC, MC_REGB, MC_REGB_24HR | MC_REGB_BINARY);
454 sps = splhigh();
455 MC146818_GETTOD(RTC, &clkregs);
456 clkregs[MC_SEC] = sec;
457 clkregs[MC_MIN] = min;
458 clkregs[MC_HOUR] = hour;
459 clkregs[MC_DOM] = days+1;
460 clkregs[MC_MONTH] = month+1;
461 clkregs[MC_YEAR] = year - GEMSTARTOFTIME;
462 MC146818_PUTTOD(RTC, &clkregs);
463 splx(sps);
464
465 return(1);
466 }
467