xref: /xnu-11215.41.3/osfmk/i386/rtclock_native.c (revision 33de042d024d46de5ff4e89f2471de6608e37fa4)
1 /*
2  * Copyright (c) 2000-2011 Apple Inc. All rights reserved.
3  *
4  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5  *
6  * This file contains Original Code and/or Modifications of Original Code
7  * as defined in and that are subject to the Apple Public Source License
8  * Version 2.0 (the 'License'). You may not use this file except in
9  * compliance with the License. The rights granted to you under the License
10  * may not be used to create, or enable the creation or redistribution of,
11  * unlawful or unlicensed copies of an Apple operating system, or to
12  * circumvent, violate, or enable the circumvention or violation of, any
13  * terms of an Apple operating system software license agreement.
14  *
15  * Please obtain a copy of the License at
16  * http://www.opensource.apple.com/apsl/ and read it before using this file.
17  *
18  * The Original Code and all software distributed under the License are
19  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23  * Please see the License for the specific language governing rights and
24  * limitations under the License.
25  *
26  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27  */
28 /*
29  * @OSF_COPYRIGHT@
30  */
31 
32 
33 #include <mach/mach_types.h>
34 
35 #include <architecture/i386/pio.h>
36 #include <i386/machine_cpu.h>
37 #include <i386/cpuid.h>
38 #include <i386/cpu_threads.h>
39 #include <i386/mp.h>
40 #include <i386/machine_routines.h>
41 #include <i386/pal_routines.h>
42 #include <i386/proc_reg.h>
43 #include <i386/misc_protos.h>
44 #include <i386/lapic.h>
45 #include <pexpert/pexpert.h>
46 #include <machine/limits.h>
47 #include <sys/kdebug.h>
48 #include <i386/tsc.h>
49 #include <i386/rtclock_protos.h>
50 #include <i386/pal_routines.h>
51 #include <kern/timer_queue.h>
52 
53 static uint64_t rtc_decrementer_min;
54 static uint64_t rtc_decrementer_max;
55 
56 static uint64_t
deadline_to_decrementer(uint64_t deadline,uint64_t now)57 deadline_to_decrementer(
58 	uint64_t        deadline,
59 	uint64_t        now)
60 {
61 	uint64_t        delta;
62 
63 	if (deadline <= now) {
64 		return rtc_decrementer_min;
65 	} else {
66 		delta = deadline - now;
67 		return MIN(MAX(rtc_decrementer_min, delta), rtc_decrementer_max);
68 	}
69 }
70 
71 
72 /*
73  * Regular local APIC timer case:
74  */
75 static void
rtc_lapic_config_timer(void)76 rtc_lapic_config_timer(void)
77 {
78 	lapic_config_timer(TRUE, one_shot, divide_by_1);
79 }
80 static uint64_t
rtc_lapic_set_timer(uint64_t deadline,uint64_t now)81 rtc_lapic_set_timer(uint64_t deadline, uint64_t now)
82 {
83 	uint64_t count;
84 	uint64_t set = 0;
85 
86 	if (deadline > 0) {
87 		/*
88 		 * Convert delta to bus ticks
89 		 * - time now is not relevant
90 		 */
91 		count = deadline_to_decrementer(deadline, now);
92 		set = now + count;
93 		lapic_set_timer_fast((uint32_t) tmrCvt(count, busFCvtn2t));
94 	} else {
95 		lapic_set_timer(FALSE, one_shot, divide_by_1, 0);
96 	}
97 
98 	KERNEL_DEBUG_CONSTANT(
99 		DECR_SET_APIC_DEADLINE | DBG_FUNC_NONE,
100 		now, deadline,
101 		set, LAPIC_READ(TIMER_CURRENT_COUNT),
102 		0);
103 
104 	return set;
105 }
106 
107 /*
108  * TSC-deadline timer case:
109  */
110 static void
rtc_lapic_config_tsc_deadline_timer(void)111 rtc_lapic_config_tsc_deadline_timer(void)
112 {
113 	lapic_config_tsc_deadline_timer();
114 }
115 static uint64_t
rtc_lapic_set_tsc_deadline_timer(uint64_t deadline,uint64_t now)116 rtc_lapic_set_tsc_deadline_timer(uint64_t deadline, uint64_t now)
117 {
118 	uint64_t delta;
119 	uint64_t delta_tsc;
120 	uint64_t tsc = rdtsc64();
121 	uint64_t set = 0;
122 	uint64_t dead_tsc;
123 
124 	if (deadline > 0) {
125 		/*
126 		 * Convert to TSC
127 		 */
128 		delta = deadline_to_decrementer(deadline, now);
129 		set = now + delta;
130 		delta_tsc = tmrCvt(delta, tscFCvtn2t);
131 		dead_tsc = tsc + delta_tsc;
132 	} else {
133 		dead_tsc = 0;
134 	}
135 	lapic_set_tsc_deadline_timer(dead_tsc);
136 
137 	KERNEL_DEBUG_CONSTANT(
138 		DECR_SET_TSC_DEADLINE | DBG_FUNC_NONE,
139 		now, deadline,
140 		tsc, dead_tsc,
141 		0);
142 
143 	return set;
144 }
145 
146 /*
147  * Definitions for timer operations table
148  */
149 
150 rtc_timer_t rtc_timer_lapic = {
151 	.rtc_config = rtc_lapic_config_timer,
152 	.rtc_set    = rtc_lapic_set_timer,
153 };
154 
155 rtc_timer_t rtc_timer_tsc_deadline = {
156 	.rtc_config = rtc_lapic_config_tsc_deadline_timer,
157 	.rtc_set    = rtc_lapic_set_tsc_deadline_timer,
158 };
159 
160 rtc_timer_t     *rtc_timer = &rtc_timer_lapic; /* defaults to LAPIC timer */
161 
162 /*
163  * rtc_timer_init() is called at startup on the boot processor only.
164  */
165 void
rtc_timer_init(void)166 rtc_timer_init(void)
167 {
168 	int     TSC_deadline_timer = 0;
169 
170 	/* See whether we can use the local apic in TSC-deadline mode */
171 	if ((cpuid_features() & CPUID_FEATURE_TSCTMR)) {
172 		TSC_deadline_timer = 1;
173 		PE_parse_boot_argn("TSC_deadline_timer", &TSC_deadline_timer,
174 		    sizeof(TSC_deadline_timer));
175 		printf("TSC Deadline Timer supported %s enabled\n",
176 		    TSC_deadline_timer ? "and" : "but not");
177 	}
178 
179 	if (TSC_deadline_timer) {
180 		rtc_timer = &rtc_timer_tsc_deadline;
181 		rtc_decrementer_max = UINT64_MAX;       /* effectively none */
182 		/*
183 		 * The min could be as low as 1nsec,
184 		 * but we're being conservative for now and making it the same
185 		 * as for the local apic timer.
186 		 */
187 		rtc_decrementer_min = 1 * NSEC_PER_USEC;  /* 1 usec */
188 	} else {
189 		/*
190 		 * Compute the longest interval using LAPIC timer.
191 		 */
192 		rtc_decrementer_max = tmrCvt(0x7fffffffULL, busFCvtt2n);
193 		kprintf("maxDec: %lld\n", rtc_decrementer_max);
194 		rtc_decrementer_min = 1 * NSEC_PER_USEC;  /* 1 usec */
195 	}
196 
197 	/* Point LAPIC interrupts to hardclock() */
198 	lapic_set_timer_func((i386_intr_func_t) rtclock_intr);
199 }
200