]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - sys/sparc64/sparc64/tick.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / sys / sparc64 / sparc64 / tick.c
1 /*-
2  * Copyright (c) 2001 Jake Burkholder.
3  * Copyright (c) 2005, 2008 Marius Strobl <marius@FreeBSD.org>
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  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
30
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/kernel.h>
34 #include <sys/lock.h>
35 #include <sys/mutex.h>
36 #include <sys/pcpu.h>
37 #include <sys/proc.h>
38 #include <sys/sched.h>
39 #include <sys/smp.h>
40 #include <sys/sysctl.h>
41 #include <sys/timeet.h>
42 #include <sys/timetc.h>
43
44 #include <dev/ofw/openfirm.h>
45
46 #include <vm/vm.h>
47 #include <vm/pmap.h>
48
49 #include <machine/cpu.h>
50 #include <machine/frame.h>
51 #include <machine/intr_machdep.h>
52 #include <machine/smp.h>
53 #include <machine/tick.h>
54 #include <machine/ver.h>
55
56 #define TICK_QUALITY_MP 10
57 #define TICK_QUALITY_UP 1000
58
59 static SYSCTL_NODE(_machdep, OID_AUTO, tick, CTLFLAG_RD, 0, "tick statistics");
60
61 static int adjust_edges = 0;
62 SYSCTL_INT(_machdep_tick, OID_AUTO, adjust_edges, CTLFLAG_RD, &adjust_edges,
63     0, "total number of times tick interrupts got more than 12.5% behind");
64
65 static int adjust_excess = 0;
66 SYSCTL_INT(_machdep_tick, OID_AUTO, adjust_excess, CTLFLAG_RD, &adjust_excess,
67     0, "total number of ignored tick interrupts");
68
69 static int adjust_missed = 0;
70 SYSCTL_INT(_machdep_tick, OID_AUTO, adjust_missed, CTLFLAG_RD, &adjust_missed,
71     0, "total number of missed tick interrupts");
72
73 static int adjust_ticks = 0;
74 SYSCTL_INT(_machdep_tick, OID_AUTO, adjust_ticks, CTLFLAG_RD, &adjust_ticks,
75     0, "total number of tick interrupts with adjustment");
76
77 u_int tick_et_use_stick = 0;
78 SYSCTL_INT(_machdep_tick, OID_AUTO, tick_et_use_stick, CTLFLAG_RD,
79     &tick_et_use_stick, 0, "tick event timer uses STICK instead of TICK");
80
81 typedef uint64_t rd_tick_t(void);
82 static rd_tick_t *rd_tick;
83 typedef void wr_tick_cmpr_t(uint64_t);
84 static wr_tick_cmpr_t *wr_tick_cmpr;
85
86 static struct timecounter stick_tc;
87 static struct eventtimer tick_et;
88 static struct timecounter tick_tc;
89
90 #ifdef SMP
91 static timecounter_get_t stick_get_timecount_mp;
92 #endif
93 static timecounter_get_t stick_get_timecount_up;
94 static rd_tick_t stick_rd;
95 static wr_tick_cmpr_t stick_wr_cmpr;
96 static int tick_et_start(struct eventtimer *et, sbintime_t first,
97     sbintime_t period);
98 static int tick_et_stop(struct eventtimer *et);
99 #ifdef SMP
100 static timecounter_get_t tick_get_timecount_mp;
101 #endif
102 static timecounter_get_t tick_get_timecount_up;
103 static void tick_intr(struct trapframe *tf);
104 static inline void tick_process(struct trapframe *tf);
105 static rd_tick_t tick_rd;
106 static wr_tick_cmpr_t tick_wr_cmpr;
107 static wr_tick_cmpr_t tick_wr_cmpr_bbwar;
108 static uint64_t tick_cputicks(void);
109
110 static uint64_t
111 stick_rd(void)
112 {
113
114         return (rdstick());
115 }
116
117 static void
118 stick_wr_cmpr(uint64_t tick)
119 {
120
121         wrstickcmpr(tick, 0);
122 }
123
124 static uint64_t
125 tick_rd(void)
126 {
127
128         return (rd(tick));
129 }
130
131 static void
132 tick_wr_cmpr(uint64_t tick_cmpr)
133 {
134
135         wrtickcmpr(tick_cmpr, 0);
136 }
137
138 static void
139 tick_wr_cmpr_bbwar(uint64_t tick_cmpr)
140 {
141
142         wrtickcmpr_bbwar(tick_cmpr, 0);
143 }
144
145 static uint64_t
146 tick_cputicks(void)
147 {
148
149         return (rd(tick));
150 }
151
152 void
153 cpu_initclocks(void)
154 {
155         uint32_t clock, sclock;
156
157         clock = PCPU_GET(clock);
158         sclock = 0;
159         if (PCPU_GET(impl) == CPU_IMPL_SPARC64V ||
160             PCPU_GET(impl) >= CPU_IMPL_ULTRASPARCIII) {
161                 if (OF_getprop(OF_peer(0), "stick-frequency", &sclock,
162                     sizeof(sclock)) == -1) {
163                         panic("%s: could not determine STICK frequency",
164                             __func__);
165                 }
166         }
167         /*
168          * Given that the STICK timers typically are driven at rather low
169          * frequencies they shouldn't be used except when really necessary.
170          */
171         if (tick_et_use_stick != 0) {
172                 rd_tick = stick_rd;
173                 wr_tick_cmpr = stick_wr_cmpr;
174                 /*
175                  * We don't provide a CPU ticker as long as the frequency
176                  * supplied isn't actually used per-CPU.
177                  */
178         } else {
179                 rd_tick = tick_rd;
180                 if (PCPU_GET(impl) >= CPU_IMPL_ULTRASPARCI &&
181                     PCPU_GET(impl) < CPU_IMPL_ULTRASPARCIII)
182                         wr_tick_cmpr = tick_wr_cmpr_bbwar;
183                 else
184                         wr_tick_cmpr = tick_wr_cmpr;
185                 set_cputicker(tick_cputicks, clock, 0);
186         }
187         intr_setup(PIL_TICK, tick_intr, -1, NULL, NULL);
188
189         /*
190          * Initialize the (S)TICK-based timecounter(s).
191          * Note that we (try to) sync the (S)TICK timers of APs with the BSP
192          * during their startup but not afterwards.  The resulting drift can
193          * cause problems when the time is calculated based on (S)TICK values
194          * read on different CPUs.  Thus we always read the register on the
195          * BSP (if necessary via an IPI as sched_bind(9) isn't available in
196          * all circumstances) and use a low quality for the otherwise high
197          * quality (S)TICK timers in the MP case.
198          */
199         tick_tc.tc_get_timecount = tick_get_timecount_up;
200         tick_tc.tc_counter_mask = ~0u;
201         tick_tc.tc_frequency = clock;
202         tick_tc.tc_name = "tick";
203         tick_tc.tc_quality = TICK_QUALITY_UP;
204 #ifdef SMP
205         if (cpu_mp_probe()) {
206                 tick_tc.tc_get_timecount = tick_get_timecount_mp;
207                 tick_tc.tc_quality = TICK_QUALITY_MP;
208         }
209 #endif
210         tc_init(&tick_tc);
211         if (sclock != 0) {
212                 stick_tc.tc_get_timecount = stick_get_timecount_up;
213                 stick_tc.tc_counter_mask = ~0u;
214                 stick_tc.tc_frequency = sclock;
215                 stick_tc.tc_name = "stick";
216                 stick_tc.tc_quality = TICK_QUALITY_UP;
217 #ifdef SMP
218                 if (cpu_mp_probe()) {
219                         stick_tc.tc_get_timecount = stick_get_timecount_mp;
220                         stick_tc.tc_quality = TICK_QUALITY_MP;
221                 }
222 #endif
223                 tc_init(&stick_tc);
224         }
225         tick_et.et_name = tick_et_use_stick ? "stick" : "tick";
226         tick_et.et_flags = ET_FLAGS_PERIODIC | ET_FLAGS_ONESHOT |
227             ET_FLAGS_PERCPU;
228         tick_et.et_quality = 1000;
229         tick_et.et_frequency = tick_et_use_stick ? sclock : clock;
230         tick_et.et_min_period = 0x00010000LLU; /* To be safe. */
231         tick_et.et_max_period = (0xfffffffeLLU << 32) / tick_et.et_frequency;
232         tick_et.et_start = tick_et_start;
233         tick_et.et_stop = tick_et_stop;
234         tick_et.et_priv = NULL;
235         et_register(&tick_et);
236
237         cpu_initclocks_bsp();
238 }
239
240 static inline void
241 tick_process(struct trapframe *tf)
242 {
243         struct trapframe *oldframe;
244         struct thread *td;
245
246         td = curthread;
247         td->td_intr_nesting_level++;
248         critical_enter();
249         if (tick_et.et_active) {
250                 oldframe = td->td_intr_frame;
251                 td->td_intr_frame = tf;
252                 tick_et.et_event_cb(&tick_et, tick_et.et_arg);
253                 td->td_intr_frame = oldframe;
254         }
255         td->td_intr_nesting_level--;
256         critical_exit();
257 }
258
259 static void
260 tick_intr(struct trapframe *tf)
261 {
262         u_long adj, ref, tick, tick_increment;
263         long delta;
264         register_t s;
265         int count;
266
267         tick_increment = PCPU_GET(tickincrement);
268         if (tick_increment != 0) {
269                 /*
270                  * NB: the sequence of reading the (S)TICK register,
271                  * calculating the value of the next tick and writing it to
272                  * the (S)TICK_COMPARE register must not be interrupted, not
273                  * even by an IPI, otherwise a value that is in the past could
274                  * be written in the worst case and thus causing the periodic
275                  * timer to stop.
276                  */
277                 s = intr_disable();
278                 adj = PCPU_GET(tickadj);
279                 tick = rd_tick();
280                 wr_tick_cmpr(tick + tick_increment - adj);
281                 intr_restore(s);
282                 ref = PCPU_GET(tickref);
283                 delta = tick - ref;
284                 count = 0;
285                 while (delta >= tick_increment) {
286                         tick_process(tf);
287                         delta -= tick_increment;
288                         ref += tick_increment;
289                         if (adj != 0)
290                                 adjust_ticks++;
291                         count++;
292                 }
293                 if (count > 0) {
294                         adjust_missed += count - 1;
295                         if (delta > (tick_increment >> 3)) {
296                                 if (adj == 0)
297                                         adjust_edges++;
298                                 adj = tick_increment >> 4;
299                         } else
300                                 adj = 0;
301                 } else {
302                         adj = 0;
303                         adjust_excess++;
304                 }
305                 PCPU_SET(tickref, ref);
306                 PCPU_SET(tickadj, adj);
307         } else
308                 tick_process(tf);
309 }
310
311 static u_int
312 stick_get_timecount_up(struct timecounter *tc)
313 {
314
315         return ((u_int)rdstick());
316 }
317
318 static u_int
319 tick_get_timecount_up(struct timecounter *tc)
320 {
321
322         return ((u_int)rd(tick));
323 }
324
325 #ifdef SMP
326 static u_int
327 stick_get_timecount_mp(struct timecounter *tc)
328 {
329         u_long stick;
330
331         sched_pin();
332         if (curcpu == 0)
333                 stick = rdstick();
334         else
335                 ipi_wait(ipi_rd(0, tl_ipi_stick_rd, &stick));
336         sched_unpin();
337         return (stick);
338 }
339
340 static u_int
341 tick_get_timecount_mp(struct timecounter *tc)
342 {
343         u_long tick;
344
345         sched_pin();
346         if (curcpu == 0)
347                 tick = rd(tick);
348         else
349                 ipi_wait(ipi_rd(0, tl_ipi_tick_rd, &tick));
350         sched_unpin();
351         return (tick);
352 }
353 #endif
354
355 static int
356 tick_et_start(struct eventtimer *et, sbintime_t first, sbintime_t period)
357 {
358         u_long base, div, fdiv;
359         register_t s;
360
361         if (period != 0)
362                 div = (tick_et.et_frequency * period) >> 32;
363         else
364                 div = 0;
365         if (first != 0)
366                 fdiv = (tick_et.et_frequency * first) >> 32;
367         else
368                 fdiv = div;
369         PCPU_SET(tickincrement, div);
370
371         /*
372          * Try to make the (S)TICK interrupts as synchronously as possible
373          * on all CPUs to avoid inaccuracies for migrating processes.  Leave
374          * out one tick to make sure that it is not missed.
375          */
376         s = intr_disable();
377         base = rd_tick();
378         if (div != 0) {
379                 PCPU_SET(tickadj, 0);
380                 base = roundup(base, div);
381         }
382         PCPU_SET(tickref, base);
383         wr_tick_cmpr(base + fdiv);
384         intr_restore(s);
385         return (0);
386 }
387
388 static int
389 tick_et_stop(struct eventtimer *et)
390 {
391
392         PCPU_SET(tickincrement, 0);
393         tick_stop(PCPU_GET(impl));
394         return (0);
395 }
396
397 void
398 tick_clear(u_int cpu_impl)
399 {
400
401         if (cpu_impl == CPU_IMPL_SPARC64V ||
402             cpu_impl >= CPU_IMPL_ULTRASPARCIII)
403                 wrstick(0, 0);
404         wrpr(tick, 0, 0);
405 }
406
407 void
408 tick_stop(u_int cpu_impl)
409 {
410
411         if (cpu_impl == CPU_IMPL_SPARC64V ||
412             cpu_impl >= CPU_IMPL_ULTRASPARCIII)
413                 wrstickcmpr(1L << 63, 0);
414         wrtickcmpr(1L << 63, 0);
415 }