1 /* $NetBSD: clock.c,v 1.49 2009/07/07 16:16:18 tsutsui Exp $ */
4 * Copyright (c) 1982, 1990 The Regents of the University of California.
7 * This code is derived from software contributed to Berkeley by
8 * the Systems Programming Group of the University of Utah Computer
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 * from: Utah $Hdr: clock.c 1.18 91/01/21$
37 * @(#)clock.c 7.6 (Berkeley) 5/7/91
40 * Copyright (c) 1988 University of Utah.
42 * This code is derived from software contributed to Berkeley by
43 * the Systems Programming Group of the University of Utah Computer
46 * Redistribution and use in source and binary forms, with or without
47 * modification, are permitted provided that the following conditions
49 * 1. Redistributions of source code must retain the above copyright
50 * notice, this list of conditions and the following disclaimer.
51 * 2. Redistributions in binary form must reproduce the above copyright
52 * notice, this list of conditions and the following disclaimer in the
53 * documentation and/or other materials provided with the distribution.
54 * 3. All advertising materials mentioning features or use of this software
55 * must display the following acknowledgement:
56 * This product includes software developed by the University of
57 * California, Berkeley and its contributors.
58 * 4. Neither the name of the University nor the names of its contributors
59 * may be used to endorse or promote products derived from this software
60 * without specific prior written permission.
62 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
63 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
64 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
65 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
66 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
67 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
68 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
69 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
70 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
71 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
74 * from: Utah $Hdr: clock.c 1.18 91/01/21$
76 * @(#)clock.c 7.6 (Berkeley) 5/7/91
79 #include <sys/cdefs.h>
80 __KERNEL_RCSID(0, "$NetBSD: clock.c,v 1.49 2009/07/07 16:16:18 tsutsui Exp $");
82 #include <sys/param.h>
83 #include <sys/kernel.h>
84 #include <sys/systm.h>
85 #include <sys/device.h>
89 #include <sys/event.h>
90 #include <sys/timetc.h>
92 #include <dev/clock_subr.h>
94 #include <machine/psl.h>
95 #include <machine/cpu.h>
96 #include <machine/iomap.h>
97 #include <machine/mfp.h>
98 #include <atari/dev/clockreg.h>
99 #include <atari/dev/clockvar.h>
100 #include <atari/atari/device.h>
102 #if defined(GPROF) && defined(PROFTIMER)
103 #include <machine/profile.h>
106 static int atari_rtc_get(todr_chip_handle_t
, struct clock_ymdhms
*);
107 static int atari_rtc_set(todr_chip_handle_t
, struct clock_ymdhms
*);
110 * The MFP clock runs at 2457600Hz. We use a {system,stat,prof}clock divider
111 * of 200. Therefore the timer runs at an effective rate of:
112 * 2457600/200 = 12288Hz.
114 #define CLOCK_HZ 12288
116 static u_int
clk_getcounter(struct timecounter
*);
118 static struct timecounter clk_timecounter
= {
119 clk_getcounter
, /* get_timecount */
121 ~0u, /* counter_mask */
122 CLOCK_HZ
, /* frequency */
123 "clock", /* name, overriden later */
130 * Machine-dependent clock routines.
132 * Inittodr initializes the time of day hardware which provides
135 * Resettodr restores the time of day hardware after a time change.
139 struct device sc_dev
;
144 * 'sc_flags' state info. Only used by the rtc-device functions.
148 dev_type_open(rtcopen
);
149 dev_type_close(rtcclose
);
150 dev_type_read(rtcread
);
151 dev_type_write(rtcwrite
);
153 static void clockattach(struct device
*, struct device
*, void *);
154 static int clockmatch(struct device
*, struct cfdata
*, void *);
156 CFATTACH_DECL(clock
, sizeof(struct clock_softc
),
157 clockmatch
, clockattach
, NULL
, NULL
);
159 extern struct cfdriver clock_cd
;
161 const struct cdevsw rtc_cdevsw
= {
162 rtcopen
, rtcclose
, rtcread
, rtcwrite
, noioctl
,
163 nostop
, notty
, nopoll
, nommap
, nokqfilter
,
166 void statintr(struct clockframe
);
168 static int twodigits(char *, int);
170 static int divisor
; /* Systemclock divisor */
173 * Statistics and profile clock intervals and variances. Variance must
174 * be a power of 2. Since this gives us an even number, not an odd number,
175 * we discard one case and compensate. That is, a variance of 64 would
176 * give us offsets in [0..63]. Instead, we take offsets in [1..63].
177 * This is symmetric around the point 32, or statvar/2, and thus averages
178 * to that value (assuming uniform random numbers).
181 static int statvar
= 32; /* {stat,prof}clock variance */
182 static int statmin
; /* statclock divisor - variance/2 */
183 static int profmin
; /* profclock divisor - variance/2 */
184 static int clk2min
; /* current, from above choices */
188 clockmatch(struct device
*pdp
, struct cfdata
*cfp
, void *auxp
)
191 if (!strcmp("clock", auxp
))
197 * Start the real-time clock.
199 void clockattach(struct device
*pdp
, struct device
*dp
, void *auxp
)
202 struct clock_softc
*sc
= (void *)dp
;
203 static struct todr_chip_handle tch
;
205 tch
.todr_gettime_ymdhms
= atari_rtc_get
;
206 tch
.todr_settime_ymdhms
= atari_rtc_set
;
207 tch
.todr_setwen
= NULL
;
214 * Initialize Timer-A in the ST-MFP. We use a divisor of 200.
215 * The MFP clock runs at 2457600Hz. Therefore the timer runs
216 * at an effective rate of: 2457600/200 = 12288Hz. The
217 * following expression works for 48, 64 or 96 hz.
219 divisor
= CLOCK_HZ
/hz
;
220 MFP
->mf_tacr
= 0; /* Stop timer */
221 MFP
->mf_iera
&= ~IA_TIMA
; /* Disable timer interrupts */
222 MFP
->mf_tadr
= divisor
; /* Set divisor */
224 clk_timecounter
.tc_frequency
= CLOCK_HZ
;
226 if (hz
!= 48 && hz
!= 64 && hz
!= 96) { /* XXX */
227 printf (": illegal value %d for systemclock, reset to %d\n\t",
231 printf(": system hz %d timer-A divisor 200/%d\n", hz
, divisor
);
232 tc_init(&clk_timecounter
);
235 if ((stathz
== 0) || (stathz
> hz
) || (CLOCK_HZ
% stathz
))
237 if ((profhz
== 0) || (profhz
> (hz
<< 1)) || (CLOCK_HZ
% profhz
))
240 MFP
->mf_tcdcr
&= 0x7; /* Stop timer */
241 MFP
->mf_ierb
&= ~IB_TIMC
; /* Disable timer inter. */
242 MFP
->mf_tcdr
= CLOCK_HZ
/stathz
; /* Set divisor */
244 statmin
= (CLOCK_HZ
/stathz
) - (statvar
>> 1);
245 profmin
= (CLOCK_HZ
/profhz
) - (statvar
>> 1);
247 #endif /* STATCLOCK */
250 void cpu_initclocks(void)
253 MFP
->mf_tacr
= T_Q200
; /* Start timer */
254 MFP
->mf_ipra
= (u_int8_t
)~IA_TIMA
;/* Clear pending interrupts */
255 MFP
->mf_iera
|= IA_TIMA
; /* Enable timer interrupts */
256 MFP
->mf_imra
|= IA_TIMA
; /* ..... */
259 MFP
->mf_tcdcr
= (MFP
->mf_tcdcr
& 0x7) | (T_Q200
<<4); /* Start */
260 MFP
->mf_iprb
= (u_int8_t
)~IB_TIMC
;/* Clear pending interrupts */
261 MFP
->mf_ierb
|= IB_TIMC
; /* Enable timer interrupts */
262 MFP
->mf_imrb
|= IB_TIMC
; /* ..... */
263 #endif /* STATCLOCK */
267 setstatclockrate(int newhz
)
273 else clk2min
= profmin
;
274 #endif /* STATCLOCK */
279 statintr(struct clockframe frame
)
289 * Note that we are always lagging behind as the new divisor
290 * value will not be loaded until the next interrupt. This
291 * shouldn't disturb the median frequency (I think ;-) ) as
292 * only the value used when switching frequencies is used
293 * twice. This shouldn't happen very often.
295 MFP
->mf_tcdr
= clk2min
+ r
;
299 #endif /* STATCLOCK */
302 clk_getcounter(struct timecounter
*tc
)
304 uint32_t delta
, count
, cur_hardclock
;
307 static uint32_t lastcount
;
310 cur_hardclock
= hardclock_ticks
;
313 delta
= divisor
- tadr
;
319 count
= (divisor
* cur_hardclock
) + delta
;
320 if ((int32_t)(count
- lastcount
) < 0) {
321 /* XXX wrapped; maybe hardclock() is blocked more than 2/HZ */
322 count
= lastcount
+ 1;
329 #define TIMB_FREQ 614400
330 #define TIMB_LIMIT 256
337 * Initialize Timer-B in the ST-MFP. This timer is used by
338 * the 'delay' function below. This timer is setup to be
339 * continueously counting from 255 back to zero at a
340 * frequency of 614400Hz. We do this *early* in the
341 * initialisation process.
343 MFP
->mf_tbcr
= 0; /* Stop timer */
344 MFP
->mf_iera
&= ~IA_TIMB
; /* Disable timer interrupts */
346 MFP
->mf_tbcr
= T_Q004
; /* Start timer */
350 * Wait "n" microseconds.
351 * Relies on MFP-Timer B counting down from TIMB_LIMIT at TIMB_FREQ Hz.
352 * Note: timer had better have been programmed before this is first used!
355 delay(unsigned int n
)
357 int ticks
, otick
, remaining
;
360 * Read the counter first, so that the rest of the setup overhead is
363 otick
= MFP
->mf_tbdr
;
365 if (n
<= UINT_MAX
/ TIMB_FREQ
) {
367 * For unsigned arithmetic, division can be replaced with
368 * multiplication with the inverse and a shift.
370 remaining
= n
* TIMB_FREQ
/ 1000000;
372 /* This is a very long delay.
373 * Being slow here doesn't matter.
375 remaining
= (unsigned long long) n
* TIMB_FREQ
/ 1000000;
378 while (remaining
> 0) {
379 ticks
= MFP
->mf_tbdr
;
381 remaining
-= TIMB_LIMIT
- (ticks
- otick
);
383 remaining
-= otick
- ticks
;
390 * profclock() is expanded in line in lev6intr() unless profiling kernel.
391 * Assumes it is called with clock interrupts blocked.
393 profclock(void *pc
, int ps
)
397 * Came from user mode.
398 * If this process is being profiled record the tick.
401 if (p
->p_stats
.p_prof
.pr_scale
)
402 addupc(pc
, &curproc
->p_stats
.p_prof
, 1);
405 * Came from kernel (supervisor) mode.
406 * If we are profiling the kernel, record the tick.
408 else if (profiling
< 2) {
409 register int s
= pc
- s_lowpc
;
412 kcount
[s
/ (HISTFRACTION
* sizeof(*kcount
))]++;
415 * Kernel profiling was on but has been disabled.
416 * Mark as no longer profiling kernel and if all profiling done,
419 if (profiling
&& (profon
& PRF_KERNEL
)) {
420 profon
&= ~PRF_KERNEL
;
421 if (profon
== PRF_NONE
)
427 /***********************************************************************
428 * Real Time Clock support *
429 ***********************************************************************/
431 u_int
mc146818_read(void *cookie
, u_int regno
)
433 struct rtc
*rtc
= cookie
;
435 rtc
->rtc_regno
= regno
;
436 return rtc
->rtc_data
& 0xff;
439 void mc146818_write(void *cookie
, u_int regno
, u_int value
)
441 struct rtc
*rtc
= cookie
;
443 rtc
->rtc_regno
= regno
;
444 rtc
->rtc_data
= value
;
448 atari_rtc_get(todr_chip_handle_t todr
, struct clock_ymdhms
*dtp
)
455 regb
= mc146818_read(RTC
, MC_REGB
);
456 MC146818_GETTOD(RTC
, &clkregs
);
459 regb
&= MC_REGB_24HR
|MC_REGB_BINARY
;
460 if (regb
!= (MC_REGB_24HR
|MC_REGB_BINARY
)) {
461 printf("Error: Nonstandard RealTimeClock Configuration -"
463 " A write to /dev/rtc will correct this.\n");
466 if (clkregs
[MC_SEC
] > 59)
468 if (clkregs
[MC_MIN
] > 59)
470 if (clkregs
[MC_HOUR
] > 23)
472 if (range_test(clkregs
[MC_DOM
], 1, 31))
474 if (range_test(clkregs
[MC_MONTH
], 1, 12))
476 if (clkregs
[MC_YEAR
] > 99)
479 dtp
->dt_year
= clkregs
[MC_YEAR
] + GEMSTARTOFTIME
;
480 dtp
->dt_mon
= clkregs
[MC_MONTH
];
481 dtp
->dt_day
= clkregs
[MC_DOM
];
482 dtp
->dt_hour
= clkregs
[MC_HOUR
];
483 dtp
->dt_min
= clkregs
[MC_MIN
];
484 dtp
->dt_sec
= clkregs
[MC_SEC
];
490 atari_rtc_set(todr_chip_handle_t todr
, struct clock_ymdhms
*dtp
)
495 clkregs
[MC_YEAR
] = dtp
->dt_year
- GEMSTARTOFTIME
;
496 clkregs
[MC_MONTH
] = dtp
->dt_mon
;
497 clkregs
[MC_DOM
] = dtp
->dt_day
;
498 clkregs
[MC_HOUR
] = dtp
->dt_hour
;
499 clkregs
[MC_MIN
] = dtp
->dt_min
;
500 clkregs
[MC_SEC
] = dtp
->dt_sec
;
503 MC146818_PUTTOD(RTC
, &clkregs
);
509 /***********************************************************************
510 * RTC-device support *
511 ***********************************************************************/
513 rtcopen(dev_t dev
, int flag
, int mode
, struct lwp
*l
)
515 int unit
= minor(dev
);
516 struct clock_softc
*sc
;
518 sc
= device_lookup_private(&clock_cd
, unit
);
521 if (sc
->sc_flags
& RTC_OPEN
)
524 sc
->sc_flags
= RTC_OPEN
;
529 rtcclose(dev_t dev
, int flag
, int mode
, struct lwp
*l
)
531 int unit
= minor(dev
);
532 struct clock_softc
*sc
= device_lookup_private(&clock_cd
, unit
);
539 rtcread(dev_t dev
, struct uio
*uio
, int flags
)
541 struct clock_softc
*sc
;
546 sc
= device_lookup_private(&clock_cd
, minor(dev
));
549 MC146818_GETTOD(RTC
, &clkregs
);
552 sprintf(buffer
, "%4d%02d%02d%02d%02d.%02d\n",
553 clkregs
[MC_YEAR
] + GEMSTARTOFTIME
,
554 clkregs
[MC_MONTH
], clkregs
[MC_DOM
],
555 clkregs
[MC_HOUR
], clkregs
[MC_MIN
], clkregs
[MC_SEC
]);
557 if (uio
->uio_offset
> strlen(buffer
))
560 length
= strlen(buffer
) - uio
->uio_offset
;
561 if (length
> uio
->uio_resid
)
562 length
= uio
->uio_resid
;
564 return uiomove((void *)buffer
, length
, uio
);
568 twodigits(char *buffer
, int pos
)
572 if (buffer
[pos
] >= '0' && buffer
[pos
] <= '9')
573 result
= (buffer
[pos
] - '0') * 10;
574 if (buffer
[pos
+1] >= '0' && buffer
[pos
+1] <= '9')
575 result
+= (buffer
[pos
+1] - '0');
580 rtcwrite(dev_t dev
, struct uio
*uio
, int flags
)
583 int s
, length
, error
;
587 * We require atomic updates!
589 length
= uio
->uio_resid
;
590 if (uio
->uio_offset
|| (length
!= sizeof(buffer
)
591 && length
!= sizeof(buffer
- 1)))
594 if ((error
= uiomove((void *)buffer
, sizeof(buffer
), uio
)))
597 if (length
== sizeof(buffer
) && buffer
[sizeof(buffer
) - 1] != '\n')
601 mc146818_write(RTC
, MC_REGB
,
602 mc146818_read(RTC
, MC_REGB
) | MC_REGB_24HR
| MC_REGB_BINARY
);
603 MC146818_GETTOD(RTC
, &clkregs
);
606 clkregs
[MC_SEC
] = twodigits(buffer
, 13);
607 clkregs
[MC_MIN
] = twodigits(buffer
, 10);
608 clkregs
[MC_HOUR
] = twodigits(buffer
, 8);
609 clkregs
[MC_DOM
] = twodigits(buffer
, 6);
610 clkregs
[MC_MONTH
] = twodigits(buffer
, 4);
611 s
= twodigits(buffer
, 0) * 100 + twodigits(buffer
, 2);
612 clkregs
[MC_YEAR
] = s
- GEMSTARTOFTIME
;
615 MC146818_PUTTOD(RTC
, &clkregs
);