ixp12x0_clk.c revision 1.16 1 /* $NetBSD: ixp12x0_clk.c,v 1.16 2012/10/27 17:17:39 chs 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.16 2012/10/27 17:17:39 chs Exp $");
42
43 #include <sys/types.h>
44 #include <sys/param.h>
45 #include <sys/atomic.h>
46 #include <sys/systm.h>
47 #include <sys/kernel.h>
48 #include <sys/time.h>
49 #include <sys/timetc.h>
50 #include <sys/device.h>
51
52 #include <sys/bus.h>
53 #include <machine/intr.h>
54
55 #include <arm/cpufunc.h>
56
57 #include <arm/ixp12x0/ixpsipvar.h>
58
59 #include <arm/ixp12x0/ixp12x0_pcireg.h>
60 #include <arm/ixp12x0/ixp12x0_clkreg.h>
61 #include <arm/ixp12x0/ixp12x0var.h>
62
63 static int ixpclk_match(device_t, cfdata_t, void *);
64 static void ixpclk_attach(device_t, device_t, void *);
65
66 static u_int ixpclk_get_timecount(struct timecounter *);
67
68 int gettick(void);
69 void rtcinit(void);
70
71 /* callback functions for intr_functions */
72 static int ixpclk_intr(void* arg);
73
74 struct ixpclk_softc {
75 bus_addr_t sc_baseaddr;
76 bus_space_tag_t sc_iot;
77 bus_space_handle_t sc_ioh;
78 bus_space_handle_t sc_pll_ioh;
79
80 u_int32_t sc_clock_count;
81 u_int32_t sc_count_per_usec;
82 u_int32_t sc_coreclock_freq;
83 };
84
85 #define XTAL_FREQ 3686400 /* 3.6864MHz */
86 #define XTAL_FREQ3686400
87 #undef XTAL_FREQ3787800
88 #undef XTAL_FREQ3579500
89 #define MAX_CCF 22
90
91 #if defined(XTAL_FREQ3686400)
92 static u_int32_t ccf_to_coreclock[MAX_CCF + 1] = {
93 29491000,
94 36865000,
95 44237000,
96 51610000,
97 58982000,
98 66355000,
99 73728000,
100 81101000,
101 88474000,
102 95846000,
103 103219000,
104 110592000,
105 132710000,
106 147456000,
107 154829000,
108 162202000,
109 165890000,
110 176947000,
111 191693000,
112 199066000,
113 206438000,
114 221184000,
115 232243000,
116 };
117 #elif defined(XTAL_FREQ3787800)
118 #elif defined(XTAL_FREQ3579500)
119 #else
120 #error
121 #endif
122
123 static struct ixpclk_softc *ixpclk_sc = NULL;
124
125 static struct timecounter ixpclk_timecounter = {
126 ixpclk_get_timecount, /* get_timecount */
127 0, /* no poll_pps */
128 0xffffffff, /* counter_mask */
129 0, /* frequency */
130 "ixpclk", /* name */
131 100, /* quality */
132 NULL, /* prev */
133 NULL, /* next */
134 };
135
136 static volatile uint32_t ixpclk_base;
137
138 #define TIMER_FREQUENCY 3686400 /* 3.6864MHz */
139 #define TICKS_PER_MICROSECOND (TIMER_FREQUENCY/1000000)
140
141 CFATTACH_DECL_NEW(ixpclk, sizeof(struct ixpclk_softc),
142 ixpclk_match, ixpclk_attach, NULL, NULL);
143
144 #define GET_TIMER_VALUE(sc) (bus_space_read_4((sc)->sc_iot, \
145 (sc)->sc_ioh, \
146 IXPCLK_VALUE) \
147 & IXPCL_CTV)
148
149 static int
150 ixpclk_match(device_t parent, cfdata_t match, void *aux)
151 {
152
153 return 2;
154 }
155
156 static void
157 ixpclk_attach(device_t parent, device_t self, void *aux)
158 {
159 struct ixpclk_softc *sc;
160 struct ixpsip_attach_args *sa;
161 u_int32_t ccf;
162 bool first_run = ixpclk_sc == NULL;
163
164 printf("\n");
165
166 sc = device_private(self);
167 sa = aux;
168 sc->sc_iot = sa->sa_iot;
169 sc->sc_baseaddr = sa->sa_addr;
170
171 /* using first timer for system ticks */
172 if (ixpclk_sc == NULL)
173 ixpclk_sc = sc;
174
175 if (bus_space_map(sa->sa_iot, sa->sa_addr, sa->sa_size, 0,
176 &sc->sc_ioh))
177 panic("%s: Cannot map registers", device_xname(self));
178 if (bus_space_map(sa->sa_iot, sa->sa_addr + IXPCLK_PLL_CFG_OFFSET,
179 IXPCLK_PLL_CFG_SIZE, 0, &sc->sc_pll_ioh))
180 panic("%s: Cannot map registers", device_xname(self));
181
182 /* disable all channel and clear interrupt status */
183 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CONTROL,
184 IXPCL_DISABLE | IXPCL_PERIODIC | IXPCL_STP_CORE);
185 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CLEAR, 0);
186
187
188 ccf = bus_space_read_4(sc->sc_iot, sc->sc_pll_ioh, 0)
189 & IXP12X0_PLL_CFG_CCF;
190 sc->sc_coreclock_freq = ccf_to_coreclock[ccf];
191
192 sc->sc_clock_count = sc->sc_coreclock_freq / hz;
193 sc->sc_count_per_usec = sc->sc_coreclock_freq / 1000000;
194
195 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CLEAR, IXPT_CLEAR);
196 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_LOAD,
197 sc->sc_clock_count);
198 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CONTROL,
199 IXPCL_ENABLE | IXPCL_PERIODIC | IXPCL_STP_CORE);
200
201 if (first_run) {
202 ixpclk_timecounter.tc_frequency = sc->sc_coreclock_freq;
203 tc_init(&ixpclk_timecounter);
204 }
205
206 printf("%s: IXP12x0 Interval Timer (core clock %d.%03dMHz)\n",
207 device_xname(self),
208 sc->sc_coreclock_freq / 1000000,
209 (sc->sc_coreclock_freq % 1000000) / 1000);
210 }
211
212 /*
213 * ixpclk_intr:
214 *
215 * Handle the hardclock interrupt.
216 */
217 static int
218 ixpclk_intr(void *arg)
219 {
220
221 bus_space_write_4(ixpclk_sc->sc_iot, ixpclk_sc->sc_ioh,
222 IXPCLK_CLEAR, 1);
223
224 atomic_add_32(&ixpclk_base, ixpclk_sc->sc_coreclock_freq);
225
226 hardclock((struct clockframe*) arg);
227 return (1);
228 }
229
230 /*
231 * setstatclockrate:
232 *
233 * Set the rate of the statistics clock.
234 *
235 * We assume that hz is either stathz or profhz, and that neither
236 * will change after being set by cpu_initclocks(). We could
237 * recalculate the intervals here, but that would be a pain.
238 */
239 void
240 setstatclockrate(int newhz)
241 {
242
243 /* use hardclock */
244
245 /* XXX should I use TIMER2? */
246 }
247
248 /*
249 * cpu_initclocks:
250 *
251 * Initialize the clock and get them going.
252 */
253 void
254 cpu_initclocks(void)
255 {
256 struct ixpclk_softc* sc;
257
258 sc = ixpclk_sc;
259 stathz = profhz = 0;
260
261 printf("clock: hz = %d stathz = %d\n", hz, stathz);
262
263 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CONTROL,
264 IXPCL_DISABLE);
265 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CLEAR, IXPT_CLEAR);
266
267 ixp12x0_intr_establish(IXPPCI_INTR_T1, IPL_CLOCK, ixpclk_intr, NULL);
268
269 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_LOAD,
270 sc->sc_clock_count);
271 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CONTROL,
272 IXPCL_ENABLE | IXPCL_PERIODIC
273 | IXPCL_STP_CORE);
274 }
275
276 int
277 gettick(void)
278 {
279 int counter;
280 u_int savedints;
281
282 savedints = disable_interrupts(I32_bit);
283 counter = GET_TIMER_VALUE(ixpclk_sc);
284 restore_interrupts(savedints);
285 return counter;
286 }
287
288 static u_int
289 ixpclk_get_timecount(struct timecounter *tc)
290 {
291 u_int savedints, base, counter;
292
293 savedints = disable_interrupts(I32_bit);
294 do {
295 base = ixpclk_base;
296 counter = GET_TIMER_VALUE(ixpclk_sc);
297 } while (base != ixpclk_base);
298 restore_interrupts(savedints);
299
300 return base - counter;
301 }
302
303 /*
304 * delay:
305 *
306 * Delay for at least N microseconds.
307 */
308 void
309 delay(unsigned int usecs)
310 {
311 u_int32_t count;
312 u_int32_t ticks;
313 u_int32_t otick;
314 u_int32_t delta;
315 int j;
316 int csec;
317 int usec;
318
319 if (ixpclk_sc == NULL) {
320 #ifdef DEBUG
321 printf("delay: called befor start ixpclk\n");
322 #endif
323
324 csec = usecs / 10000;
325 usec = usecs % 10000;
326
327 usecs = (TIMER_FREQUENCY / 100) * csec
328 + (TIMER_FREQUENCY / 100) * usec / 10000;
329 /* clock isn't initialized yet */
330 for(; usecs > 0; usecs--)
331 for(j = 100; j > 0; j--)
332 ;
333 return;
334 }
335
336 count = ixpclk_sc->sc_count_per_usec * usecs;
337
338 otick = gettick();
339
340 for (;;) {
341 for(j = 100; j > 0; j--)
342 ;
343
344 ticks = gettick();
345 delta = otick < ticks
346 ? ixpclk_sc->sc_clock_count + otick - ticks
347 : otick - ticks;
348
349 if (delta > count)
350 break;
351
352 count -= delta;
353 otick = ticks;
354 }
355 }
356