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