]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - sys/powerpc/aim/clock.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / sys / powerpc / aim / 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         if (s->mode == 1) {
123                 /*
124                  * Based on the actual time delay since the last decrementer
125                  * reload, we arrange for earlier interrupt next time.
126                  */
127                 __asm ("mfdec %0" : "=r"(val));
128                 while (val < 0) {
129                         val += s->div;
130                         nticks++;
131                 }
132                 mtdec(val);
133         } else if (s->mode == 2) {
134                 nticks = 1;
135                 decr_et_stop(NULL);
136         }
137
138         while (nticks-- > 0) {
139                 if (decr_et.et_active)
140                         decr_et.et_event_cb(&decr_et, decr_et.et_arg);
141         }
142 }
143
144 /*
145  * BSP early initialization.
146  */
147 void
148 decr_init(void)
149 {
150         struct cpuref cpu;
151         char buf[32];
152
153         /*
154          * Check the BSP's timebase frequency. Sometimes we can't find the BSP, so fall
155          * back to the first CPU in this case.
156          */
157         if (platform_smp_get_bsp(&cpu) != 0)
158                 platform_smp_first_cpu(&cpu);
159         ticks_per_sec = platform_timebase_freq(&cpu);
160         ns_per_tick = 1000000000 / ticks_per_sec;
161
162         set_cputicker(mftb, ticks_per_sec, 0);
163         snprintf(buf, sizeof(buf), "cpu%d:decrementer", curcpu);
164         intrcnt_add(buf, &decr_counts[curcpu]);
165         decr_et_stop(NULL);
166         initialized = 1;
167 }
168
169 #ifdef SMP
170 /*
171  * AP early initialization.
172  */
173 void
174 decr_ap_init(void)
175 {
176         char buf[32];
177
178         snprintf(buf, sizeof(buf), "cpu%d:decrementer", curcpu);
179         intrcnt_add(buf, &decr_counts[curcpu]);
180         decr_et_stop(NULL);
181 }
182 #endif
183
184 /*
185  * Final initialization.
186  */
187 void
188 decr_tc_init(void)
189 {
190
191         decr_tc.tc_frequency = ticks_per_sec;
192         tc_init(&decr_tc);
193         decr_et.et_name = "decrementer";
194         decr_et.et_flags = ET_FLAGS_PERIODIC | ET_FLAGS_ONESHOT |
195             ET_FLAGS_PERCPU;
196         decr_et.et_quality = 1000;
197         decr_et.et_frequency = ticks_per_sec;
198         decr_et.et_min_period = (0x00000002LLU << 32) / ticks_per_sec;
199         decr_et.et_max_period = (0x7fffffffLLU << 32) / ticks_per_sec;
200         decr_et.et_start = decr_et_start;
201         decr_et.et_stop = decr_et_stop;
202         decr_et.et_priv = NULL;
203         et_register(&decr_et);
204 }
205
206 /*
207  * Event timer start method.
208  */
209 static int
210 decr_et_start(struct eventtimer *et,
211     sbintime_t first, sbintime_t period)
212 {
213         struct decr_state *s = DPCPU_PTR(decr_state);
214         uint32_t fdiv;
215
216         if (period != 0) {
217                 s->mode = 1;
218                 s->div = (decr_et.et_frequency * period) >> 32;
219         } else {
220                 s->mode = 2;
221                 s->div = 0;
222         }
223         if (first != 0) {
224                 fdiv = (decr_et.et_frequency * first) >> 32;
225         } else
226                 fdiv = s->div;
227
228         mtdec(fdiv);
229         return (0);
230 }
231
232 /*
233  * Event timer stop method.
234  */
235 static int
236 decr_et_stop(struct eventtimer *et)
237 {
238         struct decr_state *s = DPCPU_PTR(decr_state);
239
240         s->mode = 0;
241         s->div = 0x7fffffff;
242         mtdec(s->div);
243         return (0);
244 }
245
246 /*
247  * Timecounter get method.
248  */
249 static unsigned
250 decr_get_timecount(struct timecounter *tc)
251 {
252         register_t tb;
253
254         __asm __volatile("mftb %0" : "=r"(tb));
255         return (tb);
256 }
257
258 /*
259  * Wait for about n microseconds (at least!).
260  */
261 void
262 DELAY(int n)
263 {
264         u_quad_t        tb, ttb;
265
266         tb = mftb();
267         ttb = tb + (n * 1000 + ns_per_tick - 1) / ns_per_tick;
268         while (tb < ttb)
269                 tb = mftb();
270 }
271