clock.c revision 1.2 1 /* $NetBSD: clock.c,v 1.2 1995/05/05 16:31:46 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(PROF) && defined(PROFTIMER)
55 #include <sys/PROF.h>
56 #endif
57
58
59 /*
60 * Machine-dependent clock routines.
61 *
62 * Startrtclock restarts the real-time clock, which provides
63 * hardclock interrupts to kern_clock.c.
64 *
65 * Inittodr initializes the time of day hardware which provides
66 * date functions.
67 *
68 * Resettodr restores the time of day hardware after a time change.
69 *
70 * A note on the real-time clock:
71 * We actually load the clock with CLK_INTERVAL-1 instead of CLK_INTERVAL.
72 * This is because the counter decrements to zero after N+1 enabled clock
73 * periods where N is the value loaded into the counter.
74 */
75
76 int clockmatch __P((struct device *, struct cfdata *, void *));
77 void clockattach __P((struct device *, struct device *, void *));
78
79 struct cfdriver clockcd = {
80 NULL, "clock", (cfmatch_t)clockmatch, clockattach,
81 DV_DULL, sizeof(struct device), NULL, 0
82 };
83
84 static u_long gettod __P((void));
85 static int settod __P((u_long));
86
87 static int divisor;
88
89 int
90 clockmatch(pdp, cfp, auxp)
91 struct device *pdp;
92 struct cfdata *cfp;
93 void *auxp;
94 {
95 if(!strcmp("clock", auxp))
96 return(1);
97 return(0);
98 }
99
100 /*
101 * Start the real-time clock.
102 */
103 void clockattach(pdp, dp, auxp)
104 struct device *pdp, *dp;
105 void *auxp;
106 {
107 /*
108 * Initialize Timer-A in the ST-MFP. An exact reduce to HZ is not
109 * possible by hardware. We use a divisor of 64 and reduce by software
110 * with a factor of 4. The MFP clock runs at 2457600Hz. Therefore the
111 * timer runs at an effective rate of: 2457600/(64*4) = 9600Hz. The
112 * following expression works for all 'normal' values of hz.
113 */
114 divisor = 9600/hz;
115 MFP->mf_tacr = 0; /* Stop timer */
116 MFP->mf_iera &= ~IA_TIMA; /* Disable timer interrupts */
117 MFP->mf_tadr = divisor; /* Set divisor */
118
119 printf(": system hz %d timer-A divisor %d\n", hz, divisor);
120
121 /*
122 * Initialize Timer-B in the ST-MFP. This timer is used by the 'delay'
123 * function below. This time is setup to be continueously counting from
124 * 255 back to zero at a frequency of 614400Hz.
125 */
126 MFP->mf_tbcr = 0; /* Stop timer */
127 MFP->mf_iera &= ~IA_TIMB; /* Disable timer interrupts */
128 MFP->mf_tbdr = 0;
129 MFP->mf_tbcr = T_Q004; /* Start timer */
130
131 }
132
133 void cpu_initclocks()
134 {
135 MFP->mf_tacr = T_Q064; /* Start timer */
136 MFP->mf_ipra &= ~IA_TIMA; /* Clear pending interrupts */
137 MFP->mf_iera |= IA_TIMA; /* Enable timer interrupts */
138 MFP->mf_imra |= IA_TIMA; /* ..... */
139 }
140
141 setstatclockrate(hz)
142 int hz;
143 {
144 }
145
146 /*
147 * Returns number of usec since last recorded clock "tick"
148 * (i.e. clock interrupt).
149 */
150 clkread()
151 {
152 extern short clk_div;
153 u_int delta, elapsed;
154
155 elapsed = (divisor - MFP->mf_tadr) + ((4 - clk_div) * divisor);
156 delta = (elapsed * tick) / (divisor << 2);
157
158 /*
159 * Account for pending clock interrupts
160 */
161 if(MFP->mf_iera & IA_TIMA)
162 return(delta + tick);
163 return(delta);
164 }
165
166 #define TIMB_FREQ 614400
167 #define TIMB_LIMIT 256
168
169 /*
170 * Wait "n" microseconds.
171 * Relies on MFP-Timer B counting down from TIMB_LIMIT at TIMB_FREQ Hz.
172 * Note: timer had better have been programmed before this is first used!
173 */
174 void delay(n)
175 int n;
176 {
177 int tick, otick;
178
179 /*
180 * Read the counter first, so that the rest of the setup overhead is
181 * counted.
182 */
183 otick = MFP->mf_tbdr;
184
185 /*
186 * Calculate ((n * TIMER_FREQ) / 1e6) using explicit assembler code so
187 * we can take advantage of the intermediate 64-bit quantity to prevent
188 * loss of significance.
189 */
190 n -= 5;
191 if(n < 0)
192 return;
193 {
194 u_int temp;
195
196 __asm __volatile ("mulul %2,%1:%0" : "=d" (n), "=d" (temp)
197 : "d" (TIMB_FREQ));
198 __asm __volatile ("divul %1,%2:%0" : "=d" (n)
199 : "d"(1000000),"d"(temp),"0"(n));
200 }
201
202 while(n > 0) {
203 tick = MFP->mf_tbdr;
204 if(tick > otick)
205 n -= TIMB_LIMIT - (tick - otick);
206 else n -= otick - tick;
207 otick = tick;
208 }
209 }
210
211 #ifdef PROFTIMER
212 /*
213 * This code allows the amiga kernel to use one of the extra timers on
214 * the clock chip for profiling, instead of the regular system timer.
215 * The advantage of this is that the profiling timer can be turned up to
216 * a higher interrupt rate, giving finer resolution timing. The profclock
217 * routine is called from the lev6intr in locore, and is a specialized
218 * routine that calls addupc. The overhead then is far less than if
219 * hardclock/softclock was called. Further, the context switch code in
220 * locore has been changed to turn the profile clock on/off when switching
221 * into/out of a process that is profiling (startprofclock/stopprofclock).
222 * This reduces the impact of the profiling clock on other users, and might
223 * possibly increase the accuracy of the profiling.
224 */
225 int profint = PRF_INTERVAL; /* Clock ticks between interrupts */
226 int profscale = 0; /* Scale factor from sys clock to prof clock */
227 char profon = 0; /* Is profiling clock on? */
228
229 /* profon values - do not change, locore.s assumes these values */
230 #define PRF_NONE 0x00
231 #define PRF_USER 0x01
232 #define PRF_KERNEL 0x80
233
234 initprofclock()
235 {
236 #if NCLOCK > 0
237 struct proc *p = curproc; /* XXX */
238
239 /*
240 * If the high-res timer is running, force profiling off.
241 * Unfortunately, this gets reflected back to the user not as
242 * an error but as a lack of results.
243 */
244 if (clockon) {
245 p->p_stats->p_prof.pr_scale = 0;
246 return;
247 }
248 /*
249 * Keep track of the number of user processes that are profiling
250 * by checking the scale value.
251 *
252 * XXX: this all assumes that the profiling code is well behaved;
253 * i.e. profil() is called once per process with pcscale non-zero
254 * to turn it on, and once with pcscale zero to turn it off.
255 * Also assumes you don't do any forks or execs. Oh well, there
256 * is always adb...
257 */
258 if (p->p_stats->p_prof.pr_scale)
259 profprocs++;
260 else
261 profprocs--;
262 #endif
263 /*
264 * The profile interrupt interval must be an even divisor
265 * of the CLK_INTERVAL so that scaling from a system clock
266 * tick to a profile clock tick is possible using integer math.
267 */
268 if (profint > CLK_INTERVAL || (CLK_INTERVAL % profint) != 0)
269 profint = CLK_INTERVAL;
270 profscale = CLK_INTERVAL / profint;
271 }
272
273 startprofclock()
274 {
275 unsigned short interval;
276
277 /* stop timer B */
278 ciab.crb = ciab.crb & 0xc0;
279
280 /* load interval into registers.
281 the clocks run at NTSC: 715.909kHz or PAL: 709.379kHz */
282
283 interval = profint - 1;
284
285 /* order of setting is important ! */
286 ciab.tblo = interval & 0xff;
287 ciab.tbhi = interval >> 8;
288
289 /* enable interrupts for timer B */
290 ciab.icr = (1<<7) | (1<<1);
291
292 /* start timer B in continuous shot mode */
293 ciab.crb = (ciab.crb & 0xc0) | 1;
294 }
295
296 stopprofclock()
297 {
298 /* stop timer B */
299 ciab.crb = ciab.crb & 0xc0;
300 }
301
302 #ifdef PROF
303 /*
304 * profclock() is expanded in line in lev6intr() unless profiling kernel.
305 * Assumes it is called with clock interrupts blocked.
306 */
307 profclock(pc, ps)
308 caddr_t pc;
309 int ps;
310 {
311 /*
312 * Came from user mode.
313 * If this process is being profiled record the tick.
314 */
315 if (USERMODE(ps)) {
316 if (p->p_stats.p_prof.pr_scale)
317 addupc(pc, &curproc->p_stats.p_prof, 1);
318 }
319 /*
320 * Came from kernel (supervisor) mode.
321 * If we are profiling the kernel, record the tick.
322 */
323 else if (profiling < 2) {
324 register int s = pc - s_lowpc;
325
326 if (s < s_textsize)
327 kcount[s / (HISTFRACTION * sizeof (*kcount))]++;
328 }
329 /*
330 * Kernel profiling was on but has been disabled.
331 * Mark as no longer profiling kernel and if all profiling done,
332 * disable the clock.
333 */
334 if (profiling && (profon & PRF_KERNEL)) {
335 profon &= ~PRF_KERNEL;
336 if (profon == PRF_NONE)
337 stopprofclock();
338 }
339 }
340 #endif
341 #endif
342
343 /*
344 * Initialize the time of day register, based on the time base which is, e.g.
345 * from a filesystem.
346 */
347 inittodr(base)
348 time_t base;
349 {
350 u_long timbuf = base; /* assume no battery clock exists */
351
352 timbuf = gettod();
353
354 if(timbuf < base) {
355 printf("WARNING: bad date in battery clock\n");
356 timbuf = base;
357 }
358
359 /* Battery clock does not store usec's, so forget about it. */
360 time.tv_sec = timbuf;
361 }
362
363 resettodr()
364 {
365 if(settod(time.tv_sec) == 1)
366 return;
367 printf("Cannot set battery backed clock\n");
368 }
369
370 static char dmsize[12] =
371 {
372 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
373 };
374
375 static char ldmsize[12] =
376 {
377 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
378 };
379
380 static __inline__ int rtc_getclkreg(regno)
381 int regno;
382 {
383 RTC->rtc_regno = RTC_REGA;
384 RTC->rtc_regno = regno;
385 return(RTC->rtc_data & 0377);
386 }
387
388 static __inline__ void rtc_setclkreg(regno, value)
389 int regno, value;
390 {
391 RTC->rtc_regno = regno;
392 RTC->rtc_data = value;
393 }
394
395 static u_long
396 gettod()
397 {
398 int i, year, mon, day, hour, min, sec;
399 u_long new_time = 0;
400 char *msize;
401
402 /*
403 * Hold clock
404 */
405 rtc_setclkreg(RTC_REGB, rtc_getclkreg(RTC_REGB) | RTC_B_SET);
406
407 /*
408 * Read clock
409 */
410 sec = rtc_getclkreg(RTC_SEC);
411 min = rtc_getclkreg(RTC_MIN);
412 hour = rtc_getclkreg(RTC_HOUR);
413 day = rtc_getclkreg(RTC_DAY) - 1;
414 mon = rtc_getclkreg(RTC_MONTH) - 1;
415 year = rtc_getclkreg(RTC_YEAR) + STARTOFTIME;
416
417 /*
418 * Let it run again..
419 */
420 rtc_setclkreg(RTC_REGB, rtc_getclkreg(RTC_REGB) & ~RTC_B_SET);
421
422 if(range_test(hour, 0, 23))
423 return(0);
424 if(range_test(day, 0, 30))
425 return(0);
426 if (range_test(mon, 0, 11))
427 return(0);
428 if(range_test(year, STARTOFTIME, 2000))
429 return(0);
430
431 for(i = STARTOFTIME; i < year; i++) {
432 if(is_leap(i))
433 new_time += 366;
434 else new_time += 365;
435 }
436
437 msize = is_leap(year) ? ldmsize : dmsize;
438 for(i = 0; i < mon; i++)
439 new_time += msize[i];
440 new_time += day;
441 return((new_time * SECS_DAY) + (hour * 3600) + (min * 60) + sec);
442 }
443
444 static int
445 settod(newtime)
446 u_long newtime;
447 {
448 register long days, rem, year;
449 register char *ml;
450 int sec, min, hour, month;
451
452 /* Number of days since Jan. 1 1970 */
453 days = newtime / SECS_DAY;
454 rem = newtime % SECS_DAY;
455
456 /*
457 * Calculate sec, min, hour
458 */
459 hour = rem / SECS_HOUR;
460 rem %= SECS_HOUR;
461 min = rem / 60;
462 sec = rem % 60;
463
464 /*
465 * Figure out the year. Day in year is left in 'days'.
466 */
467 year = STARTOFTIME;
468 while(days >= (rem = is_leap(year) ? 366 : 365)) {
469 ++year;
470 days -= rem;
471 }
472 while(days < 0) {
473 --year;
474 days += is_leap(year) ? 366 : 365;
475 }
476
477 /*
478 * Determine the month
479 */
480 ml = is_leap(year) ? ldmsize : dmsize;
481 for(month = 0; days >= ml[month]; ++month)
482 days -= ml[month];
483
484 /*
485 * Now that everything is calculated, program the RTC
486 */
487 rtc_setclkreg(RTC_REGB, RTC_B_SET);
488 rtc_setclkreg(RTC_REGA, RTC_A_DV1|RTC_A_RS2|RTC_A_RS3);
489 rtc_setclkreg(RTC_REGB, RTC_B_SET|RTC_B_SQWE|RTC_B_DM|RTC_B_24_12);
490 rtc_setclkreg(RTC_SEC, sec);
491 rtc_setclkreg(RTC_MIN, min);
492 rtc_setclkreg(RTC_HOUR, hour);
493 rtc_setclkreg(RTC_DAY, days+1);
494 rtc_setclkreg(RTC_MONTH, month+1);
495 rtc_setclkreg(RTC_YEAR, year-1970);
496 rtc_setclkreg(RTC_REGB, RTC_B_SQWE|RTC_B_DM|RTC_B_24_12);
497
498 return(1);
499 }
500