1 /* $NetBSD: ixp425_timer.c,v 1.14 2008/01/20 16:28:24 joerg Exp $ */
5 * Ichiro FUKUHARA <ichiro@ichiro.org>.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * THIS SOFTWARE IS PROVIDED BY ICHIRO FUKUHARA ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL ICHIRO FUKUHARA OR THE VOICES IN HIS HEAD BE LIABLE FOR
21 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 #include <sys/cdefs.h>
31 __KERNEL_RCSID(0, "$NetBSD: ixp425_timer.c,v 1.14 2008/01/20 16:28:24 joerg Exp $");
33 #include "opt_ixp425.h"
34 #include "opt_perfctrs.h"
36 #include <sys/types.h>
37 #include <sys/param.h>
38 #include <sys/systm.h>
39 #include <sys/kernel.h>
40 #include <sys/atomic.h>
42 #include <sys/timetc.h>
43 #include <sys/device.h>
45 #include <dev/clock_subr.h>
47 #include <machine/bus.h>
48 #include <machine/intr.h>
50 #include <arm/cpufunc.h>
52 #include <arm/xscale/ixp425reg.h>
53 #include <arm/xscale/ixp425var.h>
54 #include <arm/xscale/ixp425_sipvar.h>
56 static int ixpclk_match(struct device
*, struct cfdata
*, void *);
57 static void ixpclk_attach(struct device
*, struct device
*, void *);
58 static u_int
ixpclk_get_timecount(struct timecounter
*);
60 static uint32_t counts_per_hz
;
62 static void *clock_ih
;
64 /* callback functions for intr_functions */
65 int ixpclk_intr(void *);
69 bus_addr_t sc_baseaddr
;
70 bus_space_tag_t sc_iot
;
71 bus_space_handle_t sc_ioh
;
74 #ifndef IXP425_CLOCK_FREQ
75 #define COUNTS_PER_SEC 66666600 /* 66MHz */
77 #define COUNTS_PER_SEC IXP425_CLOCK_FREQ
79 #define COUNTS_PER_USEC ((COUNTS_PER_SEC / 1000000) + 1)
81 static struct ixpclk_softc
*ixpclk_sc
;
83 static struct timecounter ixpclk_timecounter
= {
84 ixpclk_get_timecount
, /* get_timecount */
86 0xffffffff, /* counter_mask */
87 COUNTS_PER_SEC
, /* frequency */
94 static volatile uint32_t ixpclk_base
;
96 CFATTACH_DECL(ixpclk
, sizeof(struct ixpclk_softc
),
97 ixpclk_match
, ixpclk_attach
, NULL
, NULL
);
99 #define GET_TIMER_VALUE(sc) (bus_space_read_4((sc)->sc_iot, \
103 #define GET_TS_VALUE(sc) (*(volatile u_int32_t *) \
104 (IXP425_TIMER_VBASE + IXP425_OST_TS))
107 ixpclk_match(struct device
*parent
, struct cfdata
*match
, void *aux
)
113 ixpclk_attach(struct device
*parent
, struct device
*self
, void *aux
)
115 struct ixpclk_softc
*sc
= (struct ixpclk_softc
*) self
;
116 struct ixpsip_attach_args
*sa
= aux
;
122 sc
->sc_iot
= sa
->sa_iot
;
123 sc
->sc_baseaddr
= sa
->sa_addr
;
125 if (bus_space_map(sc
->sc_iot
, sa
->sa_addr
, sa
->sa_size
, 0,
127 panic("%s: Cannot map registers", self
->dv_xname
);
129 aprint_normal("%s: IXP425 Interval Timer\n", sc
->sc_dev
.dv_xname
);
135 * Initialize the clock and get them going.
140 struct ixpclk_softc
* sc
= ixpclk_sc
;
142 #if defined(PERFCTRS)
146 if (hz
< 50 || COUNTS_PER_SEC
% hz
) {
147 aprint_error("Cannot get %d Hz clock; using 100 Hz\n", hz
);
152 * We only have one timer available; stathz and profhz are
153 * always left as 0 (the upper-layer clock code deals with
157 aprint_error("Cannot get %d Hz statclock\n", stathz
);
161 aprint_error("Cannot get %d Hz profclock\n", profhz
);
164 /* Report the clock frequency. */
165 aprint_normal("clock: hz=%d stathz=%d profhz=%d\n", hz
, stathz
, profhz
);
167 oldirqstate
= disable_interrupts(I32_bit
);
169 /* Hook up the clock interrupt handler. */
170 clock_ih
= ixp425_intr_establish(IXP425_INT_TMR0
, IPL_CLOCK
,
172 if (clock_ih
== NULL
)
173 panic("cpu_initclocks: unable to register timer interrupt");
175 #if defined(PERFCTRS)
176 pmu_ih
= ixp425_intr_establish(IXP425_INT_XPMU
, IPL_STATCLOCK
,
177 xscale_pmc_dispatch
, NULL
);
179 panic("cpu_initclocks: unable to register timer interrupt");
182 /* Set up the new clock parameters. */
184 /* clear interrupt */
185 bus_space_write_4(sc
->sc_iot
, sc
->sc_ioh
, IXP425_OST_STATUS
,
186 OST_WARM_RESET
| OST_WDOG_INT
| OST_TS_INT
|
187 OST_TIM1_INT
| OST_TIM0_INT
);
189 counts_per_hz
= COUNTS_PER_SEC
/ hz
;
191 /* reload value & Timer enable */
192 bus_space_write_4(sc
->sc_iot
, sc
->sc_ioh
, IXP425_OST_TIM0_RELOAD
,
193 (counts_per_hz
& TIMERRELOAD_MASK
) | OST_TIMER_EN
);
195 restore_interrupts(oldirqstate
);
197 tc_init(&ixpclk_timecounter
);
203 * Set the rate of the statistics clock.
205 * We assume that hz is either stathz or profhz, and that neither
206 * will change after being set by cpu_initclocks(). We could
207 * recalculate the intervals here, but that would be a pain.
210 setstatclockrate(int newhz
)
219 ixpclk_get_timecount(struct timecounter
*tc
)
221 u_int savedints
, base
, counter
;
223 savedints
= disable_interrupts(I32_bit
);
225 counter
= GET_TIMER_VALUE(ixpclk_sc
);
226 restore_interrupts(savedints
);
228 return base
- counter
;
234 * Delay for at least N microseconds.
239 u_int32_t first
, last
;
246 * Clamp the timeout at a maximum value (about 32 seconds with
247 * a 66MHz clock). *Nobody* should be delay()ing for anywhere
248 * near that length of time and if they are, they should be hung
251 if (n
>= (0x80000000U
/ COUNTS_PER_USEC
))
252 usecs
= (0x80000000U
/ COUNTS_PER_USEC
) - 1;
254 usecs
= n
* COUNTS_PER_USEC
;
256 /* Note: Timestamp timer counts *up*, unlike the other timers */
257 first
= GET_TS_VALUE();
260 last
= GET_TS_VALUE();
261 usecs
-= (int)(last
- first
);
269 * Handle the hardclock interrupt.
272 ixpclk_intr(void *arg
)
274 struct ixpclk_softc
* sc
= ixpclk_sc
;
275 struct clockframe
*frame
= arg
;
277 bus_space_write_4(sc
->sc_iot
, sc
->sc_ioh
, IXP425_OST_STATUS
,
280 atomic_add_32(&ixpclk_base
, counts_per_hz
);