dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / uts / i86pc / io / pcplusmp / apic_timer.c
blob348f5034fc29721387bfa4548fb66aa9933387eb
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
19 * CDDL HEADER END
22 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
25 * Copyright (c) 2010, Intel Corporation.
26 * All rights reserved.
29 * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
32 #include <sys/time.h>
33 #include <sys/psm.h>
34 #include <sys/psm_common.h>
35 #include <sys/apic.h>
36 #include <sys/pit.h>
37 #include <sys/x86_archext.h>
38 #include <sys/archsystm.h>
39 #include <sys/machsystm.h>
40 #include <sys/cpuvar.h>
41 #include <sys/clock.h>
42 #include <sys/apic_timer.h>
45 * preferred apic timer mode, allow tuning from the /etc/system file.
47 int apic_timer_preferred_mode = APIC_TIMER_MODE_DEADLINE;
49 int apic_oneshot = 0;
50 uint_t apic_hertz_count;
51 uint_t apic_nsec_per_intr = 0;
52 uint64_t apic_ticks_per_SFnsecs; /* # of ticks in SF nsecs */
54 static int apic_min_timer_ticks = 1; /* minimum timer tick */
55 static hrtime_t apic_nsec_max;
57 static void periodic_timer_enable(void);
58 static void periodic_timer_disable(void);
59 static void periodic_timer_reprogram(hrtime_t);
60 static void oneshot_timer_enable(void);
61 static void oneshot_timer_disable(void);
62 static void oneshot_timer_reprogram(hrtime_t);
63 static void deadline_timer_enable(void);
64 static void deadline_timer_disable(void);
65 static void deadline_timer_reprogram(hrtime_t);
67 extern int apic_clkvect;
68 extern uint32_t apic_divide_reg_init;
71 * apic timer data structure
73 typedef struct apic_timer {
74 int mode;
75 void (*apic_timer_enable_ops)(void);
76 void (*apic_timer_disable_ops)(void);
77 void (*apic_timer_reprogram_ops)(hrtime_t);
78 } apic_timer_t;
80 static apic_timer_t apic_timer;
83 * apic timer initialization
85 * For the one-shot mode request case, the function returns the
86 * resolution (in nanoseconds) for the hardware timer interrupt.
87 * If one-shot mode capability is not available, the return value
88 * will be 0.
90 int
91 apic_timer_init(int hertz)
93 uint_t apic_ticks = 0;
94 uint_t pit_ticks;
95 int ret, timer_mode;
96 uint16_t pit_ticks_adj;
97 static int firsttime = 1;
99 if (firsttime) {
100 /* first time calibrate on CPU0 only */
102 apic_reg_ops->apic_write(APIC_DIVIDE_REG, apic_divide_reg_init);
103 apic_reg_ops->apic_write(APIC_INIT_COUNT, APIC_MAXVAL);
104 apic_ticks = apic_calibrate(apicadr, &pit_ticks_adj);
106 /* total number of PIT ticks corresponding to apic_ticks */
107 pit_ticks = APIC_TIME_COUNT + pit_ticks_adj;
110 * Determine the number of nanoseconds per APIC clock tick
111 * and then determine how many APIC ticks to interrupt at the
112 * desired frequency
113 * apic_ticks / (pitticks / PIT_HZ) = apic_ticks_per_s
114 * (apic_ticks * PIT_HZ) / pitticks = apic_ticks_per_s
115 * apic_ticks_per_ns = (apic_ticks * PIT_HZ) / (pitticks * 10^9)
116 * pic_ticks_per_SFns =
117 * (SF * apic_ticks * PIT_HZ) / (pitticks * 10^9)
119 apic_ticks_per_SFnsecs = ((SF * apic_ticks * PIT_HZ) /
120 ((uint64_t)pit_ticks * NANOSEC));
122 /* the interval timer initial count is 32 bit max */
123 apic_nsec_max = APIC_TICKS_TO_NSECS(APIC_MAXVAL);
124 firsttime = 0;
127 if (hertz == 0) {
128 /* requested one_shot */
131 * return 0 if TSC is not supported.
133 if (!tsc_gethrtime_enable)
134 return (0);
136 * return 0 if one_shot is not preferred.
137 * here, APIC_TIMER_DEADLINE is also an one_shot mode.
139 if ((apic_timer_preferred_mode != APIC_TIMER_MODE_ONESHOT) &&
140 (apic_timer_preferred_mode != APIC_TIMER_MODE_DEADLINE))
141 return (0);
143 apic_oneshot = 1;
144 ret = (int)APIC_TICKS_TO_NSECS(1);
145 if ((apic_timer_preferred_mode == APIC_TIMER_MODE_DEADLINE) &&
146 cpuid_deadline_tsc_supported()) {
147 timer_mode = APIC_TIMER_MODE_DEADLINE;
148 } else {
149 timer_mode = APIC_TIMER_MODE_ONESHOT;
151 } else {
152 /* periodic */
153 apic_nsec_per_intr = NANOSEC / hertz;
154 apic_hertz_count = APIC_NSECS_TO_TICKS(apic_nsec_per_intr);
156 /* program the local APIC to interrupt at the given frequency */
157 apic_reg_ops->apic_write(APIC_INIT_COUNT, apic_hertz_count);
158 apic_reg_ops->apic_write(APIC_LOCAL_TIMER,
159 (apic_clkvect + APIC_BASE_VECT) | AV_PERIODIC);
160 apic_oneshot = 0;
161 timer_mode = APIC_TIMER_MODE_PERIODIC;
162 ret = NANOSEC / hertz;
166 * initialize apic_timer data structure, install the timer ops
168 apic_timer.mode = timer_mode;
169 switch (timer_mode) {
170 default:
171 /* FALLTHROUGH */
172 case APIC_TIMER_MODE_ONESHOT:
173 apic_timer.apic_timer_enable_ops = oneshot_timer_enable;
174 apic_timer.apic_timer_disable_ops = oneshot_timer_disable;
175 apic_timer.apic_timer_reprogram_ops = oneshot_timer_reprogram;
176 break;
178 case APIC_TIMER_MODE_PERIODIC:
179 apic_timer.apic_timer_enable_ops = periodic_timer_enable;
180 apic_timer.apic_timer_disable_ops = periodic_timer_disable;
181 apic_timer.apic_timer_reprogram_ops = periodic_timer_reprogram;
182 break;
184 case APIC_TIMER_MODE_DEADLINE:
185 apic_timer.apic_timer_enable_ops = deadline_timer_enable;
186 apic_timer.apic_timer_disable_ops = deadline_timer_disable;
187 apic_timer.apic_timer_reprogram_ops = deadline_timer_reprogram;
188 break;
191 return (ret);
195 * periodic timer mode ops
197 /* periodic timer enable */
198 static void
199 periodic_timer_enable(void)
201 apic_reg_ops->apic_write(APIC_LOCAL_TIMER,
202 (apic_clkvect + APIC_BASE_VECT) | AV_PERIODIC);
205 /* periodic timer disable */
206 static void
207 periodic_timer_disable(void)
209 apic_reg_ops->apic_write(APIC_LOCAL_TIMER,
210 (apic_clkvect + APIC_BASE_VECT) | AV_MASK);
213 /* periodic timer reprogram */
214 static void
215 periodic_timer_reprogram(hrtime_t time)
217 uint_t ticks;
218 /* time is the interval for periodic mode */
219 ticks = APIC_NSECS_TO_TICKS(time);
221 if (ticks < apic_min_timer_ticks)
222 ticks = apic_min_timer_ticks;
224 apic_reg_ops->apic_write(APIC_INIT_COUNT, ticks);
228 * oneshot timer mode ops
230 /* oneshot timer enable */
231 static void
232 oneshot_timer_enable(void)
234 apic_reg_ops->apic_write(APIC_LOCAL_TIMER,
235 (apic_clkvect + APIC_BASE_VECT));
238 /* oneshot timer disable */
239 static void
240 oneshot_timer_disable(void)
242 apic_reg_ops->apic_write(APIC_LOCAL_TIMER,
243 (apic_clkvect + APIC_BASE_VECT) | AV_MASK);
246 /* oneshot timer reprogram */
247 static void
248 oneshot_timer_reprogram(hrtime_t time)
250 hrtime_t now;
251 int64_t delta;
252 uint_t ticks;
254 now = gethrtime();
255 delta = time - now;
257 if (delta <= 0) {
259 * requested to generate an interrupt in the past
260 * generate an interrupt as soon as possible
262 ticks = apic_min_timer_ticks;
263 } else if (delta > apic_nsec_max) {
265 * requested to generate an interrupt at a time
266 * further than what we are capable of. Set to max
267 * the hardware can handle
269 ticks = APIC_MAXVAL;
270 #ifdef DEBUG
271 cmn_err(CE_CONT, "apic_timer_reprogram, request at"
272 " %lld too far in future, current time"
273 " %lld \n", time, now);
274 #endif
275 } else {
276 ticks = APIC_NSECS_TO_TICKS(delta);
279 if (ticks < apic_min_timer_ticks)
280 ticks = apic_min_timer_ticks;
282 apic_reg_ops->apic_write(APIC_INIT_COUNT, ticks);
286 * deadline timer mode ops
288 /* deadline timer enable */
289 static void
290 deadline_timer_enable(void)
292 uint64_t ticks;
294 apic_reg_ops->apic_write(APIC_LOCAL_TIMER,
295 (apic_clkvect + APIC_BASE_VECT) | AV_DEADLINE);
297 * Now we have to serialize this per the SDM. That is to
298 * say, the above enabling can race in the pipeline with
299 * changes to the MSR. We need to make sure the above
300 * operation is complete before we proceed to reprogram
301 * the deadline value in reprogram(). The algorithm
302 * recommended by the Intel SDM 3A in 10.5.1.4 is:
304 * a) write a big value to the deadline register
305 * b) read the register back
306 * c) if it reads zero, go back to a and try again
309 do {
310 /* write a really big value */
311 wrmsr(IA32_DEADLINE_TSC_MSR, 1ULL << 63);
312 ticks = rdmsr(IA32_DEADLINE_TSC_MSR);
313 } while (ticks == 0);
316 /* deadline timer disable */
317 static void
318 deadline_timer_disable(void)
320 apic_reg_ops->apic_write(APIC_LOCAL_TIMER,
321 (apic_clkvect + APIC_BASE_VECT) | AV_MASK);
324 /* deadline timer reprogram */
325 static void
326 deadline_timer_reprogram(hrtime_t time)
328 int64_t delta;
329 uint64_t ticks;
332 * Note that this entire routine is called with
333 * CBE_HIGH_PIL, so we needn't worry about preemption.
335 delta = time - gethrtime();
337 /* The unscalehrtime wants unsigned values. */
338 delta = max(delta, 0);
340 /* Now we shouldn't be interrupted, we can set the deadline */
341 ticks = (uint64_t)tsc_read() + unscalehrtime(delta);
342 wrmsr(IA32_DEADLINE_TSC_MSR, ticks);
346 * This function will reprogram the timer.
348 * When in oneshot mode the argument is the absolute time in future to
349 * generate the interrupt at.
351 * When in periodic mode, the argument is the interval at which the
352 * interrupts should be generated. There is no need to support the periodic
353 * mode timer change at this time.
355 void
356 apic_timer_reprogram(hrtime_t time)
359 * we should be Called from high PIL context (CBE_HIGH_PIL),
360 * so kpreempt is disabled.
362 apic_timer.apic_timer_reprogram_ops(time);
366 * This function will enable timer interrupts.
368 void
369 apic_timer_enable(void)
372 * we should be Called from high PIL context (CBE_HIGH_PIL),
373 * so kpreempt is disabled.
375 apic_timer.apic_timer_enable_ops();
379 * This function will disable timer interrupts.
381 void
382 apic_timer_disable(void)
385 * we should be Called from high PIL context (CBE_HIGH_PIL),
386 * so kpreempt is disabled.
388 apic_timer.apic_timer_disable_ops();
392 * Set timer far into the future and return timer
393 * current count in nanoseconds.
395 hrtime_t
396 apic_timer_stop_count(void)
398 hrtime_t ns_val;
399 int enable_val, count_val;
402 * Should be called with interrupts disabled.
404 ASSERT(!interrupts_enabled());
406 enable_val = apic_reg_ops->apic_read(APIC_LOCAL_TIMER);
407 if ((enable_val & AV_MASK) == AV_MASK)
408 return ((hrtime_t)-1); /* timer is disabled */
410 count_val = apic_reg_ops->apic_read(APIC_CURR_COUNT);
411 ns_val = APIC_TICKS_TO_NSECS(count_val);
413 apic_reg_ops->apic_write(APIC_INIT_COUNT, APIC_MAXVAL);
415 return (ns_val);
419 * Reprogram timer after Deep C-State.
421 void
422 apic_timer_restart(hrtime_t time)
424 apic_timer_reprogram(time);