xref: /netbsd-src/sys/arch/arm/ixp12x0/ixp12x0_clk.c (revision 92e958de60c71aa0f2452bd7074cbb006fe6546b)
1 /*	$NetBSD: ixp12x0_clk.c,v 1.17 2012/11/12 18:00:37 skrll 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.17 2012/11/12 18:00:37 skrll 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 	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 	uint32_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 	uint32_t	count;
312 	uint32_t	ticks;
313 	uint32_t	otick;
314 	uint32_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