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