1 /* $NetBSD: ixp12x0_clk.c,v 1.19 2021/07/31 14:36:33 andvar 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.19 2021/07/31 14:36:33 andvar 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 uint32_t sc_clock_count;
81 uint32_t sc_count_per_usec;
82 uint32_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 uint32_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 .tc_get_timecount = ixpclk_get_timecount,
127 .tc_counter_mask = 0xffffffff,
128 .tc_name = "ixpclk",
129 .tc_quality = 100,
130 };
131
132 static volatile uint32_t ixpclk_base;
133
134 #define TIMER_FREQUENCY 3686400 /* 3.6864MHz */
135 #define TICKS_PER_MICROSECOND (TIMER_FREQUENCY/1000000)
136
137 CFATTACH_DECL_NEW(ixpclk, sizeof(struct ixpclk_softc),
138 ixpclk_match, ixpclk_attach, NULL, NULL);
139
140 #define GET_TIMER_VALUE(sc) (bus_space_read_4((sc)->sc_iot, \
141 (sc)->sc_ioh, \
142 IXPCLK_VALUE) \
143 & IXPCL_CTV)
144
145 static int
ixpclk_match(device_t parent,cfdata_t match,void * aux)146 ixpclk_match(device_t parent, cfdata_t match, void *aux)
147 {
148
149 return 2;
150 }
151
152 static void
ixpclk_attach(device_t parent,device_t self,void * aux)153 ixpclk_attach(device_t parent, device_t self, void *aux)
154 {
155 struct ixpclk_softc *sc;
156 struct ixpsip_attach_args *sa;
157 uint32_t ccf;
158 bool first_run = ixpclk_sc == NULL;
159
160 printf("\n");
161
162 sc = device_private(self);
163 sa = aux;
164 sc->sc_iot = sa->sa_iot;
165 sc->sc_baseaddr = sa->sa_addr;
166
167 /* using first timer for system ticks */
168 if (ixpclk_sc == NULL)
169 ixpclk_sc = sc;
170
171 if (bus_space_map(sa->sa_iot, sa->sa_addr, sa->sa_size, 0,
172 &sc->sc_ioh))
173 panic("%s: Cannot map registers", device_xname(self));
174 if (bus_space_map(sa->sa_iot, sa->sa_addr + IXPCLK_PLL_CFG_OFFSET,
175 IXPCLK_PLL_CFG_SIZE, 0, &sc->sc_pll_ioh))
176 panic("%s: Cannot map registers", device_xname(self));
177
178 /* disable all channel and clear interrupt status */
179 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CONTROL,
180 IXPCL_DISABLE | IXPCL_PERIODIC | IXPCL_STP_CORE);
181 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CLEAR, 0);
182
183
184 ccf = bus_space_read_4(sc->sc_iot, sc->sc_pll_ioh, 0)
185 & IXP12X0_PLL_CFG_CCF;
186 sc->sc_coreclock_freq = ccf_to_coreclock[ccf];
187
188 sc->sc_clock_count = sc->sc_coreclock_freq / hz;
189 sc->sc_count_per_usec = sc->sc_coreclock_freq / 1000000;
190
191 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CLEAR, IXPT_CLEAR);
192 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_LOAD,
193 sc->sc_clock_count);
194 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CONTROL,
195 IXPCL_ENABLE | IXPCL_PERIODIC | IXPCL_STP_CORE);
196
197 if (first_run) {
198 ixpclk_timecounter.tc_frequency = sc->sc_coreclock_freq;
199 tc_init(&ixpclk_timecounter);
200 }
201
202 printf("%s: IXP12x0 Interval Timer (core clock %d.%03dMHz)\n",
203 device_xname(self),
204 sc->sc_coreclock_freq / 1000000,
205 (sc->sc_coreclock_freq % 1000000) / 1000);
206 }
207
208 /*
209 * ixpclk_intr:
210 *
211 * Handle the hardclock interrupt.
212 */
213 static int
ixpclk_intr(void * arg)214 ixpclk_intr(void *arg)
215 {
216
217 bus_space_write_4(ixpclk_sc->sc_iot, ixpclk_sc->sc_ioh,
218 IXPCLK_CLEAR, 1);
219
220 atomic_add_32(&ixpclk_base, ixpclk_sc->sc_coreclock_freq);
221
222 hardclock((struct clockframe*) arg);
223 return (1);
224 }
225
226 /*
227 * setstatclockrate:
228 *
229 * Set the rate of the statistics clock.
230 *
231 * We assume that hz is either stathz or profhz, and that neither
232 * will change after being set by cpu_initclocks(). We could
233 * recalculate the intervals here, but that would be a pain.
234 */
235 void
setstatclockrate(int newhz)236 setstatclockrate(int newhz)
237 {
238
239 /* use hardclock */
240
241 /* XXX should I use TIMER2? */
242 }
243
244 /*
245 * cpu_initclocks:
246 *
247 * Initialize the clock and get them going.
248 */
249 void
cpu_initclocks(void)250 cpu_initclocks(void)
251 {
252 struct ixpclk_softc* sc;
253
254 sc = ixpclk_sc;
255 stathz = profhz = 0;
256
257 printf("clock: hz = %d stathz = %d\n", hz, stathz);
258
259 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CONTROL,
260 IXPCL_DISABLE);
261 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CLEAR, IXPT_CLEAR);
262
263 ixp12x0_intr_establish(IXPPCI_INTR_T1, IPL_CLOCK, ixpclk_intr, NULL);
264
265 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_LOAD,
266 sc->sc_clock_count);
267 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CONTROL,
268 IXPCL_ENABLE | IXPCL_PERIODIC
269 | IXPCL_STP_CORE);
270 }
271
272 int
gettick(void)273 gettick(void)
274 {
275 int counter;
276 u_int savedints;
277
278 savedints = disable_interrupts(I32_bit);
279 counter = GET_TIMER_VALUE(ixpclk_sc);
280 restore_interrupts(savedints);
281 return counter;
282 }
283
284 static u_int
ixpclk_get_timecount(struct timecounter * tc)285 ixpclk_get_timecount(struct timecounter *tc)
286 {
287 u_int savedints, base, counter;
288
289 savedints = disable_interrupts(I32_bit);
290 do {
291 base = ixpclk_base;
292 counter = GET_TIMER_VALUE(ixpclk_sc);
293 } while (base != ixpclk_base);
294 restore_interrupts(savedints);
295
296 return base - counter;
297 }
298
299 /*
300 * delay:
301 *
302 * Delay for at least N microseconds.
303 */
304 void
delay(unsigned int usecs)305 delay(unsigned int usecs)
306 {
307 uint32_t count;
308 uint32_t ticks;
309 uint32_t otick;
310 uint32_t delta;
311 int j;
312 int csec;
313 int usec;
314
315 if (ixpclk_sc == NULL) {
316 #ifdef DEBUG
317 printf("delay: called before start ixpclk\n");
318 #endif
319
320 csec = usecs / 10000;
321 usec = usecs % 10000;
322
323 usecs = (TIMER_FREQUENCY / 100) * csec
324 + (TIMER_FREQUENCY / 100) * usec / 10000;
325 /* clock isn't initialized yet */
326 for(; usecs > 0; usecs--)
327 for(j = 100; j > 0; j--)
328 ;
329 return;
330 }
331
332 count = ixpclk_sc->sc_count_per_usec * usecs;
333
334 otick = gettick();
335
336 for (;;) {
337 for(j = 100; j > 0; j--)
338 ;
339
340 ticks = gettick();
341 delta = otick < ticks
342 ? ixpclk_sc->sc_clock_count + otick - ticks
343 : otick - ticks;
344
345 if (delta > count)
346 break;
347
348 count -= delta;
349 otick = ticks;
350 }
351 }
352