1 /* $NetBSD: becc_timer.c,v 1.13 2008/01/06 01:37:57 matt Exp $ */
4 * Copyright (c) 2001, 2002 Wasabi Systems, Inc.
7 * Written by Jason R. Thorpe for Wasabi Systems, Inc.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. All advertising materials mentioning features or use of this software
18 * must display the following acknowledgement:
19 * This product includes software developed for the NetBSD Project by
20 * Wasabi Systems, Inc.
21 * 4. The name of Wasabi Systems, Inc. may not be used to endorse
22 * or promote products derived from this software without specific prior
25 * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
27 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC
29 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35 * POSSIBILITY OF SUCH DAMAGE.
39 * Timer/clock support for the ADI Engineering Big Endian Companion Chip.
42 #include <sys/cdefs.h>
43 __KERNEL_RCSID(0, "$NetBSD: becc_timer.c,v 1.13 2008/01/06 01:37:57 matt Exp $");
45 #include <sys/param.h>
46 #include <sys/systm.h>
47 #include <sys/kernel.h>
48 #include <sys/atomic.h>
50 #include <sys/timetc.h>
52 #include <dev/clock_subr.h>
54 #include <machine/bus.h>
55 #include <arm/cpufunc.h>
57 #include <arm/xscale/beccreg.h>
58 #include <arm/xscale/beccvar.h>
60 void (*becc_hardclock_hook
)(void);
63 * Note, since COUNTS_PER_USEC doesn't divide evenly, we round up.
65 #define COUNTS_PER_SEC BECC_PERIPH_CLOCK
66 #define COUNTS_PER_USEC ((COUNTS_PER_SEC / 1000000) + 1)
68 static void *clock_ih
;
70 static u_int
becc_get_timecount(struct timecounter
*);
72 static struct timecounter becc_timecounter
= {
73 becc_get_timecount
, /* get_timecount */
75 0xffffffff, /* counter_mask */
76 COUNTS_PER_SEC
, /* frequency */
83 static volatile uint32_t becc_base
;
86 * Since the timer interrupts when the counter underflows, we need to
87 * subtract 1 from counts_per_hz when loading the preload register.
89 static uint32_t counts_per_hz
;
91 int clockhandler(void *);
94 * becc_calibrate_delay:
96 * Calibrate the delay loop.
99 becc_calibrate_delay(void)
103 * Just use hz=100 for now -- we'll adjust it, if necessary,
104 * in cpu_initclocks().
106 counts_per_hz
= COUNTS_PER_SEC
/ 100;
108 /* Stop both timers, clear interrupts. */
109 BECC_CSR_WRITE(BECC_TSCRA
, TSCRx_TIF
);
110 BECC_CSR_WRITE(BECC_TSCRB
, TSCRx_TIF
);
112 /* Set the timer preload value. */
113 BECC_CSR_WRITE(BECC_TPRA
, counts_per_hz
- 1);
115 /* Start the timer. */
116 BECC_CSR_WRITE(BECC_TSCRA
, TSCRx_TE
| TSCRx_CM
);
122 * Initialize the clock and get them going.
130 if (hz
< 50 || COUNTS_PER_SEC
% hz
) {
131 printf("Cannot get %d Hz clock; using 100 Hz\n", hz
);
137 * We only have one timer available; stathz and profhz are
138 * always left as 0 (the upper-layer clock code deals with
142 printf("Cannot get %d Hz statclock\n", stathz
);
146 printf("Cannot get %d Hz profclock\n", profhz
);
149 /* Report the clock frequency. */
150 aprint_normal("clock: hz=%d stathz=%d profhz=%d\n", hz
, stathz
, profhz
);
152 oldirqstate
= disable_interrupts(I32_bit
);
154 /* Hook up the clock interrupt handler. */
155 clock_ih
= becc_intr_establish(ICU_TIMERA
, IPL_CLOCK
,
157 if (clock_ih
== NULL
)
158 panic("cpu_initclocks: unable to register timer interrupt");
160 /* Set up the new clock parameters. */
162 /* Stop timer, clear interrupt */
163 BECC_CSR_WRITE(BECC_TSCRA
, TSCRx_TIF
);
165 counts_per_hz
= COUNTS_PER_SEC
/ hz
;
167 /* Set the timer preload value. */
168 BECC_CSR_WRITE(BECC_TPRA
, counts_per_hz
- 1);
170 /* ...and start it in motion. */
171 BECC_CSR_WRITE(BECC_TSCRA
, TSCRx_TE
| TSCRx_CM
);
173 #ifdef __HAVE_FAST_SOFTINTS
174 /* register soft interrupt handler as well */
175 becc_intr_establish(ICU_SOFT
, IPL_SOFTCLOCK
, becc_softint
, NULL
);
178 restore_interrupts(oldirqstate
);
180 tc_init(&becc_timecounter
);
186 * Set the rate of the statistics clock.
188 * We assume that hz is either stathz or profhz, and that neither
189 * will change after being set by cpu_initclocks(). We could
190 * recalculate the intervals here, but that would be a pain.
193 setstatclockrate(int new_hz
)
202 becc_get_timecount(struct timecounter
*tc
)
204 uint32_t counter
, base
;
207 oldirqstate
= disable_interrupts(I32_bit
);
208 counter
= BECC_CSR_READ(BECC_TCVRA
);
210 restore_interrupts(oldirqstate
);
212 return base
- counter
;
218 * Delay for at least N microseconds.
223 uint32_t cur
, last
, delta
, usecs
;
226 * This works by polling the timer and counting the
227 * number of microseconds that go by.
229 last
= BECC_CSR_READ(BECC_TCVRA
);
233 cur
= BECC_CSR_READ(BECC_TCVRA
);
235 /* Check to see if the timer has wrapped around. */
237 delta
+= (last
+ (counts_per_hz
- cur
));
239 delta
+= (last
- cur
);
243 if (delta
>= COUNTS_PER_USEC
) {
244 usecs
+= delta
/ COUNTS_PER_USEC
;
245 delta
%= COUNTS_PER_USEC
;
253 * Handle the hardclock interrupt.
256 clockhandler(void *arg
)
258 struct clockframe
*frame
= arg
;
260 /* ACK the interrupt. */
261 BECC_CSR_WRITE(BECC_TSCRA
, TSCRx_TE
| TSCRx_CM
| TSCRx_TIF
);
265 atomic_add_32(&becc_base
, counts_per_hz
);
267 if (becc_hardclock_hook
!= NULL
)
268 (*becc_hardclock_hook
)();