]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - sys/powerpc/powerpc/clock.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.git] / sys / powerpc / powerpc / clock.c
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 + (n * 1000 + ns_per_tick - 1) / ns_per_tick;
305         while (tb < ttb)
306                 tb = mftb();
307 }
308