ixp12x0_clk.c revision 1.3 1 /* $NetBSD: ixp12x0_clk.c,v 1.3 2002/09/27 15:35:48 provos Exp $ */
2
3 /*
4 * Copyright (c) 1997 Mark Brinicombe.
5 * Copyright (c) 1997 Causality Limited.
6 * All rights reserved.
7 *
8 * This code is derived from software contributed to The NetBSD Foundation
9 * by IWAMOTO Toshihiro and Ichiro FUKUHARA.
10 *
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.
26 *
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.
38 */
39
40 #include <sys/types.h>
41 #include <sys/param.h>
42 #include <sys/systm.h>
43 #include <sys/kernel.h>
44 #include <sys/time.h>
45 #include <sys/device.h>
46
47 #include <machine/bus.h>
48 #include <machine/intr.h>
49
50 #include <arm/cpufunc.h>
51
52 #include <arm/ixp12x0/ixpsipvar.h>
53
54 #include <arm/ixp12x0/ixp12x0_pcireg.h>
55 #include <arm/ixp12x0/ixp12x0_clkreg.h>
56 #include <arm/ixp12x0/ixp12x0var.h>
57
58 static int ixpclk_match(struct device *, struct cfdata *, void *);
59 static void ixpclk_attach(struct device *, struct device *, void *);
60
61 int gettick(void);
62 void rtcinit(void);
63
64 /* callback functions for intr_functions */
65 static int ixpclk_intr(void* arg);
66
67 struct ixpclk_softc {
68 struct device sc_dev;
69 bus_addr_t sc_baseaddr;
70 bus_space_tag_t sc_iot;
71 bus_space_handle_t sc_ioh;
72
73 u_int32_t sc_clock_count;
74 u_int32_t sc_count_per_usec;
75 u_int32_t sc_coreclock_freq;
76 };
77
78 #define XTAL_FREQ 3686400 /* 3.6864MHz */
79 #define XTAL_FREQ3686400
80 #undef XTAL_FREQ3787800
81 #undef XTAL_FREQ3579500
82 #define MAX_CCF 22
83
84 #if defined(XTAL_FREQ3686400)
85 static u_int32_t ccf_to_coreclock[MAX_CCF + 1] = {
86 29491000,
87 36865000,
88 44237000,
89 51610000,
90 58982000,
91 66355000,
92 73728000,
93 81101000,
94 88474000,
95 95846000,
96 103219000,
97 110592000,
98 132710000,
99 147456000,
100 154829000,
101 162202000,
102 165890000,
103 176947000,
104 191693000,
105 199066000,
106 206438000,
107 221184000,
108 232243000,
109 };
110 #elif defined(XTAL_FREQ3787800)
111 #elif defined(XTAL_FREQ3579500)
112 #else
113 #error
114 #endif
115
116 static struct ixpclk_softc *ixpclk_sc = NULL;
117
118 #define TIMER_FREQUENCY 3686400 /* 3.6864MHz */
119 #define TICKS_PER_MICROSECOND (TIMER_FREQUENCY/1000000)
120
121 struct cfattach ixpclk_ca = {
122 sizeof(struct ixpclk_softc), ixpclk_match, ixpclk_attach
123 };
124
125 #define GET_TIMER_VALUE(sc) (bus_space_read_4((sc)->sc_iot, \
126 (sc)->sc_ioh, \
127 IXPCLK_VALUE) \
128 & IXPCL_CTV)
129
130 static int
131 ixpclk_match(parent, match, aux)
132 struct device *parent;
133 struct cfdata *match;
134 void *aux;
135 {
136 return (1);
137 }
138
139 static void
140 ixpclk_attach(parent, self, aux)
141 struct device *parent;
142 struct device *self;
143 void *aux;
144 {
145 struct ixpclk_softc *sc = (struct ixpclk_softc*) self;
146 struct ixpsip_attach_args *sa = aux;
147
148 printf("\n");
149
150 sc->sc_iot = sa->sa_iot;
151 sc->sc_baseaddr = sa->sa_addr;
152
153 /* using first timer for system ticks */
154 if (ixpclk_sc == NULL)
155 ixpclk_sc = sc;
156
157 if (bus_space_map(sa->sa_iot, sa->sa_addr, sa->sa_size, 0,
158 &sc->sc_ioh))
159 panic("%s: Cannot map registers", self->dv_xname);
160
161 /* disable all channel and clear interrupt status */
162 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CONTROL,
163 IXPCL_DISABLE | IXPCL_PERIODIC | IXPCL_STP_CORE);
164 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CLEAR, 0);
165 printf("%s: IXP12x0 Interval Timer\n", sc->sc_dev.dv_xname);
166 }
167
168 /*
169 * ixpclk_intr:
170 *
171 * Handle the hardclock interrupt.
172 */
173 static int
174 ixpclk_intr(void *arg)
175
176 {
177 /* XXX XXX */
178 if (!(IXPREG(IXPPCI_IRQ_RAW_STATUS)
179 & (1U << (IXPPCI_INTR_T1 - SYS_NIRQ))))
180 return (0);
181
182 bus_space_write_4(ixpclk_sc->sc_iot, ixpclk_sc->sc_ioh,
183 IXPCLK_CLEAR, 1);
184
185 hardclock((struct clockframe*) arg);
186 return (1);
187 }
188
189 /*
190 * setstatclockrate:
191 *
192 * Set the rate of the statistics clock.
193 *
194 * We assume that hz is either stathz or profhz, and that neither
195 * will change after being set by cpu_initclocks(). We could
196 * recalculate the intervals here, but that would be a pain.
197 */
198 void
199 setstatclockrate(hz)
200 int hz;
201 {
202 /* use hardclock */
203
204 /* XXX should I use TIMER2? */
205 }
206
207 /*
208 * cpu_initclocks:
209 *
210 * Initialize the clock and get them going.
211 */
212 void
213 cpu_initclocks()
214 {
215 struct ixpclk_softc* sc = ixpclk_sc;
216 u_int32_t ccf;
217
218 stathz = profhz = 0;
219
220 printf("clock: hz = %d stathz = %d\n", hz, stathz);
221
222 ccf = IXPREG(IXP12X0_PLL_CFG) & IXP12X0_PLL_CFG_CCF;
223 sc->sc_coreclock_freq = ccf_to_coreclock[ccf];
224
225 printf("pll_cfg:ccf = %x coreclock frequency = %dHz\n",
226 ccf, sc->sc_coreclock_freq);
227
228 sc->sc_clock_count = sc->sc_coreclock_freq / hz;
229 sc->sc_count_per_usec = sc->sc_coreclock_freq / 10000000;
230
231 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CLEAR, IXPT_CLEAR);
232
233 ixp12x0_intr_establish(IXPPCI_INTR_T1, IPL_CLOCK, ixpclk_intr, NULL);
234
235 IXPREG(IXPPCI_IRQ_ENABLE_SET) = (1U << (IXPPCI_INTR_T1 - SYS_NIRQ));
236
237 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_LOAD,
238 sc->sc_clock_count);
239 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CONTROL,
240 IXPCL_ENABLE | IXPCL_PERIODIC
241 | IXPCL_STP_CORE);
242 }
243
244 int
245 gettick()
246 {
247 int counter;
248 u_int savedints;
249 savedints = disable_interrupts(I32_bit);
250
251 counter = GET_TIMER_VALUE(ixpclk_sc);
252
253 restore_interrupts(savedints);
254 return counter;
255 }
256
257 /*
258 * microtime:
259 *
260 * Fill in the specified timeval struct with the current time
261 * accurate to the microsecond.
262 */
263 void
264 microtime(tvp)
265 register struct timeval *tvp;
266 {
267 u_int oldirqstate;
268 u_int32_t counts;
269 static struct timeval lasttv;
270
271 if (ixpclk_sc == NULL) {
272 #ifdef DEBUG
273 printf("microtime: called befor initialize ixpclk\n");
274 #endif
275 tvp->tv_sec = 0;
276 tvp->tv_usec = 0;
277 return;
278 }
279
280 oldirqstate = disable_interrupts(I32_bit);
281
282 counts = ixpclk_sc->sc_clock_count - GET_TIMER_VALUE(ixpclk_sc);
283
284 /* Fill in the timeval struct. */
285 *tvp = time;
286 tvp->tv_usec += counts / ixpclk_sc->sc_count_per_usec;
287
288 /* Make sure microseconds doesn't overflow. */
289 while (tvp->tv_usec >= 1000000) {
290 tvp->tv_usec -= 1000000;
291 tvp->tv_sec++;
292 }
293
294 /* Make sure the time has advanced. */
295 if (tvp->tv_sec == lasttv.tv_sec &&
296 tvp->tv_usec <= lasttv.tv_usec) {
297 tvp->tv_usec = lasttv.tv_usec + 1;
298 if (tvp->tv_usec >= 1000000) {
299 tvp->tv_usec -= 1000000;
300 tvp->tv_sec++;
301 }
302 }
303
304 lasttv = *tvp;
305
306 restore_interrupts(oldirqstate);
307 }
308
309 /*
310 * delay:
311 *
312 * Delay for at least N microseconds.
313 */
314 void
315 delay(usecs)
316 u_int usecs;
317 {
318 u_int32_t tick, otick, delta;
319 int j, csec, usec;
320
321 csec = usecs / 10000;
322 usec = usecs % 10000;
323
324 if (ixpclk_sc == NULL) {
325 static u_int32_t coreclock_freq = 0;
326
327 #ifdef DEBUG
328 printf("delay: called befor initialize ixpclk\n");
329 #endif
330 if (coreclock_freq == 0) {
331 coreclock_freq
332 = ccf_to_coreclock[IXPREG(IXP12X0_PLL_CFG)
333 & IXP12X0_PLL_CFG_CCF];
334 }
335
336 usecs = (TIMER_FREQUENCY / 100) * csec
337 + (TIMER_FREQUENCY / 100) * usec / 10000;
338 /* clock isn't initialized yet */
339 for(; usecs > 0; usecs--)
340 for(j = 100; j > 0; j--)
341 ;
342 return;
343 }
344
345 usecs = (ixpclk_sc->sc_coreclock_freq / 100) * csec
346 + (ixpclk_sc->sc_coreclock_freq / 100) * usec / 10000;
347
348 otick = gettick();
349
350 while (1) {
351 for(j = 100; j > 0; j--)
352 ;
353 tick = gettick();
354
355 delta = otick > tick
356 ? otick - tick
357 : otick - tick + IXPCL_ITV + 1;
358 if (delta > usecs)
359 break;
360 usecs -= delta;
361 otick = tick;
362 }
363 }
364
365 void
366 resettodr()
367 {
368 }
369
370 void
371 inittodr(base)
372 time_t base;
373 {
374 time.tv_sec = base;
375 time.tv_usec = 0;
376 }
377