ixp12x0_clk.c revision 1.7 1 /* $NetBSD: ixp12x0_clk.c,v 1.7 2003/03/25 06:12:46 igy 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.7 2003/03/25 06:12:46 igy 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(parent, match, aux)
135 struct device *parent;
136 struct cfdata *match;
137 void *aux;
138 {
139 return 2;
140 }
141
142 static void
143 ixpclk_attach(parent, self, aux)
144 struct device *parent;
145 struct device *self;
146 void *aux;
147 {
148 struct ixpclk_softc *sc = (struct ixpclk_softc*) self;
149 struct ixpsip_attach_args *sa = aux;
150 u_int32_t ccf;
151
152 printf("\n");
153
154 sc->sc_iot = sa->sa_iot;
155 sc->sc_baseaddr = sa->sa_addr;
156
157 /* using first timer for system ticks */
158 if (ixpclk_sc == NULL)
159 ixpclk_sc = sc;
160
161 if (bus_space_map(sa->sa_iot, sa->sa_addr, sa->sa_size, 0,
162 &sc->sc_ioh))
163 panic("%s: Cannot map registers", self->dv_xname);
164 if (bus_space_map(sa->sa_iot, sa->sa_addr + IXPCLK_PLL_CFG_OFFSET,
165 IXPCLK_PLL_CFG_SIZE, 0, &sc->sc_pll_ioh))
166 panic("%s: Cannot map registers", self->dv_xname);
167
168 /* disable all channel and clear interrupt status */
169 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CONTROL,
170 IXPCL_DISABLE | IXPCL_PERIODIC | IXPCL_STP_CORE);
171 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CLEAR, 0);
172
173
174 ccf = bus_space_read_4(sc->sc_iot, sc->sc_pll_ioh, 0)
175 & IXP12X0_PLL_CFG_CCF;
176 sc->sc_coreclock_freq = ccf_to_coreclock[ccf];
177
178 sc->sc_clock_count = sc->sc_coreclock_freq / hz;
179 sc->sc_count_per_usec = sc->sc_coreclock_freq / 1000000;
180
181 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CLEAR, IXPT_CLEAR);
182 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_LOAD,
183 sc->sc_clock_count);
184 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CONTROL,
185 IXPCL_ENABLE | IXPCL_PERIODIC | IXPCL_STP_CORE);
186
187 printf("%s: IXP12x0 Interval Timer (core clock %d.%03dMHz)\n",
188 sc->sc_dev.dv_xname,
189 sc->sc_coreclock_freq / 1000000,
190 (sc->sc_coreclock_freq % 1000000) / 1000);
191 }
192
193 /*
194 * ixpclk_intr:
195 *
196 * Handle the hardclock interrupt.
197 */
198 static int
199 ixpclk_intr(void *arg)
200
201 {
202 bus_space_write_4(ixpclk_sc->sc_iot, ixpclk_sc->sc_ioh,
203 IXPCLK_CLEAR, 1);
204
205 hardclock((struct clockframe*) arg);
206 return (1);
207 }
208
209 /*
210 * setstatclockrate:
211 *
212 * Set the rate of the statistics clock.
213 *
214 * We assume that hz is either stathz or profhz, and that neither
215 * will change after being set by cpu_initclocks(). We could
216 * recalculate the intervals here, but that would be a pain.
217 */
218 void
219 setstatclockrate(hz)
220 int hz;
221 {
222 /* use hardclock */
223
224 /* XXX should I use TIMER2? */
225 }
226
227 /*
228 * cpu_initclocks:
229 *
230 * Initialize the clock and get them going.
231 */
232 void
233 cpu_initclocks()
234 {
235 struct ixpclk_softc* sc = ixpclk_sc;
236
237 stathz = profhz = 0;
238
239 printf("clock: hz = %d stathz = %d\n", hz, stathz);
240
241 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CONTROL,
242 IXPCL_DISABLE);
243 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CLEAR, IXPT_CLEAR);
244
245 ixp12x0_intr_establish(IXPPCI_INTR_T1, IPL_CLOCK, ixpclk_intr, NULL);
246
247 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_LOAD,
248 sc->sc_clock_count);
249 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CONTROL,
250 IXPCL_ENABLE | IXPCL_PERIODIC
251 | IXPCL_STP_CORE);
252 }
253
254 int
255 gettick()
256 {
257 int counter;
258 u_int savedints;
259 savedints = disable_interrupts(I32_bit);
260
261 counter = GET_TIMER_VALUE(ixpclk_sc);
262
263 restore_interrupts(savedints);
264 return counter;
265 }
266
267 /*
268 * microtime:
269 *
270 * Fill in the specified timeval struct with the current time
271 * accurate to the microsecond.
272 */
273 void
274 microtime(tvp)
275 register struct timeval *tvp;
276 {
277 u_int oldirqstate;
278 u_int32_t counts;
279 static struct timeval lasttv;
280
281 if (ixpclk_sc == NULL) {
282 #ifdef DEBUG
283 printf("microtime: called befor initialize ixpclk\n");
284 #endif
285 tvp->tv_sec = 0;
286 tvp->tv_usec = 0;
287 return;
288 }
289
290 oldirqstate = disable_interrupts(I32_bit);
291
292 counts = ixpclk_sc->sc_clock_count - GET_TIMER_VALUE(ixpclk_sc);
293
294 /* Fill in the timeval struct. */
295 *tvp = time;
296 tvp->tv_usec += counts / ixpclk_sc->sc_count_per_usec;
297
298 /* Make sure microseconds doesn't overflow. */
299 while (tvp->tv_usec >= 1000000) {
300 tvp->tv_usec -= 1000000;
301 tvp->tv_sec++;
302 }
303
304 /* Make sure the time has advanced. */
305 if (tvp->tv_sec == lasttv.tv_sec &&
306 tvp->tv_usec <= lasttv.tv_usec) {
307 tvp->tv_usec = lasttv.tv_usec + 1;
308 if (tvp->tv_usec >= 1000000) {
309 tvp->tv_usec -= 1000000;
310 tvp->tv_sec++;
311 }
312 }
313
314 lasttv = *tvp;
315
316 restore_interrupts(oldirqstate);
317 }
318
319 /*
320 * delay:
321 *
322 * Delay for at least N microseconds.
323 */
324 void
325 delay(unsigned int usecs)
326 {
327 u_int32_t count;
328 u_int32_t tick;
329 u_int32_t otick;
330 u_int32_t delta;
331 int j;
332 int csec, usec;
333
334 if (ixpclk_sc == NULL) {
335 #ifdef DEBUG
336 printf("delay: called befor start ixpclk\n");
337 #endif
338
339 csec = usecs / 10000;
340 usec = usecs % 10000;
341
342 usecs = (TIMER_FREQUENCY / 100) * csec
343 + (TIMER_FREQUENCY / 100) * usec / 10000;
344 /* clock isn't initialized yet */
345 for(; usecs > 0; usecs--)
346 for(j = 100; j > 0; j--)
347 ;
348 return;
349 }
350
351 count = ixpclk_sc->sc_count_per_usec * usecs;
352
353 otick = gettick();
354
355 for (;;) {
356 for(j = 100; j > 0; j--)
357 ;
358
359 tick = gettick();
360 delta = otick < tick
361 ? ixpclk_sc->sc_clock_count + otick - tick
362 : otick - tick;
363
364 if (delta > count)
365 break;
366
367 count -= delta;
368 otick = tick;
369 }
370 }
371
372 void
373 resettodr()
374 {
375 }
376
377 void
378 inittodr(base)
379 time_t base;
380 {
381 time.tv_sec = base;
382 time.tv_usec = 0;
383 }
384