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]
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.
34 #include <sys/psm_common.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
;
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
{
75 void (*apic_timer_enable_ops
)(void);
76 void (*apic_timer_disable_ops
)(void);
77 void (*apic_timer_reprogram_ops
)(hrtime_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
91 apic_timer_init(int hertz
)
93 uint_t apic_ticks
= 0;
96 uint16_t pit_ticks_adj
;
97 static int firsttime
= 1;
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
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
);
128 /* requested one_shot */
131 * return 0 if TSC is not supported.
133 if (!tsc_gethrtime_enable
)
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
))
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
;
149 timer_mode
= APIC_TIMER_MODE_ONESHOT
;
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
);
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
) {
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
;
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
;
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
;
195 * periodic timer mode ops
197 /* periodic timer enable */
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 */
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 */
215 periodic_timer_reprogram(hrtime_t time
)
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 */
232 oneshot_timer_enable(void)
234 apic_reg_ops
->apic_write(APIC_LOCAL_TIMER
,
235 (apic_clkvect
+ APIC_BASE_VECT
));
238 /* oneshot timer disable */
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 */
248 oneshot_timer_reprogram(hrtime_t time
)
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
271 cmn_err(CE_CONT
, "apic_timer_reprogram, request at"
272 " %lld too far in future, current time"
273 " %lld \n", time
, now
);
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 */
290 deadline_timer_enable(void)
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
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 */
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 */
326 deadline_timer_reprogram(hrtime_t time
)
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.
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.
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.
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.
396 apic_timer_stop_count(void)
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
);
419 * Reprogram timer after Deep C-State.
422 apic_timer_restart(hrtime_t time
)
424 apic_timer_reprogram(time
);