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