xref: /xnu-8020.101.4/osfmk/i386/rtclock_native.c (revision e7776783b89a353188416a9a346c6cdb4928faad)
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 
123 	if (deadline > 0) {
124 		/*
125 		 * Convert to TSC
126 		 */
127 		delta = deadline_to_decrementer(deadline, now);
128 		set = now + delta;
129 		delta_tsc = tmrCvt(delta, tscFCvtn2t);
130 		lapic_set_tsc_deadline_timer(tsc + delta_tsc);
131 	} else {
132 		lapic_set_tsc_deadline_timer(0);
133 	}
134 
135 	KERNEL_DEBUG_CONSTANT(
136 		DECR_SET_TSC_DEADLINE | DBG_FUNC_NONE,
137 		now, deadline,
138 		tsc, lapic_get_tsc_deadline_timer(),
139 		0);
140 
141 	return set;
142 }
143 
144 /*
145  * Definitions for timer operations table
146  */
147 
148 rtc_timer_t rtc_timer_lapic = {
149 	.rtc_config = rtc_lapic_config_timer,
150 	.rtc_set    = rtc_lapic_set_timer,
151 };
152 
153 rtc_timer_t rtc_timer_tsc_deadline = {
154 	.rtc_config = rtc_lapic_config_tsc_deadline_timer,
155 	.rtc_set    = rtc_lapic_set_tsc_deadline_timer,
156 };
157 
158 rtc_timer_t     *rtc_timer = &rtc_timer_lapic; /* defaults to LAPIC timer */
159 
160 /*
161  * rtc_timer_init() is called at startup on the boot processor only.
162  */
163 void
rtc_timer_init(void)164 rtc_timer_init(void)
165 {
166 	int     TSC_deadline_timer = 0;
167 
168 	/* See whether we can use the local apic in TSC-deadline mode */
169 	if ((cpuid_features() & CPUID_FEATURE_TSCTMR)) {
170 		TSC_deadline_timer = 1;
171 		PE_parse_boot_argn("TSC_deadline_timer", &TSC_deadline_timer,
172 		    sizeof(TSC_deadline_timer));
173 		printf("TSC Deadline Timer supported %s enabled\n",
174 		    TSC_deadline_timer ? "and" : "but not");
175 	}
176 
177 	if (TSC_deadline_timer) {
178 		rtc_timer = &rtc_timer_tsc_deadline;
179 		rtc_decrementer_max = UINT64_MAX;       /* effectively none */
180 		/*
181 		 * The min could be as low as 1nsec,
182 		 * but we're being conservative for now and making it the same
183 		 * as for the local apic timer.
184 		 */
185 		rtc_decrementer_min = 1 * NSEC_PER_USEC;  /* 1 usec */
186 	} else {
187 		/*
188 		 * Compute the longest interval using LAPIC timer.
189 		 */
190 		rtc_decrementer_max = tmrCvt(0x7fffffffULL, busFCvtt2n);
191 		kprintf("maxDec: %lld\n", rtc_decrementer_max);
192 		rtc_decrementer_min = 1 * NSEC_PER_USEC;  /* 1 usec */
193 	}
194 
195 	/* Point LAPIC interrupts to hardclock() */
196 	lapic_set_timer_func((i386_intr_func_t) rtclock_intr);
197 }
198