xref: /netbsd-src/sys/arch/arm/ixp12x0/ixp12x0_clk.c (revision 31f72197e0fc9652d188d0522c299cbf73698d87)
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