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