xref: /freebsd-src/sys/powerpc/powerpc/clock.c (revision 63d1fd5970ec814904aa0f4580b10a0d302d08b2)
1 /*-
2  * Copyright (C) 1995, 1996 Wolfgang Solfrank.
3  * Copyright (C) 1995, 1996 TooLs GmbH.
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. All advertising materials mentioning features or use of this software
15  *    must display the following acknowledgement:
16  *	This product includes software developed by TooLs GmbH.
17  * 4. The name of TooLs GmbH may not be used to endorse or promote products
18  *    derived from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
21  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23  * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
26  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
27  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
28  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
29  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  *
31  *	$NetBSD: clock.c,v 1.9 2000/01/19 02:52:19 msaitoh Exp $
32  */
33 /*
34  * Copyright (C) 2001 Benno Rice.
35  * All rights reserved.
36  *
37  * Redistribution and use in source and binary forms, with or without
38  * modification, are permitted provided that the following conditions
39  * are met:
40  * 1. Redistributions of source code must retain the above copyright
41  *    notice, this list of conditions and the following disclaimer.
42  * 2. Redistributions in binary form must reproduce the above copyright
43  *    notice, this list of conditions and the following disclaimer in the
44  *    documentation and/or other materials provided with the distribution.
45  *
46  * THIS SOFTWARE IS PROVIDED BY Benno Rice ``AS IS'' AND ANY EXPRESS OR
47  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
48  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
49  * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
50  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
51  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
52  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
53  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
54  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
55  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
56  */
57 
58 #include <sys/cdefs.h>
59 __FBSDID("$FreeBSD$");
60 
61 #include <sys/param.h>
62 #include <sys/systm.h>
63 #include <sys/kernel.h>
64 #include <sys/bus.h>
65 #include <sys/interrupt.h>
66 #include <sys/pcpu.h>
67 #include <sys/sysctl.h>
68 #include <sys/timeet.h>
69 #include <sys/timetc.h>
70 
71 #include <dev/ofw/openfirm.h>
72 
73 #include <machine/clock.h>
74 #include <machine/cpu.h>
75 #include <machine/intr_machdep.h>
76 #include <machine/md_var.h>
77 #include <machine/smp.h>
78 
79 /*
80  * Initially we assume a processor with a bus frequency of 12.5 MHz.
81  */
82 static int		initialized = 0;
83 static u_long		ns_per_tick = 80;
84 static u_long		ticks_per_sec = 12500000;
85 static u_long		*decr_counts[MAXCPU];
86 
87 static int		decr_et_start(struct eventtimer *et,
88     sbintime_t first, sbintime_t period);
89 static int		decr_et_stop(struct eventtimer *et);
90 static timecounter_get_t	decr_get_timecount;
91 
92 struct decr_state {
93 	int	mode;	/* 0 - off, 1 - periodic, 2 - one-shot. */
94 	int32_t	div;	/* Periodic divisor. */
95 };
96 static DPCPU_DEFINE(struct decr_state, decr_state);
97 
98 static struct eventtimer	decr_et;
99 static struct timecounter	decr_tc = {
100 	decr_get_timecount,	/* get_timecount */
101 	0,			/* no poll_pps */
102 	~0u,			/* counter_mask */
103 	0,			/* frequency */
104 	"timebase"		/* name */
105 };
106 
107 /*
108  * Decrementer interrupt handler.
109  */
110 void
111 decr_intr(struct trapframe *frame)
112 {
113 	struct decr_state *s = DPCPU_PTR(decr_state);
114 	int		nticks = 0;
115 	int32_t		val;
116 
117 	if (!initialized)
118 		return;
119 
120 	(*decr_counts[curcpu])++;
121 
122 #ifdef BOOKE
123 	/*
124 	 * Interrupt handler must reset DIS to avoid getting another
125 	 * interrupt once EE is enabled.
126 	 */
127 	mtspr(SPR_TSR, TSR_DIS);
128 #endif
129 
130 	if (s->mode == 1) {
131 		/*
132 		 * Based on the actual time delay since the last decrementer
133 		 * reload, we arrange for earlier interrupt next time.
134 		 */
135 		__asm ("mfdec %0" : "=r"(val));
136 		while (val < 0) {
137 			val += s->div;
138 			nticks++;
139 		}
140 		mtdec(val);
141 	} else if (s->mode == 2) {
142 		nticks = 1;
143 		decr_et_stop(NULL);
144 	}
145 
146 	while (nticks-- > 0) {
147 		if (decr_et.et_active)
148 			decr_et.et_event_cb(&decr_et, decr_et.et_arg);
149 	}
150 }
151 
152 void
153 cpu_initclocks(void)
154 {
155 
156 	decr_tc_init();
157 	cpu_initclocks_bsp();
158 }
159 
160 /*
161  * BSP early initialization.
162  */
163 void
164 decr_init(void)
165 {
166 	struct cpuref cpu;
167 	char buf[32];
168 
169 	/*
170 	 * Check the BSP's timebase frequency. Sometimes we can't find the BSP,
171 	 * so fall back to the first CPU in this case.
172 	 */
173 	if (platform_smp_get_bsp(&cpu) != 0)
174 		platform_smp_first_cpu(&cpu);
175 	ticks_per_sec = platform_timebase_freq(&cpu);
176 	ns_per_tick = 1000000000 / ticks_per_sec;
177 
178 	set_cputicker(mftb, ticks_per_sec, 0);
179 	snprintf(buf, sizeof(buf), "cpu%d:decrementer", curcpu);
180 	intrcnt_add(buf, &decr_counts[curcpu]);
181 	decr_et_stop(NULL);
182 	initialized = 1;
183 }
184 
185 #ifdef SMP
186 /*
187  * AP early initialization.
188  */
189 void
190 decr_ap_init(void)
191 {
192 	char buf[32];
193 
194 	snprintf(buf, sizeof(buf), "cpu%d:decrementer", curcpu);
195 	intrcnt_add(buf, &decr_counts[curcpu]);
196 	decr_et_stop(NULL);
197 }
198 #endif
199 
200 /*
201  * Final initialization.
202  */
203 void
204 decr_tc_init(void)
205 {
206 
207 	decr_tc.tc_frequency = ticks_per_sec;
208 	tc_init(&decr_tc);
209 	decr_et.et_name = "decrementer";
210 	decr_et.et_flags = ET_FLAGS_PERIODIC | ET_FLAGS_ONESHOT |
211 	    ET_FLAGS_PERCPU;
212 	decr_et.et_quality = 1000;
213 	decr_et.et_frequency = ticks_per_sec;
214 	decr_et.et_min_period = (0x00000002LLU << 32) / ticks_per_sec;
215 	decr_et.et_max_period = (0x7fffffffLLU << 32) / ticks_per_sec;
216 	decr_et.et_start = decr_et_start;
217 	decr_et.et_stop = decr_et_stop;
218 	decr_et.et_priv = NULL;
219 	et_register(&decr_et);
220 }
221 
222 /*
223  * Event timer start method.
224  */
225 static int
226 decr_et_start(struct eventtimer *et, sbintime_t first, sbintime_t period)
227 {
228 	struct decr_state *s = DPCPU_PTR(decr_state);
229 	uint32_t fdiv;
230 #ifdef BOOKE
231 	uint32_t tcr;
232 #endif
233 
234 	if (period != 0) {
235 		s->mode = 1;
236 		s->div = (decr_et.et_frequency * period) >> 32;
237 	} else {
238 		s->mode = 2;
239 		s->div = 0;
240 	}
241 	if (first != 0)
242 		fdiv = (decr_et.et_frequency * first) >> 32;
243 	else
244 		fdiv = s->div;
245 
246 #ifdef BOOKE
247 	tcr = mfspr(SPR_TCR);
248 	tcr |= TCR_DIE;
249 	if (s->mode == 1) {
250 		mtspr(SPR_DECAR, s->div);
251 		tcr |= TCR_ARE;
252 	} else
253 		tcr &= ~TCR_ARE;
254 	mtdec(fdiv);
255 	mtspr(SPR_TCR, tcr);
256 #else
257 	mtdec(fdiv);
258 #endif
259 
260 	return (0);
261 }
262 
263 /*
264  * Event timer stop method.
265  */
266 static int
267 decr_et_stop(struct eventtimer *et)
268 {
269 	struct decr_state *s = DPCPU_PTR(decr_state);
270 #ifdef BOOKE
271 	uint32_t tcr;
272 #endif
273 
274 	s->mode = 0;
275 	s->div = 0x7fffffff;
276 #ifdef BOOKE
277 	tcr = mfspr(SPR_TCR);
278 	tcr &= ~(TCR_DIE | TCR_ARE);
279 	mtspr(SPR_TCR, tcr);
280 #else
281 	mtdec(s->div);
282 #endif
283 	return (0);
284 }
285 
286 /*
287  * Timecounter get method.
288  */
289 static unsigned
290 decr_get_timecount(struct timecounter *tc)
291 {
292 	return (mftb());
293 }
294 
295 /*
296  * Wait for about n microseconds (at least!).
297  */
298 void
299 DELAY(int n)
300 {
301 	u_quad_t	tb, ttb;
302 
303 	tb = mftb();
304 	ttb = tb + howmany(n * 1000, ns_per_tick);
305 	while (tb < ttb)
306 		tb = mftb();
307 }
308 
309