ixp12x0_clk.c revision 1.9 1 /* $NetBSD: ixp12x0_clk.c,v 1.9 2005/06/04 21:19:23 he 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/cdefs.h>
41 __KERNEL_RCSID(0, "$NetBSD: ixp12x0_clk.c,v 1.9 2005/06/04 21:19:23 he Exp $");
42
43 #include <sys/types.h>
44 #include <sys/param.h>
45 #include <sys/systm.h>
46 #include <sys/kernel.h>
47 #include <sys/time.h>
48 #include <sys/device.h>
49
50 #include <machine/bus.h>
51 #include <machine/intr.h>
52
53 #include <arm/cpufunc.h>
54
55 #include <arm/ixp12x0/ixpsipvar.h>
56
57 #include <arm/ixp12x0/ixp12x0_pcireg.h>
58 #include <arm/ixp12x0/ixp12x0_clkreg.h>
59 #include <arm/ixp12x0/ixp12x0var.h>
60
61 static int ixpclk_match(struct device *, struct cfdata *, void *);
62 static void ixpclk_attach(struct device *, struct device *, void *);
63
64 int gettick(void);
65 void rtcinit(void);
66
67 /* callback functions for intr_functions */
68 static int ixpclk_intr(void* arg);
69
70 struct ixpclk_softc {
71 struct device sc_dev;
72 bus_addr_t sc_baseaddr;
73 bus_space_tag_t sc_iot;
74 bus_space_handle_t sc_ioh;
75 bus_space_handle_t sc_pll_ioh;
76
77 u_int32_t sc_clock_count;
78 u_int32_t sc_count_per_usec;
79 u_int32_t sc_coreclock_freq;
80 };
81
82 #define XTAL_FREQ 3686400 /* 3.6864MHz */
83 #define XTAL_FREQ3686400
84 #undef XTAL_FREQ3787800
85 #undef XTAL_FREQ3579500
86 #define MAX_CCF 22
87
88 #if defined(XTAL_FREQ3686400)
89 static u_int32_t ccf_to_coreclock[MAX_CCF + 1] = {
90 29491000,
91 36865000,
92 44237000,
93 51610000,
94 58982000,
95 66355000,
96 73728000,
97 81101000,
98 88474000,
99 95846000,
100 103219000,
101 110592000,
102 132710000,
103 147456000,
104 154829000,
105 162202000,
106 165890000,
107 176947000,
108 191693000,
109 199066000,
110 206438000,
111 221184000,
112 232243000,
113 };
114 #elif defined(XTAL_FREQ3787800)
115 #elif defined(XTAL_FREQ3579500)
116 #else
117 #error
118 #endif
119
120 static struct ixpclk_softc *ixpclk_sc = NULL;
121
122 #define TIMER_FREQUENCY 3686400 /* 3.6864MHz */
123 #define TICKS_PER_MICROSECOND (TIMER_FREQUENCY/1000000)
124
125 CFATTACH_DECL(ixpclk, sizeof(struct ixpclk_softc),
126 ixpclk_match, ixpclk_attach, NULL, NULL);
127
128 #define GET_TIMER_VALUE(sc) (bus_space_read_4((sc)->sc_iot, \
129 (sc)->sc_ioh, \
130 IXPCLK_VALUE) \
131 & IXPCL_CTV)
132
133 static int
134 ixpclk_match(struct device *parent, struct cfdata *match, void *aux)
135 {
136
137 return 2;
138 }
139
140 static void
141 ixpclk_attach(struct device *parent, struct device *self, void *aux)
142 {
143 struct ixpclk_softc *sc;
144 struct ixpsip_attach_args *sa;
145 u_int32_t ccf;
146
147 printf("\n");
148
149 sc = (struct ixpclk_softc*) self;
150 sa = aux;
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(int newhz)
217 {
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(void)
231 {
232 struct ixpclk_softc* sc;
233
234 sc = ixpclk_sc;
235 stathz = profhz = 0;
236
237 printf("clock: hz = %d stathz = %d\n", hz, stathz);
238
239 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CONTROL,
240 IXPCL_DISABLE);
241 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CLEAR, IXPT_CLEAR);
242
243 ixp12x0_intr_establish(IXPPCI_INTR_T1, IPL_CLOCK, ixpclk_intr, NULL);
244
245 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_LOAD,
246 sc->sc_clock_count);
247 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CONTROL,
248 IXPCL_ENABLE | IXPCL_PERIODIC
249 | IXPCL_STP_CORE);
250 }
251
252 int
253 gettick(void)
254 {
255 int counter;
256 u_int savedints;
257
258 savedints = disable_interrupts(I32_bit);
259 counter = GET_TIMER_VALUE(ixpclk_sc);
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(register struct timeval *tvp)
272 {
273 u_int oldirqstate;
274 u_int32_t counts;
275 static struct timeval lasttv;
276
277 if (ixpclk_sc == NULL) {
278 #ifdef DEBUG
279 printf("microtime: called befor initialize ixpclk\n");
280 #endif
281 tvp->tv_sec = 0;
282 tvp->tv_usec = 0;
283 return;
284 }
285
286 oldirqstate = disable_interrupts(I32_bit);
287
288 counts = ixpclk_sc->sc_clock_count - GET_TIMER_VALUE(ixpclk_sc);
289
290 /* Fill in the timeval struct. */
291 *tvp = time;
292 tvp->tv_usec += counts / ixpclk_sc->sc_count_per_usec;
293
294 /* Make sure microseconds doesn't overflow. */
295 while (tvp->tv_usec >= 1000000) {
296 tvp->tv_usec -= 1000000;
297 tvp->tv_sec++;
298 }
299
300 /* Make sure the time has advanced. */
301 if (tvp->tv_sec == lasttv.tv_sec &&
302 tvp->tv_usec <= lasttv.tv_usec) {
303 tvp->tv_usec = lasttv.tv_usec + 1;
304 if (tvp->tv_usec >= 1000000) {
305 tvp->tv_usec -= 1000000;
306 tvp->tv_sec++;
307 }
308 }
309
310 lasttv = *tvp;
311
312 restore_interrupts(oldirqstate);
313 }
314
315 /*
316 * delay:
317 *
318 * Delay for at least N microseconds.
319 */
320 void
321 delay(unsigned int usecs)
322 {
323 u_int32_t count;
324 u_int32_t ticks;
325 u_int32_t otick;
326 u_int32_t delta;
327 int j;
328 int csec;
329 int 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 ticks = gettick();
357 delta = otick < ticks
358 ? ixpclk_sc->sc_clock_count + otick - ticks
359 : otick - ticks;
360
361 if (delta > count)
362 break;
363
364 count -= delta;
365 otick = ticks;
366 }
367 }
368
369 void
370 resettodr(void)
371 {
372 }
373
374 void
375 inittodr(time_t base)
376 {
377
378 time.tv_sec = base;
379 time.tv_usec = 0;
380 }
381