ixp12x0_clk.c revision 1.13 1 /* $NetBSD: ixp12x0_clk.c,v 1.13 2008/04/28 20:23:14 martin 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 *
20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 */
32
33 #include <sys/cdefs.h>
34 __KERNEL_RCSID(0, "$NetBSD: ixp12x0_clk.c,v 1.13 2008/04/28 20:23:14 martin Exp $");
35
36 #include <sys/types.h>
37 #include <sys/param.h>
38 #include <sys/atomic.h>
39 #include <sys/systm.h>
40 #include <sys/kernel.h>
41 #include <sys/time.h>
42 #include <sys/timetc.h>
43 #include <sys/device.h>
44
45 #include <machine/bus.h>
46 #include <machine/intr.h>
47
48 #include <arm/cpufunc.h>
49
50 #include <arm/ixp12x0/ixpsipvar.h>
51
52 #include <arm/ixp12x0/ixp12x0_pcireg.h>
53 #include <arm/ixp12x0/ixp12x0_clkreg.h>
54 #include <arm/ixp12x0/ixp12x0var.h>
55
56 static int ixpclk_match(struct device *, struct cfdata *, void *);
57 static void ixpclk_attach(struct device *, struct device *, void *);
58
59 static u_int ixpclk_get_timecount(struct timecounter *);
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 static struct timecounter ixpclk_timecounter = {
120 ixpclk_get_timecount, /* get_timecount */
121 0, /* no poll_pps */
122 0xffffffff, /* counter_mask */
123 0, /* frequency */
124 "ixpclk", /* name */
125 100, /* quality */
126 NULL, /* prev */
127 NULL, /* next */
128 };
129
130 static volatile uint32_t ixpclk_base;
131
132 #define TIMER_FREQUENCY 3686400 /* 3.6864MHz */
133 #define TICKS_PER_MICROSECOND (TIMER_FREQUENCY/1000000)
134
135 CFATTACH_DECL(ixpclk, sizeof(struct ixpclk_softc),
136 ixpclk_match, ixpclk_attach, NULL, NULL);
137
138 #define GET_TIMER_VALUE(sc) (bus_space_read_4((sc)->sc_iot, \
139 (sc)->sc_ioh, \
140 IXPCLK_VALUE) \
141 & IXPCL_CTV)
142
143 static int
144 ixpclk_match(struct device *parent, struct cfdata *match, void *aux)
145 {
146
147 return 2;
148 }
149
150 static void
151 ixpclk_attach(struct device *parent, struct device *self, void *aux)
152 {
153 struct ixpclk_softc *sc;
154 struct ixpsip_attach_args *sa;
155 u_int32_t ccf;
156 bool first_run = ixpclk_sc == NULL;
157
158 printf("\n");
159
160 sc = (struct ixpclk_softc*) self;
161 sa = aux;
162 sc->sc_iot = sa->sa_iot;
163 sc->sc_baseaddr = sa->sa_addr;
164
165 /* using first timer for system ticks */
166 if (ixpclk_sc == NULL)
167 ixpclk_sc = sc;
168
169 if (bus_space_map(sa->sa_iot, sa->sa_addr, sa->sa_size, 0,
170 &sc->sc_ioh))
171 panic("%s: Cannot map registers", self->dv_xname);
172 if (bus_space_map(sa->sa_iot, sa->sa_addr + IXPCLK_PLL_CFG_OFFSET,
173 IXPCLK_PLL_CFG_SIZE, 0, &sc->sc_pll_ioh))
174 panic("%s: Cannot map registers", self->dv_xname);
175
176 /* disable all channel and clear interrupt status */
177 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CONTROL,
178 IXPCL_DISABLE | IXPCL_PERIODIC | IXPCL_STP_CORE);
179 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CLEAR, 0);
180
181
182 ccf = bus_space_read_4(sc->sc_iot, sc->sc_pll_ioh, 0)
183 & IXP12X0_PLL_CFG_CCF;
184 sc->sc_coreclock_freq = ccf_to_coreclock[ccf];
185
186 sc->sc_clock_count = sc->sc_coreclock_freq / hz;
187 sc->sc_count_per_usec = sc->sc_coreclock_freq / 1000000;
188
189 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CLEAR, IXPT_CLEAR);
190 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_LOAD,
191 sc->sc_clock_count);
192 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CONTROL,
193 IXPCL_ENABLE | IXPCL_PERIODIC | IXPCL_STP_CORE);
194
195 if (first_run) {
196 ixpclk_timecounter.tc_frequency = sc->sc_coreclock_freq;
197 tc_init(&ixpclk_timecounter);
198 }
199
200 printf("%s: IXP12x0 Interval Timer (core clock %d.%03dMHz)\n",
201 sc->sc_dev.dv_xname,
202 sc->sc_coreclock_freq / 1000000,
203 (sc->sc_coreclock_freq % 1000000) / 1000);
204 }
205
206 /*
207 * ixpclk_intr:
208 *
209 * Handle the hardclock interrupt.
210 */
211 static int
212 ixpclk_intr(void *arg)
213 {
214
215 bus_space_write_4(ixpclk_sc->sc_iot, ixpclk_sc->sc_ioh,
216 IXPCLK_CLEAR, 1);
217
218 atomic_add_32(&ixpclk_base, ixpclk_sc->sc_coreclock_freq);
219
220 hardclock((struct clockframe*) arg);
221 return (1);
222 }
223
224 /*
225 * setstatclockrate:
226 *
227 * Set the rate of the statistics clock.
228 *
229 * We assume that hz is either stathz or profhz, and that neither
230 * will change after being set by cpu_initclocks(). We could
231 * recalculate the intervals here, but that would be a pain.
232 */
233 void
234 setstatclockrate(int newhz)
235 {
236
237 /* use hardclock */
238
239 /* XXX should I use TIMER2? */
240 }
241
242 /*
243 * cpu_initclocks:
244 *
245 * Initialize the clock and get them going.
246 */
247 void
248 cpu_initclocks(void)
249 {
250 struct ixpclk_softc* sc;
251
252 sc = ixpclk_sc;
253 stathz = profhz = 0;
254
255 printf("clock: hz = %d stathz = %d\n", hz, stathz);
256
257 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CONTROL,
258 IXPCL_DISABLE);
259 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CLEAR, IXPT_CLEAR);
260
261 ixp12x0_intr_establish(IXPPCI_INTR_T1, IPL_CLOCK, ixpclk_intr, NULL);
262
263 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_LOAD,
264 sc->sc_clock_count);
265 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CONTROL,
266 IXPCL_ENABLE | IXPCL_PERIODIC
267 | IXPCL_STP_CORE);
268 }
269
270 int
271 gettick(void)
272 {
273 int counter;
274 u_int savedints;
275
276 savedints = disable_interrupts(I32_bit);
277 counter = GET_TIMER_VALUE(ixpclk_sc);
278 restore_interrupts(savedints);
279 return counter;
280 }
281
282 static u_int
283 ixpclk_get_timecount(struct timecounter *tc)
284 {
285 u_int savedints, base, counter;
286
287 savedints = disable_interrupts(I32_bit);
288 do {
289 base = ixpclk_base;
290 counter = GET_TIMER_VALUE(ixpclk_sc);
291 } while (base != ixpclk_base);
292 restore_interrupts(savedints);
293
294 return base - counter;
295 }
296
297 /*
298 * delay:
299 *
300 * Delay for at least N microseconds.
301 */
302 void
303 delay(unsigned int usecs)
304 {
305 u_int32_t count;
306 u_int32_t ticks;
307 u_int32_t otick;
308 u_int32_t delta;
309 int j;
310 int csec;
311 int usec;
312
313 if (ixpclk_sc == NULL) {
314 #ifdef DEBUG
315 printf("delay: called befor start ixpclk\n");
316 #endif
317
318 csec = usecs / 10000;
319 usec = usecs % 10000;
320
321 usecs = (TIMER_FREQUENCY / 100) * csec
322 + (TIMER_FREQUENCY / 100) * usec / 10000;
323 /* clock isn't initialized yet */
324 for(; usecs > 0; usecs--)
325 for(j = 100; j > 0; j--)
326 ;
327 return;
328 }
329
330 count = ixpclk_sc->sc_count_per_usec * usecs;
331
332 otick = gettick();
333
334 for (;;) {
335 for(j = 100; j > 0; j--)
336 ;
337
338 ticks = gettick();
339 delta = otick < ticks
340 ? ixpclk_sc->sc_clock_count + otick - ticks
341 : otick - ticks;
342
343 if (delta > count)
344 break;
345
346 count -= delta;
347 otick = ticks;
348 }
349 }
350