]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - sys/powerpc/aim/clock.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.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     struct bintime *first, struct bintime *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.sec = 0;
199         decr_et.et_min_period.frac =
200             ((0x00000002LLU << 32) / ticks_per_sec) << 32;
201         decr_et.et_max_period.sec = 0x7fffffffLLU / ticks_per_sec;
202         decr_et.et_max_period.frac =
203             ((0x7fffffffLLU << 32) / ticks_per_sec) << 32;
204         decr_et.et_start = decr_et_start;
205         decr_et.et_stop = decr_et_stop;
206         decr_et.et_priv = NULL;
207         et_register(&decr_et);
208 }
209
210 /*
211  * Event timer start method.
212  */
213 static int
214 decr_et_start(struct eventtimer *et,
215     struct bintime *first, struct bintime *period)
216 {
217         struct decr_state *s = DPCPU_PTR(decr_state);
218         uint32_t fdiv;
219
220         if (period != NULL) {
221                 s->mode = 1;
222                 s->div = (decr_et.et_frequency * (period->frac >> 32)) >> 32;
223                 if (period->sec != 0)
224                         s->div += decr_et.et_frequency * period->sec;
225         } else {
226                 s->mode = 2;
227                 s->div = 0x7fffffff;
228         }
229         if (first != NULL) {
230                 fdiv = (decr_et.et_frequency * (first->frac >> 32)) >> 32;
231                 if (first->sec != 0)
232                         fdiv += decr_et.et_frequency * first->sec;
233         } else
234                 fdiv = s->div;
235
236         mtdec(fdiv);
237         return (0);
238 }
239
240 /*
241  * Event timer stop method.
242  */
243 static int
244 decr_et_stop(struct eventtimer *et)
245 {
246         struct decr_state *s = DPCPU_PTR(decr_state);
247
248         s->mode = 0;
249         s->div = 0x7fffffff;
250         mtdec(s->div);
251         return (0);
252 }
253
254 /*
255  * Timecounter get method.
256  */
257 static unsigned
258 decr_get_timecount(struct timecounter *tc)
259 {
260         register_t tb;
261
262         __asm __volatile("mftb %0" : "=r"(tb));
263         return (tb);
264 }
265
266 /*
267  * Wait for about n microseconds (at least!).
268  */
269 void
270 DELAY(int n)
271 {
272         u_quad_t        tb, ttb;
273
274         tb = mftb();
275         ttb = tb + (n * 1000 + ns_per_tick - 1) / ns_per_tick;
276         while (tb < ttb)
277                 tb = mftb();
278 }
279