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#include <platforms.h>
33
34#include <mach/mach_types.h>
35
36#include <architecture/i386/pio.h>
37#include <i386/machine_cpu.h>
38#include <i386/cpuid.h>
39#include <i386/cpu_threads.h>
40#include <i386/mp.h>
41#include <i386/machine_routines.h>
42#include <i386/pal_routines.h>
43#include <i386/proc_reg.h>
44#include <i386/misc_protos.h>
45#include <i386/lapic.h>
46#include <pexpert/pexpert.h>
47#include <machine/limits.h>
48#include <sys/kdebug.h>
49#include <i386/tsc.h>
50#include <i386/rtclock_protos.h>
51#include <i386/pal_routines.h>
52#include <kern/timer_queue.h>
53
54static uint64_t	rtc_decrementer_min;
55static uint64_t	rtc_decrementer_max;
56
57static uint64_t
58deadline_to_decrementer(
59	uint64_t	deadline,
60	uint64_t	now)
61{
62	uint64_t	delta;
63
64	if (deadline <= now)
65		return rtc_decrementer_min;
66	else {
67		delta = deadline - now;
68		return MIN(MAX(rtc_decrementer_min,delta),rtc_decrementer_max);
69	}
70}
71
72
73/*
74 * Regular local APIC timer case:
75 */
76static void
77rtc_lapic_config_timer(void)
78{
79	lapic_config_timer(TRUE, one_shot, divide_by_1);
80}
81static uint64_t
82rtc_lapic_set_timer(uint64_t deadline, uint64_t now)
83{
84	uint64_t count;
85	uint64_t set = 0;
86
87	if (deadline > 0) {
88		/*
89		 * Convert delta to bus ticks
90		 * - time now is not relevant
91		 */
92		count = deadline_to_decrementer(deadline, now);
93		set = now + count;
94		lapic_set_timer_fast((uint32_t) tmrCvt(count, busFCvtn2t));
95	} else {
96		lapic_set_timer(FALSE, one_shot, divide_by_1, 0);
97	}
98	return set;
99}
100
101/*
102 * TSC-deadline timer case:
103 */
104static void
105rtc_lapic_config_tsc_deadline_timer(void)
106{
107	lapic_config_tsc_deadline_timer();
108}
109static uint64_t
110rtc_lapic_set_tsc_deadline_timer(uint64_t deadline, uint64_t now)
111{
112	uint64_t delta;
113	uint64_t delta_tsc;
114	uint64_t tsc = rdtsc64();
115	uint64_t set = 0;
116
117	if (deadline > 0) {
118		/*
119		 * Convert to TSC
120		 */
121		delta = deadline_to_decrementer(deadline, now);
122		set = now + delta;
123		delta_tsc = tmrCvt(delta, tscFCvtn2t);
124		lapic_set_tsc_deadline_timer(tsc + delta_tsc);
125	} else {
126		lapic_set_tsc_deadline_timer(0);
127	}
128
129	KERNEL_DEBUG_CONSTANT(
130		DECR_SET_TSC_DEADLINE | DBG_FUNC_NONE,
131		now, deadline,
132		tsc, lapic_get_tsc_deadline_timer(),
133		0);
134
135	return set;
136}
137
138/*
139 * Definitions for timer operations table
140 */
141
142rtc_timer_t	rtc_timer_lapic  = {
143	rtc_lapic_config_timer,
144	rtc_lapic_set_timer
145};
146
147rtc_timer_t	rtc_timer_tsc_deadline  = {
148	rtc_lapic_config_tsc_deadline_timer,
149	rtc_lapic_set_tsc_deadline_timer
150};
151
152rtc_timer_t	*rtc_timer = &rtc_timer_lapic; /* defaults to LAPIC timer */
153
154/*
155 * rtc_timer_init() is called at startup on the boot processor only.
156 */
157void
158rtc_timer_init(void)
159{
160	int	TSC_deadline_timer = 0;
161
162	/* See whether we can use the local apic in TSC-deadline mode */
163	if ((cpuid_features() & CPUID_FEATURE_TSCTMR)) {
164		TSC_deadline_timer = 1;
165		PE_parse_boot_argn("TSC_deadline_timer", &TSC_deadline_timer,
166				   sizeof(TSC_deadline_timer));
167		printf("TSC Deadline Timer supported %s enabled\n",
168			TSC_deadline_timer ? "and" : "but not");
169	}
170
171	if (TSC_deadline_timer) {
172		rtc_timer = &rtc_timer_tsc_deadline;
173		rtc_decrementer_max = UINT64_MAX;	/* effectively none */
174		/*
175		 * The min could be as low as 1nsec,
176		 * but we're being conservative for now and making it the same
177		 * as for the local apic timer.
178		 */
179		rtc_decrementer_min = 1*NSEC_PER_USEC;	/* 1 usec */
180	} else {
181		/*
182		 * Compute the longest interval using LAPIC timer.
183		 */
184		rtc_decrementer_max = tmrCvt(0x7fffffffULL, busFCvtt2n);
185		kprintf("maxDec: %lld\n", rtc_decrementer_max);
186		rtc_decrementer_min = 1*NSEC_PER_USEC;	/* 1 usec */
187	}
188
189	/* Point LAPIC interrupts to hardclock() */
190	lapic_set_timer_func((i386_intr_func_t) rtclock_intr);
191}
192