No empty .Rs/.Re
[netbsd-mini2440.git] / sys / arch / arm / ixp12x0 / ixp12x0_clk.c
blobb657088994f70a9a0433fba5eef079fcb2315bd8
1 /* $NetBSD: ixp12x0_clk.c,v 1.12 2008/01/20 16:28:23 joerg Exp $ */
3 /*
4 * Copyright (c) 1997 Mark Brinicombe.
5 * Copyright (c) 1997 Causality Limited.
6 * All rights reserved.
8 * This code is derived from software contributed to The NetBSD Foundation
9 * by IWAMOTO Toshihiro and Ichiro FUKUHARA.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
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. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by the NetBSD
22 * Foundation, Inc. and its contributors.
23 * 4. Neither the name of The NetBSD Foundation nor the names of its
24 * contributors may be used to endorse or promote products derived
25 * from this software without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
28 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
29 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
30 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
31 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37 * POSSIBILITY OF SUCH DAMAGE.
40 #include <sys/cdefs.h>
41 __KERNEL_RCSID(0, "$NetBSD: ixp12x0_clk.c,v 1.12 2008/01/20 16:28:23 joerg Exp $");
43 #include <sys/types.h>
44 #include <sys/param.h>
45 #include <sys/atomic.h>
46 #include <sys/systm.h>
47 #include <sys/kernel.h>
48 #include <sys/time.h>
49 #include <sys/timetc.h>
50 #include <sys/device.h>
52 #include <machine/bus.h>
53 #include <machine/intr.h>
55 #include <arm/cpufunc.h>
57 #include <arm/ixp12x0/ixpsipvar.h>
59 #include <arm/ixp12x0/ixp12x0_pcireg.h>
60 #include <arm/ixp12x0/ixp12x0_clkreg.h>
61 #include <arm/ixp12x0/ixp12x0var.h>
63 static int ixpclk_match(struct device *, struct cfdata *, void *);
64 static void ixpclk_attach(struct device *, struct device *, void *);
66 static u_int ixpclk_get_timecount(struct timecounter *);
68 int gettick(void);
69 void rtcinit(void);
71 /* callback functions for intr_functions */
72 static int ixpclk_intr(void* arg);
74 struct ixpclk_softc {
75 struct device sc_dev;
76 bus_addr_t sc_baseaddr;
77 bus_space_tag_t sc_iot;
78 bus_space_handle_t sc_ioh;
79 bus_space_handle_t sc_pll_ioh;
81 u_int32_t sc_clock_count;
82 u_int32_t sc_count_per_usec;
83 u_int32_t sc_coreclock_freq;
86 #define XTAL_FREQ 3686400 /* 3.6864MHz */
87 #define XTAL_FREQ3686400
88 #undef XTAL_FREQ3787800
89 #undef XTAL_FREQ3579500
90 #define MAX_CCF 22
92 #if defined(XTAL_FREQ3686400)
93 static u_int32_t ccf_to_coreclock[MAX_CCF + 1] = {
94 29491000,
95 36865000,
96 44237000,
97 51610000,
98 58982000,
99 66355000,
100 73728000,
101 81101000,
102 88474000,
103 95846000,
104 103219000,
105 110592000,
106 132710000,
107 147456000,
108 154829000,
109 162202000,
110 165890000,
111 176947000,
112 191693000,
113 199066000,
114 206438000,
115 221184000,
116 232243000,
118 #elif defined(XTAL_FREQ3787800)
119 #elif defined(XTAL_FREQ3579500)
120 #else
121 #error
122 #endif
124 static struct ixpclk_softc *ixpclk_sc = NULL;
126 static struct timecounter ixpclk_timecounter = {
127 ixpclk_get_timecount, /* get_timecount */
128 0, /* no poll_pps */
129 0xffffffff, /* counter_mask */
130 0, /* frequency */
131 "ixpclk", /* name */
132 100, /* quality */
133 NULL, /* prev */
134 NULL, /* next */
137 static volatile uint32_t ixpclk_base;
139 #define TIMER_FREQUENCY 3686400 /* 3.6864MHz */
140 #define TICKS_PER_MICROSECOND (TIMER_FREQUENCY/1000000)
142 CFATTACH_DECL(ixpclk, sizeof(struct ixpclk_softc),
143 ixpclk_match, ixpclk_attach, NULL, NULL);
145 #define GET_TIMER_VALUE(sc) (bus_space_read_4((sc)->sc_iot, \
146 (sc)->sc_ioh, \
147 IXPCLK_VALUE) \
148 & IXPCL_CTV)
150 static int
151 ixpclk_match(struct device *parent, struct cfdata *match, void *aux)
154 return 2;
157 static void
158 ixpclk_attach(struct device *parent, struct device *self, void *aux)
160 struct ixpclk_softc *sc;
161 struct ixpsip_attach_args *sa;
162 u_int32_t ccf;
163 bool first_run = ixpclk_sc == NULL;
165 printf("\n");
167 sc = (struct ixpclk_softc*) self;
168 sa = aux;
169 sc->sc_iot = sa->sa_iot;
170 sc->sc_baseaddr = sa->sa_addr;
172 /* using first timer for system ticks */
173 if (ixpclk_sc == NULL)
174 ixpclk_sc = sc;
176 if (bus_space_map(sa->sa_iot, sa->sa_addr, sa->sa_size, 0,
177 &sc->sc_ioh))
178 panic("%s: Cannot map registers", self->dv_xname);
179 if (bus_space_map(sa->sa_iot, sa->sa_addr + IXPCLK_PLL_CFG_OFFSET,
180 IXPCLK_PLL_CFG_SIZE, 0, &sc->sc_pll_ioh))
181 panic("%s: Cannot map registers", self->dv_xname);
183 /* disable all channel and clear interrupt status */
184 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CONTROL,
185 IXPCL_DISABLE | IXPCL_PERIODIC | IXPCL_STP_CORE);
186 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CLEAR, 0);
189 ccf = bus_space_read_4(sc->sc_iot, sc->sc_pll_ioh, 0)
190 & IXP12X0_PLL_CFG_CCF;
191 sc->sc_coreclock_freq = ccf_to_coreclock[ccf];
193 sc->sc_clock_count = sc->sc_coreclock_freq / hz;
194 sc->sc_count_per_usec = sc->sc_coreclock_freq / 1000000;
196 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CLEAR, IXPT_CLEAR);
197 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_LOAD,
198 sc->sc_clock_count);
199 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CONTROL,
200 IXPCL_ENABLE | IXPCL_PERIODIC | IXPCL_STP_CORE);
202 if (first_run) {
203 ixpclk_timecounter.tc_frequency = sc->sc_coreclock_freq;
204 tc_init(&ixpclk_timecounter);
207 printf("%s: IXP12x0 Interval Timer (core clock %d.%03dMHz)\n",
208 sc->sc_dev.dv_xname,
209 sc->sc_coreclock_freq / 1000000,
210 (sc->sc_coreclock_freq % 1000000) / 1000);
214 * ixpclk_intr:
216 * Handle the hardclock interrupt.
218 static int
219 ixpclk_intr(void *arg)
222 bus_space_write_4(ixpclk_sc->sc_iot, ixpclk_sc->sc_ioh,
223 IXPCLK_CLEAR, 1);
225 atomic_add_32(&ixpclk_base, ixpclk_sc->sc_coreclock_freq);
227 hardclock((struct clockframe*) arg);
228 return (1);
232 * setstatclockrate:
234 * Set the rate of the statistics clock.
236 * We assume that hz is either stathz or profhz, and that neither
237 * will change after being set by cpu_initclocks(). We could
238 * recalculate the intervals here, but that would be a pain.
240 void
241 setstatclockrate(int newhz)
244 /* use hardclock */
246 /* XXX should I use TIMER2? */
250 * cpu_initclocks:
252 * Initialize the clock and get them going.
254 void
255 cpu_initclocks(void)
257 struct ixpclk_softc* sc;
259 sc = ixpclk_sc;
260 stathz = profhz = 0;
262 printf("clock: hz = %d stathz = %d\n", hz, stathz);
264 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CONTROL,
265 IXPCL_DISABLE);
266 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CLEAR, IXPT_CLEAR);
268 ixp12x0_intr_establish(IXPPCI_INTR_T1, IPL_CLOCK, ixpclk_intr, NULL);
270 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_LOAD,
271 sc->sc_clock_count);
272 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CONTROL,
273 IXPCL_ENABLE | IXPCL_PERIODIC
274 | IXPCL_STP_CORE);
278 gettick(void)
280 int counter;
281 u_int savedints;
283 savedints = disable_interrupts(I32_bit);
284 counter = GET_TIMER_VALUE(ixpclk_sc);
285 restore_interrupts(savedints);
286 return counter;
289 static u_int
290 ixpclk_get_timecount(struct timecounter *tc)
292 u_int savedints, base, counter;
294 savedints = disable_interrupts(I32_bit);
295 do {
296 base = ixpclk_base;
297 counter = GET_TIMER_VALUE(ixpclk_sc);
298 } while (base != ixpclk_base);
299 restore_interrupts(savedints);
301 return base - counter;
305 * delay:
307 * Delay for at least N microseconds.
309 void
310 delay(unsigned int usecs)
312 u_int32_t count;
313 u_int32_t ticks;
314 u_int32_t otick;
315 u_int32_t delta;
316 int j;
317 int csec;
318 int usec;
320 if (ixpclk_sc == NULL) {
321 #ifdef DEBUG
322 printf("delay: called befor start ixpclk\n");
323 #endif
325 csec = usecs / 10000;
326 usec = usecs % 10000;
328 usecs = (TIMER_FREQUENCY / 100) * csec
329 + (TIMER_FREQUENCY / 100) * usec / 10000;
330 /* clock isn't initialized yet */
331 for(; usecs > 0; usecs--)
332 for(j = 100; j > 0; j--)
334 return;
337 count = ixpclk_sc->sc_count_per_usec * usecs;
339 otick = gettick();
341 for (;;) {
342 for(j = 100; j > 0; j--)
345 ticks = gettick();
346 delta = otick < ticks
347 ? ixpclk_sc->sc_clock_count + otick - ticks
348 : otick - ticks;
350 if (delta > count)
351 break;
353 count -= delta;
354 otick = ticks;